Browse Source

All templates for the feature page are converted except featurepos and phenotype needs testing.

Stephen Ficklin 11 years ago
parent
commit
ee855ade6e
24 changed files with 1013 additions and 937 deletions
  1. 2 1
      tripal_analysis/tripal_analysis.module
  2. 48 24
      tripal_core/api/tripal_core_chado.api.inc
  3. 2 12
      tripal_core/tripal_core.module
  4. 40 22
      tripal_feature/api/tripal_feature.api.inc
  5. 3 59
      tripal_feature/includes/tripal_feature.admin.inc
  6. 12 6
      tripal_feature/includes/tripal_feature.form.inc
  7. 73 55
      tripal_feature/includes/tripal_feature.gff_loader.inc
  8. 142 236
      tripal_feature/includes/tripal_feature.sync_features.inc
  9. 67 51
      tripal_feature/theme/tripal_feature/tripal_feature_alignments.tpl.php
  10. 46 41
      tripal_feature/theme/tripal_feature/tripal_feature_analyses.tpl.php
  11. 1 3
      tripal_feature/theme/tripal_feature/tripal_feature_base.tpl.php
  12. 0 28
      tripal_feature/theme/tripal_feature/tripal_feature_featureloc_sequences.tpl.php
  13. 1 2
      tripal_feature/theme/tripal_feature/tripal_feature_featurepos.tpl.php
  14. 48 35
      tripal_feature/theme/tripal_feature/tripal_feature_phenotypes.tpl.php
  15. 40 24
      tripal_feature/theme/tripal_feature/tripal_feature_properties.tpl.php
  16. 57 40
      tripal_feature/theme/tripal_feature/tripal_feature_references.tpl.php
  17. 116 64
      tripal_feature/theme/tripal_feature/tripal_feature_relationships.tpl.php
  18. 42 7
      tripal_feature/theme/tripal_feature/tripal_feature_sequence.tpl.php
  19. 11 43
      tripal_feature/theme/tripal_feature/tripal_feature_teaser.tpl.php
  20. 52 34
      tripal_feature/theme/tripal_feature/tripal_feature_terms.tpl.php
  21. 1 0
      tripal_feature/theme/tripal_organism/tripal_organism_feature_counts.tpl.php
  22. 208 139
      tripal_feature/tripal_feature.module
  23. 0 10
      tripal_organism/tripal_organism.install
  24. 1 1
      tripal_views/views/handlers/tripal_views_handler_filter_select_string.inc

+ 2 - 1
tripal_analysis/tripal_analysis.module

@@ -332,7 +332,7 @@ function chado_analysis_insert($node) {
   // if there is an analysis_id in the $node object then this must be a sync so
   // we can skip adding the analysis as it is already there, although
   // we do need to proceed with the rest of the insert
-  if (!property_exists($node,'analysis_id')) {
+  if (!property_exists($node, 'analysis_id')) {
 
     // Create a timestamp so we can insert it into the chado database
     $time  = $node->timeexecuted;
@@ -423,6 +423,7 @@ function chado_analysis_insert($node) {
     }
     $properties[$name][$index] = trim($node->new_value);
   }
+  
   // now add in the properties
   foreach ($properties as $property => $elements) {
     foreach ($elements as $rank => $value) {

+ 48 - 24
tripal_core/api/tripal_core_chado.api.inc

@@ -12,6 +12,23 @@ define('TRIPAL_NOTICE',5);
 define('TRIPAL_INFO',6);
 define('TRIPAL_DEBUG',7);
 
+
+/**
+ * This function is used to set the global Chado variables
+ */
+function tripal_core_set_globals() {
+  // these global variables are meant to be accessed by all Tripal
+  // modules to find the chado version installed and if Chado is local.
+  // these variables are stored as globals rather than using the drupal_set_variable
+  // functions because the Drupal functions make databaes queries and for long
+  // running loaders we don't want those queries repeatedly.
+  $GLOBALS["chado_is_installed"]  = tripal_core_is_chado_installed();
+  if ($GLOBALS["chado_is_installed"]) {
+    $GLOBALS["chado_is_local"]      = tripal_core_is_chado_local();
+    $GLOBALS["chado_version"]       = tripal_core_get_chado_version();
+    $GLOBALS["exact_chado_version"] = tripal_core_get_chado_version(TRUE);
+  }
+}
 /**
  * Provide better error notice for Tripal
  * @param $type
@@ -1449,7 +1466,7 @@ function tripal_core_chado_get_foreign_key($table_desc, $field, $values, $option
  */
 function tripal_core_generate_chado_var($table, $values, $base_options = array()) {
   $all = new stdClass();
-
+  
   $return_array = 0;
   if (array_key_exists('return_array', $base_options)) {
     $return_array = 1;
@@ -1559,9 +1576,10 @@ function tripal_core_generate_chado_var($table, $values, $base_options = array()
         $sql = "
           SELECT $table_primary_key, nid
           FROM {chado_$table}
-          WHERE $table_primary_key = :$table_primary_key";
+          WHERE $table_primary_key = :$table_primary_key
+        ";
         $mapping = db_query($sql, array(":$table_primary_key" => $object->{$table_primary_key}))->fetchObject();
-        if ($mapping->{$table_primary_key}) {
+        if ($mapping and $mapping->$table_primary_key) {
           $object->nid = $mapping->nid;
           $object->expandable_nodes[] = $table;
         }
@@ -1609,28 +1627,30 @@ function tripal_core_generate_chado_var($table, $values, $base_options = array()
             if (is_array($include_fk)) {
               $options['include_fk'] = $include_fk[$foreign_key];
             }
+
             $foreign_object = tripal_core_generate_chado_var($foreign_table, $foreign_values, $options);
+            
             // add the foreign record to the current object in a nested manner
             $object->{$foreign_key} = $foreign_object;
             // Flatten expandable_x arrays so only in the bottom object
-            if (is_array($object->{$foreign_key}->expandable_fields)) {
+            if (property_exists($object->{$foreign_key}, 'expandable_fields') and is_array($object->{$foreign_key}->expandable_fields)) {
               $object->expandable_fields = array_merge(
-              $object->expandable_fields,
-              $object->{$foreign_key}->expandable_fields
+                $object->expandable_fields,
+                $object->{$foreign_key}->expandable_fields
               );
               unset($object->{$foreign_key}->expandable_fields);
             }
-            if (is_array($object->{$foreign_key}->expandable_tables)) {
+            if (property_exists($object->{$foreign_key}, 'expandable_tables') and is_array($object->{$foreign_key}->expandable_tables)) {
               $object->expandable_tables = array_merge(
-              $object->expandable_tables,
-              $object->{$foreign_key}->expandable_tables
+                $object->expandable_tables,
+                $object->{$foreign_key}->expandable_tables
               );
               unset($object->{$foreign_key}->expandable_tables);
             }
-            if (is_array($object->{$foreign_key}->expandable_nodes)) {
+            if (property_exists($object->{$foreign_key}, 'expandable_nodes') and is_array($object->{$foreign_key}->expandable_nodes)) {
               $object->expandable_nodes = array_merge(
-              $object->expandable_nodes,
-              $object->{$foreign_key}->expandable_nodes
+                $object->expandable_nodes,
+                $object->{$foreign_key}->expandable_nodes
               );
               unset($object->{$foreign_key}->expandable_nodes);
             }
@@ -1697,7 +1717,7 @@ function tripal_core_generate_chado_var($table, $values, $base_options = array()
  *     multiple records exist.
  *   - include_fk:
  *     an array of FK relationships to follow. By default, the
- *     tripal_core_chado_select function will follow all FK relationships but this
+ *     tripal_core_expand_chado_vars function will follow all FK relationships but this
  *     may generate more queries then is desired slowing down this function call when
  *     there are lots of FK relationships to follow.  Provide an array specifying the
  *     fields to include.  For example, if expanding a property table (e.g. featureprop)
@@ -1817,21 +1837,22 @@ function tripal_core_expand_chado_vars($object, $type, $to_expand, $table_option
           // generate a new object for this table using the FK values in the base table.
           $new_options = $table_options;
           $foreign_object = tripal_core_generate_chado_var($foreign_table, array($left => $object->{$right}), $new_options);
-
+          
           // if the generation of the object was successful, update the base object to include it.
           if ($foreign_object) {
-            // in the case where the foreign key relationships exists more
-            // than once with the same table we want to alter the array structure. rather than
-            // add the object with a key of the table name, we will add the FK field name in between
+            // in the case where the foreign key relationship exists more
+            // than once with the same table we want to alter the array structure to
+            // include the field name.
             if (count($foreign_table_desc['foreign keys'][$base_table]['columns']) > 1) {
-              if (!is_object($object->{$foreign_table})) {
+              if (!property_exists($object, $foreign_table)) {
                 $object->{$foreign_table} = new stdClass();
               }
               $object->{$foreign_table}->{$left} = $foreign_object;
               $object->expanded = $to_expand;
+              
             }
             else {
-              if (!property_exists($object, $foreign_table) or !is_object($object->{$foreign_table})) {
+              if (!property_exists($object, $foreign_table)) {
                 $object->{$foreign_table} = new stdClass();
               }
               $object->{$foreign_table} = $foreign_object;
@@ -1840,8 +1861,11 @@ function tripal_core_expand_chado_vars($object, $type, $to_expand, $table_option
           }
           // if the object returned is NULL then handle that
           else {
+            // in the case where the foreign key relationship exists more
+            // than once with the same table we want to alter the array structure to
+            // include the field name.
             if (count($foreign_table_desc['foreign keys'][$base_table]['columns']) > 1) {
-              if (!is_object($object->{$foreign_table})) {
+              if (!property_exists($object, $foreign_table)) {
                 $object->{$foreign_table} = new stdClass();
               }
               $object->{$foreign_table}->{$left} = NULL;
@@ -1912,8 +1936,8 @@ function tripal_core_expand_chado_vars($object, $type, $to_expand, $table_option
       return FALSE;
   }
 
-  //move extended array downwards-------------------------------------------------------------------
-  if (!$object->expanded) {
+  // move extended array downwards
+  if (!property_exists($object, 'expanded')) {
     //if there's no extended field then go hunting for it
     foreach ( (array)$object as $field_name => $field_value) {
       if (is_object($field_value)) {
@@ -1925,7 +1949,7 @@ function tripal_core_expand_chado_vars($object, $type, $to_expand, $table_option
     }
   }
   //try again becasue now we might have moved it down
-  if ($object->expanded) {
+  if (property_exists($object, 'expanded')) {
     $expandable_name = 'expandable_' . $type . 's';
     if ($object->{$expandable_name}) {
       $key_to_remove = array_search($object->expanded, $object->{$expandable_name});
@@ -2762,7 +2786,7 @@ function tripal_core_get_chado_version($exact = FALSE, $warn_if_unsupported = FA
  * @ingroup tripal_core_api
  */
 function tripal_core_get_chado_table_schema($table) {
-
+  
   // first get the chado version that is installed
   $v = $GLOBALS["chado_version"];
 

+ 2 - 12
tripal_core/tripal_core.module

@@ -50,6 +50,8 @@ require_once "includes/chado_install.inc";
 require_once "includes/form_elements.inc";
 
 
+tripal_core_set_globals();  
+
 /**
  * Implements hook_init().
  * Used to set the search_path, create default content and set default variables.
@@ -63,18 +65,6 @@ function tripal_core_init() {
   drupal_add_js(drupal_get_path('module', 'tripal_core') . '/theme/js/tripal.js');
   drupal_add_css(drupal_get_path('module', 'tripal_core') . '/theme/css/tripal.css');
 
-  // these global variables are meant to be accessed by all Tripal
-  // modules to find the chado version installed and if Chado is local.
-  // these variables are stored as globals rather than using the drupal_set_variable
-  // functions because the Drupal functions make databaes queries and for long
-  // running loaders we don't want those queries repeatedly.
-  $GLOBALS["chado_is_installed"]  = tripal_core_is_chado_installed();
-  if ($GLOBALS["chado_is_installed"]) {
-    $GLOBALS["chado_is_local"]      = tripal_core_is_chado_local();
-    $GLOBALS["chado_version"]       = tripal_core_get_chado_version();
-    $GLOBALS["exact_chado_version"] = tripal_core_get_chado_version(TRUE);
-  }
-  
   
   // create the 'tripal' controlled volcabulary in chado but only if it doesn't already exist, and
   // only if the chado database is present.

+ 40 - 22
tripal_feature/api/tripal_feature.api.inc

@@ -850,18 +850,38 @@ function tripal_feature_get_feature_relationships($feature) {
   // expand the feature object to include the feature relationships.
   $options = array(
     'return_array' => 1,
-    'order_by'     => array('rank' => 'ASC'),
+    'order_by' => array('rank' => 'ASC'),
+    // we don't want to fully recurse we only need information about the 
+    // relationship type and the object and subject features (including feature type
+    // and organism)
+    'include_fk' => array(
+      'type_id' => 1,
+      'object_id' => array(
+        'type_id' => 1,
+        'organism_id' => 1
+      ),
+      'subject_id'  => array(
+        'type_id' => 1,
+        'organism_id' => 1
+      ),
+    ),
   );
-  $feature = tripal_core_expand_chado_vars($feature, 'table',
-    'feature_relationship', $options);
-
+  $feature = tripal_core_expand_chado_vars($feature, 'table', 'feature_relationship', $options);
+  
   // get the subject relationships
   $srelationships = $feature->feature_relationship->subject_id;
   $orelationships = $feature->feature_relationship->object_id;
   
+  
   // get alignment as child. The $feature->featureloc element
   // is already populated from the alignment preprocess function
-  $feature = tripal_core_expand_chado_vars($feature, 'table', 'featureloc');
+  $options = array(
+    'include_fk' => array(
+      'srcfeature_id' => 1,
+      'feature_id' => 1,
+    ),
+  );
+  $feature = tripal_core_expand_chado_vars($feature, 'table', 'featureloc', $options);
   $cfeaturelocs = $feature->featureloc->feature_id;
   if (!$cfeaturelocs) {
      $cfeaturelocs = array();
@@ -872,18 +892,14 @@ function tripal_feature_get_feature_relationships($feature) {
   
   // prepare the SQL statement to get the featureloc for the 
   // feature in the relationships. 
-  $connection = tripal_db_persistent_chado();
-  $psql = " 
-    PREPARE sel_featureloc_preprocess_relationships (int, int) AS 
+  $flrels_sql = "  
     SELECT 
       FL.featureloc_id, F.name as srcfeature_name, FL.srcfeature_id,
       FL.feature_id, FL.fmin, FL.fmax, FL.strand, FL.phase
-    FROM featureloc FL 
-      INNER JOIN feature F ON F.feature_id = FL.srcfeature_id
-    WHERE FL.feature_id = $1 and FL.srcfeature_id = $2
+    FROM {featureloc} FL 
+      INNER JOIN {feature} F ON F.feature_id = FL.srcfeature_id
+    WHERE FL.feature_id = :feature_id and FL.srcfeature_id = :srcfeature_id
   ";
-  tripal_core_chado_prepare('sel_featureloc_preprocess_relationships', $psql, array('int', 'int'));
-
   
   // combine both object and subject relationshisp into a single array
   $relationships = array();
@@ -898,11 +914,12 @@ function tripal_feature_get_feature_relationships($feature) {
        // same landmark feature.
        $rel->child_featurelocs = array();     
        foreach ($cfeaturelocs as $featureloc) {
-          $res = chado_query("EXECUTE sel_featureloc_preprocess_relationships (:relationship, :featureloc)", 
-            array(':relationship' => $relationship->subject_id->feature_id, ':featureloc' => $featureloc->srcfeature_id->feature_id));        
+          $res = chado_query($flrels_sql, array(':feature_id' => $relationship->subject_id->feature_id, ':srcfeature_id' => $featureloc->srcfeature_id->feature_id));        
           while ($loc = $res->fetchObject()) {
              // add in the node id of the src feature if it exists and save this location
-             $loc->nid = $featureloc->srcfeature_id->nid;
+             if (property_exists($featureloc->srcfeature_id, 'nid')) {
+               $loc->nid = $featureloc->srcfeature_id->nid;
+             }
              $rel->child_featurelocs[] = $loc;
           }
        }
@@ -914,7 +931,7 @@ function tripal_feature_get_feature_relationships($feature) {
        
        // get the node id of the subject
        $sql = "SELECT nid FROM {chado_feature} WHERE feature_id = :feature_id";
-       $n = db_query($sql, array('feature_id' => $relationship->subject_id->feature_id))->fetchObject();
+       $n = db_query($sql, array(':feature_id' => $relationship->subject_id->feature_id))->fetchObject();
        if ($n) {
           $rel->record->nid = $n->nid;
        }
@@ -936,12 +953,13 @@ function tripal_feature_get_feature_relationships($feature) {
        // get locations where this feature overlaps with the parent
        $rel->parent_featurelocs = array();     
        foreach ($cfeaturelocs as $featureloc) {
-          $res = chado_query("EXECUTE sel_featureloc_preprocess_relationships (:feature_id, :srcfeature_id)", 
-            array('feature_id' => $relationship->object_id->feature_id, ':srcfeature_id' => $featureloc->srcfeature_id->feature_id));
+          $res = chado_query($flrels_sql, array(':feature_id' => $relationship->object_id->feature_id, ':srcfeature_id' => $featureloc->srcfeature_id->feature_id));
           while ($loc = $res->fetchObject()) {
-             // add in the node id of the src feature if it exists and save this location
-             $loc->nid = $featureloc->srcfeature_id->nid;
-             $rel->parent_featurelocs[] = $loc;
+            // add in the node id of the src feature if it exists and save this location
+            if (property_exists($featureloc->srcfeature_id, 'nid')) {
+              $loc->nid = $featureloc->srcfeature_id->nid;
+            }
+            $rel->parent_featurelocs[] = $loc;
           }
        }
        $rel->record = $relationship;

+ 3 - 59
tripal_feature/includes/tripal_feature.admin.inc

@@ -84,7 +84,7 @@ function tripal_feature_admin() {
        '#type'        => 'textarea',
        '#description' => t("Enter the Sequence Ontology (SO) terms for the feature types that " .
                            "will be shown in the feature browser."),
-       '#default_value' => variable_get('chado_browser_feature_types', 'gene contig'),
+       '#default_value' => variable_get('chado_browser_feature_types', 'gene mRNA'),
     );
 
 
@@ -156,8 +156,7 @@ function tripal_feature_admin() {
     );
 
     get_tripal_feature_admin_form_taxonomy_set($form);
-    get_tripal_feature_admin_form_reindex_set($form);
-    get_tripal_feature_admin_form_cleanup_set($form);
+//    get_tripal_feature_admin_form_reindex_set($form);
 /*  }
   else {
     $form['notice'] = array(
@@ -191,11 +190,6 @@ function tripal_feature_admin_validate($form, &$form_state) {
 
   switch ($form_state['values']['op']) {
 
-    case  t('Sync all Features') :
-      tripal_add_job('Sync all features', 'tripal_feature',
-        'tripal_feature_sync_features', $job_args, $user->uid);
-      break;
-
     case t('Set/Reset Taxonomy for all feature nodes') :
       tripal_add_job('Set all feature taxonomy', 'tripal_feature',
         'tripal_features_set_taxonomy', $job_args, $user->uid);
@@ -206,11 +200,6 @@ function tripal_feature_admin_validate($form, &$form_state) {
         'tripal_features_reindex', $job_args, $user->uid);
       break;
 
-    case t('Clean up orphaned features') :
-      tripal_add_job('Cleanup orphaned features', 'tripal_feature',
-        'tripal_features_cleanup', $job_args, $user->uid);
-      break;
-
     case t('Set Browser') :
       variable_set('tripal_feature_browse_setting', $form_state['values']['browse_features']);
       variable_set('tripal_library_feature_browse_setting', $form_state['values']['browse_features_library']);
@@ -230,37 +219,7 @@ function tripal_feature_admin_validate($form, &$form_state) {
   }
 
 }
-/**
- *
- *
- * @ingroup tripal_feature
- */
-function get_tripal_feature_admin_form_cleanup_set(&$form) {
-  $form['cleanup'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Clean Up'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
-  );
-  $form['cleanup']['description'] = array(
-    '#type' => 'item',
-    '#value' => t("With Drupal and chado residing in different databases " .
-      "it is possible that nodes in Drupal and features in Chado become " .
-      "\"orphaned\".  This can occur if a feature node in Drupal is " .
-      "deleted but the corresponding chado feature is not and/or vice " .
-      "versa.  The Cleanup function will also remove nodes for features " .
-      "that are not in the list of allowed feature types as specified " .
-      "above.  This is helpful when a feature type needs to be " .
-      "removed but was previously present as Drupal nodes. " .
-      "Click the button below to resolve these discrepancies."),
-    '#weight' => 1,
-  );
-  $form['cleanup']['button'] = array(
-    '#type' => 'submit',
-    '#value' => t('Clean up orphaned features'),
-    '#weight' => 2,
-  );
-}
+
 
 /**
  *
@@ -591,22 +550,7 @@ function tripal_feature_set_taxonomy($node, $feature_id) {
 
 }
 
-/**
- *
- * Remove orphaned drupal nodes
- *
- * @param $dummy
- *   Not Used -kept for backwards compatibility
- * @param $job_id
- *   The id of the tripal job executing this function
- *
- * @ingroup tripal_feature
- */
-function tripal_features_cleanup($dummy = NULL, $job_id = NULL) {
-
-  return tripal_core_clean_orphaned_nodes('feature', $job_id);
 
-}
 /**
  *  This function is an extension of the chado_feature_view by providing
  *  the markup for the feature object THAT WILL BE INDEXED.

+ 12 - 6
tripal_feature/includes/tripal_feature.form.inc

@@ -210,14 +210,13 @@ function chado_feature_validate($node, &$form_state) {
   
   $result = 0;
 
-  // make sure the feature type is a real sequence ontology term
-  $type = tripal_cv_get_cvterm_by_name($node->feature_type, NULL, 'sequence');
-  if (!$type) {
-    form_set_error('feature_type', t("The feature type is not a valid name from the Sequence Ontology."));
-  }
-
   // CASE A: if the nid exists then this is an update
   if (property_exists($node, 'nid')) {
+    // make sure the feature type is a real sequence ontology term
+    $type = tripal_cv_get_cvterm_by_name($node->feature_type, NULL, 'sequence');
+    if (!$type) {
+      form_set_error('feature_type', t("The feature type is not a valid name from the Sequence Ontology."));
+    }
     
     // if this is an update, we want to make sure that a different feature for
     // the organism doesn't already have this uniquename. We don't want to give
@@ -249,6 +248,13 @@ function chado_feature_validate($node, &$form_state) {
     }
     else {
       // CASE C: We are validating a form for inserting a new node
+      
+      // make sure the feature type is a real sequence ontology term
+      $type = tripal_cv_get_cvterm_by_name($node->feature_type, NULL, 'sequence');
+      if (!$type) {
+        form_set_error('feature_type', t("The feature type is not a valid name from the Sequence Ontology."));
+      }
+      
       // if this is an insert then we just need to make sure this name doesn't
       // already exist for this organism if it does then we need to throw an error
       $sql = "

+ 73 - 55
tripal_feature/includes/tripal_feature.gff_loader.inc

@@ -375,22 +375,15 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
     $intv_read = 0;
     
     // prepare the statement used to get the cvterm for each feature.
-    $psql = "
-      PREPARE sel_cvterm_idnasy (int, text, text) AS
+    $sel_cvterm_sql = "
       SELECT CVT.cvterm_id, CVT.cv_id, CVT.name, CVT.definition,
         CVT.dbxref_id, CVT.is_obsolete, CVT.is_relationshiptype
       FROM {cvterm} CVT
         INNER JOIN {cv} CV on CVT.cv_id = CV.cv_id
         LEFT JOIN {cvtermsynonym} CVTS on CVTS.cvterm_id = CVT.cvterm_id
-      WHERE CV.cv_id = $1 and 
-       (lower(CVT.name) = lower($2) or lower(CVTS.synonym) = lower($3))
+      WHERE CV.cv_id = :cv_id and 
+       (lower(CVT.name) = lower(:name) or lower(CVTS.synonym) = lower(:synonym))
      ";
-     $status = chado_query($psql);
-     if (!$status) {
-       watchdog('T_gff3_loader', 'cannot prepare statement \'sel_cvterm_idnasy\'.', 
-         array(), WATCHDOG_ERROR);
-       return '';
-     }  
   
     // iterate through each line of the GFF file
     print "Parsing Line $line_num (0.00%). Memory: " . number_format(memory_get_usage()) . " bytes\r";
@@ -419,7 +412,7 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
           // we're done because this is a delete operation so break out of the loop.
           break;         
         }
-        tripal_feature_load_gff3_fasta($fh, $interval, $num_read, $intv_read, $line_num);
+        tripal_feature_load_gff3_fasta($fh, $interval, $num_read, $intv_read, $line_num, $filesize, $job);
         continue;
       }
       // if the ##sequence-region line is present then we want to add a new feature
@@ -428,8 +421,7 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
         $rstart = $region_matches[2];
         $rend = $region_matches[3];
         if ($landmark_type) {
-          $result = chado_query("EXECUTE sel_cvterm_idnasy (:cv_id, :name, :synonym)", 
-            array(':cv_id' => $cv->cv_id, ':name' => $landmark_type, ':synonym' => $landmark_type));
+          $result = chado_query($sel_cvterm_sql, array(':cv_id' => $cv->cv_id, ':name' => $landmark_type, ':synonym' => $landmark_type));
           $cvterm = $result->fetchObject();
           if (!$cvterm) {
             watchdog('T_gff3_loader', 'cannot find feature type \'%landmark_type\' on line %line_num of the GFF file', 
@@ -492,10 +484,8 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
       }
       if (strcmp($phase, '.') == 0) {
         $phase = '';
-      }
-    
-      $result = chado_query("EXECUTE sel_cvterm_idnasy (:cv_id, :name, :synonym)", 
-        array(':cv_id' => $cv->cv_id, ':name' => $type, ':synonym' => $type));
+      }    
+      $result = chado_query($sel_cvterm_sql, array(':cv_id' => $cv->cv_id, ':name' => $type, ':synonym' => $type));
   
       $cvterm = $result->fetchObject();
       if (!$cvterm) {
@@ -815,9 +805,9 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
           F.uniquename, FL.strand 
         FROM {tripal_gff_temp} TGT 
           INNER JOIN {feature} F                ON TGT.feature_id = F.feature_id
-          INNER JOIN {feature_relationship} FR  ON FR.object_id = TGT.feature_id
-          INNER JOIN {cvterm} CVT               ON CVT.cvterm_id = FR.type_id  
-          INNER JOIN {featureloc} FL            ON FL.feature_id = F.feature_id    
+          INNER JOIN {feature_relationship} FR  ON FR.object_id   = TGT.feature_id
+          INNER JOIN {cvterm} CVT               ON CVT.cvterm_id  = FR.type_id  
+          INNER JOIN {featureloc} FL            ON FL.feature_id  = F.feature_id    
         WHERE CVT.name = 'part_of'
       ";
       $parents = chado_query($sql);
@@ -833,17 +823,26 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
       // now set the rank of any parent/child relationships.  The order is based
       // on the fmin.  The start rank is 1.  This allows features with other
       // relationships to be '0' (the default), and doesn't interfer with the
-      // ordering defined here.        
+      // ordering defined here.  
+      $num_recs = $parents->rowCount();
+      $i = 1;
+      $interval = intval($num_recs * 0.0001);
+      if ($interval == 0) {
+        $interval = 1;
+      }
+      $percent = sprintf("%.2f", ($i / $num_recs) * 100);
+      print "Setting $i of $num_recs (" . $percent . "%). Memory: " . number_format(memory_get_usage()) . " bytes.\r";
+      
       while ($parent = $parents->fetchObject()) {
         
-        // get the children
-        if ($connection) {      
-          $result = chado_query($sel_gffchildren_sql, array(':feature_id' => $parent->feature_id));
-        }
-        else {
-          $result = chado_query($sql, array(':feature_id' => $parent->feature_id));
+        if ($i % $interval == 0) {
+          $percent = sprintf("%.2f", ($i / $num_recs) * 100);
+          print "Setting $i of $num_recs (" . $percent . "%). Memory: " . number_format(memory_get_usage()) . " bytes.\r";
         }
         
+        // get the children
+        $result = chado_query($sel_gffchildren_sql, array(':feature_id' => $parent->feature_id));
+        
         // build an array of the children
         $children = array();
         while ($child = $result->fetchObject()) {
@@ -875,6 +874,7 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
           tripal_core_chado_update('feature_relationship', $match, $values);
           $rank++;
         }
+        $i++;
       }
     }
   }
@@ -891,7 +891,7 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
     return 0;
   }
 
-  print "Done\n";
+  print "\nDone\n";
   return 1;
 }
 /**
@@ -1712,15 +1712,13 @@ function tripal_feature_load_gff3_property($feature, $property, $value) {
 /*
  * 
  */
-function tripal_feature_load_gff3_fasta($fh, $interval, &$num_read, &$intv_read, &$line_num) {
-  print "Loading FASTA sequences\n";
+function tripal_feature_load_gff3_fasta($fh, $interval, &$num_read, &$intv_read, &$line_num, $filesize, $job) {
+  print "\nLoading FASTA sequences\n";
   $residues = '';
-  $sel_gfftemp_un_sql = " 
-    SELECT feature_id FROM tripal_gff_temp
-    WHERE uniquename = :uname
-  ";
   $id = NULL;
   
+  $percent = sprintf("%.2f", ($num_read / $filesize) * 100);
+  print "Parsing Line $line_num (" . $percent . "%). Memory: " . number_format(memory_get_usage()) . " bytes.\r";
   // iterate through the remaining lines of the file
   while ($line = fgets($fh)) {
     
@@ -1741,45 +1739,49 @@ function tripal_feature_load_gff3_fasta($fh, $interval, &$num_read, &$intv_read,
     
     // if we encounter a definition line then get the name, uniquename,
     // accession and relationship subject from the definition line
-    if (preg_match('/^>/', $line)) {   
-      // if we are beginning a new sequence then save the last one we 
-      // just finished.     
-             
+    if (preg_match('/^>/', $line)) {
+      
+      // if we are beginning a new sequence then save to the database the last one we just finished.     
       if ($id) {
-        $result = chado_query($sel_gfftemp_un_sql, array(':uname' => $id));
-        if (!$result) {
+        $values = array('uniquename' => $id);
+        $result = tripal_core_chado_select('tripal_gff_temp', array('*'), $values); 
+        if (count($result) == 0) {
           watchdog('T_gff3_loader', 'Cannot find feature to assign FASTA sequence: %uname', 
-             array('%uname' => $id), WATCHDOG_WARNING); 
+             array('%uname' => $id), WATCHDOG_WARNING);
         }
-        // if we have a feature then add the residues
-        else {    
-          $feature = $result->fetchObject();    
+        else {
+          // if we have a feature then add the residues
+          $feature = $result[0];
           $values = array('residues' => $residues);
           $match = array('feature_id' => $feature->feature_id);
           tripal_core_chado_update('feature', $match, $values);
         }
       }
+      
       // get the feature ID for this ID from the tripal_gff_temp table
-      $id = preg_replace('/^>(.*)$/', '\1', $line);      
+      $id = preg_replace('/^>(.*)$/', '\1', $line);
       $residues = '';
     }
     else {
       $residues .= trim($line);
     }
   } 
+  
   // add in the last sequence
-  $result = chado_query($sel_gfftemp_un_sql, array(':uname' => $id));
-  if (!$result) {
+  $values = array('uniquename' => $id);
+  $result = tripal_core_chado_select('tripal_gff_temp', array('*'), $values); 
+  if (count($result) == 0) {
     watchdog('T_gff3_loader', 'Cannot find feature to assign FASTA sequence: %uname', 
-       array('%uname' => $id), WATCHDOG_WARNING); 
+       array('%uname' => $id), WATCHDOG_WARNING);
   }
-  // if we have a feature then add the residues
-  else {        
-    $feature = $result->fetchObject();    
+  else {
+    // if we have a feature then add the residues
+    $feature = $result[0];
     $values = array('residues' => $residues);
     $match = array('feature_id' => $feature->feature_id);
     tripal_core_chado_update('feature', $match, $values);
-  } 
+  }
+
 }
 
 /*
@@ -1878,14 +1880,30 @@ function tripal_feature_load_gff3_target($feature, $tags, $target_organism_id, $
            'name' => 'sequence',
         )
       );
+      
+      // get the cvterm_id for the target type
       $type = tripal_core_chado_select('cvterm', array('cvterm_id'), $values);
       if (count($type) == 1) {
         $t_type_id = $type[0]->cvterm_id;
       }
       else {
-        watchdog('T_gff3_loader', "The target_type attribute does not exist in the sequence ontology: %type. ", 
-          array('%type' => $gff_target_type), WATCHDOG_WARNING);
-        $t_type_id = '';
+        // check to see if this is a synonym
+        $sql = "
+          SELECT CVTS.cvterm_id
+          FROM {cvtermsynonym} CVTS
+            INNER JOIN {cvterm} CVT ON CVT.cvterm_id = CVTS.cvterm_id
+            INNER JOIN {cv} CV      ON CV.cv_id = CVT.cv_id 
+          WHERE CV.name = 'sequence' and CVTS.synonym = :synonym
+        ";
+        $synonym = chado_query($sql, array(':synonym' => $gff_target_type))->fetchObject();
+        if ($synonym) {
+          $t_type_id = $synonym->cvterm_id;
+        }
+        else {
+          watchdog('T_gff3_loader', "The target_type attribute does not exist in the sequence ontology: %type. ", 
+            array('%type' => $gff_target_type), WATCHDOG_WARNING);
+          $t_type_id = '';
+        }
       }
     }                       
     

+ 142 - 236
tripal_feature/includes/tripal_feature.sync_features.inc

@@ -5,41 +5,13 @@
  * @todo Add file header description
  */
 
-
-# This script can be run as a stand-alone script to sync all the features from chado to drupal
-// Parameter f specifies the feature_id to sync
-// -f 0 will sync all features
-
-$arguments = getopt("f:t:");
-
-if (isset($arguments['f']) and isset($arguments['t']) and $arguments['t'] == 'chado_feature') {
-  $drupal_base_url = parse_url('http://www.example.com');
-  $_SERVER['HTTP_HOST'] = $drupal_base_url['host'];
-  $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] = $_SERVER['PHP_SELF'];
-  $_SERVER['REMOTE_ADDR'] = NULL;
-  $_SERVER['REQUEST_METHOD'] = NULL;
-
-  require_once 'includes/bootstrap.inc';
-  drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
-
-  $feature_id = $arguments['f'];
-
-  if ($feature_id > 0) {
-    tripal_feature_sync_feature($feature_id);
-  }
-  else {
-    print "syncing all features...\n";
-    tripal_feature_sync_features();
-  }
-}
 /**
  *
  */
 function tripal_feature_sync_form() {
 
   $form['description'] = array(
-  '#type' => 'item',
-  '#value' => t("Add feature types, optionally select an organism and " .
+  '#markup' => t("Add feature types, optionally select an organism and " .
      "click the 'Sync all Features' button to create Drupal " .
      "content for features in chado. Only features of the types listed " .
      "below in the Feature Types box will be synced. You may limit the " .
@@ -57,7 +29,7 @@ function tripal_feature_sync_form() {
        "spaces or entered separately on new lines. The names must match " .
        "exactly (spelling and case) with terms in the sequence ontology"),
     '#required'    => TRUE,
-    '#default_value' => variable_get('chado_sync_feature_types', 'gene contig'),
+    '#default_value' => variable_get('chado_sync_feature_types', 'gene mRNA'),
   );
 
   // get the list of organisms
@@ -80,17 +52,49 @@ function tripal_feature_sync_form() {
     '#value' => t('Sync all Features'),
     '#weight' => 3,
   );
+  
+  get_tripal_feature_admin_form_cleanup_set($form);
 
   return $form;
 }
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function get_tripal_feature_admin_form_cleanup_set(&$form) {
+  $form['cleanup'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Clean Up'),
+    '#collapsible' => FALSE,
+    '#collapsed' => FALSE,
+  );
+  $form['cleanup']['description'] = array(
+    '#markup' => t("With Drupal and chado residing in different databases " .
+        "it is possible that nodes in Drupal and features in Chado become " .
+        "\"orphaned\".  This can occur if a feature node in Drupal is " .
+        "deleted but the corresponding chado feature is not and/or vice " .
+        "versa.  The Cleanup function will also remove nodes for features " .
+        "that are not in the list of allowed feature types as specified " .
+        "above.  This is helpful when a feature type needs to be " .
+        "removed but was previously present as Drupal nodes. " .
+        "Click the button below to resolve these discrepancies."),
+    '#weight' => 1,
+  );
+  $form['cleanup']['button'] = array(
+    '#type' => 'submit',
+    '#value' => t('Clean up orphaned features'),
+    '#weight' => 2,
+  );
+}
+
 /**
  *
  */
 function tripal_feature_sync_form_validate($form, &$form_state) {
   $organism_id   = $form_state['values']['organism_id'];
   $feature_types = $form_state['values']['feature_types'];
-
-  // nothing to do
 }
 /**
  *
@@ -101,20 +105,27 @@ function tripal_feature_sync_form_submit($form, &$form_state) {
 
   $organism_id   = $form_state['values']['organism_id'];
   $feature_types = $form_state['values']['feature_types'];
-
-  $job_args = array(0, $organism_id, $feature_types);
-
-  if ($organism_id) {
-    $organism = tripal_core_chado_select('organism', array('genus', 'species'), array('organism_id' => $organism_id));
-    $title = "Sync all features for " . $organism[0]->genus . " " . $organism[0]->species;
-  }
-  else {
-    $title = t('Sync all features for all synced organisms');
+  
+  switch ($form_state['values']['op']) {
+    case  t('Sync all Features') :
+      $job_args = array(0, $organism_id, $feature_types);
+      if ($organism_id) {
+        $organism = tripal_core_chado_select('organism', array('genus', 'species'), array('organism_id' => $organism_id));
+        $title = "Sync all features for " . $organism[0]->genus . " " . $organism[0]->species;
+      }
+      else {
+        $title = t('Sync all features for all synced organisms');
+      }
+      variable_set('chado_sync_feature_types', $feature_types);    
+      tripal_add_job($title, 'tripal_feature', 'tripal_feature_sync_features', $job_args, $user->uid);
+      break;
+      
+    case t('Clean up orphaned features') :
+      $job_args = array();
+      tripal_add_job('Cleanup orphaned features', 'tripal_feature',
+      'tripal_features_cleanup', $job_args, $user->uid);
+      break;
   }
-
-  variable_set('chado_sync_feature_types', $feature_types);
-
-  tripal_add_job($title, 'tripal_feature', 'tripal_feature_sync_features', $job_args, $user->uid);
 }
 /**
  *  
@@ -300,13 +311,13 @@ function tripal_feature_get_feature_url($node, $url_alias = NULL) {
 function tripal_feature_sync_features($max_sync = 0, $organism_id = NULL,
   $feature_types = NULL, $job_id = NULL) {
   
-  $i = 0;
-
+  global $user;
+    
   // get the list of available sequence ontology terms for which
   // we will build drupal pages from features in chado.  If a feature
   // is not one of the specified typse we won't build a node for it.
   if (!$feature_types) {
-    $allowed_types = variable_get('chado_sync_feature_types', 'gene contig');
+    $allowed_types = variable_get('chado_sync_feature_types', 'gene mRNA');
   }
   else {
     $allowed_types = $feature_types;
@@ -315,11 +326,14 @@ function tripal_feature_sync_features($max_sync = 0, $organism_id = NULL,
 
   print "Looking for features of type: $allowed_types\n";
 
-  # TODO: fix the hard-coding of variables in this SQL statement
   $so_terms = split(' ', $allowed_types);
   $where_cvt = "";
+  $args = array();
+  $i = 0;
   foreach ($so_terms as $term) {
-    $where_cvt .= "CVT.name = '$term' OR ";
+    $where_cvt .= "CVT.name = :term$i OR ";
+    $args[":term$i"] = $term;
+    $i++;
   }
   $where_cvt = drupal_substr($where_cvt, 0, drupal_strlen($where_cvt)-3);  # strip trailing 'OR'
 
@@ -327,218 +341,110 @@ function tripal_feature_sync_features($max_sync = 0, $organism_id = NULL,
   // those organisms
   $orgs = tripal_organism_get_synced();
   $where_org = "";
+  $i = 0;
   foreach ($orgs as $org) {
     if ($organism_id) {
       if ($org->organism_id and $org->organism_id == $organism_id) {
-        $where_org .= "F.organism_id = $org->organism_id OR ";
+        $where_org .= "F.organism_id = :org_id$i OR ";
+        $args[":org_id$i"] = $org->organism_id;
       }
     }
     else {
-    if ($org->organism_id) {
-      $where_org .= "F.organism_id = $org->organism_id OR ";
-    }
+      if ($org->organism_id) {
+        $where_org .= "F.organism_id = :org_id$i OR ";
+        $args[":org_id$i"] = $org->organism_id;
+      }
     }
+    $i++;
   }
   $where_org = drupal_substr($where_org, 0, drupal_strlen($where_org)-3);  # strip trailing 'OR'
 
-  // use this SQL statement to get the features that we're going to upload
+  // get the list of features that we will sync
   $sql = "
-    SELECT feature_id 
-    FROM {FEATURE} F 
-      INNER JOIN {Cvterm} CVT ON F.type_id = CVT.cvterm_id 
-      INNER JOIN {CV} on CV.cv_id = CVT.cv_id 
-    WHERE ($where_cvt) AND ($where_org) AND CV.name = 'sequence' 
+    SELECT F.*, CVT.name as cvtname, O.genus, O.species 
+    FROM {feature} F 
+      INNER JOIN {cvterm} CVT ON F.type_id     = CVT.cvterm_id 
+      INNER JOIN {cv} CV      ON CV.cv_id      = CVT.cv_id
+      INNER JOIN {organism} O ON O.organism_id = F.organism_id 
+      LEFT JOIN public.chado_feature CF ON CF.feature_id = F.feature_ID
+    WHERE 
+     ($where_cvt) AND ($where_org) AND CV.name = 'sequence' AND
+     CF.feature_id IS NULL
     ORDER BY feature_id";
-
-  // get the list of features
-  $results = chado_query($sql);
-
-  // load into ids array
-  $count = 0;
-  $ids = array();
-  while ($id = $results->fetchObject()) {
-    $ids[$count] = $id->feature_id;
-    $count++;
-  }
-
-  // make sure our vocabularies are set before proceeding
-  tripal_feature_set_vocabulary();
-
-  // pre-create the SQL statement that will be used to check
-  // if a feature has already been synced.  We skip features
-  // that have been synced
-  $sql = "SELECT * FROM {chado_feature} WHERE feature_id = :feature_id";
+  
+  print_r($sql);
+  print_r($args);
+  $results = chado_query($sql, $args);
+  
 
   // Iterate through features that need to be synced
+  $count = $results->rowCount();
   $interval = intval($count * 0.01);
   if ($interval < 1) {
     $interval = 1;
   }
-  $num_ids = sizeof($ids);
-  $i = 0;
-  foreach ($ids as $feature_id) {
-    // update the job status every 1% features
-    if ($job_id and $i % $interval == 0) {
-      tripal_job_set_progress($job_id, intval(($i/$count)*100));
-    }
-    // if we have a maximum number to sync then stop when we get there
-    // if not then just continue on
-    if ($max_sync and $i == $max_sync) {
-      return '';
-    }
-    if (!db_query($sql, array(':feature_id' => $feature_id))->fetchObject()) {
-
-      # parsing all the features can cause memory overruns
-      # we are not sure why PHP does not clean up the memory as it goes
-      # to avoid this problem we will call this script through an
-      # independent system call
-      print "$i of $num_ids Syncing feature id: $feature_id\n";
-      $cmd = "php " . drupal_get_path('module', 'tripal_feature') . "/includes/tripal_feature.sync_features.inc -f $feature_id -t chado_feature";
-      system($cmd);
-
+  $i = 0;  
+  $transaction = db_transaction();
+  try {
+    //  tripal_feature_set_vocabulary();
+    print "Loading feature $i of $count (0.00%). Memory: " . number_format(memory_get_usage()) . " bytes\r";
+    foreach ($results as $feature) {
+      // update the job status every 1% features
+      if ($job_id and $i % $interval == 0) {
+        $percent = sprintf("%.2f", ($i / $count) * 100);        
+        print "Parsing Line $line_num (" . $percent . "%). Memory: " . number_format(memory_get_usage()) . " bytes.\r";
+        tripal_job_set_progress($job_id, intval(($i/$count)*100));
+      }
+      $new_node = new stdClass();
+      $new_node->type = 'chado_feature';
+      $new_node->uid = $user->uid;
+      $new_node->feature_id = $feature->feature_id;
+      
+      // set these values as they are needed for constructing the title and
+      // the match the names of the fields in the feature form
+      $new_node->organism_id = $feature->organism_id;
+      $new_node->fname = $feature->name;
+      $new_node->uniquename = $feature->uniquename;
+      $new_node->feature_type = $feature->cvtname;
+      
+      node_validate($new_node, $form, $form_state);
+      if (!form_get_errors()) {
+        $node = node_submit($new_node);
+        node_save($node);
+      }
+      else {
+        watchdog('trp-fsync', "Failed to insert feature: %title", array('%title' => $new_node->title), WATCHDOG_ERROR);
+      }
+      
+      // set the taxonomy for this node
+      //  tripal_feature_set_taxonomy($node, $feature_id);
+      
+      $i++;
     }
-    $i++;
   }
-
-  return '';
+  catch (Exception $e) {
+    print "\n"; // make sure we start errors on new line
+    watchdog_exception('trp-fsync', $e);
+    $transaction->rollback();
+    print "FAILED: Rolling back database changes...\n";
+  }
 }
 
+
+
 /**
  *
+ * Remove orphaned drupal nodes
+ *
+ * @param $dummy
+ *   Not Used -kept for backwards compatibility
+ * @param $job_id
+ *   The id of the tripal job executing this function
  *
  * @ingroup tripal_feature
  */
-function tripal_feature_sync_feature($feature_id) {
-  //print "\tSyncing feature $feature_id\n";
+function tripal_features_cleanup($dummy = NULL, $job_id = NULL) {
 
-  global $user;
-  $create_node = 1;   // set to 0 if the node exists and we just sync and not create
-
-  // get the accession prefix
-  $aprefix = variable_get('chado_feature_accession_prefix', 'FID');
-
-  // if we don't have a feature_id then return
-  if (!$feature_id) {
-    drupal_set_message(t("Please provide a feature_id to sync"));
-    return '';
-  }
-
-  // get information about this feature
-  $fsql = "
-    SELECT F.feature_id, F.name, F.uniquename,O.genus, 
-      O.species,CVT.name as cvname,F.residues,F.organism_id 
-    FROM {FEATURE} F 
-      INNER JOIN {Cvterm} CVT ON F.type_id = CVT.cvterm_id 
-      INNER JOIN {Organism} O ON F.organism_id = O.organism_ID 
-    WHERE F.feature_id = :feature_id
-  ";
-  $feature = chado_query($fsql, array(':feature_id' => $feature_id))->fetchObject();
-
-  // get the synonyms for this feature
-  $synsql = "
-    SELECT S.name 
-    FROM {feature_synonym} FS 
-      INNER JOIN {synonym} S on FS.synonym_id = S.synonym_id 
-    WHERE FS.feature_id = :feature_id
-  ";
-  $synonyms = chado_query($synsql, array(':feature_id' => $feature_id));
-
-  // now add these synonyms to the feature object as a single string
-  $synstring = '';
-  while ($synonym = $synonyms->fetchObject()) {
-    $synstring .= "$synonym->name\n";
-  }
-  $feature->synonyms = $synstring;
-
-  // check to make sure that we don't have any nodes with this feature name as a title
-  // but without a corresponding entry in the chado_feature table if so then we want to
-  // clean up that node.  (If a node is found we don't know if it belongs to our feature or
-  // not since features can have the same name/title.)
-  $tsql =  "SELECT * FROM {node} N WHERE title = :title";
-  $cnsql = "SELECT * FROM {chado_feature} WHERE nid = :nid";
-  $nodes = db_query($tsql, array(':title' => $feature->name));
-  // cycle through all nodes that may have this title
-  while ($node = $nodes->fetchObject()) {
-    $feature_nid = db_query($cnsql, array(':nid' => $node->nid))->fetchObject();
-    if (!$feature_nid) {
-      drupal_set_message(t("%feature_id: A node is present but the chado_feature entry is missing... correcting", array('%feature_id' => $feature_id)));
-      node_delete($node->nid);
-    }
-  }
-
-  // check if this feature already exists in the chado_feature table.
-  // if we have a chado feature, we want to check to see if we have a node
-  $cfsql = "SELECT * FROM {chado_feature} WHERE feature_id = :feature_id";
-  // @coder-ignore: don't need to use db_rewrite_sql() since need all nodes regardless of access control
-  $nsql =  "SELECT * FROM {node} N WHERE nid = :nid";
-  $chado_feature = db_query($cfsql, array(':feature_id' => $feature->feature_id))->fetchObject();
-  if ($chado_feature) {
-    drupal_set_message(t("%feature_id: A chado_feature entry exists", array('%feature_id' => $feature_id)));
-    $node = db_query($nsql, array(':nid' => $chado_feature->nid))->fetchObject();
-    if (!$node) {
-      // if we have a chado_feature but not a node then we have a problem and
-      // need to cleanup
-      drupal_set_message(t("%feature_id: The node is missing, but has a chado_feature entry... correcting", array('%feature_id' => $feature_id)));
-      $df_sql = "DELETE FROM {chado_feature} WHERE feature_id = :feature_id";
-      db_query($df_sql, array(':feature_id' => $feature_id));
-    }
-    else {
-      drupal_set_message(t("%feature_id: A corresponding node exists", array('%feature_id' => $feature_id)));
-      $create_node = 0;
-    }
-  }
-
-  // if we've encountered an error then just return.
-  if ($error_msg = db_error()) {
-    //print "$error_msg\n";
-    return '';
-  }
-
-  // if a drupal node does not exist for this feature then we want to
-  // create one.  Note that the node_save call in this block
-  // will call the hook_submit function which
-  if ($create_node) {
-    // get the organism for this feature
-    $sql = "SELECT * FROM {organism} WHERE organism_id = :organism_id";
-    $organism = chado_query($sql, array(':organism_id' => $feature->organism_id))->fetchObject();
-
-    drupal_set_message(t("%feature_id: Creating node $feature->name", array('%feature_id' => $feature_id)));
-    $new_node = new stdClass();
-    $new_node->type = 'chado_feature';
-    $new_node->uid = $user->uid;
-    $new_node->title = "$feature->name, $feature->uniquename ($feature->cvname) $organism->genus $organism->species";
-    $new_node->fname = "$feature->name";
-    $new_node->uniquename = "$feature->uniquename";
-    $new_node->feature_id = $feature->feature_id;
-    $new_node->residues = $feature->residues;
-    $new_node->organism_id = $feature->organism_id;
-    $new_node->feature_type = $feature->cvname;
-    $new_node->synonyms = $feature->synonyms;
-
-    // validate the node and if okay then submit
-    node_validate($new_node);
-    if ($errors = form_get_errors()) {
-      print "Error encountered validating new node. Cannot sync\n";
-      foreach ($errors as $key => $msg) {        
-        watchdog('trp-fsync', "%msg", array('%msg' => $msg), 'error');
-      }
-      exit;
-    }
-    else {
-      $node = node_submit($new_node);
-      node_save($node);
-    }
-  }
-  else {
-    $node = $chado_feature;
-  }
-
-
-  // set the taxonomy for this node
-  drupal_set_message(t("%feature_id ($node->nid): setting taxonomy", array('%feature_id' => $feature_id)));
-  tripal_feature_set_taxonomy($node, $feature_id);
-
-
-  return '';
-}
+  return tripal_core_clean_orphaned_nodes('feature', $job_id);
 
+}

+ 67 - 51
tripal_feature/theme/tripal_feature/tripal_feature_alignments.tpl.php

@@ -60,57 +60,73 @@ $alignments = $feature->all_featurelocs;
 if(count($alignments) > 0){ ?>
   <div id="tripal_feature-alignments-box" class="tripal_feature-info-box tripal-info-box">
     <div class="tripal_feature-info-box-title tripal-info-box-title">Alignments</div>
-    <div class="tripal_feature-info-box-desc tripal-info-box-desc">The following features are aligned to this <b><?php print $feature->type_id->name;?></b></div>
-    <table id="tripal_feature-featurelocs_as_child-table" class="tripal_feature-table tripal-table tripal-table-horz">
-      <tr>
-        <th>Aligned Feature</th>
-        <th>Feature Type</th>
-        <th>Alignment Location</th>
-      </tr><?php
-      $i = 0; 
-      foreach ($alignments as $alignment){
-        $class = 'tripal_feature-table-odd-row tripal-table-odd-row';
-        if ($i % 2 == 0 ) {
-          $class = 'tripal_feature-table-odd-row tripal-table-even-row';
-        } ?>
-        <tr class="<?php print $class ?>">
-          <td><?php 
-            if ($alignment->nid) {
-              print "<a href=\"" . url("node/".$alignment->nid) . "\">".$alignment->name."</a>";
-            } else {
-              print $alignment->name;
-            }?>
-          </td>
-          <td><?php print $alignment->type ?></td>
-          <td><?php  
-            $strand = '.';
-            if ($alignment->strand == -1) {
-              $strand = '-';
-            } 
-            elseif ($alignment->strand == 1) {
-               $strand = '+';
-            } 
-              
-            // if this is a match then make the other location 
-            if($alignment->right_feature){
-              $rstrand = '.';
-              if ($alignment->right_strand == -1) {
-                   $rstrand = '-';
-              } 
-              elseif ($alignment->right_strand == 1) {
-                   $rstrand = '+';
-              }
-              print $feature->name .":". ($alignment->fmin + 1) . ".." . $alignment->fmax . " " . $strand; 
-              print "<br>" . $alignment->name .":". ($alignment->right_fmin + 1) . ".." . $alignment->right_fmax . " " . $rstrand; 
-            }
-            else {
-              print $alignment->name .":". ($alignment->fmin + 1) . ".." . $alignment->fmax . " " . $strand; 
-            }?>
-          </td>
-        </tr> <?php
-        $i++;  
-      } ?>
-    </table>
+    <div class="tripal_feature-info-box-desc tripal-info-box-desc">The following features are aligned</div><?php
+    // the $headers array is an array of fields to use as the colum headers.
+    // additional documentation can be found here
+    // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+    $headers = array('Aligned Feature' ,'Feature Type', 'Alignment Location');
+    
+    // the $rows array contains an array of rows where each row is an array
+    // of values for each column of the table in that row.  Additional documentation
+    // can be found here:
+    // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+    $rows = array();
+    
+    foreach ($alignments as $alignment){
+      $feature_name = $alignment->name;
+      if (property_exists($alignment, 'nid')) {
+        $feature_name = l($feature_name, "node/" . $alignment->nid);
+      }
+      $feature_loc = '';
+      $strand = '.';
+      if ($alignment->strand == -1) {
+        $strand = '-';
+      } 
+      elseif ($alignment->strand == 1) {
+         $strand = '+';
+      } 
+      // if this is a match then make the other location 
+      if(property_exists($alignment, 'right_feature')){
+        $rstrand = '.';
+        if ($alignment->right_strand == -1) {
+          $rstrand = '-';
+        } 
+        elseif ($alignment->right_strand == 1) {
+          $rstrand = '+';
+        }
+        $feature_loc = $feature->name .":". ($alignment->fmin + 1) . ".." . $alignment->fmax . " " . $strand; 
+        $feature_loc .= "<br>" . $alignment->name .":". ($alignment->right_fmin + 1) . ".." . $alignment->right_fmax . " " . $rstrand; 
+      }
+      else {
+        $feature_loc = $alignment->name .":". ($alignment->fmin + 1) . ".." . $alignment->fmax . " " . $strand; 
+      }
+      
+      $rows[] = array(
+        $feature_name,
+        $alignment->type,
+        $feature_loc
+      );
+    } 
+    
+    // the $table array contains the headers and rows array as well as other
+    // options for controlling the display of the table.  Additional
+    // documentation can be found here:
+    // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+    $table = array(
+      'header' => $headers,
+      'rows' => $rows,
+      'attributes' => array(
+        'id' => 'tripal_feature-table-alignments',
+      ),
+      'sticky' => FALSE,
+      'caption' => '',
+      'colgroups' => array(),
+      'empty' => '',
+    );
+    
+    // once we have our table array structure defined, we call Drupal's theme_table()
+    // function to generate the table.
+    print theme_table($table); ?>
   </div><?php
 }
 

+ 46 - 41
tripal_feature/theme/tripal_feature/tripal_feature_analyses.tpl.php

@@ -1,51 +1,56 @@
 <?php
 $feature = $variables['node']->feature;
-
-// expand the feature object to include the libraries from the library_feature
-// table in chado.
-$feature = tripal_core_expand_chado_vars($feature,'table','analysisfeature');
-
-// get the references. if only one reference exists then we want to convert
-// the object into an array, otherwise the value is an array
+$options = array('return_array' => 1);
+$feature = tripal_core_expand_chado_vars($feature, 'table', 'analysisfeature', $options);
 $analyses = $feature->analysisfeature;
-if (!$analyses) {
-   $analyses = array();
-} 
-elseif (!is_array($analyses)) { 
-   $analyses = array($analyses); 
-}
 
-// don't show this page if there are no libraries
+// don't show this page if there are no analyses
 if (count($analyses) > 0) { ?>
   <div id="tripal_feature-analyses-box" class="tripal_feature-info-box tripal-info-box">
     <div class="tripal_feature-info-box-title tripal-info-box-title">Analyses</div>
-    <div class="tripal_feature-info-box-desc tripal-info-box-desc">This <?php print $feature->type_id->name ?> is derived from or has results from the following analyses</div>
-    <table id="tripal_feature-analyses-table" class="tripal_feature-table tripal-table tripal-table-horz">
-      <tr>
-        <th>Analysis Name</th>
-        <th>Date Performed</th>
-      </tr> <?php
-      $i = 0; 
-      foreach ($analyses as $analysis) {
-        $class = 'tripal-table-odd-row';
-        if ($i % 2 == 0 ) {
-           $class = 'tripal-table-even-row';
-        } ?>
-        <tr class="<?php print $class ?>">
-          <td><?php 
-            $nid = chado_get_node_id('analysis', $analysis->analysis_id->analysis_id);
-            if ($nid) {
-               print "<a href=\"". url("node/".$nid) . "\">".$analysis->analysis_id->name."</a>";
-            } else {
-               print $analysis->analysis_id->name;
-            } ?>
-          </td>
-          <td> <?php print preg_replace('/\d\d:\d\d:\d\d/', '',  $analysis->analysis_id->timeexecuted) ?>
-          </td>
-        </tr> <?php
-        $i++;  
-      } ?>
-    </table> 
+    <div class="tripal_feature-info-box-desc tripal-info-box-desc">This <?php print $feature->type_id->name ?> is derived from or has results from the following analyses</div> <?php
+    
+    // the $headers array is an array of fields to use as the colum headers.
+    // additional documentation can be found here
+    // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+    $headers = array('Analysis Name' ,'Date Performed');
+    
+    // the $rows array contains an array of rows where each row is an array
+    // of values for each column of the table in that row.  Additional documentation
+    // can be found here:
+    // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+    $rows = array();
+    
+    foreach ($analyses as $analysis) {
+      $analysis_name = $analysis->analysis_id->name;
+      if (property_exists($analysis->analysis_id, 'nid')) {
+        $analysis_name = l($analysis_name, "node/" . $analysis->analysis_id->nid);
+      } 
+      $rows[] = array(
+        $analysis_name,
+        preg_replace('/\d\d:\d\d:\d\d/', '',  $analysis->analysis_id->timeexecuted),
+      );
+    }
+     
+    // the $table array contains the headers and rows array as well as other
+    // options for controlling the display of the table.  Additional
+    // documentation can be found here:
+    // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+    $table = array(
+      'header' => $headers,
+      'rows' => $rows,
+      'attributes' => array(
+        'id' => 'tripal_feature-table-analyses',
+      ),
+      'sticky' => FALSE,
+      'caption' => '',
+      'colgroups' => array(),
+      'empty' => '',
+    );
+    
+    // once we have our table array structure defined, we call Drupal's theme_table()
+    // function to generate the table.
+    print theme_table($table); ?>
   </div><?php 
 } 
 

+ 1 - 3
tripal_feature/theme/tripal_feature/tripal_feature_base.tpl.php

@@ -43,12 +43,10 @@ $feature  = $variables['node']->feature;  ?>
     $feature->type_id->name
   );
   // Organism row
+  $organism = $feature->organism_id->genus ." " . $feature->organism_id->species ." (" .$feature->organism_id->common_name .")";
   if ($feature->organism_id->nid) {
     $organism = l("<i>" . $feature->organism_id->genus . " " . $feature->organism_id->species . "</i> (" .$feature->organism_id->common_name .")", "node/".$feature->organism_id->nid, array('html' => TRUE));
   } 
-  else {
-    $organism = $feature->organism_id->genus ." " . $feature->organism_id->species ." (" .$feature->organism_id->common_name .")";
-  }
   $rows[] = array(
     array(
       'data' => 'Organism',

+ 0 - 28
tripal_feature/theme/tripal_feature/tripal_feature_featureloc_sequences.tpl.php

@@ -1,28 +0,0 @@
-<?php
-// get the featurelocs for this feature. If the variable is not already 
-// expanded then do so
-$feature = $variables['node']->feature;
-
-$feature = tripal_core_expand_chado_vars($feature, 'table', 'featureloc');
-$featurelocs = $feature->featureloc;
-
-// get the featurelocs. if only one featureloc exists then we want to convert
-// the object into an array, otherwise the value is an array
-$ffeaturelocs = $feature->featureloc->feature_id;
-if (!$ffeaturelocs) {
-   $ffeaturelocs = array();
-} elseif (!is_array($ffeaturelocs)) { 
-   $ffeaturelocs = array($ffeaturelocs); 
-}
-$featureloc_sequences = tripal_feature_load_featureloc_sequences ($feature->feature_id,$ffeaturelocs);
-
-if(count($featureloc_sequences) > 0){
-   foreach($featureloc_sequences as $src => $attrs){ ?>
-       <div id="tripal_feature-<?php print $attrs['type']?>-box" class="tripal_feature-info-box tripal-info-box">
-         <div class="tripal_feature-info-box-title tripal-info-box-title">Annotated Sequence</div>
-         <div class="tripal_feature-info-box-desc tripal-info-box-desc"></div>
-            <?php print $attrs['formatted_seq'] ?>
-       </div>
-   <?php } 
-}?>
-

+ 1 - 2
tripal_feature/theme/tripal_feature/tripal_feature_featurepos.tpl.php

@@ -1,6 +1,4 @@
 <?php
-$feature = $variables['node']->feature;
-$map_positions = array();
 
 // expand the feature object to include the records from the featurepos table
 // specify the number of features to show by default and the unique pager ID
@@ -8,6 +6,7 @@ $num_results_per_page = 25;
 $featurepos_pager_id = 0;
 
 // get the maps associated with this feature
+$feature = $variables['node']->feature;
 $options = array(  
   'return_array' => 1,
   'order_by' => array('map_feature_id' => 'ASC'),

+ 48 - 35
tripal_feature/theme/tripal_feature/tripal_feature_phenotypes.tpl.php

@@ -1,8 +1,6 @@
 <?php
+// expand the feature object to include the phenotypes from the feature_phenotypes table in chado.
 $feature = $variables['node']->feature;
-
-// expand the feature object to include the phenotypes from the feature_phenotypes 
-// table in chado.
 $options = array(
   'return_array' => 1,
   'include_fk' => array(
@@ -17,40 +15,55 @@ $options = array(
 $feature = tripal_core_expand_chado_vars($feature, 'table', 'feature_phenotype', $options);
 $feature_phenotypes = $feature->feature_phenotype;
 
-// expand the text fields
-$options = array('return_array' => 1);
-$feature = tripal_core_expand_chado_vars($feature, 'field', 'phenotype.value', $options);
-$feature = tripal_core_expand_chado_vars($feature, 'field', 'phenotype.uniquename', $options);
-$feature = tripal_core_expand_chado_vars($feature, 'field', 'phenotype.name', $options);
-
+if(count($feature_phenotypes) > 0){ 
+  
+  // expand the text fields
+  $options = array('return_array' => 1);
+  $feature = tripal_core_expand_chado_vars($feature, 'field', 'phenotype.value', $options);
+  $feature = tripal_core_expand_chado_vars($feature, 'field', 'phenotype.uniquename', $options);
+  $feature = tripal_core_expand_chado_vars($feature, 'field', 'phenotype.name', $options); ?>
 
-if(count($feature_phenotypes) > 0){ ?>
   <div id="tripal_feature-phenotypes-box" class="tripal_feature-info-box tripal-info-box">
     <div class="tripal_feature-info-box-title tripal-info-box-title">Phenotypes</div>
-    <div class="tripal_feature-info-box-desc tripal-info-box-desc">The feature is associated with the following phenotypes</div>
+    <div class="tripal_feature-info-box-desc tripal-info-box-desc">The feature is associated with the following phenotypes</div><?php
 
-    <table id="tripal_feature-phenotypes-table" class="tripal_feature-table tripal-table tripal-table-horz">
-      <tr>   
-        <th>Attribute</th>     
-        <th>Observed Unit</th>               
-        <th>Value</th>
-        <th>Evidence Type</th>
-      </tr> <?php
-      $i = 0; 
-      foreach ($feature_phenotypes as $index => $feature_phenotype){
-        $phenotype = $feature_phenotype->phenotype_id;
-        $class = 'tripal-table-odd-row';
-        if($i % 2 == 0 ){
-           $class = 'tripal-table-even-row';
-        } ?>
-        <tr class="<?php print $class ?>">
-          <td><?php print $phenotype->attr_id->name?></td>
-          <td><?php print $phenotype->observable_id->name?></td>          
-          <td><?php print $phenotype->cvalue_id ? $phenotype->cvalue_id->name : $phenotype->value ?></td>
-          <td><?php print $phenotype->assay_id ?></td>
-        </tr> <?php
-        $i++;  
-      } ?>
-    </table>
-  </div><?php
+    // the $headers array is an array of fields to use as the colum headers.
+    // additional documentation can be found here
+    // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+    $headers = array('Attribute', 'Observed Unit', 'Value', 'Evidence Type');
+    
+    // the $rows array contains an array of rows where each row is an array
+    // of values for each column of the table in that row.  Additional documentation
+    // can be found here:
+    // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+    $rows = array();
+    foreach ($feature_phenotypes as $index => $feature_phenotype){
+      $phenotype = $feature_phenotype->phenotype_id;
+      $rows[] = array(
+        $phenotype->attr_id->name,
+        $phenotype->observable_id->name,
+        $phenotype->cvalue_id ? $phenotype->cvalue_id->name : $phenotype->value,
+        $phenotype->assay_id
+      );
+    } 
+    // the $table array contains the headers and rows array as well as other
+    // options for controlling the display of the table.  Additional
+    // documentation can be found here:
+    // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+    $table = array(
+      'header' => $headers,
+      'rows' => $rows,
+      'attributes' => array(
+        'id' => 'tripal_feature-table-phenotypes',
+      ),
+      'sticky' => FALSE,
+      'caption' => '',
+      'colgroups' => array(),
+      'empty' => '',
+    );
+    
+    // once we have our table array structure defined, we call Drupal's theme_table()
+    // function to generate the table.
+    print theme_table($table); ?>
+  </div> <?php
 }

+ 40 - 24
tripal_feature/theme/tripal_feature/tripal_feature_properties.tpl.php

@@ -1,36 +1,52 @@
 <?php
 
-$feature = $node->feature;
+$feature = $variables['node']->feature;
 $options = array('return_array' => 1);
 $feature = tripal_core_expand_chado_vars($feature, 'table', 'featureprop', $options);
 $properties = $feature->featureprop;
 
-
 if(count($properties) > 0){ ?>
 
   <div id="tripal_feature-properties-box" class="tripal_feature-info-box tripal-info-box">
-    <div class="tripal_feature-info-box-title tripal-info-box-title">Properties</div>
-    <div class="tripal_feature-info-box-desc tripal-info-box-desc">Properties for the feature '<?php print $node->feature->name ?>' include:</div>
+    <div class="tripal_feature-info-box-title tripal-info-box-title">Properties</div> <?php
 
-    <table class="tripal_feature-table tripal-table tripal-table-horz">
-    <tr>
-      <th>Property Name</th>
-      <th>Value</th>
-    </tr>
-	  <?php	// iterate through each property
-		  $i = 0;
-		  foreach ($properties as $result){
-		    $result = tripal_core_expand_chado_vars($result,'field','featureprop.value');
-		    $class = 'tripal_feature-table-odd-row tripal-table-odd-row';
-        if($i % 2 == 0 ){
-           $class = 'tripal_feature-table-odd-row tripal-table-even-row';
-        } ?>
-			  <tr class="<?php print $class ?>">
-  			  <td><?php print ucfirst(preg_replace('/_/', ' ', $result->type_id->name)) ?></td>
-  			  <td><?php print urldecode($result->value) ?></td>
-  			</tr> <?php
-			  $i++;
-		  } ?>
-		</table>
+    // the $headers array is an array of fields to use as the colum headers.
+    // additional documentation can be found here
+    // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+    $headers = array('Property Name', 'Value');
+    
+    // the $rows array contains an array of rows where each row is an array
+    // of values for each column of the table in that row.  Additional documentation
+    // can be found here:
+    // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+    $rows = array();
+    
+    foreach ($properties as $property){
+      $property = tripal_core_expand_chado_vars($property,'field','featureprop.value');
+      $rows[] = array(
+        ucfirst(preg_replace('/_/', ' ', $property->type_id->name)),
+        urldecode($property->value)
+      );
+    }
+     
+    // the $table array contains the headers and rows array as well as other
+    // options for controlling the display of the table.  Additional
+    // documentation can be found here:
+    // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+    $table = array(
+      'header' => $headers,
+      'rows' => $rows,
+      'attributes' => array(
+        'id' => 'tripal_feature-table-properties',
+      ),
+      'sticky' => FALSE,
+      'caption' => '',
+      'colgroups' => array(),
+      'empty' => '',
+    );
+    
+    // once we have our table array structure defined, we call Drupal's theme_table()
+    // function to generate the table.
+    print theme_table($table); ?>
   </div> <?php
 }

+ 57 - 40
tripal_feature/theme/tripal_feature/tripal_feature_references.tpl.php

@@ -2,7 +2,7 @@
 $feature = $variables['node']->feature;
 $references = array();
 
-// First, get the dbxref record from feature recrod itself if one exists
+// First, get the dbxref record from feature record itself if one exists
 if ($feature->dbxref_id) {
   $feature->dbxref_id->is_primary = 1;  // add this new property so we know it's the primary reference
   $references[] = $feature->dbxref_id;
@@ -28,45 +28,62 @@ if (count($feature_dbxrefs) > 0 ) {
 if(count($references) > 0){ ?>
   <div id="tripal_feature-references-box" class="tripal_feature-info-box tripal-info-box">
     <div class="tripal_feature-info-box-title tripal-info-box-title">Cross References</div>
-    <div class="tripal_feature-info-box-desc tripal-info-box-desc">External references for this <?php print $feature->type_id->name ?></div>
-    <table id="tripal_feature-references-table" class="tripal_feature-table tripal-table tripal-table-horz">
-      <tr>
-        <th>Dababase</th>
-        <th>Accession</th>
-      </tr> <?php
-      $i = 0; 
-      foreach ($references as $dbxref){ 
-        if($dbxref_id->db_id->name == 'GFF_source'){
-           continue;  // skip the GFF_source entry as this is just needed for the GBrowse chado adapter
-        }
-        $class = 'tripal_feature-table-odd-row tripal-table-odd-row';
-        if($i % 2 == 0 ){
-           $class = 'tripal_feature-table-even-row tripal-table-even-row';
-        } ?>
-        <tr class="<?php print $class ?>">
-          <td> <?php 
-            if ($dbxref->db_id->url) { 
-              print l($dbxref->db_id->name, $dbxref->db_id->url);
-            } 
-            else { 
-              print $dbxref->db_id->name; 
-            } ?>
-          </td>
-          <td> <?php 
-            if ($dbxref->db_id->urlprefix) { 
-              print l($dbxref->accession, $dbxref->db_id->urlprefix.$dbxref->accession);
-            } 
-            else { 
-              print $dbxref->accession; 
-            }
-            if ($dbxref->is_primary) {
-              print " <i>(primary cross-reference)</i>";
-            } ?>
-          </td>
-        </tr> <?php
-        $i++;  
-      } ?>
-    </table>
+    <div class="tripal_feature-info-box-desc tripal-info-box-desc">External references for this <?php print $feature->type_id->name ?></div><?php
+     
+    // the $headers array is an array of fields to use as the colum headers.
+    // additional documentation can be found here
+    // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+    $headers = array('Dababase', 'Accession');
+    
+    // the $rows array contains an array of rows where each row is an array
+    // of values for each column of the table in that row.  Additional documentation
+    // can be found here:
+    // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+    $rows = array();
+ 
+    foreach ($references as $dbxref){
+    
+      // skip the GFF_source entry as this is just needed for the GBrowse chado adapter 
+      if ($dbxref->db_id->name == 'GFF_source'){
+         continue;  
+      } 
+      $dbname = $dbxref->db_id->name; 
+      if ($dbxref->db_id->url) { 
+        $dbname = l($dbname, $dbxref->db_id->url, array('attributes' => array('target' => '_blank')));
+      } 
+      
+      $accession = $dbxref->accession; 
+      if ($dbxref->db_id->urlprefix) { 
+        $accession = l($accession, $dbxref->db_id->urlprefix . $dbxref->accession, array('attributes' => array('target' => '_blank')));
+      } 
+      if (property_exists($dbxref, 'is_primary')) {
+        $accession .= " <i>(primary cross-reference)</i>";
+      }
+      $rows[] = array(
+        $dbname,
+        $accession
+      );
+    } 
+    
+    // the $table array contains the headers and rows array as well as other
+    // options for controlling the display of the table.  Additional
+    // documentation can be found here:
+    // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+    $table = array(
+      'header' => $headers,
+      'rows' => $rows,
+      'attributes' => array(
+        'id' => 'tripal_feature-table-references',
+      ),
+      'sticky' => FALSE,
+      'caption' => '',
+      'colgroups' => array(),
+      'empty' => '',
+    );
+    
+    // once we have our table array structure defined, we call Drupal's theme_table()
+    // function to generate the table.
+    print theme_table($table); ?>
   </div><?php 
 }?>
 

+ 116 - 64
tripal_feature/theme/tripal_feature/tripal_feature_relationships.tpl.php

@@ -22,73 +22,125 @@ $all_relationships = $feature->all_relationships;
 $object_rels = $all_relationships['object'];
 $subject_rels = $all_relationships['subject'];
 
-if (count($object_rels) > 0 or count($subject_rels) > 0) {
-?>
+if (count($object_rels) > 0 or count($subject_rels) > 0) { ?>
   <div id="tripal_feature-relationships-box" class="tripal_feature-info-box tripal-info-box">
     <div class="tripal_feature-info-box-title tripal-info-box-title">Relationships</div>
     <div class="tripal_feature-info-box-desc tripal-info-box-desc"></div> <?php
+    // first add in the subject relationships.  
+    foreach ($subject_rels as $rel_type => $rels){
+      foreach ($rels as $obj_type => $objects){ ?>
+        <p>This <?php print $feature->type_id->name;?> is <?php print $rel_type ?> the following <b><?php print $obj_type ?></b> feature(s): <?php
+         
+        // the $headers array is an array of fields to use as the colum headers.
+        // additional documentation can be found here
+        // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+        $headers = array('Feature Name' ,'Unique Name', 'Species', 'Type');
+        
+        // the $rows array contains an array of rows where each row is an array
+        // of values for each column of the table in that row.  Additional documentation
+        // can be found here:
+        // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+        $rows = array();
+        
+        foreach ($objects as $object){
+          // link the feature to it's node
+          $feature_name = $object->record->object_id->name;
+          if (property_exists($object->record, 'nid')) {
+            $feature_name = "<a href=\"" . url("node/" . $object->record->nid) . "\" target=\"_blank\">" . $object->record->object_id->name . "</a>";
+          }
+          // link the organism to it's node
+          $organism = $object->record->object_id->organism_id;
+          $organism_name = $organism->genus ." " . $organism->species;
+          if (property_exists($organism, 'nid')) {
+            $organism_name = l("<i>" . $organism->genus . " " . $organism->species . "</i>", "node/" . $organism->nid, array('html' => TRUE));
+          }
+          $rows[] = array(
+            $feature_name, 
+            $object->record->object_id->uniquename,
+            $organism_name,
+            $object->record->object_id->type_id->name,
+          ); 
+         } 
+         // the $table array contains the headers and rows array as well as other
+         // options for controlling the display of the table.  Additional
+         // documentation can be found here:
+         // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+         $table = array(
+           'header' => $headers,
+           'rows' => $rows,
+           'attributes' => array(
+             'id' => 'tripal_feature-table-relationship-object',
+           ),
+           'sticky' => FALSE,
+           'caption' => '',
+           'colgroups' => array(),
+           'empty' => '',
+         );
+         
+         // once we have our table array structure defined, we call Drupal's theme_table()
+         // function to generate the table.
+         print theme_table($table); ?>
+         </p>
+         <br><?php
+       }
+    }
     
-      // first add in the subject relationships.  
-      foreach ($subject_rels as $rel_type => $rels){
-         foreach ($rels as $obj_type => $objects){?>           
-           <p>This <?php print $feature->type_id->name;?> is <?php print $rel_type ?> the following <b><?php print $obj_type ?></b> feature(s):
-           <table id="tripal_feature-relationships_as_object-table" class="tripal_feature-table tripal-table tripal-table-horz">
-             <tr>
-               <th>Feature Name</th>
-               <th>Unique Name</th>
-               <th>Species</th>
-               <th>Type</th>
-             </tr> <?php
-             foreach ($objects as $object){ ?>
-               <tr>
-                 <td><?php 
-                    if ($object->record->nid) {
-                      print "<a href=\"" . url("node/" . $object->record->nid) . "\" target=\"_blank\">" . $object->record->object_id->name . "</a>";
-                    }
-                    else {
-                      print $object->record->object_id->name;
-                    } ?>
-                 </td>
-                 <td><?php print $object->record->object_id->uniquename ?></td>
-                 <td><?php print $object->record->object_id->organism_id->genus . " " . $object->record->object_id->organism_id->species; ?></td>
-                 <td><?php print $object->record->object_id->type_id->name ?></td>                 
-               </tr> <?php
-             } ?>
-             </table>
-             </p><br><?php
-         }
-      }
-      
-      // second add in the object relationships.  
-      foreach ($object_rels as $rel_type => $rels){
-         foreach ($rels as $subject_type => $subjects){?>
-           <p>The following <b><?php print $subjects[0]->record->subject_id->type_id->name ?></b> feature(s) are <?php print $rel_type ?> this <?php print $feature->type_id->name;?>:
-           <table id="tripal_feature-relationships_as_object-table" class="tripal_feature-table tripal-table tripal-table-horz">
-             <tr>
-               <th>Feature Name</th>
-               <th>Unique Name</th>
-               <th>Species</th>
-               <th>Type</th>
-             </tr> <?php
-             foreach ($subjects as $subject){ ?>
-               <tr>
-                 <td><?php 
-                    if ($subject->record->nid) {
-                      print "<a href=\"" . url("node/" . $subject->record->nid) . "\" target=\"_blank\">" . $subject->record->subject_id->name . "</a>";
-                    }
-                    else {
-                      print $subject->record->subject_id->name;
-                    } ?>
-                 </td>
-                 <td><?php print $subject->record->subject_id->uniquename ?></td>
-                 <td><?php print $subject->record->subject_id->organism_id->genus . " " . $subject->record->subject_id->organism_id->species; ?></td>
-                 <td><?php print $subject->record->subject_id->type_id->name ?></td>                 
-               </tr> <?php
-             } ?>
-             </table>
-             </p><br><?php
-         }
-      }
-    ?>
+    // second add in the object relationships.  
+    foreach ($object_rels as $rel_type => $rels){
+      foreach ($rels as $subject_type => $subjects){?>
+        <p>The following <b><?php print $subjects[0]->record->subject_id->type_id->name ?></b> feature(s) are <?php print $rel_type ?> this <?php print $feature->type_id->name;?>: <?php 
+        // the $headers array is an array of fields to use as the colum headers.
+        // additional documentation can be found here
+        // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+        $headers = array('Feature Name' ,'Unique Name', 'Species', 'Type');
+        
+        // the $rows array contains an array of rows where each row is an array
+        // of values for each column of the table in that row.  Additional documentation
+        // can be found here:
+        // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+        $rows = array();
+        
+        foreach ($subjects as $subject){
+          // link the feature to it's node
+          $feature_name = $subject->record->subject_id->name;
+          if (property_exists($subject->record, 'nid')) {
+            $feature_name = "<a href=\"" . url("node/" . $subject->record->nid) . "\" target=\"_blank\">" . $subject->record->subject_id->name . "</a>";
+          }
+          // link the organism to it's node
+          $organism = $subject->record->subject_id->organism_id;
+          $organism_name = $organism->genus ." " . $organism->species;
+          if (property_exists($organism, 'nid')) {
+            $organism_name = l("<i>" . $organism->genus . " " . $organism->species . "</i>", "node/" . $organism->nid, array('html' => TRUE));
+          }
+          $rows[] = array(
+            $feature_name, 
+            $subject->record->subject_id->uniquename,
+            $organism_name,
+            $subject->record->subject_id->type_id->name,
+          ); 
+         } 
+         // the $table array contains the headers and rows array as well as other
+         // options for controlling the display of the table.  Additional
+         // documentation can be found here:
+         // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+         $table = array(
+           'header' => $headers,
+           'rows' => $rows,
+           'attributes' => array(
+             'id' => 'tripal_feature-table-relationship-subject',
+           ),
+           'sticky' => FALSE,
+           'caption' => '',
+           'colgroups' => array(),
+           'empty' => '',
+         );
+         
+         // once we have our table array structure defined, we call Drupal's theme_table()
+         // function to generate the table.
+         print theme_table($table); ?>
+         </p>
+         <br><?php
+       }
+    }?>
   </div> <?php
 }

+ 42 - 7
tripal_feature/theme/tripal_feature/tripal_feature_sequence.tpl.php

@@ -1,4 +1,25 @@
 <?php
+/*
+ * There are two ways that sequences can be displayed.  They can come from the 
+ * feature.residues column or they can come from an alignment with another feature.  
+ * This template will show both or one or the other depending on the data available.
+ * 
+ * For retreiving the sequence from an alignment we would typically make a call to
+ * tripal_core_expand_chado_vars function.  For example, to retrieve all
+ * of the featurelocs in order to get the sequences needed for this template, the
+ * following function call would be made:
+ *
+ *   $feature = tripal_core_expand_chado_vars($feature,'table','featureloc');
+ *
+ * Then all of the sequences would need to be retreived from the alignments and
+ * formatted for display below.  However, to simplify this template, this has already
+ * been done by the tripal_feature module and the sequences are made available in
+ * the variable:
+ *
+ *   $feature->featureloc_sequences
+ *
+ */
+
 $feature = $variables['node']->feature;
 
 // we don't want to get the sequence for traditionally large types. They are
@@ -13,14 +34,28 @@ if(strcmp($feature->type_id->name,'scaffold') !=0 and
   $residues = $feature->residues;
 } 
 
-if ($residues) { ?>
+// get the sequence derived from alignments
+$feature = $variables['node']->feature;
+$featureloc_sequences = $feature->featureloc_sequences;
+
+if ($residues or count($featureloc_sequences) > 0) { ?>
   <div id="tripal_feature-sequence-box" class="tripal_feature-info-box tripal-info-box">
-    <div class="tripal_feature-info-box-title tripal-info-box-title">Sequence</div>
-    <div class="tripal_feature-info-box-desc tripal-info-box-desc">The sequence for this <?php print $feature->type_id->name; ?> </div>
+  <div class="tripal_feature-info-box-title tripal-info-box-title">Sequence</div>
+  <div class="tripal_feature-info-box-desc tripal-info-box-desc"></div> <?php
+  
+  // show the alignment sequences first as they are colored with child features
+  if(count($featureloc_sequences) > 0){
+    foreach($featureloc_sequences as $src => $attrs){ 
+      print $attrs['formatted_seq'];
+    } 
+  }
+  
+  // add in the residues if they are present
+  if ($residues) { ?>
     <pre id="tripal_feature-sequence-residues"><?php 
       // format the sequence to break every 100 residues
       print preg_replace("/(.{50})/","\\1<br>",$feature->residues); ?>  
-    </pre>
-  </div> <?php
-}
-
+    </pre> <?php 
+  } ?>
+  </div> <?php 
+}

+ 11 - 43
tripal_feature/theme/tripal_feature/tripal_feature_teaser.tpl.php

@@ -1,45 +1,13 @@
 <?php
+$node = $variables['node'];
+$feature = $variables['node']->feature; ?>
 
-$feature  = $variables['node']->feature;
-
-?>
-<div id="tripal_feature-base-box" class="tripal_feature-info-box tripal-info-box">
-  <div class="tripal_feature-info-box-title tripal-info-box-title">Feature Details</div>
-  <div class="tripal_feature-info-box-desc tripal-info-box-desc"></div>
-
-   <?php if(strcmp($feature->is_obsolete,'t')==0){ ?>
-      <div class="tripal_feature-obsolete">This feature is obsolete</div>
-   <?php }?>
-   <table id="tripal_feature-base-table" class="tripal_feature-table tripal-table tripal-table-vert">
-      <tr class="tripal_feature-table-odd-row tripal-table-even-row">
-        <th>Name</th>
-        <td><?php print $feature->name; ?></td>
-      </tr>
-      <tr class="tripal_feature-table-odd-row tripal-table-odd-row">
-        <th nowrap>Unique Name</th>
-        <td><?php print $feature->uniquename; ?></td>
-      </tr>
-      <tr class="tripal_feature-table-odd-row tripal-table-even-row">
-        <th>Internal ID</th>
-        <td><?php print $feature->feature_id; ?></td>
-      </tr>
-      <tr class="tripal_feature-table-odd-row tripal-table-odd-row">
-        <th>Length</th>
-        <td><?php print $feature->seqlen ?></td>
-      </tr>
-      <tr class="tripal_feature-table-odd-row tripal-table-even-row">
-        <th>Type</th>
-        <td><?php print $feature->type_id->name; ?></td>
-      </tr>
-      <tr class="tripal_feature-table-odd-row tripal-table-odd-row">
-        <th>Organism</th>
-        <td>
-          <?php if ($feature->organism_id->nid) { 
-      	   print "<a href=\"".url("node/".$feature->organism_id->nid)."\">".$feature->organism_id->genus ." " . $feature->organism_id->species ." (" .$feature->organism_id->common_name ." )</a>";      	 
-          } else { 
-            print $feature->organism_id->genus ." " . $feature->organism_id->species ." (" .$feature->organism_id->common_name .")";
-          } ?>
-        </td>
-     	</tr>           	                                
-   </table>
-</div>
+<div class="tripal_feature-teaser tripal-teaser"> 
+  <div class="tripal-feature-teaser-title tripal-teaser-title"><?php 
+    print l($node->title, "node/$node->nid", array('html' => TRUE));?>
+  </div>
+  <div class="tripal-feature-teaser-text tripal-teaser-text"><?php 
+    print $node->title;
+    print "... " . l("[more]", "node/$node->nid"); ?>
+  </div>
+</div>

+ 52 - 34
tripal_feature/theme/tripal_feature/tripal_feature_terms.tpl.php

@@ -1,14 +1,9 @@
 <?php
 
 $feature = $node->feature;
-$feature = tripal_core_expand_chado_vars($feature, 'table', 'feature_cvterm');
-
+$options = array('return_array' => 1);
+$feature = tripal_core_expand_chado_vars($feature, 'table', 'feature_cvterm', $options);
 $terms = $feature->feature_cvterm;
-if (!$terms) {
-  $terms = array();
-} elseif (!is_array($terms)) { 
-  $terms = array($terms); 
-}
 
 // order the terms by CV
 $s_terms = array();
@@ -20,33 +15,56 @@ if (count($s_terms) > 0) { ?>
   <div id="tripal_feature-terms-box" class="tripal_feature-info-box tripal-info-box">
     <div class="tripal_feature-info-box-title tripal-info-box-title">Annotated Terms</div>
     <div class="tripal_feature-info-box-desc tripal-info-box-desc">The following terms have been associated with this <?php print $node->feature->type_id->name ?>:</div>  <?php
+    
     // iterate through each term
-    foreach ($s_terms as $cv => $terms) {  ?>
-      <p><?php print ucwords(preg_replace('/_/', ' ', $cv)) ?></p>
-      <table class="tripal_feature-table tripal-table tripal-table-horz">
-        <tr>
-          <th>Term</th>
-          <th>Definition</th>
-        </tr> <?php
-        $i = 0;
-        foreach ($terms as $term) { 
-          $class = 'tripal_feature-table-odd-row tripal-table-odd-row';
-          if($i % 2 == 0 ){
-            $class = 'tripal_feature-table-even-row tripal-table-even-row';
-          }
-          $accession = $term->cvterm_id->dbxref_id->accession;
-          if (is_numeric($term->cvterm_id->dbxref_id->accession)) {
-            $accession = $term->cvterm_id->dbxref_id->db_id->name . ":" . $term->cvterm_id->dbxref_id->accession;
-          }
-          if ($term->cvterm_id->dbxref_id->db_id->urlprefix) {
-            $accession =  "<a href=\"" . $term->cvterm_id->dbxref_id->db_id->urlprefix . "$accession\" target=\"_blank\">$accession</a>";
-          } ?>
-          <tr class="<?php print $class ?>">
-            <td><?php print $accession ?></td>
-            <td><?php print $term->cvterm_id->name ?></td>
-          </tr> <?php
-        } ?>
-      </table> <?php
-  } ?>
+    $i = 0;
+    foreach ($s_terms as $cv => $terms) {  
+      // the $headers array is an array of fields to use as the colum headers.
+      // additional documentation can be found here
+      // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+      $headers = array('Term', 'Definition');
+      
+      // the $rows array contains an array of rows where each row is an array
+      // of values for each column of the table in that row.  Additional documentation
+      // can be found here:
+      // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+      $rows = array();
+      
+      foreach ($terms as $term) { 
+
+        $accession = $term->cvterm_id->dbxref_id->accession;
+        if (is_numeric($term->cvterm_id->dbxref_id->accession)) {
+          $accession = $term->cvterm_id->dbxref_id->db_id->name . ":" . $term->cvterm_id->dbxref_id->accession;
+        }
+        if ($term->cvterm_id->dbxref_id->db_id->urlprefix) {
+          $accession = l($accession, $term->cvterm_id->dbxref_id->db_id->urlprefix . $accession, array('attributes' => array("target" => '_blank')));
+        } 
+        
+        $rows[] = array(
+          $accession,
+          $term->cvterm_id->name
+        );
+      } 
+      // the $table array contains the headers and rows array as well as other
+      // options for controlling the display of the table.  Additional
+      // documentation can be found here:
+      // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+      $table = array(
+        'header' => $headers,
+        'rows' => $rows,
+        'attributes' => array(
+          'id' => "tripal_feature-table-terms-$i",
+        ),
+        'sticky' => FALSE,
+        'caption' => ucwords(preg_replace('/_/', ' ', $cv)),
+        'colgroups' => array(),
+        'empty' => '',
+      );
+      
+      // once we have our table array structure defined, we call Drupal's theme_table()
+      // function to generate the table.
+      print theme_table($table);
+      $i++;
+    } ?>
   </div> <?php
 } ?>

+ 1 - 0
tripal_feature/theme/tripal_organism/tripal_organism_feature_counts.tpl.php

@@ -9,6 +9,7 @@ if(property_exists($organism, 'feature_counts')) {
   $names    = $organism->feature_counts['names'];
   $enabled  = $organism->feature_counts['enabled'];
 }
+dpm($organism);
 
 // only show this block if it is enabled
 if ($enabled) { 

+ 208 - 139
tripal_feature/tripal_feature.module

@@ -29,8 +29,7 @@ require_once "includes/tripal_feature.form.inc";
  *
  * @ingroup tripal_feature
  */
-function tripal_feature_init() {
-
+function tripal_feature_init() { 
   drupal_add_css(drupal_get_path('module', 'tripal_feature') . '/theme/css/tripal_feature.css');
   drupal_add_js(drupal_get_path('module', 'tripal_feature') . '/theme/js/tripal_feature.js');
 }
@@ -301,21 +300,26 @@ function tripal_feature_theme($existing, $type, $theme, $path) {
       'base hook' => 'node',
       'path' => "$core_path/theme",
     ),
-    'tripal_organism_feature_browser' => array(
+    'tripal_feature_alignments' => array(
       'variables' => array('node' => NULL),
-      'template' => 'tripal_organism_feature_browser',
-      'path' => "$path/theme/tripal_organism",
+      'template' => 'tripal_feature_alignments',
+      'path' => "$path/theme/tripal_feature",
     ),
-    'tripal_organism_feature_counts' => array(
+    'tripal_feature_analyses' => array(
       'variables' => array('node' => NULL),
-      'template' => 'tripal_organism_feature_counts',
-      'path' => "$path/theme/tripal_organism",
+      'template' => 'tripal_feature_analyses',
+      'path' => "$path/theme/tripal_feature",
     ),
     'tripal_feature_base' => array(
       'variables' => array('node' => NULL),
       'template' => 'tripal_feature_base',
       'path' => "$path/theme/tripal_feature",
     ),
+    'tripal_feature_featurepos' => array(
+      'arguments' => array('node' => NULL),
+      'template' => 'tripal_feature_featurepos',
+      'path' => "$path/theme/tripal_feature",
+    ),
     'tripal_feature_sequence' => array(
       'variables' => array('node' => NULL),
       'template' => 'tripal_feature_sequence',
@@ -336,16 +340,6 @@ function tripal_feature_theme($existing, $type, $theme, $path) {
       'template' => 'tripal_feature_phenotypes',
       'path' => "$path/theme/tripal_feature",
     ),
-    'tripal_feature_featurepos' => array(
-      'arguments' => array('node' => NULL),
-      'template' => 'tripal_feature_featurepos',
-      'path' => "$path/theme/tripal_feature",
-    ),
-    'tripal_feature_featureloc_sequences' => array(
-      'variables' => array('node' => NULL),
-      'template' => 'tripal_feature_featureloc_sequences',
-      'path' => "$path/theme/tripal_feature",
-    ),
     'tripal_feature_references' => array(
       'variables' => array('node' => NULL),
       'template' => 'tripal_feature_references',
@@ -361,11 +355,6 @@ function tripal_feature_theme($existing, $type, $theme, $path) {
       'template' => 'tripal_feature_terms',
       'path' => "$path/theme/tripal_feature",
     ),
-    'tripal_feature_alignments' => array(
-      'variables' => array('node' => NULL),
-      'template' => 'tripal_feature_alignments',
-      'path' => "$path/theme/tripal_feature",
-    ),
     'tripal_feature_relationships' => array(
       'variables' => array('node' => NULL),
       'template' => 'tripal_feature_relationships',
@@ -377,10 +366,29 @@ function tripal_feature_theme($existing, $type, $theme, $path) {
       'path' => drupal_get_path('module', 'tripal_feature') . '/theme'
     ),
 
+    // template for the organism page
+    'tripal_organism_feature_browser' => array(
+      'variables' => array('node' => NULL),
+      'template' => 'tripal_organism_feature_browser',
+      'path' => "$path/theme/tripal_organism",
+    ),
+    'tripal_organism_feature_counts' => array(
+      'variables' => array('node' => NULL),
+      'template' => 'tripal_organism_feature_counts',
+      'path' => "$path/theme/tripal_organism",
+    ),
+    
     // themed forms
     'tripal_feature_seq_extract_form' => array(
        'arguments' => array('form'),
-    )
+    ),
+    
+    // themed teaser
+    'tripal_feature_teaser' => array(
+      'variables' => array('node' => NULL),
+      'template' => 'tripal_feature_teaser',
+      'path' => "$path/theme/tripal_feature",
+    ),
   );
   
   return $items;
@@ -505,70 +513,60 @@ function tripal_feature_block_view($delta = '') {
  * @ingroup tripal_feature
  */
 function chado_feature_insert($node) {
+  
+  $node->uniquename   = trim($node->uniquename);
+  $node->fname        = trim($node->fname);
+  $node->feature_type = trim($node->feature_type);
+  $node->residues     = trim($node->residues);
+  
   // remove spaces, newlines from residues
   $residues = preg_replace("/[\n\r\s]/", "", $node->residues);
   $obsolete = 'FALSE';
   if ($node->is_obsolete) {
     $obsolete = 'TRUE';
   }
+  
+  $feature_id = '';
 
-  // check to see if we are inserting a duplicate record.
-  $values = array(
-    'cv_id' => array(
-      'name' => 'sequence'
-    ),
-    'name' => $node->feature_type
-  );
-  $type = tripal_core_chado_select('cvterm', array('cvterm_id'), $values);
-  $values = array(
-    'organism_id' => $node->organism_id,
-    'name' => $node->fname,
-    'uniquename' => $node->uniquename,
-    'residues' => $residues,
-    'seqlen' => drupal_strlen($residues),
-    'is_obsolete' => $obsolete,
-    'type_id' => $type[0]->cvterm_id,
-    'md5checksum' => md5($residues)
-  );
-  $options = array('is_duplicate' => TRUE, 'has_record' => TRUE);
-  $exists = tripal_core_chado_select('feature', array('*'), $values, $options);
-
-  // if the record is not a duplicate then add it
-  if (!$exists) {
-    $istatus = tripal_core_chado_insert('feature', $values);
-    if (!$istatus) {
+  // if there is an feature_id in the $node object then this must be a sync so
+  // we can skip adding the feature as it is already there, although
+  // we do need to proceed with the rest of the insert
+  if (!property_exists($node, 'feature_id')) {
+    $values = array(
+      'organism_id' => $node->organism_id,
+      'name' => $node->fname,
+      'uniquename' => $node->uniquename,
+      'residues' => $residues,
+      'seqlen' => drupal_strlen($residues),
+      'is_obsolete' => $obsolete,
+      'type_id' => $type[0]->cvterm_id,
+      'md5checksum' => md5($residues)
+    );
+    $feature = tripal_core_chado_select('feature', array('*'), $values);
+    if (!$feature) {
       drupal_set_message(t('Unable to add feature.'), 'warning');
       watchdog('tripal_feature', 'Insert feature: Unable to create feature where values: %values',
         array('%values' => print_r($values, TRUE)), WATCHDOG_WARNING);
+      return;
     }
+    $feature_id = $feature->feature_id;
+    
+    // add the genbank accession and synonyms
+    chado_feature_add_synonyms($node->synonyms, $node->feature_id);
+  }
+  else {
+    $feature_id = $node->feature_id;
   }
 
-  // now get the newly added record
-  $values = array(
-    'organism_id' => $node->organism_id,
-    'uniquename' => $node->uniquename,
-    'type_id' => $type[0]->cvterm_id,
-  );
-  $feature = tripal_core_chado_select('feature', array('feature_id'), $values);
-
-  // add the genbank accession and synonyms
-  chado_feature_add_synonyms($node->synonyms, $feature[0]->feature_id);
-
-  // make sure the entry for this feature doesn't already exist in the chado_feature table
-  // if it doesn't exist then we want to add it.
-  $node_check_sql = "
-    SELECT * FROM {chado_feature}
-    WHERE feature_id = :feature_id
-  ";
-  $node_check = db_query($node_check_sql, array(':feature_id' => $feature[0]->feature_id))->fetchObject();
-  if (!$node_check) {
-    // next add the item to the drupal table
-    $sql = "
-      INSERT INTO {chado_feature} (nid, vid, feature_id, sync_date)
-      VALUES (:nid, :vid, :feature_id, :time)
-    ";
-    db_query($sql, array(':nid' => $node->nid, ':vid' => $node->vid,
-      ':feature_id' => $feature[0]->feature_id, ':time' => REQUEST_TIME));
+  // Make sure the entry for this feature doesn't already exist in the
+  // chado_feature table if it doesn't exist then we want to add it.
+  $check_org_id = chado_get_id_for_node('feature', $node->nid);
+  if (!$check_org_id) {
+    $record = new stdClass();
+    $record->nid = $node->nid;
+    $record->vid = $node->vid;
+    $record->feature_id = $feature_id;
+    drupal_write_record('chado_feature', $record);
   }
 }
 
@@ -578,6 +576,12 @@ function chado_feature_insert($node) {
  * @ingroup tripal_feature
  */
 function chado_feature_update($node) {
+  
+  $node->uniquename   = trim($node->uniquename);
+  $node->fname        = trim($node->fname);
+  $node->feature_type = trim($node->feature_type);
+  $node->residues     = trim($node->residues);
+  
   if ($node->revision) {
     // there is no way to handle revisions in Chado but leave
     // this here just to make not we've addressed it.
@@ -956,12 +960,8 @@ function tripal_feature_load_featurelocs($feature_id, $side = 'as_parent', $aggr
   while ($loc = $flresults->fetchObject()) {
     // if a drupal node exists for this feature then add the nid to the
     // results object
-    $sql = 'SELECT nid FROM {chado_feature} WHERE feature_id = :feature_id';
-
-    $ffeature = db_query($sql, array(':feature_id' => $loc->feature_id))->fetchObject();
-    $sfeature = db_query($sql, array(':feature_id' => $loc->src_feature_id))->fetchObject();
-    $loc->fnid = $ffeature->nid;
-    $loc->snid = $sfeature->nid;
+    $loc->fnid = chado_get_node_id('feature', $loc->feature_id);
+    $loc->snid = chado_get_node_id('feature', $loc->src_feature_id);
     // add the result to the array
     $featurelocs[$i++] = $loc;
   }
@@ -1118,11 +1118,6 @@ function tripal_feature_load_featureloc_sequences($feature_id, $featurelocs) {
   // 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.
-  $sql = "
-    SELECT substring(residues from :start for :size) as residues
-    FROM {feature}
-    WHERE feature_id = :feature_id
-  ";
   $floc_sequences = array();
   foreach ($featurelocs as $featureloc) {
 
@@ -1171,7 +1166,18 @@ function tripal_feature_load_featureloc_sequences($feature_id, $featurelocs) {
 
       $floc_sequences[$src]['src'] = $src;
       $floc_sequences[$src]['type'] = $featureloc->feature_id->type_id->name;
-      $args = array(':start' => $featureloc->fmin + 1, ':size' => ($featureloc->fmax - $featureloc->fmin), ':feature_id' => $featureloc->srcfeature_id->feature_id);
+      $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) {
@@ -1588,7 +1594,7 @@ function tripal_feature_color_sequence($sequence, $parts, $defline) {
     }
   }
 
-  $newseq .= "<div id=\"tripal_feature-featureloc_sequence-legend\">Legend: ";
+  $newseq = "<div id=\"tripal_feature-featureloc_sequence-legend\">Legend: ";
   foreach ($types as $type_name => $present) {
     $newseq .= "<span id=\"tripal_feature-legend-$type_name\" class=\"tripal_feature-legend-item tripal_feature-featureloc_sequence-$type_name\" script=\"\">$type_name</span>";
   }
@@ -1602,6 +1608,7 @@ function tripal_feature_color_sequence($sequence, $parts, $defline) {
 
   // iterate through the parts. They should be in order.
   $ends = array();
+  $seqcount = 0;
   foreach ($parts as $index => $types) {
 
     // get the start for this part.  All types in this part start at the
@@ -1641,7 +1648,7 @@ function tripal_feature_color_sequence($sequence, $parts, $defline) {
   }
 
   // add in rest of the sequence
-  for ($i = $pos; $i <= strlen($sequence); $i++) {
+  for ($i = $pos; $i < strlen($sequence); $i++) {
     $newseq .= $sequence{$pos};
     $seqcount++;
     if ($seqcount % 50 == 0) {
@@ -1668,7 +1675,6 @@ function tripal_feature_node_presave($node) {
   // set the title to ensure it is always unique
   switch ($node->type) {
     case 'chado_feature':
-
       $values = array('organism_id' => $node->organism_id);
       $organism = tripal_core_chado_select('organism', array('genus', 'species'), $values);
       $node->title = $node->fname . ', ' . $node->uniquename . ' (' . $node->feature_type . ') ' . $organism[0]->genus . ' ' . $organism[0]->species;
@@ -1711,15 +1717,36 @@ function tripal_feature_node_view($node, $view_mode, $langcode) {
     case 'chado_feature':
       // Show feature browser and counts
       if ($view_mode == 'full') {
+        $node->content['tripal_feature_alignments'] = array(
+          '#value' => theme('tripal_feature_alignments', array('node' => $node)),
+        );
+        $node->content['tripal_feature_analyses'] = array(
+          '#value' => theme('tripal_feature_analyses', array('node' => $node)),
+        );
         $node->content['tripal_feature_base'] = array(
           '#value' => theme('tripal_feature_base', array('node' => $node)),
         );
+        $node->content['tripal_feature_phenotypes'] = array(
+          '#value' => theme('tripal_feature_phenotypes', array('node' => $node)),
+        );
+        $node->content['tripal_feature_properties'] = array(
+          '#value' => theme('tripal_feature_properties', array('node' => $node)),
+        );
+        $node->content['tripal_feature_references'] = array(
+          '#value' => theme('tripal_feature_references', array('node' => $node)),
+        );
+        $node->content['tripal_feature_relationships'] = array(
+          '#value' => theme('tripal_feature_relationships', array('node' => $node)),
+        );
         $node->content['tripal_feature_seqence'] = array(
           '#value' => theme('tripal_feature_sequence', array('node' => $node)),
         );
         $node->content['tripal_feature_synonyms'] = array(
           '#value' => theme('tripal_feature_synonyms', array('node' => $node)),
         );
+        $node->content['tripal_feature_terms'] = array(
+          '#value' => theme('tripal_feature_terms', array('node' => $node)),
+        );
       }
       if ($view_mode == 'teaser') {
         $node->content['tripal_feature_teaser'] = array(
@@ -1767,7 +1794,33 @@ function tripal_feature_node_update($node) {
   }
 }
 
+/**
+ * @ingroup tripal_feature
+ */
+function tripal_feature_preprocess_tripal_feature_sequence(&$variables) {
+  // we want to provide a new variable that contains the matched features.
+  $feature = $variables['node']->feature;
+  
+  // get the featureloc src features
+  $options = array(
+    'return_array' => 1,
+    'include_fk' => array(
+      'srcfeature_id' => array(
+        'type_id' => 1
+      ),
+    ),
+  );
 
+  $feature = tripal_core_expand_chado_vars($feature, 'table', 'featureloc', $options);
+  
+  // because there are two foriegn keys in the featureloc table with the feature table
+  // we have to access the records for each by specifying the field name after the table name:
+  $ffeaturelocs = $feature->featureloc->feature_id;
+  
+  // now extract the sequences
+  $featureloc_sequences = tripal_feature_load_featureloc_sequences($feature->feature_id, $ffeaturelocs);
+  $feature->featureloc_sequences = $featureloc_sequences;
+}
 /**
  *
  *
@@ -1777,7 +1830,7 @@ function tripal_feature_preprocess_tripal_feature_relationships(&$variables) {
   // we want to provide a new variable that contains the matched features.
   $feature = $variables['node']->feature;
 
-  if (!$feature->all_relationships) {
+  if (!property_exists($feature, 'all_relationships')) {
     $feature->all_relationships = tripal_feature_get_feature_relationships($feature);
   }
 }
@@ -1791,7 +1844,7 @@ function tripal_feature_preprocess_tripal_feature_proteins(&$variables) {
   // we want to provide a new variable that contains the matched features.
   $feature = $variables['node']->feature;
 
-  if (!$feature->all_relationships) {
+  if (!property_exists($feature, 'all_relationships')) {
     $feature->all_relationships = tripal_feature_get_feature_relationships($feature);
   }
 }
@@ -1804,65 +1857,74 @@ function tripal_feature_preprocess_tripal_feature_alignments(&$variables) {
 
   // we want to provide a new variable that contains the matched features.
   $feature = $variables['node']->feature;
-  $feature = tripal_core_expand_chado_vars($feature, 'table', 'featureloc');
+  $options = array(
+    'return_array' => 1,
+    'include_fk' => array(
+      'srcfeature_id' => array(
+        'type_id' => 1,
+      ),
+      'feature_id' => array(
+        'type_id' => 1
+      ),
+    )
+  );
+  $feature = tripal_core_expand_chado_vars($feature, 'table', 'featureloc', $options);
 
   // get alignments as child
   $cfeaturelocs = $feature->featureloc->feature_id;
   if (!$cfeaturelocs) {
-     $cfeaturelocs = array();
-  }
-  elseif (!is_array($cfeaturelocs)) {
-     $cfeaturelocs = array($cfeaturelocs);
+    $cfeaturelocs = array();
   }
   // get alignment as parent
   $pfeaturelocs = $feature->featureloc->srcfeature_id;
   if (!$pfeaturelocs) {
-     $pfeaturelocs = array();
-  }
-  elseif (!is_array($pfeaturelocs)) {
-     $pfeaturelocs = array($pfeaturelocs);
+    $pfeaturelocs = array();
   }
 
   // get matched alignments (those with an itermediate 'match' or 'EST_match', etc
   $mfeaturelocs = tripal_feature_get_matched_alignments($feature);
-  $feature->matched_featurelocs = mfeaturelocs;
+  $feature->matched_featurelocs = $mfeaturelocs;
 
   // combine all three alignments into a single array for printing together in
   // a single list
   $alignments = array();
   foreach ($pfeaturelocs as $featureloc) {
-     // if type is a 'match' then ignore it. We will handle those below
-     if (preg_match('/(^match$|^.*?_match|match_part)$/', $featureloc->feature_id->type_id->name)) {
-        continue;
-     }
-     $alignment = new stdClass();
-     $alignment->record = $featureloc;
-     $alignment->name = $featureloc->feature_id->name;
-     $alignment->nid = $featureloc->feature_id->nid;
-     $alignment->type = $featureloc->feature_id->type_id->name;
-     $alignment->fmin = $featureloc->fmin;
-     $alignment->fmax = $featureloc->fmax;
-     $alignment->phase = $featureloc->phase;
-     $alignment->strand = $featureloc->strand;
-     $alignments[] = $alignment;
+    // if type is a 'match' then ignore it. We will handle those below
+    if (preg_match('/(^match$|^.*?_match|match_part)$/', $featureloc->feature_id->type_id->name)) {
+       continue;
+    }
+    $alignment = new stdClass();
+    $alignment->record = $featureloc;
+    $alignment->name = $featureloc->feature_id->name;
+    $alignment->type = $featureloc->feature_id->type_id->name;
+    $alignment->fmin = $featureloc->fmin;
+    $alignment->fmax = $featureloc->fmax;
+    $alignment->phase = $featureloc->phase;
+    $alignment->strand = $featureloc->strand;
+    $alignments[] = $alignment;
+    if (property_exists($featureloc->feature_id, 'nid')) {
+      $alignment->nid = $featureloc->feature_id->nid;
+    }
   }
   foreach ($cfeaturelocs as $featureloc) {
-     // if type is a 'match' then ignore it. We will handle those below
-     if (preg_match('/(^match$|^.*?_match|match_part)$/', $featureloc->feature_id->type_id->name)) {
-        continue;
-     }
-     $alignment = new stdClass();
-     $alignment->record = $featureloc;
-     $alignment->name = $featureloc->srcfeature_id->name;
-     $alignment->nid = $featureloc->srcfeature_id->nid;
-     $alignment->type = $featureloc->srcfeature_id->type_id->name;
-     $alignment->fmin = $featureloc->fmin;
-     $alignment->is_fmin_partial = $featureloc->is_fmin_partial;
-     $alignment->fmax = $featureloc->fmax;
-     $alignment->is_fmax_partial = $featureloc->is_fmax_partial;
-     $alignment->phase = $featureloc->phase;
-     $alignment->strand = $featureloc->strand;
-     $alignments[] = $alignment;
+    // if type is a 'match' then ignore it. We will handle those below
+    if (preg_match('/(^match$|^.*?_match|match_part)$/', $featureloc->feature_id->type_id->name)) {
+      continue;
+    }
+    $alignment = new stdClass();
+    $alignment->record = $featureloc;
+    $alignment->name = $featureloc->srcfeature_id->name;    
+    $alignment->type = $featureloc->srcfeature_id->type_id->name;
+    $alignment->fmin = $featureloc->fmin;
+    $alignment->is_fmin_partial = $featureloc->is_fmin_partial;
+    $alignment->fmax = $featureloc->fmax;
+    $alignment->is_fmax_partial = $featureloc->is_fmax_partial;
+    $alignment->phase = $featureloc->phase;
+    $alignment->strand = $featureloc->strand;
+    $alignments[] = $alignment;
+    if (property_exists($featureloc->srcfeature_id, 'nid')) {
+      $alignment->nid = $featureloc->srcfeature_id->nid;
+    }
   }
   // in matching features, the left feature is always the feature
   // provided to this function.
@@ -1875,7 +1937,6 @@ function tripal_feature_preprocess_tripal_feature_alignments(&$variables) {
      $alignment->record = $featureloc;
      $alignment->right_feature = $rfeature;
      $alignment->name = $rfeature->name;
-     $alignment->nid = $rfeature->nid;
      $alignment->type = $rfeature->type_id->name;
      $alignment->fmin = $featureloc->left_fmin;
      $alignment->is_fmin_partial = $featureloc->left_is_fmin_partial;
@@ -1890,6 +1951,9 @@ function tripal_feature_preprocess_tripal_feature_alignments(&$variables) {
      $alignment->right_phase = $featureloc->right_phase;
      $alignment->right_strand = $featureloc->right_strand;
      $alignments[] = $alignment;
+     if (property_exists($rfeature, 'nid')) {
+       $alignment->nid = $rfeature->nid;
+     }
   }
   $feature->all_featurelocs = $alignments;
 }
@@ -1899,8 +1963,8 @@ function tripal_feature_preprocess_tripal_feature_alignments(&$variables) {
  * @ingroup tripal_feature
  */
 function tripal_feature_preprocess_tripal_organism_feature_counts(&$variables, $hook) {
- // $organism = $variables['node']->organism;
-  //$organism->feature_counts = tripal_feature_load_organism_feature_counts($organism);
+  $organism = $variables['node']->organism;
+  $organism->feature_counts = tripal_feature_load_organism_feature_counts($organism);
 }
 
 /**
@@ -1909,8 +1973,8 @@ function tripal_feature_preprocess_tripal_organism_feature_counts(&$variables, $
  * @ingroup tripal_feature
  */
 function tripal_feature_preprocess_tripal_organism_feature_browser(&$variables, $hook) {
-  //$organism = $variables['node']->organism;
-  //$organism->feature_browser = tripal_feature_load_organism_feature_browser($organism);
+  $organism = $variables['node']->organism;
+  $organism->feature_browser = tripal_feature_load_organism_feature_browser($organism);
 }
 
 /**
@@ -2117,8 +2181,13 @@ function tripal_feature_job_describe_args($callback, $args) {
     $new_args['Import all and update'] = ($args[4] == 1) ? "Yes" : "No";
     $new_args['Import all and replace'] = ($args[5] == 1) ? "Yes" : "No";
     $new_args['Delete features'] = ($args[6] == 1) ? "Yes" : "No";
-    $target_organism = tripal_core_chado_select('organism', array('genus', 'species'), array('organism_id' => $args[8]));
-    $new_args['Target organism'] = $target_organism[0]->genus . " " . $target_organism[0]->species;
+    if ($args[8]) {
+      $target_organism = tripal_core_chado_select('organism', array('genus', 'species'), array('organism_id' => $args[8]));
+      $new_args['Target organism'] = $target_organism[0]->genus . " " . $target_organism[0]->species;
+    }
+    else {
+      $new_args['Target organism'] = '';
+    }
     $new_args['Target type'] = $args[9];
     $new_args['Create target'] = ($args[10] == 1) ? "Yes" : "No";
     $new_args['Starting line'] = $args[11];

+ 0 - 10
tripal_organism/tripal_organism.install

@@ -78,16 +78,6 @@ function tripal_organism_schema() {
  */
 function tripal_organism_uninstall() {
 
-  // Get the list of nodes to remove
-  $sql_lib_id = "
-    SELECT nid, vid
-    FROM {node}
-    WHERE type='chado_organism'
-  ";
-  $result = db_query($sql_lib_id);
-  foreach ($result as $node) {
-    node_delete($node->nid);
-  }
 }
 
 /**

+ 1 - 1
tripal_views/views/handlers/tripal_views_handler_filter_select_string.inc

@@ -143,7 +143,7 @@ class tripal_views_handler_filter_select_string extends views_handler_filter_str
   /**
    * Set values for my options
    */
-  function expose_submit(&$form, &$form_state) {
+  function expose_submit($form, &$form_state) {
     $this->options['expose']['values_form_type'] = $form_state['values']['options']['expose']['values_form_type'];
     $this->options['expose']['select_multiple'] = $form_state['values']['options']['expose']['select_multiple'];
     $this->options['expose']['select_optional'] = $form_state['values']['options']['expose']['select_optional'];