Browse Source

Fixed problems with slow loading

Stephen Ficklin 8 years ago
parent
commit
3bb12b4d5a
27 changed files with 934 additions and 648 deletions
  1. 2 3
      tripal/api/tripal.entities.api.inc
  2. 4 1
      tripal/includes/tripal.entity.inc
  3. 1 1
      tripal/tripal.module
  4. 326 1
      tripal_chado/api/modules/tripal_chado.feature.api.inc
  5. 1 1
      tripal_chado/api/tripal_chado.mviews.api.inc
  6. 2 2
      tripal_chado/api/tripal_chado.query.api.inc
  7. 39 9
      tripal_chado/api/tripal_chado.schema.api.inc
  8. 4 1
      tripal_chado/api/tripal_chado.variables.api.inc
  9. 4 4
      tripal_chado/includes/TripalFields.old/chado_linker__expression.inc
  10. 10 11
      tripal_chado/includes/TripalFields/chado_linker__expression.inc
  11. 16 16
      tripal_chado/includes/TripalFields/chado_linker__synonym_widget.inc
  12. 81 0
      tripal_chado/includes/TripalFields/data__protein_sequence.inc
  13. 50 0
      tripal_chado/includes/TripalFields/data__protein_sequence_formatter.inc
  14. 43 0
      tripal_chado/includes/TripalFields/data__protein_sequence_widget.inc
  15. 90 477
      tripal_chado/includes/TripalFields/data__sequence.inc
  16. 0 7
      tripal_chado/includes/TripalFields/data__sequence_checksum.inc
  17. 4 15
      tripal_chado/includes/TripalFields/data__sequence_formatter.inc
  18. 0 7
      tripal_chado/includes/TripalFields/data__sequence_length.inc
  19. 96 0
      tripal_chado/includes/TripalFields/so__cds.inc
  20. 44 0
      tripal_chado/includes/TripalFields/so__cds_formatter.inc
  21. 43 0
      tripal_chado/includes/TripalFields/so__cds_widget.inc
  22. 45 53
      tripal_chado/includes/TripalFields/so__transcript.inc
  23. 1 1
      tripal_chado/includes/tripal_chado.custom_tables.inc
  24. 0 11
      tripal_chado/includes/tripal_chado.field_storage.inc
  25. 3 3
      tripal_chado/includes/tripal_chado.fields.inc
  26. 20 20
      tripal_chado/includes/tripal_chado.mapping.inc
  27. 5 4
      tripal_chado/includes/tripal_chado.migrate.inc

+ 2 - 3
tripal/api/tripal.entities.api.inc

@@ -16,7 +16,7 @@
  * @param $ids
  *   An array of entity IDs, or FALSE to load all entities.
  * @param $reset: Whether to reset the internal cache for the requested entity
- *   type. Unlike the entity_load() function this defaults to TRUE.
+ *   type. Defaults to FALSE.
  * @param $field_ids
  *   A list of numeric feild IDs that should be loaded.  The
  *   TripalField named 'content_type' is always automatically added.
@@ -25,7 +25,7 @@
  *   An array of entity objects indexed by their ids. When no results are
  *   found, an empty array is returned.
  */
-function tripal_load_entity($entity_type, $ids = FALSE, $reset = TRUE,
+function tripal_load_entity($entity_type, $ids = FALSE, $reset = FALSE,
     $field_ids = array()) {
 
   // The $conditions is deprecated in the funtion arguments of entity_load
@@ -45,7 +45,6 @@ function tripal_load_entity($entity_type, $ids = FALSE, $reset = TRUE,
   if ($reset) {
     $ec->resetCache();
   }
-
   return $ec->load($ids, $conditions, $field_ids);
 }
 /**

+ 4 - 1
tripal/includes/tripal.entity.inc

@@ -159,7 +159,10 @@ function tripal_entity_info() {
     'access callback' => 'tripal_entity_access',
 
     // FALSE disables caching. Caching functionality is handled by Drupal core.
-    'static cache' => FALSE,
+    'static cache' => TRUE,
+
+    // Caching of fields
+    'field cache' => TRUE,
 
     // Bundles are added dynamically below.
     'bundles' => array (),

+ 1 - 1
tripal/tripal.module

@@ -521,7 +521,7 @@ function TripalBundle_load($bundle_type, $reset = FALSE) {
  *
  * @see tripal_entity_load_multiple()
  */
-function TripalEntity_load($id, $reset = TRUE) {
+function TripalEntity_load($id, $reset = FALSE) {
   // $entity = entity_load('TripalEntity', array($id), array(), $reset);
   $entity = tripal_load_entity('TripalEntity', array($id), $reset);
   return reset($entity);

+ 326 - 1
tripal_chado/api/modules/tripal_chado.feature.api.inc

@@ -915,4 +915,329 @@ function tripal_get_feature_relationships($feature) {
     }
   }
   return $relationships;
-}
+}
+
+/**
+ *
+ * @param unknown $feature_id
+ * @param unknown $featurelocs
+ * @return multitype:|Ambigous <multitype:, an>
+ */
+function chado_get_featureloc_sequences($feature_id, $featurelocs) {
+
+  // if we don't have any featurelocs then no point in continuing
+  if (!$featurelocs) {
+    return array();
+  }
+
+  // get the list of relationships (including any aggregators) and iterate
+  // through each one to find information needed to color-code the reference sequence
+  $relationships = chado_get_feature_aggregate_relationships($feature_id);
+  if (!$relationships) {
+    return array();
+  }
+
+
+  // iterate through each of the realtionships features and get their
+  // locations
+  foreach ($relationships as $rindex => $rel) {
+    // get the featurelocs for each of the relationship features
+    $rel_featurelocs = chado_get_featurelocs($rel->subject_id, 'as_child', 0);
+    foreach ($rel_featurelocs as $rfindex => $rel_featureloc) {
+      // keep track of this unique source feature
+      $src = $rel_featureloc->src_feature_id . "-" . $rel_featureloc->src_cvterm_id;
+
+      // copy over the results to the relationship object.  Since there can
+      // be more than one feature location for each relationship feature we
+      // use the '$src' variable to keep track of these.
+      $rel->featurelocs = new stdClass();
+      $rel->featurelocs->$src = new stdClass();
+      $rel->featurelocs->$src->src_uniquename = $rel_featureloc->src_uniquename;
+      $rel->featurelocs->$src->src_cvterm_id  = $rel_featureloc->src_cvterm_id;
+      $rel->featurelocs->$src->src_cvname     = $rel_featureloc->src_cvname;
+      $rel->featurelocs->$src->fmin           = $rel_featureloc->fmin;
+      $rel->featurelocs->$src->fmax           = $rel_featureloc->fmax;
+      $rel->featurelocs->$src->src_name       = $rel_featureloc->src_name;
+
+      // keep track of the individual parts for each relationship
+      $start = $rel->featurelocs->$src->fmin;
+      $end   = $rel->featurelocs->$src->fmax;
+      $type  = $rel->subject_type;
+      $rel_locs[$src]['parts'][$start][$type]['start'] = $start;
+      $rel_locs[$src]['parts'][$start][$type]['end']   = $end;
+      $rel_locs[$src]['parts'][$start][$type]['type']  = $type;
+    }
+  }
+
+  // the featurelocs array provided to the function contains the locations
+  // where this feature is found.   We want to get the sequence for each
+  // location and then annotate it with the parts found from the relationships
+  // locations determiend above.
+  $floc_sequences = array();
+  foreach ($featurelocs as $featureloc) {
+
+    // build the src name so we can keep track of the different parts for each feature
+    $src = $featureloc->srcfeature_id->feature_id . "-" . $featureloc->srcfeature_id->type_id->cvterm_id;
+
+    // orient the parts to the beginning of the feature sequence
+    if (!empty($rel_locs[$src]['parts'])) {
+      $parts = $rel_locs[$src]['parts'];
+      $rparts = array();  // we will fill this up if we're on the reverse strand
+
+      foreach ($parts as $start => $types) {
+        foreach ($types as $type_name => $type) {
+          if ($featureloc->strand >= 0) {
+            // this is on the forward strand.  We need to convert the start on the src feature to the
+            // start on this feature's sequence
+            $parts[$start][$type_name]['start'] = $parts[$start][$type_name]['start'] - $featureloc->fmin;
+            $parts[$start][$type_name]['end']   = $parts[$start][$type_name]['end'] - $featureloc->fmin;
+            $parts[$start][$type_name]['type']  = $type_name;
+          }
+          else {
+            // this is on the reverse strand.  We need to swap the start and stop and calculate from the
+            // begining of the reverse sequence
+            $size = ($featureloc->fmax - $featureloc->fmin);
+            $start_orig = $parts[$start][$type_name]['start'];
+            $end_orig = $parts[$start][$type_name]['end'];
+            $new_start = $size - ($end_orig - $featureloc->fmin);
+            $new_end = $size - ($start_orig - $featureloc->fmin);
+
+            $rparts[$new_start][$type_name]['start'] = $new_start;
+            $rparts[$new_start][$type_name]['end']   = $new_end;
+            $rparts[$new_start][$type_name]['type']  = $type_name;
+          }
+        }
+      }
+
+      // now sort the parts
+      // if we're on the reverse strand we need to resort
+      if ($featureloc->strand >= 0) {
+        usort($parts, 'chado_feature__residues_sort_rel_parts_by_start');
+      }
+      else {
+        usort($rparts, 'chado_feature__residues_sort_rel_parts_by_start');
+        $parts = $rparts;
+      }
+
+      $floc_sequences[$src]['id'] = $src;
+      $floc_sequences[$src]['type'] = $featureloc->feature_id->type_id->name;
+      $args = array(':feature_id' => $featureloc->srcfeature_id->feature_id);
+      $start = $featureloc->fmin + 1;
+      $size = $featureloc->fmax - $featureloc->fmin;
+
+      // TODO: fix the hard coded $start and $size
+      // the $start and $size variables are hard-coded in the SQL statement
+      // because the db_query function places quotes around all placeholders
+      // (e.g. :start & :size) and screws up the substring function
+      $sql = "
+        SELECT substring(residues from $start for $size) as residues
+        FROM {feature}
+        WHERE feature_id = :feature_id
+      ";
+      $sequence = chado_query($sql, $args)->fetchObject();
+      $residues = $sequence->residues;
+      if ($featureloc->strand < 0) {
+        $residues = tripal_reverse_compliment_sequence($residues);
+      }
+      $strand = '.';
+      if ($featureloc->strand == 1) {
+        $strand = '+';
+      }
+      elseif ($featureloc->strand == -1) {
+        $strand = '-';
+      }
+      $floc_sequences[$src]['location'] = tripal_get_location_string($featureloc);
+      $floc_sequences[$src]['defline'] = tripal_get_fasta_defline($featureloc->feature_id, '', $featureloc, '', strlen($residues));
+      $floc_sequences[$src]['featureloc'] = $featureloc;
+      $floc_sequences[$src]['residues'] = $residues;
+      //$floc_sequences[$src]['formatted_seq'] =  tripal_feature_color_sequence($residues, $parts, $floc_sequences[$src]['defline']);
+    }
+  }
+  return $floc_sequences;
+}
+
+/**
+ * Get features related to the current feature to a given depth. Recursive function.
+ *
+ * @param $feature_id
+ * @param $substitute
+ * @param $levels
+ * @param $base_type_id
+ * @param $depth
+ *
+ */
+function chado_get_aggregate_feature_relationships($feature_id, $substitute=1,
+  $levels=0, $base_type_id=NULL, $depth=0) {
+
+  // we only want to recurse to as many levels deep as indicated by the
+  // $levels variable, but only if this variable is > 0. If 0 then we
+  // recurse until we reach the end of the relationships tree.
+  if ($levels > 0 and $levels == $depth) {
+    return NULL;
+  }
+
+  // first get the relationships for this feature
+  return chado_get_feature_relationships($feature_id, 'as_object');
+
+}
+
+/**
+ * Get the relationships for a feature.
+ *
+ * @param $feature_id
+ *   The feature to get relationships for
+ * @param $side
+ *   The side of the relationship this feature is (ie: 'as_subject' or 'as_object')
+ *
+ */
+function chado_get_feature_relationships($feature_id, $side = 'as_subject') {
+  // get the relationships for this feature.  The query below is used for both
+  // querying the object and subject relationships
+  $sql = "
+    SELECT
+    FS.name as subject_name, FS.uniquename as subject_uniquename,
+      CVTS.name as subject_type, CVTS.cvterm_id as subject_type_id,
+      FR.subject_id, FR.type_id as relationship_type_id, FR.object_id, FR.rank,
+      CVT.name as rel_type,
+      FO.name as object_name, FO.uniquename as object_uniquename,
+      CVTO.name as object_type, CVTO.cvterm_id as object_type_id
+    FROM {feature_relationship} FR
+     INNER JOIN {cvterm} CVT  ON FR.type_id    = CVT.cvterm_id
+     INNER JOIN {feature} FS  ON FS.feature_id = FR.subject_id
+     INNER JOIN {feature} FO  ON FO.feature_id = FR.object_id
+     INNER JOIN {cvterm} CVTO ON FO.type_id    = CVTO.cvterm_id
+     INNER JOIN {cvterm} CVTS ON FS.type_id    = CVTS.cvterm_id
+  ";
+  if (strcmp($side, 'as_object')==0) {
+    $sql .= " WHERE FR.object_id = :feature_id";
+  }
+  if (strcmp($side, 'as_subject')==0) {
+    $sql .= " WHERE FR.subject_id = :feature_id";
+  }
+  $sql .= " ORDER BY FR.rank";
+
+  // get the relationships
+  $results = chado_query($sql, array(':feature_id' => $feature_id));
+
+  // iterate through the relationships, put these in an array and add
+  // in the Drupal node id if one exists
+  $i=0;
+  $esql = "
+    SELECT entity_id
+    FROM {chado_entity}
+    WHERE data_table = 'feature' AND record_id = :feature_id";
+  $relationships = array();
+  while ($rel = $results->fetchObject()) {
+    $entity = db_query($esql, array(':feature_id' => $rel->subject_id))->fetchObject();
+    if ($entity) {
+      $rel->subject_entity_id = $entity->entity_id;
+    }
+    $entity = db_query($esql, array(':feature_id' => $rel->object_id))->fetchObject();
+    if ($entity) {
+      $rel->object_entity_id = $entity->entity_id;
+    }
+    $relationships[$i++] = $rel;
+  }
+  return $relationships;
+}
+
+/**
+ * Load the locations for a given feature
+ *
+ * @param $feature_id
+ *   The feature to look up locations for
+ * @param $side
+ *   Whether the feature is the scrfeature, 'as_parent', or feature, 'as_child'
+ * @param $aggregate
+ *   Whether or not to get the locations for related features
+ *
+ * @ingroup tripal_feature
+ */
+function chado_get_featurelocs($feature_id, $side = 'as_parent', $aggregate = 1) {
+
+  $sql = "
+    SELECT
+       F.name, F.feature_id, F.uniquename,
+       FS.name as src_name, FS.feature_id as src_feature_id, FS.uniquename as src_uniquename,
+       CVT.name as cvname, CVT.cvterm_id,
+       CVTS.name as src_cvname, CVTS.cvterm_id as src_cvterm_id,
+       FL.fmin, FL.fmax, FL.is_fmin_partial, FL.is_fmax_partial,FL.strand, FL.phase
+     FROM {featureloc} FL
+       INNER JOIN {feature} F   ON FL.feature_id = F.feature_id
+       INNER JOIN {feature} FS  ON FS.feature_id = FL.srcfeature_id
+       INNER JOIN {cvterm} CVT  ON F.type_id     = CVT.cvterm_id
+       INNER JOIN {cvterm} CVTS ON FS.type_id    = CVTS.cvterm_id
+       ";
+  if (strcmp($side, 'as_parent')==0) {
+    $sql .= "WHERE FL.srcfeature_id = :feature_id ";
+  }
+  if (strcmp($side, 'as_child')==0) {
+    $sql .= "WHERE FL.feature_id = :feature_id ";
+  }
+
+  $flresults = chado_query($sql, array(':feature_id' => $feature_id));
+
+  // copy the results into an array
+  $i=0;
+  $featurelocs = array();
+  while ($loc = $flresults->fetchObject()) {
+    // if a drupal node exists for this feature then add the nid to the
+    // results object
+
+    $loc->feid = tripal_get_chado_entity_id('feature', $loc->feature_id);
+    $loc->seid = tripal_get_chado_entity_id('feature', $loc->src_feature_id);
+    // add the result to the array
+    $featurelocs[$i++] = $loc;
+  }
+
+  // Add the relationship feature locs if aggregate is turned on
+  if ($aggregate and strcmp($side, 'as_parent')==0) {
+  // get the relationships for this feature without substituting any children
+  // for the parent. We want all relationships
+  $relationships = tripal_feature_get_aggregate_relationships($feature_id, 0);
+  foreach ($relationships as $rindex => $rel) {
+    // get the featurelocs for each of the relationship features
+    $rel_featurelocs = tripal_feature_load_featurelocs($rel->subject_id, 'as_child', 0);
+      foreach ($rel_featurelocs as $findex => $rfloc) {
+        $featurelocs[$i++] = $rfloc;
+      }
+    }
+  }
+
+  usort($featurelocs, 'chado_feature__residues_sort_locations');
+  return $featurelocs;
+}
+
+/**
+  * Used to sort the list of relationship parts by start position
+  *
+  * @ingroup tripal_feature
+  */
+function chado_feature__residues_sort_rel_parts_by_start($a, $b) {
+  foreach ($a as $type_name => $details) {
+    $astart = $a[$type_name]['start'];
+    break;
+  }
+  foreach ($b as $type_name => $details) {
+    $bstart = $b[$type_name]['start'];
+    break;
+  }
+  return strnatcmp($astart, $bstart);
+}
+/**
+ * Used to sort the feature locs by start position
+ *
+ * @param $a
+ *   One featureloc record (as an object)
+ * @param $b
+ *   The other featureloc record (as an object)
+ *
+ * @return
+ *   Which feature location comes first
+ *
+ * @ingroup tripal_feature
+ */
+function chado_feature__residues_sort_locations($a, $b) {
+  return strnatcmp($a->fmin, $b->fmin);
+}
+

+ 1 - 1
tripal_chado/api/tripal_chado.mviews.api.inc

@@ -397,7 +397,7 @@ function tripal_delete_mview($mview_id) {
   db_query($sql);
 
   // does the table already exist?
-  $mview_exists = db_table_exists('chado.' . $mview->mv_table);
+  $mview_exists = chado_table_exists($mview->mv_table);
 
   // drop the table from chado if it exists
   if ($mview_exists) {

+ 2 - 2
tripal_chado/api/tripal_chado.query.api.inc

@@ -1394,9 +1394,9 @@ function chado_select_record($table, $columns, $values, $options = NULL) {
           // Ensure that there were results returned.
           elseif (count($results)==0) {
             tripal_report_error('tripal_chado', TRIPAL_ERROR,
-              'chado_select_record: the foreign key definition for \'%field\' '.
+              'chado_select_record: the foreign key definition for \'%field\' on table \'%table\' '.
               'returned no results where the definition supplied was %value',
-              array('%field' => $field, '%value' => print_r($value, TRUE))
+              array('%field' => $field, '%table' => $table, '%value' => print_r($value, TRUE))
             );
             return array();
           }

+ 39 - 9
tripal_chado/api/tripal_chado.schema.api.inc

@@ -38,22 +38,34 @@
  */
 function chado_table_exists($table) {
   global $databases;
-
   $default_db = $databases['default']['default']['database'];
-
+  $cached_obj = cache_get('chado_tables', 'cache');
+  $cached_tables = $cached_obj->data;
+  if (is_array($cached_tables) and array_key_exists($table, $cached_tables)) {
+    return $cached_tables[$table]['exists'];
+  }
   $sql = "
     SELECT 1
     FROM information_schema.tables
     WHERE
       table_name = :table_name AND
-      table_schema = 'chado' AND
-      table_catalog = '$default_db'
+      table_schema = :chado AND
+      table_catalog = :default_db
   ";
-  $results = db_query($sql, array(':table_name' => $table));
+  $args = array(
+    ':table_name' => $table,
+    ':chado' => tripal_get_schema_name('chado'),
+    ':default_db' => $default_db
+  );
+  $results = db_query($sql, $args);
   $exists = $results->fetchObject();
   if (!$exists) {
+    $cached_tables[$table]['exists'] = FALSE;
+    cache_set('chado_tables', $cached_tables, 'cache', CACHE_TEMPORARY);
     return FALSE;
   }
+  $cached_tables[$table]['exists'] = TRUE;
+  cache_set('chado_tables', $cached_tables, 'cache', CACHE_TEMPORARY);
   return TRUE;
 }
 /**
@@ -77,7 +89,13 @@ function chado_column_exists($table, $column) {
   global $databases;
 
   $default_db = $databases['default']['default']['database'];
-
+  $cached_obj = cache_get('chado_table_columns', 'cache');
+  $cached_cols = $cached_obj->data;
+  if (is_array($cached_cols) and
+      array_key_exists($table, $cached_cols) and
+      array_key_Exists($column, $cached_cols[$table])) {
+    return $cached_cols[$table][$column]['exists'];
+  }
   $sql = "
     SELECT 1
     FROM information_schema.columns
@@ -96,8 +114,12 @@ function chado_column_exists($table, $column) {
   $results = db_query($sql, $args);
   $exists = $results->fetchField();
   if (!$exists) {
+    $cached_cols[$table][$column]['exists'] = FALSE;
+    cache_set('chado_table_columns', $cached_cols, 'cache', CACHE_TEMPORARY);
     return FALSE;
   }
+  $cached_cols[$table][$column]['exists'] = TRUE;
+  cache_set('chado_table_columns', $cached_cols, 'cache', CACHE_TEMPORARY);
   return TRUE;
 }
 
@@ -118,7 +140,11 @@ function chado_sequence_exists($sequence) {
   global $databases;
 
   $default_db = $databases['default']['default']['database'];
-
+  $cached_obj = cache_get('chado_sequences', 'cache');
+  $cached_seqs = $cached_obj->data;
+  if (is_array($cached_seqs) and array_key_exists($sequence, $cached_seqs)) {
+    return $cached_seqs[$sequence]['exists'];
+  }
   $sql = "
     SELECT 1
     FROM information_schema.sequences
@@ -135,8 +161,12 @@ function chado_sequence_exists($sequence) {
   $results = db_query($sql, $args);
   $exists = $results->fetchField();
   if (!$exists) {
+    $cached_seqs[$sequence]['exists'] = FALSE;
+    cache_set('chado_sequences', $cached_seqs, 'cache', CACHE_TEMPORARY);
     return FALSE;
   }
+  $cached_seqs[$sequence]['exists'] = FALSE;
+  cache_set('chado_sequences', $cached_seqs, 'cache', CACHE_TEMPORARY);
   return TRUE;
 }
 /**
@@ -314,12 +344,12 @@ function chado_get_version($exact = FALSE, $warn_if_unsupported = FALSE) {
     }
     $is_local = 0;
     $previous_db = chado_set_active('chado');
-    $prop_exists = db_table_exists('chadoprop');
+    $prop_exists = chado_table_exists('chadoprop');
     chado_set_active($previous_db);
   }
   else {
     $is_local = 1;
-    $prop_exists = db_table_exists('chado.chadoprop');
+    $prop_exists = chado_table_exists('chadoprop');
   }
 
   // if the table doesn't exist then we don't know what version but we know

+ 4 - 1
tripal_chado/api/tripal_chado.variables.api.inc

@@ -278,7 +278,10 @@ function chado_generate_var($table, $values, $base_options = array()) {
   $results = chado_select_record($table, $table_columns, $values, $base_options);
 
   if ($results) {
+
+    // Iterate through each result.
     foreach ($results as $key => $object) {
+
       // Add empty expandable_x arrays
       $object->expandable_fields = $all->expandable_fields;
       $object->expandable_foreign_keys = $all->expandable_foreign_keys;
@@ -290,7 +293,7 @@ function chado_generate_var($table, $values, $base_options = array()) {
       // For Tripal v2 compatibility
       // check if the current table maps to a node type-----------------------------------------------
       // if this table is connected to a node there will be a chado_tablename table in drupal
-      if (db_table_exists('chado_' . $table)) {
+      if (module_exists('tripal_core') and db_table_exists('chado_' . $table)) {
         // that has a foreign key to this one ($table_desc['primary key'][0]
         // and to the node table (nid)
         $sql = "

+ 4 - 4
tripal_chado/includes/TripalFields.old/chado_linker__expression.inc

@@ -22,7 +22,7 @@ class chado_linker__expression extends TripalField {
   // Set this to the name of the storage backend that by default will support
   // this field.
   public static $default_storage = 'field_chado_storage';
-  
+
   /**
    * @see TripalField::formatterView()
    */
@@ -154,7 +154,7 @@ class chado_linker__expression extends TripalField {
 
         // Add the linker_expressionprop
         $linkerprop_table =  $linker_table . 'prop';
-        if (db_table_exists('chado.' . $linkerprop_table)) {
+        if (chado_table_exists($linkerprop_table)) {
           $exp_linker = chado_expand_var($exp_linker, 'table', $linkerprop_table, $options);
           $exp_linkerprops = $exp_linker->feature_expressionprop;
           if ($exp_linkerprops) {
@@ -174,12 +174,12 @@ class chado_linker__expression extends TripalField {
       }
     }
   }
-  
+
   /**
    * We don't want a widget so override this function.
    */
   public static function widgetInfo() {
     return array();
   }
-  
+
 }

+ 10 - 11
tripal_chado/includes/TripalFields/chado_linker__expression.inc

@@ -77,24 +77,23 @@ class chado_linker__expression extends TripalField {
    */
   public function load($entity, $details = array()) {
     $record = $details['record'];
-    
     $field_name = $this->field['field_name'];
     $field_type = $this->field['type'];
     $field_table = $this->field['settings']['chado_table'];
     $field_column = $this->field['settings']['chado_column'];
-    
+
     // Get the FK that links to the base record.
     $schema = chado_get_schema($field_table);
     $base_table = $details['record']->tablename;
     $pkey = $schema['primary key'][0];
     $fkey_lcolumn = key($schema['foreign keys'][$base_table]['columns']);
     $fkey_rcolumn = $schema['foreign keys'][$base_table]['columns'][$fkey_lcolumn];
-    
+
     // Set some defaults for the empty record.
     $entity->{$field_name}['und'][0] = array(
       'value' => array(),
     );
-    
+
     $linker_table = $base_table . '_expression';
     $options = array(
       'return_array' => 1,
@@ -103,11 +102,11 @@ class chado_linker__expression extends TripalField {
     $exp_linkers = $record->$linker_table;
     if ($exp_linkers) {
       foreach ($exp_linkers as $i => $exp_linker) {
-    
+
         // Because the unqiuename is a text field we must expand it.
         $expression = $exp_linker->expression_id;
         $expression = chado_expand_var($expression, 'field', 'expression.uniquename', $options);
-    
+
         // Set the values that will be seen by the user on the page and in
         // web services, or anwhere this field is viewed.
         $entity->{$field_name}['und'][$i]['value'] = array(
@@ -115,22 +114,22 @@ class chado_linker__expression extends TripalField {
           'description' => $expression->description,
           //'md5checksum' => $expression->md5checksum,
         );
-    
+
         // Add the pub information if a real pub is associated with the record.
         $pub = $exp_linker->pub_id;
         if ($pub->uniquename != 'null') {
           $pub_details = tripal_get_minimal_pub_info($pub);
-    
+
           $entity->{$field_name}['und'][$i]['value']['publication'] = $pub_details;
           $entity->{$field_name}['und'][$i]['value']['publication']['type'] = $pub->type_id->name;
           if (property_exists($pub, 'entity_id')) {
             $entity->{$field_name}['und'][$i]['publication'][0]['value']['entity'] = 'TripalEntity:' . $pub->entity_id;
           }
         }
-    
+
         // Add the linker_expressionprop
         $linkerprop_table =  $linker_table . 'prop';
-        if (db_table_exists('chado.' . $linkerprop_table)) {
+        if (chado_table_exists($linkerprop_table)) {
           $exp_linker = chado_expand_var($exp_linker, 'table', $linkerprop_table, $options);
           $exp_linkerprops = $exp_linker->feature_expressionprop;
           if ($exp_linkerprops) {
@@ -146,7 +145,7 @@ class chado_linker__expression extends TripalField {
         $entity->{$field_name}['und'][$i][$linker_table . '__uniquename'] = $expression->uniquename;
         //$entity->{$field_name}['und'][$i][$linker_table . '__md5checksum'] = $expression->md5checksum;
         $entity->{$field_name}['und'][$i][$linker_table . '__description'] = $expression->description;
-    
+
       }
     }
   }

+ 16 - 16
tripal_chado/includes/TripalFields/chado_linker__synonym_widget.inc

@@ -15,7 +15,7 @@ class chado_linker__synonym_widget extends TripalFieldWidget {
     parent::form($widget, $form, $form_state, $langcode, $items, $delta, $element);
     $entity = $form['#entity'];
     $field_name = $this->field['field_name'];
-    
+
     // Get the FK column that links to the base table.
     $table_name = $this->field['settings']['chado_table'];
     $base_table = $this->field['settings']['base_table'];
@@ -23,7 +23,7 @@ class chado_linker__synonym_widget extends TripalFieldWidget {
     $pkey = $schema['primary key'][0];
     $fkeys = array_values($schema['foreign keys'][$base_table]['columns']);
     $fkey = $fkeys[0];
-    
+
     // Get the field defaults.
     $record_id = '';
     $fkey_value = $element['#entity']->chado_record_id;
@@ -33,7 +33,7 @@ class chado_linker__synonym_widget extends TripalFieldWidget {
     $is_internal = FALSE;
     $syn_name = '';
     $syn_type = '';
-    
+
     // If the field already has a value then it will come through the $items
     // array.  This happens when editing an existing record.
     if (array_key_exists($delta, $items)) {
@@ -45,7 +45,7 @@ class chado_linker__synonym_widget extends TripalFieldWidget {
       $syn_name = $items[$delta]['name'];
       $syn_type = $items[$delta]['type_id'];
     }
-    
+
     // Check $form_state['values'] to see if an AJAX call set the values.
     if (array_key_exists('values', $form_state) and array_key_exists($delta, $form_state['values'])) {
       $record_id = $form_state['values'][$field_name]['und'][$delta]['chado-' . $table_name . '__' . $pkey];
@@ -56,7 +56,7 @@ class chado_linker__synonym_widget extends TripalFieldWidget {
       $syn_name = $form_state['values'][$field_name]['und'][$delta]['name'];
       $syn_type = $form_state['values'][$field_name]['und'][$delta]['type_id'];
     }
-    
+
     $options = array();
     $value = array('cv_id' => array('name' => 'synonym_type'));
     $op = array('return_array' => 1);
@@ -66,22 +66,22 @@ class chado_linker__synonym_widget extends TripalFieldWidget {
         $options[$type->cvterm_id] = $type->name;
       }
     }
-    
+
     // Get the schema for the synonym table so we can make sure we limit the
     // size of the name field to the proper size.
     $schema = chado_get_schema('synonym');
-    
+
     $widget['#table_name'] = $table_name;
     $widget['#fkey_field'] = $fkey;
     $widget['#theme'] = 'chado_linker__synonym_widget';
     $widget['#prefix'] =  "<span id='$table_name-$delta'>";
     $widget['#suffix'] =  "</span>";
-    
+
     $widget['value'] = array(
       '#type' => 'value',
       '#value' => array_key_exists($delta, $items) ? $items[$delta]['value'] : '',
     );
-    
+
     $widget['chado-' . $table_name . '__' . $pkey] = array(
       '#type' => 'value',
       '#default_value' => $record_id,
@@ -111,14 +111,14 @@ class chado_linker__synonym_widget extends TripalFieldWidget {
       '#default_value' => $syn_name,
       '#size' => 25,
     );
-    
+
     $widget['chado-' . $table_name . '__is_current'] = array(
       '#type' => 'checkbox',
       '#title' => t('Is Current'),
       '#default_value' => $is_current,
       '#required' => $element['#required'],
     );
-    
+
     $widget['chado-' . $table_name . '__is_internal'] = array(
       '#type' => 'checkbox',
       '#title' => t('Is Internal'),
@@ -157,7 +157,7 @@ class chado_linker__synonym_widget extends TripalFieldWidget {
     $pkey = $schema['primary key'][0];
     $fkeys = array_values($schema['foreign keys'][$base_table]['columns']);
     $fkey = $fkeys[0];
-    
+
     $record_id = isset($form_state['values'][$field_name][$langcode][$delta]['chado-' . $table_name . '__' . $pkey]) ? $form_state['values'][$field_name][$langcode][$delta]['chado-' . $table_name . '__' . $pkey] : '';
     $fkey_value = isset($form_state['values'][$field_name][$langcode][$delta]['chado-' . $table_name . '__' . $fkey]) ? $form_state['values'][$field_name][$langcode][$delta]['chado-' . $table_name . '__' . $fkey] : '';
     $synonym_id = isset($form_state['values'][$field_name][$langcode][$delta]['chado-' . $table_name . '__synonym_id']) ? $form_state['values'][$field_name][$langcode][$delta]['chado-' . $table_name . '__synonym_id'] : '';
@@ -166,11 +166,11 @@ class chado_linker__synonym_widget extends TripalFieldWidget {
     $is_internal = isset($form_state['values'][$field_name][$langcode][$delta]['chado-' . $table_name . '__is_internal']) ? $form_state['values'][$field_name][$langcode][$delta]['chado-' . $table_name . '__is_internal'] : '';
     $syn_name = isset($form_state['values'][$field_name][$langcode][$delta]['name']) ? $form_state['values'][$field_name][$langcode][$delta]['name'] : '';
     $syn_type = isset($form_state['values'][$field_name][$langcode][$delta]['type_id']) ? $form_state['values'][$field_name][$langcode][$delta]['type_id'] : '';
-    
+
     // If the user provided a $syn_name and a $syn_type then we want to set
     // the foreign key value to be the chado_record_id.
     if ($syn_name and $syn_type) {
-    
+
       // Get the synonym. If one with the same name and type is already present
       // then use that. Otherwise, insert a new one.
       if (!$synonym_id) {
@@ -183,11 +183,11 @@ class chado_linker__synonym_widget extends TripalFieldWidget {
           ));
           $synonym = (object) $synonym;
         }
-    
+
         // Set the synonym_id and FK value
         $form_state['values'][$field_name][$langcode][$delta]['chado-' . $table_name . '__synonym_id'] = $synonym->synonym_id;
       }
-    
+
       if (!$pub_id) {
         $pub = chado_generate_var('pub', array('uniquename' => 'null'));
         $form_state['values'][$field_name][$langcode][$delta]['chado-' . $table_name . '__pub_id'] = $pub->pub_id;

+ 81 - 0
tripal_chado/includes/TripalFields/data__protein_sequence.inc

@@ -0,0 +1,81 @@
+<?php
+
+class data__protein_sequence extends TripalField {
+
+
+  // --------------------------------------------------------------------------
+  //                     EDITABLE STATIC CONSTANTS
+  //
+  // The following constants SHOULD be set for each descendent class.  They are
+  // used by the static functions to provide information to Drupal about
+  // the field and it's default widget and formatter.
+  // --------------------------------------------------------------------------
+
+  // The term that this field maps to.  The format for the term should be:
+  // [vocab]:[accession] where [vocab] is the short name of the vocabulary
+  // and [acession] is the unique accession number for the term.  This term
+  // must already exist in the vocabulary storage backend. This
+  // value should never be changed once fields exist for this type.
+  public static $term = 'data:2976';
+
+  // The default lable for this field.
+  public static $label = 'Protein Sequence';
+
+  // The default description for this field.
+  public static $description = 'polypeptide sequences.';
+
+  // Provide a list of global settings. These can be accessed witihn the
+  // globalSettingsForm.  When the globalSettingsForm is submitted then
+  // Drupal will automatically change these settings for all fields.
+  public static $settings = array(
+    'chado_table' => '',
+    'chado_column' => '',
+    'base_table' => '',
+  );
+
+  // Provide a list of instance specific settings. These can be access within
+  // the instanceSettingsForm.  When the instanceSettingsForm is submitted
+  // then Drupal with automatically change these settings for the instnace.
+  // It is recommended to put settings at the instance level whenever possible.
+  public static $instance_settings  = array();
+
+  // Set this to the name of the storage backend that by default will support
+  // this field.
+  public static $storage = 'tripal_no_storage';
+
+  // The default widget for this field.
+  public static $default_widget = 'data__protein_sequence_widget';
+
+  // The default formatter for this field.
+  public static $default_formatter = 'data__protein_sequence_formatter';
+
+
+  /**
+   * @see TripalField::load()
+   */
+  public function load($entity, $details = array()) {
+    $field_name = $this->field['field_name'];
+    $feature = $details['record'];
+    $num_seqs = 0;
+
+    // Look for Protein sequences
+    $sql = "
+      SELECT F.*
+      FROM {feature_relationship} FR
+        INNER JOIN {feature} F on FR.subject_id = F.feature_id
+        INNER JOIN {cvterm} CVT on CVT.cvterm_id = F.type_id
+        INNER JOIN {cvterm} RCVT on RCVT.cvterm_id = FR.type_id
+      WHERE
+        FR.object_id = :feature_id and
+        CVT.name = 'polypeptide' and
+        RCVT.name = 'derives_from'
+      ORDER BY FR.rank ASC
+    ";
+    $results = chado_query($sql, array(':feature_id' => $feature->feature_id));
+    while ($protein = $results->fetchObject()) {
+      if ($protein->residues) {
+        $entity->{$field_name}['und'][$num_seqs++]['value'] = $protein->residues;
+      }
+    }
+  }
+}

+ 50 - 0
tripal_chado/includes/TripalFields/data__protein_sequence_formatter.inc

@@ -0,0 +1,50 @@
+<?php
+
+class data__protein_sequence_formatter extends TripalFieldFormatter {
+  // The default lable for this field.
+  public static $label = 'Protein Sequence';
+
+  // The list of field types for which this formatter is appropriate.
+  public static $field_types = array('data__protein_sequence');
+
+  // The list of default settings for this formatter.
+  public static $settings = array();
+
+  /**
+   *
+   * @param unknown $element
+   * @param unknown $entity_type
+   * @param unknown $entity
+   * @param unknown $langcode
+   * @param unknown $items
+   * @param unknown $display
+   */
+  public function view(&$element, $entity_type, $entity, $langcode, $items, $display) {
+    $element[0] = array(
+      // We create a render array to produce the desired markup,
+      '#type' => 'markup',
+      '#markup' => '',
+    );
+
+    $num_bases = 50;
+    foreach ($items as $delta => $item) {
+      // If there are no residues then skip this one.
+      if (!is_array($item['value']) or !array_key_exists('residues', $item['value'])) {
+        continue;
+      }
+
+      $residues = $item['value']['residues'];
+
+      $content .= '<pre class="residues-formatter">';
+      $content .= '>' . $defline . "<br>";
+      $content .= wordwrap($residues, $num_bases, "<br>", TRUE);
+      $content .= '</pre>';
+
+      $element[$delta] = array(
+        // We create a render array to produce the desired markup,
+        '#type' => 'markup',
+        '#markup' => $content,
+      );
+    }
+  }
+}

+ 43 - 0
tripal_chado/includes/TripalFields/data__protein_sequence_widget.inc

@@ -0,0 +1,43 @@
+<?php
+
+class data__protein_sequence_widget extends TripalFieldWidget {
+  // The default lable for this field.
+  public static $label = 'Protein Sequence';
+
+  // The list of field types for which this formatter is appropriate.
+  public static $field_types = array('data__protein_sequence');
+
+  /**
+   *
+   * @see TripalFieldWidget::form()
+   */
+  public function form(&$widget, &$form, &$form_state, $langcode, $items, $delta, $element) {
+    parent::form($widget, $form, $form_state, $langcode, $items, $delta, $element);
+
+    // TODO: add the form for setting a protein sequence.
+  }
+
+  /**
+   * Performs validation of the widgetForm.
+   *
+   * Use this validate to ensure that form values are entered correctly.  Note
+   * this is different from the validate() function which ensures that the
+   * field data meets expectations.
+   *
+   * @param $form
+   * @param $form_state
+   */
+  public function validate($form, &$form_state, $entity_type, $entity, $langcode, $delta) {
+
+  }
+
+
+  /**
+   *
+   * @see TripalFieldWidget::submit()
+   */
+  public function submit($form, &$form_state, $entity_type, $entity, $langcode, $delta) {
+    $field_name = $this->field['field_name'];
+
+  }
+}

+ 90 - 477
tripal_chado/includes/TripalFields/data__sequence.inc

@@ -49,103 +49,42 @@ class data__sequence extends TripalField {
   // The default formatter for this field.
   public static $default_formatter = 'data__sequence_formatter';
 
-  // --------------------------------------------------------------------------
-  //              PROTECTED CLASS MEMBERS -- DO NOT OVERRIDE
-  // --------------------------------------------------------------------------
-  // An array containing details about the field. The format of this array
-  // is the same as that returned by field_info_fields()
-  protected $field;
-  // An array containing details about an instance of the field. A field does
-  // not have to have an instance.  But if dealing with an instance (such as
-  // when using the widgetForm, formatterSettingsForm, etc.) it should be set.
-  protected $instance;
-
+  /**
+   * @see TripalField::load()
+   */
   public function load($entity, $details = array()) {
     $field_name = $this->field['field_name'];
     $feature = $details['record'];
-    $num_seqs = 0;
-    
+
     // 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.
-    if(strcmp($feature->type_id->name,'scaffold') == 0 or
-        strcmp($feature->type_id->name,'chromosome') == 0 or
-        strcmp($feature->type_id->name,'supercontig') == 0 or
-        strcmp($feature->type_id->name,'pseudomolecule') == 0) {
-          $entity->{$field_name}['und'][$num_seqs]['value'] = array(
-            '@type' => 'SO:0000110',
-            'type' => 'sequence_feature',
-            'label' => 'Residues',
-            'defline' => ">This sequence is too large for this display.",
-            'residues' => '',
-          );
-          $entity->{$field_name}['und'][$num_seqs]['chado-feature__residues'] = '';
-        }
-        else {
-          $feature = chado_expand_var($feature,'field','feature.residues');
-          if ($feature->residues) {
-            $entity->{$field_name}['und'][$num_seqs]['value'] = array(
-              '@type' => 'SO:0000110',
-              'type' => 'sequence_feature',
-              'label' => 'Raw Sequence',
-              'defline' => tripal_get_fasta_defline($feature, '', NULL, '', strlen($feature->residues)),
-              'residues' => $feature->residues,
-            );
-            $entity->{$field_name}['und'][$num_seqs]['chado-feature__residues'] = $feature->residues;
-          }
-          else {
-            $entity->{$field_name}['und'][$num_seqs]['value'] = array();
-            $entity->{$field_name}['und'][$num_seqs]['chado-feature__residues'] = '';
-          }
-        }
-        $num_seqs++;
-    
-        // Add in the protein sequences. It's faster to provide the SQL rather than
-        // to use chado_generate_var based on the type.
-        $sql = "
-      SELECT F.*
-      FROM {feature_relationship} FR
-        INNER JOIN {feature} F on FR.subject_id = F.feature_id
-        INNER JOIN {cvterm} CVT on CVT.cvterm_id = F.type_id
-        INNER JOIN {cvterm} RCVT on RCVT.cvterm_id = FR.type_id
-      WHERE
-        FR.object_id = :feature_id and
-        CVT.name = 'polypeptide' and
-        RCVT.name = 'derives_from'
-      ORDER BY FR.rank ASC
-    ";
-        $results = chado_query($sql, array(':feature_id' => $feature->feature_id));
-        while ($protein = $results->fetchObject()) {
-          if ($protein->residues) {
-            $entity->{$field_name}['und'][$num_seqs++]['value'] = array(
-              '@type' => 'SO:0000104',
-              'type' => 'polypeptide',
-              'label' => 'Protein Sequence',
-              'defline' => tripal_get_fasta_defline($protein, '', NULL, '', strlen($protein->residues)),
-              'residues' => $protein->residues,
-            );
-          }
-        }
-    
-    
-        // Add in sequences from alignments.
-        $options = array(
-          'return_array' => 1,
-          'include_fk' => array(
-            'srcfeature_id' => array(
-              'type_id' => 1
-            ),
-            'feature_id' => array(
-              'type_id' => 1
-            ),
-          ),
-        );
-        $feature = chado_expand_var($feature, 'table', 'featureloc', $options);
-        $featureloc_sequences = $this->get_featureloc_sequences($feature->feature_id, $feature->featureloc->feature_id);
-    
-        // Add in the coding sequences. It's faster to provide the SQL rather than
-        // to use chado_generate_var based on the type.
-        $sql = "
+    if(strcmp($feature->type_id->name,'scaffold') != 0 and
+       strcmp($feature->type_id->name,'chromosome') != 0 and
+       strcmp($feature->type_id->name,'supercontig') != 0 and
+       strcmp($feature->type_id->name,'pseudomolecule') != 0) {
+      $feature = chado_expand_var($feature,'field','feature.residues');
+      $entity->{$field_name}['und'][0]['value'] = $feature->residues;
+    }
+
+ /*    // Add in sequences from alignments.
+    $options = array(
+      'return_array' => 1,
+      'include_fk' => array(
+        'srcfeature_id' => array(
+          'type_id' => 1
+        ),
+        'feature_id' => array(
+          'type_id' => 1
+        ),
+      ),
+    );
+    $feature = chado_expand_var($feature, 'table', 'featureloc', $options);
+    $featureloc_sequences = $this->get_featureloc_sequences($feature->feature_id, $feature->featureloc->feature_id);
+
+    // Add in the coding sequences. It's faster to provide the SQL rather than
+    // to use chado_generate_var based on the type.
+    $sql = "
       SELECT F.*
       FROM {feature_relationship} FR
         INNER JOIN {feature} F on FR.subject_id = F.feature_id
@@ -158,398 +97,72 @@ class data__sequence extends TripalField {
         RCVT.name = 'part_of'
       ORDER BY FR.rank ASC
     ";
-        $results = chado_query($sql, array(':feature_id' => $feature->feature_id));
-        $coding_seq = '';
-        while ($CDS = $results->fetchObject()) {
-          if ($CDS->residues) {
-            $coding_seq .= $CDS->residues;
-          }
-        }
-        if ($coding_seq) {
+    $results = chado_query($sql, array(':feature_id' => $feature->feature_id));
+    $coding_seq = '';
+    while ($CDS = $results->fetchObject()) {
+      if ($CDS->residues) {
+        $coding_seq .= $CDS->residues;
+      }
+    }
+    if ($coding_seq) {
+      $entity->{$field_name}['und'][$num_seqs++]['value'] = array(
+        '@type' => 'SO:0000316',
+        'type' => 'coding_sequence',
+        'label' => 'Coding sequence (CDS)',
+        'defline' => tripal_get_fasta_defline($feature, 'CDS', NULL, '', strlen($coding_seq)),
+        'residues' => $coding_seq,
+      );
+    }
+
+    foreach($featureloc_sequences as $src => $attrs){
+      // the $attrs array has the following keys
+      //   * id:  a unique identifier combining the feature id with the cvterm id
+      //   * type: the type of sequence (e.g. mRNA, etc)
+      //   * location:  the alignment location
+      //   * defline: the definition line
+      //   * formatted_seq: the formatted sequences
+      //   * featureloc:  the feature object aligned to
+      $entity->{$field_name}['und'][$num_seqs++]['value'] = array(
+        'residues' => $attrs['residues'],
+        '@type' => 'SO:0000110',
+        'type' => 'sequence_feature',
+        'defline' => tripal_get_fasta_defline($feature, '', $attrs['featureloc'], 'CDS', strlen($attrs['residues'])),
+        'label' => 'Sequence from alignment at ' . $attrs['location'],
+      );
+
+
+      // check to see if this alignment has any CDS. If so, generate a CDS sequence
+      $cds_sequence = tripal_get_feature_sequences(
+          array(
+            'feature_id' => $feature->feature_id,
+            'parent_id' => $attrs['featureloc']->srcfeature_id->feature_id,
+            'name' => $feature->name,
+            'featureloc_id' => $attrs['featureloc']->featureloc_id,
+          ),
+          array(
+            '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
+            'sub_feature_types' => array('CDS'), // we're looking for CDS features
+            'is_html' => 0
+          )
+          );
+
+      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']) {
           $entity->{$field_name}['und'][$num_seqs++]['value'] = array(
+            'residues' => $cds_sequence[0]['residues'],
             '@type' => 'SO:0000316',
             'type' => 'coding_sequence',
-            'label' => 'Coding sequence (CDS)',
-            'defline' => tripal_get_fasta_defline($feature, 'CDS', NULL, '', strlen($coding_seq)),
-            'residues' => $coding_seq,
+            'defline' => tripal_get_fasta_defline($feature, '', $attrs['featureloc'], 'CDS', $cds_sequence[0]['length']),
+            'label' => 'Coding sequence (CDS) from alignment at  ' . $attrs['location'],
           );
         }
-    
-        foreach($featureloc_sequences as $src => $attrs){
-          // the $attrs array has the following keys
-          //   * id:  a unique identifier combining the feature id with the cvterm id
-          //   * type: the type of sequence (e.g. mRNA, etc)
-          //   * location:  the alignment location
-          //   * defline: the definition line
-          //   * formatted_seq: the formatted sequences
-          //   * featureloc:  the feature object aligned to
-          $entity->{$field_name}['und'][$num_seqs++]['value'] = array(
-            'residues' => $attrs['residues'],
-            '@type' => 'SO:0000110',
-            'type' => 'sequence_feature',
-            'defline' => tripal_get_fasta_defline($feature, '', $attrs['featureloc'], 'CDS', strlen($attrs['residues'])),
-            'label' => 'Sequence from alignment at ' . $attrs['location'],
-          );
-    
-    
-          // check to see if this alignment has any CDS. If so, generate a CDS sequence
-          $cds_sequence = tripal_get_feature_sequences(
-              array(
-                'feature_id' => $feature->feature_id,
-                'parent_id' => $attrs['featureloc']->srcfeature_id->feature_id,
-                'name' => $feature->name,
-                'featureloc_id' => $attrs['featureloc']->featureloc_id,
-              ),
-              array(
-                '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
-                'sub_feature_types' => array('CDS'), // we're looking for CDS features
-                'is_html' => 0
-              )
-              );
-    
-          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']) {
-              $entity->{$field_name}['und'][$num_seqs++]['value'] = array(
-                'residues' => $cds_sequence[0]['residues'],
-                '@type' => 'SO:0000316',
-                'type' => 'coding_sequence',
-                'defline' => tripal_get_fasta_defline($feature, '', $attrs['featureloc'], 'CDS', $cds_sequence[0]['length']),
-                'label' => 'Coding sequence (CDS) from alignment at  ' . $attrs['location'],
-              );
-            }
-          }
-        }
-  }
-  
-  /**
-   *
-   * @param unknown $feature_id
-   * @param unknown $featurelocs
-   * @return multitype:|Ambigous <multitype:, an>
-   */
-  private function get_featureloc_sequences($feature_id, $featurelocs) {
-  
-    // if we don't have any featurelocs then no point in continuing
-    if (!$featurelocs) {
-      return array();
-    }
-  
-    // get the list of relationships (including any aggregators) and iterate
-    // through each one to find information needed to color-code the reference sequence
-    $relationships = $this->get_aggregate_relationships($feature_id);
-    if (!$relationships) {
-      return array();
-    }
-  
-  
-    // iterate through each of the realtionships features and get their
-    // locations
-    foreach ($relationships as $rindex => $rel) {
-      // get the featurelocs for each of the relationship features
-      $rel_featurelocs = $this->get_featurelocs($rel->subject_id, 'as_child', 0);
-      foreach ($rel_featurelocs as $rfindex => $rel_featureloc) {
-        // keep track of this unique source feature
-        $src = $rel_featureloc->src_feature_id . "-" . $rel_featureloc->src_cvterm_id;
-  
-        // copy over the results to the relationship object.  Since there can
-        // be more than one feature location for each relationship feature we
-        // use the '$src' variable to keep track of these.
-        $rel->featurelocs = new stdClass();
-        $rel->featurelocs->$src = new stdClass();
-        $rel->featurelocs->$src->src_uniquename = $rel_featureloc->src_uniquename;
-        $rel->featurelocs->$src->src_cvterm_id  = $rel_featureloc->src_cvterm_id;
-        $rel->featurelocs->$src->src_cvname     = $rel_featureloc->src_cvname;
-        $rel->featurelocs->$src->fmin           = $rel_featureloc->fmin;
-        $rel->featurelocs->$src->fmax           = $rel_featureloc->fmax;
-        $rel->featurelocs->$src->src_name       = $rel_featureloc->src_name;
-  
-        // keep track of the individual parts for each relationship
-        $start = $rel->featurelocs->$src->fmin;
-        $end   = $rel->featurelocs->$src->fmax;
-        $type  = $rel->subject_type;
-        $rel_locs[$src]['parts'][$start][$type]['start'] = $start;
-        $rel_locs[$src]['parts'][$start][$type]['end']   = $end;
-        $rel_locs[$src]['parts'][$start][$type]['type']  = $type;
-      }
-    }
-  
-    // the featurelocs array provided to the function contains the locations
-    // where this feature is found.   We want to get the sequence for each
-    // location and then annotate it with the parts found from the relationships
-    // locations determiend above.
-    $floc_sequences = array();
-    foreach ($featurelocs as $featureloc) {
-  
-      // build the src name so we can keep track of the different parts for each feature
-      $src = $featureloc->srcfeature_id->feature_id . "-" . $featureloc->srcfeature_id->type_id->cvterm_id;
-  
-      // orient the parts to the beginning of the feature sequence
-      if (!empty($rel_locs[$src]['parts'])) {
-        $parts = $rel_locs[$src]['parts'];
-        $rparts = array();  // we will fill this up if we're on the reverse strand
-  
-        foreach ($parts as $start => $types) {
-          foreach ($types as $type_name => $type) {
-            if ($featureloc->strand >= 0) {
-              // this is on the forward strand.  We need to convert the start on the src feature to the
-              // start on this feature's sequence
-              $parts[$start][$type_name]['start'] = $parts[$start][$type_name]['start'] - $featureloc->fmin;
-              $parts[$start][$type_name]['end']   = $parts[$start][$type_name]['end'] - $featureloc->fmin;
-              $parts[$start][$type_name]['type']  = $type_name;
-            }
-            else {
-              // this is on the reverse strand.  We need to swap the start and stop and calculate from the
-              // begining of the reverse sequence
-              $size = ($featureloc->fmax - $featureloc->fmin);
-              $start_orig = $parts[$start][$type_name]['start'];
-              $end_orig = $parts[$start][$type_name]['end'];
-              $new_start = $size - ($end_orig - $featureloc->fmin);
-              $new_end = $size - ($start_orig - $featureloc->fmin);
-  
-              $rparts[$new_start][$type_name]['start'] = $new_start;
-              $rparts[$new_start][$type_name]['end']   = $new_end;
-              $rparts[$new_start][$type_name]['type']  = $type_name;
-            }
-          }
-        }
-  
-        // now sort the parts
-        // if we're on the reverse strand we need to resort
-        if ($featureloc->strand >= 0) {
-          usort($parts, 'chado_feature__residues_sort_rel_parts_by_start');
-        }
-        else {
-          usort($rparts, 'chado_feature__residues_sort_rel_parts_by_start');
-          $parts = $rparts;
-        }
-  
-        $floc_sequences[$src]['id'] = $src;
-        $floc_sequences[$src]['type'] = $featureloc->feature_id->type_id->name;
-        $args = array(':feature_id' => $featureloc->srcfeature_id->feature_id);
-        $start = $featureloc->fmin + 1;
-        $size = $featureloc->fmax - $featureloc->fmin;
-  
-        // TODO: fix the hard coded $start and $size
-        // the $start and $size variables are hard-coded in the SQL statement
-        // because the db_query function places quotes around all placeholders
-        // (e.g. :start & :size) and screws up the substring function
-        $sql = "
-        SELECT substring(residues from $start for $size) as residues
-        FROM {feature}
-        WHERE feature_id = :feature_id
-        ";
-        $sequence = chado_query($sql, $args)->fetchObject();
-        $residues = $sequence->residues;
-        if ($featureloc->strand < 0) {
-          $residues = tripal_reverse_compliment_sequence($residues);
-        }
-        $strand = '.';
-        if ($featureloc->strand == 1) {
-          $strand = '+';
-        }
-        elseif ($featureloc->strand == -1) {
-          $strand = '-';
-        }
-        $floc_sequences[$src]['location'] = tripal_get_location_string($featureloc);
-        $floc_sequences[$src]['defline'] = tripal_get_fasta_defline($featureloc->feature_id, '', $featureloc, '', strlen($residues));
-        $floc_sequences[$src]['featureloc'] = $featureloc;
-        $floc_sequences[$src]['residues'] = $residues;
-        //$floc_sequences[$src]['formatted_seq'] =  tripal_feature_color_sequence($residues, $parts, $floc_sequences[$src]['defline']);
-      }
-    }
-    return $floc_sequences;
-  }
-  
-  /**
-   * Get features related to the current feature to a given depth. Recursive function.
-   *
-   * @param $feature_id
-   * @param $substitute
-   * @param $levels
-   * @param $base_type_id
-   * @param $depth
-   *
-   * @ingroup tripal_feature
-   */
-  private function get_aggregate_relationships($feature_id, $substitute=1,
-      $levels=0, $base_type_id=NULL, $depth=0) {
-  
-        // we only want to recurse to as many levels deep as indicated by the
-        // $levels variable, but only if this variable is > 0. If 0 then we
-        // recurse until we reach the end of the relationships tree.
-        if ($levels > 0 and $levels == $depth) {
-          return NULL;
-        }
-  
-        // first get the relationships for this feature
-        return $this->get_relationships($feature_id, 'as_object');
-  
-  }
-  
-  /**
-   * Get the relationships for a feature.
-   *
-   * @param $feature_id
-   *   The feature to get relationships for
-   * @param $side
-   *   The side of the relationship this feature is (ie: 'as_subject' or 'as_object')
-   *
-   * @ingroup tripal_feature
-   */
-  private function get_relationships($feature_id, $side = 'as_subject') {
-    // get the relationships for this feature.  The query below is used for both
-    // querying the object and subject relationships
-    $sql = "
-    SELECT
-      FS.name as subject_name, FS.uniquename as subject_uniquename,
-      CVTS.name as subject_type, CVTS.cvterm_id as subject_type_id,
-      FR.subject_id, FR.type_id as relationship_type_id, FR.object_id, FR.rank,
-      CVT.name as rel_type,
-      FO.name as object_name, FO.uniquename as object_uniquename,
-      CVTO.name as object_type, CVTO.cvterm_id as object_type_id
-    FROM {feature_relationship} FR
-     INNER JOIN {cvterm} CVT  ON FR.type_id    = CVT.cvterm_id
-     INNER JOIN {feature} FS  ON FS.feature_id = FR.subject_id
-     INNER JOIN {feature} FO  ON FO.feature_id = FR.object_id
-     INNER JOIN {cvterm} CVTO ON FO.type_id    = CVTO.cvterm_id
-     INNER JOIN {cvterm} CVTS ON FS.type_id    = CVTS.cvterm_id
-  ";
-    if (strcmp($side, 'as_object')==0) {
-      $sql .= " WHERE FR.object_id = :feature_id";
-    }
-    if (strcmp($side, 'as_subject')==0) {
-      $sql .= " WHERE FR.subject_id = :feature_id";
-    }
-    $sql .= " ORDER BY FR.rank";
-  
-    // get the relationships
-    $results = chado_query($sql, array(':feature_id' => $feature_id));
-  
-  
-    // iterate through the relationships, put these in an array and add
-    // in the Drupal node id if one exists
-    $i=0;
-    $esql = "
-        SELECT entity_id
-        FROM {chado_entity}
-        WHERE data_table = 'feature' AND record_id = :feature_id";
-    $relationships = array();
-    while ($rel = $results->fetchObject()) {
-      $entity = db_query($esql, array(':feature_id' => $rel->subject_id))->fetchObject();
-      if ($entity) {
-        $rel->subject_entity_id = $entity->entity_id;
-      }
-      $entity = db_query($esql, array(':feature_id' => $rel->object_id))->fetchObject();
-      if ($entity) {
-        $rel->object_entity_id = $entity->entity_id;
       }
-      $relationships[$i++] = $rel;
-    }
-    return $relationships;
-  }
-  
-  /**
-   * Load the locations for a given feature
-   *
-   * @param $feature_id
-   *   The feature to look up locations for
-   * @param $side
-   *   Whether the feature is the scrfeature, 'as_parent', or feature, 'as_child'
-   * @param $aggregate
-   *   Whether or not to get the locations for related features
-   *
-   * @ingroup tripal_feature
-   */
-  private function get_featurelocs($feature_id, $side = 'as_parent', $aggregate = 1) {
-  
-    $sql = "
-    SELECT
-       F.name, F.feature_id, F.uniquename,
-       FS.name as src_name, FS.feature_id as src_feature_id, FS.uniquename as src_uniquename,
-       CVT.name as cvname, CVT.cvterm_id,
-       CVTS.name as src_cvname, CVTS.cvterm_id as src_cvterm_id,
-       FL.fmin, FL.fmax, FL.is_fmin_partial, FL.is_fmax_partial,FL.strand, FL.phase
-     FROM {featureloc} FL
-       INNER JOIN {feature} F   ON FL.feature_id = F.feature_id
-       INNER JOIN {feature} FS  ON FS.feature_id = FL.srcfeature_id
-       INNER JOIN {cvterm} CVT  ON F.type_id     = CVT.cvterm_id
-       INNER JOIN {cvterm} CVTS ON FS.type_id    = CVTS.cvterm_id
-   ";
-    if (strcmp($side, 'as_parent')==0) {
-      $sql .= "WHERE FL.srcfeature_id = :feature_id ";
-    }
-    if (strcmp($side, 'as_child')==0) {
-      $sql .= "WHERE FL.feature_id = :feature_id ";
-    }
-  
-    $flresults = chado_query($sql, array(':feature_id' => $feature_id));
-  
-    // copy the results into an array
-    $i=0;
-    $featurelocs = array();
-    while ($loc = $flresults->fetchObject()) {
-      // if a drupal node exists for this feature then add the nid to the
-      // results object
-  
-      $loc->feid = tripal_get_chado_entity_id('feature', $loc->feature_id);
-      $loc->seid = tripal_get_chado_entity_id('feature', $loc->src_feature_id);
-      // add the result to the array
-      $featurelocs[$i++] = $loc;
-    }
-  
-    // Add the relationship feature locs if aggregate is turned on
-    if ($aggregate and strcmp($side, 'as_parent')==0) {
-      // get the relationships for this feature without substituting any children
-      // for the parent. We want all relationships
-      $relationships = tripal_feature_get_aggregate_relationships($feature_id, 0);
-      foreach ($relationships as $rindex => $rel) {
-        // get the featurelocs for each of the relationship features
-        $rel_featurelocs = tripal_feature_load_featurelocs($rel->subject_id, 'as_child', 0);
-        foreach ($rel_featurelocs as $findex => $rfloc) {
-          $featurelocs[$i++] = $rfloc;
-        }
-      }
-    }
-  
-    usort($featurelocs, 'chado_feature__residues_sort_locations');
-    return $featurelocs;
+    } */
   }
 }
 
-/**
- * Used to sort the list of relationship parts by start position
- *
- * @ingroup tripal_feature
- */
-function chado_feature__residues_sort_rel_parts_by_start($a, $b) {
-  foreach ($a as $type_name => $details) {
-    $astart = $a[$type_name]['start'];
-    break;
-  }
-  foreach ($b as $type_name => $details) {
-    $bstart = $b[$type_name]['start'];
-    break;
-  }
-  return strnatcmp($astart, $bstart);
-}
-/**
- * Used to sort the feature locs by start position
- *
- * @param $a
- *   One featureloc record (as an object)
- * @param $b
- *   The other featureloc record (as an object)
- *
- * @return
- *   Which feature location comes first
- *
- * @ingroup tripal_feature
- */
-function chado_feature__residues_sort_locations($a, $b) {
-  return strnatcmp($a->fmin, $b->fmin);
-}

+ 0 - 7
tripal_chado/includes/TripalFields/data__sequence_checksum.inc

@@ -61,11 +61,4 @@ class data__sequence_checksum extends TripalField {
   protected $instance;
 
 
-
-  /**
-   * @see TripalField::load()
-   */
-  public function load($entity, $details = array()) {
-
-  }
 }

+ 4 - 15
tripal_chado/includes/TripalFields/data__sequence_formatter.inc

@@ -11,7 +11,7 @@ class data__sequence_formatter extends TripalFieldFormatter {
   public static $settings = array();
 
   /**
-   * 
+   *
    * @param unknown $element
    * @param unknown $entity_type
    * @param unknown $entity
@@ -25,24 +25,13 @@ class data__sequence_formatter extends TripalFieldFormatter {
       '#type' => 'markup',
       '#markup' => '',
     );
-    
+
     $num_bases = 50;
     foreach ($items as $delta => $item) {
-      // If there are no residues then skip this one.
-      if (!is_array($item['value']) or !array_key_exists('residues', $item['value'])) {
-        continue;
-      }
-    
-      $residues = $item['value']['residues'];
-      $label = $item['value']['label'];
-      $defline = $item['value']['defline'];
-    
-      $content = '<p>' . $label . '<p>';
       $content .= '<pre class="residues-formatter">';
-      $content .= '>' . $defline . "<br>";
-      $content .= wordwrap($residues, $num_bases, "<br>", TRUE);
+      $content .= wordwrap($item['value'], $num_bases, "<br>", TRUE);
       $content .= '</pre>';
-    
+
       $element[$delta] = array(
         // We create a render array to produce the desired markup,
         '#type' => 'markup',

+ 0 - 7
tripal_chado/includes/TripalFields/data__sequence_length.inc

@@ -60,11 +60,4 @@ class data__sequence_length extends TripalField {
   // when using the widgetForm, formatterSettingsForm, etc.) it should be set.
   protected $instance;
 
-  /**
-   * 
-   * @see TripalField::load()
-   */
-  public function load($entity, $details = array()) {
-
-  }
 }

+ 96 - 0
tripal_chado/includes/TripalFields/so__cds.inc

@@ -0,0 +1,96 @@
+<?php
+
+class so__cds extends TripalField {
+
+
+  // --------------------------------------------------------------------------
+  //                     EDITABLE STATIC CONSTANTS
+  //
+  // The following constants SHOULD be set for each descendent class.  They are
+  // used by the static functions to provide information to Drupal about
+  // the field and it's default widget and formatter.
+  // --------------------------------------------------------------------------
+
+  // The term that this field maps to.  The format for the term should be:
+  // [vocab]:[accession] where [vocab] is the short name of the vocabulary
+  // and [acession] is the unique accession number for the term.  This term
+  // must already exist in the vocabulary storage backend. This
+  // value should never be changed once fields exist for this type.
+  public static $term = 'SO:0000316';
+
+  // The default lable for this field.
+  public static $label = 'Coding Sequence';
+
+  // The default description for this field.
+  public static $description = 'A contiguous sequence which begins with, and includes, a start codon and ends with, and includes, a stop codon.';
+
+  // Provide a list of global settings. These can be accessed witihn the
+  // globalSettingsForm.  When the globalSettingsForm is submitted then
+  // Drupal will automatically change these settings for all fields.
+  public static $settings = array(
+    'chado_table' => '',
+    'chado_column' => '',
+    'base_table' => '',
+  );
+
+  // Provide a list of instance specific settings. These can be access within
+  // the instanceSettingsForm.  When the instanceSettingsForm is submitted
+  // then Drupal with automatically change these settings for the instnace.
+  // It is recommended to put settings at the instance level whenever possible.
+  public static $instance_settings  = array();
+
+  // Set this to the name of the storage backend that by default will support
+  // this field.
+  public static $storage = 'tripal_no_storage';
+
+  // The default widget for this field.
+  public static $default_widget = 'so__cds_widget';
+
+  // The default formatter for this field.
+  public static $default_formatter = 'so__cds_formatter';
+
+  /**
+   * @see TripalField::load()
+   */
+  public function load($entity, $details = array()) {
+    $field_name = $this->field['field_name'];
+    $feature = $details['record'];
+    $num_seqs = 0;
+
+    $feature = chado_expand_var($feature, 'table', 'featureloc', $options);
+    $featureloc_sequences = chado_get_featureloc_sequences($feature->feature_id, $feature->featureloc->feature_id);
+    foreach($featureloc_sequences as $src => $attrs){
+      // Generate a CDS sequence if one exsits for this feature alignment.
+      $cds_sequence = tripal_get_feature_sequences(
+        array(
+          'feature_id' => $feature->feature_id,
+          'parent_id' => $attrs['featureloc']->srcfeature_id->feature_id,
+          'name' => $feature->name,
+          'featureloc_id' => $attrs['featureloc']->featureloc_id,
+        ),
+        array(
+          // CDS are in parent-child relationships so we want to use the
+          // sequence from the parent
+          'derive_from_parent' => 1,
+          // we want to combine all CDS for this feature into a single sequence
+          'aggregate' => 1,
+          // we're looking for CDS features
+          'sub_feature_types' => array('CDS'),
+          'is_html' => 0
+        )
+      );
+
+      if (count($cds_sequence) > 0) {
+        $defline = tripal_get_fasta_defline($feature, '', $attrs['featureloc'], 'CDS', strlen($attrs['residues']));
+
+        // 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']) {
+          $entity->{$field_name}['und'][$num_seqs++]['value'] = $defline . "\n" .  $cds_sequence[0]['residues'];
+        }
+      }
+    }
+  }
+}

+ 44 - 0
tripal_chado/includes/TripalFields/so__cds_formatter.inc

@@ -0,0 +1,44 @@
+<?php
+
+class so__cds_formatter extends TripalFieldFormatter {
+  // The default lable for this field.
+  public static $label = 'Coding Sequence';
+
+  // The list of field types for which this formatter is appropriate.
+  public static $field_types = array('so__cds');
+
+  // The list of default settings for this formatter.
+  public static $settings = array();
+
+  /**
+   * @see TripalFieldFormatter::view()
+   */
+  public function view(&$element, $entity_type, $entity, $langcode, $items, $display) {
+    $element[0] = array(
+      // We create a render array to produce the desired markup,
+      '#type' => 'markup',
+      '#markup' => '',
+    );
+
+    $num_bases = 50;
+    foreach ($items as $delta => $item) {
+      // If there are no residues then skip this one.
+      if (!is_array($item['value']) or !array_key_exists('residues', $item['value'])) {
+        continue;
+      }
+
+      $residues = $item['value']['residues'];
+
+      $content .= '<pre class="residues-formatter">';
+      $content .= '>' . $defline . "<br>";
+      $content .= wordwrap($residues, $num_bases, "<br>", TRUE);
+      $content .= '</pre>';
+
+      $element[$delta] = array(
+        // We create a render array to produce the desired markup,
+        '#type' => 'markup',
+        '#markup' => $content,
+      );
+    }
+  }
+}

+ 43 - 0
tripal_chado/includes/TripalFields/so__cds_widget.inc

@@ -0,0 +1,43 @@
+<?php
+
+class so__cds_widget extends TripalFieldWidget {
+  // The default lable for this field.
+  public static $label = 'Coding Sequence';
+
+  // The list of field types for which this formatter is appropriate.
+  public static $field_types = array('so__cds');
+
+  /**
+   *
+   * @see TripalFieldWidget::form()
+   */
+  public function form(&$widget, &$form, &$form_state, $langcode, $items, $delta, $element) {
+    parent::form($widget, $form, $form_state, $langcode, $items, $delta, $element);
+
+    // TODO: add a widget...
+  }
+
+  /**
+   * Performs validation of the widgetForm.
+   *
+   * Use this validate to ensure that form values are entered correctly.  Note
+   * this is different from the validate() function which ensures that the
+   * field data meets expectations.
+   *
+   * @param $form
+   * @param $form_state
+   */
+  public function validate($form, &$form_state, $entity_type, $entity, $langcode, $delta) {
+
+  }
+
+
+  /**
+   *
+   * @see TripalFieldWidget::submit()
+   */
+  public function submit($form, &$form_state, $entity_type, $entity, $langcode, $delta) {
+    $field_name = $this->field['field_name'];
+
+  }
+}

+ 45 - 53
tripal_chado/includes/TripalFields/so__transcript.inc

@@ -19,7 +19,7 @@ class so__transcript extends TripalField {
   public static $term = 'SO:0000673';
 
   // The default lable for this field.
-  public static $label = 'Transcript';
+  public static $label = 'Transcripts';
 
   // The default description for this field.
   public static $description = 'An RNA synthesized on a DNA or RNA template by an RNA polymerase.';
@@ -77,9 +77,11 @@ class so__transcript extends TripalField {
   public function load($entity, $details = array()) {
 
     $record = $details['record'];
-    
     $field_name = $this->field['field_name'];
-    
+    $field_type = $this->field['type'];
+    $field_table = $this->field['settings']['chado_table'];
+    $field_column = $this->field['settings']['chado_column'];
+
     // Set some defaults for the empty record.
     $entity->{$field_name}['und'][0] = array(
       'value' => array(
@@ -89,56 +91,46 @@ class so__transcript extends TripalField {
         'location' => '',
       ),
     );
-    
-    // TODO: If the tripal_get_feature_relationships() slows this down then
-    // we may need to write a custom function to get the data.
-    $rels = tripal_get_feature_relationships($record);
-    
-    // TODO: what if other transcripts names from SO are used. In that
-    // case we should support those too (using cvtermpath table to find them).
-    // mRNA should not be hard-coded below.
-    
-    // Set the value to be a array of "table" rows.
-    $transcripts = array();
-    if (key_exists('part of', $rels['object']) &&
-        key_exists('mRNA', $rels['object']['part of'])) {
-          $transcripts =  $rels['object']['part of']['mRNA'];
-        }
-    
-        $headers = array('Name' ,'Identifier', 'Location');
-        $rows = array();
-        $i = 0;
-        foreach ($transcripts as $transcript) {
-          // link the feature to it's node
-          $feature_name = $transcript->record->subject_id->name;
-    
-          $locations = $transcript->child_featurelocs;
-          $loc = "";
-          foreach ($locations AS $location) {
-            $loc .= $location->srcfeature_name . ":" . $location->fmin . ".." . $location->fmax;
-          }
-          $type = $transcript->record->subject_id->type_id;
-          $entity->{$field_name}['und'][$i]['value'] = array(
-            'type' => $type->name,
-            'name' => $feature_name,
-            'identifier' => $transcript->record->subject_id->uniquename,
-            'location' => $loc,
-    
-          );
-          // Add in the semantic web information that describes each key in the
-          // value array.
-          $entity->{$field_name}['und'][$i]['semantic_web'] = array(
-            'type' => $type->dbxref_id->db_id->name . ":" . $type->dbxref_id->accession,
-            'name' => tripal_get_chado_semweb_term('cvterm', 'name'),
-            'identifier' => tripal_get_chado_semweb_term('feature', 'uniquename'),
-            'location' => '',
-          );
-          if (property_exists($transcript->record->subject_id, 'entity_id')) {
-            $entity_id = $transcript->record->subject_id->entity_id;
-            $entity->{$field_name}['und'][$i]['value']['entity'] = 'TripalEntity:' . $entity_id;
-          }
-          $i++;
-        }
+
+    // Get the mRNA features for this gene.
+    $sql = "
+      SELECT FS.name, FS.uniquename, FS.feature_id, FCVT.name as type
+      FROM {feature_relationship} FR
+        INNER JOIN {feature} FS on FS.feature_id = FR.subject_id
+        INNER JOIN {cvterm} FCVT on FCVT.cvterm_id = FS.type_id
+        INNER JOIN {cv} CV on CV.cv_id = FCVT.cv_id
+      WHERE
+        FR.object_id = :feature_id and
+        FCVT.name = 'mRNA' and
+        CV.name = 'sequence'
+    ";
+    $results = chado_query($sql, array(':feature_id' => $record->feature_id));
+    $i = 0;
+    while ($transcript = $results->fetchObject()) {
+      // Get the location of this mRNA.
+      $sql = "
+        SELECT FL.*, F.name as srcfeature_name
+        FROM {featureloc} FL
+          INNER JOIN {feature} F on F.feature_id = FL.srcfeature_id
+        WHERE FL.feature_id = :object_id
+      ";
+      $floc_results = chado_query($sql, array(':object_id' => $transcript->feature_id));
+      $loc = "";
+      while ($location = $floc_results->fetchObject()) {
+        $loc .= $location->srcfeature_name . ":" . $location->fmin . ".." . $location->fmax;
+      }
+      $entity->{$field_name}['und'][$i]['value'] = array(
+        'type' => $transcript->type,
+        'name' => $transcript->name,
+        'identifier' => $transcript->uniquename,
+        'location' => $loc,
+      );
+      $entity_id = tripal_get_chado_entity_id($field_table, $record->feature_id);
+      if ($entity_id) {
+         $entity->{$field_name}['und'][$i]['value']['entity'] = 'TripalEntity:' . $entity_id;
+       }
+      $i++;
+    }
   }
 
 }

+ 1 - 1
tripal_chado/includes/tripal_chado.custom_tables.inc

@@ -328,7 +328,7 @@ function tripal_custom_tables_form_validate($form, &$form_state) {
         $results = db_query($sql, array(':table_id' => $table_id));
         $ct = $results->fetchObject();
         if ($ct->table_name != $schema_array['table']) {
-          $exists = db_table_exists('chado.' . $schema_array['table']);
+          $exists = chado_table_exists($schema_array['table']);
           if ($exists) {
             form_set_error($form_state['values']['schema'],
               t("The table name already exists, please choose a different name."));

+ 0 - 11
tripal_chado/includes/tripal_chado.field_storage.inc

@@ -223,17 +223,6 @@ function tripal_chado_field_storage_load($entity_type, $entities, $age,
     $record = chado_generate_var($base_table, $match);
     $entity->chado_record = $record;
 
-    // For now, expand all 'text' fields.
-    // TODO: we want to be a bit smarter and allow the user to configure this
-    // for now we'll expand.
-    if (isset($schema['fields'])) {
-      foreach ($schema['fields'] as $field_name => $details) {
-        if ($schema['fields'][$field_name]['type'] == 'text') {
-          $record = chado_expand_var($record, 'field', $base_table . '.' . $field_name);
-        }
-      }
-    }
-
     // Iterate through the entity's fields so we can get the column names
     // that need to be selected from each of the tables represented.
     $tables = array();

+ 3 - 3
tripal_chado/includes/tripal_chado.fields.inc

@@ -1004,7 +1004,7 @@ function tripal_chado_bundle_create_instances_custom(&$info, $entity_type, $bund
       'field_name' => $field_name,
       'entity_type' => $entity_type,
       'bundle' => $bundle->name,
-      'label' => 'Sequences',
+      'label' => 'Sequence',
       'description' => 'All available sequences for this record.',
       'required' => FALSE,
       'settings' => array(
@@ -1066,8 +1066,8 @@ function tripal_chado_bundle_create_instances_custom(&$info, $entity_type, $bund
       'field_name' => $field_name,
       'entity_type' => $entity_type,
       'bundle' => $bundle->name,
-      'label' => 'Transcript',
-      'description' => 'These transcripts are associated with this gene.',
+      'label' => 'Transcripts',
+      'description' => 'Transcripts that are part of this gene.',
       'required' => FALSE,
       'settings' => array(
         'auto_attach' => FALSE,

+ 20 - 20
tripal_chado/includes/tripal_chado.mapping.inc

@@ -37,44 +37,44 @@ function tripal_chado_map_cvterms() {
         }
       }
     }
-    
+
     // Now we also want to map tripal terms for existing bundles
-    $sql = 
-      "SELECT 
+    $sql =
+      "SELECT
          (SELECT vocabulary FROM tripal_vocab TV WHERE id = TM.vocab_id),
-          accession, 
-          name 
+          accession,
+          name
        FROM tripal_term TM";
     $results = db_query($sql);
     while ($tripal_term = $results->fetchObject()) {
       $voc = $tripal_term->vocabulary;
       $accession = $tripal_term->accession;
       $name = $tripal_term->name;
-      $dbxref_sql = 
-        "SELECT dbxref_id 
-          FROM {dbxref} 
-          WHERE 
-            accession = :accession 
-          AND 
+      $dbxref_sql =
+        "SELECT dbxref_id
+          FROM {dbxref}
+          WHERE
+            accession = :accession
+          AND
             db_id = (SELECT db_id FROM {db} WHERE name = :voc)";
       $dbxref_id = chado_query($dbxref_sql, array(':accession' => $accession, ':voc' => $voc))->fetchField();
       if ($dbxref_id) {
-        $cvterm_sql = 
-          "SELECT cvterm_id 
-            FROM {cvterm} 
-            WHERE 
-              dbxref_id = :dbxref_id 
+        $cvterm_sql =
+          "SELECT cvterm_id
+            FROM {cvterm}
+            WHERE
+              dbxref_id = :dbxref_id
             AND name = :name";
         $cvterm_id = chado_query($cvterm_sql, array(':dbxref_id' => $dbxref_id, ':name' => $name))->fetchField();
         if ($cvterm_id) {
          // Check if this term is already mapped in the tripal_cvterm_mapping table
-         $check_sql = 
-           "SELECT mapping_id 
-             FROM tripal_cvterm_mapping 
+         $check_sql =
+           "SELECT mapping_id
+             FROM tripal_cvterm_mapping
              WHERE cvterm_id = :cvterm_id";
          $mapped = db_query($check_sql, array(':cvterm_id' => $cvterm_id))->fetchField();
          // If mapping does not exist and a table name matches the term name, add it
-         if (!$mapped && db_table_exists('chado.' . $name)) {
+         if (!$mapped && chado_table_exists($name)) {
            print "Adding mapped tripal term: $name\n";
            tripal_chado_add_cvterm_mapping($cvterm_id, $name, NULL);
          }

+ 5 - 4
tripal_chado/includes/tripal_chado.migrate.inc

@@ -472,10 +472,11 @@ function tripal_chado_migrate_form_step2_ajax_callback(&$form, &$form_state) {
  */
 function tripal_chado_get_tripal_v2_content_type_options($all_option = FALSE, $has_template = FALSE) {
   // Get all available Tripal v2 chado tables
-  $sql =
-  "SELECT table_name
-      FROM information_schema.tables
-      WHERE table_schema = 'public' AND table_name LIKE 'chado_%'";
+  $sql = "
+    SELECT table_name
+    FROM information_schema.tables
+    WHERE table_schema = 'public' AND table_name LIKE 'chado_%'
+  ";
   $result = db_query($sql);
   // Store 'chado_*' tables that has at least one node
   $tables = array();