Ver código fonte

to fix themes/subtheme issue: Revert "branching out to theme only"

This reverts commit 9487bc537c0b13ecd301bfcfb9e59b6a667103ec.
alexgl 13 anos atrás
pai
commit
dff290623b
100 arquivos alterados com 42470 adições e 0 exclusões
  1. 519 0
      modules/base/tripal_analysis/tripal_analysis.admin.inc
  2. 226 0
      modules/base/tripal_analysis/tripal_analysis.api.inc
  3. 10 0
      modules/base/tripal_analysis/tripal_analysis.info
  4. 140 0
      modules/base/tripal_analysis/tripal_analysis.install
  5. 1060 0
      modules/base/tripal_analysis/tripal_analysis.module
  6. 546 0
      modules/base/tripal_analysis/tripal_analysis.views.inc
  7. 82 0
      modules/base/tripal_analysis/tripal_analysis_privacy.inc
  8. 45 0
      modules/base/tripal_analysis/views/README
  9. 290 0
      modules/base/tripal_analysis/views/analysis.views.inc
  10. 84 0
      modules/base/tripal_analysis/views/chado_analysis.views.inc
  11. 23 0
      modules/base/tripal_analysis/views/handlers/views_handler_field_computed_analysis_nid.inc
  12. 37 0
      modules/base/tripal_analysis/views/misc_tables.views.inc
  13. 84 0
      modules/base/tripal_analysis/views/template.node_join.views.inc
  14. 211 0
      modules/base/tripal_analysis/views/template.table_defn.views.inc
  15. 2061 0
      modules/base/tripal_bulk_loader/tripal_bulk_loader.admin.inc
  16. 7 0
      modules/base/tripal_bulk_loader/tripal_bulk_loader.info
  17. 108 0
      modules/base/tripal_bulk_loader/tripal_bulk_loader.install
  18. 518 0
      modules/base/tripal_bulk_loader/tripal_bulk_loader.loader.inc
  19. 409 0
      modules/base/tripal_bulk_loader/tripal_bulk_loader.module
  20. 55 0
      modules/base/tripal_contact/tripal_contact.api.inc
  21. 9 0
      modules/base/tripal_contact/tripal_contact.info
  22. 39 0
      modules/base/tripal_contact/tripal_contact.module
  23. 50 0
      modules/base/tripal_contact/tripal_contact.views.inc
  24. 45 0
      modules/base/tripal_contact/views/README
  25. 84 0
      modules/base/tripal_contact/views/template.node_join.views.inc
  26. 211 0
      modules/base/tripal_contact/views/template.table_defn.views.inc
  27. 278 0
      modules/base/tripal_core/chado_install.php
  28. 121 0
      modules/base/tripal_core/cvterms.php
  29. 2423 0
      modules/base/tripal_core/default_schema.sql
  30. 232 0
      modules/base/tripal_core/initialize.sql
  31. 439 0
      modules/base/tripal_core/jobs.php
  32. 454 0
      modules/base/tripal_core/mviews.php
  33. 1677 0
      modules/base/tripal_core/tripal_core.api.inc
  34. 7 0
      modules/base/tripal_core/tripal_core.info
  35. 105 0
      modules/base/tripal_core/tripal_core.install
  36. 260 0
      modules/base/tripal_core/tripal_core.module
  37. 12723 0
      modules/base/tripal_core/tripal_core.schema.api.inc
  38. 283 0
      modules/base/tripal_core/tripal_core.views.inc
  39. 81 0
      modules/base/tripal_core/tripal_core_job_view.tpl.php
  40. 57 0
      modules/base/tripal_core/tripal_launch_jobs.php
  41. 106 0
      modules/base/tripal_core/tripal_launch_jobs_multi.php
  42. 45 0
      modules/base/tripal_core/views/README
  43. 65 0
      modules/base/tripal_core/views/handlers/views_handler_field_chado_count.inc
  44. 209 0
      modules/base/tripal_core/views/handlers/views_handler_field_chado_rel_by_type.inc
  45. 38 0
      modules/base/tripal_core/views/handlers/views_handler_field_chado_tf_boolean.inc
  46. 67 0
      modules/base/tripal_core/views/handlers/views_handler_field_cvterm_name.inc
  47. 42 0
      modules/base/tripal_core/views/handlers/views_handler_field_dbxref_accession_link.inc
  48. 94 0
      modules/base/tripal_core/views/handlers/views_handler_field_node_optional.inc
  49. 81 0
      modules/base/tripal_core/views/handlers/views_handler_field_readable_date.inc
  50. 16 0
      modules/base/tripal_core/views/handlers/views_handler_field_type_name.inc
  51. 91 0
      modules/base/tripal_core/views/handlers/views_handler_filter_chado_boolean.inc
  52. 187 0
      modules/base/tripal_core/views/handlers/views_handler_filter_chado_select_cvterm_name.inc
  53. 97 0
      modules/base/tripal_core/views/handlers/views_handler_filter_chado_select_string.inc
  54. 84 0
      modules/base/tripal_core/views/template.node_join.views.inc
  55. 211 0
      modules/base/tripal_core/views/template.table_defn.views.inc
  56. 249 0
      modules/base/tripal_cv/charts.php
  57. 457 0
      modules/base/tripal_cv/feature_property.obo
  58. 860 0
      modules/base/tripal_cv/obo_loader.php
  59. 457 0
      modules/base/tripal_cv/trees.php
  60. 517 0
      modules/base/tripal_cv/tripal_cv.api.inc
  61. 10 0
      modules/base/tripal_cv/tripal_cv.info
  62. 150 0
      modules/base/tripal_cv/tripal_cv.install
  63. 1105 0
      modules/base/tripal_cv/tripal_cv.module
  64. 371 0
      modules/base/tripal_cv/tripal_cv.views.inc
  65. 45 0
      modules/base/tripal_cv/views/README
  66. 93 0
      modules/base/tripal_cv/views/cv.views.inc
  67. 155 0
      modules/base/tripal_cv/views/cvterm.views.inc
  68. 84 0
      modules/base/tripal_cv/views/template.node_join.views.inc
  69. 211 0
      modules/base/tripal_cv/views/template.table_defn.views.inc
  70. 333 0
      modules/base/tripal_db/tripal_db.api.inc
  71. 9 0
      modules/base/tripal_db/tripal_db.info
  72. 42 0
      modules/base/tripal_db/tripal_db.install
  73. 362 0
      modules/base/tripal_db/tripal_db.module
  74. 333 0
      modules/base/tripal_db/tripal_db.views.inc
  75. 45 0
      modules/base/tripal_db/views/README
  76. 150 0
      modules/base/tripal_db/views/db.views.inc
  77. 149 0
      modules/base/tripal_db/views/dbxref.views.inc
  78. 1 0
      modules/base/tripal_db/views/handlers/views_handler_field_dbxref_accession_link.inc
  79. 84 0
      modules/base/tripal_db/views/template.node_join.views.inc
  80. 211 0
      modules/base/tripal_db/views/template.table_defn.views.inc
  81. 709 0
      modules/base/tripal_feature/fasta_loader.php
  82. 1191 0
      modules/base/tripal_feature/gff_loader.php
  83. 170 0
      modules/base/tripal_feature/indexFeatures.php
  84. 418 0
      modules/base/tripal_feature/syncFeatures.php
  85. 362 0
      modules/base/tripal_feature/tripal_feature-db_references.inc
  86. 178 0
      modules/base/tripal_feature/tripal_feature-delete.inc
  87. 358 0
      modules/base/tripal_feature/tripal_feature-properties.inc
  88. 528 0
      modules/base/tripal_feature/tripal_feature-relationships.inc
  89. 222 0
      modules/base/tripal_feature/tripal_feature-secondary_tables.inc
  90. 788 0
      modules/base/tripal_feature/tripal_feature.admin.inc
  91. 255 0
      modules/base/tripal_feature/tripal_feature.api.inc
  92. 14 0
      modules/base/tripal_feature/tripal_feature.info
  93. 195 0
      modules/base/tripal_feature/tripal_feature.install
  94. 2189 0
      modules/base/tripal_feature/tripal_feature.module
  95. 439 0
      modules/base/tripal_feature/tripal_feature.views.inc
  96. 45 0
      modules/base/tripal_feature/views/README
  97. 83 0
      modules/base/tripal_feature/views/chado_feature.views.inc
  98. 293 0
      modules/base/tripal_feature/views/feature.views.inc
  99. 23 0
      modules/base/tripal_feature/views/handlers/views_handler_field_computed_feature_nid.inc
  100. 1 0
      modules/base/tripal_feature/views/handlers/views_handler_field_readable_date.inc

+ 519 - 0
modules/base/tripal_analysis/tripal_analysis.admin.inc

@@ -0,0 +1,519 @@
+<?php
+//
+// Copyright 2009 Clemson University
+//
+
+/**
+ * Purpose: Provide Guidance to new Tripal Admin
+ *
+ * @return 
+ *   HTML Formatted text
+ *
+ * @ingroup tripal_analysis
+ */
+function tripal_analysis_module_description_page() {
+
+  $text .= '<h3>Tripal Analysis Administrative Tools Quick Links:</h3>';
+  $text .= "<ul>
+             <li><a href=\"".url("admin/tripal/tripal_analysis/configuration") . "\">Analysis Configuration</a></li>
+           </ul>";
+ 
+
+  $text .= '<h3>Module Description:</h3>';
+  $text .= '<p>The Tripal Analysis module provides a generic analysis content type that is intended to be
+            used when a more specialized analysis module does not exist.  Because it is generic, it does not
+            provide any visualization or data loading tools for analysis data.  Specialized analysis modules,
+            such as the Tripal Analysis Blast or Tripal Analysis KEGG modules provide loading and custom
+            visualizations for the analysis results.  These modules must be installed separately.
+            </p>';
+
+  $text .= '<h3>Setup Instructions:</h3>';
+  $text .= '<p>After installation of the analysis module or any specialized analysis module.  The following tasks should be performed
+            <ol>
+              <li><p><b>Set Permissions</b>: Each analysis module supports the Drupal user permissions interface for 
+               controlling access to the content and functions. These permissions include viewing, 
+               creating, editing or administering of
+               analysis content. The default is that only the original site administrator has these 
+               permissions.  You can <a href="'.url('admin/user/roles').'">add roles</a> for classifying users, 
+               <a href="'.url('admin/user/user').'">assign users to roles</a> and
+               <a href="'.url('admin/user/permissions').'">assign permissions</a> for the analysis content to 
+               those roles.  For a simple setup, allow anonymous users access to view organism content and 
+               allow the site administrator all other permissions.</p></li>
+
+
+               <li><p><b>Create Analysis</b>:  An analysis should be <a href="'.url('node/add').'">created</a> before data is imported into
+               chado.  The generic analysis type should only be used when a more specialized analysis module
+               (e.g. Tripal Analysis Blast module) does not already exists.  All data imported into 
+               Chado should be associated with an analysis.    
+
+               <li><p><b>Sync Analyses</b>:  If Chado has preloaded analyses then you can sync those.  This process is what
+               creates the pages for viewing an analysis on the site.  Analyses can be synced using the 
+               <a href="'.url('admin/tripal/tripal_analysis/configuration').'">Analysis Configuration page</a>. 
+               However, syncing an analyses will always create a generic analysis content type.  If you would like
+               to use a specialized analysis module for visualization of data then do not sync the analysis but recreate it
+               using the appropriate specialized analysis content type.</p></li>
+ 
+            </ol>
+            </p>';
+  
+  
+  $text .= '<h3>Features of this Module:</h3>';
+  $text .= '<p>Aside from providing a generic content type the Tripal Analysis module also provides the following functionality
+            <ul>
+            
+              <li><p><b>Basic Analysis Lookup View</b>: This module provides a basic <a href="'.url('analyses').'">analysis search 
+              tool</a> for finding or listing analyses in Chado. It does not require indexing for Drupal searching but relies
+              on Drupal Views.  <a href="http://drupal.org/project/views">Drupal Views</a> must be installed. </p></li>              
+
+            </ul>
+            </p>';
+
+   $text .= '<h3>Page Customizations</h3>';
+   $text .= '<p>There are several ways to customize the look-and-feel for the way Chado data is presented through Tripal. 
+             Below is a description of several methods.  These methods may be used in conjunction with one another to
+             provide fine-grained control. 
+             <ul>
+
+             <li><p><b>Integration with Drupal Panels</b>:  <a href="http://drupal.org/project/views">Drupal Panels</a> 
+              allows for customization of a page layout if you don\'t want to do PHP/Javascript/CSS programming.  
+              Tripal comes with pre-set layouts for analysis pages.  However, 
+              Panels become useful if you prefer a layout that is different from the pre-set layouts.  Chado content
+              is provided to Panels in the form of Drupal "blocks" which you can then place anywhere on a page using the 
+              Panel\'s GUI.</p></li>
+
+             <li><p><b>Drupal\'s Content Construction Kit (CCK)</b>: the 
+             <a href="http://drupal.org/project/cck">Content Construction Kit (CCK) </a> is a powerful way to add non-Chado content
+             to any page without need to edit template files or knowing PHP.  You must first download and install CCK.
+             With CCK, the site administartor can create a new field to appear on the page.  For example, currently,
+             the Chado publication module is not yet supported by Tripal.  Therefore, the site administrator can add a text 
+             field to the analysis pages.  This content is not stored in Chado, but will appear on the analysis page.  A field
+             added by CCK will also appear in the form when editing a analysis to allow users to manually enter the appropriate
+             text.  If the default pre-set layout and themeing for Tripal is used, it is better to create the CCK element,
+             indicate that it is not to be shown (using the CCK interface), then manually add the new content type 
+             where desired by editing the templates (as described below).  If using Panels, the CCK field can be added to the
+             location desired using the Panels interface.</p></li>
+
+             <li><p><b>Drupal Node Templates</b>:  The Tripal packages comes with a "theme_tripal" directory that contains the
+             themeing for Chado content.    The analysis module has a template file for analysis "nodes" (Tripal analysis pages).  This file
+             is named "node-chado_analysis.tpl.php", and provides javascript, HTML and PHP code for display of the analysis
+             pages.  Specialized analysis modules will have their own template files as well, such as "node-chado_analysis-blast.tpl.php" for the
+             Tripal Analysis Blast module.  You can edit the template file to control which types of information (or which analysis "blocks") are displayed 
+             for analysis. Be sure to 
+             copy these template to your primary theme directory for editing. Do not edit them in the "theme_tripal" directory as
+             future Tripal updates may overwrite your customizations. See the <a href="http://tripal.sourceforge.net/">Tripal website </a>
+             for instructions on how to access variables and other Chado content within the template file.</p></li>
+
+             <li><p><b>Analysis "Block" Templates</b>:  In the "theme_tripal" directory are subdirectories named after each tripal module (e.g. "tripal_feature", "tripal_library", etc.).
+             Inside each directory is a set of templates that control distinct types of information for each content type.  For example,
+             there is a "base" template for displaying of data directly from the Chado feature table, and a "references" 
+             template for showing external site references for a feature (data from the feature_dbxref table). 
+              These templates are used both by Drupal blocks
+             for use in Drupal Panels (as described above) or for use in the default pre-set layout that the node template 
+             provides (also desribed above).  Analyses block templates can exist in any of these directories.  For example, the Tripal Analysis Unigene
+             module uses templates in the tripal_analysis_unigene, tripal_organism, and tripal_feature directories.  Content for a unigene is then
+             cusotmizable within each of these contexts.
+             You can customize block template as you desire.  Be sure to copy the
+             template to your primary theme directory for editing. Do not edit them in the "theme_tripal" directory as
+             future Tripal updates may overwrite your customizations.  See the <a href="http://tripal.sourceforge.net/">Tripal website </a>
+             for instructions on how to access variables and other Chado content within the template files.</p></li>
+             </li>
+
+             <li><p><b>Adding Links to the "Resources" Sidebar</b>: If you use the pre-set default Tripal layout for theming, you
+             will see a "Resources" sidebar on each page.  The links that appear on the sidebar are automatically generated
+             using Javascript for all of the analysis "Blocks" that appear on the page. If you want to add additional links 
+             (e.g. a dynamic link to GBrowse for the analysis) and you want that link to appear in the 
+             "Resources" sidebar, simply edit the Drupal Node Template (as described above) and add the link to the 
+             section at the bottom of the template file where the resources section is found.</p></li>
+
+             </ul>
+             </p>';
+
+  return $text;
+}
+
+/*******************************************************************************
+ * Administration page callbacks for the Tripal Analysis module
+ * We have defined a hook_get_settings() function. When a sub-module
+ * is enabled, we'll look for this function to provide a form for the
+ * administrative setting.
+ *
+ * @ingroup tripal_analysis
+ */
+function tripal_analysis_admin() {
+	// Create a new administrative form. We'll add main functions to the form
+	// first (Sync, Reindex, Clean, Taxonify). Thereafter, any sub-module that
+	// has a setting will be added.
+	$form = array();
+
+	// before proceeding check to see if we have any
+	// currently processing jobs. If so, we don't want
+	// to give the opportunity to sync libraries
+	$active_jobs = FALSE;
+	if(tripal_get_module_active_jobs('tripal_organism')){
+		$active_jobs = TRUE;
+	}
+
+	// add the field set for syncing libraries
+	if(!$active_jobs){
+		// add the field set for syncing analyses
+		get_tripal_analysis_admin_form_sync_set($form);
+//		get_tripal_analysis_admin_form_reindex_set($form);
+//		get_tripal_analysis_admin_form_taxonomy_set($form);
+		get_tripal_analysis_admin_form_cleanup_set($form);
+	} else {
+		$form['notice'] = array(
+		   '#type' => 'fieldset',
+		   '#title' => t('Analysis Management Temporarily Unavailable')
+		);
+		$form['notice']['message'] = array(
+          '#value' => t('Currently, analysis management jobs are waiting or are running. . Managemment features have been hidden until these jobs complete.  Please check back later once these jobs have finished.  You can view the status of pending jobs in the Tripal jobs page.'),
+		);
+	}
+
+	// Add sub-module settings. Pull all sub-module information from
+	// {tripal_analysis} table
+	$sql = "SELECT modulename FROM {tripal_analysis}";
+	$result = db_query($sql);
+	$counter = 0;  //keep track of the number of sub-modules
+	while ($data = db_fetch_object($result)) {
+
+		// Check if the hook_get_settings() function is already defined.
+		$func = $data->modulename."_get_settings";
+		$functions = get_defined_functions();
+		$settings;
+		foreach($functions['user'] as $function) {
+			if ($function == $func) {
+				$settings = $func();
+			}
+		}
+		
+		// Add sub-module's specific settings to the administrative view
+		if ($settings) {
+			// Define a fieldset for the sub-module
+			$form["field$counter"] = array(
+            '#type' => 'fieldset',
+            '#title' => "$settings->title",
+            '#collapsible' => TRUE
+			);
+			$form["field$counter"]["$settings->title"] = $settings->form;
+		}
+		$counter ++;
+	}
+	return system_settings_form($form);
+}
+
+/**
+ *
+ * @ingroup tripal_analysis
+ */
+function get_tripal_analysis_admin_form_taxonomy_set(&$form) {
+	$form['taxonify'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Assign Drupal Taxonomy to Analysis Features')
+	);
+
+	// get the list of analyses
+	$sql = "SELECT * FROM {Analysis} ORDER BY name";
+	$previous_db = tripal_db_set_active('chado');  // use chado database
+	$lib_rset = db_query($sql);
+	tripal_db_set_active($previous_db);  // now use drupal database
+
+	// iterate through all of the libraries
+	$lib_boxes = array();
+	while($analysis = db_fetch_object($lib_rset)){
+		$lib_boxes[$analysis->analysis_id] = "$analysis->name";
+	}
+
+	$form['taxonify']['description'] = array(
+       '#type' => 'item',
+       '#value' => t("Drupal allows for assignment of \"taxonomy\" or catagorical terms to " .
+          "nodes. These terms allow for advanced filtering during searching. This option allows ".
+          "for setting taxonomy only for features that belong to the selected analyses below.  All other features will be unaffected.  To set taxonomy for all features in the site see the Feature Administration page."),
+		 '#weight' => 1,
+	);
+
+	$form['taxonify']['tx-analyses'] = array (
+     '#title'       => t('Analyses'),
+     '#type'        => t('checkboxes'),
+     '#description' => t("Check the analyses whose features you want to reset taxonomy.  Note: this list contains all analyses, even those that may not be synced."),
+     '#required'    => FALSE,
+     '#prefix'      => '<div id="lib_boxes">',
+     '#suffix'      => '</div>',
+     '#options'     => $lib_boxes,
+     '#weight'      => 2
+	);
+	$form['taxonify']['tx-button'] = array(
+      '#type' => 'submit',
+      '#value' => t('Set Feature Taxonomy'),
+      '#weight'      => 3
+	);
+}
+/** 
+ *
+ * @ingroup tripal_analysis
+ */
+function get_tripal_analysis_admin_form_reindex_set(&$form) {
+	// define the fieldsets
+	$form['reindex'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Reindex Analysis Features')
+	);
+
+	// get the list of libraries
+	$sql = "SELECT * FROM {Analysis} ORDER BY name";
+	$previous_db = tripal_db_set_active('chado');  // use chado database
+	$lib_rset = db_query($sql);
+	tripal_db_set_active($previous_db);  // now use drupal database
+
+	// iterate through all of the libraries
+	$lib_boxes = array();
+	while($analysis = db_fetch_object($lib_rset)){
+		$lib_boxes[$analysis->analysis_id] = "$analysis->name";
+	}
+	$form['reindex']['description'] = array(
+       '#type' => 'item',
+       '#value' => t("This option allows for reindexing of only those features that belong to the selected analyses below. All other features will be unaffected.  To reindex all features in the site see the Feature Administration page."),
+		 '#weight' => 1,
+	);
+
+	$form['reindex']['re-analyses'] = array (
+     '#title'       => t('Libraries'),
+     '#type'        => t('checkboxes'),
+     '#description' => t("Check the analyses whoee features you want to reindex. Note: this list contains all analyses, even those that may not be synced."),
+     '#required'    => FALSE,
+     '#prefix'      => '<div id="lib_boxes">',
+     '#suffix'      => '</div>',
+     '#options'     => $lib_boxes,
+     '#weight' => 2,
+	);
+	$form['reindex']['re-button'] = array(
+      '#type' => 'submit',
+      '#value' => t('Reindex Features'),
+      '#weight' => 3,
+	);
+}
+/** 
+ *
+ * @ingroup tripal_analysis
+ */
+function get_tripal_analysis_admin_form_cleanup_set(&$form) {
+	$form['cleanup'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Clean Up')
+	);
+	$form['cleanup']['description'] = array(
+       '#type' => 'item',
+       '#value' => t("With Drupal and chado residing in different databases ".
+          "it is possible that nodes in Drupal and analyses in Chado become ".
+          "\"orphaned\".  This can occur if an analysis node in Drupal is ".
+          "deleted but the corresponding chado analysis is not and/or vice ".
+          "versa. Click the button below to resolve these discrepancies."),
+       '#weight' => 1,
+	);
+	$form['cleanup']['button'] = array(
+      '#type' => 'submit',
+      '#value' => t('Clean up orphaned analyses'),
+      '#weight' => 2,
+	);
+}
+/** 
+ *
+ * @ingroup tripal_analysis
+ */
+function get_tripal_analysis_admin_form_sync_set (&$form) {
+	// define the fieldsets
+	$form['sync'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Sync Analyses')
+	);
+
+	// before proceeding check to see if we have any
+	// currently processing jobs. If so, we don't want
+	// to give the opportunity to sync analyses
+	$active_jobs = FALSE;
+	if(tripal_get_module_active_jobs('tripal_analysis')){
+		$active_jobs = TRUE;
+	}
+
+	if(!$active_jobs){
+			
+		// get the list of analyses
+		$sql = "SELECT * FROM {analysis} ORDER BY name";
+		$previous_db = tripal_db_set_active('chado');  // use chado database
+		$ana_rset = db_query($sql);
+		tripal_db_set_active($previous_db);  // now use drupal database
+			
+		// if we've added any analyses to the list that can be synced
+		// then we want to build the form components to allow the user
+		// to select one or all of them.  Otherwise, just present
+		// a message stating that all analyses are currently synced.
+		$ana_boxes = array();
+		$added = 0;
+		while($analysis = db_fetch_object($ana_rset)){
+			// check to see if the analysis is already present as a node in drupal.
+			// if so, then skip it.
+			$sql = "SELECT * FROM {chado_analysis} WHERE analysis_id = %d";
+			if(!db_fetch_object(db_query($sql,$analysis->analysis_id))){
+				$ana_boxes[$analysis->analysis_id] = "$analysis->name";
+				$added++;
+			}
+		}
+
+		// if we have analyses we need to add to the checkbox then
+		// build that form element
+		if($added > 0){
+			$ana_boxes['all'] = "All analyses";
+
+			$form['sync']['analyses'] = array (
+           '#title'       => t('Available analyses'),
+           '#type'        => t('checkboxes'),
+           '#description' => t("Check the analyses you want to sync.  Drupal ".
+              "content will be created for each of the analyses listed above. ".
+              "Select 'All analyses' to sync all of them."),
+           '#required'    => FALSE,
+           '#prefix'      => '<div id="ana_boxes">',
+           '#suffix'      => '</div>',
+           '#options'     => $ana_boxes,
+			);
+			$form['sync']['button'] = array(
+            '#type' => 'submit',
+            '#value' => t('Submit Sync Job')
+			);
+		}
+		// we don't have any analyses to select from
+		else {
+			$form['sync']['value'] = array(
+            '#value' => t('All analyses in Chado are currently synced with Drupal.')
+			);
+		}
+	}
+	// we don't want to present a form since we have an active job running
+	else {
+		$form['sync']['value'] = array(
+          '#value' => t('Currently, jobs exist related to chado analyses. Please check back later for analyses that can by synced once these jobs have finished.  You can view the status of pending jobs in the Tripal jobs page.')
+		);
+	}
+}
+/**
+ *
+ * @ingroup tripal_analysis
+ */
+function tripal_analysis_admin_validate($form, &$form_state) {
+	global $user;  // we need access to the user info
+	$job_args = array();
+
+	if ($form_state['values']['op'] == t('Submit Sync Job')) {
+
+		// check to see if the user wants to sync chado and drupal.  If
+		// so then we need to register a job to do so with tripal
+		$analyses = $form_state['values']['analyses'];
+		$do_all = FALSE;
+		$to_sync = array();
+
+		foreach ($analyses as $analysis_id){
+			if(preg_match("/^all$/i",$analysis_id)){
+				$do_all = TRUE;
+			}
+			if($analysis_id and preg_match("/^\d+$/i",$analysis_id)){
+				// get the list of analyses
+				$sql = "SELECT * FROM {analysis} WHERE analysis_id = %d";
+				$previous_db = tripal_db_set_active('chado');  // use chado database
+				$analysis = db_fetch_object(db_query($sql,$analysis_id));
+				tripal_db_set_active($previous_db);  // now use drupal database
+				$to_sync[$analysis_id] = $analysis->name;
+			}
+		}
+
+		// submit the job the tripal job manager
+		if($do_all){
+			tripal_add_job('Sync all analyses','tripal_analysis','tripal_analysis_sync_analyses',$job_args,$user->uid);
+		}
+		else{
+			foreach($to_sync as $analysis_id => $name){
+				$job_args[0] = $analysis_id;
+				tripal_add_job("Sync analysis: $name",'tripal_analysis','tripal_analysis_sync_analyses',$job_args,$user->uid);
+			}
+		}
+	}
+	// -------------------------------------
+	// Submit the Reindex Job if selected
+	if ($form_state['values']['op'] == t('Reindex Features')) {
+		global $user;  // we need access to the user info
+		$job_args = array();
+		$analyses = $form_state['values']['re-analyses'];
+		foreach ($analyses as $analysis_id){
+			if($analysis_id and preg_match("/^\d+$/i",$analysis_id)){
+				// get the analysis info
+				$sql = "SELECT * FROM {analysis} WHERE analysis_id = %d";
+				$previous_db = tripal_db_set_active('chado');  // use chado database
+				$analysis = db_fetch_object(db_query($sql,$analysis_id));
+				tripal_db_set_active($previous_db);  // now use drupal database
+				$job_args[0] = $analysis_id;
+				tripal_add_job("Reindex features for analysis: $analysis->name",'tripal_analysis',
+             'tripal_analysis_reindex_features',$job_args,$user->uid);
+			}
+		}
+	}
+
+	// -------------------------------------
+	// Submit the Taxonomy Job if selected
+	if ($form_state['values']['op'] == t('Set Feature Taxonomy')) {
+		global $user;  // we need access to the user info
+		$job_args = array();
+		$analyses = $form_state['values']['tx-analyses'];
+		foreach ($analyses as $analysis_id){
+			if($analysis_id and preg_match("/^\d+$/i",$analysis_id)){
+				// get the analysis info
+				$sql = "SELECT * FROM {analysis} WHERE analysis_id = %d";
+				$previous_db = tripal_db_set_active('chado');  // use chado database
+				$analysis = db_fetch_object(db_query($sql,$analysis_id));
+				tripal_db_set_active($previous_db);  // now use drupal database
+				$job_args[0] = $analysis_id;
+				tripal_add_job("Set taxonomy for features in analysis: $analysis->name",'tripal_analysis',
+             'tripal_analysis_taxonify_features',$job_args,$user->uid);
+			}
+		}
+	}
+
+	// -------------------------------------
+	// Submit the Cleanup Job if selected
+	if ($form_state['values']['op'] == t('Clean up orphaned analyses')) {
+		tripal_add_job('Cleanup orphaned analyses','tripal_analysis',
+         'tripal_analyses_cleanup',$job_args,$user->uid);
+	}
+
+	// -------------------------------------
+	// Save blast regular expression settings
+	if ($form_state['values']['op'] == t('Save settings')) {
+		$db_id = $form_state['values']['blastdb'];
+		$name = $form_state['values']['displayname'];
+		$gbstyle = $form_state['values']['gb_style_parser'];
+		$reg1 = $form_state['values']['hit_id'];
+		$reg2 = $form_state['values']['hit_def'];
+		$reg3 = $form_state['values']['hit_accession'];
+		
+		// Check if the blast settings exists
+		
+		$sql = "SELECT db_id FROM {tripal_analysis_blast} WHERE db_id=%d";
+		$check = db_result(db_query($sql, $db_id));
+
+		// If setting exists, update it
+		if ($check) {
+			$sql = "UPDATE {tripal_analysis_blast} ".
+   	          "SET displayname = '%s', ".
+   	          "    regex_hit_id = '%s', ".
+   	          "    regex_hit_def = '%s', ".
+   	          "    regex_hit_accession = '%s', ".
+			       "    genbank_style = %d ".
+   	          "WHERE db_id=%d";
+			db_query($sql, $name, $reg1, $reg2, $reg3, $gbstyle, $db_id);
+			// Otherwise, insert a new setting for the db
+		} else {
+			$sql = "INSERT INTO {tripal_analysis_blast} (db_id, displayname, ".
+   		       "           regex_hit_id, regex_hit_def, regex_hit_accession, genbank_style) ".
+   		       "VALUES (%d, '%s', '%s', '%s', '%s', %d) ";
+			db_query($sql, $db_id, $name, $reg1, $reg2, $reg3, $gbstyle);
+		}
+	}
+}
+

+ 226 - 0
modules/base/tripal_analysis/tripal_analysis.api.inc

@@ -0,0 +1,226 @@
+<?php
+
+/**
+ * @defgroup tripal_analysis_api Analysis Module API
+ * @ingroup tripal_analysis
+ * @ingroup tripal_api
+ */
+/****************************************************************************
+ * @section Chado Table Descriptions
+ ****************************************************************************/
+/**
+ * Implements hook_chado_analysis_schema()
+ * Purpose: To add descriptions and foreign keys to default table description
+ * Note: This array will be merged with the array from all other implementations
+ *
+ * @return
+ *    Array describing the analysis table
+ *
+ * @ingroup tripal_schema_api
+ */
+function tripal_analysis_chado_analysis_schema() {
+  $description = array();
+  
+  // Default table description in tripal_core.schema.api.inc: tripal_core_chado_analysis_schema()
+  
+  $referring_tables = array(
+      'analysisfeature',
+      'analysisprop',
+      'phylotree',
+      'quantification'
+  );
+  $description['referring_tables'] = $referring_tables;
+  
+  return $description;
+}
+
+ /****************************************************************************
+ * Implements hook_chado_analysisfeature_schema()
+ * Purpose: To add descriptions and foreign keys to default table description
+ * Note: This array will be merged with the array from all other implementations
+ *
+ * @return
+ *    Array describing the analysisfeature table
+ *
+ * @ingroup tripal_schema_api
+ */
+function tripal_analysis_chado_analysisfeature_schema() {
+  $description = array();
+
+  $description['foreign keys']['feature'] = array(
+        'table' => 'feature',
+        'columns' => array(
+          'feature_id' => 'feature_id',
+        ),
+  );
+
+  $description['foreign keys']['analysis'] = array(
+        'table' => 'analysis',
+        'columns' => array(
+          'analysis_id' => 'analysis_id',
+        ),
+  );
+  
+  return $description;
+}
+
+ /****************************************************************************
+ * Implements hook_chado_analysisfeatureprop_schema()
+ * Purpose: To add descriptions and foreign keys to default table description
+ * Note: This array will be merged with the array from all other implementations
+ *
+ * @return
+ *    Array describing the analysisfeatureprop table
+ *
+ * @ingroup tripal_schema_api
+ */
+function tripal_analysis_chado_analysisfeatureprop_schema() {
+  $description = array();
+
+  $description['foreign keys']['analysisfeature'] = array(
+        'table' => 'analysisfeature',
+        'columns' => array(
+          'analysisfeature_id' => 'analysisfeature_id',
+        ),
+  );
+
+  $description['foreign keys']['cvterm'] = array(
+        'table' => 'cvterm',
+        'columns' => array(
+          'type_id' => 'cvterm_id',
+        ),
+  );
+  return $description;
+}
+
+
+ /****************************************************************************
+  * Implements hook_chado_analysisprop_schema()
+  * Purpose: To add descriptions and foreign keys to default table description
+  * Note: This array will be merged with the array from all other implementations
+  *
+  * @return
+  *    Array describing the analysisprop table
+  *
+  * @ingroup tripal_schema_api
+  */
+function tripal_analysis_chado_analysisprop_schema() {
+  $description = array();
+
+  $description['foreign keys']['cvterm'] = array(
+        'table' => 'cvterm',
+        'columns' => array(
+          'type_id' => 'cvterm_id',
+        ),
+  );
+
+  $description['foreign keys']['analysis'] = array(
+        'table' => 'analysis',
+        'columns' => array(
+          'analysis_id' => 'analysis_id',
+        ),
+  );
+
+  return $description;
+}
+
+/**
+ * Retrieve properties of a given type for a given analysis
+ *
+ * @param $analysis_id
+ *    The analysis_id of the properties you would like to retrieve
+ * @param $property
+ *    The cvterm name of the properties to retrieve
+ *
+ * @return
+ *    An analysis chado variable with the specified properties expanded
+ *
+ * @ingroup tripal_analysis_api
+ */
+function tripal_analysis_get_property($analysis_id,$property){
+   return tripal_core_get_property('analysis',$analysis_id,$property,'tripal');
+}
+
+/**
+ * Insert a given property
+ *
+ * @param $analysis_id
+ *   The analysis_id of the property to insert
+ * @param $property
+ *   The cvterm name of the property to insert
+ * @param $value
+ *   The value of the property to insert
+ * @param $update_if_present
+ *   A boolean indicated whether to update the record if it's already present
+ *
+ * @return
+ *   True of success, False otherwise
+ *
+ * @ingroup tripal_analysis_api
+ */
+function tripal_analysis_insert_property($analysis_id,$property,$value,$update_if_present = 0){
+    return tripal_core_insert_property('analysis',$analysis_id,$property,'tripal',$value,$update_if_present);
+}
+
+/**
+ * Update a given property
+ *
+ * @param $analysis_id
+ *   The analysis_id of the property to update
+ * @param $property
+ *   The cvterm name of the property to update
+ * @param $value
+ *   The value of the property to update
+ * @param $insert_if_missing 
+ *   A boolean indicated whether to insert the record if it's absent
+ *
+ * Note: The property will be identified using the unique combination of the $analysis_id and $property
+ * and then it will be updated with the supplied value
+ *
+ * @return
+ *   True of success, False otherwise
+ *
+ * @ingroup tripal_analysis_api
+ */
+function tripal_analysis_update_property($analysis_id,$property,$value,$insert_if_missing = 0){
+   return tripal_core_update_property('analysis',$analysis_id,$property,'tripal',$value, $insert_if_missing);
+}
+
+/**
+ * Delete a given property
+ *
+ * @param $analysis_id
+ *   The analysis_id of the property to delete
+ * @param $property
+ *   The cvterm name of the property to delete
+ *
+ * Note: The property will be identified using the unique combination of the $analysis_id and $property
+ * and then it will be deleted
+ *
+ * @return
+ *   True of success, False otherwise 
+ *
+ * @ingroup tripal_analysis_api
+ */
+function tripal_analysis_delete_property($analysis_id,$property){
+   return tripal_core_delete_property('analysis',$analysis_id,$property,'tripal');
+}
+/**
+ * Retreives the node of a sync'ed analysis
+ *
+ * @param $analysis_id
+ *   The analysis_id of the property to delete
+ *
+ * @return
+ *   node of analysis on success, null otherwise 
+ *
+ * @ingroup tripal_analysis_api
+ */
+function tripal_analysis_get_node($analysis_id){
+   $sql = "SELECT * 
+           FROM chado_analysis CA
+              INNER JOIN node N on CA.nid = N.nid
+           WHERE analysis_id = %d";
+   $node = db_fetch_object(db_query($sql,$analysis_id));
+   return $node;
+}

+ 10 - 0
modules/base/tripal_analysis/tripal_analysis.info

@@ -0,0 +1,10 @@
+; $Id:
+name = Tripal Analysis
+description = A module that controls the display of all analysis sub-modules. This module also interfaces the GMOD chado database with Drupal, providing viewing, inserting and editing of chado analyses.
+core = 6.x
+project = tripal_analysis
+package = Tripal
+version = 6.x-0.3.1b
+dependencies[] = tripal_core
+dependencies[] = tripal_db
+dependencies[] = tripal_cv

+ 140 - 0
modules/base/tripal_analysis/tripal_analysis.install

@@ -0,0 +1,140 @@
+<?php
+//$Id:
+
+/*******************************************************************************
+ * Implementation of hook_install().
+ */
+function tripal_analysis_install() {
+
+	// create the module's data directory
+	tripal_create_moddir('tripal_analysis');
+
+	// Use schema API to create database table.
+	drupal_install_schema('tripal_analysis');
+	
+	// Create analysisfeatureprop table in chado. This cannot be accomplished
+	// by calling drupal_install_schema because it's not in the drupal db. This
+	// table is used to store Blast xml and Interpro html/goterms
+	$previous_db = tripal_db_set_active('chado');
+	if (!db_table_exists('analysisfeatureprop')) {
+	   $sql = "CREATE TABLE {analysisfeatureprop} (".
+	          "  analysisfeatureprop_id SERIAL PRIMARY KEY, ".
+	          "  analysisfeature_id INTEGER NOT NULL REFERENCES analysisfeature(analysisfeature_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, ".
+	          "  type_id INTEGER NOT NULL REFERENCES cvterm(cvterm_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, ".
+	          "  value TEXT, ".
+	          "  rank INTEGER NOT NULL, ".
+	          "  CONSTRAINT analysisfeature_id_type_id_rank UNIQUE(analysisfeature_id, type_id, rank)".
+	          ")";
+	   db_query($sql);
+	}
+	tripal_db_set_active($previous_db);
+
+   tripal_add_cvterms('analysis_type','The type of analysis was performed. This value is automatically set by each Tripal Analysis module and should be equal to the module name (e.g. tripal_analysis_blast, tripal_analysis_go).');
+	tripal_add_cvterms('analysis_date','The date that an analysis was performed.');
+	tripal_add_cvterms('analysis_short_name','A computer legible (no spaces '.
+      'or special characters) abbreviation for the analysis.');
+}
+
+/*******************************************************************************
+ * Implementation of hook_uninstall().
+ */
+function tripal_analysis_uninstall() {
+	// Use schema API to delete database table.
+	drupal_uninstall_schema('tripal_analysis');
+	// Remove analysis nodes from drupal.
+	$sql_ana_id = "SELECT nid, vid ".
+                 "FROM {node} ".
+                 "WHERE type like 'chado_analysi%'";
+	$result = db_query($sql_ana_id);
+	while ($ana = db_fetch_object($result)) {
+		node_delete($ana->nid);
+	}
+}
+
+/*******************************************************************************
+ * Implementation of hook_schema() creates two tables. chado_analysis table
+ * stores nodes that are also saved in the analysis table of chado database.
+ * tripal_analysis table stores the sub-module names, such as
+ * tripal_analysis_blast, that are registered with this module.
+ */
+function tripal_analysis_schema() {
+	// chado_analysis table
+	$schema['chado_analysis'] = array(
+      'fields' => array(
+         'vid' => array(
+            'type' => 'int',
+            'unsigned' => TRUE,
+            'not null' => TRUE,
+            'default' => 0
+	),
+         'nid' => array(
+            'type' => 'int',
+            'unsigned' => TRUE,
+            'not null' => TRUE,
+            'default' => 0
+	),
+         'analysis_id' => array(
+            'type' => 'int',
+            'not null' => TRUE,
+            'default' => 0
+	)
+	),
+      'indexes' => array(
+         'analysis_id' => array('analysis_id')
+	),
+      'unique keys' => array(
+         'nid_vid' => array('nid','vid'),
+         'vid' => array('vid')
+	),
+      'primary key' => array('nid'),
+	);
+
+	// tripal_analysis table
+	$schema['tripal_analysis'] = array(
+      'description' => t('Table to store analysis sub-modules'),
+      'fields' => array(
+         'modulename' => array(
+            'type' => 'text',
+            'size' => 'small',
+            'not null' => TRUE,
+            'description' => t('The module name. Tripal Analysis will use the '.
+                               'module name to call module_setting_form()')
+	)
+	),
+      'unique keys' => array(
+         'modulename' => array('modulename')
+	)
+	);
+	return $schema;
+}
+
+/*******************************************************************************
+ * Implementation of hook_requirements(). Make sure 'Tripal Core' is enabled
+ * before installation
+ */
+function tripal_analysis_requirements($phase) {
+   $requirements = array();
+   if ($phase == 'install') {
+      if (!function_exists('tripal_create_moddir')) {
+         $requirements ['tripal_analysis'] = array(
+            'title' => "tripal_analysis",
+            'value' => "error. Some required modules are just being installed. Please try again.",
+            'severity' => REQUIREMENT_ERROR,
+         );
+      }
+   }
+   return $requirements;
+}
+
+/*******************************************************************************
+ * Provide update script for adding new cvterms
+ */
+function tripal_analysis_update_6001(){
+   // we have some new cvterms to add
+   tripal_add_cvterms('based_on_analysis','The analysis that this analysis was based on. For example, blast/kegg/interpro analyses are based on a unigene analysis. The unigene analysis_id should be stored in analysisprop as the rank using this cvterm. The name of said unigene analysis can be inserted as the value in analysisprop.');
+   tripal_add_cvterms('additional_files', 'Additional files for this analysis. Each file should be separated by a semi-colon and have this format: <file description>, <file path>;');
+   $ret = array(
+      '#finished' => 1,
+   );   
+   return $ret;
+}

+ 1060 - 0
modules/base/tripal_analysis/tripal_analysis.module

@@ -0,0 +1,1060 @@
+<?php
+// $Id:
+
+/**
+ * @file
+ * Contains all the main hook implementations for the tripal_analysis module
+ */
+
+/**
+ * @defgroup tripal_analysis Analysis Module
+ * @{
+ * Provides functions for managing chado analysis' including creating details pages for each one
+ * @}
+ * @ingroup tripal_modules
+ */
+
+require('tripal_analysis.api.inc');
+require('tripal_analysis_privacy.inc');
+
+
+/**
+ *
+ *
+ * @ingroup tripal_analysis
+ */
+function tripal_analysis_register_child($modulename){
+   $sql = "INSERT INTO {tripal_analysis} (modulename) VALUES ('%s')";
+   db_query($sql, $modulename);
+}
+/**
+ *
+ *
+ * @ingroup tripal_analysis
+ */
+function tripal_analysis_unregister_child($modulename){
+   if (db_table_exists('tripal_analysis')) {
+      $sql = "DELETE FROM {tripal_analysis} WHERE modulename = '%s'";
+      db_query($sql, $modulename);
+   }
+}
+
+/**
+ *
+ * @ingroup tripal_analysis
+ */
+function tripal_analysis_init(){
+   drupal_add_js(drupal_get_path('theme', 'tripal').'/js/tripal_analysis.js');
+}
+
+/**
+ * tripal_analysis_menu()
+ * HOOK: Implementation of hook_menu()
+ * Entry points and paths of the module
+ *
+ * @ingroup tripal_analysis
+ */
+function tripal_analysis_menu() {
+	// Display available analyses
+	$items['analyses'] = array(
+      'menu_name' => ('primary-links'), //Enable the 'Analysis' primary link
+      'title' => t('Analyses'),
+      'page callback' => 'tripal_analysis_show_analyses',
+      'access arguments' => array('access chado_analysis content'),
+      'type' => MENU_NORMAL_ITEM
+	);
+	//Sync analysis
+	$items['chado_sync_analyses'] = array(
+     'title' => t('Sync Data'),
+     'page callback' => 'tripal_analysis_sync_analyses',
+     'access arguments' => array('administer site configuration'),
+     'type' => MENU_CALLBACK
+	);
+	// Tripal Analysis administrative settings
+	$items['admin/tripal/tripal_analysis'] = array(
+      'title' => 'Analyses',
+      'description' => 'Basic Description of Tripal Analysis Module Functionality.',
+      'page callback' => 'tripal_analysis_module_description_page',
+      'access arguments' => array('administer site configuration'),
+      'type' => MENU_NORMAL_ITEM,
+      'file' => 'tripal_analysis.admin.inc',
+	);
+	
+	$items['admin/tripal/tripal_analysis/configuration'] = array(
+      'title' => 'Configuration',
+      'description' => 'Settings for the displays of analysis results.',
+      'page callback' => 'drupal_get_form',
+      'page arguments' => array('tripal_analysis_admin'),
+      'access arguments' => array('administer site configuration'),
+      'type' => MENU_NORMAL_ITEM,
+      'file' => 'tripal_analysis.admin.inc',
+	);
+	return $items;
+}
+
+/** 
+ * Display the summary view of analyses when click on the 'Analyses'
+ * primary-link
+ *
+ * @ingroup tripal_analysis
+ */
+function tripal_analysis_show_analyses (){
+	// Show libraries stored in Drupal's {chado_analysis} table
+	$sql = "SELECT COUNT(analysis_id) FROM {chado_analysis}";
+	$no_ana = db_result(db_query ($sql));
+	if($no_ana != 0) {
+		$analyses = get_chado_analyses ();
+		return theme('tripal_analysis_analysis_page', $analyses);
+	} else {
+		return t("No analysis available at this time.");
+	}
+
+}
+
+/** 
+ *  Provide information to drupal about the node types that we're creating
+ *  in this module
+ *
+ * @ingroup tripal_analysis
+ */
+function tripal_analysis_node_info() {
+	$nodes = array();
+	$nodes['chado_analysis'] = array(
+      'name' => t('Analysis'),
+      'module' => 'chado_analysis',
+      'description' => t('An analysis from the chado database'),
+      'has_title' => FALSE,
+      'title_label' => t('Analysis'),
+      'has_body' => FALSE,
+      'body_label' => t('Analysis Description'),
+      'locked' => TRUE
+	);
+	return $nodes;
+}
+/** 
+ *  When a new chado_analysis node is created we also need to add information
+ *  to our chado_analysis table.  This function is called on insert of a new
+ *  node of type 'chado_analysis' and inserts the necessary information.
+ *
+ * @ingroup tripal_analysis
+ */
+function chado_analysis_insert($node){
+	global $user;
+	// Create a timestamp so we can insert it into the chado database
+	$time = $node->timeexecuted;
+	$month = $time['month'];
+	$day = $time['day'];
+	$year = $time['year'];
+	$timestamp = $month.'/'.$day.'/'.$year;
+
+	// If this analysis already exists then don't recreate it in chado
+	$analysis_id = $node->analysis_id;
+	if ($analysis_id) {
+		$sql = "SELECT analysis_id ".
+               "FROM {Analysis} ".
+               "WHERE analysis_id = %d ";
+		$previous_db = tripal_db_set_active('chado');
+		$analysis = db_fetch_object(db_query($sql, $node->analysis_id));
+		tripal_db_set_active($previous_db);
+	}
+
+	// If the analysis doesn't exist then let's create it in chado.
+	if(!$analysis){
+      // insert and then get the newly inserted analysis record
+      $values = array(
+          'name' => $node->analysisname, 
+          'description' => $node->description,
+          'program' => $node->program,
+          'programversion' => $node->programversion,
+          'algorithm' => $node->algorithm,
+          'sourcename' => $node->sourcename,
+          'sourceversion' => $node->sourceversion,
+          'sourceuri' => $node->sourceuri,
+          'timeexecuted' => $timestamp
+      );
+      if(tripal_core_chado_insert('analysis',$values)){
+         $analysis = tripal_core_chado_select('analysis',array('*'),$values);
+         $analysis_id = $analysis[0]->analysis_id;
+      }
+	}
+
+	// Make sure the entry for this analysis doesn't already exist in the
+	// chado_analysis table if it doesn't exist then we want to add it.
+	$node_check_sql = "SELECT * FROM {chado_analysis} ".
+                     "WHERE analysis_id = %d";
+	$node_check = db_fetch_object(db_query($node_check_sql, $analysis_id));
+	if(!$node_check){
+		// next add the item to the drupal table
+		$sql = "INSERT INTO {chado_analysis} (nid, vid, analysis_id) ".
+             "VALUES (%d, %d, %d)";
+		db_query($sql,$node->nid,$node->vid,$analysis_id);
+		// Create a title for the analysis node using the unique keys so when the
+		// node is saved, it will have a title
+		$record = new stdClass();
+		// If the analysis has a name, use it as the node title. If not, construct
+		// the title using program, programversion, and sourcename
+		if ($node->analysisname) {
+			$record->title = $node->analysisname;
+		} else {
+			//Construct node title as "program (version)
+			$record->title = "$node->program ($node->programversion)";
+		}
+		$record->nid = $node->nid;
+		drupal_write_record('node',$record,'nid');
+		drupal_write_record('node_revisions',$record,'nid');
+	}
+	
+	if($node->setpermissions) {
+		$job_args[0] = $analysis_id;
+		$job_args[1] = $node->nid;
+		tripal_add_job("Set permission for analysis associated features",'tripal_analysis',
+	                              'tripal_analysis_set_feature_permission', $job_args, $user->uid);
+	}
+
+   // add the analysis to the node object for
+   // use by other analysis modules that may be using this function
+   $node->analysis = $analysis;
+   $node->analysis_id = $analysis_id; // we need to set this for children
+}
+/** 
+ *
+ * @ingroup tripal_analysis
+ */
+function chado_analysis_delete($node){
+	// Before removing, get analysis_id so we can remove it from chado database
+	// later
+	$sql_drupal = "SELECT analysis_id ".
+                 "FROM {chado_analysis} ".
+                 "WHERE nid = %d ".
+                 "AND vid = %d";
+	$analysis_id = db_result(db_query($sql_drupal, $node->nid, $node->vid));
+
+	// Remove data from the {chado_analysis}, {node}, and {node_revisions} tables
+	$sql_del = "DELETE FROM {chado_analysis} ".
+              "WHERE nid = %d ".
+              "AND vid = %d";
+	db_query($sql_del, $node->nid, $node->vid);
+	$sql_del = "DELETE FROM {node} ".
+              "WHERE nid = %d ".
+              "AND vid = %d";
+	db_query($sql_del, $node->nid, $node->vid);
+	$sql_del = "DELETE FROM {node_revisions} ".
+              "WHERE nid = %d ".
+              "AND vid = %d";
+	db_query($sql_del, $node->nid, $node->vid);
+
+	//Remove from analysis and analysisprop tables of chado database as well
+	$previous_db = tripal_db_set_active('chado');
+	db_query("DELETE FROM {analysis} WHERE analysis_id = %d", $analysis_id);
+	tripal_db_set_active($previous_db);
+}
+
+/**
+ * Update analyses
+ *
+ * @ingroup tripal_analysis
+ */
+function chado_analysis_update($node){
+	global $user;
+	if($node->revision){
+		// TODO -- decide what to do about revisions
+	} else {
+		// Create a timestamp so we can insert it into the chado database
+		$time = $node->timeexecuted;
+		$month = $time['month'];
+		$day = $time['day'];
+		$year = $time['year'];
+		$timestamp = $month.'/'.$day.'/'.$year;
+
+		// get the analysis_id for this node:
+		$sql = "SELECT analysis_id ".
+             "FROM {chado_analysis} ".
+             "WHERE vid = %d";
+		$analysis_id = db_fetch_object(db_query($sql, $node->vid))->analysis_id;
+
+		$sql = "UPDATE {analysis} ".
+             "SET name = '%s', ".
+             "    description = '%s', ".
+             "    program = '%s', ".
+             "    programversion = '%s', ".
+             "    algorithm = '%s', ".
+             "    sourcename = '%s', ".
+             "    sourceversion = '%s', ".
+             "    sourceuri = '%s', ".
+             "    timeexecuted = '%s' ".
+             "WHERE analysis_id = %d ";
+
+		$previous_db = tripal_db_set_active('chado');  // use chado database
+		db_query($sql, $node->analysisname, $node->description, $node->program,
+		$node->programversion,$node->algorithm,$node->sourcename,
+		$node->sourceversion, $node->sourceuri, $timestamp, $analysis_id);
+      tripal_db_set_active($previous_db);  // switch back to drupal database
+		
+		// Create a title for the analysis node using the unique keys so when the
+		// node is saved, it will have a title
+		$record = new stdClass();
+		// If the analysis has a name, use it as the node title. If not, construct
+		// the title using program, programversion, and sourcename
+		if ($node->analysisname) {
+			$record->title = $node->analysisname;
+		} else {
+			//Construct node title as "program (version)
+			$record->title = "$node->program ($node->programversion)";
+		}
+
+		if($node->setpermissions) {
+			$job_args[0] = $analysis_id;
+			$job_args[1] = $node->nid;
+			tripal_add_job("Set permission for analysis associated features",'tripal_analysis',
+		                              'tripal_analysis_set_feature_permission', $job_args, $user->uid);
+		}
+		
+		$record->nid = $node->nid;
+		drupal_write_record('node',$record,'nid');
+		drupal_write_record('node_revisions',$record,'nid');
+	}
+}
+
+/**
+ *  When editing or creating a new node of type 'chado_analysis' we need
+ *  a form.  This function creates the form that will be used for this.
+ *
+ * @ingroup tripal_analysis
+ */
+function chado_analysis_form ($node){
+
+   $analysis = $node->analysis;
+
+   // add in the description column. It is a text field and may not be included
+   // if the text is too big.
+   $analysis = tripal_core_expand_chado_vars($analysis,'field','analysis.description');
+
+   // get form defaults
+   $analysis_id = $node->analysis_id;
+   if(!$analysis_id){
+      $analysis_id = $analysis->analysis_id;
+   }
+   $analysisname = $node->analysisname;
+   if(!$analysisname){
+      $analysisname = $analysis->name;
+   }
+   $program = $node->program;
+   if(!$program){
+      $program = $analysis->program;
+   }
+   $programversion = $node->programversion;
+   if(!$programversion){
+      $programversion = $analysis->programversion;
+   }
+   $algorithm = $node->algorithm;
+   if(!$algorithm){
+      $algorithm = $analysis->algorithm;
+   }
+   $sourcename = $node->sourcename;
+   if(!$sourcename){
+      $sourcename = $analysis->sourcename;
+   }
+   $sourceversion = $node->sourceversion;
+   if(!$sourceversion){
+      $sourceversion = $analysis->sourceversion;
+   }
+   $sourceuri = $node->sourceuri;
+   if(!$sourceuri){
+      $sourceuri = $analysis->sourceuri;
+   }
+   $timeexecuted = $node->timeexecuted;
+   if(!$timeexecuted){
+      $timeexecuted = $analysis->timeexecuted;
+   }
+   $description = $node->description;
+   if(!$description){
+      $description = $analysis->description;
+   }
+	$form = array();
+	$form['title']= array(
+      '#type' => 'hidden',
+      '#default_value' => $node->title,
+	);
+	$form['analysis_id']= array(
+      '#type' => 'hidden',
+      '#default_value' => $analysis_id,
+	);
+	$form['analysisname']= array(
+      '#type' => 'textfield',
+      '#title' => t('Analysis Name'),
+      '#required' => FALSE,
+      '#default_value' => $analysisname,
+      '#description' => t("This should be a handy short identifier that 
+         describes the analysis succintly as possible which helps the user find analyses."),
+	);
+	$form['program']= array(
+      '#type' => 'textfield',
+      '#title' => t('Program'),
+      '#required' => TRUE,
+      '#default_value' => $program,
+      '#description' => t("Program name, e.g. blastx, blastp, sim4, genscan."),
+	);
+	$form['programversion']= array(
+      '#type' => 'textfield',
+      '#title' => t('Program Version'),
+      '#required' => TRUE,
+      '#default_value' => $programversion,
+      '#description' => t("Version description, e.g. TBLASTX 2.0MP-WashU [09-Nov-2000]"),
+	);
+	$form['algorithm']= array(
+      '#type' => 'textfield',
+      '#title' => t('Algorithm'),
+      '#required' => FALSE,
+      '#default_value' => $algorithm,
+      '#description' => t("Algorithm name, e.g. blast."),
+	);
+	$form['sourcename']= array(
+      '#type' => 'textfield',
+      '#title' => t('Source Name'),
+      '#required' => TRUE,
+      '#default_value' => $sourcename,
+      '#description' => t('The name of the source data.  This could be a file name, data set name or a 
+           small description for how the data was collected.  For long descriptions use the description field below'),
+
+	);
+	$form['sourceversion']= array(
+      '#type' => 'textfield',
+      '#title' => t('Source Version'),
+      '#required' => FALSE,
+      '#default_value' => $sourceversion,
+      '#description' => t('If the source dataset has a version, include it here'),
+	);
+	$form['sourceuri']= array(
+      '#type' => 'textfield',
+      '#title' => t('Source URI'),
+      '#required' => FALSE,
+      '#default_value' => $sourceuri,
+      '#description' => t("This is a permanent URL or URI for the source of the analysis. 
+         Someone could recreate the analysis directly by going to this URI and 
+         fetching the source data (e.g. the blast database, or the training model)."),
+	);
+	// Get time saved in chado
+	$default_time = $timeexecuted;
+	$year = preg_replace("/^(\d+)-\d+-\d+ .*/", "$1", $default_time);
+	$month = preg_replace("/^\d+-0?(\d+)-\d+ .*/", "$1", $default_time);
+	$day = preg_replace("/^\d+-\d+-0?(\d+) .*/", "$1", $default_time);
+	// If the time is not set, use current time
+	if (!$default_time) {
+		$default_time = time();
+		$year = format_date($default_time, 'custom', 'Y');
+		$month = format_date($default_time, 'custom', 'n');
+		$day = format_date($default_time, 'custom', 'j');
+	}
+	$form['timeexecuted']= array(
+      '#type' => 'date',
+      '#title' => t('Time Executed'),
+      '#required' => TRUE,
+      '#default_value' => array(
+         'year' => $year,
+         'month' => $month,
+         'day' => $day,
+	    ),
+	);
+	$form['description']= array(
+      '#type' => 'textarea',
+      '#rows' => 15,
+      '#title' => t('Description and/or Program Settings'),
+      '#required' => FALSE,
+      '#default_value' => $description,
+      '#description' => t('Please provide all necessary information to allow
+         someone to recreate the analysis, including materials and methods
+         for collection of the source data and performing the analysis'),
+	);
+	/* Set permissions for all features associated with this analysis */
+	if (module_exists('node_privacy_byrole')) {
+		$form['setpermissions'] = array(
+				'#type' => 'checkbox',
+	      	'#title' => t('Submit a job to set the same permissions for all features associated with this analysis'),
+	      	'#default_value' => FALSE,
+			   '#weight' => 10
+		);
+	}
+	
+	return $form;
+}
+
+/**
+ *  When a node is requested by the user this function is called to allow us
+ *  to add auxiliary data to the node object.
+ *
+ * @ingroup tripal_analysis
+ */
+function chado_analysis_load($node){
+
+   // get the feature details from chado
+   $analysis_id = chado_get_id_for_node('analysis',$node);
+
+   $values = array('analysis_id' => $analysis_id);
+   $analysis = tripal_core_generate_chado_var('analysis',$values);
+
+   $additions->analysis = $analysis;
+   return $additions;
+}
+
+/**
+ *  This function customizes the view of the chado_analysis node.  It allows
+ *  us to generate the markup.
+ *
+ * @ingroup tripal_analysis
+ */
+function chado_analysis_view ($node, $teaser = FALSE, $page = FALSE) {
+	// use drupal's default node view:
+	if (!$teaser) {
+		$node = node_prepare($node, $teaser);
+		// When previewing a node submitting form, it shows 'Array' instead of
+		// correct date format. We need to format the date here
+		$time = $node->timeexecuted;
+		if(is_array($time)){
+			$month = $time['month'];
+			$day = $time['day'];
+			$year = $time['year'];
+			$timestamp = $year.'-'.$month.'-'.$day;
+			$node->timeexecuted = $timestamp;
+		}
+	}
+	return $node;
+}
+
+/**
+ * Synchronize analyses from chado to drupal
+ *
+ * @ingroup tripal_analysis
+ */
+function tripal_analysis_sync_analyses ($analysis_id = NULL, $job_id = NULL){
+	global $user;
+	$page_content = '';
+    
+	if(!$analysis_id){
+		$sql = "SELECT Analysis_id, name, description, program, ".
+   		 "  programversion, algorithm, sourcename, sourceversion, sourceuri, ".
+          "  timeexecuted ".
+          "FROM {Analysis} ";
+		$previous_db = tripal_db_set_active('chado');  // use chado database
+		$results = db_query($sql);
+		tripal_db_set_active($previous_db);  // now use drupal database
+	} else {
+		$sql = "SELECT Analysis_id, name, description, program, ".
+   		  "  programversion, algorithm, sourcename, sourceversion, sourceuri, ".
+          "  timeexecuted ".
+          "FROM {Analysis} ".
+          "WHERE analysis_id = %d";
+		$previous_db = tripal_db_set_active('chado');  // use chado database
+		$results = db_query($sql,$analysis_id);
+		tripal_db_set_active($previous_db);  // now use drupal database
+	}
+
+
+	// We'll use the following SQL statement for checking if the analysis
+	// already exists as a drupal node.
+	$sql = "SELECT * FROM {chado_analysis} ".
+          "WHERE analysis_id = %d";
+
+	while($analysis = db_fetch_object($results)){
+        print "syncing analysis ";
+        print $analysis->name;
+        print ", ";
+        print $analysis->analysis_id;
+        print "\n";
+
+		// check if this analysis already exists in the drupal database. if it
+		// does then skip this analysis and go to the next one.
+		if(!db_fetch_object(db_query($sql,$analysis->analysis_id))){
+
+			$new_node = new stdClass();
+        
+			// try to access analysis type for this analysis
+			$sql = "SELECT * FROM {analysisprop} 
+                    WHERE analysis_id = %d 
+                    AND type_id =  
+                        (SELECT cvterm_id from {cvterm} where name = '%s')
+            ";
+			$previous_db = tripal_db_set_active('chado');
+			$analysis_type = db_fetch_object(db_query($sql, $analysis->analysis_id, "analysis_type"));
+			tripal_db_set_active($previous_db);
+
+			// Get the type of analysis using cvterm_id
+            // Current possibilities: kegg, unigene, interpro, blast
+			if ($analysis_type) {
+
+				// This is a unigene analysis
+				if ($analysis_type->value == 'tripal_analysis_unigene') {
+					$new_node->type = 'chado_analysis_unigene';
+				// This is a blast analysis
+				} else if ($analysis_type->value == 'tripal_analysis_blast') {
+					$new_node->type = 'chado_analysis_blast';
+			   // This is a interpro analysis
+				} else if ($analysis_type->value == 'tripal_analysis_interpro') {
+					$new_node->type = 'chado_analysis_interpro';
+			   // This is a kegg analysis
+				} else if ($analysis_type->value == 'tripal_analysis_kegg' ){
+				   $new_node->type = 'chado_analysis_kegg';
+				} else {
+				   $new_node->type = 'chado_analysis';
+				}
+			// If it doesn't exist, this analysis is generic
+			} else {
+				$new_node->type = 'chado_analysis';
+			}
+
+            print "analysis type is $new_node->type\n";
+
+			$new_node->uid = $user->uid;
+			$new_node->analysis_id = $analysis->analysis_id;
+			$new_node->analysisname = $analysis->name;
+			$new_node->description = $analysis->description;
+			$new_node->program = $analysis->program;
+			$new_node->programversion = $analysis->programversion;
+			$new_node->algorithm = $analysis->algorithm;
+			$new_node->sourcename = $analysis->sourcename;
+			$new_node->sourceversion = $analysis->sourceversion;
+			$new_node->sourceuri = $analysis->sourceuri;
+			$new_node->timeexecuted = $analysis->timeexecuted;
+
+			// If the analysis has a name, use it as the node title. If not,
+			// construct the title using program, programversion, and sourcename
+			if ($new_node->analysisname) {
+				$new_node->title = $new_node->analysisname;
+			} else {
+				//Construct node title as "program (version)"
+				$new_node->title = "$analysis->program ($analysis->programversion)";
+			}
+
+			node_validate($new_node);
+
+            $errors = form_get_errors();
+
+            if($errors){
+                print_r($errors);
+            }
+            else{
+			// if(!form_get_errors()){
+				$node = node_submit($new_node);
+				node_save($node);
+
+				if($node->nid){
+					$page_content .= "Added $new_node->title<br>";
+				}
+			}
+		} else {
+			$page_content .= "Skipped $new_node->title<br>";
+		}
+	}
+	return $page_content;
+}
+
+/**
+ *
+ * @ingroup tripal_analysis
+ */
+function chado_analysis_validate($node,&$form){
+   // use the analysis parent to validate the node
+   tripal_analysis_validate($node, $form); 
+}
+/**
+ *
+ *@ingroup tripal_analysis
+ */
+function tripal_analysis_validate($node, &$form){
+##dprint_r($node);
+
+    // This validation is being used for three activities:
+    // CASE A: Update a node that exists in both drupal and chado
+    // CASE B: Synchronizing a node from chado to drupal
+    // CASE C: Inserting a new node that exists in niether drupal nor chado
+
+    // Only nodes being updated will have an nid already
+    if($node->nid){
+        //---------------------------------------------------
+        // CASE A: We are validating a form for updating an existing node
+        //---------------------------------------------------
+
+        // TO DO: check that the new fields don't yield a non-unique primary key in chado
+    }
+    else{
+        // To differentiate if we are syncing or creating a new analysis altogther, see if an
+        // analysis_id already exists
+
+        if($node->analysis_id){
+
+            //---------------------------------------------------
+            // CASE B: Synchronizing a node from chado to drupal
+            //---------------------------------------------------
+
+        }
+        else{
+
+            //---------------------------------------------------
+            // CASE C: We are validating a form for inserting a new node
+            //---------------------------------------------------
+            // The primary key for the chado analysis table is 
+            // program, programversion, sourcename
+
+
+            $values = array(
+               'program' => $node->program,
+               'programversion' => $node->programversion,
+               'sourcename' => $node->sourcename,
+            );
+            $analysis = tripal_core_chado_select('analysis', array('analysis_id'),$values);
+            if(sizeof($analysis) > 0){
+               form_set_error('program','Cannot add the analysis with this program,  
+                  program version and source name. An analysis with these values already exists.');
+               return;
+            }
+        }
+    }
+}
+/**
+ * Display help and module information
+ * @param path which path of the site we're displaying help
+ * @param arg array that holds the current path as would be returned from arg()
+ * function
+ * @return help text for the path
+ *
+ * @ingroup tripal_analysis
+ */
+function tripal_analysis_help($path, $arg) {
+	$output = '';
+	switch ($path) {
+		case "admin/help#tripal_analysis":
+			$output = '<p>'.
+			t("Displays links to nodes created on this date").
+                '</p>';
+			break;
+	}
+	return $output;
+}
+/**
+ *  The following function proves access control for users trying to
+ *  perform actions on data managed by this module
+ *
+ * @ingroup tripal_analysis
+ */
+function chado_analysis_access($op, $node, $account){
+
+	if ($op == 'create') {
+		if(!user_access('create chado_analysis content', $account)){
+			return FALSE;
+      }
+	}
+	if ($op == 'update') {
+		if (!user_access('edit chado_analysis content', $account)) {
+			return FALSE;
+		}
+	}
+	if ($op == 'delete') {
+		if (!user_access('delete chado_analysis content', $account)) {
+			return FALSE;
+		}
+	}
+	if ($op == 'view') {
+		if(!user_access('access chado_analysis content', $account)){
+         return FALSE;
+      }
+	}
+   return NULL;
+}
+
+/**
+ *  Set the permission types that the chado module uses.  Essentially we
+ *  want permissionis that protect creation, editing and deleting of chado
+ #  data objects
+ *
+ * @ingroup tripal_analysis
+ */
+function tripal_analysis_perm(){
+	return array(
+      'access chado_analysis content',
+      'create chado_analysis content',
+      'delete chado_analysis content',
+      'edit chado_analysis content',
+	);
+}
+
+/**
+ *  We need to let drupal know about our theme functions and their arguments.
+ *  We create theme functions to allow users of the module to customize the
+ *  look and feel of the output generated in this module
+ *
+ * @ingroup tripal_analysis
+ */
+function tripal_analysis_theme () {
+	return array(
+      'tripal_analysis_analysis_page' => array (
+         'arguments' => array('analyses'),
+	    ),
+	);
+}
+
+/**
+ * This function uses analysis_id's of all drupal analysis nodes as input and
+ * pull the analysis information (name, description, program, programversion,
+ * algorithm, sourcename, sourceversion, sourceuri, timeexecuted) from
+ * chado database. The return type is an object array that stores $analysis
+ * objects sorted by program
+ *
+ * @ingroup tripal_analysis
+ */
+function get_chado_analyses() {
+
+	$sql_drupal = "SELECT COUNT (analysis_id) FROM {chado_analysis}";
+	$no_orgs = db_result(db_query($sql_drupal));
+	if ($no_orgs != 0) {
+		$sql = "SELECT analysis_id, CA.nid, type FROM {chado_analysis} CA INNER JOIN node ON CA.nid = node.nid";
+		$result = db_query($sql);
+		$previous_db = tripal_db_set_active('chado');
+		$sql = "SELECT Analysis_id, name, description, program, 
+		               programversion, algorithm, sourcename, sourceversion, 
+		               sourceuri, timeexecuted
+		        FROM {Analysis} WHERE analysis_id=%d";
+		$analyses = array();
+		$count = 0;
+		while ($data = db_fetch_object($result)) {
+			$analysis = db_fetch_object(db_query($sql, $data->analysis_id));
+			$analysis->node_id = $data->nid;
+			$analysis->node_type = $data->type;
+			// Use node_type as the key so we can sort by node type
+			// Since node_type is not unique by itself, we need to add
+			// $count to the key
+			$sortedBy = $analysis->timeexecuted;
+			$analyses ["$sortedBy$count"] = $analysis;
+			$count ++;
+		}
+		tripal_db_set_active($previous_db);
+
+		//Sort analyses by time, descending order
+		krsort($analyses, SORT_STRING);
+
+		return $analyses;
+	}
+}
+/**
+ *
+ *
+ * @ingroup tripal_analysis
+ */
+function theme_tripal_analysis_analysis_page($analyses) {
+   
+	$output = "<br>Analyses are listed in the descending order of their execution time.<br><a id=\"tripal_expandableBox_toggle_button\" onClick=\"toggleExpandableBoxes()\">[-] Collapse All</a>";
+	
+   foreach($analyses as $analysis){
+		// Prepare information for html output
+		$ana_node_url = url("node/$analysis->node_id");
+	   if ($analysis->sourceversion) {
+         $ver = "($analysis->sourceversion)";
+      }
+      $date =  preg_replace("/^(\d+-\d+-\d+) .*/","$1",$analysis->timeexecuted);
+      
+      // Generate html output
+		$output .= "<div class=\"tripal_chado_analysis-info-box\" style=\"padding:5px\">
+                         <div class=\"tripal_expandableBox\">
+                           <h3>$analysis->name ($date)</h3>
+                         </div>
+                         <div class=\"tripal_expandableBoxContent\">
+                           <span>
+                             <table class=\"tripal_chado_analysis_content\">
+                               <tr><td>
+                                 Name: <a href=\"$ana_node_url\">$analysis->name</a>
+                               </td></tr>
+                               <tr><td>
+                                 Program: $analysis->program ($analysis->programversion)
+                               </td></tr>
+                               <tr><td>
+                                 Algorithm: $analysis->algorithm
+                               </td></tr>
+		                         <tr><td>
+                                 Source: $analysis->sourcename $ver
+		                         </td></tr>
+		                         <tr><td>
+                                 Source URI: $analysis->sourceuri
+                               </td></tr>
+                               <tr><td>
+                                 Executed Time:$date
+                               </td></tr>
+                               <tr><td>
+                                 Description: $analysis->description
+                               </td></tr>
+                             </table>
+                           </span>
+                         </div>
+                       </div>";
+	}
+
+	return $output;
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_analysis
+ */
+function tripal_analyses_cleanup($dummy = NULL, $job_id = NULL) {
+
+    // select each node from node table with chado_analysis as type
+    // check to make sure it also exists in chado_analysis table, delete if it doesn't
+    // (this should never, ever happen, but we'll double check anyway)
+	$sql_drupal_node = "SELECT * FROM {node} WHERE type LIKE 'chado_analysis%' order by nid";
+    $sql_drupal_ca = "SELECT * from {chado_analysis} WHERE nid = %d";
+
+	$results = db_query($sql_drupal_node);
+	while($node = db_fetch_object($results)){
+        $ca_record = db_fetch_object(db_query($sql_drupal_ca, $node->nid));
+        if(!$ca_record){ 
+            node_delete($node->nid); 
+			$message = "Missing in chado_analysis table.... DELETING node: $nid->nid\n";
+			watchdog('tripal_analysis',$message,array(),WATCHDOG_WARNING);
+        }
+	}
+
+    // get nodes from chado_analysis table and load into array, saving chado analysis_id
+    // as we iterate through, we'll check that they are actual nodes and
+    // delete if they aren't
+    // (this should never, ever happen, but we'll double check anyway)
+	$sql_drupal_ca2 = "SELECT * FROM {chado_analysis}";
+	$sql_drupal_node2 = "SELECT * FROM {node} WHERE type LIKE 'chado_analysis%' AND nid = %d";
+
+	$results = db_query($sql_drupal_ca2);
+	$nid2aid = array();
+	while($ca_record = db_fetch_object($results)){
+        $node = db_fetch_object(db_query($sql_drupal_node2, $ca_record->nid));
+        if(!$node){ 
+			db_query("DELETE FROM {chado_analysis} WHERE nid = $ca_record->nid");
+			$message = "chado_analysis missing node.... DELETING chado_analysis record with nid: $ca_record->nid\n";
+			watchdog('tripal_analysis',$message,array(),WATCHDOG_WARNING);
+        }
+        else{
+		    $nid2aid[$ca_record->nid] = $ca_record->analysis_id;
+        }
+	}
+
+	// iterate through all of the chado_analysis nodes in drupal
+    // and delete those that aren't valid in chado
+    $sql_chado = "SELECT analysis_id from {analysis} WHERE analysis_id = %d";
+
+	foreach($nid2aid as $nid => $aid){
+        $previous_db = tripal_db_set_active('chado');
+		$chado_record = db_fetch_object(db_query($sql_chado,$aid));
+        tripal_db_set_active($previous_db);
+		if(!$chado_record){
+            node_delete($nid); 
+			$message = "Missing in analysis table in chado.... DELETING node: $nid\n";
+			watchdog('tripal_analysis',$message,array(),WATCHDOG_WARNING);
+		}
+	}
+	return '';
+}
+ 
+/**
+function tripal_analysis_reindex_features ($analysis_id = NULL, $job_id = NULL){
+	$i = 0;
+
+	// if the caller provided a analysis_id then get all of the features
+	// associated with the analysis. Otherwise get all sequences associated
+	// with all libraries.
+	if(!$analysis_id){
+		$sql = "SELECT Analysis_id, Feature_id ".
+          "FROM {Analysisfeature} ".
+          "ORDER BY analysis_id";
+		$previous_db = tripal_db_set_active('chado');  // use chado database
+		$results = db_query($sql);
+		tripal_db_set_active($previous_db);  // now use drupal database
+	} else {
+		$sql = "SELECT Analysis_id, Feature_id ".
+             "FROM {Analysisfeature} ".
+             "WHERE analysis_id = %d";
+             "ORDER BY analysis_id";
+		$previous_db = tripal_db_set_active('chado');  // use chado database
+		$results = db_query($sql,$analysis_id);
+		tripal_db_set_active($previous_db);  // now use drupal database
+	}
+
+	// load into ids array
+	$count = 0;
+	$ids = array();
+	while($id = db_fetch_object($results)){
+		$ids[$count] = $id->feature_id;
+		$count++;
+	}
+
+	$interval = intval($count * 0.01);
+	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));
+		}
+		tripal_feature_sync_feature ($feature_id);
+		$i++;
+	}
+} */
+ 
+/**
+function tripal_analysis_taxonify_features ($analysis_id = NULL, $job_id = NULL){
+	$i = 0;
+
+	// if the caller provided a analysis_id then get all of the features
+	// associated with the analysis. Otherwise get all sequences assoicated
+	// with all libraries.
+	if(!$analysis_id){
+		$sql = "SELECT Analysis_id, Feature_id ".
+          "FROM {Analysisfeature} ".
+          "ORDER BY analysis_id";
+		$previous_db = tripal_db_set_active('chado');  // use chado database
+		$results = db_query($sql);
+		tripal_db_set_active($previous_db);  // now use drupal database
+	} else {
+		$sql = "SELECT Analysis_id, Feature_id ".
+             "FROM {Analysisfeature} ".
+             "WHERE analysis_id = %d";
+             "ORDER BY analysis_id";
+		$previous_db = tripal_db_set_active('chado');  // use chado database
+		$results = db_query($sql,$analysis_id);
+		tripal_db_set_active($previous_db);  // now use drupal database
+	}
+
+	// load into ids array
+	$count = 0;
+	$ids = array();
+	while($id = db_fetch_object($results)){
+		$ids[$count] = $id->feature_id;
+		$count++;
+	}
+
+	// make sure our vocabularies are set before proceeding
+	tripal_feature_set_vocabulary();
+
+	// use this SQL for getting the nodes
+	$nsql =  "SELECT * FROM {chado_feature} CF ".
+            "  INNER JOIN {node} N ON N.nid = CF.nid ".
+            "WHERE feature_id = %d";
+
+	// iterate through the features and set the taxonomy
+	$interval = intval($count * 0.01);
+	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));
+		}
+		$node = db_fetch_object(db_query($nsql,$feature_id));
+		tripal_feature_set_taxonomy($node,$feature_id);
+		$i++;
+	}
+}
+*/
+/**
+ * Implements hook_views_api()
+ * Purpose: Essentially this hook tells drupal that there is views support for
+ *  for this module which then includes tripal_analysis.views.inc where all the
+ *  views integration code is
+ *
+ * @ingroup tripal_analysis
+ */ 
+function tripal_analysis_views_api() {
+   return array(
+      'api' => 2.0,
+   );
+}

+ 546 - 0
modules/base/tripal_analysis/tripal_analysis.views.inc

@@ -0,0 +1,546 @@
+<?php
+
+/**
+ *  @file
+ *  This file contains the basic functions for views integration of
+ *  chado/tripal analysis tables. Supplementary functions can be found in
+ *  ./views/
+ *
+ *  Documentation on views integration can be found at 
+ *  http://views2.logrus.com/doc/html/index.html.
+ */
+
+/**
+ * @defgroup tripal_analysis_views Analysis Views Integration
+ * @ingroup views
+ * @ingroup tripal_analysis
+ */
+
+require('views/analysis.views.inc');
+require('views/chado_analysis.views.inc');
+require('views/misc_tables.views.inc'); 
+/**
+ * Implements hook_views_data()
+ *
+ * Purpose: Describe chado/tripal tables & fields to views
+ *
+ * @return: a data array which follows the structure outlined in the
+ *   views2 documentation for this hook. Essentially, it's an array of table
+ *   definitions keyed by chado/tripal table name. Each table definition 
+ *   includes basic details about the table, fields in that table and
+ *   relationships between that table and others (joins)
+ *
+ * @ingroup tripal_analysis_views
+ */
+function tripal_analysis_views_data()  {
+  $data = array();
+
+  $data = array_merge($data, retrieve_analysis_views_data());
+  $data = array_merge($data, retrieve_chado_analysis_views_data());
+  $data = array_merge($data, retrieve_analysis_misc_tables_views_data());
+
+  return $data;
+}
+
+/**
+ * Implements hook_views_handlers()
+ *
+ * Purpose: Register all custom handlers with views
+ *   where a handler describes either "the type of field", 
+ *   "how a field should be filtered", "how a field should be sorted"
+ *
+ * @return
+ *   An array of handler definitions
+ *
+ * @ingroup tripal_analysis_views
+ */
+function tripal_analysis_views_views_handlers() {
+  return array(
+    'info' => array(
+      'path' => drupal_get_path('module', 'tripal_analysis') . '/views/handlers',
+    ),
+    'handlers' => array(
+      'views_handler_field_computed_analysis_nid' => array(
+        'parent' => 'views_handler_field_numeric',
+      ),
+      'views_handler_field_readable_date' => array(
+        'parent' => 'views_handler_field',
+      ),
+    ),
+  );
+}
+
+/**
+ * Implementation of hook_views_data_alter().
+ */
+function tripal_analysis_views_data_alter(&$data) {
+
+  if( !(is_array($db_url) and array_key_exists('chado',$db_url)) ){
+
+    // Add featuer relationship to node
+    $data['node']['analysis_chado_nid'] = array(
+      'group' => 'Analysis',
+      'title' => 'Analysis Node',
+      'help' => 'Links Chado analysis Fields/Data to the Nodes in the current View.',
+      'real field' => 'nid',
+      'relationship' => array(
+        'handler' => 'views_handler_relationship',
+        'title' => t('Node => Chado'),
+        'label' => t('Node => Chado'),
+        'real field' => 'nid',
+        'base' => 'chado_analysis',
+        'base field' => 'nid'
+      ),
+    );
+  }
+  
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_analysis_views
+ */
+function tripal_analysis_views_default_views () {
+  $views = array();
+  
+  // Main default view
+  // List all cvterms based on cv
+  $view = new view;
+  $view->name = 'all_analysis';
+  $view->description = 'A listing of all analysis\'';
+  $view->tag = 'chado';
+  $view->view_php = '';
+  $view->base_table = 'analysis';
+  $view->is_cacheable = FALSE;
+  $view->api_version = 2;
+  $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
+  $handler = $view->new_display('default', 'Defaults', 'default');
+  $handler->override_option('fields', array(
+    'name' => array(
+      'label' => 'Name',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'link_to_node' => 1,
+      'exclude' => 0,
+      'id' => 'name',
+      'table' => 'analysis',
+      'field' => 'name',
+      'relationship' => 'none',
+    ),
+    'algorithm' => array(
+      'label' => 'Algorithm',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'algorithm',
+      'table' => 'analysis',
+      'field' => 'algorithm',
+      'relationship' => 'none',
+    ),
+    'program' => array(
+      'label' => 'Program',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'program',
+      'table' => 'analysis',
+      'field' => 'program',
+      'relationship' => 'none',
+    ),
+    'programversion' => array(
+      'label' => 'Program Version',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'programversion',
+      'table' => 'analysis',
+      'field' => 'programversion',
+      'relationship' => 'none',
+    ),
+    'description' => array(
+      'label' => 'Description',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'description',
+      'table' => 'analysis',
+      'field' => 'description',
+      'relationship' => 'none',
+    ),
+    'sourcename' => array(
+      'label' => 'Source Name',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'sourcename',
+      'table' => 'analysis',
+      'field' => 'sourcename',
+      'relationship' => 'none',
+    ),
+    'sourceuri' => array(
+      'label' => 'Source URL',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'sourceuri',
+      'table' => 'analysis',
+      'field' => 'sourceuri',
+      'relationship' => 'none',
+    ),
+    'sourceversion' => array(
+      'label' => 'Source Version',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'sourceversion',
+      'table' => 'analysis',
+      'field' => 'sourceversion',
+      'relationship' => 'none',
+    ),
+    'num_features' => array(
+      'label' => 'Number of Features',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'num_features',
+      'table' => 'analysis',
+      'field' => 'num_features',
+      'relationship' => 'none',
+    ),
+    'timeexecuted' => array(
+      'label' => 'Time Executed',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'date_format' => 'large',
+      'custom_date_format' => '',
+      'exclude' => 0,
+      'id' => 'timeexecuted',
+      'table' => 'analysis',
+      'field' => 'timeexecuted',
+      'relationship' => 'none',
+    ),
+  ));
+  $handler->override_option('filters', array(
+    'program' => array(
+      'operator' => '=',
+      'value' => 'All',
+      'group' => '0',
+      'exposed' => TRUE,
+      'expose' => array(
+        'use_operator' => 0,
+        'operator' => 'program_op',
+        'identifier' => 'program',
+        'label' => 'Program',
+        'optional' => 1,
+        'remember' => 0,
+      ),
+      'case' => 0,
+      'id' => 'program',
+      'table' => 'analysis',
+      'field' => 'program',
+      'relationship' => 'none',
+    ),
+    'sourcename' => array(
+      'operator' => 'contains',
+      'value' => '',
+      'group' => '0',
+      'exposed' => TRUE,
+      'expose' => array(
+        'use_operator' => 0,
+        'operator' => 'sourcename_op',
+        'identifier' => 'sourcename',
+        'label' => 'Source Name Contains',
+        'optional' => 1,
+        'remember' => 0,
+      ),
+      'case' => 0,
+      'id' => 'sourcename',
+      'table' => 'analysis',
+      'field' => 'sourcename',
+      'relationship' => 'none',
+    ),
+  ));
+  $handler->override_option('access', array(
+    'type' => 'perm',
+    'perm' => 'access chado_analysis content',
+  ));
+  $handler->override_option('cache', array(
+    'type' => 'none',
+  ));
+  $handler->override_option('title', 'Analysis');
+  $handler->override_option('empty', 'No analysis match the supplied criteria.');
+  $handler->override_option('empty_format', '1');
+  $handler->override_option('items_per_page', 50);
+  $handler->override_option('use_pager', '1');
+  $handler->override_option('style_plugin', 'table');
+  $handler->override_option('style_options', array(
+    'grouping' => '',
+    'override' => 1,
+    'sticky' => 0,
+    'order' => 'asc',
+    'columns' => array(
+      'algorithm' => 'algorithm',
+      'description' => 'description',
+      'name' => 'name',
+      'num_features' => 'num_features',
+      'program' => 'program',
+      'programversion' => 'programversion',
+      'sourcename' => 'sourcename',
+      'sourceuri' => 'sourceuri',
+      'sourceversion' => 'sourceversion',
+      'timeexecuted' => 'timeexecuted',
+    ),
+    'info' => array(
+      'algorithm' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'description' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'name' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'num_features' => array(
+        'separator' => '',
+      ),
+      'program' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'programversion' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'sourcename' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'sourceuri' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'sourceversion' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'timeexecuted' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+    ),
+    'default' => 'timeexecuted',
+  ));
+  $handler = $view->new_display('page', 'Page', 'page_1');
+  $handler->override_option('path', 'analyses');
+  $handler->override_option('menu', array(
+    'type' => 'normal',
+    'title' => 'Analyses',
+    'description' => '',
+    'weight' => '0',
+    'name' => 'primary-links',
+  ));
+  $handler->override_option('tab_options', array(
+    'type' => 'none',
+    'title' => '',
+    'description' => '',
+    'weight' => 0,
+    'name' => 'navigation',
+  ));
+  $views[$view->name] = $view;
+  
+  return $views;
+}

+ 82 - 0
modules/base/tripal_analysis/tripal_analysis_privacy.inc

@@ -0,0 +1,82 @@
+<?php
+
+/*
+ *  Perform permission check by analysis_id only if 'node_privacy_byrole' module is enabled
+ */
+function tripal_analysis_check_permission ($analysis_id) {
+	if (module_exists('node_privacy_byrole')) {
+		global $user;
+		$roles = $user->roles;
+		$node_access = 0;
+		foreach ($roles AS $rid => $role) {
+			$p_sql = "SELECT grant_view FROM {node_access} NA INNER JOIN {chado_analysis} CA ON NA.nid = CA.nid WHERE analysis_id=%d AND gid = %d";
+			$access = db_result(db_query($p_sql,$analysis_id, $rid));
+			if ($access == 1) {
+				$node_access = 1;
+				break;
+			}
+		}
+		if ($node_access == 1 || $user->uid == 1) {
+			return TRUE;
+		} else {
+			return FALSE;
+		}
+	
+	// If 'node_privacy_byrole' module is not enabled, return TRUE;
+	} else {
+		return TRUE;
+	}
+}
+
+/*
+ * Set permissions for features associated with an analysis
+ */
+function tripal_analysis_set_feature_permission ($analysis_id, $nid) {
+	
+	print "Updating feature permissions:\n";
+	
+	// Get features associated with the analysis
+	$sql = "SELECT feature_id FROM {analysisfeature} WHERE analysis_id = %d";
+	$previous_db = db_set_active('chado');
+	$features = db_query($sql, $analysis_id);
+	db_set_active($previous_db);
+	
+	// Convert feature_id into node_id
+	$feature_nids = array ();
+	$counter = 0;
+	$sql = "SELECT nid FROM {chado_feature} WHERE feature_id = %d";
+	while ($feature = db_fetch_object($features)) {
+		$feature_nids [$counter] = db_result(db_query($sql, $feature->feature_id));
+		$counter ++;
+	}
+	
+	//Convert analysis_id into node_id
+	$ana_nid = db_result(db_query("SELECT nid FROM {chado_analysis} WHERE analysis_id = $analysis_id"));
+	// Get available roles
+	$roles = array_keys(user_roles());
+	
+	$interval = intval(($counter + 1) * 0.01);
+	$idx_iterations = 1;
+	
+	// Update permission node by node
+	foreach ($feature_nids AS $fnid) {
+		if ($idx_iterations % $interval == 0) {
+			$percentage = (int) ($idx_iterations / ($counter + 1) * 100);
+			tripal_job_set_progress($job_id, $percentage);
+			print $percentage."% ";
+		}
+
+		db_query("DELETE FROM {node_privacy_byrole} WHERE nid = %d AND realm = 'node_privacy_byrole_role'", $fnid);
+		foreach($roles AS $rid) {
+			// Get permissions of this analysis for this role
+			$rsql = "SELECT * FROM {node_privacy_byrole} WHERE gid = %d AND nid = %d AND realm = 'node_privacy_byrole_role'";
+			$ana_perm = db_fetch_object(db_query($rsql, $rid, $ana_nid));
+			db_query("INSERT INTO {node_privacy_byrole} (nid, gid, realm, grant_view, grant_update, grant_delete)
+                  VALUES (%d, %d, '%s', %d, %d, %d)", $fnid, $rid, 'node_privacy_byrole_role', $ana_perm->grant_view,
+                  $ana_perm->grant_update, $ana_perm->grant_delete);
+		}
+		$node = node_load($fnid);
+		node_save($node);
+		$idx_iterations ++;
+	}	
+}

+ 45 - 0
modules/base/tripal_analysis/views/README

@@ -0,0 +1,45 @@
+
+This directory contains all additional views code needed to integrate this module with views2.
+
+FILE/FOLDER DESCRIPTIONS
+---------------------------
+<chado table name>.views.inc:
+	contains a single function retrieve_<chado table name>_views_data()
+	which describes that table to views. This function is called by
+	tripal_analysis_views_data() in ../tripal_analysis.views.inc.
+	For more information on the form of this data array look up the
+	views2 documentation for hook_views_data() 
+	-http://views2.logrus.com/doc/html/index.html
+	
+handlers/
+	Each file contained within this folder defines a views handler. Only custom
+	handlers are included in this folder and each must be described in 
+	hook_views_handlers() in ../tripal_analysis.views.inc.
+	A views handler does one of the following:
+		1) describe the type of a field and how it should be displayed
+		2) describe a method to sort this field
+		3) describe a method to filter this field
+
+
+STANDARDS TO FOLLOW
+---------------------------
+
+1. All table definition files should be named tablename.views.inc
+2. All handlers should be in a handlers sub-directory and follow the naming convention of
+   views handlers (ie: views_handler_field/filter/sort_handlername.inc )
+
+Views Table Definitions:
+- Please use the template files provided whenever you are describing a new table to views.
+   For any table in chado simply copy template.table_defn.views.inc to tablename.views.inc and 
+   follow the instructions listed at the top of the template file.
+- ONLY ONE TABLE DEFINITION PER FILE
+- To join a chado table to it's drupal node simply copy template.node_join.views.inc to 
+   basetablename.views.inc and replace all XXX with basetablename.
+   
+   NOTE: Creating the table definition file is not enough. You also need to call the 
+         retrieve_XXX_views_data() function from ../tripal_analysis.views.inc:tripal_analysis_views_data()
+         by adding the following line:
+            $data = array_merge($data, retrieve_XXX_views_data());
+         to the function and including the file directly above the function (blow the function 
+         header by adding:
+            require_once('views/XXX.views.inc');

+ 290 - 0
modules/base/tripal_analysis/views/analysis.views.inc

@@ -0,0 +1,290 @@
+<?php
+	/**
+ * Purpose: this function returns the portion of the data array 
+ *   which describes the analysis table, it's fields and any joins between it and other tables
+ * @see tripal_analysis_views_data() --in tripal_analysis.views.inc
+ *
+ * @todo Add support for analysisprop table
+ * @todo Add support for multiple analysis' listed per feature
+ * @todo Add join to node table within if <chado/drupal same db>; also addd if not around nid field
+ *
+ * BASE TABLE: analysis
+ * @code
+ *   create table analysis (
+ *       analysis_id serial not null,
+ *       primary key (analysis_id),
+ *       name varchar(255),
+ *       description text,
+ *       program varchar(255) not null,
+ *       programversion varchar(255) not null,
+ *       algorithm varchar(255),
+ *       sourcename varchar(255),
+ *       sourceversion varchar(255),
+ *       sourceuri text,
+ *       timeexecuted timestamp not null default current_timestamp,
+ *       constraint analysis_c1 unique (program,programversion,sourcename)
+ *   );
+ * @endcode
+ *
+ * @ingroup tripal_analysis_views
+ */
+function retrieve_analysis_views_data() {
+	global $db_url;
+	$data = array();
+
+  // if the chado database is not local to the drupal database
+  // then we need to set the database name.  This should always
+  // be 'chado'.
+  if(is_array($db_url) and array_key_exists('chado',$db_url)){
+     $database = 'chado';
+  }
+	
+  // Basic table definition
+  $data['analysis']['table']['group'] = 'Chado Analysis';
+ 	$data['analysis']['table']['base'] = array(
+ 		'field' => 'analysis_id',
+ 		'title' => t('Chado Analysis'),
+ 		'help' => t("An analysis is a particular type of a computational analysis; it may be a blast of one sequence against another, or an all by all blast, or a different kind of analysis altogether. It is a single unit of computation."),
+ 	);
+  if($database){
+     $data['analysis']['table']['base']['database'] = $database;
+  }
+
+  // Define relationships between this table and others
+  $data['analysis']['table']['join'] = array(
+    'analysisfeature' => array(
+      'left_field' => 'analysis_id',
+      'field' => 'analysis_id',
+    ),
+    'feature' => array(
+      'left_table' => 'analysisfeature',
+      'left_field' => 'analysis_id',
+      'field' => 'analysis_id',
+    ),
+  );
+
+  // Describe the joins with the analysis_feature table
+  $data['analysisfeature']['table']['join'] = array(
+    'feature' => array(
+      'left_field' => 'feature_id',
+      'field' => 'feature_id',
+    ),
+  );
+	
+	// Table Field Definitions----------------------
+	// Field: analysis_id (primary key)
+	$data['analysis']['analysis_id'] = array(
+	  'title' => 'analysis ID',
+	  'help' => 'The primary key of the analysis table.',
+	  'field' => array(
+	    'handler' => 'views_handler_field_numeric',
+	    'click sortable' => TRUE,
+	  ),
+	  'filter' => array(
+	    'handler' => 'views_handler_filter_numeric',
+	  ),
+	  'sort' => array(
+	    'handler' => 'views_handler_sort',
+	  ),
+	);
+	
+  // Calculated Field: Node ID
+  //  use custom field handler to query drupal for the node ID
+  //  this is only needed if chado is in a separate database from drupal
+  if($database){
+		$data['analysis']['analysis_nid'] = array(
+	  	'title' => 'Node ID',
+	  	'help' => 'The node ID for the current analysis',
+	  	'field' => array(
+	    	'handler' => 'views_handler_field_computed_analysis_nid',
+	  	),
+		);
+  } else {
+    // Add relationship between chado_analysis and analysis
+    $data['analysis']['analysis_nid'] = array(
+      'group' => 'Analysis',
+      'title' => 'Analysis Node',
+      'help' => 'Links Chado Analysis Fields/Data to the Nodes in the current View.',
+      'real field' => 'analysis_id',
+      'relationship' => array(
+        'handler' => 'views_handler_relationship',
+        'title' => t('Analysis => Chado'),
+        'label' => t('Analysis => Chado'),
+        'real field' => 'analysis_id',
+        'base' => 'chado_analysis',
+        'base field' => 'analysis_id'
+      ),
+    );
+
+  }
+	
+	// Field: name (varchar 255)
+ 	$data['analysis']['name'] = array(
+    'title' => t('Name'),
+    'help' => t(''),
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+  // if joined to the node table add a "Link to Node" option for the field  
+  if (!$database) {
+    $data['analysis']['name']['field']['handler'] = 'views_handler_field_node_optional';
+  }
+
+  // Field: description (text)
+ 	$data['analysis']['description'] = array(
+    'title' => t('Description'),
+    'help' => t(''),
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+
+  // Field: program (varchar 255)
+ 	$data['analysis']['program'] = array(
+    'title' => t('Program'),
+    'help' => t('Program name, e.g. blastx, blastp, sim4, genscan.'),
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_chado_select_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+
+  // Field: program version (varchar 255)
+ 	$data['analysis']['programversion'] = array(
+    'title' => t('Program Version'),
+    'help' => t('Version description, e.g. TBLASTX 2.0MP-WashU [09-Nov-2000].'),
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_chado_select_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+
+  // Field: algorithm (varchar 255)
+ 	$data['analysis']['algorithm'] = array(
+    'title' => t('Algorithm'),
+    'help' => t('Algorithm name, e.g. blast.'),
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_chado_select_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+
+ 	$data['analysis']['sourcename'] = array(
+    'title' => t('Source Name'),
+    'help' => t('Source name, e.g. cDNA, SwissProt.'),
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+
+  // Field: source version (varchar 255)
+ 	$data['analysis']['sourceversion'] = array(
+    'title' => t('Source Version'),
+    'help' => t('The version of the source.'),
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+
+  // Field: source URI/URL (text)
+ 	$data['analysis']['sourceuri'] = array(
+    'title' => t('Source URL'),
+    'help' => t('This is an optional, permanent URL or URI for the source of the analysis.'),
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+
+  // Field: time executed (datetime)
+ 	$data['analysis']['timeexecuted'] = array(
+    'title' => 'Time Executed',
+    'help' => 'The date & time when this analysis was executed.',
+    'field' => array(
+      'handler' => 'views_handler_field_readable_date',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort_date',
+    ),
+  );
+
+ 	return $data;
+}

+ 84 - 0
modules/base/tripal_analysis/views/chado_analysis.views.inc

@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * Purpose: this function returns the portion of the data array 
+ *   which describes the chado_analysis drupal table, it's fields and any joins between it and other tables
+ * @see tripal_analysis_views_data() --in tripal_analysis.views.inc
+ *
+ * The main need for description of this table to views is to join chado data with drupal nodes
+ *
+ *
+ * @ingroup tripal_analysis_views
+ */
+function retrieve_chado_analysis_views_data () {
+	global $db_url;
+  $data = array();
+  
+  // if the chado database is not local to the drupal database
+  // then we need to set the database name.  This should always
+  // be 'chado'.
+  if(is_array($db_url) and array_key_exists('chado',$db_url)){
+     // return empty data array b/c if chado is external then no join to the nodetable can be made
+     return $data;
+  }
+
+  // Basic table definition
+  $data['chado_analysis']['table'] = array(
+    'field' => 'nid',
+  );
+  
+  // Note: No joins need to be made from $data['analysis']['table']
+  
+  // Join the chado analysis table to analysis
+  $data['chado_analysis']['table']['join']['analysis'] = array(
+  	'left_field' => 'analysis_id',
+  	'field' => 'analysis_id',
+  );
+  
+  // Join the node table to chado analysis
+  $data['node']['table']['join']['chado_analysis'] = array(
+  	'left_field' => 'nid',
+  	'field' => 'nid',
+  );
+  
+  // Join the node table to analysis
+  $data['node']['table']['join']['analysis'] = array(
+  	'left_table' => 'chado_analysis',
+  	'left_field' => 'nid',
+  	'field' => 'nid',
+  );  
+
+  // Add relationship between chado_analysis and analysis
+  $data['chado_analysis']['analysis_nid'] = array(
+    'group' => 'Analysis',
+    'title' => 'Analysis Node',
+    'help' => 'Links Chado Analysis Fields/Data to the Nodes in the current View.',
+    'real field' => 'analysis_id',
+    'relationship' => array(
+      'handler' => 'views_handler_relationship',
+      'title' => t('Chado => Analysis'),
+      'label' => t('Chado => Analysis'),
+      'real field' => 'analysis_id',
+      'base' => 'analysis',
+      'base field' => 'analysis_id'
+    ),
+  );
+
+  // Add node relationship to analysis
+  $data['chado_analysis']['analysis_chado_nid'] = array(
+    'group' => 'Analysis',
+    'title' => 'Analysis Node',
+    'help' => 'Links Chado Analysis Fields/Data to the Nodes in the current View.',
+    'real field' => 'nid',
+    'relationship' => array(
+      'handler' => 'views_handler_relationship',
+      'title' => t('Chado => Node'),
+      'label' => t('Chado => Node'),
+      'real field' => 'nid',
+      'base' => 'node',
+      'base field' => 'nid'
+    ),
+  );
+  
+	return $data;
+}

+ 23 - 0
modules/base/tripal_analysis/views/handlers/views_handler_field_computed_analysis_nid.inc

@@ -0,0 +1,23 @@
+<?php
+
+/**
+ *
+ *
+ * @ingroup tripal_analysis
+ */
+class views_handler_field_computed_analysis_nid extends views_handler_field_numeric {
+	function construct() {
+		parent::construct();
+		drupal_set_message('Computed Analysis NID Handler loaded');
+		$this->additional_fields['analysis_id'] = array('table' => 'analysis', 'field' => 'analysis_id');
+	}
+
+	function query() { 
+		$this->ensure_my_table();
+		$this->add_additional_fields(); 
+	}
+
+	function render($values) { 
+		return $values->analysis_nid;
+	}
+} 

+ 37 - 0
modules/base/tripal_analysis/views/misc_tables.views.inc

@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * Purpose: Allows the analysis module to add fields to other module views
+ *
+ *   For example, a field counting the number of analysis' associted with a given feature would be
+ *   added to the feature view as follows:
+ *   @code
+      $data['feature']['num_analysis''] = array(
+        'title' => "Number of analysis'",
+        'help' => "Provides a count of the number of analysis' associated with a given feature",
+        'field' => array(
+          'handler' => 'views_handler_field_chado_count',
+          'table_to_query' => 'analysis_feature',
+        ),
+      ); 
+ *   @endcode
+ *
+ * @ingroup tripal_analysis_views
+ */
+function retrieve_analysis_misc_tables_views_data() {
+  $data = array();
+ 
+  // Table: Feature---------------------------------------------------------------------------------
+  // Calculated Field: Number of analysis' (Count -Int)
+  // Provides the number of analysis' for a given feature
+  $data['feature']['num_analysis'] = array(
+    'title' => "Number of analysis'",
+    'help' => "Provides a count of the number of analysis' associated with a given feature",
+    'field' => array(
+      'handler' => 'views_handler_field_chado_count',
+      'table_to_query' => 'analysisfeature',
+    ),
+  );  
+  
+  return $data;
+}

+ 84 - 0
modules/base/tripal_analysis/views/template.node_join.views.inc

@@ -0,0 +1,84 @@
+<?php
+
+/*************************************************************************
+ * @file: THIS IS A TEMPLATE AND SHOULD NOT BE INCLUDED IN THE MODULE CODE
+ *
+ *  - simply replace all XXX with the original chado table you want to join to it's drupal nodes.
+ *    (ie: If you want to join features to their drupal nodes then XXX=feature)
+ * 
+ *  NOTE: Creating the table definition file is not enough. You also need to call the 
+ *        retrieve_XXX_views_data() function from ../tripal_analysis.views.inc in 
+ *        tripal_analysis_views_data() by adding the following line:
+ *           $data = array_merge($data, retrieve_XXX_views_data());
+ *        to the function and including the file directly above the function (blow the function 
+ *        header by adding:
+ *           require_once('views/XXX.views.inc');
+ *
+ *  REMOVE THIS COMMENT IN THE COPY!
+ */ 
+ 
+/**
+ *  @file
+ *  This file defines the data array for a given chado table. This array
+ *  is merged into a larger array containing definitions of all tables associated
+ *  with this module in:
+ *  @see tripal_analysis.views.inc --in tripal_analysis_views_data()
+ *
+ *  Note: All chado tables are joined to their drupal nodes through the chado_XXX linking table. 
+ *        This file simply defines this linking table and joins the three tables together.
+ *        No modification of XXX.views.inc is needed.
+ *
+ *  Documentation on views integration can be found at 
+ *  http://views2.logrus.com/doc/html/index.html.
+ */
+ 
+/**
+ * Purpose: this function returns the portion of the data array 
+ *   which describes the chado_XXX drupal table, it's fields and any joins between it 
+ *   and other tables
+ * @see tripal_analysis_views_data() --in tripal_analysis.views.inc
+ *
+ * The main need for description of this table to views is to join chado data with drupal nodes
+ *
+ */
+function retrieve_chado_XXX_views_data () {
+	global $db_url;
+  $data = array();
+  
+  // if the chado database is not local to the drupal database
+  // then we need to set the database name.  This should always
+  // be 'chado'.
+  if(is_array($db_url) and array_key_exists('chado',$db_url)){
+     // return empty data array b/c if chado is external then no join to the nodetable can be made
+     return $data;
+  }
+
+  //Basic table definition-----------------------------------
+  $data['chado_XXX']['table'] = array(
+    'field' => 'nid',
+  );
+  
+  //Relationship Definitions---------------------------------
+  // Note: No joins need to be made from $data['XXX']['table']
+  
+  // Join the chado_XXX table to XXX
+  $data['chado_XXX']['table']['join']['XXX'] = array(
+  	'left_field' => 'XXX_id',
+  	'field' => 'XXX_id',
+  );
+  
+  // Join the node table to chado_XXX
+  $data['node']['table']['join']['chado_XXX'] = array(
+  	'left_field' => 'nid',
+  	'field' => 'nid',
+  );
+  
+  // Join the node table to XXX
+  $data['node']['table']['join']['XXX'] = array(
+  	'left_table' => 'chado_XXX',
+  	'left_field' => 'nid',
+  	'field' => 'nid',
+  );  
+
+	return $data;
+}

+ 211 - 0
modules/base/tripal_analysis/views/template.table_defn.views.inc

@@ -0,0 +1,211 @@
+<?php
+
+/*************************************************************************
+ * @file: THIS IS A TEMPLATE AND SHOULD NOT BE INCLUDED IN THE MODULE CODE
+ *
+ *   - Every instance of XXX should be replaced with the name of your table
+ *   - If this is a base table (you want a view where every row is a row from this table)
+ *     then change $data['XXX']['table'] to $data['XXX']['table']['base'] 
+ *     and $data['XXX']['table']['database'] to $data['XXX']['table']['base']['database']
+ *   - Relationships between this table and others: YYY is the table you are trying to join to this
+ *     one. You want to join a table to this one if this table contains a foreign key to the other
+ *     table. If the join between this table and another is through a linking table
+ *     (ie: library-XXX/YYY => library_feature-XY => feature-XXX/YYY) then make the join in both
+ *     directions (ie: in the file XXX.views.inc and the file YYY.views.inc
+ *   - Create a field definition for each field in this table using the example fields already
+ *     listed. Match the type of the database field to the field definition listed below.
+ *     (ie: for a text/varchar field from the database use plain_text_field below)
+ * 
+ *  NOTE: Creating the table definition file is not enough. You also need to call the 
+ *        retrieve_XXX_views_data() function from ../tripal_analysis.views.inc in 
+ *        tripal_analysis_views_data() by adding the following line:
+ *           $data = array_merge($data, retrieve_XXX_views_data());
+ *        to the function and including the file directly above the function (blow the function 
+ *        header by adding:
+ *           require_once('views/XXX.views.inc');
+ *
+ *  REMOVE THIS COMMENT IN THE COPY!
+ */ 
+ 
+/**
+ *  @file
+ *  This file defines the data array for a given chado table. This array
+ *  is merged into a larger array containing definitions of all tables associated
+ *  with this module in:
+ *  @see tripal_analysis.views.inc --in tripal_analysis_views_data()
+ *
+ *  Documentation on views integration can be found at 
+ *  http://views2.logrus.com/doc/html/index.html.
+ */
+
+/*************************************************************************
+ * Purpose: this function returns the portion of the data array 
+ *   which describes the XXX table, it's fields and any joins between it and other tables
+ * @see tripal_analysis_views_data() --in tripal_analysis.views.inc
+ *
+ * Table: XXX
+ * @code
+ * XXX-Copy/Paste Table SQL code here-XXX
+ * @endcode
+ */
+ function retrieve_XXX_views_data() {
+  global $db_url;
+  $data = array();
+
+  // if the chado database is not local to the drupal database
+  // then we need to set the database name.  This should always
+  // be 'chado'.
+  if(is_array($db_url) and array_key_exists('chado',$db_url)){
+    $database = 'chado';
+  }
+   
+  //Basic table definition-----------------------------------
+  $data['XXX']['table']['group'] = t('Chado XXX');
+  
+  $data['XXX']['table'] = array(
+    'field' => 'primary_id',
+    'title' => t('Chado XXX'),
+    'help' => t('Enter some user-friendly description of this tables purpose to the user.'),
+  );
+  if($database){
+     $data['XXX']['table']['database'] = $database;
+  }
+
+  
+  //Relationship Definitions---------------------------------
+  //Join: YYY => XXX
+  // Notice that this relationship tells the primary table to show it's fields to the
+  // table referencing it by a foreign key and thus the relationship is from
+  // primary table to table referenceing it (ie: cvterm => feature)
+  $data['XXX']['table']['join']['YYY'] = array(
+    'left_field' => 'foreign key in YYY table',
+    'field' => 'primary key in XXX table',
+  );  
+  
+  //Join: XXX => XY => YYY
+  // This relationship should be described in both directions
+  // in the appropriate files (ie: for feature => library 
+  // describe in both feature.views.inc and library.views.inc)
+  $data['XXX']['table']['join']['XY'] = array(
+    'left_field' => 'matching XXX key in the XY table',
+    'field' => 'primary key in XXX table',
+  );  
+  $data['XXX']['table']['join']['YYY'] = array(
+    'left_table' => 'XY',
+    'left_field' => 'matching XXX key in the XY table',
+    'field' => 'primary key in XXX table',
+  );
+  $data['XY']['table']['join']['YYY'] = array(
+    'left_field' => 'primary key in YYY table',
+    'field' => 'matching YYY key in the XY table',
+  );
+   
+  //Table Field Definitions----------------------------------
+      
+  //Field: XXX_id (primary key)
+  $data['XXX']['field_name'] = array(
+    'title' => t('XXX Primary Key'),
+    'help' => t('A unique index for every XXX.'),
+    'field' => array(
+      'handler' => 'views_handler_field_numeric',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_numeric',
+    ),
+  );
+
+  /*.......................................................
+   * Beginning of Example Field definitions
+   * Remove this section when done
+   */
+
+  //Field: plain_text_field (chado datatype)   
+  $data['XXX']['plain_text_field'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+
+  //Field: numeric_field (chado datatype)   
+  $data['XXX']['numeric_field'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_numeric',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_numeric',
+    ),
+  );
+
+  //Field: boolean_field (chado datatype)   
+  $data['XXX']['boolean_field'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_boolean',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_boolean_operator',
+    ),
+  );
+
+  //Field: unix_timestamp (chado datatype)   
+  $data['XXX']['unix_timestamp'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_date',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort_date',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_date',
+    ),
+  );
+
+  //Field: human_readable_date (chado datatype)   
+  $data['XXX']['human_readable_date'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_readble_date',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort_date',
+    ),
+  );
+   
+   /*
+    * End of Example Field definitions
+    */
+    
+  return $data;
+}

+ 2061 - 0
modules/base/tripal_bulk_loader/tripal_bulk_loader.admin.inc

@@ -0,0 +1,2061 @@
+<?php
+
+/**
+ * @file
+ * Handles Create/Edit/Delete Template Admin Forms
+ */
+ 
+/**
+ * Provides a description page and quick links for the bulk loader
+ */
+function tripal_bulk_loader_admin_template () {
+	$output = '';
+	
+	$output .= '<br><h3>Quick Links:</h3>';
+  $output .= l('Create a new bulk loader template', 'admin/tripal/tripal_bulk_loader_template/create')."<br>"; 
+  $output .= l('Edit a bulk loader template', 'admin/tripal/tripal_bulk_loader_template/edit')."<br>"; 
+  $output .= l('Delete a bulk loader template', 'admin/tripal/tripal_bulk_loader_template/delete')."<br>"; 
+  $output .= l('Export a bulk loader template', 'admin/tripal/tripal_bulk_loader_template/export')."<br>";
+  $output .= l('Import a bulk loader template', 'admin/tripal/tripal_bulk_loader_template/import')."<br>";
+  $output .= '<br>';
+  
+  $output .= '<h3>Module Description:</h3>';
+  $output .= '<p>This module provides the ability to create loading templates for any tab-delimited '
+    .'data file allowing it to be loaded into chado. The Loading Templates are a direct mapping '
+    .'between the columns in your file and the columns in chado tables. As such to use this tool '
+    .'you need to be very familar with the chado schema -See '
+    .l('Chado -Getting Started', 'http://gmod.org/wiki/Chado_-_Getting_Started')
+    .'. The ability to add constants and specify foreign key contraints is also provided '
+    .'in order for the loader to fill chado columns which may be required but are not specified '
+    .'in your input file.</p>';
+  $output .= '<br>';
+  
+  $output .= '<h3>Setup Instructions</h3>';
+  $output .= '<p>After intallation of the bulk loader module, the following tasks should be performed:</p>';
+  $output .= '<ol>';
+  $output .= '<li><b>Install Theme:</b> In order for Bulk Loading pages to be displayed correctly, '
+    .'the contents of the Tripal Bulk Loader theme directory ([drupal root]/sites/all/modules/tripal/tripal_bulk_loader/theme) '
+    .'should be moved to the base directory of the Tripal theme ([drupal root]/sites/all/themes/tripal). '
+    .'Finally the drupal cache should be cleared for the new theme to take effect -navigate to admin/settings/performance '
+    .'and click the Clear Cached Data button.</li>';
+  $output .= '</ol>';
+  
+  return $output;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////
+// Modify Template
+//////////////////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * The main form reached at admin/tripal/tripal_bulk_loader/create and /edit
+ */
+function tripal_bulk_loader_modify_template_base_form ($form_state = NULL, $mode){
+	$form = array();
+
+ 	// get template id from path and rebuild form
+ 	if ($_GET['template_id']) {
+    if (preg_match('/^\d+$/', $_GET['template_id'])) {
+      $form_state['storage']['template_id'] = $_GET['template_id'];
+    }
+    
+    $sql = "SELECT * FROM {tripal_bulk_loader_template} WHERE template_id=%d";
+    $result = db_fetch_object(db_query($sql, $form_state['storage']['template_id']));
+    $form_state['storage']['template'] = unserialize($result->template_array);
+    $form_state['storage']['template_name'] = $result->name;
+  
+    $form_state['storage']['record2priority'] = array();
+    foreach ($form_state['storage']['template'] as $priority => $record_array) {
+      if (!is_array($record_array)) { continue; }
+      $form_state['storage']['record2priority'][$record_array['record_id']] = $priority;
+    }
+  }
+
+  $form['mode'] = array(
+    '#type' => 'hidden',
+    '#value' => $mode,
+  );
+
+  if ($form_state['storage']['template_id']) {    
+    $form['template_name'] = array(
+        '#type' => 'item',
+        '#title' => 'Template',
+        '#value' => $form_state['storage']['template_name'],
+        '#weight' => 1,
+    );
+  } else {
+    if (preg_match('/create/', $mode)) {
+     $form['new_template_name'] = array(
+        '#type' => 'textfield',
+        '#title' => 'Template Name',
+        '#weight' => 1,
+      );
+    } elseif (preg_match('/edit/', $mode)) {
+      $sql = "SELECT * FROM {tripal_bulk_loader_template}";
+      $resource = db_query($sql);
+      $templates = array();
+      $templates[''] = 'Select a Template';
+      while ($r = db_fetch_object($resource)) {
+        $templates[$r->template_id] = $r->name;
+      }
+      
+      $form['template_id'] = array(
+        '#title'         => t('Template'),
+        '#description'   => t('Please select the template you would like to edit.'),
+        '#type'          => 'select',
+        '#options'       => $templates,
+        '#default_value' => $form_state['storage']['template_id'],
+        '#weight'        => 0,
+        '#required'      => TRUE,
+        '#weight' => 1,
+      );	
+    }
+  }
+
+  $form['records'] = array(
+		'#type' => ($form_state['storage']['template_id'])? 'fieldset' : 'hidden',
+		'#title' => t('Current Records'), 
+		'#weight' => 2,
+  );
+  
+  $form['records']['description'] = array(
+    '#type' => 'item',
+    '#value' => 'Records will be inserted into the chado database in the order listed below. To '
+      .'change this order: <ul><li>Drag the rows into the correct order OR</li><li>Enter '
+      .'the numbers 1 and up in the Order textboxes to indicate the correct order.</li></ul>',
+  );
+  
+  $form['records']['records-data'] = array(
+    '#tree' => TRUE,
+  );
+
+  $form['records']['no_records'] = array(
+    '#type' => 'hidden',
+    '#value' => TRUE,
+  );
+
+  $form['records']['submit-new_record'] = array(
+    '#type' => 'submit',
+    '#value' => 'New Record/Field',
+  );
+
+  $form['records']['submit-reorder'] = array(
+    '#type' => 'submit',
+    '#value' => 'Save Order',
+  );
+	
+	$form['fields'] = array(
+		'#type' => ($form_state['storage']['template_id'])? 'fieldset' : 'hidden',
+		'#title' => t('Current Fields'),
+		'#weight' => 3,
+	);
+
+  $form['fields']['fields-data'] = array(
+    '#tree' => TRUE,
+  );
+  
+	if ($form_state['storage']['template']) {	
+    
+    // List Current Fields -------------------------------------------------------------
+    $i=1;
+    foreach ($form_state['storage']['template'] as $priority => $table_array) {
+      if (!is_array($table_array)) { continue; }
+        
+        $form['records']['no_records']['#value'] = FALSE;
+        
+        $form['records']['records-data'][$priority] = array(
+          'title' => array(
+            '#type' => 'markup',
+            '#value' => $table_array['record_id'],
+          ),
+          'chado_table' => array(
+            '#type' => 'markup',
+            '#value' => $table_array['table'],
+          ),
+          'mode' => array(
+            '#type' => 'item',
+            '#value' => ($table_array['mode']) ? $table_array['mode'] : 'insert_unique',
+          ),
+          'new_priority' => array(
+            '#type' => 'select',
+            '#options' => range(1, sizeof($form_state['storage']['template'])),
+            '#default_value' => $priority,
+          ),
+          'old_priority' => array(
+            '#type' => 'hidden',
+            '#value' => $priority,
+          ),
+          'id'  => array(
+            '#type' => 'hidden',
+            '#value' => $priority,
+          ),
+          'submit-edit_record' => array(
+            '#type' => 'submit',
+            '#name' => (string)$priority,
+            '#value' => 'Edit Record',
+          ),          
+          'submit-add_field' => array(
+            '#type' => 'submit',
+            '#name' => (string)$priority,
+            '#value' => 'Add Field',
+          ),
+          'submit-duplicate_record' => array(
+            '#type' => 'submit',
+            '#name' => (string)$priority,
+            '#value' => 'Duplicate Record'
+          ),
+        );
+
+        foreach ($table_array['fields'] as $field_index => $field) {
+        
+          $form['fields']['fields-data'][$i] = array(
+            'record_id' => array(
+              '#type' => 'item',
+              '#value' => $table_array['record_id'],
+            ),
+            'priority_hidden' => array(
+              '#type' => 'hidden',
+              '#value' => $priority,
+            ),
+            'field_name' => array(
+              '#type' => 'item',
+              '#value' => $field['title'],
+            ),
+            'chado_table_name' => array(
+              '#type' => 'item',
+              '#value' => $table_array['table'],
+            ),		
+            'chado_table_hidden' => array(
+              '#type' => 'hidden',
+              '#value' => $table_array['table'],
+            ),
+            'chado_field_name' => array(
+              '#type' => 'item',
+              '#value' => $field['field'],
+            ),
+            'sheet_name' => array(
+              '#type' => 'item',
+              '#value' => $field['spreadsheet sheet'],
+            ),
+            'column_num' => array(
+              '#type' => 'item',
+              '#value' => $field['spreadsheet column'],
+            ),
+            'constant_value' => array(
+              '#type' => 'item',
+              '#value' => $field['constant value'],
+            ),          
+            'field_index' => array(
+              '#type' => 'hidden',
+              '#value' => $field_index
+            ),
+            'foreign_record_id' => array(
+              '#type' => 'item',
+              '#value' => $field['foreign key'],
+            ),
+            'edit_submit' => array(
+              '#type' => 'submit',
+              '#name' => (string)$i,
+              '#value' => "Edit Field",
+            ),
+            'delete_submit' => array(
+              '#type' => 'submit',
+              '#name' => (string)$i,
+              '#value' => "Delete Field",
+            ),
+          );
+          
+          $i++;
+        }
+    }
+    $form['fields']['total_fields'] = array(
+      '#type' => 'item',
+      '#value' => $i,
+    );
+
+	}
+
+  if ($form['records']['no_records']['#value']) {
+    $form['records']['description'] = array(
+      '#type' => 'item',
+      '#value' => 'There are currently no records.',
+    );    
+    unset($form['records']['submit-reorder']);
+  
+    $form['fields']['description'] = array(
+      '#type' => 'item',
+      '#value' => 'There are currently no fields.',
+    );
+    
+  }
+    
+  $mode_title = (preg_match('/create/',$mode)) ? 'Create Template' : 'Edit Template';
+	$value = ($form_state['storage']['template_id'])? 'Save Template' : $mode_title;
+	$form['submit'] = array(
+		'#type' => 'submit',
+		'#value' => $value,
+		'#weight' => 4,
+	);
+	
+	return $form;
+}
+
+/**
+ * Submit for tripal_bulk_loader_modify_template_base_form
+ */
+function tripal_bulk_loader_modify_template_base_form_submit($form, &$form_state){
+
+  $form_state['rebuild'] = TRUE;
+  if ($form_state['storage']['template_id']) {
+    $sql = "SELECT * FROM {tripal_bulk_loader_template} WHERE template_id=%d";
+    $result = db_fetch_object(db_query($sql, $form_state['storage']['template_id']));
+    $form_state['storage']['template'] = unserialize($result->template_array);
+  }
+  
+  $op = $form_state['values'][ $form_state['clicked_button']['#name'] ];
+  switch ($op) {
+    // Initialize after template is chosen ----------------------------------------
+    case 'Edit Template':
+      $form_state['storage']['template_id'] = $form_state['values']['template_id'];
+      
+      $sql = "SELECT * FROM {tripal_bulk_loader_template} WHERE template_id=%d";
+      $result = db_fetch_object(db_query($sql, $form_state['storage']['template_id']));
+      $form_state['storage']['template'] = unserialize($result->template_array);
+      $form_state['storage']['template_name'] = $result->name;
+    
+      $form_state['storage']['record2priority'] = array();
+      foreach ($form_state['storage']['template'] as $priority => $record_array) {
+        if (!is_array($record_array)) { continue; }
+        $form_state['storage']['record2priority'][$record_array['record_id']] = $priority;
+      }
+    break;
+    
+    case 'Create Template':
+      $record = array(
+        'name' => $form_state['values']['new_template_name'],
+        'template_array' => array(),
+      );
+      drupal_write_record('tripal_bulk_loader_template', $record);
+      $form_state['storage']['template_id'] = $record['template_id'];
+      $form_state['storage']['template_name'] = $record['name'];
+      $form_state['storage']['template'] = array();
+    break;
+    
+    // Save Reordered Records -----------------------------------------------------
+    case 'Save Order':
+      $new_template = $form_state['storage']['template'];
+      // unset old elements
+       $form_state['storage']['record2priority'] = array();
+      foreach ($new_template as $priority => $record_array) { 
+        if (preg_match('/\d+/', $priority)) { unset($new_template[$priority]); }
+      }
+      //set elements in new order
+      foreach ($form_state['values']['records-data'] as $item) {
+        $new_template[$item['new_priority']] = $form_state['storage']['template'][$item['old_priority']];
+        $record_name = $new_template[$item['new_priority']]['record_id'];
+        $form_state['storage']['record2priority'][$record_name] = $item['new_priority'];
+      }
+      ksort($new_template);
+      $form_state['storage']['template'] = $new_template;
+    break;
+    
+    case 'New Record/Field':
+      $query = array(
+        'template_id'=> $form_state['storage']['template_id'], 
+        'record_id' => 'NEW',
+      );
+      drupal_goto('admin/tripal/tripal_bulk_loader_template/add_field', $query);    
+    break;
+
+    case 'Edit Record':
+      $query = array(
+        'template_id'=> $form_state['storage']['template_id'], 
+        'record_id' => $form_state['clicked_button']['#name'],
+      );
+      drupal_goto('admin/tripal/tripal_bulk_loader_template/edit_record', $query);
+    break;
+    
+    case 'Add Field':
+      $query = array(
+        'template_id'=> $form_state['storage']['template_id'], 
+        'record_id' => $form_state['clicked_button']['#name'],
+      );
+      drupal_goto('admin/tripal/tripal_bulk_loader_template/add_field', $query);
+    break;
+
+    case 'Duplicate Record':
+      // original record (one to be duplicated)
+      $orig_priority = $form_state['clicked_button']['#name'];
+      $record = $form_state['storage']['template'][ $orig_priority ];
+      
+      // new record
+      $new_priority = sizeof($form_state['storage']['template']) + 1;
+      $record['record_id'] = $record['record_id'] . '_' . date('YzHi');
+      $form_state['storage']['template'][ $new_priority ] = $record;
+    break;
+    
+    case 'Edit Field':
+      $field_data_index = $form_state['clicked_button']['#name'];
+      $query = array(
+        'template_id'=> $form_state['storage']['template_id'], 
+        'record_id' => $form_state['values']['fields-data'][$field_data_index]['priority_hidden'],
+        'field_index' => $form_state['values']['fields-data'][$field_data_index]['field_index'],
+      );
+      drupal_goto('admin/tripal/tripal_bulk_loader_template/edit_field', $query);    
+    break;
+    
+    case 'Delete Field':
+      $field_data = $form_state['values']['fields-data'][$form_state['clicked_button']['#name']];
+      $priority = $field_data['priority_hidden'];
+      $field_key = $field_data['field_index'];
+      unset($form_state['storage']['template'][$priority]['fields'][$field_key]);
+      if (!$form_state['storage']['template'][$priority]['fields']) {
+        unset($form_state['storage']['record2priority'][$form_state['storage']['template'][$priority]['record_id']]);
+        unset($form_state['storage']['template'][$priority]);
+      } 
+      drupal_set_message('Deleted Field from Template.');
+    break;
+  } //end of switch
+    
+  // Save Template
+  $record = array(
+    'template_id' => $form_state['storage']['template_id'],
+    'template_array' => serialize($form_state['storage']['template'])
+  );
+  drupal_write_record('tripal_bulk_loader_template', $record, array('template_id'));
+  drupal_set_message('Template Saved.');
+	
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+// Delete Template
+//////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Delete Template Form
+ * This form allows admin to delete already existing templates
+ */
+function tripal_bulk_loader_delete_template_base_form () {
+	$form = array();
+
+	$sql = "SELECT * FROM {tripal_bulk_loader_template}";
+	$resource = db_query($sql);
+	$templates = array();
+	$templates[''] = 'Select a Template';
+	while ($r = db_fetch_object($resource)) {
+		$templates[$r->template_id] = $r->name;
+	}
+  $form['template_name'] = array(
+      '#title'         => t('Template'),
+      '#description'   => t('Please select the template you would like to delete.'),
+      '#type'          => 'select',
+      '#options'       => $templates,
+      '#weight'        => 0,
+      '#required'      => TRUE,
+	);	
+	
+	$form['submit'] = array(
+		'#type' => 'submit',
+		'#value' => 'Delete Template',
+	);
+	
+	return $form;
+}
+
+/**
+ * Delete Template Form Submit
+ *
+ * @param $form
+ *   The form that was submitted
+ * @param $form_state
+ *   The values and storage that were submitted
+ */
+function tripal_bulk_loader_delete_template_base_form_submit ($form, &$form_state) {
+	$sql = "DELETE FROM {tripal_bulk_loader_template} WHERE template_id=%d";
+	db_query($sql, $form_state['values']['template_name']);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+// Import/Export Template
+//////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Import/Export Template Form
+ *
+ * On export, simply selects the serialized array from the db for a given template 
+ * and presents it to the user. On import, a serialized template array and a name is 
+ * supplied and a template record is created.
+ *
+ * @todo Make array presented to the user more readable. (ie: unserialize and print to the screen)
+ *
+ * @param $form_state
+ *   The values and storage for the form
+ * @param $mode
+ *   Either 'import' or 'export' to indicate which function is being performed
+ * @return
+ *   A form array to be rendered by drupal_get_form
+ */
+function tripal_bulk_loader_import_export_template_form ($form_state = NULL, $mode) {
+  $form = array();
+
+  $form['mode'] = array(
+    '#type' => 'hidden',
+    '#value' => $mode,
+  );
+  
+  if (preg_match('/import/', $mode)) {
+   $form['new_template_name'] = array(
+      '#type' => 'textfield',
+      '#title' => 'Template Name',
+      '#weight' => 1,
+    );
+  } elseif (preg_match('/export/', $mode)) {
+    $sql = "SELECT * FROM {tripal_bulk_loader_template}";
+    $resource = db_query($sql);
+    $templates = array();
+    $templates[''] = 'Select a Template';
+    while ($r = db_fetch_object($resource)) {
+      $templates[$r->template_id] = $r->name;
+    }
+    
+    $form['template_id'] = array(
+      '#title'         => t('Template'),
+      '#description'   => t('Please select the template you would like to edit.'),
+      '#type'          => 'select',
+      '#options'       => $templates,
+      '#default_value' => $form_state['storage']['template_id'],
+      '#weight'        => 0,
+      '#required'      => TRUE,
+      '#weight' => 1,
+    );	
+  }
+
+  $form['template_array'] = array(
+    '#type' => 'textarea',
+    '#title' => 'Template Array',
+    '#default_value' => $form_state['storage']['template_array'],
+    '#weight' => 2,
+  );
+  
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => 'Submit',
+    '#weight' => 10,
+  );
+  
+  return $form;
+}
+
+/**
+ * Import/Export Template Form Submit
+ *
+ * @param $form
+ *   The form that was submitted
+ * @param $form_state
+ *   The values and storage that were submitted
+ */
+function tripal_bulk_loader_import_export_template_form_submit ($form, &$form_state) {
+  switch ($form_state['values']['mode']) {
+    case 'export':
+      $record = db_fetch_object(db_query("SELECT * FROM {tripal_bulk_loader_template} WHERE template_id=%d", $form_state['values']['template_id']));
+      $form_state['storage']['template_array'] = $record->template_array;
+      $form_state['storage']['template_id'] = $form_state['values']['template_id'];
+    break;
+    case 'import':
+      $record = array(
+        'name' => $form_state['values']['new_template_name'],
+        'template_array' => $form_state['values']['template_array'],
+      );
+      drupal_write_record('tripal_bulk_loader_template', $record);
+      if ($record->template_id) {
+        drupal_set_message('Successfully imported Tripal Bulk Loader Template.');
+      }
+    break;
+  }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+// Edit Record Form
+//////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Edit Record Form
+ *
+ * This form is meant to be called from a bulk loader form. The following should be set 
+ * in the query section of the path:
+ *  - template_id=\d+: the template which the edited record is part of
+ *  - record_id=\d+: the priority or key in the template array of the record to be edited
+ *
+ * @param $form_state
+ *   Contains the values and storage for the form
+ * @return
+ *   A form array to be rendered by drupal_get_form
+ */
+function tripal_bulk_loader_edit_template_record_form (&$form_state = NULL) {
+ 	$form['#cache'] = TRUE; // Make sure the form is cached.
+  
+ 	// get template id from path
+ 	$template_id = ($_GET['template_id'] !== NULL) ? $_GET['template_id'] : $form_state['values']['template_id'];
+ 	
+ 	// if there is no template supplied don't return rest of form
+ 	if (!$template_id) {
+ 	  return $form;
+ 	}
+
+  // Pre-process values/defaults ---------------------------
+
+ 	// If this is the first load of the form (no form state) we need to initialize some variables
+ 	if (!$form_state['storage']['template']) {
+    $sql = "SELECT * FROM {tripal_bulk_loader_template} WHERE template_id=%d";
+    $template = db_fetch_object(db_query($sql, $template_id));
+    $form_state['storage']['template_array'] = unserialize($template->template_array);
+    $form_state['storage']['template'] = $template;
+    
+    $form_state['storage']['record2priority'] = array();
+    foreach ($form_state['storage']['template_array'] as $priority => $record_array) {
+      if (!is_array($record_array)) { continue; }
+      $form_state['storage']['record2priority'][$record_array['record_id']] = $priority;
+    }
+    
+    $form_state['storage']['referring URL'] = $_SERVER["HTTP_REFERER"];
+  } else {
+    $template = $form_state['storage']['template'];
+  }
+
+ 	// get the record_id from the path
+ 	if ($_GET['record_id'] !== NULL) {
+   	$form_state['values']['field_group'] = $_GET['record_id'];
+   	$form_state['storage']['original_priority'] = $_GET['record_id'];
+  }
+  
+  
+  // Tables and default table
+  $tables = tripal_core_get_chado_tables();
+  if ($form_state['values']['chado_table']) {
+    $table = $form_state['values']['chado_table'];
+  } else {
+    $table = $form_state['storage']['template_array'][$form_state['storage']['original_priority']]['table'];  
+  }
+
+  //dpm($form_state, 'form state');
+  
+ 	// Form Proper ------------------------------------------- 	
+  $form['template_name'] = array(
+    '#type' => 'item',
+    '#title' => 'Template',
+    '#value' => $template->name,
+  );
+
+  $form['template_id'] = array(
+    '#type' => 'hidden',
+    '#value' => $template_id,
+  );
+  
+  $form['edit_record'] = array(
+    '#type' => 'fieldset',
+  );
+
+  // check template array for records then add one more
+  if (!$form_state['storage']['record2priority']) { 
+    $groups = array();
+  } else {
+    $groups = array_flip($form_state['storage']['record2priority']);
+  }
+  $priority_default = $form_state['values']['field_group'];
+  $form['edit_record']['field_group']  = array(
+    '#type' => 'select',
+    '#title' => 'Record',
+    '#description' => 'By Changing the record here, you can move all the fields from the current record into the selected record.',
+    '#options' => $groups,
+    '#default_value' => $priority_default,
+    '#required' => TRUE,
+  );
+  
+  $form['edit_record']['record_name'] = array(
+    '#type' => 'textfield',
+    '#title' => 'Unique Record Name',
+    '#default_value' => $groups[$priority_default],
+  );
+
+  $form['edit_record']['chado_table'] = array(
+    '#type' => 'select',
+    '#title' => t('Chado Table'),
+    '#description' => 'This changes the chado table for all fields in this record.',
+    '#options' => $tables,
+    '#default_value' => $table,
+  );
+  
+  $form['edit_record']['mode'] = array(
+    '#type' => 'radios',
+    '#title' => 'Action to take when Loading Record',
+    '#options' => array(
+      'select' => 'SELECT: Don\'t insert this record: it\'s used to define a foreign key in another record',
+      'insert' => 'INSERT: Insert the record',
+      'optional' => 'OPTIONAL: Record will only be inserted if all required data is filled in',
+      'insert_once' => 'INSERT ONCE: Record will be inserted once for the entire spreadsheet',
+      'insert_unique' => 'INSERT UNIQUE: Only insert record if there isn\'t a record with the same values',
+    ),
+    '#default_value' => 'insert_unique'
+  );
+  
+  $form['edit_record']['submit-edit_record'] = array(
+      '#type' => 'submit',
+      '#value' => 'Edit Record'
+  );
+
+  $form['edit_record']['submit-cancel'] = array(
+      '#type' => 'submit',
+      '#value' => 'Cancel'
+  );
+  
+  return $form;
+}
+
+
+/**
+ * Edit Record Form Submit
+ *
+ * @param $form
+ *   The form that was submitted
+ * @param $form_state
+ *   Contains the values and storage for the form
+ */
+function tripal_bulk_loader_edit_template_record_form_submit ($form, &$form_state) {
+  //dpm($form_state, 'form state -start submit');
+  
+  if (!$form_state['ahah_submission']) {
+    if ($form_state['values']['op'] ==  'Edit Record') {
+
+      $template = $form_state['storage']['template_array'];
+      
+      // Edit Record
+      $record = $template[ $form_state['storage']['original_priority'] ];
+      $record['record_id'] = $form_state['values']['record_name'];
+      $record['mode'] = $form_state['values']['mode'];
+      $record['table'] = $form_state['values']['chado_table'];
+      
+      if ($form_state['storage']['original_priority'] != $form_state['values']['field_group']) {
+        $record['fields'] = array_merge($record['fields'], $template[ $form_state['values']['field_group'] ]['fields']);
+        $template[ $form_state['values']['field_group'] ] = $record;
+        unset($template[ $form_state['storage']['original_priority'] ]);
+      } else {
+        $template[ $form_state['storage']['original_priority'] ] = $record;
+      }
+
+      // Save Template
+      $form_state['storage']['template']->template_array = serialize($template);
+      $success = drupal_write_record('tripal_bulk_loader_template', $form_state['storage']['template'], array('template_id'));
+  
+      if ($success) {
+        drupal_set_message('Successfully Updated Template Record');
+        drupal_set_message('Template Saved.'); 
+        
+        $path = explode('?',$form_state['storage']['referring URL']);
+        parse_str($path[1], $query);
+        $query['template_id'] = $form_state['storage']['template']->template_id;
+        drupal_goto($path[0], $query);
+      } else {
+        drupal_set_message('Unable to Save Template!', 'error');
+        watchdog('T_bulk_loader',
+          'Unable to save bulk loader template: %template',
+          array('%template' => print_r($form_state['storage']['template'], TRUE)),
+          WATCHDOG_ERROR
+        );
+      }
+    } elseif ($form_state['values']['op'] ==  'Cancel') {
+        $path = explode('?',$form_state['storage']['referring URL']);
+        parse_str($path[1], $query);
+        $query['template_id'] = $form_state['storage']['template']->template_id;
+        //dpm('Redirecting to: '.$path[0].'?'.print_r($query,TRUE).' where the referring URL:'.$form_state['storage']['referring URL']);
+        drupal_goto($path[0], $query);    
+    }
+  }  
+  
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+// Add/Edit Field Forms
+//////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Add Field Form
+ *
+ * This form is meant to be called from a bulk loader form. Blank Defaults are in place but you
+ * can use the following in the query of the path to set defaults for a given template:
+ *  - template_id=\d+: the template to add the field to
+ *  - record_id=\d+: the priority or key in the template array of the record to add the field to
+ * 
+ * @param $form_state
+ *   Contains the values and storage for the form
+ * @return
+ *   A form array to be rendered by drupal_get_form
+ */
+function tripal_bulk_loader_add_template_field_form (&$form_state = NULL) {
+  $form = array();
+ 	$form['#cache'] = TRUE; // Make sure the form is cached.
+ 
+ 	// get template id from path
+ 	$template_id = ($_GET['template_id']) ? $_GET['template_id'] : $form_state['values']['template_id'];
+ 	
+ 	// if there is no template supplied don't return rest of form
+ 	if (!$template_id) {
+ 	  return $form;
+ 	}
+
+  // Pre-set Variables needed for form proper------------------------------------------
+
+ 	// If this is the first load of the form (no form state) we need to initialize some variables
+ 	if (!$form_state['storage']['template']) {
+    $sql = "SELECT * FROM {tripal_bulk_loader_template} WHERE template_id=%d";
+    $template = db_fetch_object(db_query($sql, $template_id));
+    $form_state['storage']['template_array'] = unserialize($template->template_array);
+    $form_state['storage']['template'] = $template;
+    
+    $form_state['storage']['record2priority'] = array();
+    foreach ($form_state['storage']['template_array'] as $priority => $record_array) {
+      if (!is_array($record_array)) { continue; }
+      $form_state['storage']['record2priority'][$record_array['record_id']] = $priority;
+    }
+    
+    $form_state['storage']['referring URL'] = $_SERVER["HTTP_REFERER"];
+  } else {
+    $template = $form_state['storage']['template'];
+  }
+
+  $field_type = ($form_state['values']['field_type'])? $form_state['values']['field_type'] : 'table field';
+
+  // Tables and default table
+  $tables = tripal_core_get_chado_tables();
+  if ($form_state['values']) {
+    if (!preg_match('/^'.current($tables).'$/', $form_state['values']['chado_table'])) {
+      $table = $form_state['values']['chado_table'];
+    } elseif ($form_state['values']['record_name']) {
+      $record_name = $form_state['values']['record_name'];
+      $priority = $form_state['storage']['record2priority'][$record_name];
+      $table = $form_state['storage']['template_array'][$priority]['table'];
+    } else {
+      $priority = $form_state['values']['field_group'];
+      $table = $form_state['storage']['template_array'][$priority]['table'];  
+    }
+  } 
+  if (!$table) {
+    $table = reset($tables);
+  }
+
+ 	// get the record_id from the path
+ 	if ($_GET['record_id'] !== NULL) {
+   	$form_state['values']['field_group'] = $_GET['record_id'];
+    if (preg_match('/\d+/', $_GET['record_id'])) {
+      $priority = $form_state['values']['field_group'];
+      $table = $form_state['storage']['template_array'][$priority]['table'];  
+    }
+  }
+  
+  // Fields and foreign key mappings
+  $chado_fields = array();
+  $fk_options = array();
+  $fk_options['NULL'] = 'None';
+  $table_description = module_invoke_all('chado_'.$table.'_schema');
+  //dpm($table_description, 'table description for |'.$table.'|');
+  if ($field_type == 'foreign key') {
+    $foreign_field2table = array();
+    foreach ($table_description['foreign keys'] as $key_table => $key_array) {
+      foreach ($key_array['columns'] as $left_field => $right_field) {
+        $chado_fields[$left_field] = $left_field;
+        $foreign_field2table[$left_field] = $key_table;
+      }
+    }
+    reset($chado_fields);
+    
+    // set default field
+    if (empty($chado_fields)) {
+      $field = NULL;
+    } elseif ($chado_fields[$form_state['values']['chado_field']]) {
+      $field = $form_state['values']['chado_field'];
+    } else {
+      $field = current($chado_fields);
+    }
+    
+    // Foreign key options
+    $foreign_table = $foreign_field2table[$field];
+    if ($foreign_table) {
+      foreach ($form_state['storage']['record2priority'] as $record_name => $priority ) {
+        if (preg_match('/^'.$foreign_table.'$/', $form_state['storage']['template_array'][$priority]['table'])) {
+          $fk_options[$record_name] = $record_name;
+        }
+      }
+    }
+  } else {
+    foreach($table_description['fields'] as $field_name => $field_array) {
+        $chado_fields[$field_name] = $field_name;
+    }
+  }
+
+  $variables = array(
+    'form_state' => $form_state,
+    'tables' => $tables,
+    'default table' => $table,
+    'fields' => $chado_fields,
+    'default_field' => $field,
+    'priority' => $priority,
+    'record_name' => $record_name,
+    'table description' => $table_description,
+    'foreign key options' => $fk_options,    
+    'foreign field=>table' => $foreign_field2table,
+    'foreign table' => $foreign_table,
+  );
+  //dpm($variables, 'variables');
+    
+  // Start of Form Proper--------------------------------------------------------------
+  //dpm($form_state, 'Form State');
+  
+  $form['template_name'] = array(
+    '#type' => 'item',
+    '#title' => 'Template',
+    '#value' => $template->name,
+  );
+
+  $form['template_id'] = array(
+    '#type' => 'hidden',
+    '#value' => $template_id,
+  );
+
+  $form['add_fields'] = array(
+    '#type' => 'fieldset',
+    '#prefix' => '<div id="tripal_bulk_loader_template-add_field">',
+    '#suffix' => '</div>',
+  );
+
+	$form['add_fields']['field_type'] = array(
+		'#type' => 'radios',
+		'#title' => t('Type of Field'),
+		'#options' => array(
+			'table field' => t('Spreadsheet: Fields which maps to a Spreadsheet Column'),
+			'constant' => t('Constant: Field which remains Constant throughout the Spreadsheet'),
+			'foreign key' => t('Foreign Key: Fields which map to a record in another table'),
+		),
+		'#required' => TRUE,
+		'#default_value' => $field_type,
+    '#ahah' => array(
+      'path' => 'admin/tripal/tripal_bulk_loader_template/add_field_ahah',
+      'wrapper' => 'tripal_bulk_loader_template-add_field',
+      'effect' => 'fade'
+    ),
+	);
+
+  // check template array for records then add one more
+  if (!$form_state['storage']['record2priority']) { 
+    $groups = array();
+  } else {
+    $groups = array_flip($form_state['storage']['record2priority']);
+  }
+  $groups['NONE'] = 'Select a Record';
+  $groups['NEW'] = 'New Record';
+  $form['add_fields']['field_group']  = array(
+    '#type' => 'select',
+    '#title' => 'Record',
+    '#description' => 'This is used to group a set of fields together allowing '
+      .'multiple records to be inserted into the same table per line of the spreadsheet',
+    '#options' => $groups,
+    '#default_value' => (preg_match('/\w+.*/',$form_state['values']['field_group'])) ? $form_state['values']['field_group'] : 'NONE',
+    '#ahah' => array(
+      'path' => 'admin/tripal/tripal_bulk_loader_template/add_field_ahah',
+      'wrapper' => 'tripal_bulk_loader_template-add_field',
+      'effect' => 'fade'
+    ),
+    '#required' => TRUE,
+  );
+  
+  $form['add_fields']['record_name'] = array(
+    '#type' => (preg_match('/NEW/', $form_state['values']['field_group'])) ? 'textfield' : 'hidden',
+    '#title' => 'Unique Record Name',
+    '#prefix' => '<div id="tripal_bulk_loader_template-add_record">',
+    '#suffix' => '</div>',
+    '#default_value' => $form_state['values']['record_name'],
+  );
+  
+  $form['add_fields']['field_title'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Human-readable Title for Field'),
+    '#default_value' => $form_state['values']['field_title'],
+  );
+  
+  // Spreadsheet column
+  $form['add_fields']['columns'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Spreadsheet Column'),
+    '#collapsible' => TRUE,
+    '#collapsed' => ($field_type == 'table field')? FALSE : TRUE,
+  );
+
+  $form['add_fields']['columns']['sheet_name'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Worksheet'),
+    '#description' => t('Specify the name of the worksheet.'),
+    '#size' => 5,
+    '#default_value' => ($form_state['values']['sheet_name'])? $form_state['values']['sheet_name'] : 'Sheet1',    
+  );
+  
+  $form['add_fields']['columns']['column_number'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Column'),
+    '#description' => t('Specify the column in the spreadsheet that this field maps to where the first column is 1.'),
+    '#size' => 5,
+    '#default_value' => $form_state['values']['column_number'],
+  );
+
+  // Global Value
+  $form['add_fields']['constant'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Constant'),
+    '#collapsible' => TRUE,
+    '#collapsed' => ($field_type == 'constant')? FALSE : TRUE,
+  );
+  
+  $form['add_fields']['constant']['constant_value'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Constant Value'),
+    '#description' => t('Specify the value you wish this field to have regardless of spreadsheet data.'),
+    '#default_value' => $form_state['values']['constant_value']
+  );
+
+  // Foreign Key
+  $form['add_fields']['foreign_key'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'Foreign Key',
+    '#collapsible' => TRUE,
+    '#collapsed' => ($field_type == 'foreign key')? FALSE : TRUE,
+  );
+
+  $form['add_fields']['foreign_key']['foreign_record'] = array(
+    '#type' => 'select',
+    '#title' => 'Record to refer to',
+    '#descripion' => 'Select the record that this foreign key shouold refer to. The record needs to already exist and be of the correct table.',
+    '#options' => $fk_options,
+  );
+
+  // Chado Field
+  $form['add_fields']['chado'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Chado Field/Column Details'),
+    '#description' => t('Specify the Table/Field in chado that this field maps to.'),
+  );
+  
+  $form['add_fields']['chado']['chado_table'] = array(
+    '#type' => 'select',
+    '#title' => t('Chado Table'),
+    '#options' => $tables,
+    '#default_value' => $table,
+    '#ahah' => array(
+      'path' => 'admin/tripal/tripal_bulk_loader_template/add_field_ahah',
+      'wrapper' => 'tripal_bulk_loader_template-add_field',
+      'effect' => 'fade'
+      ),
+  );
+  
+  $form['add_fields']['chado']['chado_field'] = array(
+    '#type' => 'select',
+    '#title' => t('Chado Field/Column'),
+    '#options' => $chado_fields,
+    '#default_value' => $form_state['values']['chado_field'],
+    '#ahah' => array(
+      'path' => 'admin/tripal/tripal_bulk_loader_template/add_field_ahah',
+      'wrapper' => 'tripal_bulk_loader_template-add_field',
+      'effect' => 'fade'
+    ),
+  );
+
+  $form['add_fields']['additional'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'Additional Options',
+  );
+
+  $form['add_fields']['additional']['required'] = array(
+    '#type' => 'checkbox',
+    '#title' => 'Make this file required',
+  );
+  
+  $form['add_fields']['additional']['regex_transform'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'Transform Spreadsheet Value Rules',
+    '#collapsible' => TRUE, 
+    '#collapsed' => (!$form_state['storage']['regex']['pattern']) ? TRUE : FALSE,
+  );
+  
+  $form['add_fields']['additional']['regex_transform']['regex_description'] = array(
+    '#type' => 'item',
+    '#value' => 'A transformation rule allows you to transform the original value '
+      .'(usually from a user submitted spreadsheet) into the form you would like it stored '
+      .'in the chado database. Each rule consists of a match pattern (a php regular expression '
+      .'which determines which replacement patterns are applied and captures regions of the '
+      .'original value) and a replacement pattern (a string which may contain capture references '
+      .'that describes what the new value should be). Each rule is applied to the result of the '
+      .'previous rule.'
+  );
+
+  $form['add_fields']['additional']['regex_transform']['regex-data'] = array(
+    '#tree' => TRUE,
+  );
+  if (!$form_state['storage']['regex']['pattern']) { $form_state['storage']['regex']['pattern'] = array(); }
+  foreach ($form_state['storage']['regex']['pattern'] as $index => $pattern) {
+    $data_element = array(
+      'pattern' => array(
+        '#type' => 'item',
+        '#value' => $pattern,
+      ),
+      'replace' => array(
+        '#type' => 'item',
+        '#value' => $form_state['storage']['regex']['replace'][$index],
+      ),
+      'old_index' => array(
+        '#type' => 'hidden',
+        '#value' => $index,
+      ),
+      'new_index' => array(
+        '#type' => 'select',
+        '#options' => range(0,sizeof($form_state['storage']['regex']['pattern'])-1),
+        '#default_value' => $index,
+      ),
+      'id' => array(
+        '#type' => 'hidden',
+        '#value' => $index,
+      ),
+      'submit-delete' => array(
+        '#type' => 'submit',
+        '#value' => 'Delete Transformation',
+        '#name' => $index,
+      ),
+    );
+    $form['add_fields']['additional']['regex_transform']['regex-data'][$index] = $data_element;
+  }
+
+  $form['add_fields']['additional']['regex_transform']['submit-reorder_regex'] = array(
+    '#type' => ($form_state['storage']['regex']['pattern']) ? 'submit' : 'hidden',
+    '#value' => 'Save Transformation Rule Order'
+  );
+  
+  $form['add_fields']['additional']['regex_transform']['new_regex'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'Add a new Transformation Rule',
+  );
+  
+  $form['add_fields']['additional']['regex_transform']['new_regex']['pattern'] = array(
+    '#type' => 'textfield',
+    '#title' => 'Match Pattern',
+    '#description' => 'You can use standard php regular expressions in this field to specify a '
+      .'pattern. Only if this pattern matches the value in the spreadsheet does the replacement '
+      .'pattern get applied to the value. To capture a section of your value for use in the '
+      .'replacement patten surround with round brackets. For example, <i>GI:(\d+)</i> will match '
+      .' NCBI gi numbers and will capture the numerical digits for use in the replacement pattern. '
+      .' To match and capture any value use <i>.*</i>',
+  );
+  
+  $form['add_fields']['additional']['regex_transform']['new_regex']['replace'] = array(
+    '#type' => 'textfield',
+    '#title' => 'Replacement Pattern',
+    '#description' => 'This pattern should contain the text you want to replace the match pattern '
+    .'mentioned above. It can include references of the form \n where n is the number of the '
+    .'capture in the match pattern. For example, \1 will be replaced with the text matched in your '
+    .'first set of round brackets.',
+  ); 
+
+  if ($field_type == 'table field') {
+    $tab = '&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp';
+    $form['add_fields']['additional']['regex_transform']['new_regex']['replace']['#description'] .= '<p>'
+      .'The following references are also available for spreadsheet fields: <b><#column:<i>number</i>#></b>. '
+      .'This allows you to substitute other spreadsheet values into the current field. For example, '
+      .'if you had the following line:<br>'
+      .$tab.'SNP'.$tab.'15-Jan-2011'.$tab.'1'.$tab.'54'.$tab.'Contig34355'
+      .'<br> and your current field is for column #1 and you\'re inserting into the chado field '
+      .'feature.uniquename then you might want to add in the data to ensure your uniquename is '
+      .'unique. The Match Pattern is (.*) to select all the first column and the Replacement '
+      .'Pattern could be \1_<#column:2#> which would insert SNP_15-Jan-2011 into the database.</p>';
+  }
+  
+  $form['add_fields']['additional']['regex_transform']['new_regex']['submit-add_transform'] = array(
+    '#type' => 'submit',
+    '#value' => 'Add Transformation',
+  );
+  
+  $form['add_fields']['additional']['regex_transform']['test_regex'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'Test Transformation Rules',
+  );
+  
+  $form['add_fields']['additional']['regex_transform']['test_regex']['test_string'] = array(
+    '#type' => 'textfield',
+    '#title' => 'Test Value',
+    '#description' => 'This should be a value that you expect the above transformation rules '
+      .'to be applied to.',
+    '#default_value' => $form_state['storage']['test_regex_test'],
+  );
+  
+  $form['add_fields']['additional']['regex_transform']['test_regex']['test_result'] = array(
+    '#type' => 'textfield',
+    '#title' => 'Test Result',
+    '#description' => 'This is the value that would be saved to the database after the above transformation '
+      .'riles were applied to the Test Value.',
+    '#default_value' => $form_state['storage']['test_regex_result'],
+  );
+  
+  $form['add_fields']['additional']['regex_transform']['test_regex']['submit-test'] = array(
+    '#type' => 'submit',
+    '#value' => 'Test Transformation Rules'
+  );
+  
+  $form['add_fields']['submit-add_field'] = array(
+      '#type' => 'submit',
+      '#value' => 'Add Field'
+  );
+
+  $form['add_fields']['submit-cancel'] = array(
+      '#type' => 'submit',
+      '#value' => 'Cancel'
+  );
+  
+  return $form;
+}
+
+/**
+ * Add Field Submit
+ * 
+ * @param $form
+ *   The form that was submitted
+ * @param $form_state
+ *   The values and storage for the form
+ */
+function tripal_bulk_loader_add_template_field_form_submit ($form, &$form_state) {
+  
+  $op = $form_state['values'][ $form_state['clicked_button']['#name'] ];
+  
+  if (!$form_state['ahah_submission']) {
+    if ($op ==  'Add Field') {
+
+      $template = $form_state['storage']['template_array'];
+
+       // If new record
+      if (preg_match('/NEW/',$form_state['values']['field_group'])) {
+        $record_name = $form_state['values']['record_name'];
+        $priority = sizeof($form_state['storage']['template_array']) + 1;
+        $record2priority[$record_name] = $priority;
+        $template[$priority]['table'] = $form_state['values']['chado_table'];
+        $template[$priority]['record_id'] = $record_name;
+        
+      } else {
+        $priority = $form_state['values']['field_group'];
+        $record_name = $record2priority[$priority];
+      }
+      
+      // Add field to template array
+      if ($form_state['values']['field_type'] == 'table field') {
+        $field = array(
+          'type' => 'table field',
+          'title' => $form_state['values']['field_title'],
+          'field' => $form_state['values']['chado_field'],
+          'required' => $form_state['values']['required'],
+          //'allowed values' => empty by default,
+          'spreadsheet sheet' => $form_state['values']['sheet_name'],
+          'spreadsheet column' => $form_state['values']['column_number'],
+          //'exposed' => 'true|false'  If exposed, will give a select box first from allowed values if set, second from database if values not set.     
+        );
+      } elseif ($form_state['values']['field_type'] == 'constant') {
+        $field = array(
+          'type' => 'constant',
+          'title' => $form_state['values']['field_title'],
+          'field' => $form_state['values']['chado_field'],
+          'required' => $form_state['values']['required'],
+          //'allowed values' => empty by default,
+          'constant value' => $form_state['values']['constant_value'],
+          //'exposed' => 'true|false'  If exposed, will give a select box first from allowed values if set, second from database if values not set.     
+        );      
+      } elseif ($form_state['values']['field_type'] == 'foreign key') {
+        $field = array(
+          'type' => 'foreign key',
+          'title' => $form_state['values']['field_title'],
+          'field' => $form_state['values']['chado_field'],
+          'foreign key' => $form_state['values']['foreign_record'],
+          'required' => $form_state['values']['required'],
+        );        
+      }
+
+      // Deal with any additional options
+      if ($form_state['storage']['regex']) {
+        $field['regex'] = $form_state['storage']['regex'];
+      }
+
+      // Save Template
+      $template[$priority]['fields'][] = $field;
+      $form_state['storage']['template']->template_array = serialize($template);
+      $success = drupal_write_record('tripal_bulk_loader_template', $form_state['storage']['template'], array('template_id'));
+  
+      if ($success) {
+        drupal_set_message('Successfully Added Field to Template');
+        drupal_set_message('Template Saved.'); 
+        
+        $path = explode('?',$form_state['storage']['referring URL']);
+        parse_str($path[1], $query);
+        $query['template_id'] = $form_state['storage']['template']->template_id;
+        drupal_goto($path[0], $query);
+      } else {
+        drupal_set_message('Unable to Save Template!', 'error');
+        watchdog('T_bulk_loader',
+          'Unable to save bulk loader template: %template',
+          array('%template' => print_r($form_state['storage']['template'], TRUE)),
+          WATCHDOG_ERROR
+        );
+      }
+    } elseif ($op ==  'Cancel') {
+        $path = explode('?',$form_state['storage']['referring URL']);
+        parse_str($path[1], $query);
+        $query['template_id'] = $form_state['storage']['template']->template_id;
+        drupal_goto($path[0], $query);    
+    } elseif ($op == 'Add Transformation') {
+
+      // Add transformation rule to original field
+      $form_state['storage']['regex']['pattern'][] = '/' . $form_state['values']['pattern'] . '/';
+      $form_state['storage']['regex']['replace'][] = $form_state['values']['replace'];      
+      drupal_set_message('Successfully Added Transformation Rule');
+      
+    } elseif ($op == 'Save Transformation Rule Order') {
+      
+      // Generate new regex array
+      $new_regex = array();
+      $old_regex = $form_state['storage']['regex'];
+      foreach ($form_state['values']['regex-data'] as $key => $element) {
+        $new_regex['pattern'][ $element['new_index'] ] = $old_regex['pattern'][ $element['old_index'] ];
+        $new_regex['replace'][ $element['new_index'] ] = $old_regex['replace'][ $element['old_index'] ];
+      }
+      
+      // sort new regex arrays
+      asort($new_regex['pattern']);
+      asort($new_regex['replace']);
+
+      $form_state['storage']['regex'] = $new_regex;
+    } elseif ($op == 'Delete Transformation') {
+    
+      // Unset regex rule
+      $index = $form_state['clicked_button']['#name'];
+      unset($form_state['storage']['regex']['pattern'][$index]);
+      unset($form_state['storage']['regex']['replace'][$index]);
+      
+    } elseif ($op == 'Test Transformation Rules') {
+    
+      $patterns = $form_state['storage']['regex']['pattern'];
+      $replaces = $form_state['storage']['regex']['replace'];
+      $test_string = $form_state['values']['test_string'];
+      $form_state['storage']['test_regex_result'] = preg_replace($patterns, $replaces, $test_string);
+      $form_state['storage']['test_regex_test'] = $test_string;
+    }
+  }  
+
+}
+
+/**
+ * Edit Field Form
+ *
+ * This form is meant to be called from a bulk loader form. The following should be set 
+ * in the query section of the path:
+ *  - template_id=\d+: the template which the edited field is part of
+ *  - record_id=\d+: the priority or key in the template array of the record the field 
+ *      is currently part of
+ *  - field_index=\d+: the key of the field in the fields array of the previously
+ *      specified record
+ * 
+ * @param $form_state
+ *   Contains the values and storage for the form
+ * @return
+ *   A form array to be rendered by drupal_get_form
+ */
+function tripal_bulk_loader_edit_template_field_form (&$form_state = NULL) {
+  $form = array();
+ 	$form['#cache'] = TRUE; // Make sure the form is cached.
+ 
+ 	// get template id from path
+ 	$template_id = ($_GET['template_id']) ? $_GET['template_id'] : $form_state['values']['template_id'];
+ 	
+ 	// if there is no template supplied don't return rest of form
+ 	if (!$template_id) {
+ 	  return $form;
+ 	}
+
+  // Pre-set Variables needed for form proper------------------------------------------
+
+ 	// If this is the first load of the form (no form state) we need to initialize some variables
+ 	if (!$form_state['storage']['template']) {
+    $sql = "SELECT * FROM {tripal_bulk_loader_template} WHERE template_id=%d";
+    $template = db_fetch_object(db_query($sql, $template_id));
+    $form_state['storage']['template_array'] = unserialize($template->template_array);
+    $form_state['storage']['template'] = $template;
+    
+    $form_state['storage']['record2priority'] = array();
+    foreach ($form_state['storage']['template_array'] as $priority => $record_array) {
+      if (!is_array($record_array)) { continue; }
+      $form_state['storage']['record2priority'][$record_array['record_id']] = $priority;
+    }
+    
+    $form_state['storage']['referring URL'] = $_SERVER["HTTP_REFERER"];
+  } else {
+    $template = $form_state['storage']['template'];
+  }
+  
+  // get the field from the path
+  if (!($_GET['record_id']===NULL || $_GET['field_index']===NULL)) {
+    $priority = $_GET['record_id'];
+    $field_index = $_GET['field_index'];
+    $template_field = $form_state['storage']['template_array'][$priority]['fields'][$field_index];
+    $form_state['storage']['original_field'] = $template_field;
+    $form_state['storage']['original_field']['priority'] = $priority;
+    $form_state['storage']['original_field']['field_index'] = $field_index;
+  }
+  
+  $field_type = ($form_state['values']['field_type'])? $form_state['values']['field_type'] : $template_field['type'];
+
+  // Tables and default table
+  $tables = tripal_core_get_chado_tables();
+  if ($form_state['values']) {
+    $table = $form_state['values']['chado_table'];
+  } else {
+    $table = $form_state['storage']['template_array'][$priority]['table'];  
+  }
+  
+  
+  // Fields and foreign key mappings
+  $chado_fields = array();
+  $fk_options = array();
+  $fk_options['NULL'] = 'None';
+  $table_description = module_invoke_all('chado_'.$table.'_schema');
+  if ($field_type == 'foreign key') {
+    $foreign_field2table = array();
+    foreach ($table_description['foreign keys'] as $key_table => $key_array) {
+      foreach ($key_array['columns'] as $left_field => $right_field) {
+        $chado_fields[$left_field] = $left_field;
+        $foreign_field2table[$left_field] = $key_table;
+      }
+    }
+    reset($chado_fields);
+    
+    // set default field
+    if (empty($chado_fields)) {
+      $field = NULL;
+    } elseif ($chado_fields[$form_state['values']['chado_field']]) {
+      $field = $form_state['values']['chado_field'];
+    } elseif ($template_field['field']) {
+      $field = $template_field['field'];
+    } else {
+      $field = current($chado_fields);
+    }
+    //dpm($field, 'field');
+    
+    // Foreign key options
+    $foreign_table = $foreign_field2table[$field];
+    if ($foreign_table) {
+      foreach ($form_state['storage']['record2priority'] as $record_nameT => $priorityT ) {
+        if (preg_match('/^'.$foreign_table.'$/', $form_state['storage']['template_array'][$priorityT]['table'])) {
+          $fk_options[$record_nameT] = $record_nameT;
+        }
+      }
+    }
+  } else {
+    foreach($table_description['fields'] as $field_name => $field_array) {
+        $chado_fields[$field_name] = $field_name;
+    }
+  }
+    
+//  dpm(array( 'tables' => $tables, 'default table' => $table, 'record name' => $record_name, 'priority' => $priority,
+//    'fields' => $chad_fields, 'default field' => $field, 'foreign field=>table' => $foreign_field2table, 
+//    'table desc' => $table_description, 'foreign record options' => $fk_options, 'foreign table' => $foreign_table
+//    ), 'Variables');
+  
+  // Start of Form Proper--------------------------------------------------------------
+  
+  $form['template_name'] = array(
+    '#type' => 'item',
+    '#title' => 'Template',
+    '#value' => $template->name,
+  );
+
+  $form['template_id'] = array(
+    '#type' => 'hidden',
+    '#value' => $template_id,
+  );
+
+  $form['edit_fields'] = array(
+    '#type' => 'fieldset',
+    '#prefix' => '<div id="tripal_bulk_loader_template-edit_field">',
+    '#suffix' => '</div>',
+  );
+
+	$form['edit_fields']['field_type'] = array(
+		'#type' => 'radios',
+		'#title' => t('Type of Field'),
+		'#options' => array(
+			'table field' => t('Spreadsheet: Fields which maps to a Spreadsheet Column'),
+			'constant' => t('Constant: Field which remains Constant throughout the Spreadsheet'),
+			'foreign key' => t('Foreign Key: Fields which map to a record in another table'),
+		),
+		'#required' => TRUE,
+		'#default_value' => $field_type,
+    '#ahah' => array(
+      'path' => 'admin/tripal/tripal_bulk_loader_template/edit_field_ahah',
+      'wrapper' => 'tripal_bulk_loader_template-edit_field',
+      'effect' => 'fade'
+    ),
+	);
+
+  // check template array for records then edit one more
+  if (!$form_state['storage']['record2priority']) { 
+    $groups = array();
+  } else {
+    $groups = array_flip($form_state['storage']['record2priority']);
+  }
+  $groups['NONE'] = 'Select a Record';
+  $groups['NEW'] = 'New Record';
+  $form['edit_fields']['field_group']  = array(
+    '#type' => 'select',
+    '#title' => 'Record',
+    '#description' => 'This is used to group a set of fields together allowing '
+      .'multiple records to be inserted into the same table per line of the spreadsheet',
+    '#options' => $groups,
+    '#default_value' => (preg_match('/(\d+|\w+)/',$form_state['values']['field_group'])) ? $form_state['values']['field_group'] : $priority,
+    '#ahah' => array(
+      'path' => 'admin/tripal/tripal_bulk_loader_template/edit_field_ahah',
+      'wrapper' => 'tripal_bulk_loader_template-edit_field',
+      'effect' => 'fade'
+    ),
+    '#required' => TRUE,
+  );
+  
+  $form['edit_fields']['record_name'] = array(
+    '#type' => (preg_match('/NEW/', $form_state['values']['field_group'])) ? 'textfield' : 'hidden',
+    '#title' => 'Unique Record Name',
+    '#prefix' => '<div id="tripal_bulk_loader_template-edit_record">',
+    '#suffix' => '</div>',
+    '#default_value' => $form_state['values']['record_name'],
+  );
+  
+  $form['edit_fields']['field_title'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Human-readable Title for Field'),
+    '#default_value' => ($form_state['values']['field_title']) ? $form_state['values']['field_title'] : $template_field['title'],
+  );
+  
+  // Spreadsheet column
+  $form['edit_fields']['columns'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Spreadsheet Column'),
+    '#collapsible' => TRUE,
+    '#collapsed' => ($field_type == 'table field')? FALSE : TRUE,
+  );
+
+  $form['edit_fields']['columns']['sheet_name'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Worksheet'),
+    '#description' => t('Specify the name of the worksheet.'),
+    '#size' => 5,
+    '#default_value' => ($form_state['values']['sheet_name'])? $form_state['values']['sheet_name'] : $template_field['spreadsheet sheet'],    
+  );
+  
+  $form['edit_fields']['columns']['column_number'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Column'),
+    '#description' => t('Specify the column in the spreadsheet that this field maps to where the first column is 1.'),
+    '#size' => 5,
+    '#default_value' => ($form_state['values']['column_number']) ? $form_state['values']['column_number'] : $template_field['spreadsheet column'],
+  );
+
+  // Global Value
+  $form['edit_fields']['constant'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Constant'),
+    '#collapsible' => TRUE,
+    '#collapsed' => ($field_type == 'constant')? FALSE : TRUE,
+  );
+  
+  $form['edit_fields']['constant']['constant_value'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Constant Value'),
+    '#description' => t('Specify the value you wish this field to have regardless of spreadsheet data.'),
+    '#default_value' => ($form_state['values']['constant_value']) ? $form_state['values']['constant_value'] : $template_field['constant value'],
+  );
+
+  // Foreign Key
+  $form['edit_fields']['foreign_key'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'Foreign Key',
+    '#collapsible' => TRUE,
+    '#collapsed' => ($field_type == 'foreign key')? FALSE : TRUE,
+  );
+
+  $form['edit_fields']['foreign_key']['foreign_record'] = array(
+    '#type' => 'select',
+    '#title' => 'Record to refer to',
+    '#descripion' => 'Select the record that this foreign key shouold refer to. The record needs to already exist and be of the correct table.',
+    '#options' => $fk_options,
+  );
+
+  // Chado Field
+  $form['edit_fields']['chado'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Chado Field/Column Details'),
+    '#description' => t('Specify the Table/Field in chado that this field maps to.'),
+  );
+  
+  $form['edit_fields']['chado']['chado_table'] = array(
+    '#type' => 'select',
+    '#title' => t('Chado Table'),
+    '#options' => $tables,
+    '#default_value' => $table,
+    '#ahah' => array(
+      'path' => 'admin/tripal/tripal_bulk_loader_template/edit_field_ahah',
+      'wrapper' => 'tripal_bulk_loader_template-edit_field',
+      'effect' => 'fade'
+      ),
+  );
+  
+  $form['edit_fields']['chado']['chado_field'] = array(
+    '#type' => 'select',
+    '#title' => t('Chado Field/Column'),
+    '#options' => $chado_fields,
+    '#default_value' => ($form_state['values']['chado_field']) ? $form_state['values']['chado_field'] : $template_field['field'],
+    '#ahah' => array(
+      'path' => 'admin/tripal/tripal_bulk_loader_template/edit_field_ahah',
+      'wrapper' => 'tripal_bulk_loader_template-edit_field',
+      'effect' => 'fade'
+    ),
+  );
+
+  $form['edit_fields']['additional'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'Additional Options',
+  );
+
+  $form['edit_fields']['additional']['required'] = array(
+    '#type' => 'checkbox',
+    '#title' => 'Make this file required',
+    '#default_value' => $template_field['required'],
+  );
+  
+  $form['edit_fields']['additional']['regex_transform'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'Transform Spreadsheet Value Rules',
+    '#collapsible' => TRUE, 
+    '#collapsed' => (!$template_field['regex']['pattern']) ? TRUE : FALSE,
+  );
+  
+  $transformation_msg = '<p>A transformation rule allows you to transform the original value '
+      .'(usually from a user submitted spreadsheet) into the form you would like it stored '
+      .'in the chado database. Each rule consists of a match pattern (a php regular expression '
+      .'which determines which replacement patterns are applied and captures regions of the '
+      .'original value) and a replacement pattern (a string which may contain capture references '
+      .'that describes what the new value should be). Each rule is applied to the result of the '
+      .'previous rule.<p>'; 
+  $form['edit_fields']['additional']['regex_transform']['regex_description'] = array(
+    '#type' => 'item',
+    '#value' => $transformation_msg,
+  );
+
+  $form['edit_fields']['additional']['regex_transform']['regex-data'] = array(
+    '#tree' => TRUE,
+  );
+  foreach ($template_field['regex']['pattern'] as $index => $pattern) {
+    $data_element = array(
+      'pattern' => array(
+        '#type' => 'item',
+        '#value' => $pattern,
+      ),
+      'replace' => array(
+        '#type' => 'item',
+        '#value' => $template_field['regex']['replace'][$index],
+      ),
+      'old_index' => array(
+        '#type' => 'hidden',
+        '#value' => $index,
+      ),
+      'new_index' => array(
+        '#type' => 'select',
+        '#options' => range(0,sizeof($template_field['regex']['pattern'])-1),
+        '#default_value' => $index,
+      ),
+      'id' => array(
+        '#type' => 'hidden',
+        '#value' => $index,
+      ),
+      'submit-delete' => array(
+        '#type' => 'submit',
+        '#value' => 'Delete Transformation',
+        '#name' => $index,
+      ),
+    );
+    $form['edit_fields']['additional']['regex_transform']['regex-data'][$index] = $data_element;
+  }
+  
+  $form['edit_fields']['additional']['regex_transform']['submit-reorder_regex'] = array(
+    '#type' => 'submit',
+    '#value' => 'Save Transformation Rule Order'
+  );
+  
+  $form['edit_fields']['additional']['regex_transform']['new_regex'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'Add a new Transformation Rule',
+  );
+  
+  $form['edit_fields']['additional']['regex_transform']['new_regex']['pattern'] = array(
+    '#type' => 'textfield',
+    '#title' => 'Match Pattern',
+    '#description' => 'You can use standard <b>php regular expressions</b> in this field to specify a '
+      .'pattern. Only if this pattern matches the value in the spreadsheet does the replacement '
+      .'pattern get applied to the value. To capture a section of your value for use in the '
+      .'replacement patten surround with round brackets. For example, <i>GI:(\d+)</i> will match '
+      .' NCBI gi numbers and will capture the numerical digits for use in the replacement pattern. '
+      .' To match and capture any value use <i>.*</i>',
+  );
+  
+  $form['edit_fields']['additional']['regex_transform']['new_regex']['replace'] = array(
+    '#type' => 'textfield',
+    '#title' => 'Replacement Pattern',
+    '#description' => '<p>This pattern should contain the text you want to replace the match pattern '
+    .'mentioned above. It can include references of the form <b>\n</b> where n is the number of the '
+    .'capture in the match pattern. For example, \1 will be replaced with the text matched in your '
+    .'first set of round brackets.</p>',
+  ); 
+  
+  if ($field_type == 'table field') {
+    $tab = '&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp';
+    $form['edit_fields']['additional']['regex_transform']['new_regex']['replace']['#description'] .= '<p>'
+      .'The following references are also available for spreadsheet fields: <b><#column:<i>number</i>#></b>. '
+      .'This allows you to substitute other spreadsheet values into the current field. For example, '
+      .'if you had the following line:<br>'
+      .$tab.'SNP'.$tab.'15-Jan-2011'.$tab.'1'.$tab.'54'.$tab.'Contig34355'
+      .'<br> and your current field is for column #1 and you\'re inserting into the chado field '
+      .'feature.uniquename then you might want to add in the data to ensure your uniquename is '
+      .'unique. The Match Pattern is (.*) to select all the first column and the Replacement '
+      .'Pattern could be \1_<#column:2#> which would insert SNP_15-Jan-2011 into the database.</p>';
+  }
+  
+  $form['edit_fields']['additional']['regex_transform']['new_regex']['submit-add_transform'] = array(
+    '#type' => 'submit',
+    '#value' => 'Add Transformation',
+  );
+  
+  $form['edit_fields']['additional']['regex_transform']['test_regex'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'Test Transformation Rules',
+  );
+  
+  $form['edit_fields']['additional']['regex_transform']['test_regex']['test_string'] = array(
+    '#type' => 'textfield',
+    '#title' => 'Test Value',
+    '#description' => 'This should be a value that you expect the above transformation rules '
+      .'to be applied to.',
+    '#default_value' => $form_state['storage']['test_regex_test'],
+  );
+  
+  $form['edit_fields']['additional']['regex_transform']['test_regex']['test_result'] = array(
+    '#type' => 'textfield',
+    '#title' => 'Test Result',
+    '#description' => 'This is the value that would be saved to the database after the above transformation '
+      .'riles were applied to the Test Value.',
+    '#default_value' => $form_state['storage']['test_regex_result'],
+  );
+  
+  $form['edit_fields']['additional']['regex_transform']['test_regex']['submit-test'] = array(
+    '#type' => 'submit',
+    '#value' => 'Test Transformation Rules'
+  );
+  
+  $form['edit_fields']['submit-edit_field'] = array(
+      '#type' => 'submit',
+      '#value' => 'Edit Field'
+  );
+
+  $form['edit_fields']['submit-cancel'] = array(
+      '#type' => 'submit',
+      '#value' => 'Cancel'
+  );
+  
+  return $form;
+}
+
+/**
+ * Edit Field Form Submit
+ * 
+ * @param $form
+ *   The form that was submitted
+ * @param $form_state
+ *   The values and storage for the form
+ */
+function tripal_bulk_loader_edit_template_field_form_submit ($form, &$form_state) {
+
+  $op = $form_state['values'][ $form_state['clicked_button']['#name'] ];
+  //dpm($op, 'Operation Submitted');
+  
+  //Clear Test
+  $form_state['storage']['test_regex_result'] = NULL;
+  $form_state['storage']['test_regex_test'] = NULL;
+  
+  if (!$form_state['ahah_submission']) {
+    if ($op ==  'Edit Field') {
+
+      // If new record
+      if (preg_match('/NEW/',$form_state['values']['field_group'])) {
+        // add new record
+        $record_name = $form_state['values']['record_name'];
+        $priority = sizeof($form_state['storage']['template_array']) + 1;
+        $old_priority = $form_state['storage']['original_field']['priority'];
+        $field_index = $form_state['storage']['original_field']['field_index'];
+        $form_state['storage']['record2priority'][$record_name] = $priority;
+        $form_state['storage']['template_array'][$priority]['table'] = $form_state['values']['chado_table'];
+        $form_state['storage']['template_array'][$priority]['record_id'] = $record_name;
+        
+      } else {
+        $priority = $form_state['values']['field_group'];
+        $old_priority = $form_state['storage']['original_field']['priority'];
+        $field_index = $form_state['storage']['original_field']['field_index'];
+        $record_name = $form_state['storage']['record2priority'][$priority];
+      }
+      
+      $field = $form_state['storage']['original_field'];
+      if ($form_state['values']['field_type'] == 'table field') {
+          $field['type'] = 'table field';
+          $field['title'] = $form_state['values']['field_title'];
+          $field['field'] = $form_state['values']['chado_field'];
+          $field['required'] = $form_state['values']['required'];
+          //$field['allowed values'] = empty by default;
+          $field['spreadsheet sheet'] = $form_state['values']['sheet_name'];
+          $field['spreadsheet column'] = $form_state['values']['column_number'];
+          //$field['exposed'] = 'true|false';  If exposed, will give a select box first from allowed values if set, second from database if values not set.  
+      } elseif ($form_state['values']['field_type'] == 'constant') {
+          $field['type'] = 'constant';
+          $field['title'] = $form_state['values']['field_title'];
+          $field['field'] = $form_state['values']['chado_field'];
+          $field['required'] = $form_state['values']['required'];
+          //$field['allowed values'] = empty by default;
+          $field['constant value'] = $form_state['values']['constant_value'];
+          //$field['exposed'] = 'true|false';  If exposed, will give a select box first from allowed values if set, second from database if values not set.      
+      } elseif ($form_state['values']['field_type'] == 'foreign key') {
+          $field['type'] = 'foreign key';
+          $field['title'] = $form_state['values']['field_title'];
+          $field['field'] = $form_state['values']['chado_field'];
+          $field['foreign key'] = $form_state['values']['foreign_record'];
+          $field['required'] = $form_state['values']['required'];     
+      }
+      
+      // Deal with any additional options
+      
+      // if the record has changed...
+      $form_state['storage']['template_array'][$priority]['table'] = $form_state['values']['chado_table'];
+      if ($old_priority != $priority) {
+        $form_state['storage']['template_array'][$priority]['fields'][] = $field;
+        unset($form_state['storage']['template_array'][$old_priority]['fields'][$field_index]);
+        
+        // if there are no fields left delete the old record
+        if (!$form_state['storage']['template_array'][$old_priority]['fields']) {
+          unset($form_state['storage']['template_array'][$old_priority]);
+        }
+      } else {
+        $form_state['storage']['template_array'][$priority]['fields'][$field_index] = $field;
+      }
+
+      // Save Template
+      $form_state['storage']['template']->template_array = serialize($form_state['storage']['template_array']);
+      $success = drupal_write_record('tripal_bulk_loader_template', $form_state['storage']['template'], array('template_id'));
+  
+      if ($success) {
+        drupal_set_message('Successfully Updated Field');
+        drupal_set_message('Template Saved.'); 
+
+        $path = explode('?',$form_state['storage']['referring URL']);
+        parse_str($path[1], $query);
+        $query['template_id'] = $form_state['storage']['template']->template_id;
+        drupal_goto($path[0], $query);        
+      } else {
+        drupal_set_message('Unable to Save Template!', 'error');
+        watchdog('T_bulk_loader',
+          'Unable to save bulk loader template: %template',
+          array('%template' => print_r($form_state['storage']['template'], TRUE)),
+          WATCHDOG_ERROR
+        );
+      }
+      
+    } elseif ($op ==  'Cancel') {
+    
+        $path = explode('?',$form_state['storage']['referring URL']);
+        parse_str($path[1], $query);
+        $query['template_id'] = $form_state['storage']['template']->template_id;
+        drupal_goto($path[0], $query);    
+
+    } elseif ($op == 'Add Transformation') {
+
+      // Add transformation rule to original field
+      $form_state['storage']['original_field']['regex']['pattern'][] = '/' . $form_state['values']['pattern'] . '/';
+      $form_state['storage']['original_field']['regex']['replace'][] = $form_state['values']['replace'];      
+
+      // Add original field back into template
+      $priority = $form_state['storage']['original_field']['priority'];
+      $field_index = $form_state['storage']['original_field']['field_index'];
+      $form_state['storage']['template_array'][$priority]['fields'][$field_index] = $form_state['storage']['original_field'];
+      
+      // Save Template
+      $form_state['storage']['template']->template_array = serialize($form_state['storage']['template_array']);
+      $success = drupal_write_record('tripal_bulk_loader_template', $form_state['storage']['template'], array('template_id'));
+  
+      if ($success) {
+        drupal_set_message('Successfully Added Transformation Rule');
+        drupal_set_message('Template Saved.'); 
+      } else {
+        drupal_set_message('Unable to Save Template!', 'error');
+        watchdog('T_bulk_loader',
+          'Unable to save bulk loader template: %template',
+          array('%template' => print_r($form_state['storage']['template'], TRUE)),
+          WATCHDOG_ERROR
+        );
+      }
+    } elseif ($op == 'Save Transformation Rule Order') {
+      
+      // Generate new regex array
+      $new_regex = array();
+      $old_regex = $form_state['storage']['original_field']['regex'];
+      foreach ($form_state['values']['regex-data'] as $key => $element) {
+        $new_regex['pattern'][ $element['new_index'] ] = $old_regex['pattern'][ $element['old_index'] ];
+        $new_regex['replace'][ $element['new_index'] ] = $old_regex['replace'][ $element['old_index'] ];
+      }
+      
+      // sort new regex arrays
+      asort($new_regex['pattern']);
+      asort($new_regex['replace']);
+      
+      // Add back to original field
+      $form_state['storage']['original_field']['regex'] = $new_regex;
+      $priority = $form_state['storage']['original_field']['priority'];
+      $field_index = $form_state['storage']['original_field']['field_index'];
+      $form_state['storage']['template_array'][$priority]['fields'][$field_index] = $form_state['storage']['original_field'];
+      
+      // Save Template
+      $form_state['storage']['template']->template_array = serialize($form_state['storage']['template_array']);
+      $success = drupal_write_record('tripal_bulk_loader_template', $form_state['storage']['template'], array('template_id'));
+  
+      if ($success) {
+        drupal_set_message('Successfully Reordered Transformation Rules');
+        drupal_set_message('Template Saved.'); 
+      } else {
+        drupal_set_message('Unable to Save Template!', 'error');
+        watchdog('T_bulk_loader',
+          'Unable to save bulk loader template: %template',
+          array('%template' => print_r($form_state['storage']['template'], TRUE)),
+          WATCHDOG_ERROR
+        );
+      }
+    } elseif ($op == 'Delete Transformation') {
+    
+      // Unset regex rule
+      $index = $form_state['clicked_button']['#name'];
+      unset($form_state['storage']['original_field']['regex']['pattern'][$index]);
+      unset($form_state['storage']['original_field']['regex']['replace'][$index]);
+      
+      $priority = $form_state['storage']['original_field']['priority'];
+      $field_index = $form_state['storage']['original_field']['field_index'];
+      $form_state['storage']['template_array'][$priority]['fields'][$field_index] = $form_state['storage']['original_field'];
+      
+      // Save Template
+      $form_state['storage']['template']->template_array = serialize($form_state['storage']['template_array']);
+      $success = drupal_write_record('tripal_bulk_loader_template', $form_state['storage']['template'], array('template_id'));
+  
+      if ($success) {
+        drupal_set_message('Successfully Reordered Transformation Rules');
+        drupal_set_message('Template Saved.'); 
+      } else {
+        drupal_set_message('Unable to Save Template!', 'error');
+        watchdog('T_bulk_loader',
+          'Unable to save bulk loader template: %template',
+          array('%template' => print_r($form_state['storage']['template'], TRUE)),
+          WATCHDOG_ERROR
+        );
+      }
+      
+    } elseif ($op == 'Test Transformation Rules') {
+    
+      $patterns = $form_state['storage']['original_field']['regex']['pattern'];
+      $replaces = $form_state['storage']['original_field']['regex']['replace'];
+      $test_string = $form_state['values']['test_string'];
+      $form_state['storage']['test_regex_result'] = preg_replace($patterns, $replaces, $test_string);
+      $form_state['storage']['test_regex_test'] = $test_string;
+    }
+  } 
+  
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+// AHAH Callbacks
+//////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * AHAH Function: Replace $form['add_fields'] in tripal_bulk_loader_add_template_field_form
+ *
+ * @return
+ *  JSON Data printed to the screen
+ */
+function tripal_bulk_loader_add_field_ahah () {
+
+  $form_state = array('storage' => NULL, 'submitted' => FALSE);
+  $form_build_id = $_POST['form_build_id'];
+  $form = form_get_cache($form_build_id, $form_state);
+  $args = $form['#parameters'];
+  $form_id = array_shift($args);
+  $form_state['post'] = $form['#post'] = $_POST;
+  
+  // Enable the submit/validate handlers to determine whether AHAH-submittted.
+  $form_state['ahah_submission'] = TRUE;
+  
+  $form['#programmed'] = $form['#redirect'] = FALSE;
+  drupal_process_form($form_id, $form, $form_state);
+  $form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id);
+  
+  $form_element = $form['add_fields'];
+  // Remove the wrapper so we don't double it up.
+  //unset($form_element['#prefix'], $form_element['#suffix']);
+
+  $output = theme('status_messages');
+  $output .= drupal_render($form_element);
+
+  // Final rendering callback.
+  print drupal_json(array('status' => TRUE, 'data' => $output));
+  exit();
+  
+}
+
+/**
+ * AHAH Function: Replace $form['edit_fields'] in tripal_bulk_loader_edit_template_field_form
+ *
+ * @return
+ *  JSON Data printed to the screen
+ */
+function tripal_bulk_loader_edit_field_ahah () {
+
+  $form_state = array('storage' => NULL, 'submitted' => FALSE);
+  $form_build_id = $_POST['form_build_id'];
+  $form = form_get_cache($form_build_id, $form_state);
+  $args = $form['#parameters'];
+  $form_id = array_shift($args);
+  $form_state['post'] = $form['#post'] = $_POST;
+  
+  // Enable the submit/validate handlers to determine whether AHAH-submittted.
+  $form_state['ahah_submission'] = TRUE;
+  
+  $form['#programmed'] = $form['#redirect'] = FALSE;
+  drupal_process_form($form_id, $form, $form_state);
+  $form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id);
+  
+  $form_element = $form['edit_fields'];
+  // Remove the wrapper so we don't double it up.
+  unset($form_element['#prefix'], $form_element['#suffix']);
+
+  $output = theme('status_messages');
+  $output .= drupal_render($form_element);
+
+  // Final rendering callback.
+  print drupal_json(array('status' => TRUE, 'data' => $output));
+  exit();
+  
+}

+ 7 - 0
modules/base/tripal_bulk_loader/tripal_bulk_loader.info

@@ -0,0 +1,7 @@
+name = Tripal Bulk Loader
+description = A module for uploading tab-delimit data into GMOD chado database using templates.
+core = 6.x
+project = tripal_bulk_loader
+package = Tripal
+dependencies[] = tripal_core
+version = 6.x-0.3.1b-1.0

+ 108 - 0
modules/base/tripal_bulk_loader/tripal_bulk_loader.install

@@ -0,0 +1,108 @@
+<?php
+
+/**
+ * Implements hook_install
+ */
+function tripal_bulk_loader_install(){
+   drupal_install_schema('tripal_bulk_loader');
+}
+
+/**
+ * Implements hook_uninstall
+ */
+function tripal_bulk_loader_uninstall(){
+   drupal_uninstall_schema('tripal_bulk_loader');
+}
+
+/**
+ * Implements hook_schema
+ *
+ * Creates the following tables in the Drupal database:
+ *  - tripal_bulk_loader: Stores extra details for bulk loading jobs (nodes)
+ *  - tripal_bulk_loader_template: Stores all loading templates
+ *  - tripal_bulk_loader_inserted: Keeps track of all records inserted for a given bulk loading job
+ */
+function tripal_bulk_loader_schema() {
+	$schema = array();
+	$schema['tripal_bulk_loader'] = array(
+      'fields' => array(
+         'nid' => array(
+            'type' => 'int',
+            'unsigned' => TRUE,
+            'not null' => TRUE,
+			),
+         'loader_name' => array(
+            'type' => 'varchar',
+			),
+         'template_id' => array(
+            'type' => 'varchar',
+			),
+			'file' => array(
+        'type' => 'varchar',
+        'not null' => TRUE
+			),
+			'job_id' => array(
+			  'type' => 'int',
+			),
+			'job_status' => array(
+			  'type' => 'varchar',
+			),
+			'file_has_header' => array(
+				'type' => 'int',
+				'size' => 'tiny',
+				'not null' => TRUE,
+        'default' => 0,
+			),
+		),
+  		'primary key' => array('nid'),
+  		'unique keys' => array(
+    		'name' => array('loader_name')
+		),
+	);
+	$schema['tripal_bulk_loader_template'] = array(
+      'fields' => array(
+         'template_id' => array(
+            'type' => 'serial',
+            'unsigned' => TRUE,
+            'not null' => TRUE,
+			),
+			'name' => array(
+            'type' => 'varchar',
+			),
+         'template_array' => array(
+            'type' => 'varchar',
+			)
+		),
+  		'primary key' => array('template_id'),
+		'unique keys' => array(
+    		'name' => array('name')
+		),
+	);
+	$schema['tripal_bulk_loader_inserted'] = array(
+	  'fields' => array(
+	    'tripal_bulk_loader_inserted_id' => array(
+	      'type' => 'serial',
+	      'not null' => TRUE
+	    ),
+      'nid' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+			),
+			'table_inserted_into' => array(
+			  'type' => 'varchar',
+			  'not null' => TRUE,
+			),
+			'table_primary_key' => array(
+			  'type' => 'varchar',
+			  'not null' => TRUE,
+			),
+			'ids_inserted' => array(
+			  'type' => 'text',
+			  'not null' => TRUE
+			),
+	  ),
+	  'primary key' => array('tripal_bulk_loader_inserted_id'),
+	);
+	return $schema;
+}

+ 518 - 0
modules/base/tripal_bulk_loader/tripal_bulk_loader.loader.inc

@@ -0,0 +1,518 @@
+<?php
+
+/**
+ * Add Loader Job Form
+ *
+ * This form is meant to be included on the node page to allow users to submit/re-submit 
+ * loading jobs
+ */
+function tripal_bulk_loader_add_loader_job_form ($form_state, $node) {
+  $form = array();
+  
+  $form['nid'] = array(
+    '#type' => 'hidden',
+    '#value' => $node->nid,
+  ); 
+  
+  $form['file'] = array(
+  	'#type' => 'hidden',
+  	'#value' => $node->file
+  );
+  
+  $form['job_id'] = array(
+    '#type' => 'hidden',
+    '#value' => $node->job_id,
+  );
+  
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => ($node->job_id) ? 'Re-Submit Job' : 'Submit Job',
+  );
+
+  $form['submit-cancel'] = array(
+    '#type' => ($node->job_id)? 'submit' : 'hidden',
+    '#value' => 'Cancel Job',
+  );
+  
+  $form['submit-revert'] = array(
+    '#type' => ($node->job_id) ? 'submit' : 'hidden',
+    '#value' => 'Revert',
+  );  
+  
+  return $form;
+}
+
+/**
+ * Add Loader Job Form (Submit)
+ */
+function tripal_bulk_loader_add_loader_job_form_submit ($form, $form_state) {
+  global $user;
+  
+  if (preg_match('/Submit Job/', $form_state['values']['op'])) {
+    //Submit Tripal Job
+		$job_args[1] = $form_state['values']['nid'];
+		if (is_readable($form_state['values']['file'])) {
+			$fname = basename($form_state['values']['file']);
+			$job_id = tripal_add_job("Bulk Loading Job: $fname",'tripal_bulk_loader', 'tripal_bulk_loader_load_data', $job_args, $user->uid);
+			
+			// add job_id to bulk_loader node
+      $success = db_query("UPDATE {tripal_bulk_loader} SET job_id=%d WHERE nid=%d", $job_id, $form_state['values']['nid']);
+      
+      // change status
+      db_query("UPDATE tripal_bulk_loader SET job_status='%s' WHERE nid=%d",'Submitted to Queue', $form_state['values']['nid']);
+		} else {
+			drupal_set_message("Can not open ".$form_state['values']['file'].". Job not scheduled.");
+		}    
+  } elseif (preg_match('/Re-Submit Job/', $form_state['values']['op'])) {
+    tripal_jobs_rerun($form_state['values']['job_id']);
+    db_query("UPDATE tripal_bulk_loader SET job_status='%s' WHERE nid=%d",'Submitted to Queue', $form_state['values']['nid']);
+  } elseif (preg_match('/Cancel Job/', $form_state['values']['op'])) {
+    db_query("UPDATE tripal_bulk_loader SET job_status='%s' WHERE nid=%d",'Job Cancelled', $form_state['values']['nid']);
+    tripal_jobs_cancel($form_state['values']['job_id']);
+  } elseif (preg_match('/Revert/', $form_state['values']['op'])) {
+  
+    // Remove the records from the database that were already inserted
+    $resource = db_query('SELECT * FROM {tripal_bulk_loader_inserted} WHERE nid=%d ORDER BY tripal_bulk_loader_inserted_id DESC', $form_state['values']['nid']);
+    while ($r = db_fetch_object($resource)) {
+      $ids = preg_split('/,/',$r->ids_inserted);
+      db_query('DELETE FROM %s WHERE %s IN (%s)',$r->table_inserted_into, $r->table_primary_key, $r->ids_inserted);
+      $result = db_fetch_object(db_query('SELECT true as present FROM %s WHERE %s IN (%s)', $r->table_inserted_into, $r->table_primary_key, $r->ids_inserted));
+      if (!$result->present) {
+        drupal_set_message('Successfully Removed data Inserted into the '.$r->table_inserted_into.' table.');
+        db_query('DELETE FROM {tripal_bulk_loader_inserted} WHERE tripal_bulk_loader_inserted_id=%d',$r->tripal_bulk_loader_inserted_id);
+      } else {
+        drupal_set_message('Unable to remove data Inserted into the '.$r->table_inserted_into.' table!', 'error');
+      }
+    }
+    
+    // reset status
+    db_query("UPDATE tripal_bulk_loader SET job_status='%s' WHERE nid=%d",'Reverted -Data Deleted', $form_state['values']['nid']);
+  }
+
+}
+
+/**
+ * Tripal Bulk Loader
+ *
+ * This is the function that's run by tripal_launch_jobs to bulk load chado data.
+ *
+ * @param $nid
+ *   The Node ID of the bulk loading job node to be loaded. All other needed data is expected to be 
+ *   in the node (ie: template ID and file)
+ *
+ * Note: Instead of returning a value this function updates the tripal_bulk_loader.status.
+ *   Errors are thrown through watchdog and can be viewed at admin/reports/dblog.
+ */
+function tripal_bulk_loader_load_data($nid) {
+  
+  // ensure no timeout
+  set_time_limit(0);
+  
+  // set the status of the job (in the node not the tripal jobs)
+  db_query("UPDATE tripal_bulk_loader SET job_status='%s' WHERE nid=%d",'Loading...', $nid);
+  
+  print "Memory Usage (Start): ".number_format((memory_get_usage() * 0.000000953674316), 5, '.', ',') . " Mb\n";
+  
+  $node = node_load($nid);
+  print "Template: ".$node->template->name." (".$node->template_id.")\n";
+  print "File: ".$node->file."\n";
+  
+  print "Memory Usage (After Node Load): ".number_format((memory_get_usage() * 0.000000953674316), 5, '.', ',') . " Mb\n";
+  
+  // Prep Work ==================================================================================
+  $loaded_without_errors = TRUE;
+  
+  // Generate default values array
+  $default_data = array();
+  $field2column = array();
+  $record2priority = array();
+  foreach ($node->template->template_array as $priority => $record_array) {
+    if (!is_array($record_array)) { continue; }
+    
+    //watchdog('T_bulk_loader','1)'.$record_array['record_id']." => \n<pre>".print_r($record_array,TRUE).'</pre>', array(), WATCHDOG_NOTICE);
+    
+    foreach ($record_array['fields'] as $field_index => $field_array) {
+      
+      $default_data[$priority]['table'] = $record_array['table'];
+      $default_data[$priority]['mode'] = ($record_array['mode']) ? $record_array['mode'] : 'insert_unique';
+      $default_data[$priority]['record_id'] = $record_array['record_id'];
+      $record2priority[$record_array['record_id']] = $priority;
+      $default_data[$priority]['required'][$field_array['field']] = $field_array['required'];
+      
+      $one = $default_data[$priority];
+      if (isset($field_array['regex'])) {
+        $default_data[$priority]['regex_transform'][$field_array['field']] = $field_array['regex'];
+      }
+      
+      $two = $default_data[$priority];
+      
+      if (preg_match('/table field/', $field_array['type'])) {
+        $default_data[$priority]['values_array'][$field_array['field']] = '';
+        $default_data[$priority]['need_further_processing'] = TRUE;
+        $field2column[$priority][$field_array['field']] = $field_array['spreadsheet column'];
+        
+      } elseif (preg_match('/constant/', $field_array['type'])) {
+        $default_data[$priority]['values_array'][$field_array['field']] = $field_array['constant value'];
+        
+      } elseif (preg_match('/foreign key/', $field_array['type'])) {
+        $default_data[$priority]['values_array'][$field_array['field']] = array();
+        $default_data[$priority]['values_array'][$field_array['field']]['foreign record'] = $field_array['foreign key'];
+        $default_data[$priority]['need_further_processing'] = TRUE;
+        
+      } else {
+        print 'WARNING: Unsupported type: '. $field_array['type'] . ' for ' . $table . '.' . $field_array['field']."!\n";
+      }   
+      
+      $three = $default_data[$priority];
+      //watchdog('T_bulk_loader','A)'.$field_index.':<pre>Field Array =>'.print_r($field_array,TRUE)."Initial => \n".print_r($one, TRUE)."\nAfter Regex =>".print_r($two, TRUE)."Final =>\n".print_r($three,TRUE).'</pre>', array(), WATCHDOG_NOTICE);
+      
+    } // end of foreach field
+    //watchdog('T_bulk_loader','2)'.$record_array['record_id'].':<pre>'.print_r($default_data[$priority], TRUE).'</pre>', array(), WATCHDOG_NOTICE);
+  } //end of foreach record
+  
+  print "Memory Usage (end of prep work): ".number_format((memory_get_usage() * 0.000000953674316), 5, '.', ',') . " Mb\n";
+  
+  //print "Default Data:".print_r($default_data,TRUE)."\n";
+  //watchdog('T_bulk_loader','Default Data:<pre>'.print_r($default_data, TRUE).'</pre>', array(), WATCHDOG_NOTICE);
+  
+  //print "\nDefault Values Array: ".print_r($default_data, TRUE)."\n";
+  //print "\nField to Column Mapping: ".print_r($field2column, TRUE)."\n";
+  
+  // Parse File adding records as we go ========================================================
+  $file_handle = fopen($node->file, 'r');
+  if (preg_match('/(t|true|1)/', $node->file_has_header)) { fgets($file_handle, 4096); }
+  $num_records = 0;
+  $num_lines = 0;
+  $num_errors = 0;
+  while (!feof($file_handle)) {
+
+    // Clear variables
+    // Was added to fix memory leak
+    unset($line);                     unset($raw_line);
+    unset($data);                     unset($data_keys);
+    unset($priority);                 unset($sql);
+    unset($result);                   
+    
+    $raw_line = fgets($file_handle, 4096);
+    $raw_line = trim($raw_line);
+    if (empty($raw_line)) { continue; } // skips blank lines
+    $line = explode("\t", $raw_line);
+    $num_lines++;
+
+    $data = $default_data;
+
+    $data_keys = array_keys($data); 
+    foreach ($data_keys as $priority) {
+      $status = process_data_array_for_line($priority, $data, $default_data, $field2column, $record2priority, $line, $nid);
+      if (!$status ) { $loaded_without_errors = FALSE; }
+    } // end of foreach table in default data array
+
+    // determine memory increase
+    $line_mem_increase = memory_get_usage() - $memory;
+    if ($num_lines != 1) { 
+      $increased_mem = $increased_mem + $line_mem_increase;
+    }
+    $memory = memory_get_usage();
+    
+  } //end of foreach line of file
+  
+  // check that data was inserted and update job_status
+  $sql = 'SELECT count(*) as num_tables FROM {tripal_bulk_loader_inserted} WHERE nid=%d GROUP BY nid';
+  $result = db_fetch_object(db_query($sql, $nid));
+  if ($result->num_tables > 0) {
+    $node->job_status = 'Data Inserted';
+    drupal_write_record('node',$node,'nid');
+  }
+  
+  // set the status of the job (in the node not the tripal jobs)
+  if ($loaded_without_errors) { $status = 'Loading Completed Successfully'; } else { $status = 'Errors Encountered'; }
+  db_query("UPDATE tripal_bulk_loader SET job_status='%s' WHERE nid=%d",$status, $nid);
+  
+  $avg_line_increase = ( $increased_mem / $num_lines) * 0.0078125;
+  print "Average Increase in Memory per Line: ".number_format($avg_line_increase, 5, '.', ',') . " Kb\n";
+  print "Peak Memory Usage: ".number_format((memory_get_peak_usage() * 0.000000953674316), 5, '.', ',') . " Mb\n";
+  print "End Memory Usage: ".number_format((memory_get_usage() * 0.000000953674316), 5, '.', ',') . " Mb\n";
+
+}
+
+/**
+ * 
+ *
+ */
+function process_data_array_for_line ($priority, &$data, &$default_data, $field2column, $record2priority, $line, $nid) {
+  $table_data = $data[$priority];
+
+  $no_errors = TRUE;
+
+  $table = $table_data['table'];
+  $values = $table_data['values_array'];
+  
+  //watchdog('T_bulk_loader','Original:<pre>'.print_r($table_data, TRUE).'</pre>', array(), WATCHDOG_NOTICE);
+  
+  //print 'default values:'.print_r($values,TRUE)."\n";
+  if ($table_data['need_further_processing']) {
+    $values = tripal_bulk_loader_add_spreadsheetdata_to_values ($values, $line, $field2column[$priority]);
+    if (!$values) {
+      watchdog('T_bulk_loader','Spreadsheet Added:'.print_r($values, TRUE), array(), WATCHDOG_NOTICE);
+    }
+    
+    $values = tripal_bulk_loader_add_foreignkey_to_values($values, $data, $record2priority);
+    if (!$values) {
+      watchdog('T_bulk_loader','FK Added:<pre>'.print_r($values, TRUE).print_r($data[$priority],TRUE).'</pre>', array(), WATCHDOG_NOTICE);
+    }
+  }
+  $values = tripal_bulk_loader_regex_tranform_values($values, $table_data, $line);
+  if (!$values) {
+    watchdog('T_bulk_loader','Regex:<pre>'.print_r($values, TRUE).print_r($table_data, TRUE).'</pre>'.'</pre>', array(), WATCHDOG_NOTICE);
+  }
+
+  if (!$values) {
+    $msg = $table_data['record_id'].' ('.$table_data['mode'].') Aborted due to error in previous record. Values of current record:'.print_r($table_data['values_array'],TRUE);
+    watchdog('T_bulk_loader', $msg, array(), WATCHDOG_WARNING); 
+    print "ERROR: ".$msg."\n";
+    $data[$priority]['error'] = TRUE;
+    $no_errors = FALSE;
+  }
+  
+  $table_desc = module_invoke_all('chado_'.$table.'_schema');
+  if (preg_match('/optional/', $table_array['mode'])) {
+    // Check all db required fields are set
+    $fields = $table_desc['fields'];
+    foreach($fields as $field => $def){
+      // a field is considered missing if it cannot be null and there is no default
+      // value for it or it is of type 'serial'
+      if($def['not null'] == 1 and !array_key_exists($field,$insert_values) and !isset($def['default']) and strcmp($def['type'],serial)!=0){
+         $msg = $table_data['record_id'].' ('.$table_data['mode'].') Missing Database Required Value: '.$table.'.'.$field;
+         watchdog('T_bulk_loader', $msg, array(), WATCHDOG_NOTICE); 
+         $data[$priority]['error'] = TRUE;
+      }
+    }
+  } //end of if optional record
+  
+  // Check required fields are present
+  foreach ($table_data['required'] as $field => $required) {
+    if ($required) {
+      if (!isset($values[$field])) {
+        $msg = $table_data['record_id'].' ('.$table_data['mode'].') Missing Template Required Value: '.$table.'.'.$field;
+        watchdog('T_bulk_loader', $msg, array(), WATCHDOG_NOTICE); 
+        $data[$priority]['error'] = TRUE;  
+      }
+    }
+  }
+  
+  // add new values array into the data array
+  $data[$priority]['values_array'] = $values;
+  
+  // check if it is already inserted
+  if ($table_data['inserted']) {
+    //watchdog('T_bulk_loader','Already Inserted:'.print_r($values,TRUE),array(),WATCHDOG_NOTICE);
+    return $no_errors;
+  }
+  
+  // if there was an error already -> don't insert
+  if ($data[$priority]['error']) {
+    return $no_errors;
+  }
+  
+  $header = '';
+  if (isset($values['feature_id'])) {
+    $header = $values['feature_id']['uniquename'] .' '. $table_data['record_id'];
+  } else {
+    $header = $values['uniquename'] .' '. $table_data['record_id'];
+  }
+  
+  // if insert unique then check to ensure unique
+  if (preg_match('/insert_unique/',$table_data['mode'])) {
+    $unique = tripal_core_chado_select($table, array_keys($table_desc['fields']), $values, array('has_record'=>TRUE));
+    //print 'Unique?'.print_r(array('table' => $table, 'columns' => array_keys($table_desc['fields']), 'values' => $values),TRUE).' returns '.$unique."\n";
+    if ($unique > 0) {
+      //$default_data[$priority]['inserted'] = TRUE;
+      //watchdog('T_bulk_loader', $header.': Not unique ('.$unique.'):'.print_r($values,'values')."\n".print_r($data,TRUE),array(),WATCHDOG_NOTICE);;
+      return $no_errors;
+    }
+  }
+  
+    
+  if (!preg_match('/select/',$table_data['mode'])) {
+    //watchdog('T_bulk_loader',$header.': Inserting:'.print_r($values, TRUE), array(), WATCHDOG_NOTICE);
+    $record = tripal_core_chado_insert($table, $values);
+    if (!$record) {
+      $msg = $table_data['record_id'].' ('.$table_data['mode'].') Unable to insert record into '.$table.' where values:'.print_r($values,TRUE);
+      watchdog('T_bulk_loader', $msg, array(), WATCHDOG_ERROR); 
+      print "ERROR: ".$msg."\n";  
+      $data[$priority]['error'] = TRUE;
+      $no_errors = FALSE;
+    } else {
+      //add changes back to values array
+      $data[$priority]['values_array'] = $record;
+      $values = $record;
+      
+      // if mode=insert_once then ensure we only insert it once
+      if (preg_match('/insert_once/',$table_data['mode'])) {
+        $default_data[$priority]['inserted'] = TRUE;
+      }
+      
+      // add to tripal_bulk_loader_inserted
+      $insert_record = db_fetch_object(db_query(
+        "SELECT * FROM {tripal_bulk_loader_inserted} WHERE table_inserted_into='%s' AND nid=%d",
+        $table,
+        $nid
+      ));
+      if ($insert_record) {
+        $insert_record->ids_inserted .= ',' . $values[ $table_desc['primary key'][0] ];
+        drupal_write_record('tripal_bulk_loader_inserted', $insert_record, 'tripal_bulk_loader_inserted_id');
+        //print 'Update: '.print_r($insert_record,TRUE)."\n";
+        return $no_errors;
+      } else {
+        $insert_record = array(
+          'nid' => $nid,
+          'table_inserted_into' => $table,
+          'table_primary_key' => $table_desc['primary key'][0],
+          'ids_inserted' => $values[ $table_desc['primary key'][0] ],
+        );
+        //print 'New: '.print_r($insert_record,TRUE)."\n";
+        $success = drupal_write_record('tripal_bulk_loader_inserted', $insert_record);
+        return $no_errors;
+      }//end of if insert record
+      
+    } //end of if insert was successful
+  } else {
+    $exists = tripal_core_chado_select($table, array_keys($table_desc['fields']), $values, array('has_record'=>TRUE));
+    if (!$exists) {
+      // No record on select
+      $msg = $table_data['record_id'].' ('.$table_data['mode'].') No Matching record in '.$table.' where values:'.print_r($values,TRUE);
+      watchdog('T_bulk_loader', $msg, array(), WATCHDOG_WARNING); 
+      $data[$priority]['error'] = TRUE;  
+    }
+  }
+  return $no_errors;
+}
+
+/**
+ * This function adds the file data to the values array
+ *
+ * @param $values
+ *   The default values array -contains all constants
+ * @param $line
+ *   An array of values for the current line
+ * @param $field2column
+ *   An array mapping values fields to line columns
+ * @return 
+ *   Supplemented values array
+ */
+function tripal_bulk_loader_add_spreadsheetdata_to_values ($values, $line, $field2column) {
+
+  foreach ($values as $field => $value) {
+    if (is_array($value)) { continue; }
+    
+    $column = $field2column[$field] - 1;
+    if ($column < 0) { continue; }
+    
+    if (preg_match('/\S+/',$line[$column])) {
+      $values[$field] = $line[$column];
+    } else {
+      unset($values[$field]);
+    }
+  }
+  
+  return $values;
+}
+
+/**
+ * Handles foreign keys in the values array.
+ *
+ * Specifically, if the value for a field is an array then it is assumed that the array contains
+ * the name of the record whose values array should be substituted here. Thus the foreign
+ * record is looked up and the values array is substituted in.
+ *
+ */
+function tripal_bulk_loader_add_foreignkey_to_values($values, $data, $record2priority) {
+
+  foreach ($values as $field => $value) {
+    if (is_array($value)) {
+      $foreign_record = $value['foreign record'];
+      $foreign_priority = $record2priority[$foreign_record];
+      $foreign_values = $data[$foreign_priority]['values_array'];
+      
+      // add to current values array
+      $values[$field] = $foreign_values;
+    }
+  }
+
+  return $values;
+}
+
+/**
+ * Uses a supplied regex to transform spreadsheet values
+ *
+ * @param $values
+ *   The select/insert values array for the given table
+ * @param $table_data
+ *   The data array for the given table
+ */
+function tripal_bulk_loader_regex_tranform_values ($values, $table_data, $line) {
+
+  if (empty($table_data['regex_transform']) OR !is_array($table_data['regex_transform'])) { return $values; }
+  
+  //watchdog('T_bulk_loader','Regex Transformation:<pre>'.print_r($table_data['regex_transform'], TRUE).'</pre>', array(), WATCHDOG_NOTICE);
+  
+  foreach ($table_data['regex_transform'] as $field => $regex_array) {
+    if (!is_array($regex_array['replace'])) { continue; }
+    
+    //print 'Match:'.print_r($regex_array['pattern'],TRUE)."\n";
+    //print 'Replace:'.print_r($regex_array['replace'],TRUE)."\n";
+    //print 'Was:'.$values[$field]."\n";
+    
+    // Check for <#column:\d+#> notation
+    // if present replace with that column in the current line
+    foreach ($regex_array['replace'] as $key => $replace) {
+      if (preg_match_all('/<#column:(\d+)#>/', $replace, $matches)) {
+        foreach ($matches[1] as $k => $column_num) {
+          $replace = preg_replace('/'.$matches[0][$k].'/', $line[$column_num-1], $replace);
+        }
+        $regex_array['replace'][$key] = $replace;
+      }
+    }
+    
+    // do the full replacement
+    $old_value = $values[$field];
+    $new_value = preg_replace($regex_array['pattern'], $regex_array['replace'], $old_value);
+    $values[$field] = $new_value;
+    
+    if ($values[$field] === '') {
+      unset($values[$field]); 
+    }
+    //print 'Now:'.$values[$field]."\n";
+  }
+  
+  
+
+  return $values;
+}
+
+/**
+ * Flattens an array up to two levels
+ * Used for printing of arrays without taking up much space
+ */
+function tripal_bulk_loader_flatten_array ($values) {
+  $flattened_values = array();
+  
+  foreach ($values as $k => $v) {
+    if (is_array($v)) {
+      $vstr = array();
+      foreach ($v as $vk => $vv) {
+        if (strlen($vv) > 20) {
+          $vstr[] = $vk .'=>'. substr($vv, 0, 20) . '...';
+        } else {
+          $vstr[] = $vk .'=>'. $vv;
+        }
+      }
+      $v = '{'. implode(',',$vstr) .'}';
+    } elseif (strlen($v) > 20) {
+      $v = substr($v, 0, 20) . '...';
+    }
+    $flattened_values[] = $k .'=>'. $v;
+  }
+  
+  return implode(', ',$flattened_values);
+}

+ 409 - 0
modules/base/tripal_bulk_loader/tripal_bulk_loader.module

@@ -0,0 +1,409 @@
+<?php
+
+include('tripal_bulk_loader.loader.inc');
+
+/**
+ * Implements hook_init
+ * Used to add stylesheets and javascript files to the header
+ */
+function tripal_bulk_loader_init(){
+	// Add javascript and style sheet
+	drupal_add_css(drupal_get_path('theme', 'tripal').'/css/tripal_bulk_loader.css');
+	drupal_add_js(drupal_get_path('theme', 'tripal').'/js/tripal_bulk_loader.js');
+}
+
+/**
+ * Implements hook_menu
+ */
+function tripal_bulk_loader_menu() {
+  $items = array();
+   // Show all loaders
+   $items['tripal_bulk_loaders'] = array(
+     'title' => 'Tripal Bulk Loaders',
+     'description' => 'Tripal bulk loaders for loading tab-delimited file into chado database',
+     'page callback' => 'tripal_bulk_loader_list',
+     'access arguments' => array('access tripal_bulk_loader'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+   // Admin page to create the template
+   $items['admin/tripal/tripal_bulk_loader_template'] = array(
+      'title' => 'Bulk Loader Template',
+      'description' => 'Templates for loading tab-delimited data',
+    	'page callback' => 'tripal_bulk_loader_admin_template',
+      'access arguments' => array('administer site configuration'),
+      'type' => MENU_NORMAL_ITEM,   
+		  'file' => 'tripal_bulk_loader.admin.inc',
+	);
+	// Create/Edit Template -------
+  $items['admin/tripal/tripal_bulk_loader_template/create'] = array(
+      'title' => 'Create Bulk Loader Template',
+      'description' => 'Create loader template for loading tab-delimited data',
+      'page callback' => 'drupal_get_form',
+      'page arguments' => array('tripal_bulk_loader_modify_template_base_form', 'create'),
+      'access arguments' => array('administer site configuration'),
+      'type' => MENU_NORMAL_ITEM,   
+		'file' => 'tripal_bulk_loader.admin.inc',
+  );
+  $items['admin/tripal/tripal_bulk_loader_template/edit'] = array(
+      'title' => 'Edit Bulk Loader Template',
+      'description' => 'Edit loader template for loading tab-delimited data',
+      'page callback' => 'drupal_get_form',
+      'page arguments' => array('tripal_bulk_loader_modify_template_base_form', 'edit'),
+      'access arguments' => array('administer site configuration'),
+      'type' => MENU_NORMAL_ITEM,   
+		  'file' => 'tripal_bulk_loader.admin.inc',  
+  );
+	$items['admin/tripal/tripal_bulk_loader_template/edit_record'] = array(
+      'title' => 'Edit Template Record',
+      'description' => 'Edit a record in an existing tripal bulk loader template.',
+      'page callback' => 'drupal_get_form',
+      'page arguments' => array('tripal_bulk_loader_edit_template_record_form'),
+      'access arguments' => array('administer site configuration'),
+      'type' => MENU_CALLBACK,   
+		'file' => 'tripal_bulk_loader.admin.inc',
+	);
+	$items['admin/tripal/tripal_bulk_loader_template/add_field'] = array(
+      'title' => 'Add Template Field',
+      'description' => 'Add a template field to an existing tripal bulk loader template.',
+      'page callback' => 'drupal_get_form',
+      'page arguments' => array('tripal_bulk_loader_add_template_field_form'),
+      'access arguments' => array('administer site configuration'),
+      'type' => MENU_CALLBACK,   
+		'file' => 'tripal_bulk_loader.admin.inc',
+	);
+	$items['admin/tripal/tripal_bulk_loader_template/edit_field'] = array(
+      'title' => 'Edit Template Field',
+      'description' => 'Edit an existing field from a tripal bulk loader template.',
+      'page callback' => 'drupal_get_form',
+      'page arguments' => array('tripal_bulk_loader_edit_template_field_form'),
+      'access arguments' => array('administer site configuration'),
+      'type' => MENU_CALLBACK,   
+		'file' => 'tripal_bulk_loader.admin.inc',
+	);
+	// Delete Template -----
+	$items['admin/tripal/tripal_bulk_loader_template/delete'] = array(
+      'title' => 'Delete Bulk Loader Template',
+      'description' => 'Delete bulk loader template',
+      'page callback' => 'drupal_get_form',
+      'page arguments' => array('tripal_bulk_loader_delete_template_base_form'),
+      'access arguments' => array('administer site configuration'),
+      'type' => MENU_NORMAL_ITEM,   
+		'file' => 'tripal_bulk_loader.admin.inc',
+	);
+	// Import/Export ---------
+  $items['admin/tripal/tripal_bulk_loader_template/import'] = array(
+      'title' => 'Import Bulk Loader Template',
+      'description' => 'Import Loaders',
+      'page callback' => 'drupal_get_form',
+      'page arguments' => array('tripal_bulk_loader_import_export_template_form', 'import'),
+      'access arguments' => array('administer site configuration'),
+      'type' => MENU_NORMAL_ITEM,   
+		  'file' => 'tripal_bulk_loader.admin.inc',  
+  );	
+  $items['admin/tripal/tripal_bulk_loader_template/export'] = array(
+      'title' => 'Export Bulk Loader Template',
+      'description' => 'Export Loaders',
+      'page callback' => 'drupal_get_form',
+      'page arguments' => array('tripal_bulk_loader_import_export_template_form', 'export'),
+      'access arguments' => array('administer site configuration'),
+      'type' => MENU_NORMAL_ITEM,   
+		  'file' => 'tripal_bulk_loader.admin.inc',  
+  );	
+	// AHAH ---------
+	$items['admin/tripal/tripal_bulk_loader_template/add_field_ahah'] = array(
+      'page callback' => 'tripal_bulk_loader_add_field_ahah',
+      'access arguments' => array('administer site configuration'),
+      'type' => MENU_CALLBACK,   
+		  'file' => 'tripal_bulk_loader.admin.inc',	
+	);
+	$items['admin/tripal/tripal_bulk_loader_template/edit_field_ahah'] = array(
+      'page callback' => 'tripal_bulk_loader_edit_field_ahah',
+      'access arguments' => array('administer site configuration'),
+      'type' => MENU_CALLBACK,   
+		  'file' => 'tripal_bulk_loader.admin.inc',	
+	);
+	
+  return $items;
+}
+
+/**
+ * Implements hook_theme
+ */
+function tripal_bulk_loader_theme() {
+  return array(
+    'tripal_bulk_loader_template' => array(
+    	'arguments'=> array('template_id' => NULL),
+    	'template' => 'tripal_bulk_loader_template'
+    ),
+    'tripal_bulk_loader_modify_template_base_form' => array(
+    	'arguments' => array('form' => NULL),
+    	'template' => 'tripal_bulk_loader_modify_template_base_form',
+    ),
+    'tripal_bulk_loader_edit_template_field_form' => array(
+      'arguments' => array('form' => NULL),
+      'template' => 'tripal_bulk_loader_edit_template_field_form',
+    ),
+    'tripal_bulk_loader_add_template_field_form' => array(
+      'arguments' => array('form' => NULL),
+      'template' => 'tripal_bulk_loader_add_template_field_form',
+    ),
+  );
+}
+
+/**
+ *  Implements hook_access
+ */
+function tripal_bulk_loader_access($op, $node, $account){
+	if ($op == 'create') {
+		if(!user_access('create tripal_bulk_loader', $account)){
+         return FALSE;
+      }
+	}
+	if ($op == 'update') {
+		if (!user_access('edit tripal_bulk_loader', $account)) {
+			return FALSE;
+		}
+	}
+	if ($op == 'delete') {
+		if (!user_access('delete tripal_bulk_loader', $account)) {
+			return FALSE;
+		}
+	}
+	if ($op == 'view') {
+		if (!user_access('access tripal_bulk_loader', $account)) {
+			return FALSE;
+		}
+	}
+	return NULL;
+}
+
+/**
+ * Implements hook_perm
+ */
+function tripal_bulk_loader_perm(){
+	return array(
+      'access tripal_bulk_loader',
+      'create tripal_bulk_loader',
+      'delete tripal_bulk_loader',
+      'edit tripal_bulk_loader',
+	);
+}
+
+/**
+ * Creates a listing page for all bulk loading jobs
+ */
+function tripal_bulk_loader_list () {
+  $num_results_per_page = 50;
+  $output = '';
+  
+  $header = array('','Status','Loader','File');
+  $rows = array();
+  
+  $query = 'SELECT * FROM {tripal_bulk_loader} l '
+    .'LEFT JOIN {node} n ON n.nid = l.nid '
+    .'LEFT JOIN {tripal_bulk_loader_template} t ON t.template_id = cast(l.template_id as integer)';
+  $resource = pager_query($query, $num_results_per_page, 0, NULL);
+  while ($r = db_fetch_object($resource)) {
+    $row = array(
+      l($r->title, 'node/'.$r->nid),
+      $r->job_status,
+      $r->name,
+      $r->file
+    );
+    $rows[] = $row;
+  }
+  
+  $output .= theme('table', $header, $rows);
+  return $output;
+  
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Node Functions
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Implements hook_node_info
+ */
+function tripal_bulk_loader_node_info() {
+	$nodes = array();
+	$nodes['tripal_bulk_loader'] = array(
+      'name' => t('Bulk Loading Job'),
+      'module' => 'tripal_bulk_loader',
+      'description' => t('A bulk loader for inserting tab-delimited data into chado database'),
+      'has_title' => TRUE,
+      'has_body' => FALSE,
+      'locked' => TRUE
+	);
+	return $nodes;
+}
+
+/**
+ * Implements node_form
+ * Used to gather the extra details stored with a Bulk Loading Job Node
+ */
+function tripal_bulk_loader_form ($node){
+   $form = array();
+   $sql = "SELECT * FROM {tripal_bulk_loader_template}";
+   $results = db_query($sql);
+   $templates = array ();
+   while ($template = db_fetch_object ($results)) {
+   	$templates [$template->template_id] = $template->name;
+   }
+   
+   if (!$templates) {
+   	$form['label'] = array(
+   	'#type' => 'item',
+      '#description' => t("Loader template needs to be created before any bulk loader can be added. Go to 'Tripal Management > Bulk Loader Template' to create the template."),
+      '#weight'        => -10,
+		);
+		
+		return $form;
+   }
+   
+   $form['loader_name'] = array(
+   	'#type'          => 'textfield',
+      '#title'         => t('Loading Job Name'),
+      '#weight'        => -10,
+      '#required'      => TRUE,
+   	'#default_value' => $node->loader_name
+   );
+   
+   $form['template_id'] = array(
+      '#type' => 'select',
+      '#title' => t('Template'),
+   	'#description'   => t('Please specify a template for this loader'),
+    	'#options'       => $templates,
+   	'#weight'        => -9,
+      '#required'      => TRUE,
+   	'#default_value' => $node->template_id
+   );
+   
+   $form['file']= array(
+      '#type'          => 'textfield',
+      '#title'         => t('Data File'),
+      '#description'   => t('Please specify the data file to be loaded.'),
+      '#weight'        => -8,
+   	'#default_value' => $node->file
+   );
+   
+   $form['has_header'] = array(
+    '#type' => 'radios',
+    '#title' => 'File has a Header',
+    '#options' => array( 1 => 'Yes', 2 => 'No'),
+    '#weight' => -7,
+    '#default_value' => $node->file_has_header,
+   );
+
+   return $form;
+}
+
+/**
+ * Implements node_load
+ */
+function tripal_bulk_loader_load($node){
+	$sql = "SELECT * FROM {tripal_bulk_loader} WHERE nid = %d";
+	$node = db_fetch_object(db_query($sql, $node->nid));
+
+	$node->title = 'Bulk Loading Job: '.$node->loader_name;
+	
+	// Add the loader template
+	$sql = "SELECT * FROM {tripal_bulk_loader_template} WHERE template_id=%d";
+	$results = db_fetch_object(db_query($sql, $node->template_id));
+	$template = unserialize($results->template_array);
+	$node->template = $results;
+	$node->template->template_array = $template;
+
+  // Add inserted records
+  $sql = 'SELECT * FROM {tripal_bulk_loader_inserted} WHERE nid=%d';
+  $resource = db_query($sql,$node->nid);
+  while ($r = db_fetch_object($resource)) {
+    $r->num_inserted = sizeof(preg_split('/,/',$r->ids_inserted)); 
+    $node->inserted_records->{$r->table_inserted_into} = $r;
+  }
+  
+	return $node;
+}
+
+/**
+ * Implements node_insert
+ * Insert the data from the node form on Create content
+ */
+function tripal_bulk_loader_insert ($node) {
+  
+	$sql = "INSERT INTO {tripal_bulk_loader} (nid, loader_name, template_id, file, file_has_header, job_status) VALUES (%d, '%s', %d, '%s', %d, '%s')";
+	db_query($sql, $node->nid, $node->loader_name, $node->template_id, $node->file, $node->has_header, 'Initialized');
+	$node->title =$node->loader_name;
+	drupal_write_record('node',$node,'nid');
+	drupal_write_record('node_revision',$node,'nid');	
+	
+	drupal_set_message('After reviewing the details, please Submit this Job (by clicking the "Submit Job" button below). No data will be loaded until the submitted job is reached in the queue.');
+
+}
+
+/**
+ * Implements node_delete
+ * Deletes the data when the delete button on the node form is clicked
+ */
+function tripal_bulk_loader_delete ($node) {
+	$sql = "DELETE FROM {tripal_bulk_loader} WHERE nid = %d";
+	db_query($sql, $node->nid);
+}
+
+/**
+ * Implements node_update
+ * Updates the data submitted by the node form on edit
+ */
+function tripal_bulk_loader_update ($node) {
+	$sql = "UPDATE {tripal_bulk_loader} SET nid = %d, loader_name = '%s', template_id = %d, file = '%s', file_has_header = '%s' WHERE nid = %d";
+	db_query($sql, $node->nid, $node->loader_name, $node->template_id, $node->file, $node->has_header, $node->nid);
+	// Add a job if the user want to load the data
+	global $user;
+	if($node->job) {
+		$job_args[0] =$node->loader_name;
+		$job_args[1] = $node->template_id;
+		$job_args[2] = $node->file;
+		if (is_readable($node->file)) {
+			$fname = preg_replace("/.*\/(.*)/", "$1", $node->file);
+			tripal_add_job("Bulk Load: $fname",'tripal_bulk_loader', 'tripal_bulk_loader_load_data', $job_args, $user->uid);
+		} else {
+			drupal_set_message("Can not open $node->file. Job not scheduled.");
+		}
+	}
+}
+
+/**
+ * Preprocessor function for the tripal_bulk_loader template
+ */
+function tripal_bulk_loader_preprocess_tripal_bulk_loader_template (&$variables) {
+
+	$sql = "SELECT * FROM {tripal_bulk_loader_template} WHERE template_id=%d";
+	$template = db_fetch_object(db_query($sql, $variables['template_id']));
+	$template->template_array = unserialize($template->template_array);	
+	$variables['template'] = $template;
+	
+}
+
+/**
+ * Implements hook_job_describe_args()
+ * Specifically to make viewing past tripal jobs more readable for jobs registered by this module
+ *
+ * @params $callback
+ *   The callback passed into tripal_add_job()
+ * @param $args
+ *   The arguements passed into tripal_add_job()
+ * @return 
+ *   An array where keys are the human readable headers describing each arguement 
+ *   and the value is the aguement passed in after formatting
+ */
+function tripal_bulk_loader_job_describe_args($callback,$args){
+
+  $new_args = array();
+  if($callback == 'tripal_bulk_loader_load_data'){
+    //1st arg is the nid for a bulk loader node
+    $node = node_load($args[0]);
+    $new_args['Bulk Loading Job'] = l($node->title, 'node/'.$args[0]);
+    return $new_args;
+  }
+  
+}

+ 55 - 0
modules/base/tripal_contact/tripal_contact.api.inc

@@ -0,0 +1,55 @@
+<?php
+
+/****************************************************************************
+ * @section Chado Table Descriptions
+ ****************************************************************************/
+ 
+ /**
+ * Implements hook_chado_contact_schema()
+ * Purpose: To add descriptions and foreign keys to default table description
+ * Note: This array will be merged with the array from all other implementations
+ *
+ * @return
+ *    Array describing the contact table
+ */
+function tripal_stock_chado_contact_schema() {
+  $description = array();
+
+  $description['foreign keys']['cvterm'] = array(
+        'table' => 'cvterm',
+        'columns' => array(
+          'type_id' => 'cvterm_id',
+        ),
+  );
+
+  return $description;
+}
+
+ /**
+ * Implements hook_chado_contact_relationship_schema()
+ * Purpose: To add descriptions and foreign keys to default table description
+ * Note: This array will be merged with the array from all other implementations
+ *
+ * @return
+ *    Array describing the contact_relationship table
+ */
+function tripal_stock_chado_contact_relationship_schema() {
+  $description = array();
+
+  $description['foreign keys']['cvterm'] = array(
+        'table' => 'cvterm',
+        'columns' => array(
+          'type_id' => 'cvterm_id',
+        ),
+  );
+
+  $description['foreign keys']['contact'] = array(
+        'table' => 'contact',
+        'columns' => array(
+          'subject_id' => 'contact_id',
+          'object_id' => 'contact_id',
+        ),
+  );
+  
+  return $description;
+}

+ 9 - 0
modules/base/tripal_contact/tripal_contact.info

@@ -0,0 +1,9 @@
+; $Id$
+package = Tripal
+name = Tripal Contact
+version = 6.x-0.3.1b
+description = A module for interfacing the GMOD chado database with Drupal, providing viewing of Contacts
+core = 6.x
+
+dependencies[] = tripal_core
+dependencies[] = tripal_cv

+ 39 - 0
modules/base/tripal_contact/tripal_contact.module

@@ -0,0 +1,39 @@
+<?php
+
+/**
+ *  @file
+ * This file contains the basic functions needed for this drupal module.
+ * The drupal tripal_contact module maps directly to the chado X module. 
+ *
+ * For documentation regarding the Chado X module: 
+ * @see http://gmod.org/wiki/Chado_General_Module
+ */
+
+/**
+ * @defgroup tripal_contact Contact Module
+ * @{
+ * Currently this module only provides support for integration with Drupal 
+ * views and some support for querying using the Tripal Core API. 
+ *
+ * This module needs further development to support full management of 
+ * contact information within Chado, and full definitions for foreign 
+ * key relationships in Chado.
+ * @}
+ * @ingroup tripal_modules
+ */
+
+require('tripal_contact.api.inc');
+
+/*************************************************************************
+ * Implements hook_views_api()
+ * Purpose: Essentially this hook tells drupal that there is views support for
+ *  for this module which then includes tripal_contact.views.inc where all the
+ *  views integration code is
+ *
+ * @ingroup tripal_contact
+ */ 
+function tripal_contact_views_api() {
+   return array(
+      'api' => 2.0,
+   );
+}

+ 50 - 0
modules/base/tripal_contact/tripal_contact.views.inc

@@ -0,0 +1,50 @@
+<?php
+
+/**
+ *  @file
+ *  This file contains the basic functions for views integration of
+ *  chado/tripal_contact tables. Supplementary functions can be found in
+ *  ./views/
+ *
+ *  Documentation on views integration can be found at 
+ *  http://views2.logrus.com/doc/html/index.html.
+ */
+ 
+/*************************************************************************
+ * Implements hook_views_data()
+ * Purpose: Describe chado/tripal tables & fields to views
+ *
+ * @return: a data array which follows the structure outlined in the
+ *   views2 documentation for this hook. Essentially, it's an array of table
+ *   definitions keyed by chado/tripal table name. Each table definition 
+ *   includes basic details about the table, fields in that table and
+ *   relationships between that table and others (joins)
+ *
+ * @ingroup tripal_contact
+ */
+function tripal_contact_views_data()  {
+  $data = array();
+  
+  return $data;
+}
+
+/*************************************************************************
+ * Implements hook_views_handlers()
+ * Purpose: Register all custom handlers with views
+ *   where a handler describes either "the type of field", 
+ *   "how a field should be filtered", "how a field should be sorted"
+ *
+ * @return: An array of handler definitions
+ *
+ * @ingroup tripal_contact
+ */
+function tripal_contact_views_handlers() {
+ return array(
+   'info' => array(
+     'path' => drupal_get_path('module', 'tripal_contact') . '/views/handlers',
+   ),
+   'handlers' => array(
+
+   ),
+ );
+}

+ 45 - 0
modules/base/tripal_contact/views/README

@@ -0,0 +1,45 @@
+
+This directory contains all additional views code needed to integrate this module with views2.
+
+FILE/FOLDER DESCRIPTIONS
+---------------------------
+<chado table name>.views.inc:
+	contains a single function retrieve_<chado table name>_views_data()
+	which describes that table to views. This function is called by
+	tripal_contact_views_data() in ../tripal_contact.views.inc.
+	For more information on the form of this data array look up the
+	views2 documentation for hook_views_data() 
+	-http://views2.logrus.com/doc/html/index.html
+	
+handlers/
+	Each file contained within this folder defines a views handler. Only custom
+	handlers are included in this folder and each must be described in 
+	hook_views_handlers() in ../tripal_contact.views.inc.
+	A views handler does one of the following:
+		1) describe the type of a field and how it should be displayed
+		2) describe a method to sort this field
+		3) describe a method to filter this field
+
+
+STANDARDS TO FOLLOW
+---------------------------
+
+1. All table definition files should be named tablename.views.inc
+2. All handlers should be in a handlers sub-directory and follow the naming convention of
+   views handlers (ie: views_handler_field/filter/sort_handlername.inc )
+
+Views Table Definitions:
+- Please use the template files provided whenever you are describing a new table to views.
+   For any table in chado simply copy template.table_defn.views.inc to tablename.views.inc and 
+   follow the instructions listed at the top of the template file.
+- ONLY ONE TABLE DEFINITION PER FILE
+- To join a chado table to it's drupal node simply copy template.node_join.views.inc to 
+   basetablename.views.inc and replace all XXX with basetablename.
+   
+   NOTE: Creating the table definition file is not enough. You also need to call the 
+         retrieve_XXX_views_data() function from ../tripal_contact.views.inc:tripal_contact_views_data()
+         by adding the following line:
+            $data = array_merge($data, retrieve_XXX_views_data());
+         to the function and including the file directly above the function (blow the function 
+         header by adding:
+            require_once('views/XXX.views.inc');

+ 84 - 0
modules/base/tripal_contact/views/template.node_join.views.inc

@@ -0,0 +1,84 @@
+<?php
+
+/*************************************************************************
+ * @file: THIS IS A TEMPLATE AND SHOULD NOT BE INCLUDED IN THE MODULE CODE
+ *
+ *  - simply replace all XXX with the original chado table you want to join to it's drupal nodes.
+ *    (ie: If you want to join features to their drupal nodes then XXX=feature)
+ * 
+ *  NOTE: Creating the table definition file is not enough. You also need to call the 
+ *        retrieve_XXX_views_data() function from ../tripal_contact.views.inc:tripal_contact_views_data()
+ *        by adding the following line:
+ *           $data = array_merge($data, retrieve_XXX_views_data());
+ *        to the function and including the file directly above the function (blow the function 
+ *        header by adding:
+ *           require_once('views/XXX.views.inc');
+ *
+ *  REMOVE THIS COMMENT IN THE COPY!
+ */ 
+ 
+/**
+ *  @file
+ *  This file defines the data array for a given chado table. This array
+ *  is merged into a larger array containing definitions of all tables associated
+ *  with this module in:
+ *  @see tripal_contact.views.inc --in tripal_contact_views_data()
+ *
+ *  Note: All chado tables are joined to their drupal nodes through the chado_XXX linking table. 
+ *        This file simply defines this linking table and joins the three tables together.
+ *        No modification of XXX.views.inc is needed.
+ *
+ *  Documentation on views integration can be found at 
+ *  http://views2.logrus.com/doc/html/index.html.
+ */
+ 
+/**
+ * Purpose: this function returns the portion of the data array 
+ *   which describes the chado_XXX drupal table, it's fields and any joins between it 
+ *   and other tables
+ * @see tripal_contact_views_data() --in tripal_contact.views.inc
+ *
+ * The main need for description of this table to views is to join chado data with drupal nodes
+ *
+ */
+function retrieve_chado_XXX_views_data () {
+	global $db_url;
+  $data = array();
+  
+  // if the chado database is not local to the drupal database
+  // then we need to set the database name.  This should always
+  // be 'chado'.
+  if(is_array($db_url) and array_key_exists('chado',$db_url)){
+     // return empty data array b/c if chado is external then no join to the nodetable can be made
+     return $data;
+  }
+
+  //Basic table definition-----------------------------------
+  $data['chado_XXX']['table'] = array(
+    'field' => 'nid',
+  );
+  
+  //Relationship Definitions---------------------------------
+  // Note: No joins need to be made from $data['XXX']['table']
+  
+  // Join the chado_XXX table to XXX
+  $data['chado_XXX']['table']['join']['XXX'] = array(
+  	'left_field' => 'XXX_id',
+  	'field' => 'XXX_id',
+  );
+  
+  // Join the node table to chado_XXX
+  $data['node']['table']['join']['chado_XXX'] = array(
+  	'left_field' => 'nid',
+  	'field' => 'nid',
+  );
+  
+  // Join the node table to XXX
+  $data['node']['table']['join']['XXX'] = array(
+  	'left_table' => 'chado_XXX',
+  	'left_field' => 'nid',
+  	'field' => 'nid',
+  );  
+
+	return $data;
+}

+ 211 - 0
modules/base/tripal_contact/views/template.table_defn.views.inc

@@ -0,0 +1,211 @@
+<?php
+
+/*************************************************************************
+ * @file: THIS IS A TEMPLATE AND SHOULD NOT BE INCLUDED IN THE MODULE CODE
+ *
+ *   - Every instance of XXX should be replaced with the name of your table
+ *   - If this is a base table (you want a view where every row is a row from this table)
+ *     then change $data['XXX']['table'] to $data['XXX']['table']['base'] 
+ *     and $data['XXX']['table']['database'] to $data['XXX']['table']['base']['database']
+ *   - Relationships between this table and others: YYY is the table you are trying to join to this
+ *     one. You want to join a table to this one if this table contains a foreign key to the other
+ *     table. If the join between this table and another is through a linking table
+ *     (ie: library-XXX/YYY => library_feature-XY => feature-XXX/YYY) then make the join in both
+ *     directions (ie: in the file XXX.views.inc and the file YYY.views.inc
+ *   - Create a field definition for each field in this table using the example fields already
+ *     listed. Match the type of the database field to the field definition listed below.
+ *     (ie: for a text/varchar field from the database use plain_text_field below)
+ * 
+ *  NOTE: Creating the table definition file is not enough. You also need to call the 
+ *        retrieve_XXX_views_data() function from ../tripal_contact.views.inc:tripal_contact_views_data()
+ *        by adding the following line:
+ *           $data = array_merge($data, retrieve_XXX_views_data());
+ *        to the function and including the file directly above the function (blow the function 
+ *        header by adding:
+ *           require_once('views/XXX.views.inc');
+ *
+ *  REMOVE THIS COMMENT IN THE COPY!
+ */ 
+ 
+/**
+ *  @file
+ *  This file defines the data array for a given chado table. This array
+ *  is merged into a larger array containing definitions of all tables associated
+ *  with this module in:
+ *  @see tripal_contact.views.inc --in tripal_contact_views_data()
+ *
+ *  Documentation on views integration can be found at 
+ *  http://views2.logrus.com/doc/html/index.html.
+ */
+
+/*************************************************************************
+ * Purpose: this function returns the portion of the data array 
+ *   which describes the XXX table, it's fields and any joins between it and other tables
+ * @see tripal_contact_views_data() --in tripal_contact.views.inc
+ *
+ * Table: XXX
+ * @code
+ * XXX-Copy/Paste Table SQL code here-XXX
+ * @endcode
+ */
+ function retrieve_XXX_views_data() {
+  global $db_url;
+  $data = array();
+
+  // if the chado database is not local to the drupal database
+  // then we need to set the database name.  This should always
+  // be 'chado'.
+  if(is_array($db_url) and array_key_exists('chado',$db_url)){
+    $database = 'chado';
+  }
+   
+  //Basic table definition-----------------------------------
+  $data['XXX']['table']['group'] = t('Chado XXX');
+  
+  $data['XXX']['table'] = array(
+    'field' => 'primary_id',
+    'title' => t('Chado XXX'),
+    'help' => t('Enter some user-friendly description of this tables purpose to the user.'),
+  );
+  if($database){
+     $data['XXX']['table']['database'] = $database;
+  }
+
+  
+  //Relationship Definitions---------------------------------
+  //Join: YYY => XXX
+  // Notice that this relationship tells the primary table to show it's fields to the
+  // table referencing it by a foreign key and thus the relationship is from
+  // primary table to table referenceing it (ie: cvterm => feature)
+  $data['XXX']['table']['join']['YYY'] = array(
+    'left_field' => 'foreign key in YYY table',
+    'field' => 'primary key in XXX table',
+  );  
+  
+  //Join: XXX => XY => YYY
+  // This relationship should be described in both directions
+  // in the appropriate files (ie: for feature => library 
+  // describe in both feature.views.inc and library.views.inc)
+  $data['XXX']['table']['join']['XY'] = array(
+    'left_field' => 'matching XXX key in the XY table',
+    'field' => 'primary key in XXX table',
+  );  
+  $data['XXX']['table']['join']['YYY'] = array(
+    'left_table' => 'XY',
+    'left_field' => 'matching XXX key in the XY table',
+    'field' => 'primary key in XXX table',
+  );
+  $data['XY']['table']['join']['YYY'] = array(
+    'left_field' => 'primary key in YYY table',
+    'field' => 'matching YYY key in the XY table',
+  );
+   
+  //Table Field Definitions----------------------------------
+      
+  //Field: XXX_id (primary key)
+  $data['XXX']['field_name'] = array(
+    'title' => t('XXX Primary Key'),
+    'help' => t('A unique index for every XXX.'),
+    'field' => array(
+      'handler' => 'views_handler_field_numeric',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_numeric',
+    ),
+  );
+
+  /*.......................................................
+   * Beginning of Example Field definitions
+   * Remove this section when done
+   */
+
+  //Field: plain_text_field (chado datatype)   
+  $data['XXX']['plain_text_field'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+
+  //Field: numeric_field (chado datatype)   
+  $data['XXX']['numeric_field'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_numeric',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_numeric',
+    ),
+  );
+
+  //Field: boolean_field (chado datatype)   
+  $data['XXX']['boolean_field'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_boolean',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_boolean_operator',
+    ),
+  );
+
+  //Field: unix_timestamp (chado datatype)   
+  $data['XXX']['unix_timestamp'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_date',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort_date',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_date',
+    ),
+  );
+
+  //Field: human_readable_date (chado datatype)   
+  $data['XXX']['human_readable_date'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_readble_date',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort_date',
+    ),
+  );
+   
+   /*
+    * End of Example Field definitions
+    */
+    
+  return $data;
+}

+ 278 - 0
modules/base/tripal_core/chado_install.php

@@ -0,0 +1,278 @@
+<?php
+
+/**
+*
+*
+* @ingroup tripal_core
+*/
+function tripal_core_chado_v1_11_load_form (){
+   $form['description'] = array(
+       '#type' => 'item',
+       '#value' => t("Click the submit button below to install Chado into the Drupal database. <br><font color=\"red\">WARNING:</font> use this only for a new chado installation or reinstall completely.  This will erase any data currently in the chado database.  If you are 
+using chado in a database external to the Drupal database with a 'chado' entry in the 'settings.php' \$db_url argument then this option will intall chado but it will not be usable.  The external database specified in the settings.php file takes precedence."),
+       '#weight' => 1,
+   );
+   $form['button'] = array(
+      '#type' => 'submit',
+      '#value' => t('Install Chado'),
+      '#weight' => 2,
+   );
+   return $form;
+}
+
+/**
+*
+* @ingroup tripal_core
+*/
+function tripal_core_chado_v1_11_load_form_submit ($form, &$form_state){
+   global $user;
+
+   $args = array();
+   tripal_add_job("Install Chado",'tripal_core',
+      'tripal_core_install_chado',$args,$user->uid);
+
+   return '';
+}
+/**
+*
+*
+* @ingroup tripal_core
+*/
+function tripal_core_install_chado ($dummy = NULL, $job = NULL){
+   $schema_file = drupal_get_path('module', 'tripal_core').'/default_schema.sql';
+   $init_file = drupal_get_path('module', 'tripal_core').'/initialize.sql';
+   if(tripal_core_reset_chado_schema()){
+      tripal_core_install_sql($schema_file);
+      tripal_core_install_sql($init_file);      
+   } else {
+      print "ERROR: cannot install chado.  Please check database permissions\n";
+      exit;
+   }
+}
+/**
+*
+*
+* @ingroup tripal_core
+*/
+function tripal_core_reset_chado_schema (){
+   global $active_db;
+
+   // iterate through the lines of the schema file and rebuild the SQL
+   if(tripal_core_schema_exists('chado')){
+      print "Dropping existing 'chado' schema\n";
+      pg_query($active_db,"drop schema chado cascade");
+   }
+   if(tripal_core_schema_exists('genetic_code')){
+      print "Dropping existing 'genetic_code' schema\n";
+      pg_query($active_db,"drop schema genetic_code cascade");
+   }
+   if(tripal_core_schema_exists('so')){
+      print "Dropping existing 'so' schema\n";
+      pg_query($active_db,"drop schema so cascade");
+   }
+   if(tripal_core_schema_exists('frange')){
+      print "Dropping existing 'frange' schema\n";
+      pg_query($active_db,"drop schema frange cascade");
+   }
+   // create the new chado schema
+   print "Creating 'chado' schema\n";
+   pg_query($active_db,"create schema chado");
+   if(tripal_core_schema_exists('chado')){
+      pg_query($active_db,"create language plpgsql");
+      return 1;
+   }
+   return 0;
+}
+/**
+*
+*
+* @ingroup tripal_core
+*/
+
+function tripal_core_schema_exists($schema){
+   // check that the chado schema now exists
+   $sql = "SELECT nspname
+           FROM pg_namespace
+           WHERE has_schema_privilege(nspname, 'USAGE') and nspname = '%s'
+           ORDER BY nspname";
+   $name = db_fetch_object(db_query($sql,$schema));
+   if(strcmp($name->nspname,$schema)!=0){
+      return 0;
+   }
+   return 1;
+}
+/**
+*
+*
+* @ingroup tripal_core
+*/
+function tripal_core_install_sql ($sql_file){
+   global $active_db;
+
+   pg_query($active_db,"set search_path to chado,public");
+
+   print "Loading $sql_file...\n";
+   $lines = file($sql_file,FILE_SKIP_EMPTY_LINES);
+   if(!$lines){
+      return 'Cannot open $schema_file';
+   }
+
+   $stack = array();
+   $in_string = 0;
+
+   $query = '';
+   $i = 0;
+   foreach ($lines as $line_num => $line) {
+      $i++;
+      $type = '';
+
+      // find and remove comments except when inside of strings
+      if(preg_match('/--/',$line) and !$in_string and !preg_match("/'.*?--.*?'/",$line)){
+         $line = preg_replace('/--.*$/','',$line);  // remove comments
+      }
+      if(preg_match('/\/\*.*?\*\//',$line)){
+         $line = preg_replace('/\/\*.*?\*\//','',$line);  // remove comments
+      }
+      // skip empty lines
+      if(preg_match('/^\s*$/',$line) or strcmp($line,'')==0){
+         continue;
+      }
+
+      // Find SQL for new objects
+      if(preg_match('/^\s*CREATE\s+TABLE/i',$line) and !$in_string){
+         $stack[] = 'table';
+         $line = preg_replace("/public./","chado.",$line);
+      }
+      if(preg_match('/^\s*ALTER\s+TABLE/i',$line) and !$in_string){
+         $stack[] = 'alter table';
+         $line = preg_replace("/public./","chado.",$line);
+      }
+      if(preg_match('/^\s*SET/i',$line) and !$in_string){
+         $stack[] = 'set';
+      }
+      if(preg_match('/^\s*CREATE\s+SCHEMA/i',$line) and !$in_string){
+         $stack[] = 'schema';
+      }
+      if(preg_match('/^\s*CREATE\s+SEQUENCE/i',$line) and !$in_string){
+         $stack[] = 'sequence';
+         $line = preg_replace("/public./","chado.",$line);
+      }
+      if(preg_match('/^\s*CREATE\s+(?:OR\s+REPLACE\s+)*VIEW/i',$line) and !$in_string){
+         $stack[] = 'view';
+         $line = preg_replace("/public./","chado.",$line);
+      }
+      if(preg_match('/^\s*COMMENT/i',$line) and !$in_string and sizeof($stack)==0){
+         $stack[] = 'comment';  
+         $line = preg_replace("/public./","chado.",$line);
+      }
+      if(preg_match('/^\s*CREATE\s+(?:OR\s+REPLACE\s+)*FUNCTION/i',$line) and !$in_string){
+         $stack[] = 'function';
+         $line = preg_replace("/public./","chado.",$line);
+      }
+      if(preg_match('/^\s*CREATE\s+INDEX/i',$line) and !$in_string){
+         $stack[] = 'index';
+      }
+      if(preg_match('/^\s*INSERT\s+INTO/i',$line) and !$in_string){
+         $stack[] = 'insert';
+         $line = preg_replace("/public./","chado.",$line);
+      }
+      if(preg_match('/^\s*CREATE\s+TYPE/i',$line) and !$in_string){
+         $stack[] = 'type';
+      }
+      if(preg_match('/^\s*GRANT/i',$line) and !$in_string){
+         $stack[] = 'grant';
+      }
+      if(preg_match('/^\s*CREATE\s+AGGREGATE/i',$line) and !$in_string){
+         $stack[] = 'aggregate';
+      }
+
+      // determine if we are in a string that spans a line
+      $matches = preg_match_all("/[']/i",$line,$temp);
+      $in_string = $in_string - ($matches % 2);
+      $in_string = abs($in_string);
+
+      // if we've reached the end of an object the pop the stack 
+      if(strcmp($stack[sizeof($stack)-1],'table') == 0 and preg_match('/\);\s*$/',$line)){
+         $type = array_pop($stack);
+      }
+      if(strcmp($stack[sizeof($stack)-1],'alter table') == 0 and preg_match('/;\s*$/',$line) and !$in_string){
+         $type = array_pop($stack);
+      }
+      if(strcmp($stack[sizeof($stack)-1],'set') == 0 and preg_match('/;\s*$/',$line) and !$in_string){
+         $type = array_pop($stack);
+      }
+      if(strcmp($stack[sizeof($stack)-1],'schema') == 0 and preg_match('/;\s*$/',$line) and !$in_string){
+         $type = array_pop($stack);
+      }
+      if(strcmp($stack[sizeof($stack)-1],'sequence') == 0 and preg_match('/;\s*$/',$line) and !$in_string){
+         $type = array_pop($stack);
+      }
+      if(strcmp($stack[sizeof($stack)-1],'view') == 0 and preg_match('/;\s*$/',$line) and !$in_string){
+         $type = array_pop($stack);
+      }
+      if(strcmp($stack[sizeof($stack)-1],'comment') == 0 and preg_match('/;\s*$/',$line) and !$in_string){
+         $type = array_pop($stack);
+      }
+      if(strcmp($stack[sizeof($stack)-1],'function') == 0 and preg_match("/LANGUAGE.*?;\s+$/i",$line)){
+         $type = array_pop($stack);
+      }
+      if(strcmp($stack[sizeof($stack)-1],'index') == 0 and preg_match('/;\s*$/',$line) and !$in_string){
+         $type = array_pop($stack);
+      }
+      if(strcmp($stack[sizeof($stack)-1],'insert') == 0 and preg_match('/\);\s*$/',$line)){
+         $type = array_pop($stack);
+      }
+      if(strcmp($stack[sizeof($stack)-1],'type') == 0 and preg_match('/\);\s*$/',$line)){
+         $type = array_pop($stack);
+      }
+      if(strcmp($stack[sizeof($stack)-1],'grant') == 0 and preg_match('/;\s*$/',$line) and !$in_string){
+         $type = array_pop($stack);
+      }
+      if(strcmp($stack[sizeof($stack)-1],'aggregate') == 0 and preg_match('/\);\s*$/',$line)){
+         $type = array_pop($stack);
+      }
+
+
+      // if we're in a recognized SQL statement then let's keep track of lines
+      if($type or sizeof($stack) > 0){
+         $query .= "$line";
+      } else {
+         print "UNHANDLED $i, $in_string: $line";
+         return tripal_core_chado_install_done();
+      }
+
+      if(preg_match_all("/\n/",$query,$temp) > 100){
+         print "SQL query is too long.  Terminating:\n$query\n";
+         return tripal_core_chado_install_done();
+      }
+
+      if($type and sizeof($stack) == 0){
+         print "Adding $type: line $i\n";
+         // rewrite the set serach_path to make 'public' be 'chado'
+         if(strcmp($type,'set')==0){
+            $query = preg_replace("/public/m","chado",$query);
+         }
+         $result = pg_query($active_db, $query);
+         if(!$result){
+            $error  = pg_last_error();
+            print "Installation failed:\nSQL $i, $in_string: $query\n$error\n";
+            pg_query($active_db,"set search_path to public,chado");  
+            return tripal_core_chado_install_done();
+         }
+         $query = '';
+      }    
+   }
+
+   print "Installation Complete!\n";
+   tripal_core_chado_install_done(); 
+}
+/**
+*
+*
+* @ingroup tripal_core
+*/
+function tripal_core_chado_install_done (){
+   // return the search path to normal
+   global $active_db;
+   pg_query($active_db,"set search_path to public,chado");  
+}

+ 121 - 0
modules/base/tripal_core/cvterms.php

@@ -0,0 +1,121 @@
+<?php
+
+/** 
+ * @defgroup tripal_core_cv_api Core Module CV API
+ * @{
+ * For working with controlled vocabularies see the Controlled Vocabulary
+ * API.  However, there are cases where a CV does not exist. For example in
+ * some cases a module may want to support adding of properties (such as to
+ * for an analysis) to a record but an organized CV does not exist.  The 
+ * progammer may create their own ontology and populate it, or they may use
+ * the basic CV interface provided by Tripal.  Tripal provides a CV named
+ * 'Tripal' and modules may add terms to this CV to support the functionality
+ * of their modules.  
+ * @}
+ * @ingroup tripal_api
+ */
+
+/**
+ * Add a materialized view to the chado database to help speed data access.
+ *
+ * @param $name 
+ *   The name of the term
+ * @param $definition 
+ *   The definition for the term
+ * @param $cv_name 
+ *   The name of the controlled vocabulary to which the term will be added.
+ *   The default CV is 'tripal'.
+ * @param $db_name 
+ *   CV records also have a corresponding database reference.  This argument
+ *   specifies the name of the database to which this term belongs. The default
+ *   database name is 'tripal'.
+ *
+ * @ingroup tripal_core_cv_api
+ */
+function tripal_add_cvterms ($name,$definition,$cv_name = 'tripal',$db_name='tripal'){
+   
+   
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $cv = db_fetch_object(db_query("SELECT * FROM {cv} WHERE name = '$cv_name'"));
+   if (!$cv->cv_id) {
+      tripal_db_set_active($previous_db);
+      tripal_add_cv('tripal', 'Terms used by Tripal for modules to manage data such as that 
+                               stored in property tables like featureprop, analysisprop, etc');
+      tripal_db_set_active('chado');
+      $cv = db_fetch_object(db_query("SELECT * FROM {cv} WHERE name = '$cv_name'"));
+   }
+   $db = db_fetch_object(db_query("SELECT * FROM {db} WHERE name = '$db_name'"));
+	if (!$db->db_id) {
+	   tripal_db_set_active($previous_db);
+	   tripal_add_db('tripal', 'Used as a database placeholder for tripal defined objects such as tripal cvterms', '', '');
+	   tripal_db_set_active('chado');
+	   $db = db_fetch_object(db_query("SELECT * FROM {db} WHERE name = '$db_name'"));
+	}
+
+	// check to see if the dbxref already exists if not then add it
+	$sql = "SELECT * FROM {dbxref} WHERE db_id = $db->db_id and accession = '$name'";
+	$dbxref = db_fetch_object(db_query($sql));
+	if(!$dbxref){
+	   db_query("INSERT INTO {dbxref} (db_id,accession) VALUES ($db->db_id,'$name')");
+		$dbxref = db_fetch_object(db_query($sql));
+	}
+
+   
+   // now add the cvterm only if it doesn't already exist
+	$sql = "SELECT * FROM {cvterm} ".
+	       "WHERE cv_id = $cv->cv_id and dbxref_id = $dbxref->dbxref_id and name = '$name'";
+	$cvterm = db_fetch_object(db_query($sql));
+	if(!$cvterm){
+      $result = db_query("INSERT INTO {cvterm} (cv_id,name,definition,dbxref_id) ".
+                         "VALUES ($cv->cv_id,'$name','$definition',$dbxref->dbxref_id)");
+	}
+   tripal_db_set_active($previous_db);  // now use drupal database	
+	
+   if(!$result){
+     // TODO -- ERROR HANDLING
+   }
+}
+/**
+ * Add a database to the Chado 'db' table.
+ * NOTE: This function is deprecated and may not be used in future releases of Tripal
+ *  
+ * @ingroup tripal_core_cv_api
+ */
+function tripal_add_db($db_name,$description,$urlprefix,$url){
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+
+   // use this SQL statement to get the db_id for the database name
+   $id_sql = "SELECT db_id FROM {db} WHERE name = '%s'";
+
+   $db = db_fetch_object(db_query($id_sql,$db_name));
+
+   // if the database doesn't exist then let's add it.
+   if(!$db->db_id){
+      $sql = "
+         INSERT INTO {db} (name,description,urlprefix,url) VALUES 
+         ('%s','%s','%s','%s');
+      ";
+      db_query($sql,$db_name,$description,$urlprefix,$url);
+    
+      # now get the id for this new db entry
+      $db = db_fetch_object(db_query($id_sql,$db_name));
+   }
+   tripal_db_set_active($previous_db);  // now use drupal database	
+   return $db->db_id;
+}
+
+
+/**
+ * Get cvterm_id for a tripal term by passing its name
+ * NOTE: This function is deprecated and may not be used in future releases of Tripal
+ *  
+ * @ingroup tripal_core_cv_api
+ */
+function tripal_get_cvterm_id ($cvterm){
+	$sql = "SELECT CVT.cvterm_id FROM {cvterm} CVT
+         	    INNER JOIN cv ON cv.cv_id = CVT.cv_id 
+            	 WHERE CVT.name = '$cvterm' 
+                AND CV.name = 'tripal'";
+	return db_result(chado_query($sql));
+}
+

Diferenças do arquivo suprimidas por serem muito extensas
+ 2423 - 0
modules/base/tripal_core/default_schema.sql


+ 232 - 0
modules/base/tripal_core/initialize.sql

@@ -0,0 +1,232 @@
+/* For load_gff3.pl */
+insert into organism (abbreviation, genus, species, common_name)
+       values ('H.sapiens', 'Homo','sapiens','human');
+insert into organism (abbreviation, genus, species, common_name)
+       values ('D.melanogaster', 'Drosophila','melanogaster','fruitfly');
+insert into organism (abbreviation, genus, species, common_name)
+       values ('M.musculus', 'Mus','musculus','mouse');
+insert into organism (abbreviation, genus, species, common_name)
+       values ('A.gambiae', 'Anopheles','gambiae','mosquito');
+insert into organism (abbreviation, genus, species, common_name)
+       values ('R.norvegicus', 'Rattus','norvegicus','rat');
+insert into organism (abbreviation, genus, species, common_name)
+       values ('A.thaliana', 'Arabidopsis','thaliana','mouse-ear cress');
+insert into organism (abbreviation, genus, species, common_name)
+       values ('C.elegans', 'Caenorhabditis','elegans','worm');
+insert into organism (abbreviation, genus, species, common_name)
+       values ('D.rerio', 'Danio','rerio','zebrafish');
+insert into organism (abbreviation, genus, species, common_name)
+       values ('O.sativa', 'Oryza','sativa','rice');
+insert into organism (abbreviation, genus, species, common_name)
+       values ('S.cerevisiae', 'Saccharomyces','cerevisiae','yeast');
+insert into organism (abbreviation, genus, species, common_name)
+       values ('X.laevis', 'Xenopus','laevis','frog');
+insert into organism (abbreviation, genus, species,common_name) 
+       values ('D.discoideum','Dictyostelium','discoideum','dicty');
+insert into contact (name) values ('Affymetrix');
+insert into contact (name,description) values ('null','null');
+insert into cv (name) values ('null');
+insert into cv (name,definition) values ('local','Locally created terms');
+insert into cv (name,definition) values ('Statistical Terms','Locally created terms for statistics');
+insert into db (name, description) values ('null','a fake database for local items');
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'local:null');
+insert into cvterm (name,cv_id,dbxref_id) values ('null',(select cv_id from cv where name = 'null'),(select dbxref_id from dbxref where accession='local:null'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'local:computer file');
+insert into cvterm (name,cv_id,dbxref_id) values ('computer file', (select cv_id from cv where name = 'null'),(select dbxref_id from dbxref where accession='local:computer file'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'local:glass');
+insert into cvterm (name,definition,cv_id,dbxref_id) values ('glass','glass array',(select cv_id from cv where name = 'local'),(select dbxref_id from dbxref where accession='local:glass'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'local:photochemical_oligo');
+insert into cvterm (name,definition,cv_id,dbxref_id) values ('photochemical_oligo','in-situ photochemically synthesized oligoes',(select cv_id from cv where name = 'local'),(select dbxref_id from dbxref where accession='local:photochemical_oligo'));
+
+insert into pub (miniref,uniquename,type_id) values ('null','null',(select cvterm_id from cvterm where name = 'null'));
+insert into db (name, description) values ('GFF_source', 'A collection of sources (ie, column 2) from GFF files');
+
+insert into db (name) values ('ATCC');
+
+insert into db (name) values ('DB:refseq');
+insert into db (name) values ('DB:genbank');
+insert into db (name) values ('DB:EMBL');
+insert into db (name) values ('DB:TIGR');
+insert into db (name) values ('DB:ucsc');
+insert into db (name) values ('DB:ucla');
+insert into db (name) values ('DB:SGD');
+
+insert into db (name) values ('DB:PFAM');
+insert into db (name) values ('DB:SUPERFAMILY');
+insert into db (name) values ('DB:PROFILE');
+insert into db (name) values ('DB:PRODOM');
+insert into db (name) values ('DB:PRINTS');
+insert into db (name) values ('DB:SMART');
+insert into db (name) values ('DB:TIGRFAMs');
+insert into db (name) values ('DB:PIR');
+
+insert into db (name) values ('DB:Affymetrix_U133');
+insert into db (name) values ('DB:Affymetrix_U133PLUS');
+insert into db (name) values ('DB:Affymetrix_U95');
+insert into db (name) values ('DB:LocusLink');
+insert into db (name) values ('DB:RefSeq_protein');
+insert into db (name) values ('DB:GenBank_protein');
+insert into db (name) values ('DB:OMIM');
+insert into db (name) values ('DB:Swiss');
+insert into db (name) values ('DB:RefSNP');
+insert into db (name) values ('DB:TSC');
+--insert into db (name, contact_id, description, urlprefix) values ('DB:affy:U133',(select contact_id from contact where name = 'null'),'Affymetrix U133','http://https://www.affymetrix.com/analysis/netaffx/fullrecord.affx?pk=HG-U133_PLUS_2:');
+--insert into db (name, contact_id, description, urlprefix) values ('DB:affy:U95',(select contact_id from contact where name = 'null'),'Affymetrix U95','http://https://www.affymetrix.com/analysis/netaffx/fullrecord.affx?pk=HG-U95AV2:');
+
+insert into db (name, description) values ('DB:GR','Gramene');
+insert into db (name, description, urlprefix) values ('DB:uniprot','UniProt/TrEMBL','http://us.expasy.org/cgi-bin/niceprot.pl?');
+insert into db (name, description, urlprefix) values ('DB:refseq:mrna','RefSeq mRNA','http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=search&db=nucleotide&dopt=GenBank&term=');
+insert into db (name, description, urlprefix) values ('DB:refseq:protein','RefSeq Protein','http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=search&db=protein&dopt=GenBank&term=');
+insert into db (name, description, urlprefix) values ('DB:unigene','Unigene','http://www.ncbi.nih.gov/entrez/query.fcgi?db=unigene&cmd=search&term=');
+insert into db (name, description, urlprefix) values ('DB:omim','OMIM','http://www.ncbi.nlm.nih.gov/entrez/dispomim.cgi?id=');
+insert into db (name, description, urlprefix) values ('DB:locuslink','LocusLink','http://www.ncbi.nlm.nih.gov/LocusLink/LocRpt.cgi?l=');
+insert into db (name, description, urlprefix) values ('DB:genbank:mrna','GenBank mRNA','http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=search&db=nucleotide&dopt=GenBank&term=');
+insert into db (name, description, urlprefix) values ('DB:genbank:protein','GenBank Protein','http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=search&db=protein&dopt=GenBank&term=');
+insert into db (name, description, urlprefix) values ('DB:swissprot:display','SwissProt','http://us.expasy.org/cgi-bin/niceprot.pl?');
+insert into db (name, description, urlprefix) values ('DB:pfam','Pfam','http://www.sanger.ac.uk/cgi-bin/Pfam/dql.pl?query=');
+
+insert into analysis (name,program,programversion) values ('dabg' ,'dabg' ,'dabg' );
+insert into analysis (name,program,programversion) values ('dchip','dchip','dchip');
+insert into analysis (name,program,programversion) values ('gcrma','gcrma','gcrma');
+insert into analysis (name,program,programversion) values ('mas5' ,'mas5' ,'mas5' );
+insert into analysis (name,program,programversion) values ('mpam' ,'mpam' ,'mpam' );
+insert into analysis (name,program,programversion) values ('plier','plier','plier');
+insert into analysis (name,program,programversion) values ('rma'  ,'rma'  ,'rma'  );
+insert into analysis (name,program,programversion) values ('sea'  ,'sea'  ,'sea'  );
+insert into analysis (name,program,programversion) values ('vsn'  ,'vsn'  ,'vsn'  );
+
+insert into arraydesign (name,manufacturer_id,platformtype_id) values ('unknown'                                    , (select contact_id from contact where name = 'null'),(select cvterm_id from cvterm where name = 'null'));
+insert into arraydesign (name,manufacturer_id,platformtype_id) values ('virtual array'                              , (select contact_id from contact where name = 'null'),(select cvterm_id from cvterm where name = 'null'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_HG-U133_Plus_2' , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_HG-U133A'       , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_HG-U133A_2'     , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_HG-U133B'       , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_HG-U95Av2'      , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_HG-U95B'        , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_HG-U95C'        , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_HG-U95D'        , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_HG-U95E'        , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_HuExon1'        , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_HuGeneFL'       , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_U74Av2'         , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_MG-U74Av2'      , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_MG-U74Bv2'      , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_MG-U74Cv2'      , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_RG-U34A'        , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_RG-U34B'        , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_RG-U34C'        , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_RT-U34'         , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_RN-U34'         , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_YG-S98'         , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_Yeast_2'        , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_RAE230A'        , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_RAE230B'        , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_Rat230_2'       , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_MOE430A'        , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_MOE430B'        , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_Mouse430_2'     , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_Mouse430A_2'    , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_ATH1-121501'    , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_Mapping100K_Hind240' , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_Mapping100K_Xba240'  , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_Mapping10K_Xba131'   , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_Mapping10K_Xba142'   , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_Mapping500K_NspI'    , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+insert into arraydesign (name,manufacturer_id,platformtype_id,substratetype_id) values ('Affymetrix_Mapping500K_StyI'    , (select contact_id from contact where name = 'Affymetrix'),(select cvterm_id from cvterm where name = 'photochemical_oligo'),(select cvterm_id from cvterm where name = 'glass'));
+
+insert into cv (name) values ('developmental stages');
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'developmental stages:fetus');
+insert into cvterm (name,cv_id,dbxref_id) values ('fetus',      (select cv_id from cv where name = 'local'),(select dbxref_id from dbxref where accession='developmental stages:fetus'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'developmental stages:neonate');
+insert into cvterm (name,cv_id,dbxref_id) values ('neonate',    (select cv_id from cv where name = 'developmental stages'), (select dbxref_id from dbxref where accession='developmental stages:neonate'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'developmental stages:child');
+insert into cvterm (name,cv_id,dbxref_id) values ('child',      (select cv_id from cv where name = 'developmental stages'), (select dbxref_id from dbxref where accession='developmental stages:child'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'developmental stages:adult_young');
+insert into cvterm (name,cv_id,dbxref_id) values ('adult_young',(select cv_id from cv where name = 'developmental stages'),(select dbxref_id from dbxref where accession='developmental stages:adult_young'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'developmental stages:adult');
+insert into cvterm (name,cv_id,dbxref_id) values ('adult',      (select cv_id from cv where name = 'developmental stages'),(select dbxref_id from dbxref where accession='developmental stages:adult'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'developmental stages:adult_old');
+insert into cvterm (name,cv_id,dbxref_id) values ('adult_old',  (select cv_id from cv where name = 'developmental stages'), (select dbxref_id from dbxref where accession='developmental stages:adult_old'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'local:survival_time');
+insert into cvterm (name,cv_id,dbxref_id) values ('survival_time',(select cv_id from cv where name = 'local'),(select dbxref_id from dbxref where accession='local:survival_time'));
+
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'Statistical Terms:n');
+insert into cvterm (name,definition,cv_id,dbxref_id) values ('n','sensu statistica',  (select cv_id from cv where name = 'Statistical Terms'),(select dbxref_id from dbxref where accession='Statistical Terms:n'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'Statistical Terms:minimum');
+insert into cvterm (name,definition,cv_id,dbxref_id) values ('minimum','sensu statistica',  (select cv_id from cv where name = 'Statistical Terms'),(select dbxref_id from dbxref where accession='Statistical Terms:minimum'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'Statistical Terms:maximum');
+insert into cvterm (name,definition,cv_id,dbxref_id) values ('maximum','sensu statistica',  (select cv_id from cv where name = 'Statistical Terms'),(select dbxref_id from dbxref where accession='Statistical Terms:maximum'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'Statistical Terms:modality');
+insert into cvterm (name,definition,cv_id,dbxref_id) values ('modality','sensu statistica',  (select cv_id from cv where name = 'Statistical Terms'),(select dbxref_id from dbxref where accession='Statistical Terms:modality'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'Statistical Terms:modality p');
+insert into cvterm (name,definition,cv_id,dbxref_id) values ('modality p','sensu statistica',  (select cv_id from cv where name = 'Statistical Terms'),(select dbxref_id from dbxref where accession='Statistical Terms:modality p'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'Statistical Terms:mean');
+insert into cvterm (name,definition,cv_id,dbxref_id) values ('mean','sensu statistica',  (select cv_id from cv where name = 'Statistical Terms'),(select dbxref_id from dbxref where accession='Statistical Terms:mean'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'Statistical Terms:median');
+insert into cvterm (name,definition,cv_id,dbxref_id) values ('median','sensu statistica',  (select cv_id from cv where name = 'Statistical Terms'),(select dbxref_id from dbxref where accession='Statistical Terms:median'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'Statistical Terms:mode');
+insert into cvterm (name,definition,cv_id,dbxref_id) values ('mode','sensu statistica',  (select cv_id from cv where name = 'Statistical Terms'),(select dbxref_id from dbxref where accession='Statistical Terms:mode'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'Statistical Terms:quartile 1');
+insert into cvterm (name,definition,cv_id,dbxref_id) values ('quartile 1','sensu statistica',  (select cv_id from cv where name = 'Statistical Terms'),(select dbxref_id from dbxref where accession='Statistical Terms:quartile 1'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'Statistical Terms:quartile 3');
+insert into cvterm (name,definition,cv_id,dbxref_id) values ('quartile 3','sensu statistica',  (select cv_id from cv where name = 'Statistical Terms'),(select dbxref_id from dbxref where accession='Statistical Terms:quartile 3'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'Statistical Terms:skewness');
+insert into cvterm (name,definition,cv_id,dbxref_id) values ('skewness','sensu statistica',  (select cv_id from cv where name = 'Statistical Terms'),(select dbxref_id from dbxref where accession='Statistical Terms:skewness'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'Statistical Terms:kurtosis');
+insert into cvterm (name,definition,cv_id,dbxref_id) values ('kurtosis','sensu statistica',  (select cv_id from cv where name = 'Statistical Terms'),(select dbxref_id from dbxref where accession='Statistical Terms:kurtosis'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'Statistical Terms:chi square p');
+insert into cvterm (name,definition,cv_id,dbxref_id) values ('chi square p','sensu statistica',  (select cv_id from cv where name = 'Statistical Terms'),(select dbxref_id from dbxref where accession='Statistical Terms:chi square p'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'Statistical Terms:standard deviation');
+insert into cvterm (name,definition,cv_id,dbxref_id) values ('standard deviation','sensu statistica',  (select cv_id from cv where name = 'Statistical Terms'),(select dbxref_id from dbxref where accession='Statistical Terms:standard deviation'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'Statistical Terms:expectation maximization gaussian mean');
+insert into cvterm (name,definition,cv_id,dbxref_id) values ('expectation maximization gaussian mean','sensu statistica',  (select cv_id from cv where name = 'Statistical Terms'),(select dbxref_id from dbxref where accession='Statistical Terms:expectation maximization gaussian mean'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'Statistical Terms:expectation maximization p');
+insert into cvterm (name,definition,cv_id,dbxref_id) values ('expectation maximization p','sensu statistica',  (select cv_id from cv where name = 'Statistical Terms'),(select dbxref_id from dbxref where accession='Statistical Terms:expectation maximization p'));
+
+insert into dbxref (db_id,accession) values ((select db_id from db where name='null'), 'Statistical Terms:histogram');
+insert into cvterm (name,definition,cv_id,dbxref_id) values ('histogram','sensu statistica',  (select cv_id from cv where name = 'Statistical Terms'),(select dbxref_id from dbxref where accession='Statistical Terms:histogram'));
+
+insert into cv (name,definition) values ('autocreated','Terms that are automatically inserted by loading software');
+
+
+--this table will probably end up in general.sql
+ CREATE TABLE public.materialized_view   (       
+                                materialized_view_id SERIAL,
+                                last_update TIMESTAMP,
+                                refresh_time INT,
+                                name VARCHAR(64) UNIQUE,
+                                mv_schema VARCHAR(64),
+                                mv_table VARCHAR(128),
+                                mv_specs TEXT,
+                                indexed TEXT,
+                                query TEXT,
+                                special_index TEXT
+                                );
+
+

+ 439 - 0
modules/base/tripal_core/jobs.php

@@ -0,0 +1,439 @@
+<?php
+
+/** 
+ * @defgroup tripal_jobs_api Core Module Jobs API
+ * @{
+ * Tripal offers a job management subsystem for managing tasks that may require an extended period of time for 
+ * completion.  Drupal uses a UNIX-based cron job to handle tasks such as  checking  the  availability of updates, 
+ * indexing new nodes for searching, etc.   Drupal's cron uses the web interface for launching these tasks, however, 
+ * Tripal provides several administrative tasks that may time out and not complete due to limitations of the web 
+ * server.  Examples including syncing of a large number of features between chado and Drupal.  To circumvent this, 
+ * as well as provide more fine-grained control and monitoring, Tripal uses a jobs management sub-system built into 
+ * the Tripal Core module.   It is anticipated that this functionality will be used for managing analysis jobs provided by 
+ * future tools, with eventual support for distributed computing.   
+ *
+ * The  Tripal jobs management system allows administrators to submit tasks to be performed which can then  be 
+ * launched through a UNIX command-line PHP script or cron job.  This command-line script can be added to a cron 
+ * entry along-side the Drupal cron entry for automatic, regular launching of Tripal jobs.  The order of execution of 
+ * waiting jobs is determined first by priority and second by the order the jobs were entered.  
+ *
+ * The API functions described below provide a programmatic interface for adding, checking and viewing jobs.
+ * @}
+ * @ingroup tripal_api
+ */
+ 
+/**
+ * Adds a job to the Tripal Jbo queue
+ *
+ * @param $job_name
+ *    The human readable name for the job
+ * @param $modulename
+ *    The name of the module adding the job
+ * @param $callback
+ *    The name of a function to be called when the job is executed
+ * @param $arguments
+ *    An array of arguements to be passed on to the callback
+ * @param $uid
+ *    The uid of the user adding the job
+ * @param $priority
+ *    The priority at which to run the job where the highest priority is 10 and the lowest priority 
+ *    is 1. The default priority is 10.
+ *
+ * @return
+ *    The job_id of the registered job
+ *
+ * @ingroup tripal_jobs_api
+ */
+function tripal_add_job ($job_name,$modulename,$callback,$arguments,$uid,$priority = 10){
+
+   # convert the arguments into a string for storage in the database
+   $args = implode("::",$arguments);
+
+   $record = new stdClass();
+   $record->job_name = $job_name;
+   $record->modulename = $modulename;
+   $record->callback = $callback;
+   $record->status = 'Waiting';
+   $record->submit_date = time();
+   $record->uid = $uid;
+   $record->priority = $priority;  # the lower the number the higher the priority
+   if($args){
+      $record->arguments = $args;
+   }
+   if(drupal_write_record('tripal_jobs',$record)){
+      $jobs_url = url("admin/tripal/tripal_jobs");
+      drupal_set_message(t("Job '$job_name' submitted.  Check the <a href='$jobs_url'>jobs page</a> for status"));
+   } else {
+      drupal_set_message("Failed to add job $job_name.");
+   }
+
+   return $record->job_id;
+}
+
+/**
+ * An internal function for setting the progress for a current job
+ *
+ * @param $job_id
+ *   The job_id to set the progress for
+ * @param $percentage
+ *   The progress to set the job to
+ *
+ * @return
+ *   True on success and False otherwise
+ *
+ * @ingroup tripal_core
+ */
+function tripal_job_set_progress($job_id,$percentage){
+
+   if(preg_match("/^(\d\d|100)$/",$percentage)){
+      $record = new stdClass();
+      $record->job_id = $job_id; 
+      $record->progress = $percentage;
+	  if(drupal_write_record('tripal_jobs',$record,'job_id')){
+	     return 1;
+	  }
+   }
+   return 0;
+}
+
+/**
+ * Returns a list of jobs associated with the given module
+ *
+ * @param $modulename
+ *    The module to return a list of jobs for
+ *
+ * @return
+ *    An array of objects where each object describes a tripal job
+ *
+ * @ingroup tripal_jobs_api
+ */
+function tripal_get_module_active_jobs ($modulename){
+   $sql =  "SELECT * FROM {tripal_jobs} TJ ".
+           "WHERE TJ.end_time IS NULL and TJ.modulename = '%s' ";
+  return db_fetch_object(db_query($sql,$modulename));
+
+}
+/**
+ * Returns the Tripal Job Report
+ *
+ * @return
+ *   The HTML to be rendered which describes the job report
+ *
+ * @ingroup tripal_core
+ */
+function tripal_jobs_report () {
+   //$jobs = db_query("SELECT * FROM {tripal_jobs} ORDER BY job_id DESC");
+   $jobs = pager_query(
+      "SELECT TJ.job_id,TJ.uid,TJ.job_name,TJ.modulename,TJ.progress,
+              TJ.status as job_status, TJ,submit_date,TJ.start_time,
+              TJ.end_time,TJ.priority,U.name as username
+       FROM {tripal_jobs} TJ 
+         INNER JOIN {users} U on TJ.uid = U.uid 
+       ORDER BY job_id DESC", 10,0,"SELECT count(*) FROM {tripal_jobs}");
+	
+   // create a table with each row containig stats for 
+   // an individual job in the results set.
+   $output .= "Waiting jobs are executed first by priority level (the lower the ".
+              "number the higher the priority) and second by the order they ".
+              "were entered";
+   $output .= "<table class=\"tripal-table tripal-table-horz\">". 
+              "  <tr>".
+              "    <th>Job ID</th>".
+              "    <th>User</th>".
+              "    <th>Job Name</th>".
+              "    <th nowrap>Dates</th>".             
+			     "    <th>Priority</th>".
+			     "    <th>Progress</th>".
+              "    <th>Status</th>".
+              "    <th>Actions</th>".
+              "  </tr>";
+   $i = 0;
+   while($job = db_fetch_object($jobs)){
+      $class = 'tripal-table-odd-row';
+      if($i % 2 == 0 ){
+         $class = 'tripal-table-even-row';
+      }
+      $submit = tripal_jobs_get_submit_date($job);
+      $start = tripal_jobs_get_start_time($job);
+      $end = tripal_jobs_get_end_time($job);
+
+      $cancel_link = '';
+      if($job->start_time == 0 and $job->end_time == 0){
+         $cancel_link = "<a href=\"".url("admin/tripal/tripal_jobs/cancel/".$job->job_id)."\">Cancel</a><br>";
+      }
+      $rerun_link = "<a href=\"".url("admin/tripal/tripal_jobs/rerun/".$job->job_id)."\">Re-run</a><br>";
+      $view_link ="<a href=\"".url("admin/tripal/tripal_jobs/view/".$job->job_id)."\">View</a>";
+      $output .= "  <tr class=\"$class\">";
+      $output .= "    <td>$job->job_id</td>".
+                 "    <td>$job->username</td>".
+                 "    <td>$job->job_name</td>".
+                 "    <td nowrap>Submit Date: $submit".
+                 "    <br>Start Time: $start".
+                 "    <br>End Time: $end</td>".
+                 "    <td>$job->priority</td>".
+				     "    <td>$job->progress%</td>".
+                 "    <td>$job->job_status</td>".
+                 "    <td>$cancel_link $rerun_link $view_link</td>".
+                 "  </tr>";
+      $i++;
+   }
+   $output .= "</table>";
+	$output .= theme_pager();
+   return $output;
+}
+
+/**
+ * Returns the start time for a given job
+ * 
+ * @param $job
+ *   An object describing the job
+ *
+ * @return
+ *   The start time of the job if it was already run and either "Cancelled" or "Not Yet Started" otherwise
+ *
+ * @ingroup tripal_jobs_api
+ */
+function tripal_jobs_get_start_time($job){
+   if($job->start_time > 0){
+      $start = format_date($job->start_time);
+   } else {
+      if(strcmp($job->job_status,'Cancelled')==0){
+         $start = 'Cancelled';
+      } else {
+         $start = 'Not Yet Started';
+      }
+   }
+   return $start;
+}
+
+/**
+ * Returns the end time for a given job
+ * 
+ * @param $job
+ *   An object describing the job
+ *
+ * @return
+ *   The end time of the job if it was already run and empty otherwise
+ *
+ * @ingroup tripal_jobs_api
+ */
+function tripal_jobs_get_end_time($job){
+   if($job->end_time > 0){
+      $end = format_date($job->end_time);
+   } else {
+      $end = '';
+   }
+   return $end;
+}
+
+/**
+ * Returns the date the job was added to the queue
+ *
+ * @param $job
+ *   An object describing the job
+ *
+ * @return
+ *   The date teh job was submitted
+ *
+ * @ingroup tripal_jobs_api
+ */
+function tripal_jobs_get_submit_date($job){
+   return format_date($job->submit_date);
+}
+
+/**
+ * A function used to manually launch all queued tripal jobs
+ *
+ * @param $do_parallel
+ *   A boolean indicating whether jobs should be attempted to run in parallel
+ *
+ * @ingroup tripal_jobs_api
+ */
+function tripal_jobs_launch ($do_parallel = 0){
+   
+   // first check if any jobs are currently running
+   // if they are, don't continue, we don't want to have
+   // more than one job script running at a time
+   if(!$do_parallel and tripal_jobs_check_running()){
+      return;
+   }
+   
+   // get all jobs that have not started and order them such that
+   // they are processed in a FIFO manner. 
+   $sql =  "SELECT * FROM {tripal_jobs} TJ ".
+           "WHERE TJ.start_time IS NULL and TJ.end_time IS NULL ".
+           "ORDER BY priority ASC,job_id ASC";
+   $job_res = db_query($sql);
+   while($job = db_fetch_object($job_res)){
+
+		// set the start time for this job
+		$record = new stdClass();
+		$record->job_id = $job->job_id;
+		$record->start_time = time();
+		$record->status = 'Running';
+		$record->pid = getmypid();
+		drupal_write_record('tripal_jobs',$record,'job_id');
+
+		// call the function provided in the callback column.
+		// Add the job_id as the last item in the list of arguments. All
+		// callback functions should support this argument.
+		$callback = $job->callback;
+		$args = split("::",$job->arguments);
+		$args[] = $job->job_id;
+		print "Calling: $callback(" . implode(", ",$args) . ")\n";   
+		call_user_func_array($callback,$args);
+		
+		// set the end time for this job
+		$record->end_time = time();
+		$record->status = 'Completed';
+		$record->progress = '100';
+		drupal_write_record('tripal_jobs',$record,'job_id');
+		
+		// send an email to the user advising that the job has finished
+   }
+}
+
+/**
+ * Returns a list of running tripal jobs
+ *
+ * @return
+ *    and array of objects where each object describes a running job or false if no jobs are running
+ *
+ * @ingroup tripal_jobs_api
+ */
+function tripal_jobs_check_running () {
+   // iterate through each job that has not ended
+   // and see if it is still running. If it is not
+   // running but does not have an end_time then
+   // set the end time and set the status to 'Error'
+   $sql =  "SELECT * FROM {tripal_jobs} TJ ".
+           "WHERE TJ.end_time IS NULL and NOT TJ.start_time IS NULL ";
+   $jobs = db_query($sql);
+   while($job = db_fetch_object($jobs)){
+      if($job->pid and posix_kill($job->pid, 0)) {
+         // the job is still running so let it go
+		   // we return 1 to indicate that a job is running
+		   print "Job is still running (pid $job->pid)\n";
+		   return 1;
+      } else {
+	      // the job is not running so terminate it
+	      $record = new stdClass();
+         $record->job_id = $job->job_id;
+	      $record->end_time = time();
+         $record->status = 'Error';
+	      $record->error_msg = 'Job has terminated unexpectedly.';
+         drupal_write_record('tripal_jobs',$record,'job_id');
+	   }
+   }
+   // return 1 to indicate that no jobs are currently running.
+   return 0;
+}
+
+/**
+ * Returns the HTML code to display a given job
+ *
+ * @param $job_id
+ *   The job_id of the job to display
+ * 
+ * @return
+ *   The HTML describing the indicated job
+ * @ingroup tripal_core
+ */
+function tripal_jobs_view ($job_id){
+   return theme('tripal_core_job_view',$job_id);
+}
+
+/**
+ * Registers variables for the tripal_core_job_view themeing function
+ *
+ * @param $variables
+ *   An array containing all variables supplied to this template
+ *
+ * @ingroup tripal_core
+ */
+function tripal_core_preprocess_tripal_core_job_view (&$variables){
+   // get the job record
+   $job_id = $variables['job_id'];
+   $sql = 
+      "SELECT TJ.job_id,TJ.uid,TJ.job_name,TJ.modulename,TJ.progress,
+              TJ.status as job_status, TJ,submit_date,TJ.start_time,
+              TJ.end_time,TJ.priority,U.name as username,TJ.arguments,
+              TJ.callback,TJ.error_msg,TJ.pid
+       FROM {tripal_jobs} TJ 
+         INNER JOIN users U on TJ.uid = U.uid 
+       WHERE TJ.job_id = %d";
+   $job = db_fetch_object(db_query($sql,$job_id));
+
+   // we do not know what the arguments are for and we want to provide a 
+   // meaningful description to the end-user. So we use a callback function
+   // deinfed in the module that created the job to describe in an array
+   // the arguments provided.  If the callback fails then just use the 
+   // arguments as they are
+   $args = preg_split("/::/",$job->arguments);
+   $arg_hook = $job->modulename."_job_describe_args";
+   if(is_callable($arg_hook)){
+      $new_args = call_user_func_array($arg_hook,array($job->callback,$args));
+      if(is_array($new_args) and count($new_args)){
+         $job->arguments = $new_args;
+      } else {
+         $job->arguments = $args;
+      }
+   } else {
+      $job->arguments = $args;
+   }
+
+   // make our start and end times more legible
+   $job->submit_date = tripal_jobs_get_submit_date($job);
+   $job->start_time = tripal_jobs_get_start_time($job);
+   $job->end_time = tripal_jobs_get_end_time($job);
+
+   // add the job to the variables that get exported to the template
+   $variables['job'] = $job;
+}
+/**
+ * Set a job to be re-ran (ie: add it back into the job queue)
+ * 
+ * @param $job_id
+ *   The job_id of the job to be re-ran
+ * 
+ * @ingroup tripal_jobs_api
+ */
+function tripal_jobs_rerun ($job_id){
+   global $user;
+
+   $sql = "select * from {tripal_jobs} where job_id = %d";
+   $job = db_fetch_object(db_query($sql,$job_id));
+
+   $args = explode("::",$job->arguments);
+   tripal_add_job ($job->job_name,$job->modulename,$job->callback,$args,$user->uid,
+      $job->priority);
+
+   drupal_goto("admin/tripal/tripal_jobs");
+}
+
+/**
+ * Cancel a Tripal Job currently waiting in the job queue
+ *
+ * @param $job_id
+ *   The job_id of the job to be cancelled
+ *
+ * @ingroup tripal_jobs_api
+ */
+function tripal_jobs_cancel ($job_id){
+   $sql = "select * from {tripal_jobs} where job_id = %d";
+   $job = db_fetch_object(db_query($sql,$job_id));
+
+   // set the end time for this job
+   if($job->start_time == 0){
+      $record = new stdClass();
+      $record->job_id = $job->job_id;
+	   $record->end_time = time();
+	   $record->status = 'Cancelled';
+	   $record->progress = '0';
+	   drupal_write_record('tripal_jobs',$record,'job_id');
+      drupal_set_message("Job #$job_id cancelled");
+   } else {
+      drupal_set_message("Job #$job_id cannot be cancelled. It is in progress or has finished.");
+   }
+   drupal_goto("admin/tripal/tripal_jobs");
+}

+ 454 - 0
modules/base/tripal_core/mviews.php

@@ -0,0 +1,454 @@
+<?php
+
+/** 
+ * @defgroup tripal_mviews_api Core Module Materalized Views API
+ * @{
+ * Provides an application programming interface (API) to manage materialized views in Chado.
+ * The Perl-based chado comes with an interface for managing materialzed views.  This
+ * API provides an alternative Drupal-based method.  
+ * @}
+ * @ingroup tripal_api
+ */
+
+/**
+ * Add a materialized view to the chado database to help speed data access.
+ *
+ * @param $name 
+ *   The name of the materialized view.
+ * @param $modulename 
+ *   The name of the module submitting the materialized view (e.g. 'tripal_library')
+ * @param $mv_table 
+ *   The name of the table to add to chado. This is the table that can be queried.
+ * @param $mv_specs 
+ *   The table definition 
+ * @param $indexed 
+ *   The columns that are to be indexed
+ * @param $query 
+ *   The SQL query that loads the materialized view with data
+ * @param $special_index  
+ *   function
+ *
+ * @ingroup tripal_mviews_api
+ */
+function tripal_add_mview ($name,$modulename,$mv_table,$mv_specs,$indexed,$query,$special_index){
+
+   $record = new stdClass();
+   $record->name = $name;
+   $record->modulename = $modulename;
+   $record->mv_schema = 'DUMMY';
+   $record->mv_table = $mv_table;
+   $record->mv_specs = $mv_specs;
+   $record->indexed = $indexed;
+   $record->query = $query;
+   $record->special_index = $special_index;
+
+   // add the record to the tripal_mviews table and if successful
+   // create the new materialized view in the chado schema
+   if(drupal_write_record('tripal_mviews',$record)){
+
+      // drop the table from chado if it exists
+      $previous_db = tripal_db_set_active('chado');  // use chado database
+      if (db_table_exists($mv_table)) {
+         $sql = "DROP TABLE $mv_table";
+         db_query($sql);
+      }
+      tripal_db_set_active($previous_db);  // now use drupal database
+      
+      // now add the table for this view
+      $index = '';
+      if($indexed){
+         $index = ", CONSTRAINT ". $mv_table . "_index UNIQUE ($indexed) ";
+      }
+      $sql = "CREATE TABLE {$mv_table} ($mv_specs $index)"; 
+      $previous_db = tripal_db_set_active('chado');  // use chado database
+      $results = db_query($sql);
+      tripal_db_set_active($previous_db);  // now use drupal database
+      if($results){
+         drupal_set_message(t("View '$name' created"));
+      } else {
+         // if we failed to create the view in chado then
+         // remove the record from the tripal_jobs table
+         $sql = "DELETE FROM {tripal_mviews} ".
+                "WHERE mview_id = $record->mview_id";
+         db_query($sql);
+      }
+   }
+}
+/**
+ * Retrieve the materialized view_id given the name
+ *
+ * @param $view_name
+ *   The name of the materialized view
+ *
+ * @return
+ *   The unique identifier for the given view
+ *
+ * @ingroup tripal_mviews_api
+ */
+function tripal_mviews_get_mview_id ($view_name){
+
+   $sql = "SELECT * FROM {tripal_mviews} ".
+          "WHERE name = '%s'";
+   if(db_table_exists('tripal_mviews')){
+      $mview = db_fetch_object(db_query($sql,$view_name));
+	   if($mview){
+	      return $mview->mview_id;
+	   }
+   }
+   return 0;
+}
+/**
+*
+*
+* @ingroup tripal_core
+*/
+function tripal_mviews_action ($op,$mview_id){
+   global $user;
+   $args = array("$mview_id");
+   
+   if(!$mview_id){
+      return '';
+   }
+
+   // get this mview details
+   $sql = "SELECT * FROM {tripal_mviews} WHERE mview_id = $mview_id ";
+   $mview = db_fetch_object(db_query($sql));
+   
+   // add a job or perform the action based on the given operation
+   if($op == 'update'){
+      tripal_add_job("Update materialized view '$mview->name'",'tripal_core',
+         'tripal_update_mview',$args,$user->uid);
+	}
+   if($op == 'delete'){
+	   // remove the mview from the tripal_mviews table
+	   $sql = "DELETE FROM {tripal_mviews} ".
+             "WHERE mview_id = $mview_id";
+      db_query($sql);
+		
+	   // drop the table from chado if it exists
+      $previous_db = tripal_db_set_active('chado');  // use chado database
+      if (db_table_exists($mview->mv_table)) {
+         $sql = "DROP TABLE $mview->mv_table";
+         db_query($sql);
+      }
+      tripal_db_set_active($previous_db);  // now use drupal database
+   }
+   return '';
+}
+/**
+* Update a Materialized View
+*
+* @param $mview_id
+*   The unique identifier for the materialized view to be updated
+*
+* @return
+*   True if successful, false otherwise
+*
+* @ingroup tripal_mviews_api
+*/
+function tripal_update_mview ($mview_id){
+   $sql = "SELECT * FROM {tripal_mviews} WHERE mview_id = %d ";
+   $mview = db_fetch_object(db_query($sql,$mview_id));
+   if($mview){
+      $previous_db = tripal_db_set_active('chado');  // use chado database
+	   $results = db_query("DELETE FROM {$mview->mv_table}");
+      $results = db_query("INSERT INTO $mview->mv_table ($mview->query)");
+      tripal_db_set_active($previous_db);  // now use drupal database
+      if($results){
+	      $record = new stdClass();
+         $record->mview_id = $mview_id;
+         $record->last_update = time();
+		   drupal_write_record('tripal_mviews',$record,'mview_id');
+		   return 1;
+      } else {
+	     // TODO -- error handling
+	     return 0;
+	  }
+   }
+}
+/**
+*
+*
+* @ingroup tripal_mviews_api
+*/
+function tripal_mview_report ($mview_id) {
+   // get this mview details
+   $sql = "SELECT * FROM {tripal_mviews} WHERE mview_id = $mview_id ";
+   $mview = db_fetch_object(db_query($sql));
+
+   // create a table with each row containig stats for
+   // an individual job in the results set.
+
+   $return_url = url("admin/tripal/tripal_mviews/");
+
+   $output .= "<p><a href=\"$return_url\">Return to table of materialized views.</a></p>";
+   $output .= "<br />";
+   $output .= "<p>Details for <b>$mview->name</b>:</p>";
+   $output .= "<br />";
+   $output .= "<table class=\"border-table\">";
+   if($mview->name){
+      $output .= "  <tr>".
+      "    <th>View Name</th>".
+      "    <td>$mview->name</td>".
+      "  </tr>";
+   }   
+   if($mview->modulename){
+      $output .= "  <tr>".
+      "    <th>Module Name</th>".
+      "    <td>$mview->modulename</td>".
+      "  </tr>";
+   }
+   if($mview->mv_table){
+      $output .= "  <tr>".
+      "    <th>Table Name</th>".
+      "    <td>$mview->mv_table</td>".
+      "  </tr>";
+   }   
+   if($mview->mv_specs){
+      $output .= "  <tr>".
+      "    <th>Table Field Definitions</th>".
+      "    <td>$mview->mv_specs</td>".
+      "  </tr>";
+   }   
+   if($mview->query){
+      $output .= "  <tr>".
+      "    <th>Query</th>".
+      "    <td><pre>$mview->query</pre></td>".
+      "  </tr>";
+   }   
+   if($mview->indexed){
+      $output .= "  <tr>".
+      "    <th>Indexed Fields</th>".
+      "    <td>$mview->indexed</td>".
+      "  </tr>";
+   }   
+   if($mview->special_index){
+      $output .= "  <tr>".
+      "    <th>Special Indexed Fields</th>".
+      "    <td>$mview->speical_index</td>".
+      "  </tr>";
+   }   
+   if($mview->last_update > 0){
+      $update = format_date($mview->last_update);
+   } else {
+      $update = 'Not yet populated';
+   }
+   $output .= "  <tr>".
+      "    <th>Last Update</th>".
+      "    <td>$update</td>".
+      "  </tr>";
+
+   // build the URLs using the url function so we can handle installations where
+   // clean URLs are or are not used
+   $update_url = url("admin/tripal/tripal_mviews/action/update/$mview->mview_id");
+   $delete_url = url("admin/tripal/tripal_mviews/action/delete/$mview->mview_id");
+   $edit_url = url("admin/tripal/tripal_mviews/edit/$mview->mview_id");
+
+   $output .= "<tr><th>Actions</th>".
+              "<td> <a href='$update_url'>Update</a>, ".
+              "     <a href='$edit_url'>Edit</a>, ".
+              "     <a href='$delete_url'>Delete</a></td></tr>";
+
+   $output .= "</table>";
+
+   return $output;
+}
+/**
+*
+*
+* @ingroup tripal_mviews_api
+*/
+function tripal_mviews_report () {
+   $mviews = db_query("SELECT * FROM {tripal_mviews} ORDER BY name");
+
+   // create a table with each row containig stats for
+   // an individual job in the results set.
+   $output .= "<table class=\"border-table\">". 
+              "  <tr>".
+              "    <th nowrap></th>".
+              "    <th>Name</th>".
+              "    <th>Last_Update</th>".
+              "    <th nowrap></th>".
+              "  </tr>";
+   
+   while($mview = db_fetch_object($mviews)){
+      if($mview->last_update > 0){
+         $update = format_date($mview->last_update);
+      } else {
+         $update = 'Not yet populated';
+      }
+	  // build the URLs using the url function so we can handle installations where
+	  // clean URLs are or are not used
+	  $view_url = url("admin/tripal/tripal_mview/$mview->mview_id");
+	  $update_url = url("admin/tripal/tripal_mviews/action/update/$mview->mview_id");
+	  $delete_url = url("admin/tripal/tripal_mviews/action/delete/$mview->mview_id");
+	  // create the row for the table
+      $output .= "  <tr>";
+      $output .= "    <td><a href='$view_url'>View</a>&nbsp".
+                 "        <a href='$update_url'>Update</a></td>".
+	             "    <td>$mview->name</td>".
+                 "    <td>$update</td>".
+                 "    <td><a href='$delete_url'>Delete</a></td>".
+                 "  </tr>";
+   }
+   $new_url = url("admin/tripal/tripal_mviews/new");
+   $output .= "</table>";
+   $output .= "<br />";
+   $output .= "<p><a href=\"$new_url\">Create a new materialized view.</a></p>";
+   return $output;
+}
+/**
+*
+*
+* @ingroup tripal_core
+*/
+function tripal_mviews_form(&$form_state = NULL,$mview_id = NULL){
+
+   if(!$mview_id){
+      $action = 'Add';
+   } else {
+      $action = 'Update';
+   }
+
+   // get this requested view
+   if(strcmp($action,'Update')==0){
+      $sql = "SELECT * FROM {tripal_mviews} WHERE mview_id = $mview_id ";
+      $mview = db_fetch_object(db_query($sql));
+
+
+      # set the default values.  If there is a value set in the 
+      # form_state then let's use that, otherwise, we'll pull 
+      # the values from the database 
+      $default_name = $form_state['values']['name'];
+      $default_mv_table = $form_state['values']['mv_table'];
+      $default_mv_specs = $form_state['values']['mv_specs'];
+      $default_indexed = $form_state['values']['indexed'];
+      $default_mvquery = $form_state['values']['mvquery'];
+      $default_special_index = $form_state['values']['special_index'];
+      if(!$default_name){
+         $default_name = $mview->name;
+      }
+      if(!$default_mv_table){
+         $default_mv_table = $mview->mv_table;
+      }
+      if(!$default_mv_specs){
+         $default_mv_specs = $mview->mv_specs;
+      }
+      if(!$default_indexed){
+         $default_indexed = $mview->indexed;
+      }
+      if(!$default_mvquery){
+         $default_mvquery = $mview->query;
+      }
+      if(!$default_special_index){
+         $default_special_index = $mview->special_index;
+      }
+   }
+   // Build the form
+   $form['action'] = array(
+      '#type' => 'value',
+      '#value' => $action
+   );
+   $form['mview_id'] = array(
+      '#type' => 'value',
+      '#value' => $mview_id
+   );
+   $form['name']= array(
+      '#type'          => 'textfield',
+      '#title'         => t('View Name'),
+      '#description'   => t('Please enter the name for this materialized view.'),
+      '#required'      => TRUE,
+      '#default_value' => $default_name,
+      '#weight'        => 1
+   );
+
+   $form['mv_table']= array(
+      '#type'          => 'textfield',
+      '#title'         => t('Table Name'),
+      '#description'   => t('Please enter the Postgres table name that this view will generate in the database.  You can use the schema and table name for querying the view'),
+      '#required'      => TRUE,
+      '#default_value' => $default_mv_table,
+      '#weight'        => 3
+   );
+   $form['mv_specs']= array(
+      '#type'          => 'textarea',
+      '#title'         => t('Table Definition'),
+      '#description'   => t('Please enter the field definitions for this view. Each field should be separated by a comma or enter each field definition on each line.'),
+      '#required'      => TRUE,
+      '#default_value' => $default_mv_specs,
+      '#weight'        => 4
+   );
+   $form['indexed']= array(
+      '#type'          => 'textarea',
+      '#title'         => t('Indexed Fields'),
+      '#description'   => t('Please enter the field names (as provided in the table definition above) that will be indexed for this view.  Separate by a comma or enter each field on a new line.'),
+      '#required'      => FALSE,
+      '#default_value' => $default_indexed,
+      '#weight'        => 5
+   );
+   $form['mvquery']= array(
+      '#type'          => 'textarea',
+      '#title'         => t('Query'),
+      '#description'   => t('Please enter the SQL statement used to populate the table.'),
+      '#required'      => TRUE,
+      '#default_value' => $default_mvquery,
+      '#weight'        => 6
+   );
+/**
+   $form['special_index']= array(
+      '#type'          => 'textarea',
+      '#title'         => t('View Name'),
+      '#description'   => t('Please enter the name for this materialized view.'),
+      '#required'      => TRUE,
+      '#default_value' => $default_special_index,
+      '#weight'        => 7
+   );
+*/
+   $form['submit'] = array (
+     '#type'         => 'submit',
+     '#value'        => t($action),
+     '#weight'       => 8,
+     '#executes_submit_callback' => TRUE,
+   );
+
+   $form['#redirect'] = 'admin/tripal/tripal_mviews';
+   return $form;
+}
+/**
+*
+*
+* @ingroup tripal_core
+*/
+function tripal_mviews_form_submit($form, &$form_state){
+   
+   $action = $form_state['values']['action'];
+
+   if(strcmp($action,'Update')==0){
+      $record = new stdClass();
+      $record->mview_id = $form_state['values']['mview_id'];
+      $record->name = $form_state['values']['name'];
+      $record->mv_table = $form_state['values']['mv_table'];
+      $record->mv_specs = $form_state['values']['mv_specs'];
+      $record->indexed = $form_state['values']['indexed'];
+      $record->query = $form_state['values']['mvquery'];
+      $record->special_index = $form_state['values']['special_index'];
+
+      // add the record to the tripal_mviews table and if successful
+      // create the new materialized view in the chado schema
+      if(drupal_write_record('tripal_mviews',$record,'mview_id')){
+         drupal_set_message('View updated successfullly');
+      } else {
+         drupal_set_message('View update failed');
+      }
+   }
+   else if(strcmp($action,'Add')==0){
+      tripal_add_mview ($form_state['values']['name'], 'tripal_core',
+         $form_state['values']['mv_table'], $form_state['values']['mv_specs'],
+         $form_state['values']['indexed'], $form_state['values']['mvquery'],
+         $form_state['values']['special_index']);
+   }
+   else {
+        drupal_set_message("No action performed.");
+   }
+   return '';
+}

+ 1677 - 0
modules/base/tripal_core/tripal_core.api.inc

@@ -0,0 +1,1677 @@
+<?php
+/**
+ * @file
+ * The Tripal Core API
+ *
+ * This file provides the API needed for all other Tripal and Tripal dependent
+ * modules.
+ */
+ 
+/**
+ * @defgroup tripal_api Tripal API
+ * @{
+ * Provides an application programming interface (API) for Tripal
+ *
+ * The Tripal API currently provides generic insert/update/select functions for all chado content as 
+ * well as some module specific functions that insert/update/delete/select specific chado content. 
+ *
+ * This API is currently in its infancy and some necessary functions might be missing. If you find
+ * a missing function that you think should be included go to the sourceforge feature request 
+ * page and request it's inclusion in the API. Such feature requests with a working function 
+ * definition will be given priority.
+ * @}
+ */
+
+require_once "tripal_core.schema.api.inc";
+
+/** 
+ * @defgroup tripal_chado_api Core Module Chado API
+ * @{
+ * Provides an application programming interface (API) to manage data withing the Chado database.
+ * This includes functions for selecting, inserting, updating and deleting records 
+ * in Chado tables.  The functions will ensure proper integrity contraints are met
+ * for inserts and updates.  
+ *
+ * Also, a set of functions is provided for creating template variables.  First,
+ * is the tripal_core_generate_chado_vars which is used to select one ore more
+ * records from a table and return an array with foreign key relationships fully
+ * populated.  For example, if selecting a feature, the organism_id and type_id
+ * would be present in the returned array as a nested array with their respective
+ * foreign keys also nested.  The only fields that are not included are text
+ * fields (which may be very large) or many-to-many foreign key relationships.  
+ * However, these fields and relationships can be expanded using the 
+ * tripal_core_expand_chado_vars.
+ * 
+ * When a row from a chado table is selected using these two functions, it provides
+ * a way for users who want to cutomize Drupal template files to access all data
+ * associate with a specific record.
+ *
+ * Finally, the property tables in Chado generally follow the same format.  Therefore
+ * there is a set of functions for inserting, updating and deleting properties for
+ * any table.  This provides quick lookup of properties (provided the CV term is
+ * known).
+ *
+ * @}
+ * @ingroup tripal_api
+ */
+
+/** 
+ * @defgroup tripal_files_api Core Module Files API
+ * @{
+ * Provides an application programming interface (API) for managing files within 
+ * the Tripal data directory structure.
+ *
+ * @}
+ * @ingroup tripal_api
+ */
+ 
+/**
+* Provides a generic routine for inserting into any Chado table
+*
+* Use this function to insert a record into any Chado table.  The first
+* argument specifies the table for inserting and the second is an array
+* of values to be inserted.  The array is mutli-dimensional such that
+* foreign key lookup values can be specified.  
+*
+* @param $table
+*  The name of the chado table for inserting
+* @param $values
+*  An associative array containing the values for inserting.
+* 
+* @return
+*  On success this function returns TRUE. On failure, it returns FALSE.
+*
+* Example usage:
+* @code
+*   $values =  array(
+*     'organism_id' => array(
+*         'genus' => 'Citrus',
+*         'species' => 'sinensis',
+*      ),
+*     'name' => 'orange1.1g000034m.g',
+*     'uniquename' => 'orange1.1g000034m.g',
+*     'type_id' => array (
+*         'cv_id' => array (
+*            'name' => 'sequence',
+*         ),
+*         'name' => 'gene',
+*         'is_obsolete' => 0
+*      ),
+*   );
+*   $result = tripal_core_chado_insert('feature',$values);
+* @endcode
+* The above code inserts a record into the feature table.  The $values array is
+* nested such that the organism is selected by way of the organism_id foreign
+* key constraint by specifying the genus and species.  The cvterm is also
+* specified using its foreign key and the cv_id for the cvterm is nested as
+* well.
+*
+* @ingroup tripal_chado_api
+*/
+function tripal_core_chado_insert($table,$values){
+   $insert_values = array();
+   
+   // get the table description
+   $table_desc = module_invoke_all('chado_'.$table.'_schema');
+
+   // iterate through the values array and create a new 'insert_values' array
+   // that has all the values needed for insert with all foreign relationsihps
+   // resolved.
+   foreach($values as $field => $value){
+      if(is_array($value)){
+         // select the value from the foreign key relationship for this value
+         $results = tripal_core_chado_get_foreign_key($table_desc,$field,$value);
+         if (sizeof($results) > 1) {
+           watchdog('tripal_core', 'tripal_core_chado_insert: Too many records match the criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value,TRUE)), WATCHDOG_ERROR);         
+         } elseif (sizeof($results) < 1) {
+           //watchdog('tripal_core', 'tripal_core_chado_insert: no record matches criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value,TRUE)), WATCHDOG_ERROR);
+         } else {
+           $insert_values[$field] = $results[0];
+         }
+      }  
+      else {
+         $insert_values[$field] = $value;
+      }
+   }
+
+   // check for violation of any unique constraints
+   $ukeys = $table_desc['unique keys'];
+   $ukselect_cols = array();
+   $ukselect_vals = array();
+   if ($ukeys) {
+     foreach($ukeys as $name => $fields){
+        foreach($fields as $index => $field){
+           // build the arrays for performing a select that will check the contraint
+           array_push($ukselect_cols,$field);
+           $ukselect_vals[$field] = $insert_values[$field];
+        }
+        // now check the constraint
+        if(tripal_core_chado_select($table,$ukselect_cols,$ukselect_vals)){
+           watchdog('tripal_core',"tripal_core_chado_insert: Cannot insert duplicate record into $table table: " . print_r($values,1),array(),'WATCHDOG_ERROR');
+           return false;
+        }
+     }
+   }
+
+   // if trying to insert a field that is the primary key, make sure it also is unique
+   $pkey = $table_desc['primary key'][0];
+   if($insert_values[$pkey]){
+      if(tripal_core_chado_select($table,array($pkey),array($pkey => $insert_values[$pkey]))){
+         watchdog('tripal_core',"tripal_core_chado_insert: Cannot insert duplicate primary key into $table table: " . print_r($values,1),array(),'WATCHDOG_ERROR');
+         return false;
+      }
+   }
+
+   // make sure required fields have a value
+   $fields = $table_desc['fields'];
+   foreach($fields as $field => $def){
+      // a field is considered missing if it cannot be null and there is no default
+      // value for it or it is of type 'serial'
+      if($def['not null'] == 1 and !array_key_exists($field,$insert_values) and !isset($def['default']) and strcmp($def['type'],serial)!=0){
+         watchdog('tripal_core',"tripal_core_chado_insert: Field $table.$field cannot be null: " . print_r($values,1),array(),'WATCHDOG_ERROR');
+         return false;
+      }
+   }
+
+   // Now build the insert SQL statement
+   $ifields = array();
+   $ivalues = array();
+   $itypes = array();
+   foreach ($insert_values as $field => $value){
+      array_push($ifields,$field);
+      array_push($ivalues,$value);
+      if(strcmp($value,'__NULL__')==0){
+         array_push($itypes,"NULL");
+      } 
+      elseif(strcmp($fields[$field]['type'],'serial')==0 or 
+         strcmp($fields[$field]['type'],'int')==0){
+         array_push($itypes,"%d");
+      } 
+      else {
+         array_push($itypes,"'%s'");
+      }
+   }
+   $sql = "INSERT INTO {$table} (" . implode(", ",$ifields) . ") VALUES (". implode(", ",$itypes) .")";
+
+   // finally perform the insert. 
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $result = db_query($sql,$ivalues);
+   tripal_db_set_active($previous_db);  // now use drupal database 
+   if($result){
+      // add primary keys to values before return
+      $primary_key = array();
+      foreach ($table_desc['primary key'] as $field) {
+        $value = db_last_insert_id($table, $field);
+        $values[$field] = $value;
+      }
+      return $values;
+   } 
+   else {
+      watchdog('tripal_core',"tripal_core_chado_insert: Cannot insert record into $table table: " . print_r($values,1),array(),'WATCHDOG_ERROR');
+      return false;
+   }
+   return false;
+}
+
+/**
+ * Provides a generic function for deleting a record(s) from any chado table
+ *
+ * Use this function to delete a record(s) in any Chado table.  The first
+ * argument specifies the table to delete from and the second is an array
+ * of values to match for locating the record(s) to be deleted.  The arrays 
+ * are mutli-dimensional such that foreign key lookup values can be specified.
+ *
+ * @param $table
+ *  The name of the chado table for inserting
+ * @param $match
+ *  An associative array containing the values for locating a record to update.
+ *
+ * @return 
+ *   On success this function returns TRUE. On failure, it returns FALSE.
+ *
+ * Example usage:
+ * @code
+   $umatch = array(
+     'organism_id' => array(
+         'genus' => 'Citrus',
+         'species' => 'sinensis',
+      ),
+     'uniquename' => 'orange1.1g000034m.g7',
+     'type_id' => array (
+         'cv_id' => array (
+            'name' => 'sequence',
+         ),
+         'name' => 'gene',
+         'is_obsolete' => 0
+      ),
+   );
+   $uvalues = array(
+      'name' => 'orange1.1g000034m.g',
+      'type_id' => array (
+         'cv_id' => array (
+            'name' => 'sequence',
+         ),
+         'name' => 'mRNA',
+         'is_obsolete' => 0
+      ),
+   );
+ *   $result = tripal_core_chado_update('feature',$umatch,$uvalues);
+ * @endcode
+ * The above code species that a feature with a given uniquename, organism_id,
+ * and type_id (the unique constraint for the feature table) will be deleted.
+ * The organism_id is specified as a nested array that uses the organism_id
+ * foreign key constraint to lookup the specified values to find the exact 
+ * organism_id. The same nested struture is also used for specifying the 
+ * values to update.  The function will find all records that match the 
+ * columns specified and delete them.
+ *
+ * @ingroup tripal_chado_api
+ */
+function tripal_core_chado_delete($table,$match){
+   $delete_matches = array();  // contains the values for the where clause
+   
+   // get the table description
+   $table_desc = module_invoke_all('chado_'.$table.'_schema');
+
+   // get the values needed for matching in the SQL statement
+   foreach ($match as $field => $value){
+      if(is_array($value)){
+         // if the user has specified an array of values to delete rather than
+         // FK relationships the keep those in our match 
+         if(array_values($value) === $value){
+            $delete_matches[$field] = $value;
+         } else {
+            $results = tripal_core_chado_get_foreign_key($table_desc,$field,$value);
+            if (sizeof($results) > 1) {
+              watchdog('tripal_core', 'tripal_core_chado_delete: When trying to find record to delete, too many records match the criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value,TRUE)), WATCHDOG_ERROR);         
+            } elseif (sizeof($results) < 1) {
+              //watchdog('tripal_core', 'tripal_core_chado_delete: When trying to find record to delete, no record matches criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value,TRUE)), WATCHDOG_ERROR);
+            } else {
+              $delete_matches[$field] = $results[0];
+            }
+         }
+      }
+      else {
+         $delete_matches[$field] = $value;
+      }
+   }
+   // now build the SQL statement
+   $sql = "DELETE FROM {$table} WHERE ";
+   $dargs = array();
+   foreach($delete_matches as $field => $value){
+      if (count($value) > 1) {
+         $sql .= "$field IN (".db_placeholders($value,'varchar').") AND ";
+         foreach ($value as $v) { $dargs[] = $v; }
+      } else {
+         if(strcmp($value,'__NULL__')==0){
+            $sql .= " $field = NULL AND ";
+         } 
+         elseif(strcmp($fields[$field]['type'],'serial')==0 or 
+            strcmp($fields[$field]['type'],'int')==0){
+            $sql .= " $field = %d AND ";
+
+         } 
+         else {
+            $sql .= " $field = '%s' AND ";
+         }
+         array_push($dargs,$value);
+      }
+   }
+   $sql = substr($sql,0,-4);  // get rid of the trailing 'AND'
+   
+   // finally perform the delete.  If successful, return the updated record
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $result = db_query($sql,$dargs);
+   tripal_db_set_active($previous_db);  // now use drupal database 
+   if($result){
+      return true;
+   } 
+   else {
+      watchdog('tripal_core',"Cannot delete record in $table table.  Match:" . print_r($match,1) . ". Values: ". print_r($values,1),array(),'WATCHDOG_ERROR');
+      return false;
+   }
+   return false;
+}
+
+/**
+* Provides a generic routine for updating into any Chado table
+*
+* Use this function to update a record in any Chado table.  The first
+* argument specifies the table for inserting, the second is an array
+* of values to matched for locating the record for updating, and the third 
+* argument give the values to update.  The arrays are mutli-dimensional such 
+* that foreign key lookup values can be specified.  
+*
+* @param $table
+*  The name of the chado table for inserting
+* @param $match
+*  An associative array containing the values for locating a record to update.
+* @param $values
+*  An associative array containing the values for updating.
+*
+* @return
+*  On success this function returns TRUE. On failure, it returns FALSE.
+*
+* Example usage:
+* @code
+   $umatch = array(
+     'organism_id' => array(
+         'genus' => 'Citrus',
+         'species' => 'sinensis',
+      ),
+     'uniquename' => 'orange1.1g000034m.g7',
+     'type_id' => array (
+         'cv_id' => array (
+            'name' => 'sequence',
+         ),
+         'name' => 'gene',
+         'is_obsolete' => 0
+      ),
+   );
+   $uvalues = array(
+      'name' => 'orange1.1g000034m.g',
+      'type_id' => array (
+         'cv_id' => array (
+            'name' => 'sequence',
+         ),
+         'name' => 'mRNA',
+         'is_obsolete' => 0
+      ),
+   );
+*   $result = tripal_core_chado_update('feature',$umatch,$uvalues);
+* @endcode
+* The above code species that a feature with a given uniquename, organism_id,
+* and type_id (the unique constraint for the feature table) will be updated.
+* The organism_id is specified as a nested array that uses the organism_id
+* foreign key constraint to lookup the specified values to find the exact 
+* organism_id. The same nested struture is also used for specifying the 
+* values to update.  The function will find the record that matches the 
+* columns specified and update the record with the avlues in the $uvalues array.
+*
+* @ingroup tripal_chado_api
+*/
+function tripal_core_chado_update($table,$match,$values){
+   $update_values = array();   // contains the values to be updated
+   $update_matches = array();  // contains the values for the where clause
+   
+   // get the table description
+   $table_desc = module_invoke_all('chado_'.$table.'_schema');
+
+   // get the values needed for matching in the SQL statement
+   foreach ($match as $field => $value){
+      if(is_array($value)){
+         $results = tripal_core_chado_get_foreign_key($table_desc,$field,$value);
+         if (sizeof($results) > 1) {
+           watchdog('tripal_core', 'tripal_core_chado_update: When trying to find record to update, too many records match the criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value,TRUE)), WATCHDOG_ERROR);         
+         } elseif (sizeof($results) < 1) {
+           //watchdog('tripal_core', 'tripal_core_chado_update: When trying to find record to update, no record matches criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value,TRUE)), WATCHDOG_ERROR);
+         } else {
+           $update_matches[$field] = $results[0];
+         }
+      }
+      else {
+         $update_matches[$field] = $value;
+      }
+   }
+
+   // get the values used for updating
+   foreach ($values as $field => $value){
+      if(is_array($value)){
+         $results = tripal_core_chado_get_foreign_key($table_desc,$field,$value);
+         if (sizeof($results) > 1) {
+           watchdog('tripal_core', 'tripal_core_chado_update: When trying to find update values, too many records match the criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value,TRUE)), WATCHDOG_ERROR);         
+         } elseif (sizeof($results) < 1) {
+           //watchdog('tripal_core', 'tripal_core_chado_update: When trying to find update values, no record matches criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value,TRUE)), WATCHDOG_ERROR);
+         } else {
+           $update_values[$field] = $results[0];
+         }         
+      }
+      else {
+         $update_values[$field] = $value;
+      }
+   }
+
+   // now build the SQL statement
+   $sql = "UPDATE {$table} SET ";
+   $fields = $table_desc['fields'];
+   $uargs = array();
+   foreach($update_values as $field => $value){
+      if(strcmp($value,'__NULL__')==0){
+         $sql .= " $field = NULL, ";
+      } 
+      elseif(strcmp($fields[$field]['type'],'serial')==0 or 
+         strcmp($fields[$field]['type'],'int')==0){
+         $sql .= " $field = %d, ";
+      } else {
+         $sql .= " $field = '%s', ";
+      }
+      array_push($uargs,$value);
+   }
+   $sql = substr($sql,0,-2);  // get rid of the trailing comma & space
+   $sql .= " WHERE ";
+   foreach($update_matches as $field => $value){
+      if(strcmp($value,'__NULL__')==0){
+         $sql .= " $field = NULL AND ";
+      } 
+      elseif(strcmp($fields[$field]['type'],'serial')==0 or 
+         strcmp($fields[$field]['type'],'int')==0){
+         $sql .= " $field = %d AND ";
+      } 
+      else {
+         $sql .= " $field = '%s' AND ";
+      }
+      array_push($uargs,$value);
+   }
+   $sql = substr($sql,0,-4);  // get rid of the trailing 'AND'
+   
+   // finally perform the update.  If successful, return the updated record
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $result = db_query($sql,$uargs);
+   tripal_db_set_active($previous_db);  // now use drupal database 
+   if($result){
+      return true;
+   } 
+   else {
+      watchdog('tripal_core',"Cannot update record in $table table.  Match:" . print_r($match,1) . ". Values: ". print_r($values,1),array(),'WATCHDOG_ERROR');
+      return false;
+   }
+   return false;
+}
+
+/**
+* Provides a generic routine for selecting data from a Chado table
+*
+* Use this function to perform a simple select from any Chado table.  
+*
+* @param $table
+*  The name of the chado table for inserting
+* @param $columns
+*  An array of column names
+* @param $values
+*  An associative array containing the values for filtering the results. In the 
+*  case where multiple values for the same time are to be selected an additional
+*  entry for the field should appear for each value
+* @param $options
+*  An associative array of additional options where the key is the option 
+*  and the value is the value of that option.
+*
+* Additional Options Include:
+*  - has_record
+*     Set this argument to 'true' to have this function return a numeric 
+*     value for the number of recrods rather than the array of records.  this
+*     can be useful in 'if' statements to check the presence of particula records.
+*  - return_sql
+*     Set this to 'true' to have this function return an array where the first 
+*     element is the sql that would have been run and the second is an array of 
+*     arguments.
+*  - case_insensitive_columns
+*     An array of columns to do a case insensitive search on.
+*  - regex_columns
+*     An array of columns where the value passed in should be treated as a regular expression
+*  - order_by
+*     An associative array containing the column names of the table as keys
+*     and the type of sort (i.e. ASC, DESC) as the values.  The results in the
+*     query will be sorted by the key values in the direction listed by the value
+* 
+* @return
+*  A database query result resource, FALSE if the query was not executed 
+*  correctly, or the number of records in the dataset if $has_record is set.
+*
+* Example usage:
+* @code
+*   $columns = array('feature_id','name');
+*   $values =  array(
+*     'organism_id' => array(
+*         'genus' => 'Citrus',
+*         'species' => array('sinensis','clementina'),
+*      ),
+*     'uniquename' => 'orange1.1g000034m.g',
+*     'type_id' => array (
+*         'cv_id' => array (
+*            'name' => 'sequence',
+*         ),
+*         'name' => 'gene',
+*         'is_obsolete' => 0
+*      ),
+*   );
+*   $result = tripal_core_chado_select('feature',$columns,$values);
+* @endcode
+* The above code selects a record from the feature table using the three fields 
+* that uniquely identify a feature.  The $columns array simply lists the columns
+* to select. The $values array is nested such that the organism is identified by 
+* way of the organism_id foreign key constraint by specifying the genus and 
+* species.  The cvterm is also specified using its foreign key and the cv_id 
+* for the cvterm is nested as well.  In the example above, two different species
+* are allowed to match
+*
+* @ingroup tripal_chado_api
+*/
+function tripal_core_chado_select($table,$columns,$values,$options = null){
+    if (!is_array($options)) { $options = array(); }
+    if (!$options['case_insensitive_columns']) { $options['case_insensitive_columns'] = array(); }
+    if (!$options['regex_columns']) { $options['regex_columns'] = array(); }
+    if (!$options['order_by']) { $options['order_by'] = array(); }
+    
+   if (!is_array($columns)){
+      watchdog('tripal_core', 'the $columns argument for tripal_core_chado_select must be an array.');
+      return false;
+   }
+
+   if (!is_array($values)){
+      watchdog('tripal_core', 'the $values argument for tripal_core_chado_select must be an array.');
+      return false;
+   }
+
+   // get the table description
+   $table_desc = module_invoke_all('chado_'.$table.'_schema');
+
+   $select = '';
+   $from = ''; 
+   $where = '';
+   $args = array();
+   foreach($values as $field => $value){
+      $select[] = $field;
+      if(is_array($value)){
+         // if the user has specified multiple values for matching then this we
+         // want to catch that and save them in our $where array, otherwise
+         // we'll descend for a foreign key relationship
+         if(array_values($value) === $value){
+            $where[$field] = $value;
+         } else {
+            // select the value from the foreign key relationship for this value
+            $foreign_options = array(
+              'regex_columns' => $options['regex_columns'],
+              'case_insensitive_columns' => $options['case_insensitive_columns']
+            );
+            $results = tripal_core_chado_get_foreign_key($table_desc,$field,$value, $foreign_options);
+            if (sizeof($results) < 1) {
+              // foreign key records are required
+              // thus if none matched then return false and alert the admin through watchdog
+              //watchdog('tripal_core', 
+              // 'tripal_core_chado_select: no record in the table referenced by the foreign key (!field)   exists. tripal_core_chado_select table=!table, columns=!columns, values=!values', 
+              // array('!table' => $table, 
+              //   '!columns' => '<pre>' . print_r($columns, TRUE) . '</pre>', 
+              //   '!values' => '<pre>' . print_r($values, TRUE) . '</pre>',
+              //   '!field' => $field,
+              // ), 
+              // WATCHDOG_WARNING);
+              return false;           
+            } else {
+              $where[$field] = $results;
+            }
+         }
+      } 
+      else {
+        //need to catch a 0 and make int if integer field
+        if ($table_desc['fields'][$field]['type'] == 'int') {
+          $where[$field][] = (int) $value;
+        } else {
+          $where[$field][] = $value;
+        }
+      }
+   }
+
+   // now build the SQL select statement
+   if (empty($where)) {
+     // sometimes want to select everything
+     $sql  = "SELECT " . implode(',',$columns) . " ";
+     $sql .= "FROM {$table} ";
+   } else {
+     $sql  = "SELECT " . implode(',',$columns) . " ";
+     $sql .= "FROM {$table} ";
+     $sql .= "WHERE ";
+     foreach($where as $field => $value){
+       if (count($value) > 1) {
+         $sql .= "$field IN (".db_placeholders($value,'varchar').") AND ";
+         foreach ($value as $v) { $args[] = $v; }
+       } else {
+         $operator = '=';
+         if (in_array($field, $options['regex_columns'])) {
+           $operator = '~*';
+         }
+         if (in_array($field, $options['case_insensitive_columns'])) {
+           $sql .= "lower($field) $operator lower('%s') AND ";
+           $args[] = $value[0];
+         }  else {
+           $sql .= "$field $operator '%s' AND ";
+           $args[] = $value[0];
+         }
+       }
+     }
+     $sql = substr($sql,0,-4);  // get rid of the trailing 'AND '
+   }
+   // finally add any ordering of the results to the SQL statement
+   if(count($options['order_by']) > 0){
+      $sql .= " ORDER BY ";
+      foreach($options['order_by'] as $field => $dir){
+         $sql .= "$field $dir, ";
+      }
+      $sql = substr($sql,0,-2);  // get rid of the trailing ', '
+   }
+
+
+   // if the caller has requested the SQL rather than the results...
+   // which happens in the case of wanting to use the Drupal pager, then do so
+   if($options['return_sql']){
+      return array('sql'=> $sql, 'args' => $args);
+   }
+
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $resource = db_query($sql,$args);
+   tripal_db_set_active($previous_db);  // now use drupal database   
+
+   $results = array();
+   while ($r = db_fetch_object($resource)) {
+     $results[] = $r;    
+   }
+   
+   if(!$options['has_record']){
+      return $results;
+   } else{
+      return count($results);
+   }
+}
+
+/**
+* Gets the value of a foreign key relationship
+*
+* This function is used by tripal_core_chado_select, tripal_core_chado_insert,
+* and tripal_core_chado_update to iterate through the associate array of
+* values that gets passed to each of those routines.  The values array
+* is nested where foreign key contraints are used to specify a value that.  See
+* documentation for any of those functions for further information.
+*
+* @param $table_desc
+*  A table description for the table with the foreign key relationship to be identified generated by 
+*  hook_chado_<table name>_schema()
+* @param $field
+*  The field in the table that is the foreign key.
+* @param $values
+*  An associative array containing the values 
+* @param $options
+*  An associative array of additional options where the key is the option 
+*  and the value is the value of that option. These options are passed on to tripal_core_chado_select.
+*
+* Additional Options Include:
+*  - case_insensitive_columns
+*     An array of columns to do a case insensitive search on.
+*  - regex_columns
+*     An array of columns where the value passed in should be treated as a regular expression
+* 
+* @return
+*  A string containg the results of the foreign key lookup, or FALSE if failed.
+*
+* Example usage:
+* @code
+*
+*   $values = array(
+*     'genus' => 'Citrus',
+*     'species' => 'sinensis',
+*   );
+*   $value = tripal_core_chado_get_foreign_key('feature','organism_id',$values);
+*
+* @endcode
+* The above code selects a record from the feature table using the three fields 
+* that uniquely identify a feature.  The $columns array simply lists the columns
+* to select. The $values array is nested such that the organism is identified by 
+* way of the organism_id foreign key constraint by specifying the genus and 
+* species.  The cvterm is also specified using its foreign key and the cv_id 
+* for the cvterm is nested as well.
+*
+* @ingroup tripal_chado_api
+*/
+function tripal_core_chado_get_foreign_key($table_desc,$field,$values, $options = null){
+    if (!is_array($options)) { $options = array(); }
+    if (!$options['case_insensitive_columns']) { $options['case_insensitive_columns'] = array(); }
+    if (!$options['regex_columns']) { $options['regex_columns'] = array(); }
+    
+   // get the list of foreign keys for this table description and
+   // iterate through those until we find the one we're looking for
+   $fkeys = $table_desc['foreign keys'];
+   if($fkeys){
+      foreach($fkeys as $name => $def){
+         if (is_array($def['table'])) {
+           //foreign key was described 2X
+           $message = "The foreign key ".$name." was defined twice. Please check modules to determine if hook_chado_".$table_desc['table']."_schema() was implemented and defined this foreign key when it wasn't supposed to. Modules this hook was implemented in: ".implode(', ', module_implements("chado_".$table_desc['table']."_schema")).".";
+           watchdog('tripal_core', $message);
+           drupal_set_message($message,'error');
+           continue;
+         }
+         $table = $def['table'];
+         $columns = $def['columns'];
+         // iterate through the columns of the foreign key relationship
+         foreach($columns as $left => $right){
+            // does the left column in the relationship match our field?
+            if(strcmp($field,$left)==0){
+               // the column name of the foreign key matches the field we want 
+               // so this is the right relationship.  Now we want to select
+               $select_cols = array($right);
+               $result = tripal_core_chado_select($table,$select_cols,$values, $options);
+               $fields = array();
+               foreach ($result as $obj) {
+                 $fields[] = $obj->$right;
+               }
+               return $fields;
+            }
+         }
+      } 
+   } 
+   else {
+      // TODO: what do we do if we get to this point and we have a fk 
+      // relationship expected but we don't have any definition for one in the
+      // table schema??
+      $message = "There is no foreign key relationship defined for ".$field.". 
+         To define a foreign key relationship, determine the table this foreign 
+         key referrs to (<foreign table>) and then implement 
+         hook_chado_<foreign table>_schema(). See 
+         tripal_feature_chado_feature_schema for an example.";
+      watchdog('tripal_core', $message);
+      drupal_set_message($message,'error');      
+   }
+   return false;
+}
+
+/** 
+ * Generates an object containing the full details of a record(s) in chado. 
+ *
+ * This differs from the objects returned by tripal_core_chado_select in so far as all foreign key 
+ * relationships have been followed meaning you have more complete details. Thus this function 
+ * should be used whenever you need a full variable and tripal_core_chado_select should be used if 
+ * you only case about a few columns.
+ *
+ * @param $table
+ *   The name of the base table to generate a variable for
+ * @param $values
+ *   A select values array that selects the records you want from the base table
+ *   (this has the same form as tripal_core_chado_select)
+ * @param $base_options
+ *   An array containing options for the base table.  For example, an
+ *   option of 'order_by' may be used to sort results in the base table
+ *   if more than one are returned.  The options must be compatible with
+ *   the options accepted by the tripal_core_chado_select() function.
+ * @return
+ *   Either an object (if only one record was selected from the base table) 
+ *   or an array of objects (if more than one record was selected from the base table).
+ *
+ * Example Usage:
+ * @code
+      $values = array(
+        'name' => 'Medtr4g030710'
+      );
+      $features = tripal_core_generate_chado_var('feature', $values);
+ * @endcode
+ * This will return an object if there is only one feature with the name Medtr4g030710 or it will 
+ * return an array of feature objects if more than one feature has that name.
+ *
+ * Note to Module Designers: Fields can be excluded by default from these objects by implementing 
+ * one of the following hooks:
+ *  - hook_exclude_field_from_tablename_by_default (where tablename is the name of the table):
+ *      This hook allows you to add fields to be excluded on a per table basis. Simply implement 
+ *      this hook to return an array of fields to be excluded. For example:  
+ * @code
+          mymodule_exclude_field_from_feature_by_default() {
+            return array('residues' => TRUE);
+          }
+ * @endcode
+ *      will ensure that feature.residues is ecluded from a feature object by default.
+ *  - hook_exclude_type_by_default:
+ *      This hook allows you to exclude fields from all tables that are of a given postgresql field 
+ *      type. Simply implement this hook to return an array of postgresql types mapped to criteria.
+ *      Then all fields of that type where the criteria supplied returns TRUE will be excluded from 
+ *      any table. Tokens available in criteria are &gt;field_value&lt;  and &gt;field_name&lt; . For example:
+ * @code
+          mymodule_exclude_type_by_default() {
+            return array('text' => 'length(&gt;field_value&lt; ) > 50');
+          }
+ * @endcode
+ *      will exclude all text fields with a length > 50. Thus if $feature.residues is longer than 50 *      it will be excluded, otherwise it will be added.
+ *
+ * @ingroup tripal_chado_api
+ */
+ function tripal_core_generate_chado_var($table, $values, $base_options=array()) {
+  
+  // get description for the current table----------------------------------------------------------
+  $table_desc = module_invoke_all('chado_'.$table.'_schema');
+  $table_primary_key = $table_desc['primary key'][0];
+  $table_columns = array_keys($table_desc['fields']);
+
+  // Expandable fields without value needed for criteria--------------------------------------------
+  $all->expandable_fields = array();
+  if ($table_desc['referring_tables']) {
+    $all->expandable_tables = $table_desc['referring_tables'];
+  } else {
+    $all->expandable_tables = array();
+  }
+  $all->expandable_nodes = array();
+    
+  // Get fields to be removed by name.................................
+  $fields_to_remove = module_invoke_all('exclude_field_from_'.$table.'_by_default');
+  foreach ($fields_to_remove as $field_name => $criteria) {
+    //replace &gt;field_name&lt;  with the current field name & 
+    $criteria = preg_replace('/&gt;field_name&lt; /', $field_name, $criteria);
+
+    // if field_value needed we can't deal with this field yet
+    if (preg_match('/&gt;field_value&lt; /', $criteria)) { break; }
+
+    //if criteria then remove from query
+    $success = drupal_eval('<?php return '.$criteria.'; ?>');
+//    watchdog('tripal_core', 
+//      'Evaluating criteria (%criteria) for field %field in tripal_core_generate_chado_var for %table evaluated to %success',
+//      array('%table' => $table, '%criteria'=>$criteria, '%field' => $field_name, '%success'=>$success),
+//      WATCHDOG_NOTICE
+//    );
+    if ($success) {
+      unset($table_columns[array_search($field_name, $table_columns)]);
+      unset($fields_to_remove[$field_name]);
+      $all->expandable_fields[] = $table . '.' . $field_name;
+    }
+  }
+  
+  //Get fields to be removed by type................................
+  $types_to_remove = module_invoke_all('exclude_type_by_default');
+  $field_types = array();
+  foreach ($table_desc['fields'] as $field_name => $field_array) {
+    $field_types[$field_array['type']][] = $field_name; 
+  }
+  foreach ($types_to_remove as $field_type => $criteria) {
+    // if there are fields of that type to remove
+    if (is_array($field_types[$field_type])) {
+      //replace &gt;field_name&lt;  with the current field name & 
+      $criteria = preg_replace('/&gt;field_name&lt; /', $field_name, $criteria);
+      
+      foreach ($field_types[$field_type] as $field_name) {
+        // if field_value needed we can't deal with this field yet
+        if (preg_match('/&gt;field_value&lt; /', $criteria)) { 
+          $fields_to_remove[$field_name] = $criteria;
+          continue; 
+        }
+
+        // if field_value needed we can't deal with this field yet
+        if (preg_match('/&gt;field_value&lt; /', $criteria)) { break; }
+
+        //if criteria then remove from query
+        $success = drupal_eval('<?php return '.$criteria.'; ?>');
+//        watchdog('tripal_core', 
+//          'Evaluating criteria (%criteria) for field %field of $type in tripal_core_generate_chado_var for %table evaluated to %success',
+//          array('%table'=>$table, '%criteria'=>$criteria, '%field'=>$field_name, '%type'=>$field_type, '%success'=>$success),
+//          WATCHDOG_NOTICE
+//        );
+        if ($success) {
+          unset($table_columns[array_search($field_name, $table_columns)]);
+          $all->expandable_fields[] = $table . '.' . $field_name;
+        }
+      } //end of foreach field of that type
+    }
+  } //end of foreach type to be removed
+  
+  // get the values for the record in the current table---------------------------------------------
+  $results = tripal_core_chado_select($table, $table_columns, $values,$base_options);   
+  
+  if($results){
+     foreach ($results as $key => $object) {
+       // Add empty expandable_x arrays
+       $object->expandable_fields = $all->expandable_fields;
+       $object->expandable_tables = $all->expandable_tables;
+       $object->expandable_nodes = $all->expandable_nodes;
+       
+       // add curent table
+       $object->tablename = $table;
+       
+       // check if the current table maps to a node type-----------------------------------------------
+       // if this table is connected to a node there will be a chado_tablename table in drupal
+       if (db_table_exists('chado_'.$table)) {
+         // that has a foreign key to this one ($table_desc['primary key'][0] 
+         // and to the node table (nid)
+         $sql = "SELECT %s, nid FROM chado_%s WHERE %s=%d";
+         $mapping = db_fetch_object(db_query(
+           $sql,
+           $table_primary_key,
+           $table,
+           $table_primary_key, 
+           $object->{$table_primary_key}
+         ));
+         if ($mapping->{$table_primary_key}) {
+           $object->nid = $mapping->nid;
+           $object->expandable_nodes[] = $table;
+         }
+       }
+
+       // remove any fields where criteria need to be evalulated---------------------------------------
+       foreach ($fields_to_remove as $field_name => $criteria) {
+         if (!isset($object->{$field_name})) { break; }
+         $criteria = preg_replace('/&gt;field_value&lt; /', $object->{$field_name}, $criteria);
+         //if criteria then remove from query
+         $success = drupal_eval('<?php return '.$criteria.'; ?>');
+   //      watchdog('tripal_core', 
+   //        'Evaluating criteria (%criteria) for field %field in tripal_core_generate_chado_var for   %table evaluated to %success',
+   //        array('%table' => $table, '%criteria'=>$criteria, '%field' => $field_name, '%success'=>$success),
+   //        WATCHDOG_NOTICE
+   //      );
+         if ($success) {
+           unset($object->{$field_name});
+           $object->expandable_fields[] = $table . '.' . $field_name;
+         }      
+       }
+       
+       // recursively follow foreign key relationships nesting objects as we go------------------------
+       if ($table_desc['foreign keys']) {
+         foreach ($table_desc['foreign keys'] as $foreign_key_array) {
+           $foreign_table = $foreign_key_array['table'];
+           foreach ($foreign_key_array['columns'] as $foreign_key => $primary_key) {
+             // Note: Foreign key is the field in the current table whereas primary_key is the field in 
+             // the table referenced by the foreign key
+             
+             //Dont do anything if the foreign key is empty
+             if (empty($object->{$foreign_key})) {
+               break;
+             }
+             
+             // get the record from the foreign table
+             $foreign_values = array($primary_key => $object->{$foreign_key});
+             $foreign_object = tripal_core_generate_chado_var($foreign_table, $foreign_values);
+       
+             // 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)) {
+               $object->expandable_fields = array_merge(
+                 $object->expandable_fields, 
+                 $object->{$foreign_key}->expandable_fields
+               );
+               unset($object->{$foreign_key}->expandable_fields);
+             }
+             if (is_array($object->{$foreign_key}->expandable_tables)) {
+               $object->expandable_tables = array_merge(
+                 $object->expandable_tables, 
+                 $object->{$foreign_key}->expandable_tables
+               );
+               unset($object->{$foreign_key}->expandable_tables);
+             }
+             if (is_array($object->{$foreign_key}->expandable_nodes)) {
+               $object->expandable_nodes = array_merge(
+                 $object->expandable_nodes, 
+                 $object->{$foreign_key}->expandable_nodes
+               );
+               unset($object->{$foreign_key}->expandable_nodes);
+             }
+           }
+         }    
+         
+         $results[$key] = $object;
+       }
+     }
+  }
+  
+    // check only one result returned
+  if (sizeof($results) == 1) {
+    // add results to object
+    return $results[0];
+  } elseif (!empty($results)) {
+    return $results;
+  } else {
+    // no results returned
+  } 
+  
+}
+
+/**
+ * Retrieves fields/tables/nodes that were excluded by default from a variable and adds them
+ *
+ * This function exists to allow tripal_core_generate_chado_var() to excldue some 
+ * fields/tables/nodes from the default form of a variable without making it extremely difficult for 
+ * the tripal admin to get at these variables if he/she wants them.
+ *
+ * @param $object
+ *   This must be an object generated using tripal_core_generate_chado_var()
+ * @param $type
+ *   Must be one of 'field', 'table', 'node'. Indicates what is being expanded.
+ * @param $to_expand
+ *   The name of the field/table/node to be expanded
+ * @param $table_options
+ *   An array containing options for the base table.  For example, an
+ *   option of 'order_by' may be used to sort results in the base table
+ *   if more than one are returned.  The options must be compatible with
+ *   the options accepted by the tripal_core_chado_select() function.
+ * @return
+ *   A chado object supplemented with the field/table/node requested to be expanded
+ *
+ * Example Usage:
+ * @code
+      // Get a chado object to be expanded
+      $values = array(
+        'name' => 'Medtr4g030710'
+      );
+      $features = tripal_core_generate_chado_var('feature', $values);
+      
+      // Expand the organism node
+      $feature = tripal_core_expand_chado_vars($feature, 'node', 'organism');
+      
+      // Expand the feature.residues field
+      $feature = tripal_core_expand_chado_vars($feature, 'field', 'feature.residues');
+      
+      // Expand the feature properties (featureprop table)
+      $feature = tripal_core_expand_chado_vars($feature, 'table', 'featureprop');
+ * @endcode
+ *
+ * @ingroup tripal_chado_api
+ */
+function tripal_core_expand_chado_vars ($object, $type, $to_expand,$table_options = array()) {
+  $base_table = $object->tablename;
+  
+  // check to see if they are expanding an array of objects
+  if (is_array($object)) {
+    foreach ($object as $index => $o) {
+      $object[$index] = tripal_core_expand_chado_vars($o,$type,$to_expand);
+    }
+    return $object;
+  }
+  
+  
+  switch ($type) {
+    case "field": //--------------------------------------------------------------------------------
+      if (preg_match('/(\w+)\.(\w+)/', $to_expand, $matches)) {
+        $tablename = $matches[1];
+        $fieldname = $matches[2];
+        $table_desc = module_invoke_all('chado_'.$tablename.'_schema');
+
+        $values = array();
+        foreach($table_desc['primary key'] as $key) {
+          $values[$key] = $object->{$key};
+        }
+        
+        if ($base_table == $tablename) {
+          //get the field
+          $results = tripal_core_chado_select(
+            $tablename, 
+            array($fieldname), 
+            $values
+          );
+          $object->{$fieldname} = $results[0]->{$fieldname};
+          $object->expanded = $to_expand;
+        } else {
+          //We need to recurse -the field is in a nested object
+          foreach ((array) $object as $field_name => $field_value) {
+            if (is_object($field_value)) {
+              $object->{$field_name} = tripal_core_expand_chado_vars(
+                $field_value,
+                'field',
+                $to_expand
+              );
+            }
+          } //end of for each field in the current object          
+        }
+      } else {
+        watchdog(
+          'tripal_core',
+          'tripal_core_expand_chado_vars: Field (%field) not in the right format. It should be <tablename>.<fieldname>',
+          WATCHDOG_ERROR
+        );
+      }
+    
+    break;
+    case "table": //--------------------------------------------------------------------------------
+      $foreign_table = $to_expand;
+      $foreign_table_desc = module_invoke_all('chado_'.$foreign_table.'_schema');
+      
+      // If it's connected to the base table
+      if ($foreign_table_desc['foreign keys'][$base_table]) {
+        foreach ($foreign_table_desc['foreign keys'][$base_table]['columns'] as $left => $right) {
+          if (!$object->{$right}) { break; }
+          
+          if (is_array($values)) { 
+            $values = array_merge($values, array($left => $object->{$right}) );
+          } else {
+            $values = array($left => $object->{$right});
+          }
+          $foreign_object = tripal_core_generate_chado_var(
+            $foreign_table,
+            array($left => $object->{$right}),
+            $table_options
+          );
+  
+          if ($foreign_object) {
+            // in the case where the a foreign key relationships exists more 
+            // than once with the same table we want to alter the 
+            // array structure
+            if(count($foreign_table_desc['foreign keys'][$base_table]['columns']) > 1){
+               $object->{$foreign_table}->{$left} = $foreign_object;
+               $object->expanded = $to_expand;
+            } else {
+               $object->{$foreign_table} = $foreign_object;
+               $object->expanded = $to_expand;
+            }
+          }
+        }
+      } else {
+        //We need to recurse -the table has a relationship to one of the nested objects
+        foreach ((array) $object as $field_name => $field_value) {
+          // if we have a nested object ->expand the table in it
+          if (is_object($field_value)) {
+            $object->{$field_name} = tripal_core_expand_chado_vars(
+              $field_value,
+              'table',
+              $foreign_table
+            );
+          }
+        }
+        
+      }
+      
+    break;
+    case "node": //---------------------------------------------------------------------------------
+      //if the node to be expanded is for our base table, then just expand it
+      if ($object->tablename == $to_expand) {
+        $node = node_load($object->nid);
+        if ($node) {
+          $object->expanded = $to_expand;
+          $node->expandable_fields = $object->expandable_fields;
+          unset($object->expandable_fields);
+          $node->expandable_tables = $object->expandable_tables;
+          unset($object->expandable_tables);
+          $node->expandable_nodes = $object->expandable_nodes;
+          unset($object->expandable_nodes);
+          $node->{$base_table} = $object;
+          $object = $node;
+        } else {
+          watchdog(
+            'tripal_core',
+            'tripal_core_expand_chado_vars: No node matches the nid (%nid) supplied.',
+            array('%nid'=>$object->nid),
+            WATCHDOG_ERROR
+          );
+        } //end of if node
+      } else {
+        //We need to recurse -the node to expand is one of the nested objects
+        foreach ((array) $object as $field_name => $field_value) {
+          if (is_object($field_value)) {
+            $object->{$field_name} = tripal_core_expand_chado_vars(
+              $field_value,
+              'node',
+              $to_expand
+            );
+          }
+        } //end of for each field in the current object
+      }
+      
+    break;
+    default:
+     watchdog('tripal_core',
+      'tripal_core_expand_chado_vars: Unrecognized type (%type). Should be one of "field", "table", "node".',
+      array('%type'=>$type),
+      WATCHDOG_ERROR
+    );
+    return FALSE;
+  }
+
+  //move extended array downwards-------------------------------------------------------------------
+  if (!$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)) {
+        if (isset($field_value->expanded)) {
+          $object->expanded = $field_value->expanded;
+          unset($field_value->expanded);
+        }
+      }
+    }  
+  }
+  //try again becasue now we might have moved it down
+  if ($object->expanded) {
+    $expandable_name = 'expandable_'.$type.'s';
+    if ($object->{$expandable_name}) {
+      $key_to_remove = array_search($object->expanded, $object->{$expandable_name});
+      unset($object->{$expandable_name}[$key_to_remove]);
+      unset($object->expanded);
+    } else {
+      // if there is an expandable array then we've reached the base object
+      // if we get here and don't have anything expanded then something went wrong
+//      watchdog(
+//        'tripal_core',
+//        'tripal_core_expand_chado_vars: Unable to expand the %type %to_expand',
+//        array('%type'=>$type, '%to_expand'=>$to_expand),
+//        WATCHDOG_ERROR
+//      );
+    } //end of it we've reached the base object
+  }
+  
+  return $object;
+}
+
+/**
+ * Implements hook_exclude_type_by_default()
+ *
+ * This hooks allows fields of a specified type that match a specified criteria to be excluded by 
+ * default from any table when tripal_core_generate_chado_var() is called. Keep in mind that if
+ * fields are excluded by default they can always be expanded at a later date using 
+ * tripal_core_expand_chado_vars(). 
+ *
+ * Criteria are php strings that evaluate to either TRUE or FALSE. These strings are evaluated using 
+ * drupal_eval() which suppresses syntax errors and throws watchdog entries of type php. There are 
+ * also watchdog entries of type tripal_core stating the exact criteria evaluated. Criteria can 
+ * contain the following tokens:
+ *   - &gt;field_name&lt; 
+ *       Replaced by the name of the field to be excluded
+ *   - &gt;field_value&lt; 
+ *       Replaced by the value of the field in the current record
+ * Also keep in mind that if your criteria doesn't contain the &gt;field_value&lt;  token then it will be 
+ * evaluated before the query is executed and if the field is excluded it won't be included in the 
+ * query.
+ *
+ * @return
+ *   An array of type => criteria where the type is excluded if the criteria evaluates to TRUE
+ *
+ * @ingroup tripal_chado_api
+ */
+function tripal_core_exclude_type_by_default() {
+  return array('text' => "strlen('&gt;field_value&lt; ') > 100");
+}
+
+/**
+ * Implements hook_exclude_field_from_<tablename>_by_default()
+ *
+ * This hooks allows fields from a specified table that match a specified criteria to be excluded by 
+ * default from any table when tripal_core_generate_chado_var() is called. Keep in mind that if
+ * fields are excluded by default they can always be expanded at a later date using 
+ * tripal_core_expand_chado_vars(). 
+ *
+ * Criteria are php strings that evaluate to either TRUE or FALSE. These strings are evaluated using 
+ * drupal_eval() which suppresses syntax errors and throws watchdog entries of type php. There are 
+ * also watchdog entries of type tripal_core stating the exact criteria evaluated. Criteria can 
+ * contain the following tokens:
+ *   - &gt;field_name&lt; 
+ *       Replaced by the name of the field to be excluded
+ *   - &gt;field_value&lt; 
+ *       Replaced by the value of the field in the current record
+ * Also keep in mind that if your criteria doesn't contain the &gt;field_value&lt;  token then it will be 
+ * evaluated before the query is executed and if the field is excluded it won't be included in the 
+ * query.
+ *
+ * @return
+ *   An array of type => criteria where the type is excluded if the criteria evaluates to TRUE
+ *
+ * @ingroup tripal_chado_api
+ */
+function tripal_core_exclude_field_from_feature_by_default() {
+  return array();
+}
+
+/**
+ *  Use this function instead of db_query() to avoid switching databases
+ *  when making query to the chado database
+ */
+function chado_query($sql) {
+	$args = func_get_args();
+	array_shift($args);
+	$sql = db_prefix_tables($sql);
+	if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
+		$args = $args[0];
+	}
+	_db_query_callback($args, TRUE);
+	$sql = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $sql);
+	$previous_db = tripal_db_set_active('chado');
+	$results = _db_query($sql);
+	tripal_db_set_active($previous_db);
+	return $results;
+}
+
+/**
+ * Get chado id for a node. E.g, if you want to get 'analysis_id' from the 
+ * 'analysis' table for a synced 'chado_analysis' node, use:
+ * $analysis_id = chado_get_id_for_node ('analysis', $node)
+ * Likewise,
+ * $organism_id = chado_get_id_for_node ('organism', $node)
+ * $feature_id = chado_get_id_for_node ('feature', $node)
+ */
+function chado_get_id_for_node ($table, $node) {
+	 return db_result(db_query("SELECT $table"."_id FROM {chado_".$table."} WHERE nid = $node->nid"));
+}
+
+/**
+ *  Get node id for a chado feature/organism/analysis. E.g, if you want to
+ *  get the node id for an analysis, use:
+ *  $nid = chado_get_node_id ('analysis', $analysis_id)
+ *  Likewise,
+ *  $nid = chado_get_node_id ('organism', $organism_id)
+ *  $nid = chado_get_node_id ('feature', $feature_id) 
+ */
+function chado_get_node_id ($table, $id) {
+	 return db_result(db_query("SELECT nid FROM {chado_".$table."} WHERE $table"."_id = $id"));
+}
+
+/**
+ * Retrieve a property for a given base table record
+ *
+ * @param $basetable
+ *   The base table for which the property should be retrieved. Thus to retrieve a property
+ *   for a feature the basetable=feature and property is retrieved from featureprop
+ * @param $record_id
+ *   The primary key of the basetable to retrieve properties for. This should be in integer.
+ * @param $property
+ *   The cvterm name describing the type of properties to be retrieved
+ * @param $cv_name
+ *   The name of the cv that the above cvterm is part of
+ *
+ * @return
+ *   A chado variable with the specified properties expanded
+ *
+ * @ingroup tripal_chado_api
+ */
+function tripal_core_get_property($basetable, $record_id, $property, $cv_name){
+
+   // get the foreign key for this property table
+   $table_desc = module_invoke_all('chado_'.$basetable.'prop_schema');
+   $fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
+
+   // construct the array of values to be inserted  
+   $values = array (
+      $fkcol => $record_id,
+      'type_id' => array ( 
+         'cv_id' => array (
+            'name' => $cv_name,
+         ),
+         'name' => $property,
+         'is_obsolete' => 0
+      ),
+   );
+   $results = tripal_core_generate_chado_var($basetable.'prop',$values);
+   $results = tripal_core_expand_chado_vars($results,'field',$basetable.'prop.value');
+   return $results;
+}
+/**
+ * Insert a property for a given basetable record
+ *
+ * @param $basetable
+ *   The base table for which the property should be inserted. Thus to insert a property
+ *   for a feature the basetable=feature and property is inserted into featureprop
+ * @param $record_id
+ *   The primary key of the basetable to insert a property for. This should be in integer.
+ * @param $property
+ *   The cvterm name describing the type of properties to be inserted
+ * @param $cv_name
+ *   The name of the cv that the above cvterm is part of
+ * @param $value
+ *   The value of the property to be inserted (can be empty)
+ * @param $update_if_present
+ *   A boolean indicating whether an existing record should be updated or an error thrown
+ *
+ * @return
+ *   Return True on Insert/Update and False otherwise
+ *
+ * @ingroup tripal_chado_api
+ */
+function tripal_core_insert_property($basetable, $record_id, $property, 
+   $cv_name, $value, $update_if_present = 0)
+{
+   // first see if the property already exists, if so we can't insert
+   $prop = tripal_core_get_property($basetable,$record_id,$property,$cv_name);
+   if(count($prop)>0){ 
+      if($update_if_present){
+        return tripal_core_update_property($basetable,$record_id,$property,$cv_name,$value) ;
+      } else {
+        return FALSE;
+      }
+   }
+
+   // get the foreign key for this property table
+   $table_desc = module_invoke_all('chado_'.$basetable.'prop_schema');
+   $fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
+
+   // construct the array of values to be inserted  
+   $values = array (
+      $fkcol => $record_id,
+      'type_id' => array ( 
+         'cv_id' => array (
+            'name' => $cv_name,
+         ),
+         'name' => $property,
+         'is_obsolete' => 0
+      ),
+      'value' => $value, 
+      'rank' => 0,
+   );
+   return tripal_core_chado_insert($basetable.'prop',$values);
+}
+
+/**
+ * Update a property for a given basetable record
+ *
+ * @param $basetable
+ *   The base table for which the property should be updated. Thus to update a property
+ *   for a feature the basetable=feature and property is updated in featureprop
+ * @param $record_id
+ *   The primary key of the basetable to update a property for. This should be in integer.
+ * @param $property
+ *   The cvterm name describing the type of property to be updated
+ * @param $cv_name
+ *   The name of the cv that the above cvterm is part of
+ * @param $value
+ *   The value of the property to be inserted (can be empty)
+ * @param $insert_if_missing
+ *   A boolean indicating whether a record should be inserted if one doesn't exist to update
+ *
+ * Note: The property to be updated is select via theu nique combination of $record_id and
+ * $property and then it is updated with the supplied value
+ *
+ * @return
+ *   Return True on Update/Insert and False otherwise
+ *
+ * @ingroup tripal_chado_api
+ */
+function tripal_core_update_property($basetable, $record_id,$property,$cv_name,
+   $value,$insert_if_missing = 0)
+{
+
+   // first see if the property is missing (we can't update a missing property
+   $prop = tripal_core_get_property($basetable,$record_id,$property,$cv_name);
+   if(count($prop)==0){
+      if($insert_if_missing){
+        return tripal_core_insert_property($basetable,$record_id,$property,$cv_name,$value);
+      } else {
+        return FALSE;
+      }
+   }
+
+   // get the foreign key for this property table
+   $table_desc = module_invoke_all('chado_'.$basetable.'prop_schema');
+   $fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
+
+   // construct the array that will match the exact record to update
+   $match = array (
+      $fkcol => $record_id,
+      'type_id' => array ( 
+         'cv_id' => array (
+            'name' => $cv_name,
+         ),
+         'name' => $property,
+      ),
+   );
+   // construct the array of values to be updated
+   $values = array (      
+      'value' => $value, 
+   );
+   return tripal_core_chado_update($basetable.'prop',$match,$values);
+}
+
+/**
+ * Deletes a property for a given basetable record
+ *
+ * @param $basetable
+ *   The base table for which the property should be deleted. Thus to deleted a property
+ *   for a feature the basetable=feature and property is deleted from featureprop
+ * @param $record_id
+ *   The primary key of the basetable to delete a property for. This should be in integer.
+ * @param $property
+ *   The cvterm name describing the type of property to be deleted
+ * @param $cv_name
+ *   The name of the cv that the above cvterm is part of
+ *
+ * Note: The property to be deleted is select via theu nique combination of $record_id and $property 
+ *
+ * @return
+ *   Return True on Delete and False otherwise
+ *
+ * @ingroup tripal_chado_api
+ */
+function tripal_core_delete_property($basetable, $record_id,$property,$cv_name){
+   // get the foreign key for this property table
+   $table_desc = module_invoke_all('chado_'.$basetable.'prop_schema');
+   $fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
+
+   // construct the array that will match the exact record to update
+   $match = array (
+      $fkcol => $record_id,
+      'type_id' => array ( 
+         'cv_id' => array (
+            'name' => $cv_name,
+         ),
+         'name' => $property,
+      ),
+   );
+   return tripal_core_chado_delete($basetable.'prop',$match);
+}
+
+/**
+ * This function is typically used in the '.install' file for a Tripal module
+ * Each module should call this function during installation to create
+ * the module data directory which is sites/default/files/tripal/[module_name] 
+ * for default Drupal settings.  This directory can then be used by the module 
+ * for storing files.
+ *
+ * @param $module_name
+ *   the name of the module being installed.
+ *
+ * @returns
+ *   nothing
+ *
+ * @ingroup tripal_files_api
+ */
+function tripal_create_moddir($module_name){
+   // make the data directory for this module
+   $data_dir = file_directory_path() . "/tripal/$module_name";
+   if(!file_check_directory($data_dir,FILE_CREATE_DIRECTORY|FILE_MODIFY_PERMISSIONS)){
+      $message = "Cannot create directory $data_dir. This module may not ".
+                 "behave correctly without this directory.  Please  create ".
+                 "the directory manually or fix the problem and reinstall.";
+      drupal_set_message($message,'error');      
+      watchdog('tripal_core',$message,array(),WATCHDOG_ERROR);
+   }
+}
+
+/**
+ * Each Tripal module has a unique data directory which was creatd using the
+ * tripal_create_moddir function during installation.  This function 
+ * retrieves the directory path.
+ *
+ * @param $module_name
+ *   The name of the module
+ *
+ * @returns
+ *   The path within the Drupal installation where the data directory resides
+ * @ingroup tripal_files_api
+ */
+function tripal_get_moddir($module_name){
+   $data_dir = file_directory_path() . "/tripal/$module_name";
+   return $data_dir;
+}
+/**
+ * Set the Tripal Database
+ *
+ * The tripal_db_set_active function is used to prevent namespace collisions
+ * when chado and drupal are installed in the same database but in different
+ * schemas.  It is also used for backwards compatibility with older versions
+ * of tripal or in cases where chado is located outside of the Drupal database.
+ *
+ * @ingroup tripal_chado_api
+ */
+function tripal_db_set_active($dbname){
+   global $db_url, $db_type;
+   $chado_exists = 0;
+
+   // only postgres can support search paths.  So if this is MysQL then
+   // just run the normal tripal_db_set_active function.
+   if(strcmp($db_type,'pgsql')==0){
+
+      // if the 'chado' database is in the $db_url variable then chado is 
+      // not in the same Drupal database
+      if(is_array($db_url)){ 
+         if(isset($db_url[$dbname])){
+            return db_set_active($dbname);
+         } 
+      }
+
+      // check to make sure the chado schema exists
+      $sql = "select nspname from pg_catalog.pg_namespace where nspname = 'chado'";
+      if(db_fetch_object(db_query($sql))){
+         $chado_exists = 1;
+      }
+
+      // here we make the assumption that the default database schema is
+      // 'public'.  This will most likely always be the case but if not,
+      // then this code will break
+      if($chado_exists && strcmp($dbname,'chado')==0){
+         db_query("set search_path to %s",'chado,public');  
+         return 'public,chado';
+      } 
+      elseif($chado_exists) {
+         db_query("set search_path to %s",'public,chado');  
+         return 'chado,public';
+      }
+      else {
+         return db_set_active($dbname);
+      }
+   }
+   else return db_set_active($dbname);
+}
+/**
+ * Purpose: Get max rank for a given set of criteria
+ *   This function was developed with the many property tables in chado in mind
+ *
+ * @param $tablename
+ *    The name of the chado table you want to select the max rank from this table must contain a 
+ *    rank column of type integer
+ * @param $where_options
+ *   where options should include the id and type for that table to correctly
+ *     group a set of records together where the only difference are the value and rank
+ * @code 
+ *  array(
+ *     <column_name> => array(
+ *	      'type' => <type of column: INT/STRING>,
+ *		   'value' => <the value you want to filter on>,
+ *			'exact' => <if TRUE use =; if FALSE use ~>,
+ *		)
+ *	)
+ * @endcode
+ * @return the maximum rank
+ *
+ * @ingroup tripal_chado_api
+ */
+function tripal_get_max_chado_rank ($tablename, $where_options) {
+
+	$where= array();
+	//generate the where clause from supplied options
+	// the key is the column name
+	foreach ($where_options as $key => $val_array) {
+		if (preg_match('/INT/', $val_array['type'])) {
+			$where[] = $key."=".$val_array['value'];
+		} else {
+			if ($val_array['exact']) { $operator='='; }
+			else { $operator='~'; }
+			$where[] = $key.$operator."'".$val_array['value']."'";
+		}
+	}
+	
+  $previous_db = tripal_db_set_active('chado');
+  $result = db_fetch_object(db_query(
+    "SELECT max(rank) as max_rank, count(rank) as count FROM %s WHERE %s",
+    $tablename,
+    implode(' AND ',$where)
+  ));
+  tripal_db_set_active($previous_db);
+	//drupal_set_message("Max Rank Query=SELECT max(rank) as max_rank, count(rank) as count FROM ".$tablename." WHERE ".implode(' AND ',$where));
+	if ($result->count > 0) {
+	  return $result->max_rank;
+	} else {
+		return -1;
+	}
+}

+ 7 - 0
modules/base/tripal_core/tripal_core.info

@@ -0,0 +1,7 @@
+; $Id: tripal_core.info,v 1.3 2009/10/23 02:12:34 ccheng Exp $
+name = Tripal Core
+description = The core module for the Tripal package that integrates Drupal and GMOD chado. This module provides common support for all Tripal modules.
+core = 6.x
+project = tripal_core
+package = Tripal
+version = 6.x-0.3.1b

+ 105 - 0
modules/base/tripal_core/tripal_core.install

@@ -0,0 +1,105 @@
+<?php
+
+/************************************************************************
+*  Implementation of hook_install();
+* 
+* @ingroup tripal_core
+*/
+function tripal_core_install(){
+
+   // make the data directory for this module
+   $data_dir = file_directory_path() . "/tripal";
+   if(!file_check_directory($data_dir,FILE_CREATE_DIRECTORY)){
+      $message = "Cannot create directory $data_dir. This module may not ".
+                 "behave correctly without this directory.  Please  create ".
+                 "the directory manually or fix the problem and reinstall.";
+      drupal_set_message($message,'error');      
+      watchdog('tripal_core',$message,array(),WATCHDOG_ERROR);
+   }
+
+  // create the tables that manage materialized views and jobs
+  drupal_install_schema('tripal_core');
+
+}
+
+/************************************************************************
+* Implementation of hook_schema().
+*
+* @ingroup tripal_core
+*/
+function tripal_core_schema() {
+   $schema = tripal_core_get_schemas();
+   return $schema;
+}
+/************************************************************************
+* Implementation of hook_uninstall()
+*
+* @ingroup tripal_core
+*/
+function tripal_core_uninstall(){
+   drupal_uninstall_schema('tripal_core');
+}
+
+/************************************************************************
+* This function simply defines all tables needed for the module to work
+* correctly.  By putting the table definitions in a separate function we
+* can easily provide the entire list for hook_install or individual
+* tables for an update.
+*
+* @ingroup tripal_core
+*/
+function tripal_core_get_schemas (){  
+  $schema = array();
+
+
+  $schema['tripal_jobs'] = array(
+      'fields' => array(
+         'job_id' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
+         'uid' => array ('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'description' => 'The Drupal userid of the submitee'),
+         'job_name' => array('type' => 'varchar','length' => 255, 'not null' => TRUE),
+         'modulename' => array('type' => 'varchar','length' => 50, 'not null' => TRUE, 'description' => 'The module name that provides the callback for this job'),
+         'callback' => array('type' => 'varchar','length' => 255, 'not null' => TRUE),
+         'arguments' => array('type' => 'text', 'size' => 'normal', 'not null' => FALSE),
+         'progress' => array('type' => 'int', 'unsigned' => TRUE, 'default' => 0, 'not null' => FALSE, 'description' => 'a value from 0 to 100 indicating percent complete'),
+         'status' => array('type' => 'varchar','length' => 50, 'not null' => TRUE),
+         'submit_date' => array ('type' => 'int', 'not null' => TRUE, 'description' => 'UNIX integer submit time'),
+         'start_time' => array ('type' => 'int', 'not null' => FALSE, 'description' => 'UNIX integer start time'),
+         'end_time' => array ('type' => 'int', 'not null' => FALSE, 'description' => 'UNIX integer end time'),
+         'error_msg' => array('type' => 'text','size' => 'normal', 'not null' => FALSE),
+         'pid' => array ('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'description' => 'The process id for the job'),
+         'priority' => array ('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => '0', 'description' => 'The job priority'),
+         'mlock' => array ('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'description' => 'If set to 1 then all jobs for the module are held until this one finishes'),
+         'lock' => array ('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'description' => 'If set to 1 then all jobs are held until this one finishes'),
+      ),
+      'indexes' => array(
+         'job_id' => array('job_id'),
+         'job_name' => array('job_name')
+      ),
+      'primary key' => array('job_id'),
+  );
+
+  $schema['tripal_mviews'] = array(
+      'fields' => array(
+         'mview_id' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
+         'name' => array('type' => 'varchar','length' => 255, 'not null' => TRUE),
+         'modulename' => array('type' => 'varchar','length' => 50, 'not null' => TRUE, 'description' => 'The module name that provides the callback for this job'),
+         'mv_table' => array('type' => 'varchar','length' => 128, 'not null' => TRUE),
+         'mv_specs' => array('type' => 'text', 'size' => 'normal', 'not null' => TRUE),
+         'indexed' => array('type' => 'text', 'size' => 'normal', 'not null' => TRUE),
+         'query' => array('type' => 'text', 'size' => 'normal', 'not null' => TRUE),
+         'special_index' => array('type' => 'text', 'size' => 'normal', 'not null' => FALSE),
+         'last_update' => array ('type' => 'int', 'not null' => FALSE, 'description' => 'UNIX integer time'),
+      ),
+      'indexes' => array(
+         'mview_id' => array('mview_id')
+      ),
+      'unique keys' => array(
+         'mv_table' => array('mv_table'),
+         'mv_name' => array('name'),
+      ),
+      'primary key' => array('mview_id'),
+  );
+
+  return $schema;
+}
+?>

+ 260 - 0
modules/base/tripal_core/tripal_core.module

@@ -0,0 +1,260 @@
+<?php
+
+require_once "jobs.php";
+require_once "mviews.php";
+require_once "cvterms.php";
+require_once "chado_install.php";
+
+require_once "tripal_core.api.inc";
+
+/**
+ * @defgroup tripal_modules Tripal Modules
+ * @{
+ * All documented functions for the various Tripal Modules
+ * @}
+ */
+
+/**
+ * @defgroup tripal_core Core Tripal Module
+ * @ingroup tripal_modules
+ */
+ 
+/**
+ *
+ *
+ * @ingroup tripal_core
+ */
+function tripal_core_init(){
+   // the two lines below are necessary to ensure that the search_path
+   // variable is always set.  In the case where a view needs to query the
+   // chado schema when it is local to the Drupal database.  Otherwise the
+   // search_path isn't set.  When tripal_db_set_active is called it
+   // automatically sets the serach path if chado is local to the 
+   // Drupal database
+   $previous = tripal_db_set_active('chado');
+   tripal_db_set_active($previous);
+
+   // create the 'tripal' controlled volcabulary in chado but only if it doesn't already exist, and
+   // only if the chado database is present.
+   if(tripal_core_is_chado_installed()){
+      $previous_db = tripal_db_set_active('chado');  // use chado database
+      if(!db_fetch_object(db_query("SELECT * FROM {cv} WHERE name = 'tripal'"))){
+         $results = db_query("INSERT INTO {cv} (name,definition) ".
+    	                     "VALUES ('tripal','Terms used by Tripal for modules to manage data such as that stored in property tables like featureprop, analysisprop, etc')");
+      } 
+      if(!db_fetch_object(db_query("SELECT * FROM {db} WHERE name = 'tripal'"))){
+         $results = db_query("INSERT INTO {db} (name,description) ". 
+	                        "VALUES ('tripal','Used as a database placeholder for tripal defined objects such as tripal cvterms')");
+      }  
+      tripal_db_set_active($previous_db);  // now use drupal database
+   }
+
+   // add some variables for all javasript to use for building URLs
+   global $base_url;
+   $theme_dir = drupal_get_path('theme', 'tripal');
+   $clean_urls  = variable_get('clean_url', 0);
+   drupal_add_js("
+      var baseurl = '$base_url'; 
+      var themedir = '$theme_dir'; 
+      var isClean = $clean_urls;",'inline');
+
+   // make sure the date time settings are the way Tripal will insert them
+   // otherwise PostgreSQL version that may have a different datestyle setting
+   // will fail when inserting or updating a date column in a table.
+   db_query("SET DATESTYLE TO '%s'",'MDY');
+}
+
+
+
+/**
+ *
+ *
+ * @ingroup tripal_core
+ */
+function tripal_core_menu() {
+   $items = array();
+   
+   // Triapl setting groups
+   $items['admin/tripal'] = array(
+      'title' => 'Tripal Management',
+      'description' => "Manage the behavior or Tripal and its various modules.",
+      'position' => 'right',
+      'weight' => -5,
+      'page callback' => 'system_admin_menu_block_page',
+      'access arguments' => array('administer site configuration'),
+      'file' => 'system.admin.inc',
+      'file path' => drupal_get_path('module', 'system'),
+   );
+
+   $items['admin/tripal/tripal_jobs'] = array(
+     'title' => 'Jobs',
+     'description' => 'Jobs managed by Tripal',
+     'page callback' => 'tripal_jobs_report',
+     'access arguments' => array('access administration pages'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+   $items['admin/tripal/tripal_jobs/cancel/%'] = array(
+     'title' => 'Jobs',
+     'description' => 'Cancel a pending job',
+     'page callback' => 'tripal_jobs_cancel',
+     'page arguments' => array(4),
+     'access arguments' => array('access administration pages'),
+     'type' => MENU_CALLBACK,
+   );
+   $items['admin/tripal/tripal_jobs/rerun/%'] = array(
+     'title' => 'Jobs',
+     'description' => 'Re-run an existing job.',
+     'page callback' => 'tripal_jobs_rerun',
+     'page arguments' => array(4),
+     'access arguments' => array('access administration pages'),
+     'type' => MENU_CALLBACK,
+   );
+   $items['admin/tripal/tripal_jobs/view/%'] = array(
+     'title' => 'Jobs Details',
+     'description' => 'View job details.',
+     'page callback' => 'tripal_jobs_view',
+     'page arguments' => array(4),
+     'access arguments' => array('access administration pages'),
+     'type' => MENU_CALLBACK,
+   );
+   $items['admin/tripal/tripal_mview/%'] = array(
+     'title' => 'Materialized View',
+     'description' => 'Materialized views are used to improve speed of large or complex queries.',
+     'page callback' => 'tripal_mview_report',
+     'page arguments' => array(3),
+     'access arguments' => array('access administration pages'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+   $items['admin/tripal/tripal_mviews'] = array(
+     'title' => 'Materialized Views',
+     'description' => 'Materialized views are used to improve speed of large or complex queries.',
+     'page callback' => 'tripal_mviews_report',
+     'access arguments' => array('access administration pages'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+   $items['admin/tripal/tripal_mviews/new'] = array(
+     'title' => 'Create View',
+     'description' => 'Materialized views are used to improve speed of large or complex queries.',
+     'page callback' => 'drupal_get_form',
+     'page arguments' => array('tripal_mviews_form'),
+     'access arguments' => array('access administration pages'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+   $items['admin/tripal/tripal_mviews/edit/%'] = array(
+     'title' => 'Edit View',
+     'page callback' => 'drupal_get_form',
+     'page arguments' => array('tripal_mviews_form',4),
+     'access arguments' => array('access administration pages'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+   $items['admin/tripal/tripal_mviews/action/%/%'] = array(
+     'title' => 'Create View',
+     'description' => 'Materialized views are used to improve speed of large or complex queries.',
+     'page callback' => 'tripal_mviews_action',
+     'page arguments' => array(4,5),
+     'access arguments' => array('access administration pages'),
+     'type' => MENU_CALLBACK,
+   );
+
+   $items['tripal_toggle_box_menu/%/%/%'] = array(
+     'title' => t('Libraries'),
+     'page callback' => 'tripal_toggle_box_menu',
+     'page arguments' => array(1,2,3),
+     'access arguments' => array('access administration pages'),
+     'type' => MENU_CALLBACK | MENU_LINKS_TO_PARENT 
+   );
+   $items['admin/tripal/chado_1_11_install'] = array(
+     'title' => 'Install Chado v1.11',
+     'description' => 'Installs Chado version 1.11 inside the current Drupal database',
+     'page callback' => 'drupal_get_form',
+     'page arguments' => array('tripal_core_chado_v1_11_load_form'),
+     'access arguments' => array('access administration pages'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+
+  return $items;
+}
+
+
+
+/**
+ *
+ *
+ * @param $dbname
+ *   The name of the database to switch to as indicated in settings.php
+ *   Should be either default or chado
+ *
+ * @return 
+ *   The name of the previously set database
+ *
+ * @ingroup tripal_chado_api
+ */
+function tripal_core_is_chado_installed(){
+   global $db_url, $db_type;
+
+   // first check if chado is in the db_url of the 
+   // settings.php file
+   if(is_array($db_url)){ 
+      if(isset($db_url['chado'])){
+         return true;
+      } 
+   }
+   // check to make sure the chado schema exists
+   $sql = "select nspname from pg_catalog.pg_namespace where nspname = 'chado'";
+   if(db_fetch_object(db_query($sql))){
+      return true;
+   }
+   return false;
+}
+
+/**
+ * Implements hook_views_api()
+ *
+ * Purpose: Essentially this hook tells drupal that there is views support for
+ *  for this module which then includes tripal_core.views.inc where all the
+ *  views integration code is
+ *
+ * @ingroup tripal_core
+ */ 
+function tripal_core_views_api() {
+   return array(
+      'api' => 2.0,
+   );
+}
+
+
+/**
+ *
+ * @ingroup tripal_core
+ */
+function tripal_core_theme () {
+   return array(      
+      'tripal_core_job_view' => array (
+         'arguments' => array('job_id'=> null),
+         'template' => 'tripal_core_job_view',
+      ),
+   );
+}
+/**
+ *
+ * @ingroup tripal_core
+ */
+function tripal_core_job_describe_args($callback,$args){
+   $new_args = array();
+   if($callback == 'tripal_update_mview'){
+      // get this mview details
+      $sql = "SELECT * FROM {tripal_mviews} WHERE mview_id = %d ";
+      $mview = db_fetch_object(db_query($sql,$args[0]));     
+      $new_args['View Name'] = $mview->name;
+   }
+   return $new_args;
+}
+
+// this is just a wrapper for backwards compatibility with a naming mistake.
+// it can go away in the future as it only is useful for jobs created by v0.3b 
+function tripal_core_load_gff3($gff_file, $organism_id,$analysis_id,$add_only =0, 
+   $update = 0, $refresh = 0, $remove = 0, $job = NULL)
+{
+   tripal_feature_load_gff3($gff_file, $organism_id,$analysis_id,$add_only, 
+      $update, $refresh, $remove, $job);
+}

+ 12723 - 0
modules/base/tripal_core/tripal_core.schema.api.inc

@@ -0,0 +1,12723 @@
+<?php
+
+/* @file: This file contains default schema definitions for all chado tables
+ *        to be used with tripal_core chado insert/update/select functions.
+ *        These schema definitions can be augmented by the various modules
+ *        (specifically to add missing foreign key definitions) by implementing
+ *        hook_chado_<table name>_schema().
+ */
+
+/** 
+ * @defgroup tripal_schema_api Core Module Schema API
+ * @{
+ * Provides an application programming interface (API) for describing Chado tables.
+ * This API consists of a set of functions, one for each table in Chado.  Each 
+ * function simply returns a Drupal style array that defines the table. 
+ * 
+ * Because Drupal 6 does not handle foreign key relationships, a separate 
+ * hook is used to define the foreign key relationships for each table.  As
+ * of version 0.3b of Tripal, not all foreign key relationships have been 
+ * defined.  Therefore, if you need a foreign key you must create the appropriate
+ * hook.  Hooks are named hook_chado_table_schema() where 'hook' is replaced
+ * with the module name, and 'table' is replaced by the Chado table name.
+ *
+ * The functions provided in this documentation should not be called as is, but if you need
+ * the Drupal-style array definition for any table, use the following function
+ * call:
+ *
+ *   $table_desc = module_invoke_all('chado_'.$table.'_schema');
+ * 
+ * where the variable $table contains the name of the table you want to 
+ * retireve.  The function call above uses the Drupal hook infrastructure to
+ * call the appropriate function in the Schema API as well as the appropriate
+ * hook to add in any foreign key relationships. 
+ * 
+ * @}
+ * @ingroup tripal_api
+ */
+
+/**
+ *
+ * @ingroup tripal_schema_api
+ */
+function tripal_core_get_chado_tables() {
+  if(is_array($db_url) and array_key_exists('chado',$db_url)){
+    $previous_db = tripal_db_set_active('chado');
+    $sql = 'SELECT tablename FROM pg_tables';
+    $resource = db_query($sql);
+    tripal_db_set_active($previous_db);
+  } else {
+    $sql = "SELECT tablename FROM pg_tables WHERE schemaname='chado'";
+    $resource = db_query($sql);
+  }
+  
+  $tables = array();
+  while ($r = db_fetch_object($resource)) {
+    $tables[$r->tablename] = $r->tablename;
+  }
+  asort($tables);
+  return $tables;
+}
+ 
+/**
+ * Implements hook_organism_dbxref_schema()
+ * Purpose: To describe the structure of organism_dbxref to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+ */
+function tripal_core_chado_organism_dbxref_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'organism_dbxref',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'organism_dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'organism_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'organism_dbxref_id',
+    ),
+    'unique keys' => array(
+      'organism_dbxref_c1' => array(
+        '0' => 'organism_id',
+        '1' => 'dbxref_id',
+      ),
+    ),
+    'indexes' => array(
+      'organism_dbxref_idx1' => array(
+        '0' => 'organism_id',
+      ),
+      'organism_dbxref_idx2' => array(
+        '0' => 'dbxref_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_organismprop_schema()
+ * Purpose: To describe the structure of organismprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_organismprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'organismprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'organismprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'organism_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'organismprop_id',
+    ),
+    'unique keys' => array(
+      'organismprop_c1' => array(
+        '0' => 'organism_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'organismprop_idx1' => array(
+        '0' => 'organism_id',
+      ),
+      'organismprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_phendesc_schema()
+ * Purpose: To describe the structure of phendesc to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_phendesc_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'phendesc',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'phendesc_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'genotype_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'environment_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'description' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'phendesc_id',
+    ),
+    'unique keys' => array(
+      'phendesc_c1' => array(
+        '0' => 'genotype_id',
+        '1' => 'environment_id',
+        '2' => 'type_id',
+        '3' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'phendesc_idx1' => array(
+        '0' => 'genotype_id',
+      ),
+      'phendesc_idx2' => array(
+        '0' => 'environment_id',
+      ),
+      'phendesc_idx3' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_phenotype_schema()
+ * Purpose: To describe the structure of phenotype to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_phenotype_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'phenotype',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'phenotype_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'uniquename' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+      'observable_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'attr_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'cvalue_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'assay_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'phenotype_id',
+    ),
+    'unique keys' => array(
+      'phenotype_c1' => array(
+        '0' => 'uniquename',
+      ),
+    ),
+    'indexes' => array(
+      'phenotype_idx1' => array(
+        '0' => 'cvalue_id',
+      ),
+      'phenotype_idx2' => array(
+        '0' => 'observable_id',
+      ),
+      'phenotype_idx3' => array(
+        '0' => 'attr_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_organism_schema()
+ * Purpose: To describe the structure of organism to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_organism_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'organism',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'organism_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'abbreviation' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'genus' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '1',
+      ),
+      'species' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '1',
+      ),
+      'common_name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'comment' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'organism_id',
+    ),
+    'unique keys' => array(
+      'organism_c1' => array(
+        '0' => 'genus',
+        '1' => 'species',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_materialized_view_schema()
+ * Purpose: To describe the structure of materialized_view to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_materialized_view_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'materialized_view',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'materialized_view_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'last_update' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'datetime',
+        'not null' => '',
+      ),
+      'refresh_time' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '64',
+        'not null' => '',
+      ),
+      'mv_schema' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '64',
+        'not null' => '',
+      ),
+      'mv_table' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '128',
+        'not null' => '',
+      ),
+      'mv_specs' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'indexed' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'query' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'special_index' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+      'name' => array(
+        '0' => 'name',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_libraryprop_schema()
+ * Purpose: To describe the structure of libraryprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_libraryprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'libraryprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'libraryprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'library_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'libraryprop_id',
+    ),
+    'unique keys' => array(
+      'libraryprop_c1' => array(
+        '0' => 'library_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'libraryprop_idx1' => array(
+        '0' => 'library_id',
+      ),
+      'libraryprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_libraryprop_pub_schema()
+ * Purpose: To describe the structure of libraryprop_pub to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_libraryprop_pub_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'libraryprop_pub',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'libraryprop_pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'libraryprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'libraryprop_pub_id',
+    ),
+    'unique keys' => array(
+      'libraryprop_pub_c1' => array(
+        '0' => 'libraryprop_id',
+        '1' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'libraryprop_pub_idx1' => array(
+        '0' => 'libraryprop_id',
+      ),
+      'libraryprop_pub_idx2' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_magedocumentation_schema()
+ * Purpose: To describe the structure of magedocumentation to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_magedocumentation_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'magedocumentation',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'magedocumentation_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'mageml_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'tableinfo_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'row_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'mageidentifier' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'magedocumentation_id',
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+      'magedocumentation_idx1' => array(
+        '0' => 'mageml_id',
+      ),
+      'magedocumentation_idx2' => array(
+        '0' => 'tableinfo_id',
+      ),
+      'magedocumentation_idx3' => array(
+        '0' => 'row_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_mageml_schema()
+ * Purpose: To describe the structure of mageml to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_mageml_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'mageml',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'mageml_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'mage_package' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+      'mage_ml' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'mageml_id',
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_phenotype_comparison_schema()
+ * Purpose: To describe the structure of phenotype_comparison to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_phenotype_comparison_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'phenotype_comparison',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'phenotype_comparison_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'genotype1_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'environment1_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'genotype2_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'environment2_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'phenotype1_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'phenotype2_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'organism_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'phenotype_comparison_id',
+    ),
+    'unique keys' => array(
+      'phenotype_comparison_c1' => array(
+        '0' => 'genotype1_id',
+        '1' => 'environment1_id',
+        '2' => 'genotype2_id',
+        '3' => 'environment2_id',
+        '4' => 'phenotype1_id',
+        '5' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'phenotype_comparison_idx1' => array(
+        '0' => 'genotype1_id',
+      ),
+      'phenotype_comparison_idx2' => array(
+        '0' => 'genotype2_id',
+      ),
+      'phenotype_comparison_idx4' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_phenotype_comparison_cvterm_schema()
+ * Purpose: To describe the structure of phenotype_comparison_cvterm to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_phenotype_comparison_cvterm_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'phenotype_comparison_cvterm',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'phenotype_comparison_cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'phenotype_comparison_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'phenotype_comparison_cvterm_id',
+    ),
+    'unique keys' => array(
+      'phenotype_comparison_cvterm_c1' => array(
+        '0' => 'phenotype_comparison_id',
+        '1' => 'cvterm_id',
+      ),
+    ),
+    'indexes' => array(
+      'phenotype_comparison_cvterm_idx1' => array(
+        '0' => 'phenotype_comparison_id',
+      ),
+      'phenotype_comparison_cvterm_idx2' => array(
+        '0' => 'cvterm_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_phylonode_relationship_schema()
+ * Purpose: To describe the structure of phylonode_relationship to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_phylonode_relationship_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'phylonode_relationship',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'phylonode_relationship_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'phylotree_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'phylonode_relationship_id',
+    ),
+    'unique keys' => array(
+      'subject_id' => array(
+        '0' => 'subject_id',
+        '1' => 'object_id',
+        '2' => 'type_id',
+      ),
+    ),
+    'indexes' => array(
+      'phylonode_relationship_idx1' => array(
+        '0' => 'subject_id',
+      ),
+      'phylonode_relationship_idx2' => array(
+        '0' => 'object_id',
+      ),
+      'phylonode_relationship_idx3' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_phylonodeprop_schema()
+ * Purpose: To describe the structure of phylonodeprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_phylonodeprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'phylonodeprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'phylonodeprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'phylonode_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+        'default' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'phylonodeprop_id',
+    ),
+    'unique keys' => array(
+      'phylonode_id' => array(
+        '0' => 'phylonode_id',
+        '1' => 'type_id',
+        '2' => 'value',
+        '3' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'phylonodeprop_idx1' => array(
+        '0' => 'phylonode_id',
+      ),
+      'phylonodeprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_phylotree_schema()
+ * Purpose: To describe the structure of phylotree to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_phylotree_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'phylotree',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'phylotree_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'analysis_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'comment' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'phylotree_id',
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+      'phylotree_idx1' => array(
+        '0' => 'phylotree_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_phylotree_pub_schema()
+ * Purpose: To describe the structure of phylotree_pub to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_phylotree_pub_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'phylotree_pub',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'phylotree_pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'phylotree_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'phylotree_pub_id',
+    ),
+    'unique keys' => array(
+      'phylotree_id' => array(
+        '0' => 'phylotree_id',
+        '1' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'phylotree_pub_idx1' => array(
+        '0' => 'phylotree_id',
+      ),
+      'phylotree_pub_idx2' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_phylonode_pub_schema()
+ * Purpose: To describe the structure of phylonode_pub to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_phylonode_pub_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'phylonode_pub',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'phylonode_pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'phylonode_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'phylonode_pub_id',
+    ),
+    'unique keys' => array(
+      'phylonode_id' => array(
+        '0' => 'phylonode_id',
+        '1' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'phylonode_pub_idx1' => array(
+        '0' => 'phylonode_id',
+      ),
+      'phylonode_pub_idx2' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_phylonode_organism_schema()
+ * Purpose: To describe the structure of phylonode_organism to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_phylonode_organism_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'phylonode_organism',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'phylonode_organism_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'phylonode_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'organism_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'phylonode_organism_id',
+    ),
+    'unique keys' => array(
+      'phylonode_id' => array(
+        '0' => 'phylonode_id',
+      ),
+    ),
+    'indexes' => array(
+      'phylonode_organism_idx1' => array(
+        '0' => 'phylonode_id',
+      ),
+      'phylonode_organism_idx2' => array(
+        '0' => 'organism_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_phenotype_cvterm_schema()
+ * Purpose: To describe the structure of phenotype_cvterm to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_phenotype_cvterm_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'phenotype_cvterm',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'phenotype_cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'phenotype_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'phenotype_cvterm_id',
+    ),
+    'unique keys' => array(
+      'phenotype_cvterm_c1' => array(
+        '0' => 'phenotype_id',
+        '1' => 'cvterm_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'phenotype_cvterm_idx1' => array(
+        '0' => 'phenotype_id',
+      ),
+      'phenotype_cvterm_idx2' => array(
+        '0' => 'cvterm_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_phenstatement_schema()
+ * Purpose: To describe the structure of phenstatement to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_phenstatement_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'phenstatement',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'phenstatement_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'genotype_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'environment_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'phenotype_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'phenstatement_id',
+    ),
+    'unique keys' => array(
+      'phenstatement_c1' => array(
+        '0' => 'genotype_id',
+        '1' => 'phenotype_id',
+        '2' => 'environment_id',
+        '3' => 'type_id',
+        '4' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'phenstatement_idx1' => array(
+        '0' => 'genotype_id',
+      ),
+      'phenstatement_idx2' => array(
+        '0' => 'phenotype_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_phylonode_schema()
+ * Purpose: To describe the structure of phylonode to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_phylonode_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'phylonode',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'phylonode_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'phylotree_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'parent_phylonode_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'left_idx' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'right_idx' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'label' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'distance' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'float',
+        'size' => 'big',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'phylonode_id',
+    ),
+    'unique keys' => array(
+      'phylotree_id' => array(
+        '0' => 'phylotree_id',
+        '1' => 'left_idx',
+      ),
+      'phylonode_phylotree_id_key1' => array(
+        '0' => 'phylotree_id',
+        '1' => 'right_idx',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_phylonode_dbxref_schema()
+ * Purpose: To describe the structure of phylonode_dbxref to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_phylonode_dbxref_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'phylonode_dbxref',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'phylonode_dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'phylonode_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'phylonode_dbxref_id',
+    ),
+    'unique keys' => array(
+      'phylonode_id' => array(
+        '0' => 'phylonode_id',
+        '1' => 'dbxref_id',
+      ),
+    ),
+    'indexes' => array(
+      'phylonode_dbxref_idx1' => array(
+        '0' => 'phylonode_id',
+      ),
+      'phylonode_dbxref_idx2' => array(
+        '0' => 'dbxref_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_library_synonym_schema()
+ * Purpose: To describe the structure of library_synonym to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_library_synonym_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'library_synonym',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'library_synonym_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'synonym_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'library_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'is_current' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '1',
+        'default' => 'ru',
+      ),
+      'is_internal' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '1',
+        'default' => 'als',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'library_synonym_id',
+    ),
+    'unique keys' => array(
+      'library_synonym_c1' => array(
+        '0' => 'synonym_id',
+        '1' => 'library_id',
+        '2' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'library_synonym_idx1' => array(
+        '0' => 'synonym_id',
+      ),
+      'library_synonym_idx2' => array(
+        '0' => 'library_id',
+      ),
+      'library_synonym_idx3' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_library_pub_schema()
+ * Purpose: To describe the structure of library_pub to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_library_pub_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'library_pub',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'library_pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'library_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'library_pub_id',
+    ),
+    'unique keys' => array(
+      'library_pub_c1' => array(
+        '0' => 'library_id',
+        '1' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'library_pub_idx1' => array(
+        '0' => 'library_id',
+      ),
+      'library_pub_idx2' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_featureprop_pub_schema()
+ * Purpose: To describe the structure of featureprop_pub to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_featureprop_pub_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'featureprop_pub',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'featureprop_pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'featureprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'featureprop_pub_id',
+    ),
+    'unique keys' => array(
+      'featureprop_pub_c1' => array(
+        '0' => 'featureprop_id',
+        '1' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'featureprop_pub_idx1' => array(
+        '0' => 'featureprop_id',
+      ),
+      'featureprop_pub_idx2' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_featureprop_schema()
+ * Purpose: To describe the structure of featureprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_featureprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'featureprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'featureprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'featureprop_id',
+    ),
+    'unique keys' => array(
+      'featureprop_c1' => array(
+        '0' => 'feature_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'featureprop_idx1' => array(
+        '0' => 'feature_id',
+      ),
+      'featureprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_featurerange_schema()
+ * Purpose: To describe the structure of featurerange to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_featurerange_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'featurerange',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'featurerange_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'featuremap_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'leftstartf_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'leftendf_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'rightstartf_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'rightendf_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'rangestr' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'featurerange_id',
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+      'featurerange_idx1' => array(
+        '0' => 'featuremap_id',
+      ),
+      'featurerange_idx2' => array(
+        '0' => 'feature_id',
+      ),
+      'featurerange_idx3' => array(
+        '0' => 'leftstartf_id',
+      ),
+      'featurerange_idx4' => array(
+        '0' => 'leftendf_id',
+      ),
+      'featurerange_idx5' => array(
+        '0' => 'rightstartf_id',
+      ),
+      'featurerange_idx6' => array(
+        '0' => 'rightendf_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_featureset_meets_schema()
+ * Purpose: To describe the structure of featureset_meets to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_featureset_meets_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'featureset_meets',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_fnr_type_schema()
+ * Purpose: To describe the structure of fnr_type to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_fnr_type_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'fnr_type',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'type' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '1024',
+        'not null' => '',
+      ),
+      'residues' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'seqlen' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'md5checksum' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'char',
+        'length' => '32',
+        'not null' => '',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'timeaccessioned' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'datetime',
+        'not null' => '',
+      ),
+      'timelastmodified' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'datetime',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_featurepos_schema()
+ * Purpose: To describe the structure of featurepos to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_featurepos_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'featurepos',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'featurepos_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'featuremap_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'map_feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'mappos' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'float',
+        'size' => 'big',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'featurepos_id',
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+      'featurepos_idx1' => array(
+        '0' => 'featuremap_id',
+      ),
+      'featurepos_idx2' => array(
+        '0' => 'feature_id',
+      ),
+      'featurepos_idx3' => array(
+        '0' => 'map_feature_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_featuremap_pub_schema()
+ * Purpose: To describe the structure of featuremap_pub to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_featuremap_pub_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'featuremap_pub',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'featuremap_pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'featuremap_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'featuremap_pub_id',
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+      'featuremap_pub_idx1' => array(
+        '0' => 'featuremap_id',
+      ),
+      'featuremap_pub_idx2' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_union_schema()
+ * Purpose: To describe the structure of feature_union to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_union_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_union',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'srcfeature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'subject_strand' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => '',
+      ),
+      'object_strand' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => '',
+      ),
+      'fmin' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'fmax' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_featureloc_schema()
+ * Purpose: To describe the structure of featureloc to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_featureloc_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'featureloc',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'featureloc_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'srcfeature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'fmin' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'is_fmin_partial' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '1',
+        'default' => 'als',
+      ),
+      'fmax' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'is_fmax_partial' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '1',
+        'default' => 'als',
+      ),
+      'strand' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => '',
+      ),
+      'phase' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'residue_info' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'locgroup' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'featureloc_id',
+    ),
+    'unique keys' => array(
+      'featureloc_c1' => array(
+        '0' => 'feature_id',
+        '1' => 'locgroup',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'binloc_boxrange' => array(
+        '0' => 'fmin',
+      ),
+      'binloc_boxrange_src' => array(
+        '0' => 'srcfeature_id',
+        '1' => 'fmin',
+      ),
+      'featureloc_idx1' => array(
+        '0' => 'feature_id',
+      ),
+      'featureloc_idx2' => array(
+        '0' => 'srcfeature_id',
+      ),
+      'featureloc_idx3' => array(
+        '0' => 'srcfeature_id',
+        '1' => 'fmin',
+        '2' => 'fmax',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_featureloc_pub_schema()
+ * Purpose: To describe the structure of featureloc_pub to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_featureloc_pub_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'featureloc_pub',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'featureloc_pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'featureloc_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'featureloc_pub_id',
+    ),
+    'unique keys' => array(
+      'featureloc_pub_c1' => array(
+        '0' => 'featureloc_id',
+        '1' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'featureloc_pub_idx1' => array(
+        '0' => 'featureloc_id',
+      ),
+      'featureloc_pub_idx2' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_featuremap_schema()
+ * Purpose: To describe the structure of featuremap to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_featuremap_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'featuremap',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'featuremap_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'description' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'unittype_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'featuremap_id',
+    ),
+    'unique keys' => array(
+      'featuremap_c1' => array(
+        '0' => 'name',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_fp_key_schema()
+ * Purpose: To describe the structure of fp_key to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_fp_key_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'fp_key',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'pkey' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '1024',
+        'not null' => '',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_genotype_schema()
+ * Purpose: To describe the structure of genotype to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_genotype_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'genotype',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'genotype_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'uniquename' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+      'description' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'genotype_id',
+    ),
+    'unique keys' => array(
+      'genotype_c1' => array(
+        '0' => 'uniquename',
+      ),
+    ),
+    'indexes' => array(
+      'genotype_idx1' => array(
+        '0' => 'uniquename',
+      ),
+      'genotype_idx2' => array(
+        '0' => 'name',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_library_cvterm_schema()
+ * Purpose: To describe the structure of library_cvterm to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_library_cvterm_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'library_cvterm',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'library_cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'library_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'library_cvterm_id',
+    ),
+    'unique keys' => array(
+      'library_cvterm_c1' => array(
+        '0' => 'library_id',
+        '1' => 'cvterm_id',
+        '2' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'library_cvterm_idx1' => array(
+        '0' => 'library_id',
+      ),
+      'library_cvterm_idx2' => array(
+        '0' => 'cvterm_id',
+      ),
+      'library_cvterm_idx3' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_library_dbxref_schema()
+ * Purpose: To describe the structure of library_dbxref to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_library_dbxref_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'library_dbxref',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'library_dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'library_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'is_current' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '1',
+        'default' => 'ru',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'library_dbxref_id',
+    ),
+    'unique keys' => array(
+      'library_dbxref_c1' => array(
+        '0' => 'library_id',
+        '1' => 'dbxref_id',
+      ),
+    ),
+    'indexes' => array(
+      'library_dbxref_idx1' => array(
+        '0' => 'library_id',
+      ),
+      'library_dbxref_idx2' => array(
+        '0' => 'dbxref_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_library_feature_schema()
+ * Purpose: To describe the structure of library_feature to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_library_feature_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'library_feature',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'library_feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'library_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'library_feature_id',
+    ),
+    'unique keys' => array(
+      'library_feature_c1' => array(
+        '0' => 'library_id',
+        '1' => 'feature_id',
+      ),
+    ),
+    'indexes' => array(
+      'library_feature_idx1' => array(
+        '0' => 'library_id',
+      ),
+      'library_feature_idx2' => array(
+        '0' => 'feature_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_library_feature_count_schema()
+ * Purpose: To describe the structure of library_feature_count to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_library_feature_count_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'library_feature_count',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'library_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'num_features' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'feature_type' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+      'library_feature_count_index' => array(
+        '0' => 'library_id',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_library_schema()
+ * Purpose: To describe the structure of library to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_library_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'library',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'library_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'organism_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'uniquename' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'is_obsolete' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+      'timeaccessioned' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'datetime',
+        'not null' => '1',
+        'default' => 'ow(',
+      ),
+      'timelastmodified' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'datetime',
+        'not null' => '1',
+        'default' => 'ow(',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'library_id',
+    ),
+    'unique keys' => array(
+      'library_c1' => array(
+        '0' => 'organism_id',
+        '1' => 'uniquename',
+        '2' => 'type_id',
+      ),
+    ),
+    'indexes' => array(
+      'library_idx1' => array(
+        '0' => 'organism_id',
+      ),
+      'library_idx2' => array(
+        '0' => 'type_id',
+      ),
+      'library_idx3' => array(
+        '0' => 'uniquename',
+      ),
+      'library_name_ind1' => array(
+        '0' => 'name',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_intronloc_view_schema()
+ * Purpose: To describe the structure of intronloc_view to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_intronloc_view_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'intronloc_view',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'exon1_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'exon2_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'fmin' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'fmax' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'strand' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => '',
+      ),
+      'srcfeature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_gff3atts_schema()
+ * Purpose: To describe the structure of gff3atts to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_gff3atts_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'gff3atts',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'type' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'attribute' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_gff3view_schema()
+ * Purpose: To describe the structure of gff3view to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_gff3view_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'gff3view',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'ref' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'source' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'type' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '1024',
+        'not null' => '',
+      ),
+      'fstart' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'fend' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'score' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'float',
+        'size' => 'big',
+        'not null' => '',
+      ),
+      'strand' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => '',
+      ),
+      'phase' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'seqlen' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'organism_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_gffatts_schema()
+ * Purpose: To describe the structure of gffatts to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_gffatts_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'gffatts',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'type' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'attribute' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_intron_combined_view_schema()
+ * Purpose: To describe the structure of intron_combined_view to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_intron_combined_view_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'intron_combined_view',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'exon1_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'exon2_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'fmin' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'fmax' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'strand' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => '',
+      ),
+      'srcfeature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'intron_rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'transcript_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_project_schema()
+ * Purpose: To describe the structure of project to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_project_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'project',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'project_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '1',
+      ),
+      'description' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'project_id',
+    ),
+    'unique keys' => array(
+      'project_c1' => array(
+        '0' => 'name',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_protein_coding_gene_schema()
+ * Purpose: To describe the structure of protein_coding_gene to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_protein_coding_gene_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'protein_coding_gene',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'organism_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'uniquename' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'residues' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'seqlen' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'md5checksum' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'char',
+        'length' => '32',
+        'not null' => '',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'is_analysis' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '',
+      ),
+      'is_obsolete' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '',
+      ),
+      'timeaccessioned' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'datetime',
+        'not null' => '',
+      ),
+      'timelastmodified' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'datetime',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_studyfactorvalue_schema()
+ * Purpose: To describe the structure of studyfactorvalue to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_studyfactorvalue_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'studyfactorvalue',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'studyfactorvalue_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'studyfactor_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'assay_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'factorvalue' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'studyfactorvalue_id',
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+      'studyfactorvalue_idx1' => array(
+        '0' => 'studyfactor_id',
+      ),
+      'studyfactorvalue_idx2' => array(
+        '0' => 'assay_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_studyfactor_schema()
+ * Purpose: To describe the structure of studyfactor to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_studyfactor_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'studyfactor',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'studyfactor_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'studydesign_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+      'description' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'studyfactor_id',
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+      'studyfactor_idx1' => array(
+        '0' => 'studydesign_id',
+      ),
+      'studyfactor_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_studyprop_schema()
+ * Purpose: To describe the structure of studyprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_studyprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'studyprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'studyprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'study_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'studyprop_id',
+    ),
+    'unique keys' => array(
+      'study_id' => array(
+        '0' => 'study_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'studyprop_idx1' => array(
+        '0' => 'study_id',
+      ),
+      'studyprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_studyprop_feature_schema()
+ * Purpose: To describe the structure of studyprop_feature to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_studyprop_feature_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'studyprop_feature',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'studyprop_feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'studyprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'studyprop_feature_id',
+    ),
+    'unique keys' => array(
+      'studyprop_id' => array(
+        '0' => 'studyprop_id',
+        '1' => 'feature_id',
+      ),
+    ),
+    'indexes' => array(
+      'studyprop_feature_idx1' => array(
+        '0' => 'studyprop_id',
+      ),
+      'studyprop_feature_idx2' => array(
+        '0' => 'feature_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_synonym_schema()
+ * Purpose: To describe the structure of synonym to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_synonym_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'synonym',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'synonym_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'synonym_sgml' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'synonym_id',
+    ),
+    'unique keys' => array(
+      'synonym_c1' => array(
+        '0' => 'name',
+        '1' => 'type_id',
+      ),
+    ),
+    'indexes' => array(
+      'synonym_idx1' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_studydesignprop_schema()
+ * Purpose: To describe the structure of studydesignprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_studydesignprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'studydesignprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'studydesignprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'studydesign_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'studydesignprop_id',
+    ),
+    'unique keys' => array(
+      'studydesignprop_c1' => array(
+        '0' => 'studydesign_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'studydesignprop_idx1' => array(
+        '0' => 'studydesign_id',
+      ),
+      'studydesignprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_studydesign_schema()
+ * Purpose: To describe the structure of studydesign to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_studydesign_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'studydesign',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'studydesign_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'study_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'description' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'studydesign_id',
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+      'studydesign_idx1' => array(
+        '0' => 'study_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_stockprop_schema()
+ * Purpose: To describe the structure of stockprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_stockprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'stockprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'stockprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'stock_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'stockprop_id',
+    ),
+    'unique keys' => array(
+      'stockprop_c1' => array(
+        '0' => 'stock_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'stockprop_idx1' => array(
+        '0' => 'stock_id',
+      ),
+      'stockprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_stockprop_pub_schema()
+ * Purpose: To describe the structure of stockprop_pub to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_stockprop_pub_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'stockprop_pub',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'stockprop_pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'stockprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'stockprop_pub_id',
+    ),
+    'unique keys' => array(
+      'stockprop_pub_c1' => array(
+        '0' => 'stockprop_id',
+        '1' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'stockprop_pub_idx1' => array(
+        '0' => 'stockprop_id',
+      ),
+      'stockprop_pub_idx2' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_study_schema()
+ * Purpose: To describe the structure of study to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_study_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'study',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'study_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'contact_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+      'description' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'study_id',
+    ),
+    'unique keys' => array(
+      'study_c1' => array(
+        '0' => 'name',
+      ),
+    ),
+    'indexes' => array(
+      'study_idx1' => array(
+        '0' => 'contact_id',
+      ),
+      'study_idx2' => array(
+        '0' => 'pub_id',
+      ),
+      'study_idx3' => array(
+        '0' => 'dbxref_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_study_assay_schema()
+ * Purpose: To describe the structure of study_assay to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_study_assay_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'study_assay',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'study_assay_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'study_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'assay_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'study_assay_id',
+    ),
+    'unique keys' => array(
+      'study_assay_c1' => array(
+        '0' => 'study_id',
+        '1' => 'assay_id',
+      ),
+    ),
+    'indexes' => array(
+      'study_assay_idx1' => array(
+        '0' => 'study_id',
+      ),
+      'study_assay_idx2' => array(
+        '0' => 'assay_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_tableinfo_schema()
+ * Purpose: To describe the structure of tableinfo to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_tableinfo_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'tableinfo',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'tableinfo_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '30',
+        'not null' => '1',
+      ),
+      'primary_key_column' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '30',
+        'not null' => '',
+      ),
+      'is_view' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+      'view_on_table_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'superclass_table_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'is_updateable' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '1',
+      ),
+      'modification_date' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'date',
+        'not null' => '1',
+        'default' => 'ow(',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'tableinfo_id',
+    ),
+    'unique keys' => array(
+      'tableinfo_c1' => array(
+        '0' => 'name',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_treatment_schema()
+ * Purpose: To describe the structure of treatment to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_treatment_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'treatment',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'treatment_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+      'biomaterial_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'protocol_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'treatment_id',
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+      'treatment_idx1' => array(
+        '0' => 'biomaterial_id',
+      ),
+      'treatment_idx2' => array(
+        '0' => 'type_id',
+      ),
+      'treatment_idx3' => array(
+        '0' => 'protocol_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_tripal_obo_schema()
+ * Purpose: To describe the structure of tripal_obo to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_tripal_obo_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'tripal_obo',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cv_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'unsigned' => '1',
+        'not null' => '1',
+        'default' => '0',
+      ),
+      'file' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '1024',
+        'not null' => '',
+      ),
+      'url' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '1024',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'cv_id',
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+      'cv_id' => array(
+        '0' => 'cv_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_tripal_organism_views_common_name_schema()
+ * Purpose: To describe the structure of tripal_organism_views_common_name to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_tripal_organism_views_common_name_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'tripal_organism_views_common_name',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'nid' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'unsigned' => '1',
+        'not null' => '1',
+        'default' => '0',
+      ),
+      'common_name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '1',
+        'default' => 'NA',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_type_feature_count_schema()
+ * Purpose: To describe the structure of type_feature_count to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_type_feature_count_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'type_feature_count',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'type' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '1024',
+        'not null' => '',
+      ),
+      'num_features' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'big',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_stockcollectionprop_schema()
+ * Purpose: To describe the structure of stockcollectionprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_stockcollectionprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'stockcollectionprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'stockcollectionprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'stockcollection_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'stockcollectionprop_id',
+    ),
+    'unique keys' => array(
+      'stockcollectionprop_c1' => array(
+        '0' => 'stockcollection_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'stockcollectionprop_idx1' => array(
+        '0' => 'stockcollection_id',
+      ),
+      'stockcollectionprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_stockcollection_stock_schema()
+ * Purpose: To describe the structure of stockcollection_stock to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_stockcollection_stock_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'stockcollection_stock',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'stockcollection_stock_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'stockcollection_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'stock_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'stockcollection_stock_id',
+    ),
+    'unique keys' => array(
+      'stockcollection_stock_c1' => array(
+        '0' => 'stockcollection_id',
+        '1' => 'stock_id',
+      ),
+    ),
+    'indexes' => array(
+      'stockcollection_stock_idx1' => array(
+        '0' => 'stockcollection_id',
+      ),
+      'stockcollection_stock_idx2' => array(
+        '0' => 'stock_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_public_feature_schema()
+ * Purpose: To describe the structure of public_feature to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_public_feature_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'public_feature',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'vid' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'unsigned' => '1',
+        'not null' => '1',
+        'default' => '0',
+      ),
+      'nid' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'unsigned' => '1',
+        'not null' => '1',
+        'default' => '0',
+      ),
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+      'sync_date' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'nid',
+    ),
+    'unique keys' => array(
+      'nid_vid' => array(
+        '0' => 'nid',
+        '1' => 'vid',
+      ),
+      'vid' => array(
+        '0' => 'vid',
+      ),
+    ),
+    'indexes' => array(
+      'feature_id' => array(
+        '0' => 'feature_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_public_library_schema()
+ * Purpose: To describe the structure of public_library to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_public_library_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'public_library',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'vid' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'unsigned' => '1',
+        'not null' => '1',
+        'default' => '0',
+      ),
+      'nid' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'unsigned' => '1',
+        'not null' => '1',
+        'default' => '0',
+      ),
+      'library_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'nid',
+    ),
+    'unique keys' => array(
+      'nid_vid' => array(
+        '0' => 'nid',
+        '1' => 'vid',
+      ),
+      'vid' => array(
+        '0' => 'vid',
+      ),
+    ),
+    'indexes' => array(
+      'library_id' => array(
+        '0' => 'library_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_public_organism_schema()
+ * Purpose: To describe the structure of public_organism to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_public_organism_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'public_organism',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'vid' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'unsigned' => '1',
+        'not null' => '1',
+        'default' => '0',
+      ),
+      'nid' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'unsigned' => '1',
+        'not null' => '1',
+        'default' => '0',
+      ),
+      'organism_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'nid',
+    ),
+    'unique keys' => array(
+      'nid_vid' => array(
+        '0' => 'nid',
+        '1' => 'vid',
+      ),
+      'vid' => array(
+        '0' => 'vid',
+      ),
+    ),
+    'indexes' => array(
+      'organism_id' => array(
+        '0' => 'organism_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_pubprop_schema()
+ * Purpose: To describe the structure of pubprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_pubprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'pubprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'pubprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'pubprop_id',
+    ),
+    'unique keys' => array(
+      'pubprop_c1' => array(
+        '0' => 'pub_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'pubprop_idx1' => array(
+        '0' => 'pub_id',
+      ),
+      'pubprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_pubauthor_schema()
+ * Purpose: To describe the structure of pubauthor to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_pubauthor_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'pubauthor',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'pubauthor_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'editor' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '',
+        'default' => 'als',
+      ),
+      'surname' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '100',
+        'not null' => '1',
+      ),
+      'givennames' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '100',
+        'not null' => '',
+      ),
+      'suffix' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '100',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'pubauthor_id',
+    ),
+    'unique keys' => array(
+      'pubauthor_c1' => array(
+        '0' => 'pub_id',
+        '1' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'pubauthor_idx2' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_pub_relationship_schema()
+ * Purpose: To describe the structure of pub_relationship to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_pub_relationship_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'pub_relationship',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'pub_relationship_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'pub_relationship_id',
+    ),
+    'unique keys' => array(
+      'pub_relationship_c1' => array(
+        '0' => 'subject_id',
+        '1' => 'object_id',
+        '2' => 'type_id',
+      ),
+    ),
+    'indexes' => array(
+      'pub_relationship_idx1' => array(
+        '0' => 'subject_id',
+      ),
+      'pub_relationship_idx2' => array(
+        '0' => 'object_id',
+      ),
+      'pub_relationship_idx3' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_protocol_schema()
+ * Purpose: To describe the structure of protocol to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_protocol_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'protocol',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'protocol_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+      'uri' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'protocoldescription' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'hardwaredescription' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'softwaredescription' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'protocol_id',
+    ),
+    'unique keys' => array(
+      'protocol_c1' => array(
+        '0' => 'name',
+      ),
+    ),
+    'indexes' => array(
+      'protocol_idx1' => array(
+        '0' => 'type_id',
+      ),
+      'protocol_idx2' => array(
+        '0' => 'pub_id',
+      ),
+      'protocol_idx3' => array(
+        '0' => 'dbxref_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_protocolparam_schema()
+ * Purpose: To describe the structure of protocolparam to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_protocolparam_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'protocolparam',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'protocolparam_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'protocol_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+      'datatype_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'unittype_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'protocolparam_id',
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+      'protocolparam_idx1' => array(
+        '0' => 'protocol_id',
+      ),
+      'protocolparam_idx2' => array(
+        '0' => 'datatype_id',
+      ),
+      'protocolparam_idx3' => array(
+        '0' => 'unittype_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_pub_schema()
+ * Purpose: To describe the structure of pub to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_pub_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'pub',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'title' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'volumetitle' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'volume' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'series_name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'issue' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'pyear' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'pages' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'miniref' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'uniquename' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'is_obsolete' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '',
+        'default' => 'als',
+      ),
+      'publisher' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'pubplace' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'pub_id',
+    ),
+    'unique keys' => array(
+      'pub_c1' => array(
+        '0' => 'uniquename',
+      ),
+    ),
+    'indexes' => array(
+      'pub_idx1' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_pub_dbxref_schema()
+ * Purpose: To describe the structure of pub_dbxref to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_pub_dbxref_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'pub_dbxref',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'pub_dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'is_current' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '1',
+        'default' => 'ru',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'pub_dbxref_id',
+    ),
+    'unique keys' => array(
+      'pub_dbxref_c1' => array(
+        '0' => 'pub_id',
+        '1' => 'dbxref_id',
+      ),
+    ),
+    'indexes' => array(
+      'pub_dbxref_idx1' => array(
+        '0' => 'pub_id',
+      ),
+      'pub_dbxref_idx2' => array(
+        '0' => 'dbxref_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_quantification_schema()
+ * Purpose: To describe the structure of quantification to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_quantification_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'quantification',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'quantification_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'acquisition_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'operator_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'protocol_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'analysis_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'quantificationdate' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'datetime',
+        'not null' => '',
+        'default' => 'ow(',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'uri' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'quantification_id',
+    ),
+    'unique keys' => array(
+      'quantification_c1' => array(
+        '0' => 'name',
+        '1' => 'analysis_id',
+      ),
+    ),
+    'indexes' => array(
+      'quantification_idx1' => array(
+        '0' => 'acquisition_id',
+      ),
+      'quantification_idx2' => array(
+        '0' => 'operator_id',
+      ),
+      'quantification_idx3' => array(
+        '0' => 'protocol_id',
+      ),
+      'quantification_idx4' => array(
+        '0' => 'analysis_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_quantification_relationship_schema()
+ * Purpose: To describe the structure of quantification_relationship to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_quantification_relationship_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'quantification_relationship',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'quantification_relationship_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'quantification_relationship_id',
+    ),
+    'unique keys' => array(
+      'quantification_relationship_c1' => array(
+        '0' => 'subject_id',
+        '1' => 'object_id',
+        '2' => 'type_id',
+      ),
+    ),
+    'indexes' => array(
+      'quantification_relationship_idx1' => array(
+        '0' => 'subject_id',
+      ),
+      'quantification_relationship_idx2' => array(
+        '0' => 'type_id',
+      ),
+      'quantification_relationship_idx3' => array(
+        '0' => 'object_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_stock_pub_schema()
+ * Purpose: To describe the structure of stock_pub to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_stock_pub_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'stock_pub',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'stock_pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'stock_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'stock_pub_id',
+    ),
+    'unique keys' => array(
+      'stock_pub_c1' => array(
+        '0' => 'stock_id',
+        '1' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'stock_pub_idx1' => array(
+        '0' => 'stock_id',
+      ),
+      'stock_pub_idx2' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_stock_relationship_schema()
+ * Purpose: To describe the structure of stock_relationship to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_stock_relationship_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'stock_relationship',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'stock_relationship_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'stock_relationship_id',
+    ),
+    'unique keys' => array(
+      'stock_relationship_c1' => array(
+        '0' => 'subject_id',
+        '1' => 'object_id',
+        '2' => 'type_id',
+        '3' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'stock_relationship_idx1' => array(
+        '0' => 'subject_id',
+      ),
+      'stock_relationship_idx2' => array(
+        '0' => 'object_id',
+      ),
+      'stock_relationship_idx3' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_stock_relationship_pub_schema()
+ * Purpose: To describe the structure of stock_relationship_pub to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_stock_relationship_pub_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'stock_relationship_pub',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'stock_relationship_pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'stock_relationship_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'stock_relationship_pub_id',
+    ),
+    'unique keys' => array(
+      'stock_relationship_pub_c1' => array(
+        '0' => 'stock_relationship_id',
+        '1' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'stock_relationship_pub_idx1' => array(
+        '0' => 'stock_relationship_id',
+      ),
+      'stock_relationship_pub_idx2' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_stockcollection_schema()
+ * Purpose: To describe the structure of stockcollection to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_stockcollection_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'stockcollection',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'stockcollection_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'contact_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'uniquename' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'stockcollection_id',
+    ),
+    'unique keys' => array(
+      'stockcollection_c1' => array(
+        '0' => 'uniquename',
+        '1' => 'type_id',
+      ),
+    ),
+    'indexes' => array(
+      'stockcollection_idx1' => array(
+        '0' => 'contact_id',
+      ),
+      'stockcollection_idx2' => array(
+        '0' => 'type_id',
+      ),
+      'stockcollection_idx3' => array(
+        '0' => 'uniquename',
+      ),
+      'stockcollection_name_ind1' => array(
+        '0' => 'name',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_stock_genotype_schema()
+ * Purpose: To describe the structure of stock_genotype to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_stock_genotype_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'stock_genotype',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'stock_genotype_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'stock_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'genotype_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'stock_genotype_id',
+    ),
+    'unique keys' => array(
+      'stock_genotype_c1' => array(
+        '0' => 'stock_id',
+        '1' => 'genotype_id',
+      ),
+    ),
+    'indexes' => array(
+      'stock_genotype_idx1' => array(
+        '0' => 'stock_id',
+      ),
+      'stock_genotype_idx2' => array(
+        '0' => 'genotype_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_stock_dbxref_schema()
+ * Purpose: To describe the structure of stock_dbxref to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_stock_dbxref_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'stock_dbxref',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'stock_dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'stock_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'is_current' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '1',
+        'default' => 'ru',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'stock_dbxref_id',
+    ),
+    'unique keys' => array(
+      'stock_dbxref_c1' => array(
+        '0' => 'stock_id',
+        '1' => 'dbxref_id',
+      ),
+    ),
+    'indexes' => array(
+      'stock_dbxref_idx1' => array(
+        '0' => 'stock_id',
+      ),
+      'stock_dbxref_idx2' => array(
+        '0' => 'dbxref_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_quantificationprop_schema()
+ * Purpose: To describe the structure of quantificationprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_quantificationprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'quantificationprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'quantificationprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'quantification_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'quantificationprop_id',
+    ),
+    'unique keys' => array(
+      'quantificationprop_c1' => array(
+        '0' => 'quantification_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'quantificationprop_idx1' => array(
+        '0' => 'quantification_id',
+      ),
+      'quantificationprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_stats_paths_to_root_schema()
+ * Purpose: To describe the structure of stats_paths_to_root to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_stats_paths_to_root_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'stats_paths_to_root',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'total_paths' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'big',
+        'not null' => '',
+      ),
+      'avg_distance' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'numeric',
+        'not null' => '',
+        'precision' => '0',
+        'scale' => '0',
+      ),
+      'min_distance' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'max_distance' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_stock_schema()
+ * Purpose: To describe the structure of stock to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_stock_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'stock',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'stock_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'organism_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'uniquename' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+      'description' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'is_obsolete' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '1',
+        'default' => 'als',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'stock_id',
+    ),
+    'unique keys' => array(
+      'stock_c1' => array(
+        '0' => 'organism_id',
+        '1' => 'uniquename',
+        '2' => 'type_id',
+      ),
+    ),
+    'indexes' => array(
+      'stock_idx1' => array(
+        '0' => 'dbxref_id',
+      ),
+      'stock_idx2' => array(
+        '0' => 'organism_id',
+      ),
+      'stock_idx3' => array(
+        '0' => 'type_id',
+      ),
+      'stock_idx4' => array(
+        '0' => 'uniquename',
+      ),
+      'stock_name_ind1' => array(
+        '0' => 'name',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_stock_cvterm_schema()
+ * Purpose: To describe the structure of stock_cvterm to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_stock_cvterm_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'stock_cvterm',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'stock_cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'stock_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'stock_cvterm_id',
+    ),
+    'unique keys' => array(
+      'stock_cvterm_c1' => array(
+        '0' => 'stock_id',
+        '1' => 'cvterm_id',
+        '2' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'stock_cvterm_idx1' => array(
+        '0' => 'stock_id',
+      ),
+      'stock_cvterm_idx2' => array(
+        '0' => 'cvterm_id',
+      ),
+      'stock_cvterm_idx3' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_synonym_schema()
+ * Purpose: To describe the structure of feature_synonym to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_synonym_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_synonym',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_synonym_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'synonym_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'is_current' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '1',
+        'default' => 'als',
+      ),
+      'is_internal' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '1',
+        'default' => 'als',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'feature_synonym_id',
+    ),
+    'unique keys' => array(
+      'feature_synonym_c1' => array(
+        '0' => 'synonym_id',
+        '1' => 'feature_id',
+        '2' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'feature_synonym_idx1' => array(
+        '0' => 'synonym_id',
+      ),
+      'feature_synonym_idx2' => array(
+        '0' => 'feature_id',
+      ),
+      'feature_synonym_idx3' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_relationshipprop_pub_schema()
+ * Purpose: To describe the structure of feature_relationshipprop_pub to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_relationshipprop_pub_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_relationshipprop_pub',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_relationshipprop_pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'feature_relationshipprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'feature_relationshipprop_pub_id',
+    ),
+    'unique keys' => array(
+      'feature_relationshipprop_pub_c1' => array(
+        '0' => 'feature_relationshipprop_id',
+        '1' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'feature_relationshipprop_pub_idx1' => array(
+        '0' => 'feature_relationshipprop_id',
+      ),
+      'feature_relationshipprop_pub_idx2' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cell_line_relationship_schema()
+ * Purpose: To describe the structure of cell_line_relationship to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cell_line_relationship_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cell_line_relationship',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cell_line_relationship_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'cell_line_relationship_id',
+    ),
+    'unique keys' => array(
+      'cell_line_relationship_c1' => array(
+        '0' => 'subject_id',
+        '1' => 'object_id',
+        '2' => 'type_id',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cell_line_pub_schema()
+ * Purpose: To describe the structure of cell_line_pub to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cell_line_pub_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cell_line_pub',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cell_line_pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'cell_line_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'cell_line_pub_id',
+    ),
+    'unique keys' => array(
+      'cell_line_pub_c1' => array(
+        '0' => 'cell_line_id',
+        '1' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cell_line_synonym_schema()
+ * Purpose: To describe the structure of cell_line_synonym to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cell_line_synonym_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cell_line_synonym',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cell_line_synonym_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'cell_line_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'synonym_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'is_current' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '1',
+        'default' => 'als',
+      ),
+      'is_internal' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '1',
+        'default' => 'als',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'cell_line_synonym_id',
+    ),
+    'unique keys' => array(
+      'cell_line_synonym_c1' => array(
+        '0' => 'synonym_id',
+        '1' => 'cell_line_id',
+        '2' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cell_lineprop_schema()
+ * Purpose: To describe the structure of cell_lineprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cell_lineprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cell_lineprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cell_lineprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'cell_line_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'cell_lineprop_id',
+    ),
+    'unique keys' => array(
+      'cell_lineprop_c1' => array(
+        '0' => 'cell_line_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cell_lineprop_pub_schema()
+ * Purpose: To describe the structure of cell_lineprop_pub to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cell_lineprop_pub_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cell_lineprop_pub',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cell_lineprop_pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'cell_lineprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'cell_lineprop_pub_id',
+    ),
+    'unique keys' => array(
+      'cell_lineprop_pub_c1' => array(
+        '0' => 'cell_lineprop_id',
+        '1' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cell_line_library_schema()
+ * Purpose: To describe the structure of cell_line_library to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cell_line_library_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cell_line_library',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cell_line_library_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'cell_line_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'library_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'cell_line_library_id',
+    ),
+    'unique keys' => array(
+      'cell_line_library_c1' => array(
+        '0' => 'cell_line_id',
+        '1' => 'library_id',
+        '2' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cell_line_feature_schema()
+ * Purpose: To describe the structure of cell_line_feature to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cell_line_feature_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cell_line_feature',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cell_line_feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'cell_line_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'cell_line_feature_id',
+    ),
+    'unique keys' => array(
+      'cell_line_feature_c1' => array(
+        '0' => 'cell_line_id',
+        '1' => 'feature_id',
+        '2' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cell_line_schema()
+ * Purpose: To describe the structure of cell_line to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cell_line_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cell_line',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cell_line_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'uniquename' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '1',
+      ),
+      'organism_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'timeaccessioned' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'datetime',
+        'not null' => '1',
+        'default' => 'ow(',
+      ),
+      'timelastmodified' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'datetime',
+        'not null' => '1',
+        'default' => 'ow(',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'cell_line_id',
+    ),
+    'unique keys' => array(
+      'cell_line_c1' => array(
+        '0' => 'uniquename',
+        '1' => 'organism_id',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cell_line_cvterm_schema()
+ * Purpose: To describe the structure of cell_line_cvterm to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cell_line_cvterm_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cell_line_cvterm',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cell_line_cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'cell_line_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'cell_line_cvterm_id',
+    ),
+    'unique keys' => array(
+      'cell_line_cvterm_c1' => array(
+        '0' => 'cell_line_id',
+        '1' => 'cvterm_id',
+        '2' => 'pub_id',
+        '3' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cell_line_cvtermprop_schema()
+ * Purpose: To describe the structure of cell_line_cvtermprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cell_line_cvtermprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cell_line_cvtermprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cell_line_cvtermprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'cell_line_cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'cell_line_cvtermprop_id',
+    ),
+    'unique keys' => array(
+      'cell_line_cvtermprop_c1' => array(
+        '0' => 'cell_line_cvterm_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cell_line_dbxref_schema()
+ * Purpose: To describe the structure of cell_line_dbxref to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cell_line_dbxref_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cell_line_dbxref',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cell_line_dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'cell_line_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'is_current' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '1',
+        'default' => 'ru',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'cell_line_dbxref_id',
+    ),
+    'unique keys' => array(
+      'cell_line_dbxref_c1' => array(
+        '0' => 'cell_line_id',
+        '1' => 'dbxref_id',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_channel_schema()
+ * Purpose: To describe the structure of channel to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_channel_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'channel',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'channel_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+      'definition' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'channel_id',
+    ),
+    'unique keys' => array(
+      'channel_c1' => array(
+        '0' => 'name',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_common_ancestor_cvterm_schema()
+ * Purpose: To describe the structure of common_ancestor_cvterm to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_common_ancestor_cvterm_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'common_ancestor_cvterm',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cvterm1_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'cvterm2_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'ancestor_cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'pathdistance1' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'pathdistance2' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'total_pathdistance' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cv_cvterm_count_with_obs_schema()
+ * Purpose: To describe the structure of cv_cvterm_count_with_obs to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cv_cvterm_count_with_obs_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cv_cvterm_count_with_obs',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'num_terms_incl_obs' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'big',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cv_leaf_schema()
+ * Purpose: To describe the structure of cv_leaf to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cv_leaf_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cv_leaf',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cv_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cv_link_count_schema()
+ * Purpose: To describe the structure of cv_link_count to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cv_link_count_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cv_link_count',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cv_name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'relation_name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '1024',
+        'not null' => '',
+      ),
+      'relation_cv_name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'num_links' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'big',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cv_path_count_schema()
+ * Purpose: To describe the structure of cv_path_count to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cv_path_count_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cv_path_count',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cv_name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'relation_name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '1024',
+        'not null' => '',
+      ),
+      'relation_cv_name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'num_paths' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'big',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cv_cvterm_count_schema()
+ * Purpose: To describe the structure of cv_cvterm_count to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cv_cvterm_count_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cv_cvterm_count',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'num_terms_excl_obs' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'big',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cv_schema()
+ * Purpose: To describe the structure of cv to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cv_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cv',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cv_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '1',
+      ),
+      'definition' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'cv_id',
+    ),
+    'unique keys' => array(
+      'cv_c1' => array(
+        '0' => 'name',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_common_descendant_cvterm_schema()
+ * Purpose: To describe the structure of common_descendant_cvterm to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_common_descendant_cvterm_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'common_descendant_cvterm',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cvterm1_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'cvterm2_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'ancestor_cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'pathdistance1' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'pathdistance2' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'total_pathdistance' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_contact_schema()
+ * Purpose: To describe the structure of contact to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_contact_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'contact',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'contact_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '1',
+      ),
+      'description' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'contact_id',
+    ),
+    'unique keys' => array(
+      'contact_c1' => array(
+        '0' => 'name',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_contact_relationship_schema()
+ * Purpose: To describe the structure of contact_relationship to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_contact_relationship_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'contact_relationship',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'contact_relationship_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'contact_relationship_id',
+    ),
+    'unique keys' => array(
+      'contact_relationship_c1' => array(
+        '0' => 'subject_id',
+        '1' => 'object_id',
+        '2' => 'type_id',
+      ),
+    ),
+    'indexes' => array(
+      'contact_relationship_idx1' => array(
+        '0' => 'type_id',
+      ),
+      'contact_relationship_idx2' => array(
+        '0' => 'subject_id',
+      ),
+      'contact_relationship_idx3' => array(
+        '0' => 'object_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_control_schema()
+ * Purpose: To describe the structure of control to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_control_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'control',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'control_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'assay_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'tableinfo_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'row_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'control_id',
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+      'control_idx1' => array(
+        '0' => 'type_id',
+      ),
+      'control_idx2' => array(
+        '0' => 'assay_id',
+      ),
+      'control_idx3' => array(
+        '0' => 'tableinfo_id',
+      ),
+      'control_idx4' => array(
+        '0' => 'row_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_biomaterialprop_schema()
+ * Purpose: To describe the structure of biomaterialprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_biomaterialprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'biomaterialprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'biomaterialprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'biomaterial_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'biomaterialprop_id',
+    ),
+    'unique keys' => array(
+      'biomaterialprop_c1' => array(
+        '0' => 'biomaterial_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'biomaterialprop_idx1' => array(
+        '0' => 'biomaterial_id',
+      ),
+      'biomaterialprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_biomaterial_treatment_schema()
+ * Purpose: To describe the structure of biomaterial_treatment to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_biomaterial_treatment_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'biomaterial_treatment',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'biomaterial_treatment_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'biomaterial_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'treatment_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'unittype_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'float',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'biomaterial_treatment_id',
+    ),
+    'unique keys' => array(
+      'biomaterial_treatment_c1' => array(
+        '0' => 'biomaterial_id',
+        '1' => 'treatment_id',
+      ),
+    ),
+    'indexes' => array(
+      'biomaterial_treatment_idx1' => array(
+        '0' => 'biomaterial_id',
+      ),
+      'biomaterial_treatment_idx2' => array(
+        '0' => 'treatment_id',
+      ),
+      'biomaterial_treatment_idx3' => array(
+        '0' => 'unittype_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_acquisition_relationship_schema()
+ * Purpose: To describe the structure of acquisition_relationship to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_acquisition_relationship_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'acquisition_relationship',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'acquisition_relationship_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'acquisition_relationship_id',
+    ),
+    'unique keys' => array(
+      'acquisition_relationship_c1' => array(
+        '0' => 'subject_id',
+        '1' => 'object_id',
+        '2' => 'type_id',
+        '3' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'acquisition_relationship_idx1' => array(
+        '0' => 'subject_id',
+      ),
+      'acquisition_relationship_idx2' => array(
+        '0' => 'type_id',
+      ),
+      'acquisition_relationship_idx3' => array(
+        '0' => 'object_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_acquisition_schema()
+ * Purpose: To describe the structure of acquisition to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_acquisition_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'acquisition',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'acquisition_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'assay_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'protocol_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'channel_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'acquisitiondate' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'datetime',
+        'not null' => '',
+        'default' => 'ow(',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'uri' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'acquisition_id',
+    ),
+    'unique keys' => array(
+      'acquisition_c1' => array(
+        '0' => 'name',
+      ),
+    ),
+    'indexes' => array(
+      'acquisition_idx1' => array(
+        '0' => 'assay_id',
+      ),
+      'acquisition_idx2' => array(
+        '0' => 'protocol_id',
+      ),
+      'acquisition_idx3' => array(
+        '0' => 'channel_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_acquisitionprop_schema()
+ * Purpose: To describe the structure of acquisitionprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_acquisitionprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'acquisitionprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'acquisitionprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'acquisition_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'acquisitionprop_id',
+    ),
+    'unique keys' => array(
+      'acquisitionprop_c1' => array(
+        '0' => 'acquisition_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'acquisitionprop_idx1' => array(
+        '0' => 'acquisition_id',
+      ),
+      'acquisitionprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_all_feature_names_schema()
+ * Purpose: To describe the structure of all_feature_names to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_all_feature_names_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'all_feature_names',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'organism_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_analysis_schema()
+ * Purpose: To describe the structure of analysis to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_analysis_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'analysis',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'analysis_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'description' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'program' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '1',
+      ),
+      'programversion' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '1',
+      ),
+      'algorithm' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'sourcename' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'sourceversion' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'sourceuri' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'timeexecuted' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'datetime',
+        'not null' => '1',
+        'default' => 'ow(',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'analysis_id',
+    ),
+    'unique keys' => array(
+      'analysis_c1' => array(
+        '0' => 'program',
+        '1' => 'programversion',
+        '2' => 'sourcename',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_analysisfeature_schema()
+ * Purpose: To describe the structure of analysisfeature to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_analysisfeature_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'analysisfeature',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'analysisfeature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'analysis_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'rawscore' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'float',
+        'size' => 'big',
+        'not null' => '',
+      ),
+      'normscore' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'float',
+        'size' => 'big',
+        'not null' => '',
+      ),
+      'significance' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'float',
+        'size' => 'big',
+        'not null' => '',
+      ),
+      'identity' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'float',
+        'size' => 'big',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'analysisfeature_id',
+    ),
+    'unique keys' => array(
+      'analysisfeature_c1' => array(
+        '0' => 'feature_id',
+        '1' => 'analysis_id',
+      ),
+    ),
+    'indexes' => array(
+      'analysisfeature_idx1' => array(
+        '0' => 'feature_id',
+      ),
+      'analysisfeature_idx2' => array(
+        '0' => 'analysis_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_analysisfeatureprop_schema()
+ * Purpose: To describe the structure of analysisfeatureprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_analysisfeatureprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'analysisfeatureprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'analysisfeatureprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'analysisfeature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'analysisfeatureprop_id',
+    ),
+    'unique keys' => array(
+      'analysisfeature_id_type_id_rank' => array(
+        '0' => 'analysisfeature_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_assayprop_schema()
+ * Purpose: To describe the structure of assayprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_assayprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'assayprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'assayprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'assay_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'assayprop_id',
+    ),
+    'unique keys' => array(
+      'assayprop_c1' => array(
+        '0' => 'assay_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'assayprop_idx1' => array(
+        '0' => 'assay_id',
+      ),
+      'assayprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_biomaterial_schema()
+ * Purpose: To describe the structure of biomaterial to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_biomaterial_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'biomaterial',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'biomaterial_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'taxon_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'biosourceprovider_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'description' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'biomaterial_id',
+    ),
+    'unique keys' => array(
+      'biomaterial_c1' => array(
+        '0' => 'name',
+      ),
+    ),
+    'indexes' => array(
+      'biomaterial_idx1' => array(
+        '0' => 'taxon_id',
+      ),
+      'biomaterial_idx2' => array(
+        '0' => 'biosourceprovider_id',
+      ),
+      'biomaterial_idx3' => array(
+        '0' => 'dbxref_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_biomaterial_dbxref_schema()
+ * Purpose: To describe the structure of biomaterial_dbxref to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_biomaterial_dbxref_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'biomaterial_dbxref',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'biomaterial_dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'biomaterial_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'biomaterial_dbxref_id',
+    ),
+    'unique keys' => array(
+      'biomaterial_dbxref_c1' => array(
+        '0' => 'biomaterial_id',
+        '1' => 'dbxref_id',
+      ),
+    ),
+    'indexes' => array(
+      'biomaterial_dbxref_idx1' => array(
+        '0' => 'biomaterial_id',
+      ),
+      'biomaterial_dbxref_idx2' => array(
+        '0' => 'dbxref_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_biomaterial_relationship_schema()
+ * Purpose: To describe the structure of biomaterial_relationship to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_biomaterial_relationship_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'biomaterial_relationship',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'biomaterial_relationship_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'biomaterial_relationship_id',
+    ),
+    'unique keys' => array(
+      'biomaterial_relationship_c1' => array(
+        '0' => 'subject_id',
+        '1' => 'object_id',
+        '2' => 'type_id',
+      ),
+    ),
+    'indexes' => array(
+      'biomaterial_relationship_idx1' => array(
+        '0' => 'subject_id',
+      ),
+      'biomaterial_relationship_idx2' => array(
+        '0' => 'object_id',
+      ),
+      'biomaterial_relationship_idx3' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_assay_project_schema()
+ * Purpose: To describe the structure of assay_project to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_assay_project_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'assay_project',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'assay_project_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'assay_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'project_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'assay_project_id',
+    ),
+    'unique keys' => array(
+      'assay_project_c1' => array(
+        '0' => 'assay_id',
+        '1' => 'project_id',
+      ),
+    ),
+    'indexes' => array(
+      'assay_project_idx1' => array(
+        '0' => 'assay_id',
+      ),
+      'assay_project_idx2' => array(
+        '0' => 'project_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_assay_biomaterial_schema()
+ * Purpose: To describe the structure of assay_biomaterial to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_assay_biomaterial_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'assay_biomaterial',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'assay_biomaterial_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'assay_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'biomaterial_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'channel_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'assay_biomaterial_id',
+    ),
+    'unique keys' => array(
+      'assay_biomaterial_c1' => array(
+        '0' => 'assay_id',
+        '1' => 'biomaterial_id',
+        '2' => 'channel_id',
+        '3' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'assay_biomaterial_idx1' => array(
+        '0' => 'assay_id',
+      ),
+      'assay_biomaterial_idx2' => array(
+        '0' => 'biomaterial_id',
+      ),
+      'assay_biomaterial_idx3' => array(
+        '0' => 'channel_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_analysisprop_schema()
+ * Purpose: To describe the structure of analysisprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_analysisprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'analysisprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'analysisprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'analysis_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'analysisprop_id',
+    ),
+    'unique keys' => array(
+      'analysisprop_c1' => array(
+        '0' => 'analysis_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'analysisprop_idx1' => array(
+        '0' => 'analysis_id',
+      ),
+      'analysisprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_arraydesign_schema()
+ * Purpose: To describe the structure of arraydesign to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_arraydesign_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'arraydesign',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'arraydesign_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'manufacturer_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'platformtype_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'substratetype_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'protocol_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+      'version' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'description' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'array_dimensions' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'element_dimensions' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'num_of_elements' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'num_array_columns' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'num_array_rows' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'num_grid_columns' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'num_grid_rows' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'num_sub_columns' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'num_sub_rows' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'arraydesign_id',
+    ),
+    'unique keys' => array(
+      'arraydesign_c1' => array(
+        '0' => 'name',
+      ),
+    ),
+    'indexes' => array(
+      'arraydesign_idx1' => array(
+        '0' => 'manufacturer_id',
+      ),
+      'arraydesign_idx2' => array(
+        '0' => 'platformtype_id',
+      ),
+      'arraydesign_idx3' => array(
+        '0' => 'substratetype_id',
+      ),
+      'arraydesign_idx4' => array(
+        '0' => 'protocol_id',
+      ),
+      'arraydesign_idx5' => array(
+        '0' => 'dbxref_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_arraydesignprop_schema()
+ * Purpose: To describe the structure of arraydesignprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_arraydesignprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'arraydesignprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'arraydesignprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'arraydesign_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'arraydesignprop_id',
+    ),
+    'unique keys' => array(
+      'arraydesignprop_c1' => array(
+        '0' => 'arraydesign_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'arraydesignprop_idx1' => array(
+        '0' => 'arraydesign_id',
+      ),
+      'arraydesignprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_assay_schema()
+ * Purpose: To describe the structure of assay to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_assay_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'assay',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'assay_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'arraydesign_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'protocol_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'assaydate' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'datetime',
+        'not null' => '',
+        'default' => 'ow(',
+      ),
+      'arrayidentifier' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'arraybatchidentifier' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'operator_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'description' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'assay_id',
+    ),
+    'unique keys' => array(
+      'assay_c1' => array(
+        '0' => 'name',
+      ),
+    ),
+    'indexes' => array(
+      'assay_idx1' => array(
+        '0' => 'arraydesign_id',
+      ),
+      'assay_idx2' => array(
+        '0' => 'protocol_id',
+      ),
+      'assay_idx3' => array(
+        '0' => 'operator_id',
+      ),
+      'assay_idx4' => array(
+        '0' => 'dbxref_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cv_root_schema()
+ * Purpose: To describe the structure of cv_root to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cv_root_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cv_root',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cv_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'root_cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cvterm_schema()
+ * Purpose: To describe the structure of cvterm to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cvterm_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cvterm',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'cv_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '1024',
+        'not null' => '1',
+      ),
+      'definition' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'is_obsolete' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+      'is_relationshiptype' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'cvterm_id',
+    ),
+    'unique keys' => array(
+      'cvterm_c1' => array(
+        '0' => 'name',
+        '1' => 'cv_id',
+        '2' => 'is_obsolete',
+      ),
+      'cvterm_c2' => array(
+        '0' => 'dbxref_id',
+      ),
+    ),
+    'indexes' => array(
+      'cvterm_idx1' => array(
+        '0' => 'cv_id',
+      ),
+      'cvterm_idx2' => array(
+        '0' => 'name',
+      ),
+      'cvterm_idx3' => array(
+        '0' => 'dbxref_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_dbxref_schema()
+ * Purpose: To describe the structure of feature_dbxref to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_dbxref_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_dbxref',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'is_current' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '1',
+        'default' => 'ru',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'feature_dbxref_id',
+    ),
+    'unique keys' => array(
+      'feature_dbxref_c1' => array(
+        '0' => 'feature_id',
+        '1' => 'dbxref_id',
+      ),
+    ),
+    'indexes' => array(
+      'feature_dbxref_idx1' => array(
+        '0' => 'feature_id',
+      ),
+      'feature_dbxref_idx2' => array(
+        '0' => 'dbxref_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_cvtermprop_schema()
+ * Purpose: To describe the structure of feature_cvtermprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_cvtermprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_cvtermprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_cvtermprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'feature_cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'feature_cvtermprop_id',
+    ),
+    'unique keys' => array(
+      'feature_cvtermprop_c1' => array(
+        '0' => 'feature_cvterm_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'feature_cvtermprop_idx1' => array(
+        '0' => 'feature_cvterm_id',
+      ),
+      'feature_cvtermprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_difference_schema()
+ * Purpose: To describe the structure of feature_difference to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_difference_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_difference',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'srcfeature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => '',
+      ),
+      'fmin' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'fmax' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'strand' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_disjoint_schema()
+ * Purpose: To describe the structure of feature_disjoint to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_disjoint_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_disjoint',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_distance_schema()
+ * Purpose: To describe the structure of feature_distance to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_distance_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_distance',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'srcfeature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'subject_strand' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => '',
+      ),
+      'object_strand' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => '',
+      ),
+      'distance' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_cvterm_pub_schema()
+ * Purpose: To describe the structure of feature_cvterm_pub to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_cvterm_pub_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_cvterm_pub',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_cvterm_pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'feature_cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'feature_cvterm_pub_id',
+    ),
+    'unique keys' => array(
+      'feature_cvterm_pub_c1' => array(
+        '0' => 'feature_cvterm_id',
+        '1' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'feature_cvterm_pub_idx1' => array(
+        '0' => 'feature_cvterm_id',
+      ),
+      'feature_cvterm_pub_idx2' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_cvterm_dbxref_schema()
+ * Purpose: To describe the structure of feature_cvterm_dbxref to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_cvterm_dbxref_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_cvterm_dbxref',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_cvterm_dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'feature_cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'feature_cvterm_dbxref_id',
+    ),
+    'unique keys' => array(
+      'feature_cvterm_dbxref_c1' => array(
+        '0' => 'feature_cvterm_id',
+        '1' => 'dbxref_id',
+      ),
+    ),
+    'indexes' => array(
+      'feature_cvterm_dbxref_idx1' => array(
+        '0' => 'feature_cvterm_id',
+      ),
+      'feature_cvterm_dbxref_idx2' => array(
+        '0' => 'dbxref_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_f_type_schema()
+ * Purpose: To describe the structure of f_type to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_f_type_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'f_type',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'type' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '1024',
+        'not null' => '',
+      ),
+      'residues' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'seqlen' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'md5checksum' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'char',
+        'length' => '32',
+        'not null' => '',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'timeaccessioned' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'datetime',
+        'not null' => '',
+      ),
+      'timelastmodified' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'datetime',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_schema()
+ * Purpose: To describe the structure of feature to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'organism_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'uniquename' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+      'residues' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'seqlen' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'md5checksum' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'char',
+        'length' => '32',
+        'not null' => '',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'is_analysis' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '1',
+        'default' => 'als',
+      ),
+      'is_obsolete' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '1',
+        'default' => 'als',
+      ),
+      'timeaccessioned' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'datetime',
+        'not null' => '1',
+        'default' => 'ow(',
+      ),
+      'timelastmodified' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'datetime',
+        'not null' => '1',
+        'default' => 'ow(',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'feature_id',
+    ),
+    'unique keys' => array(
+      'feature_c1' => array(
+        '0' => 'organism_id',
+        '1' => 'uniquename',
+        '2' => 'type_id',
+      ),
+    ),
+    'indexes' => array(
+      'feature_idx1' => array(
+        '0' => 'dbxref_id',
+      ),
+      'feature_idx2' => array(
+        '0' => 'organism_id',
+      ),
+      'feature_idx3' => array(
+        '0' => 'type_id',
+      ),
+      'feature_idx4' => array(
+        '0' => 'uniquename',
+      ),
+      'feature_name_ind1' => array(
+        '0' => 'name',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_contains_schema()
+ * Purpose: To describe the structure of feature_contains to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_contains_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_contains',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_cvterm_schema()
+ * Purpose: To describe the structure of feature_cvterm to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_cvterm_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_cvterm',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'is_not' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '1',
+        'default' => 'als',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'feature_cvterm_id',
+    ),
+    'unique keys' => array(
+      'feature_cvterm_c1' => array(
+        '0' => 'feature_id',
+        '1' => 'cvterm_id',
+        '2' => 'pub_id',
+        '3' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'feature_cvterm_idx1' => array(
+        '0' => 'feature_id',
+      ),
+      'feature_cvterm_idx2' => array(
+        '0' => 'cvterm_id',
+      ),
+      'feature_cvterm_idx3' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_expression_schema()
+ * Purpose: To describe the structure of feature_expression to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_expression_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_expression',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_expression_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'expression_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'feature_expression_id',
+    ),
+    'unique keys' => array(
+      'feature_expression_c1' => array(
+        '0' => 'expression_id',
+        '1' => 'feature_id',
+        '2' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'feature_expression_idx1' => array(
+        '0' => 'expression_id',
+      ),
+      'feature_expression_idx2' => array(
+        '0' => 'feature_id',
+      ),
+      'feature_expression_idx3' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_expressionprop_schema()
+ * Purpose: To describe the structure of feature_expressionprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_expressionprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_expressionprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_expressionprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'feature_expression_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'feature_expressionprop_id',
+    ),
+    'unique keys' => array(
+      'feature_expressionprop_c1' => array(
+        '0' => 'feature_expression_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'feature_expressionprop_idx1' => array(
+        '0' => 'feature_expression_id',
+      ),
+      'feature_expressionprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_pubprop_schema()
+ * Purpose: To describe the structure of feature_pubprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_pubprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_pubprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_pubprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'feature_pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'feature_pubprop_id',
+    ),
+    'unique keys' => array(
+      'feature_pubprop_c1' => array(
+        '0' => 'feature_pub_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'feature_pubprop_idx1' => array(
+        '0' => 'feature_pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_relationship_schema()
+ * Purpose: To describe the structure of feature_relationship to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_relationship_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_relationship',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_relationship_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'feature_relationship_id',
+    ),
+    'unique keys' => array(
+      'feature_relationship_c1' => array(
+        '0' => 'subject_id',
+        '1' => 'object_id',
+        '2' => 'type_id',
+        '3' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'feature_relationship_idx1' => array(
+        '0' => 'subject_id',
+      ),
+      'feature_relationship_idx2' => array(
+        '0' => 'object_id',
+      ),
+      'feature_relationship_idx3' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_relationship_pub_schema()
+ * Purpose: To describe the structure of feature_relationship_pub to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_relationship_pub_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_relationship_pub',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_relationship_pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'feature_relationship_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'feature_relationship_pub_id',
+    ),
+    'unique keys' => array(
+      'feature_relationship_pub_c1' => array(
+        '0' => 'feature_relationship_id',
+        '1' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'feature_relationship_pub_idx1' => array(
+        '0' => 'feature_relationship_id',
+      ),
+      'feature_relationship_pub_idx2' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_relationshipprop_schema()
+ * Purpose: To describe the structure of feature_relationshipprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_relationshipprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_relationshipprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_relationshipprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'feature_relationship_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'feature_relationshipprop_id',
+    ),
+    'unique keys' => array(
+      'feature_relationshipprop_c1' => array(
+        '0' => 'feature_relationship_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'feature_relationshipprop_idx1' => array(
+        '0' => 'feature_relationship_id',
+      ),
+      'feature_relationshipprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_pub_schema()
+ * Purpose: To describe the structure of feature_pub to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_pub_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_pub',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'feature_pub_id',
+    ),
+    'unique keys' => array(
+      'feature_pub_c1' => array(
+        '0' => 'feature_id',
+        '1' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'feature_pub_idx1' => array(
+        '0' => 'feature_id',
+      ),
+      'feature_pub_idx2' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_phenotype_schema()
+ * Purpose: To describe the structure of feature_phenotype to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_phenotype_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_phenotype',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_phenotype_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'phenotype_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'feature_phenotype_id',
+    ),
+    'unique keys' => array(
+      'feature_phenotype_c1' => array(
+        '0' => 'feature_id',
+        '1' => 'phenotype_id',
+      ),
+    ),
+    'indexes' => array(
+      'feature_phenotype_idx1' => array(
+        '0' => 'feature_id',
+      ),
+      'feature_phenotype_idx2' => array(
+        '0' => 'phenotype_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_genotype_schema()
+ * Purpose: To describe the structure of feature_genotype to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_genotype_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_genotype',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_genotype_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'genotype_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'chromosome_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'cgroup' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'feature_genotype_id',
+    ),
+    'unique keys' => array(
+      'feature_genotype_c1' => array(
+        '0' => 'feature_id',
+        '1' => 'genotype_id',
+        '2' => 'cvterm_id',
+        '3' => 'chromosome_id',
+        '4' => 'rank',
+        '5' => 'cgroup',
+      ),
+    ),
+    'indexes' => array(
+      'feature_genotype_idx1' => array(
+        '0' => 'feature_id',
+      ),
+      'feature_genotype_idx2' => array(
+        '0' => 'genotype_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_intersection_schema()
+ * Purpose: To describe the structure of feature_intersection to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_intersection_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_intersection',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'srcfeature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'subject_strand' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => '',
+      ),
+      'object_strand' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => '',
+      ),
+      'fmin' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'fmax' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_meets_schema()
+ * Purpose: To describe the structure of feature_meets to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_meets_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_meets',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_feature_meets_on_same_strand_schema()
+ * Purpose: To describe the structure of feature_meets_on_same_strand to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_feature_meets_on_same_strand_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'feature_meets_on_same_strand',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_f_loc_schema()
+ * Purpose: To describe the structure of f_loc to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_f_loc_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'f_loc',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'nbeg' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'nend' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'strand' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_expressionprop_schema()
+ * Purpose: To describe the structure of expressionprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_expressionprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'expressionprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'expressionprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'expression_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'expressionprop_id',
+    ),
+    'unique keys' => array(
+      'expressionprop_c1' => array(
+        '0' => 'expression_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'expressionprop_idx1' => array(
+        '0' => 'expression_id',
+      ),
+      'expressionprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_db_dbxref_count_schema()
+ * Purpose: To describe the structure of db_dbxref_count to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_db_dbxref_count_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'db_dbxref_count',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'num_dbxrefs' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'big',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_dbxref_schema()
+ * Purpose: To describe the structure of dbxref to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_dbxref_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'dbxref',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'db_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'accession' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '1',
+      ),
+      'version' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '1',
+        'default' => '',
+      ),
+      'description' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'dbxref_id',
+    ),
+    'unique keys' => array(
+      'dbxref_c1' => array(
+        '0' => 'db_id',
+        '1' => 'accession',
+        '2' => 'version',
+      ),
+    ),
+    'indexes' => array(
+      'dbxref_idx1' => array(
+        '0' => 'db_id',
+      ),
+      'dbxref_idx2' => array(
+        '0' => 'accession',
+      ),
+      'dbxref_idx3' => array(
+        '0' => 'version',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_dbxrefprop_schema()
+ * Purpose: To describe the structure of dbxrefprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_dbxrefprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'dbxrefprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'dbxrefprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+        'default' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'dbxrefprop_id',
+    ),
+    'unique keys' => array(
+      'dbxrefprop_c1' => array(
+        '0' => 'dbxref_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'dbxrefprop_idx1' => array(
+        '0' => 'dbxref_id',
+      ),
+      'dbxrefprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_dfeatureloc_schema()
+ * Purpose: To describe the structure of dfeatureloc to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_dfeatureloc_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'dfeatureloc',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'featureloc_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'srcfeature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'nbeg' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'is_nbeg_partial' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '',
+      ),
+      'nend' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'is_nend_partial' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'boolean',
+        'not null' => '',
+      ),
+      'strand' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => '',
+      ),
+      'phase' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'residue_info' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'locgroup' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_db_schema()
+ * Purpose: To describe the structure of db to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_db_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'db',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'db_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'name' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '1',
+      ),
+      'description' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'urlprefix' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+      'url' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'db_id',
+    ),
+    'unique keys' => array(
+      'db_c1' => array(
+        '0' => 'name',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cvtermsynonym_schema()
+ * Purpose: To describe the structure of cvtermsynonym to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cvtermsynonym_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cvtermsynonym',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cvtermsynonym_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'synonym' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '1024',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'cvtermsynonym_id',
+    ),
+    'unique keys' => array(
+      'cvtermsynonym_c1' => array(
+        '0' => 'cvterm_id',
+        '1' => 'synonym',
+      ),
+    ),
+    'indexes' => array(
+      'cvtermsynonym_idx1' => array(
+        '0' => 'cvterm_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cvterm_dbxref_schema()
+ * Purpose: To describe the structure of cvterm_dbxref to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cvterm_dbxref_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cvterm_dbxref',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cvterm_dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'is_for_definition' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'cvterm_dbxref_id',
+    ),
+    'unique keys' => array(
+      'cvterm_dbxref_c1' => array(
+        '0' => 'cvterm_id',
+        '1' => 'dbxref_id',
+      ),
+    ),
+    'indexes' => array(
+      'cvterm_dbxref_idx1' => array(
+        '0' => 'cvterm_id',
+      ),
+      'cvterm_dbxref_idx2' => array(
+        '0' => 'dbxref_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cvterm_relationship_schema()
+ * Purpose: To describe the structure of cvterm_relationship to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cvterm_relationship_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cvterm_relationship',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cvterm_relationship_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'cvterm_relationship_id',
+    ),
+    'unique keys' => array(
+      'cvterm_relationship_c1' => array(
+        '0' => 'subject_id',
+        '1' => 'object_id',
+        '2' => 'type_id',
+      ),
+    ),
+    'indexes' => array(
+      'cvterm_relationship_idx1' => array(
+        '0' => 'type_id',
+      ),
+      'cvterm_relationship_idx2' => array(
+        '0' => 'subject_id',
+      ),
+      'cvterm_relationship_idx3' => array(
+        '0' => 'object_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cvtermpath_schema()
+ * Purpose: To describe the structure of cvtermpath to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cvtermpath_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cvtermpath',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cvtermpath_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'cv_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pathdistance' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'cvtermpath_id',
+    ),
+    'unique keys' => array(
+      'cvtermpath_c1' => array(
+        '0' => 'subject_id',
+        '1' => 'object_id',
+        '2' => 'type_id',
+        '3' => 'pathdistance',
+      ),
+    ),
+    'indexes' => array(
+      'cvtermpath_idx1' => array(
+        '0' => 'type_id',
+      ),
+      'cvtermpath_idx2' => array(
+        '0' => 'subject_id',
+      ),
+      'cvtermpath_idx3' => array(
+        '0' => 'object_id',
+      ),
+      'cvtermpath_idx4' => array(
+        '0' => 'cv_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_cvtermprop_schema()
+ * Purpose: To describe the structure of cvtermprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_cvtermprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'cvtermprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'cvtermprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+        'default' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'cvtermprop_id',
+    ),
+    'unique keys' => array(
+      'cvterm_id' => array(
+        '0' => 'cvterm_id',
+        '1' => 'type_id',
+        '2' => 'value',
+        '3' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'cvtermprop_idx1' => array(
+        '0' => 'cvterm_id',
+      ),
+      'cvtermprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_eimage_schema()
+ * Purpose: To describe the structure of eimage to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_eimage_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'eimage',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'eimage_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'eimage_data' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'eimage_type' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '1',
+      ),
+      'image_uri' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'eimage_id',
+    ),
+    'unique keys' => array(
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_element_schema()
+ * Purpose: To describe the structure of element to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_element_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'element',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'element_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'feature_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'arraydesign_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+      'dbxref_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'element_id',
+    ),
+    'unique keys' => array(
+      'element_c1' => array(
+        '0' => 'feature_id',
+        '1' => 'arraydesign_id',
+      ),
+    ),
+    'indexes' => array(
+      'element_idx1' => array(
+        '0' => 'feature_id',
+      ),
+      'element_idx2' => array(
+        '0' => 'arraydesign_id',
+      ),
+      'element_idx3' => array(
+        '0' => 'type_id',
+      ),
+      'element_idx4' => array(
+        '0' => 'dbxref_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_expression_cvterm_schema()
+ * Purpose: To describe the structure of expression_cvterm to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_expression_cvterm_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'expression_cvterm',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'expression_cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'expression_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+      'cvterm_type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'expression_cvterm_id',
+    ),
+    'unique keys' => array(
+      'expression_cvterm_c1' => array(
+        '0' => 'expression_id',
+        '1' => 'cvterm_id',
+        '2' => 'cvterm_type_id',
+      ),
+    ),
+    'indexes' => array(
+      'expression_cvterm_idx1' => array(
+        '0' => 'expression_id',
+      ),
+      'expression_cvterm_idx2' => array(
+        '0' => 'cvterm_id',
+      ),
+      'expression_cvterm_idx3' => array(
+        '0' => 'cvterm_type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_expression_cvtermprop_schema()
+ * Purpose: To describe the structure of expression_cvtermprop to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_expression_cvtermprop_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'expression_cvtermprop',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'expression_cvtermprop_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'expression_cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'expression_cvtermprop_id',
+    ),
+    'unique keys' => array(
+      'expression_cvtermprop_c1' => array(
+        '0' => 'expression_cvterm_id',
+        '1' => 'type_id',
+        '2' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'expression_cvtermprop_idx1' => array(
+        '0' => 'expression_cvterm_id',
+      ),
+      'expression_cvtermprop_idx2' => array(
+        '0' => 'type_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_expression_image_schema()
+ * Purpose: To describe the structure of expression_image to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_expression_image_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'expression_image',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'expression_image_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'expression_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'eimage_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'expression_image_id',
+    ),
+    'unique keys' => array(
+      'expression_image_c1' => array(
+        '0' => 'expression_id',
+        '1' => 'eimage_id',
+      ),
+    ),
+    'indexes' => array(
+      'expression_image_idx1' => array(
+        '0' => 'expression_id',
+      ),
+      'expression_image_idx2' => array(
+        '0' => 'eimage_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_expression_pub_schema()
+ * Purpose: To describe the structure of expression_pub to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_expression_pub_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'expression_pub',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'expression_pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'expression_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'pub_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'expression_pub_id',
+    ),
+    'unique keys' => array(
+      'expression_pub_c1' => array(
+        '0' => 'expression_id',
+        '1' => 'pub_id',
+      ),
+    ),
+    'indexes' => array(
+      'expression_pub_idx1' => array(
+        '0' => 'expression_id',
+      ),
+      'expression_pub_idx2' => array(
+        '0' => 'pub_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_expression_schema()
+ * Purpose: To describe the structure of expression to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_expression_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'expression',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'expression_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'uniquename' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+      'md5checksum' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'char',
+        'length' => '32',
+        'not null' => '',
+      ),
+      'description' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'expression_id',
+    ),
+    'unique keys' => array(
+      'expression_c1' => array(
+        '0' => 'uniquename',
+      ),
+    ),
+    'indexes' => array(
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_environment_cvterm_schema()
+ * Purpose: To describe the structure of environment_cvterm to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_environment_cvterm_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'environment_cvterm',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'environment_cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'environment_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'cvterm_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'environment_cvterm_id',
+    ),
+    'unique keys' => array(
+      'environment_cvterm_c1' => array(
+        '0' => 'environment_id',
+        '1' => 'cvterm_id',
+      ),
+    ),
+    'indexes' => array(
+      'environment_cvterm_idx1' => array(
+        '0' => 'environment_id',
+      ),
+      'environment_cvterm_idx2' => array(
+        '0' => 'cvterm_id',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_element_relationship_schema()
+ * Purpose: To describe the structure of element_relationship to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_element_relationship_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'element_relationship',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'element_relationship_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'element_relationship_id',
+    ),
+    'unique keys' => array(
+      'element_relationship_c1' => array(
+        '0' => 'subject_id',
+        '1' => 'object_id',
+        '2' => 'type_id',
+        '3' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'element_relationship_idx1' => array(
+        '0' => 'subject_id',
+      ),
+      'element_relationship_idx2' => array(
+        '0' => 'type_id',
+      ),
+      'element_relationship_idx3' => array(
+        '0' => 'object_id',
+      ),
+      'element_relationship_idx4' => array(
+        '0' => 'value',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_elementresult_schema()
+ * Purpose: To describe the structure of elementresult to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_elementresult_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'elementresult',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'elementresult_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'element_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'quantification_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'signal' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'float',
+        'size' => 'big',
+        'not null' => '1',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'elementresult_id',
+    ),
+    'unique keys' => array(
+      'elementresult_c1' => array(
+        '0' => 'element_id',
+        '1' => 'quantification_id',
+      ),
+    ),
+    'indexes' => array(
+      'elementresult_idx1' => array(
+        '0' => 'element_id',
+      ),
+      'elementresult_idx2' => array(
+        '0' => 'quantification_id',
+      ),
+      'elementresult_idx3' => array(
+        '0' => 'signal',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_elementresult_relationship_schema()
+ * Purpose: To describe the structure of elementresult_relationship to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_elementresult_relationship_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'elementresult_relationship',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'elementresult_relationship_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'subject_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'type_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'object_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+      ),
+      'value' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+      'rank' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'int',
+        'not null' => '1',
+        'default' => '0',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'elementresult_relationship_id',
+    ),
+    'unique keys' => array(
+      'elementresult_relationship_c1' => array(
+        '0' => 'subject_id',
+        '1' => 'object_id',
+        '2' => 'type_id',
+        '3' => 'rank',
+      ),
+    ),
+    'indexes' => array(
+      'elementresult_relationship_idx1' => array(
+        '0' => 'subject_id',
+      ),
+      'elementresult_relationship_idx2' => array(
+        '0' => 'type_id',
+      ),
+      'elementresult_relationship_idx3' => array(
+        '0' => 'object_id',
+      ),
+      'elementresult_relationship_idx4' => array(
+        '0' => 'value',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}
+
+/**
+ * Implements hook_environment_schema()
+ * Purpose: To describe the structure of environment to tripal
+ * @see tripal_core.api.inc: tripal_core_chado_insert()
+ * @see tripal_core.api.inc: tripal_core_chado_update()
+ * @see tripal_core.api.inc: tripal_core_chado_select()
+ *
+ * @return
+ *    An array describing the current table
+ *
+ * @ingroup tripal_schema_api
+ *
+*/
+function tripal_core_chado_environment_schema () {
+  $description = array();
+  
+  $description = array(
+    'table' => 'environment',
+//    'description' => 'TODO: please describe this table!',
+    'fields' => array(
+      'environment_id' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'serial',
+        'not null' => '1',
+      ),
+      'uniquename' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '1',
+      ),
+      'description' => array(
+//        'description' => 'TODO: please describe this field!',
+        'type' => 'text',
+        'not null' => '',
+      ),
+    ),
+    'primary key' => array(
+      '0' => 'environment_id',
+    ),
+    'unique keys' => array(
+      'environment_c1' => array(
+        '0' => 'uniquename',
+      ),
+    ),
+    'indexes' => array(
+      'environment_idx1' => array(
+        '0' => 'uniquename',
+      ),
+    ),
+    'foreign keys' => array(
+    ),
+  );
+  
+  return $description;
+}

+ 283 - 0
modules/base/tripal_core/tripal_core.views.inc

@@ -0,0 +1,283 @@
+<?php
+
+/**
+ * @defgroup views Views Integration
+ * @{
+ * Provide rules for formatting and composition of fields
+ * @}
+ */
+ 
+/**
+ * @defgroup views_field_handlers Views Field Handlers
+ * @{
+ * Provide rules for formatting and composition of fields
+ * @}
+ * @ingroup views
+ */
+
+/**
+ * @defgroup views_filter_handlers Views Filter Handlers
+ * @{
+ * Provide the ability to filter based on specified data
+ * @}
+ * @ingroup views
+ */
+
+/**
+ * @defgroup views_sort_handlers Views Sort Handlers
+ * @{
+ * Provide methods describing how specific data should be sorted
+ * @}
+ * @ingroup views
+ */
+
+/**
+ * @defgroup views_argument_handlers Views Arguement Handlers
+ * @{
+ * Provide the ability to filter pased on arguments in the path of the view
+ * @}
+ * @ingroup views
+ */
+ 
+/**
+ * Implements hook_views_handlers()
+ *
+ * Purpose: Register all custom handlers with views
+ *   where a handler describes either "the type of field", 
+ *   "how a field should be filtered", "how a field should be sorted"
+ *
+ * @return
+ *   An array of handler definitions
+ *
+ * @ingroup tripal_core
+ */
+function tripal_core_views_handlers() {
+ return array(
+   'info' => array(
+     'path' => drupal_get_path('module', 'tripal_core') . '/views/handlers',
+   ),
+   'handlers' => array(
+     'views_handler_field_node_optional' => array(
+       'parent' => 'views_handler_field_node',
+     ),
+     'views_handler_field_cvterm_name' => array(
+       'parent' => 'views_handler_field',
+     ),
+     'views_handler_field_chado_tf_boolean' => array(
+      'parent' => 'views_handler_field_boolean',
+     ),
+     'views_handler_field_chado_count' => array(
+      'parent' => 'views_handler_field',
+     ),
+     'views_handler_filter_chado_select_string' => array(
+      'parent' => 'views_handler_filter_string',
+     ),
+     'views_handler_filter_chado_select_cvterm_name' => array(
+      'parent' => 'views_handler_filter_string',
+     ),
+     'views_handler_filter_chado_boolean' => array(
+      'parent' => 'views_handler_filter_boolean_operator',
+     ),
+     'views_handler_field_chado_rel_by_type' => array(
+      'parent' => 'views_handler_field_prerender_list',
+     ),
+   ),
+ );
+}
+    
+/**
+ * Implements hook_views_pre_render
+ *
+ * Purpose: Intercepts the view after the query has been executed
+ *   All the results are stored in $view->result
+ *   Looking up the NID here ensures the query is only executed once
+ *   for all stocks in the table.
+ *
+ * @todo add if !<chado/drupal same db> around NID portion
+ *
+ * @ingroup tripal_core
+ */
+function tripal_core_views_pre_render	(&$view) {
+		
+		//Add Node IDs in to every table that needs them
+		// @see file: tripal_core.views.inc
+		tripal_core_add_node_ids_to_view ($view);
+		
+}
+
+/**
+ * Adds nid fields to all pertinent view results
+ *
+ * Purpose: To add basetable_nid fields to all result arrays of a view
+ *   only if the basetable_nid field is added. This function will only be
+ *   called if chado/drupal are not in the same database (ie: only if 
+ *   a join between the base and node table isn't possible.
+ *
+ * Note: Supports adding Node IDs to analysis, feature, library, organism, stock
+ *
+ * @param $view
+ *   the view object passed to hook_views_pre_render
+ *
+ * @return the views object with nids added to the result array
+ *
+ * @ingroup tripal_core
+ */
+function tripal_core_add_node_ids_to_view (&$view) {
+	
+	//-----Analysis----------------------------------------------
+	if (!empty($view->field['analysis_nid'])) {
+		// retrieve the analysis_id for each record in the views current page
+		$analysis_ids = array();
+		foreach ($view->result as $row_num => $row) {
+			if (!empty($row->analysis_id)) {
+				//we're looking at analysis id field already in table
+				$analysis_ids[$row_num] = $row->analysis_id;
+			} else {
+				//we're looking at analysis id added by field handler
+				$analysis_ids[$row_num] = $row->analysis_analysis_id;
+			}
+		}
+		$unique_analysis_ids = array_filter($analysis_ids);
+		$unique_analysis_ids = array_unique($unique_analysis_ids);
+
+		if (!empty($unique_analysis_ids)) {
+			// Using the list of analysis_ids from the view
+			// lookup the NIDs from drupal
+			// and add that to the results of the view
+			$sql = "SELECT nid, analysis_id FROM {chado_analysis} WHERE analysis_id IN (".implode(',',$unique_analysis_ids).")";
+			$resource = db_query($sql);
+			while ($r = db_fetch_object($resource)) {
+				$keys = array_keys($analysis_ids, $r->analysis_id);
+				foreach ($keys as $k) {
+					$view->result[$k]->analysis_nid = $r->nid;
+				}
+			}
+		} // if there are any analysis'
+	} //end of case for analysis NID
+	
+	//-----Feature-----------------------------------------------
+	if (!empty($view->field['feature_nid'])) {
+		// retrieve the feature_id for each record in the views current page
+		$feature_ids = array();
+		foreach ($view->result as $row_num => $row) {
+			if (!empty($row->feature_id)) {
+				//we're looking at feature id field already in table
+				$feature_ids[$row_num] = $row->feature_id;
+			} else {
+				//we're looking at feature id added by field handler
+				$feature_ids[$row_num] = $row->feature_feature_id;
+			}
+		}
+		$unique_feature_ids = array_filter($feature_ids);
+		$unique_feature_ids = array_unique($unique_feature_ids);
+
+		if (!empty($unique_feature_ids)) {
+			// Using the list of feature_ids from the view
+			// lookup the NIDs from drupal
+			// and add that to the results of the view
+			$sql = "SELECT nid, feature_id FROM {chado_feature} WHERE feature_id IN (".implode(',',$unique_feature_ids).")";
+			$resource = db_query($sql);
+			while ($r = db_fetch_object($resource)) {
+				$keys = array_keys($feature_ids, $r->feature_id);
+				foreach ($keys as $k) {
+					$view->result[$k]->feature_nid = $r->nid;
+				}
+			}
+		} // if there are any features
+	} //end of case for feature NID
+	
+	//-----Library-----------------------------------------------
+	if (!empty($view->field['library_nid'])) {
+		// retrieve the library_id for each record in the views current page
+		$library_ids = array();
+		foreach ($view->result as $row_num => $row) {
+			if (!empty($row->library_id)) {
+				//we're looking at library id field already in table
+				$library_ids[$row_num] = $row->library_id;
+			} else {
+				//we're looking at library id added by field handler
+				$library_ids[$row_num] = $row->library_library_id;
+			}
+		}
+		$unique_library_ids = array_filter($library_ids);
+		$unique_library_ids = array_unique($unique_library_ids);
+
+		if (!empty($unique_library_ids)) {
+			// Using the list of library_ids from the view
+			// lookup the NIDs from drupal
+			// and add that to the results of the view
+			$sql = "SELECT nid, library_id FROM {chado_library} WHERE library_id IN (".implode(',',$unique_library_ids).")";
+			$resource = db_query($sql);
+			while ($r = db_fetch_object($resource)) {
+				$keys = array_keys($library_ids, $r->library_id);
+				foreach ($keys as $k) {
+					$view->result[$k]->library_nid = $r->nid;
+				}
+			}
+		} // if there are libraries
+	} //end of case for library NID
+	
+	//-----Organism----------------------------------------------
+	if (!empty($view->field['organism_nid'])) {
+		// retrieve the organism_id for each record in the views current page
+		$organism_ids = array();
+		foreach ($view->result as $row_num => $row) {
+			if (!empty($row->organism_id)) {
+				//we're looking at organism id field already in table
+				$organism_ids[$row_num] = $row->organism_id;
+			} else {
+				//we're looking at organism id added by field handler
+				$organism_ids[$row_num] = $row->organism_organism_id;
+			}
+		}
+		$unique_organism_ids = array_filter($organism_ids);
+		$unique_organism_ids = array_unique($unique_organism_ids);
+
+		if (!empty($unique_organism_ids)) {
+			// Using the list of organism_ids from the view
+			// lookup the NIDs from drupal
+			// and add that to the results of the view
+			$sql = "SELECT nid, organism_id FROM {chado_organism} WHERE organism_id IN (".implode(',',$unique_organism_ids).")";
+			$resource = db_query($sql);
+			while ($r = db_fetch_object($resource)) {
+				$keys = array_keys($organism_ids, $r->organism_id);
+				foreach ($keys as $k) {
+					$view->result[$k]->organism_nid = $r->nid;
+				}
+			}
+		} // if there are organisms
+	} //end of case for organism NID
+	
+	//-----Stock-------------------------------------------------
+	if (!empty($view->field['stock_nid'])) {
+		// retrieve the stock_id for each record in the views current page
+		$stock_ids = array();
+		foreach ($view->result as $row_num => $row) {
+			if (!empty($row->stock_id)) {
+				//we're looking at stock id field already in table
+				$stock_ids[$row_num] = $row->stock_id;
+			} else {
+				//we're looking at stock id added by field handler
+				$stock_ids[$row_num] = $row->stock_stock_id;
+			}
+		}
+		$unique_stock_ids = array_filter($stock_ids);
+		$unique_stock_ids = array_unique($unique_stock_ids);
+
+		if (!empty($unique_stock_ids)) {
+			// Using the list of stock_ids from the view
+			// lookup the NIDs from drupal
+			// and add that to the results of the view
+			$sql = "SELECT nid, stock_id FROM {chado_stock} WHERE stock_id IN (".implode(',',$unique_stock_ids).")";
+			$resource = db_query($sql);
+			while ($r = db_fetch_object($resource)) {
+				$keys = array_keys($stock_ids, $r->stock_id);
+				foreach ($keys as $k) {
+					$view->result[$k]->stock_nid = $r->nid;
+				}
+			}
+		} //if there are stocks
+	} //end of case for stock NID
+		
+	return $view;
+}

+ 81 - 0
modules/base/tripal_core/tripal_core_job_view.tpl.php

@@ -0,0 +1,81 @@
+<?php
+$job = $variables['job'];
+
+$cancel_link = '';
+$return_link = url("admin/tripal/tripal_jobs/");
+if($job->start_time == 0 and $job->end_time == 0){
+   $cancel_link = url("admin/tripal/tripal_jobs/cancel/".$job->job_id);
+}
+$rerun_link = url("admin/tripal/tripal_jobs/rerun/".$job->job_id);
+ 
+?>
+
+<div id="tripal_core-job_view-box" class="tripal_core-info-box tripal-info-box">
+  <div class="tripal_core-info-box-title tripal-info-box-title">Details for Job <?php print $job->job_id?></div>
+  <div class="tripal_core-info-box-desc tripal-info-box-desc"></div>
+   <a href="<?php print $return_link?>">Return to jobs list</a> | 
+   <a href="<?php print $rerun_link?>">Re-run this job</a> | 
+   <a href="<?php print $cancel_link?>">Cancel this Job</a><br>
+   <table id="tripal_core-job_view-table" class="tripal_core-table tripal-table tripal-table-vert">
+      <tr class="tripal_core-table-odd-row tripal-table-even-row tripal-table-first-row">
+         <th>Job Description</th>
+         <td><?php print $job->job_name?></td>
+      </tr>
+      <tr class="tripal_core-table-odd-row tripal-table-odd-row">
+         <th>Submitting Module</th>
+         <td><?php print $job->modulename?></td>
+      </tr>
+      <tr class="tripal_core-table-odd-row tripal-table-even-row">
+         <th>Callback function</th>
+         <td><?php print $job->callback?></td>
+      </tr>
+      <tr class="tripal_core-table-odd-row tripal-table-odd-row">
+         <th>Arguments</th>
+         <td>
+           <table>
+           <?php foreach($job->arguments as $key => $value){
+              print "<tr><td>$key</td><td>$value</td></tr>";
+           } ?>
+           </table>
+         </td>
+      </tr>
+      <tr class="tripal_core-table-odd-row tripal-table-even-row">
+         <th>Progress</th>
+         <td><?php print $job->progress?>%</td>
+      </tr>
+      <tr class="tripal_core-table-odd-row tripal-table-odd-row">
+         <th>Status</th>
+         <td><?php print $job->job_status?></td>
+      </tr>
+      <tr class="tripal_core-table-odd-row tripal-table-even-row">
+         <th>Process ID</th>
+         <td><?php print $job->pid?></td>
+      </tr>
+      <tr class="tripal_core-table-odd-row tripal-table-odd-row">
+         <th>Submit Date</th>
+         <td><?php print $job->submit_date?></td>
+      </tr>
+      <tr class="tripal_core-table-odd-row tripal-table-even-row">
+         <th>Start time</th>
+         <td><?php print $job->start_time?></td>
+      </tr>
+      <tr class="tripal_core-table-odd-row tripal-table-odd-row">
+         <th>End time</th>
+         <td><?php print $job->end_time?></td>
+      </tr>
+      <tr class="tripal_core-table-odd-row tripal-table-even-row">
+         <th>Error Message</th>
+         <td><?php print $job->error_msg?></td>
+      </tr>
+      <tr class="tripal_core-table-odd-row tripal-table-odd-row">
+         <th>Priority</th>
+         <td><?php print $job->priority?></td>
+      </tr>
+      <tr class="tripal_core-table-odd-row tripal-table-even-row tripal-table-last-row">
+         <th>Submitting User</th>
+         <td><?php print $job->username?></td>
+      </tr>
+   </table>
+</div>
+
+

+ 57 - 0
modules/base/tripal_core/tripal_launch_jobs.php

@@ -0,0 +1,57 @@
+<?php
+//
+// Copyright 2009 Clemson University
+//
+
+/* 
+
+This script must be run at the base directory level of the drupal installation 
+in order to pick up all necessary dependencies 
+
+*/
+
+  $stdout = fopen('php://stdout', 'w');
+
+  // we require one command-line argument
+  if(sizeof($argv) < 2){
+     print_usage($stdout);
+     exit;
+  }
+
+  $drupal_base_url = parse_url('http://www.example.com');
+  $_SERVER['HTTP_HOST'] = $drupal_base_url['host'];
+//  $_SERVER['PHP_SELF'] = $drupal_base_url['path'].'/index.php';
+  $_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);
+
+  fwrite($stdout, "Tripal Job Launcher\n");
+  fwrite($stdout, "-------------------\n");
+
+  // check to make sure the username is valid
+  $username = $argv[1];
+  $do_parallel = $argv[2];
+  if(!db_fetch_object(db_query("SELECT * FROM {users} WHERE name = '$username'"))){
+     fwrite($stdout, "'$username' is not a valid Drupal username. exiting...\n");
+     exit;
+  }
+  global $user;
+  $user = user_load(array('name' => $username));
+
+  tripal_jobs_launch($do_parallel);
+
+/**
+ *
+ *
+ * @ingroup tripal_core
+ */
+function print_usage ($stdout){
+  fwrite($stdout,"Usage:\n");
+  fwrite($stdout,"  php ./sites/all/modules/tripal_core/tripal_launch_jobs <username> \n\n");
+  fwrite($stdout,"    where <username> is a Drupal user name\n\n");
+}
+
+?>

+ 106 - 0
modules/base/tripal_core/tripal_launch_jobs_multi.php

@@ -0,0 +1,106 @@
+<?php
+
+/* This script can be used to launch jobs on a multi-site Drupal installation*/
+
+include_once './includes/bootstrap.inc';
+
+fwrite(STDOUT,"Running Tripal Job Launcher\n");
+
+/***********
+* SETTINGS
+**********/
+
+//the location of the 'sites' directory relative to this script.
+$sitesDir = 'sites';
+$debug=0;
+/***********
+* END SETTINGS
+**********/
+
+
+//error_reporting(E_ALL);
+
+include ("Console/Getopt.php");
+
+// initialize object
+$cg = new Console_Getopt();
+
+/* define list of allowed options - p = h:sitename, u:username  */
+$allowedShortOptions = "h:u:";
+
+// read the command line
+$args = $cg->readPHPArgv();
+
+// get the options
+$ret = $cg->getopt($args, $allowedShortOptions);
+
+// check for errors and die with an error message if there was a problem
+if (PEAR::isError($ret)) {
+    die ("Error in command line: " . $ret->getMessage() . "\n");
+}
+
+ini_set('include_path',ini_get('include_path'). PATH_SEPARATOR . './scripts');
+
+/* This doesn't work in every case: getopt function is not always available
+$options = getopt("h:r:");
+var_dump($options);
+*/
+
+$hostname = "";
+$username = "";
+
+// parse the options array
+$opts = $ret[0];
+if (sizeof($opts) > 0) {
+    // if at least one option is present
+    foreach ($opts as $opt) {
+        switch ($opt[0]) {
+            case 'h':
+                $hostname = $opt[1];
+                break;
+            case 'u':
+                $username = $opt[1];
+                break;
+            default:
+                fwrite(STDOUT,'Usage: \n');
+                fwrite(STDOUT,'- h hostname\n');
+                fwrite(STDOUT." -u username\n");
+                break;
+
+        }
+    }
+}else{
+  fwrite(STDOUT,"Usage: \n");
+  fwrite(STDOUT," -h hostname\n");
+  fwrite(STDOUT," -u username\n"); 
+  
+}
+
+runjob($hostname, $username);
+
+/**
+ *
+ *
+ * @ingroup tripal_core
+ */
+function runjob($sitename, $username) {
+
+    $_SERVER['SCRIPT_NAME'] = '/sites/all/modules/tripal_jobs/tripal_launch_jobs_multi.php';
+    $_SERVER['SCRIPT_FILENAME'] = '/sites/all/modules/tripal_jobs/tripal_launch_jobs_multi.php';
+    $_SERVER['HTTP_HOST'] = $sitename;
+    $_SERVER['REMOTE_ADDR'] = 'localhost';
+    $_SERVER['REQUEST_METHOD'] = 'GET';
+
+    drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
+        
+    if(!db_fetch_object(db_query("SELECT * FROM {users} WHERE name = '$username'"))){
+     fwrite(STDOUT, "'$username' is not a valid Drupal username. exiting...\n");
+     exit;
+    }
+    global $user;
+    $user = $username;
+    $user = user_load(array('name' => $username));
+
+    tripal_jobs_launch();
+}
+?>

+ 45 - 0
modules/base/tripal_core/views/README

@@ -0,0 +1,45 @@
+
+This directory contains all additional views code needed to integrate this module with views2.
+
+FILE/FOLDER DESCRIPTIONS
+---------------------------
+<chado table name>.views.inc:
+	contains a single function retrieve_<chado table name>_views_data()
+	which describes that table to views. This function is called by
+	tripal_core_views_data() in ../tripal_core.views.inc.
+	For more information on the form of this data array look up the
+	views2 documentation for hook_views_data() 
+	-http://views2.logrus.com/doc/html/index.html
+	
+handlers/
+	Each file contained within this folder defines a views handler. Only custom
+	handlers are included in this folder and each must be described in 
+	hook_views_handlers() in ../tripal_core.views.inc.
+	A views handler does one of the following:
+		1) describe the type of a field and how it should be displayed
+		2) describe a method to sort this field
+		3) describe a method to filter this field
+
+
+STANDARDS TO FOLLOW
+---------------------------
+
+1. All table definition files should be named tablename.views.inc
+2. All handlers should be in a handlers sub-directory and follow the naming convention of
+   views handlers (ie: views_handler_field/filter/sort_handlername.inc )
+
+Views Table Definitions:
+- Please use the template files provided whenever you are describing a new table to views.
+   For any table in chado simply copy template.table_defn.views.inc to tablename.views.inc and 
+   follow the instructions listed at the top of the template file.
+- ONLY ONE TABLE DEFINITION PER FILE
+- To join a chado table to it's drupal node simply copy template.node_join.views.inc to 
+   basetablename.views.inc and replace all XXX with basetablename.
+   
+   NOTE: Creating the table definition file is not enough. You also need to call the 
+         retrieve_XXX_views_data() function from ../tripal_core.views.inc:tripal_core_views_data()
+         by adding the following line:
+            $data = array_merge($data, retrieve_XXX_views_data());
+         to the function and including the file directly above the function (blow the function 
+         header by adding:
+            require_once('views/XXX.views.inc');

+ 65 - 0
modules/base/tripal_core/views/handlers/views_handler_field_chado_count.inc

@@ -0,0 +1,65 @@
+<?php
+ 
+/**
+ * Purpose: Provide a field that counts the number of records in the current table
+ *   are connected to the base table. For example, this field could be used to count
+ *   the number of features connected to a given organism -base table=organism, 
+ *   current table=feature: for each record in the organism view count feature records where 
+ *   feature.organism_id=organism_id of current organism record
+ *
+ * @ingroup views_field_handlers
+ * @ingroup tripal_core
+ */
+class views_handler_field_chado_count extends views_handler_field {
+
+  function init(&$view, $options) {
+    parent::init($view, $options);
+    
+    // the table to query is required
+    // check that it was provided in the field definition and if not warn
+    if ($this->definition['table_to_query']) {
+      $this->aliases['current_table'] = $this->definition['table_to_query'];
+    } else {
+      drupal_set_message("The field definition ( in hook_views_data() ) needs to specify the 'table_to_query' in order for this fields to work. Field:".$this->field." in the ".$this->table." table definition", 'error');
+    }
+    
+    // set aliases
+    $this->aliases['primary_id'] = $this->table . '_id';
+    $this->aliases['foreign_key'] = $this->table . '_id';
+  }
+  
+  //Needed to ensure that the name of this field is not added to the query
+  function query() {
+    $this->add_additional_fields();
+  }
+  
+  function pre_render(&$values) {
+    // Render nothing if the current table wasn't set in the field definition
+    if(!$this->aliases['current_table']) {
+      return '';
+    }
+
+    foreach ($values as $key => $record) {
+      $primary_id = $record->{$this->aliases['primary_id']};
+      
+      //Select count from database
+      $sql = 'SELECT count(*) as count FROM %s WHERE %s=%d';
+      $previous_db = tripal_db_set_active('chado');
+      $result = db_fetch_object(db_query(
+        $sql, 
+        $this->aliases['current_table'], 
+        $this->aliases['foreign_key'],
+        $primary_id
+      ));
+      tripal_db_set_active($previous_db);
+      
+      //Add to view results
+      $this->view->result[$key]->{$this->field} = $result->count;
+    }
+  }
+  
+  function render($values) {
+    return $values->{$this->field};
+  }
+
+}

+ 209 - 0
modules/base/tripal_core/views/handlers/views_handler_field_chado_rel_by_type.inc

@@ -0,0 +1,209 @@
+<?php
+
+/**
+ * Field handler allowing all relationships of a specified type for a given chado entity to be displayed in 
+ * a single cell
+ *
+ * @ingroup views_field_handlers
+ */
+class views_handler_field_chado_rel_by_type extends views_handler_field_prerender_list {
+  function init(&$view, $options) {
+    parent::init($view, $options);
+
+    // Boolean to determine whether
+    //    TRUE =>  value (property type)      -more than one property type displayed
+    //    FALSE => or just value is rendered  -only 1 porperty type is displayed    
+    $this->display_type = TRUE;
+
+    $this->chado_type = $this->table;
+    
+    // Check that this chado type has a <chado type>_relationship table
+    $sql = "SELECT true as bool FROM pg_tables WHERE tablename='%s_relationship'";
+    $previous_db = tripal_db_set_active('chado');
+    $exists = db_fetch_object(db_query($sql,$this->table));
+    tripal_db_set_active($previous_db);
+    if (!$exists->bool) {
+      drupal_set_message('The Relationship by Type handler cannot be used with this table since the '.$this->table.'_relationship table doesn\'t exist', 'error');
+      $this->broken = TRUE;
+    }
+  }
+
+  function option_definition() {
+    $options = parent::option_definition();
+    $options['rel_type_ids'] = array('default' => array());
+    $options['rel_display_options'] = array('default' => array('subject','type','object'));
+    return $options;
+  }
+
+  /**
+   * Provide "link to term" option.
+   */
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+
+    if ($this->broken) {
+      $form = array(
+        'broken' => array(
+          '#type' => 'item',
+          '#value' => 'The Relationship by Type handler cannot be used with this table since the '.$this->table.'_relationship table doesn\'t exist',
+        ),
+      );
+    } else {    
+      $form['rel_display_parts'] = array(
+        '#type' => 'checkboxes',
+        '#title' => t('Display Relationship Parts'),
+        '#description' => t('Check each part of the relationship you want displayed where the part '
+          .'of a relationship are: [Subject] [Relationship Type] [Object]. '
+          .'For example, with the relationship Sarah is the maternal parent of Fred '
+          .'if you checked only Object then "Fred" would be displayed.'),
+        '#options' => array(
+          'subject' => 'Subject',
+          'type' => 'Relationship Type',
+          'object' => 'Object',
+        ),
+        '#default_value' => array($this->options['rel_display_parts']['subject'], $this->options['rel_display_parts']['type'], $this->options['rel_display_parts']['object']),
+      );
+      
+      $form['rel_display_rels'] = array(
+        '#type' => 'radios',
+        '#title' => t('Display Relationships Where'),
+        '#description' => t('Only relationships where the selected criteria is met will be shown. '
+          .'The parts of a relationship are: [Subject] [Relationship Type] [Object]. '
+          .'For example, with the relationships Sarah is the maternal parent of Fred and '
+          .'Fred is the paternal_parent of Max where Fred is the current '.$this->chado_type.', '
+          .'if you selected "Current '.$this->chado_type.' is the Object" only Sarah is the maternal parent of Fred'
+          .' would be displayed.'),
+        '#options' => array(
+          'subject' => 'Current '.$this->chado_type.' is the Subject',
+          'object' => 'Current '.$this->chado_type.' is the Object',
+          'all' => 'Current '.$this->chado_type.' is the Subject and/or Object',
+        ),
+        '#default_value' => $this->options['rel_display_rels'],
+      );
+      
+      $options = array();
+      $sql = 'SELECT rel.type_id, cvt.name FROM %s_relationship rel LEFT JOIN cvterm cvt ON cvt.cvterm_id=rel.type_id GROUP BY rel.type_id,cvt.name';
+      $previous_db = tripal_db_set_active('chado');
+      $resource = db_query($sql,$this->table);
+      tripal_db_set_active($previous_db);      
+      while ($r = db_fetch_object($resource)) {
+        $options[ $r->type_id ] = $r->name;
+      }
+      $form['rel_type_ids'] = array(
+        '#type' => 'checkboxes',
+        '#title' => t('Relationship Types'),
+        '#options' => $options,
+        '#default_value' => $this->options['rel_type_ids'],
+      );
+    }
+  }
+
+  /**
+   * Add this term to the query
+   */
+  function query() {
+    $this->add_additional_fields();
+  }
+  
+  function pre_render($values) {
+    $this->aliases['relationships'] = $this->chado_type . '_relationships';
+    $chado_id = $this->table . '_id';
+    $chado_relationship_id = $this->table . '_relationship_id';
+    $this->aliases[$chado_id] = $chado_id;
+    $this->field_alias = $this->aliases[$chado_id];
+    
+    //check if relationships added to results, if not then add
+    if (empty( $this->view->result[0]->{$this->aliases['relationships']} )) {
+      
+      // retrieve the chado_id for each record in the views current page
+      $chado_ids = array();
+      foreach ($this->view->result as $row_num => $row) {
+        $chado_ids[$row_num] = $row->{$chado_id};
+      }
+      
+      // Add relationships to the view results
+      $sql = "SELECT rel.*, cvterm.name as type_name, "
+        ."subject.name as subject_name, object.name as object_name "
+        ."FROM ".$this->table."_relationship rel "
+        ."LEFT JOIN ".$this->table." subject ON rel.subject_id=subject.".$chado_id." "
+        ."LEFT JOIN ".$this->table." object ON rel.object_id=object.".$chado_id." "
+        ."LEFT JOIN cvterm cvterm ON rel.type_id = cvterm.cvterm_id "
+        ."WHERE rel.subject_id IN (".implode(',',$chado_ids).") "
+        ."OR rel.object_id IN (".implode(',',$chado_ids).") ";
+      $previous_db = tripal_db_set_active('chado');
+      $resource = db_query($sql);
+      tripal_db_set_active($previous_db);
+    
+      while ($r = db_fetch_object($resource)) {
+        if (in_array($r->subject_id, $chado_ids)) {
+          $key = array_search($r->subject_id, $chado_ids);
+          $r->{$chado_id} = $r->subject_id;
+          $this->view->result[$key]->{$this->aliases['relationships']}[] = clone $r;
+        }
+        if (in_array($r->object_id, $chado_ids)) {
+          $key = array_search($r->object_id, $chado_ids);
+          $r->{$chado_id} = $r->object_id;
+          $this->view->result[$key]->{$this->aliases['relationships']}[] = clone $r;
+        }
+      }
+
+    }
+    
+    // add relationships to this field
+    foreach ($this->view->result as $result) {
+      if (!empty($result->{$this->aliases['relationships']})) {
+        $relationships2keep = array_filter($this->options['rel_type_ids']);
+        
+        // all relationships including the current chado guy
+        foreach ($result->{$this->aliases['relationships']} as $relationship) {      
+          // perform filtering------
+          //type
+          if (!empty($this->options['rel_type_ids'])) {
+            if (!in_array($relationship->type_id, $relationships2keep)) {
+              continue;
+            }
+          }
+          
+          //"Display Relationships Where" criteria
+          if (preg_match('/subject/', $this->options['rel_display_rels'])) {
+            if ($relationship->{$chado_id} != $relationship->subject_id) {
+              continue;
+            }      
+          } elseif (preg_match('/object/', $this->options['rel_display_rels'])) {
+            if ($relationship->{$chado_id} != $relationship->object_id) {
+              continue;
+            }
+          }
+          
+          // Add relationship to the list of items to be rendered
+          $this->items[$relationship->{$chado_id}][$relationship->{$chado_relationship_id}][$chado_id] = $relationship->{$chado_id};
+          $this->items[$relationship->{$chado_id}][$relationship->{$chado_relationship_id}][$chado_relationship_id] = $relationship->{$chado_relationship_id};
+          $this->items[$relationship->{$chado_id}][$relationship->{$chado_relationship_id}]['subject_id'] = $relationship->subject_id;
+          $this->items[$relationship->{$chado_id}][$relationship->{$chado_relationship_id}]['subject_name'] = $relationship->subject_name;
+          $this->items[$relationship->{$chado_id}][$relationship->{$chado_relationship_id}]['object_id'] = $relationship->object_id;
+          $this->items[$relationship->{$chado_id}][$relationship->{$chado_relationship_id}]['object_name'] = $relationship->object_name;
+          $this->items[$relationship->{$chado_id}][$relationship->{$chado_relationship_id}]['type_id'] = $relationship->type_id;
+          $this->items[$relationship->{$chado_id}][$relationship->{$chado_relationship_id}]['type_name'] = $relationship->type_name;
+        }
+      }
+    } 
+    
+  }
+
+  function render_item($count, $item) {
+    $text = array();
+    
+    // Render Parts
+    if ($this->options['rel_display_parts']['subject']) {
+      $text[] = $item['subject_name'];
+    }
+    if ($this->options['rel_display_parts']['type']) {
+      $text[] = $item['type_name'];
+    }
+    if ($this->options['rel_display_parts']['object']) {
+      $text[] = $item['object_name'];
+    }
+    
+    return implode(' ', $text);
+  }
+}

+ 38 - 0
modules/base/tripal_core/views/handlers/views_handler_field_chado_tf_boolean.inc

@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * A handler to provide proper displays for booleans.
+ *
+ * Allows for display of true/false, yes/no, on/off.
+ *
+ * Definition terms:
+ *   - output formats: An array where the first entry is displayed on boolean false
+ *      and the second is displayed on boolean true. An example for sticky is:
+ *      @code
+ *      'output formats' => array(
+ *        'sticky' => array('', t('Sticky')),
+ *      ),
+ *      @endcode
+ *
+ * @ingroup views_field_handlers
+ * @ingroup tripal_core
+ */
+class views_handler_field_chado_tf_boolean extends views_handler_field_boolean {
+
+  // Changes the rendered value: t='Yes' & f='No'
+  // Rendered value depends on type of value chosen in options
+  function render($values) {
+    $value = $values->{$this->field_alias};
+    if (!empty($this->options['not'])) {
+      $value = !$value;
+    }
+
+    if (isset($this->formats[$this->options['type']])) {
+      return preg_match('/t/',$value) ? $this->formats[$this->options['type']][0] : $this->formats[$this->options['type']][1];
+    }
+    else {
+      return preg_match('/t/', $value) ? $this->formats['yes-no'][0] : $this->formats['yes-no'][1];
+    }
+  }
+  
+}

+ 67 - 0
modules/base/tripal_core/views/handlers/views_handler_field_cvterm_name.inc

@@ -0,0 +1,67 @@
+<?php 
+// $Id$
+
+/**
+ *
+ * @ingroup views_field_handlers
+ * @ingroup tripal_core
+ */
+class views_handler_field_cvterm_name extends views_handler_field {
+
+  /**
+   * Add join to the cvterm table and cvterm.name field to the query
+   */
+  function query() {
+      
+    // if this table isn't a base table
+    // if it is this can be done by describing a join between base table and cvterm
+    if (!preg_match('/^'.$this->view->base_table.'$/', $this->table)) {
+
+      //Want to add join: LEFT JOIN cvterm nd_experimentprop_cvterm ON nd_experimentprop.type_id = nd_experimentprop_cvterm.cvterm_id
+      //===============================================================      
+
+      $this->query->ensure_table($this->table);
+      
+      // add to table_queue--------------------------------------------
+      $cvterm_join['table'] = 'cvterm';
+      $cvterm_join['num'] = 1;
+      $cvterm_join['alias'] = $this->table . '_cvterm';
+      $cvterm_join['relationship'] = $this->table;
+
+      $cvterm_join['join'] = clone($this->query->table_queue[$this->table]['join']);
+      $cvterm_join['join']->table = 'cvterm';
+      $cvterm_join['join']->field = 'cvterm_id';
+      $cvterm_join['join']->left_table = $this->table;
+      $cvterm_join['join']->left_field = 'type_id';
+      
+      $cvterm_join['join']->definition['table'] = 'cvterm';
+      $cvterm_join['join']->definition['field'] = 'cvterm_id';
+      $cvterm_join['join']->definition['left_table'] = $this->table;
+      $cvterm_join['join']->definition['left_field'] = 'type_id';
+
+      $this->query->table_queue[$cvterm_join['alias']] = $cvterm_join;
+
+      // add to table--------------------------------------------------
+      $this->query->tables[$this->view->base_table][$this->table.'_cvterm'] = array(
+        'count' => 1,
+        'alias' => $this->table.'_cvterm',
+      );    
+
+      //Want to add field: nd_experimentprop_cvterm.name as nd_experimentprop_type
+      //===============================================================
+      $field_alias = $this->table.'_cvterm_name';
+      $this->query->add_field($this->table.'_cvterm', 'name');
+      $this->add_additional_fields();
+      $this->aliases['name'] = $field_alias;  
+      
+    }
+  }
+  
+  /**
+   * Ensure that the type/cvterm name is rendered
+   */
+ function render($values) { 
+   return $values->{$this->aliases['name']}; 
+ }
+  
+}

+ 42 - 0
modules/base/tripal_core/views/handlers/views_handler_field_dbxref_accession_link.inc

@@ -0,0 +1,42 @@
+<?php
+
+/**
+ *
+ * @ingroup views_field_handlers
+ * @ingroup tripal_core
+ */
+class views_handler_field_dbxref_accession_link extends views_handler_field {
+ function construct() {
+   parent::construct();
+   $this->additional_fields['accession'] = 'accession';
+   $this->additional_fields['db_id'] = 'db_id';
+
+ }
+
+ function query() { 
+   $this->ensure_my_table();
+   $this->add_additional_fields(); 
+ }
+
+ function render($values) { 
+
+   $accession = $values->{$this->aliases['accession']}; 
+   $db_id = $values->{$this->aliases['db_id']};
+
+   if (!empty($db_id) AND !empty($accession)) {
+     $previous_db = tripal_db_set_active('chado');
+     $result = db_fetch_object(db_query('SELECT urlprefix FROM db WHERE db_id=%d', $db_id));
+     tripal_db_set_active($previous_db);   
+     $urlprefix = $result->urlprefix;
+
+     if (!empty($urlprefix)) {
+       $accession_link = $urlprefix . $accession;
+       return($accession_link); 
+     } else {
+       return $accession;
+     }
+   } else {
+     return $accession;
+   }
+ } 
+} 

+ 94 - 0
modules/base/tripal_core/views/handlers/views_handler_field_node_optional.inc

@@ -0,0 +1,94 @@
+<?php
+
+/**
+ * Field handler to provide simple renderer that allows linking to a node.
+ *
+ * @ingroup views_field_handlers
+ * @ingroup tripal_core
+ */
+class views_handler_field_node_optional extends views_handler_field_node {
+
+  function construct() {
+    parent::construct();
+    $this->additional_fields['nid'] = array('table' => 'node', 'field' => 'nid');
+    if (module_exists('translation')) {
+      $this->additional_fields['language'] = array('table' => 'node', 'field' => 'language');
+    }
+  }
+  
+  /**
+   * Add chado_* and *_node alias'd left joins to the table
+   */
+  function query() {
+    
+    // Check what we have (ie: current table? node table? )
+    $chado_table = 'chado_' . $this->table;
+    foreach ($this->query->table_queue as $table_def) {
+      //check is $this->table
+      if ($table_def['table'] == $this->table) {
+        $this_table_alias = $table_def['alias'];
+      }
+      
+      // check is node joined to #this->table
+      if ($table_def['table'] == $chado_table) {
+        $node_table_alias = $table_def['alias'];
+      }
+    }
+    
+    // First: Add the main field-----------------------------------------------
+    if (!$this_table_alias) {
+      $this_table_alias = $this->ensure_my_table();
+    }
+    $field_alias = $this->query->add_field($this_table_alias, $this->real_field); 
+    $this->aliases[ $this->real_field ] = $field_alias;
+    $this->field_alias = $field_alias;
+    
+    // Second: Add nid field and Joins if necessary----------------------------
+    // Add node join if needed
+    if (!$node_table_alias) {
+      $def['table'] = $chado_table;
+      $def['field'] = $this->table . '_id';
+      $def['left_table'] = $this_table_alias;
+      $def['left_field'] = $this->table . '_id';
+      
+      $join = new views_join();
+      $join->definition = $def;
+      $join->construct();
+      $join->adjusted = TRUE;
+    
+      $node_table_alias = $this->query->add_relationship($def['table'], $join, $def['table']);
+    }
+    
+    // Finally Add Field
+    $field_alias = $this->query->add_field($node_table_alias, 'nid'); 
+    $this->aliases['nid'] = $field_alias;   
+
+  }
+
+  /**
+   * Render whatever the data is as a link to the node.
+   *
+   * Data should be made XSS safe prior to calling this function.
+   */
+  function render_link($data, $values) {
+    if (!empty($this->options['link_to_node']) && $data !== NULL && $data !== '') {  
+      if (!empty($values->{$this->aliases['nid']})) {
+        $this->options['alter']['make_link'] = TRUE;
+        $this->options['alter']['path'] = "node/" . $values->{$this->aliases['nid']};
+        if (isset($this->aliases['language'])) {
+          $languages = language_list();
+          if (isset($languages[$values->{$this->aliases['language']}])) {
+            $this->options['alter']['language'] = $languages[$values->{$this->aliases['language']}];
+          }
+          else {
+            unset($this->options['alter']['language']);
+          }
+        }
+      } else {
+        $this->options['alter']['make_link'] = FALSE;
+        $this->options['alter']['path'] = "";        
+      }
+    }
+    return $data;
+  }
+}

+ 81 - 0
modules/base/tripal_core/views/handlers/views_handler_field_readable_date.inc

@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * A handler to provide proper displays for dates.
+ *
+ * @ingroup views_field_handlers
+ * @ingroup tripal_core
+ */
+class views_handler_field_readable_date extends views_handler_field {
+  function option_definition() {
+    $options = parent::option_definition();
+
+    $options['date_format'] = array('default' => 'small');
+    $options['custom_date_format'] = array('default' => '');
+
+    return $options;
+  }
+
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+    $time = time();
+
+    $form['date_format'] = array(
+      '#type' => 'select',
+      '#title' => t('Date format'),
+      '#options' => array(
+        'small' => format_date($time, 'small'),
+        'medium' => format_date($time, 'medium'),
+        'large' => format_date($time, 'large'),
+        'custom' => t('Custom'),
+        'raw time ago' => t('Time ago'),
+        'time ago' => t('Time ago (with "ago" appended)'),
+        'raw time span' => t('Time span (future dates start with - )'),
+        'time span' => t('Time span (with "ago/hence" appended)'),
+      ),
+      '#default_value' => isset($this->options['date_format']) ? $this->options['date_format'] : 'small',
+    );
+    $form['custom_date_format'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Custom date format'),
+      '#description' => t('If "Custom", see <a href="http://us.php.net/manual/en/function.date.php" target="_blank">the PHP docs</a> for date formats. If "Time ago" this is the the number of different units to display, which defaults to two.'),
+      '#default_value' => isset($this->options['custom_date_format']) ? $this->options['custom_date_format'] : '',
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-date-format' => array('custom', 'raw time ago', 'time ago', 'raw time span', 'time span')),
+    );
+  }
+
+  function render($values) {
+    $value = $values->{$this->field_alias};
+
+    // value is currently a CCYY:MM:DD HH:MM:SS format
+    // change it to unix timestamp so rest works
+    $value = strtotime($value);
+
+    $format = $this->options['date_format'];
+    if (in_array($format, array('custom', 'raw time ago', 'time ago', 'raw time span', 'time span'))) {
+      $custom_format = $this->options['custom_date_format'];
+    }
+    
+    if (!$value) {
+      return theme('views_nodate');
+    }
+    else {
+      $time_diff = time() - $value; // will be positive for a datetime in the past (ago), and negative for a datetime in the future (hence)
+      switch ($format) {
+        case 'raw time ago':
+          return format_interval($time_diff, is_numeric($custom_format) ? $custom_format : 2);
+        case 'time ago':
+          return t('%time ago', array('%time' => format_interval($time_diff, is_numeric($custom_format) ? $custom_format : 2)));
+        case 'raw time span':
+          return ($time_diff < 0 ? '-' : '') . format_interval(abs($time_diff), is_numeric($custom_format) ? $custom_format : 2);
+        case 'time span':
+          return t(($time_diff < 0 ? '%time hence' : '%time ago'), array('%time' => format_interval(abs($time_diff), is_numeric($custom_format) ? $custom_format : 2)));
+        case 'custom':
+          return format_date($value, $format, $custom_format);
+        default:
+          return format_date($value, $format);
+      }
+    }
+  }
+}

+ 16 - 0
modules/base/tripal_core/views/handlers/views_handler_field_type_name.inc

@@ -0,0 +1,16 @@
+<?php
+
+/**
+ *
+ * @ingroup views_field_handlers
+ * @ingroup tripal_core
+ */
+class views_handler_field_type_name extends views_handler_field {
+ function construct() {
+   parent::construct();
+ }
+
+ function render($values) { 
+   return $values->property_type_name;
+ } 
+} 

+ 91 - 0
modules/base/tripal_core/views/handlers/views_handler_filter_chado_boolean.inc

@@ -0,0 +1,91 @@
+<?php
+
+/**
+ * Purpose: This handler provides a TRUE/FALSE or YES/NO select for chado fields
+ *  of type boolean (includes both 0/1 and t/f booleans)
+ *
+ * @ingroup views_filter_handlers
+ * @ingroup tripal_core
+ */
+class views_handler_filter_chado_boolean extends views_handler_filter_boolean_operator {
+ /**
+  * Checks if this field uses 0/1 or t/f
+  */
+  function init(&$view, $options) {
+    parent::init($view, $options);
+    
+    $sql = 'SELECT '.$this->field.' FROM '.$this->table.' LIMIT 1';
+    $previous_db = tripal_db_set_active('chado');
+    $result = db_fetch_object(db_query($sql));
+    tripal_db_set_active($previous_db);
+    
+    $this->boolean_type_tf = FALSE;
+    if (preg_match('/[tf]/', $result->{$this->field})) {
+      $this->boolean_type_tf = TRUE;
+    }
+
+  }
+
+ /**
+  * This function sets the options array for the select.
+  * If we are using a t/f boolean then the options need to evaluate to either t or f
+  * Otherwise, (0/1 boolean) the options evaluate to 0 or 1
+  */
+  function get_value_options() {
+
+    if ($this->boolean_type_tf) {
+      if (isset($this->definition['type'])) {
+        if ($this->definition['type'] == 'yes-no') {
+          $this->value_options = array('t' => t('Yes'), 'f' => t('No'));
+        }
+        if ($this->definition['type'] == 'on-off') {
+          $this->value_options = array('t' => t('On'), 'f' => t('Off'));
+        }
+      }
+
+      // Provide a fallback if the above didn't set anything.
+      if (!isset($this->value_options)) {
+        $this->value_options = array('t' => t('True'), 'f' => t('False'));
+      }    
+    } else { //end of t/f boolean
+      if (isset($this->definition['type'])) {
+        if ($this->definition['type'] == 'yes-no') {
+          $this->value_options = array(1 => t('Yes'), 0 => t('No'));
+        }
+        if ($this->definition['type'] == 'on-off') {
+          $this->value_options = array(1 => t('On'), 0 => t('Off'));
+        }
+      }
+
+      // Provide a fallback if the above didn't set anything.
+      if (!isset($this->value_options)) {
+        $this->value_options = array(1 => t('True'), 0 => t('False'));
+      }
+    } //end of 0/1 boolean
+  }
+
+  function query() {
+    $this->ensure_my_table();
+
+    $where = "$this->table_alias.$this->real_field ";
+
+    if ($this->boolean_type_tf) {
+      if (preg_match('/f/',$this->value)) {
+        $where .= "= 'f'";
+      } else {
+        $where .= "= 't'";
+      }
+      $this->query->add_where($this->options['group'], $where);    
+    } else {
+      if (empty($this->value)) {
+        $where .= '= 0';
+        if ($this->accept_null) {
+          $where = '(' . $where . " OR $this->table_alias.$this->real_field IS NULL)";
+        }
+      } else {
+        $where .= '<> 0';
+      }
+      $this->query->add_where($this->options['group'], $where);
+    }
+  }
+}

+ 187 - 0
modules/base/tripal_core/views/handlers/views_handler_filter_chado_select_cvterm_name.inc

@@ -0,0 +1,187 @@
+<?php
+
+/**
+ * Purpose: This Handler provides a select list for the type field
+ *
+ *  NOTE: This handler only works when applied to the type_id field in the base_table of
+ *  this view.
+ *
+ * @ingroup views_filter_handlers
+ * @ingroup tripal_core
+ */
+class views_handler_filter_chado_select_cvterm_name extends views_handler_filter_string {
+
+ /**
+	* Executed when the field is added
+	* Determine which cv to limit the cvterms to
+	*/
+  function init(&$view, $options) {
+    parent::init($view, $options);
+  	
+  	$cv_id = variable_get('chado_'.$this->view->base_table.'_cv', null);
+    if ($cv_id) {
+			$results = tripal_core_chado_select('cvterm',array('cvterm_id','name'), array('cv_id'=>$cv_id));				
+			foreach ($results as $c) {
+				$cvterms[$c->cvterm_id] = $c->name;
+			}    	
+    } else {
+    	//get a list of cvs currently used
+    	if ($this->view->base_table == 'cvterm') {
+      	$sql = 'SELECT distinct(cv.cv_id) FROM '.$this->view->base_table
+      		.' LEFT JOIN cv cv ON cv.cv_id=cvterm.cv_id';	
+    	} else {
+      	$sql = 'SELECT distinct(cv.cv_id) FROM '.$this->view->base_table
+      		.' LEFT JOIN cvterm cvterm ON cvterm.cvterm_id='.$this->view->base_table.'.type_id '
+      		.'LEFT JOIN cv cv ON cv.cv_id=cvterm.cv_id';
+      }
+    	$previous_db = tripal_db_set_active('chado');
+    	$resource = db_query($sql);
+    	tripal_db_set_active($previous_db);
+    	$cvterms = array();
+			while ( $r = db_fetch_object($resource) ) {
+				$results = tripal_core_chado_select('cvterm',array('cvterm_id','name'), array('cv_id'=>$r->cv_id));				
+				foreach ($results as $c) {
+					$cvterms[$c->cvterm_id] = $c->name;
+				}
+			}
+    }// end of if variable not defined
+    
+    //sort cvterms by name (case insensitive)
+    natcasesort($cvterms);
+    
+    //add to this handler
+    $this->cvterm_options = $cvterms;
+  }
+
+ /**
+  * Defines options for the option forms
+  */
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+
+    $form['values_form_type'] = array(
+      '#type' => 'radios',
+      '#title' => 'Filter Type',
+      '#options' => array(
+        'textfield' => 'Text Field',
+        'select' => 'Drop-Down Box',
+      ),
+      '#default_value' => $this->options['values_form_type'],
+    );
+  }
+  
+ /**
+  * Alters the query so that WHERE cvterm.cvterm_id=212 is used
+  */
+  function query() {
+    $this->ensure_table;
+    
+    if (preg_match('/^\d+$/', $this->value)) {
+      $where = 'cvterm.cvterm_id=%d';
+    } else {
+      $where = "cvterm.name" . $this->operator . "'%s'";
+    }
+
+    $this->query->add_where($this->options['group'], $where, $this->value);
+  }
+  
+ /**
+  * Defines the value field in both the views filter options form
+  *   and the exposed form
+  */
+  function value_form(&$form, &$form_state) {
+    parent::value_form($form, $form_state);
+    
+    if (preg_match('/select/', $this->options['values_form_type'])) {
+      // Get Options
+      if ($this->options['exposed']) {    
+        $options['All'] = '<Any>';
+      }
+      $options['<select '.$this->table.'>'] = '<None>';
+      $max_length = 40;
+      foreach ($this->cvterm_options as $cvterm_id => $cvterm_name) {
+        if (strlen($cvterm_name) > $max_length) {
+          $options[$cvterm_id] = substr($cvterm_name,0,$max_length) . '...';
+        } else {
+          $options[$cvterm_id] = $cvterm_name;    
+        }
+      }
+      
+      //Select List
+      $form['value'] = array(
+          '#type' => 'select',
+          '#title' => $this->options['label'],
+          '#options' => $options,
+          '#default_value' => $this->value,
+      );
+    } else {
+      $form['value'] = array(
+        '#type' => 'textfield',
+        '#title' => $this->options['label'],
+        '#default_value' => $this->value,
+      );
+    }
+  }
+
+ /**
+  * Ensures the select list gets rendered when the filter is exposed
+  */
+  function exposed_form(&$form, &$form_state) {
+    if (empty($this->options['exposed'])) {
+      return;
+    }
+
+    $value = $this->options['expose']['identifier'];
+    $this->value_form($form, $form_state);
+    $form[$value] = $form['value'];
+
+    if (isset($form[$value]['#title']) && !empty($form[$value]['#type']) && $form[$value]['#type'] != 'checkbox') {
+      unset($form[$value]['#title']);
+    }
+
+    $this->exposed_translate($form[$value], 'value');
+
+    if (!empty($form['#type']) && ($form['#type'] == 'checkboxes' || ($form['#type'] == 'select' && !empty($form['#multiple'])))) {
+      unset($form[$value]['#default_value']);
+    }
+
+    if (!empty($form['#type']) && $form['#type'] == 'select' && empty($form['#multiple'])) {
+      $form[$value]['#default_value'] = 'All';
+    }
+
+    if ($value != 'value') {
+      unset($form['value']);
+    }
+
+  }
+
+  /**
+   * This kind of construct makes it relatively easy for a child class
+   * to add or remove functionality by overriding this function and
+   * adding/removing items from this array.
+   */
+  function operators() {
+    $operators = array(
+      '=' => array(
+        'title' => t('Is equal to'),
+        'short' => t('='),
+        'method' => 'op_equal',
+        'values' => 1,
+      ),
+      '!=' => array(
+        'title' => t('Is not equal to'),
+        'short' => t('!='),
+        'method' => 'op_equal',
+        'values' => 1,
+      ),
+      '~' => array(
+        'title' => t('Contains'),
+        'short' => t('contains'),
+        'method' => 'op_contains',
+        'values' => 1,
+      ),
+    );
+    
+    return $operators;
+  }
+}

+ 97 - 0
modules/base/tripal_core/views/handlers/views_handler_filter_chado_select_string.inc

@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * Purpose: This Handler provides a generic select list for any chado field that is a string
+ *  The select list includes all distinct values for that field.
+ *
+ * @ingroup views_filter_handlers
+ * @ingroup tripal_core
+ */
+class views_handler_filter_chado_select_string extends views_handler_filter_string {
+
+ /**
+  * Defines the value field in both the views filter options form
+  *   and the exposed form
+  */
+  function value_form(&$form, &$form_state) {
+    parent::value_form($form, $form_state);
+    
+    // Get Options
+    if ($this->options['exposed']) {    
+      $options['All'] = '<Any>';
+    }
+    $options['<select '.$this->table.'>'] = '<None>';
+    $results = tripal_core_chado_select(
+      $this->table,
+      array($this->field),
+      array()
+    );
+    $max_length = 40;
+    foreach ($results as $r) {
+      if (strlen($r->{$this->field}) > $max_length) {
+        $options[$r->{$this->field}] = substr($r->{$this->field},0,$max_length) . '...';
+      } else {
+        $options[$r->{$this->field}] = $r->{$this->field};    
+      }
+    }
+    
+    //Select List
+    $form['value'] = array(
+        '#type' => 'select',
+        '#title' => $this->options['label'],
+        '#options' => $options,
+        '#default_value' => $this->value,
+    );
+  }
+
+ /**
+  * Ensures the select list gets rendered when the filter is exposed
+  */
+  function exposed_form(&$form, &$form_state) {
+    if (empty($this->options['exposed'])) {
+      return;
+    }
+
+    $value = $this->options['expose']['identifier'];
+    $this->value_form($form, $form_state);
+    $form[$value] = $form['value'];
+
+    if (isset($form[$value]['#title']) && !empty($form[$value]['#type']) && $form[$value]['#type'] != 'checkbox') {
+      unset($form[$value]['#title']);
+    }
+
+    $this->exposed_translate($form[$value], 'value');
+
+    if (!empty($form['#type']) && ($form['#type'] == 'checkboxes' || ($form['#type'] == 'select' && !empty($form['#multiple'])))) {
+      unset($form[$value]['#default_value']);
+    }
+
+    if (!empty($form['#type']) && $form['#type'] == 'select' && empty($form['#multiple'])) {
+      $form[$value]['#default_value'] = 'All';
+    }
+
+    if ($value != 'value') {
+      unset($form['value']);
+    }
+
+  }
+
+ /**
+  *
+  */
+  function query() {
+    $this->ensure_my_table();
+    $field = "$this->table_alias.$this->real_field";
+    $upper = $this->case_transform();
+
+    // Deal with All/Any as value
+    if (preg_match('/All/', $this->value)) {
+      // Don't do anything    
+    } else {
+      $info = $this->operators();
+      if (!empty($info[$this->operator]['method'])) {
+        $this->{$info[$this->operator]['method']}($field, $upper);
+      }
+    }
+  }
+}

+ 84 - 0
modules/base/tripal_core/views/template.node_join.views.inc

@@ -0,0 +1,84 @@
+<?php
+
+/*************************************************************************
+ * @file: THIS IS A TEMPLATE AND SHOULD NOT BE INCLUDED IN THE MODULE CODE
+ *
+ *  - simply replace all XXX with the original chado table you want to join to it's drupal nodes.
+ *    (ie: If you want to join features to their drupal nodes then XXX=feature)
+ * 
+ *  NOTE: Creating the table definition file is not enough. You also need to call the 
+ *        retrieve_XXX_views_data() function from ../tripal_core.views.inc:tripal_core_views_data()
+ *        by adding the following line:
+ *           $data = array_merge($data, retrieve_XXX_views_data());
+ *        to the function and including the file directly above the function (blow the function 
+ *        header by adding:
+ *           require_once('views/XXX.views.inc');
+ *
+ *  REMOVE THIS COMMENT IN THE COPY!
+ */ 
+ 
+/**
+ *  @file
+ *  This file defines the data array for a given chado table. This array
+ *  is merged into a larger array containing definitions of all tables associated
+ *  with this module in:
+ *  @see tripal_core.views.inc --in tripal_core_views_data()
+ *
+ *  Note: All chado tables are joined to their drupal nodes through the chado_XXX linking table. 
+ *        This file simply defines this linking table and joins the three tables together.
+ *        No modification of XXX.views.inc is needed.
+ *
+ *  Documentation on views integration can be found at 
+ *  http://views2.logrus.com/doc/html/index.html.
+ */
+ 
+/**
+ * Purpose: this function returns the portion of the data array 
+ *   which describes the chado_XXX drupal table, it's fields and any joins between it 
+ *   and other tables
+ * @see tripal_core_views_data() --in tripal_core.views.inc
+ *
+ * The main need for description of this table to views is to join chado data with drupal nodes
+ *
+ */
+function retrieve_chado_XXX_views_data () {
+	global $db_url;
+  $data = array();
+  
+  // if the chado database is not local to the drupal database
+  // then we need to set the database name.  This should always
+  // be 'chado'.
+  if(is_array($db_url) and array_key_exists('chado',$db_url)){
+     // return empty data array b/c if chado is external then no join to the nodetable can be made
+     return $data;
+  }
+
+  //Basic table definition-----------------------------------
+  $data['chado_XXX']['table'] = array(
+    'field' => 'nid',
+  );
+  
+  //Relationship Definitions---------------------------------
+  // Note: No joins need to be made from $data['XXX']['table']
+  
+  // Join the chado_XXX table to XXX
+  $data['chado_XXX']['table']['join']['XXX'] = array(
+  	'left_field' => 'XXX_id',
+  	'field' => 'XXX_id',
+  );
+  
+  // Join the node table to chado_XXX
+  $data['node']['table']['join']['chado_XXX'] = array(
+  	'left_field' => 'nid',
+  	'field' => 'nid',
+  );
+  
+  // Join the node table to XXX
+  $data['node']['table']['join']['XXX'] = array(
+  	'left_table' => 'chado_XXX',
+  	'left_field' => 'nid',
+  	'field' => 'nid',
+  );  
+
+	return $data;
+}

+ 211 - 0
modules/base/tripal_core/views/template.table_defn.views.inc

@@ -0,0 +1,211 @@
+<?php
+
+/*************************************************************************
+ * @file: THIS IS A TEMPLATE AND SHOULD NOT BE INCLUDED IN THE MODULE CODE
+ *
+ *   - Every instance of XXX should be replaced with the name of your table
+ *   - If this is a base table (you want a view where every row is a row from this table)
+ *     then change $data['XXX']['table'] to $data['XXX']['table']['base'] 
+ *     and $data['XXX']['table']['database'] to $data['XXX']['table']['base']['database']
+ *   - Relationships between this table and others: YYY is the table you are trying to join to this
+ *     one. You want to join a table to this one if this table contains a foreign key to the other
+ *     table. If the join between this table and another is through a linking table
+ *     (ie: library-XXX/YYY => library_feature-XY => feature-XXX/YYY) then make the join in both
+ *     directions (ie: in the file XXX.views.inc and the file YYY.views.inc
+ *   - Create a field definition for each field in this table using the example fields already
+ *     listed. Match the type of the database field to the field definition listed below.
+ *     (ie: for a text/varchar field from the database use plain_text_field below)
+ * 
+ *  NOTE: Creating the table definition file is not enough. You also need to call the 
+ *        retrieve_XXX_views_data() function from ../tripal_core.views.inc:tripal_core_views_data()
+ *        by adding the following line:
+ *           $data = array_merge($data, retrieve_XXX_views_data());
+ *        to the function and including the file directly above the function (blow the function 
+ *        header by adding:
+ *           require_once('views/XXX.views.inc');
+ *
+ *  REMOVE THIS COMMENT IN THE COPY!
+ */ 
+ 
+/**
+ *  @file
+ *  This file defines the data array for a given chado table. This array
+ *  is merged into a larger array containing definitions of all tables associated
+ *  with this module in:
+ *  @see tripal_core.views.inc --in tripal_core_views_data()
+ *
+ *  Documentation on views integration can be found at 
+ *  http://views2.logrus.com/doc/html/index.html.
+ */
+
+/*************************************************************************
+ * Purpose: this function returns the portion of the data array 
+ *   which describes the XXX table, it's fields and any joins between it and other tables
+ * @see tripal_core_views_data() --in tripal_core.views.inc
+ *
+ * Table: XXX
+ * @code
+ * XXX-Copy/Paste Table SQL code here-XXX
+ * @endcode
+ */
+ function retrieve_XXX_views_data() {
+  global $db_url;
+  $data = array();
+
+  // if the chado database is not local to the drupal database
+  // then we need to set the database name.  This should always
+  // be 'chado'.
+  if(is_array($db_url) and array_key_exists('chado',$db_url)){
+    $database = 'chado';
+  }
+   
+  //Basic table definition-----------------------------------
+  $data['XXX']['table']['group'] = t('Chado XXX');
+  
+  $data['XXX']['table'] = array(
+    'field' => 'primary_id',
+    'title' => t('Chado XXX'),
+    'help' => t('Enter some user-friendly description of this tables purpose to the user.'),
+  );
+  if($database){
+     $data['XXX']['table']['database'] = $database;
+  }
+
+  
+  //Relationship Definitions---------------------------------
+  //Join: YYY => XXX
+  // Notice that this relationship tells the primary table to show it's fields to the
+  // table referencing it by a foreign key and thus the relationship is from
+  // primary table to table referenceing it (ie: cvterm => feature)
+  $data['XXX']['table']['join']['YYY'] = array(
+    'left_field' => 'foreign key in YYY table',
+    'field' => 'primary key in XXX table',
+  );  
+  
+  //Join: XXX => XY => YYY
+  // This relationship should be described in both directions
+  // in the appropriate files (ie: for feature => library 
+  // describe in both feature.views.inc and library.views.inc)
+  $data['XXX']['table']['join']['XY'] = array(
+    'left_field' => 'matching XXX key in the XY table',
+    'field' => 'primary key in XXX table',
+  );  
+  $data['XXX']['table']['join']['YYY'] = array(
+    'left_table' => 'XY',
+    'left_field' => 'matching XXX key in the XY table',
+    'field' => 'primary key in XXX table',
+  );
+  $data['XY']['table']['join']['YYY'] = array(
+    'left_field' => 'primary key in YYY table',
+    'field' => 'matching YYY key in the XY table',
+  );
+   
+  //Table Field Definitions----------------------------------
+      
+  //Field: XXX_id (primary key)
+  $data['XXX']['field_name'] = array(
+    'title' => t('XXX Primary Key'),
+    'help' => t('A unique index for every XXX.'),
+    'field' => array(
+      'handler' => 'views_handler_field_numeric',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_numeric',
+    ),
+  );
+
+  /*.......................................................
+   * Beginning of Example Field definitions
+   * Remove this section when done
+   */
+
+  //Field: plain_text_field (chado datatype)   
+  $data['XXX']['plain_text_field'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+
+  //Field: numeric_field (chado datatype)   
+  $data['XXX']['numeric_field'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_numeric',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_numeric',
+    ),
+  );
+
+  //Field: boolean_field (chado datatype)   
+  $data['XXX']['boolean_field'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_boolean',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_boolean_operator',
+    ),
+  );
+
+  //Field: unix_timestamp (chado datatype)   
+  $data['XXX']['unix_timestamp'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_date',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort_date',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_date',
+    ),
+  );
+
+  //Field: human_readable_date (chado datatype)   
+  $data['XXX']['human_readable_date'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_readble_date',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort_date',
+    ),
+  );
+   
+   /*
+    * End of Example Field definitions
+    */
+    
+  return $data;
+}

+ 249 - 0
modules/base/tripal_cv/charts.php

@@ -0,0 +1,249 @@
+<?php
+
+
+/**
+ * Generates JSON used for generating a Google chart of count data associated
+ * with a controlled vocabulary.  An example would be features assigned to
+ * Gene Ontology terms. 
+ * 
+ * To generate a chart, the progammer must first create a materialized view that
+ * will generate count data for a given controlled vocabulary.  For example, the Tripal
+ * Analysis GO creates a materialized view for counting Gene Ontology assignments
+ * to features.  This view is created on install of the module and is named
+ * 'go_count_analysis'.
+ *
+ * Next, an HTML 'div' box must be added to the desired page
+ * with a class name of 'tripal_cv_chart', and an id of the following format:
+ *
+ *  tripal_[module_name]_cv_chart_[unique id]
+ *
+ * where [module_name] is the name of the tripal module (e.g. tripal_analyisis_go)
+ * and [unique id] is some unique identifier that the contolling module 
+ * recognizes. This string is the $chart_id variable passed as the first argument
+ * to the function.  For example, the Tripal GO Analysis module generates 
+ * chart ids of the form:
+ * 
+ *  tripal_analysis_go_cv_chart_10_2_bp
+ * 
+ * In this case the module that will manage this chart is identified as 'tripal_analysis_go' and within
+ * the [unique id] portion contains the
+ * organism_id (e.g. 10), analysis_id (e.g. 2) and chart type (bp = biological process). 
+ *
+ * Second, the programmer must then define a hook in the controlling module for setting
+ * some options used to build the chart.  The hook has the form:  hook_cv_chart($chart_id).
+ * This hook should accept the full $chart_id as the single parameter.  For the Tripal
+ * Analysis GO module the hook is named:  tripal_analysis_go_cv_chart.  
+ *
+ * The array returned by this hook must have the following fields:  
+ *  - count_mview
+ *      the name of the materialized view that contains the count data
+ *      this materialized view must have at least two columns, one with the cvterm_id
+ *      for each term and a second column containing the counts
+ *  - cvterm_id_column
+ *      the column name in the materialized view that contains
+ *      the cvterm_ids
+ *  - count_column
+ *      the column name in the materialized view that contains the
+ *      counts
+ *  - filter
+ *      an SQL compatible WHERE clause string (whithout the word 'WHERE')
+ *      that can be used for filtering the records in the materialized view.
+ *  - title
+ *      the title for the chart
+ *  - type
+ *      the type of chart to create (see Google Charts documenation). Leave
+ *      blank for a pie chart.
+ *  - size 
+ *      the dimensions of the chart in pixels (see Google Charts documenations 
+ *      for exact size limitations).
+ *
+ * Example from the tripal_analysis_go module:
+ * @code
+ *  function tripal_analysis_go_cv_chart($chart_id){
+ *  
+ *    // The CV module will create the JSON array necessary for buillding a
+ *    // pie chart using jgChart and Google Charts.  We have to pass to it
+ *    // a table that contains count information, tell it which column 
+ *    // contains the cvterm_id and provide a filter for getting the
+ *    // results we want from the table.
+ *    $organism_id = preg_replace("/^tripal_analysis_go_cv_chart_(\d+)-(\d+)_(bp|cc|mf)$/","$1",$chart_id);
+ *    $analysis_id = preg_replace("/^tripal_analysis_go_cv_chart_(\d+)-(\d+)_(bp|cc|mf)$/","$2",$chart_id);
+ *    $type        = preg_replace("/^tripal_analysis_go_cv_chart_(\d+)-(\d+)_(bp|cc|mf)$/","$3",$chart_id);
+ *  
+ *    $sql = "SELECT * FROM {Analysis} WHERE analysis_id = %d";
+ *    $previous_db = tripal_db_set_active('chado');  // use chado database
+ *    $analysis = db_fetch_object(db_query($sql,$analysis_id));
+ *    tripal_db_set_active($previous_db);  // now use drupal database  
+ *   
+ *    if(strcmp($type,'mf')==0){
+ *       $class = 'molecular_function';
+ *       $title = "Number of Molecular Function Terms From $analysis->name Analysis";
+ *    }
+ *    if(strcmp($type,'cc')==0){
+ *       $class = 'cellular_component';
+ *       $title = "Number of Cellular Component Terms From $analysis->name Analysis";
+ *    }
+ *    if(strcmp($type,'bp')==0){
+ *       $class = 'biological_process';
+ *       $title = "Number of Biological Process Terms From $analysis->name Analysis";
+ *    }
+ *    $options = array(
+ *       count_mview      => 'go_count_analysis',
+ *       cvterm_id_column => 'cvterm_id',
+ *       count_column     => 'feature_count',
+ *       filter           => "
+ *          CNT.organism_id = $organism_id AND 
+ *          CNT.analysis_id = $analysis_id AND 
+ *          CNT.cvterm_id IN ( 
+ *            SELECT CVTR.subject_id 
+ *            FROM {CVTerm_relationship} CVTR 
+ *              INNER JOIN CVTerm CVT on CVTR.object_id = CVT.cvterm_id
+ *              INNER JOIN CV on CVT.cv_id = CV.cv_id
+ *            WHERE CVT.name = '$class' AND  
+ *                   CV.name = '$class'
+ *          )
+ *       ",
+ *       type             => 'p',
+ *       size             => '550x175',
+ *       title            => $title,
+ *    );
+ *    return $options;
+ *  }
+ *
+ * @endcode
+ *
+ * @param $chart_id
+ *   The unique identifier for the chart
+ *
+ * @return
+ *   JSON array needed for the js caller
+ *
+ * With these three components (materialized view, a 'div' box with proper CSS class and ID, and a hook_cv_chart)
+ * a chart will be created on the page.  There is no need to call this function directly.
+ *
+ * @ingroup tripal_cv
+ */
+function tripal_cv_chart($chart_id){
+  // parse out the tripal module name from the chart_id to find out 
+  // which Tripal "hook" to call:
+  $tripal_mod = preg_replace("/^(tripal_.+?)_cv_chart_(.+)$/","$1",$chart_id);
+  $callback = $tripal_mod . "_cv_chart";
+
+  // now call the function in the module responsible for the chart to fill out
+  // an options array needed by the tripal_cv_count_chart call below.
+  $opt = call_user_func_array($callback,array($chart_id));
+
+  // build the JSON array to return to the javascript caller
+  $json_arr = tripal_cv_count_chart($opt[count_mview],$opt[cvterm_id_column],
+     $opt[count_column],$opt[filter],$opt[title], $opt[type],$opt[size]);
+  $json_arr[] = $chart_id;  // add the chart_id back into the json array
+
+  return drupal_json($json_arr);
+
+}
+
+ /**
+  * This function generates an array with fields compatible with Google charts used
+  * for generating pie charts of counts associated with terms in
+  * a controlled vocabulary.  An example would be counts of features assigned
+  * to terms in the Sequence Ontology.
+  *
+  * @param $cnt_table
+  *   The name of the table (most likely a materialized view) that
+  *   contains count data for each term.  The table must have at least
+  *   two columns, one with the cvterm_id for each term, and a second
+  *   column with the count (i.e. features assigned the term).
+  * @param $fk_column
+  *   This is the name of the column in the $cnt_table that holds the 
+  *   cvterm_id for each term.
+  * @param $cnt_column
+  *   The name of the column in the $cnt_table containing the counts
+  * @param $filter
+  *   An SQL compatible 'where' clause (without the word 'WHERE') used
+  *   to filter the records in the $cnt_table
+  * @param $title
+  *   The title of the chart to be rendered.
+  * @param $type
+  *   The type of chart to be rendered. The value used here is the same as
+  *   the type names for Google charts. Default is p3 (pie chart).
+  * @param $size
+  *   The size in pixels of the chart to be rendered. Default is 300x75. The
+  *   size of the chart is constrained by Google charts.  See the Google
+  *   chart documentation for exact limitations.
+  *  
+  * @return 
+  *   An array that has the settings needed for Google Charts to creat the chart.
+  *
+  * @ingroup tripal_cv
+  */
+function tripal_cv_count_chart($cnt_table, $fk_column,
+   $cnt_column, $filter = null, $title = '', $type = 'p3', $size='300x75') {
+
+   if(!$type){
+      $type = 'p3';
+   }
+
+   if(!$size){
+     $size = '300x75';
+   }
+
+   if(!$filter){
+      $filter = '(1=1)'; 
+   }
+
+   $isPie = 0;
+   if(strcmp($type,'p')==0 or strcmp($type,'p3')==0){
+      $isPie = 1;
+   }
+   $sql = "
+      SELECT CVT.name, CVT.cvterm_id, CNT.$cnt_column as num_items
+      FROM {$cnt_table} CNT 
+       INNER JOIN {cvterm} CVT on CNT.$fk_column = CVT.cvterm_id 
+      WHERE $filter
+   ";    
+
+   $features = array();
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $results = db_query($sql);
+   tripal_db_set_active($previous_db);  // now use drupal database
+   $data = array();
+   $axis = array();
+   $legend = array();
+   $total = 0;
+   $max = 0;
+   $i = 1;
+   while($term = db_fetch_object($results)){
+      
+      if($isPie){
+         $axis[] = "$term->name (".number_format($term->num_items).")";
+         $data[] = array($term->num_items,0,0);
+      } else {
+         $axis[] = "$term->name (".number_format($term->num_items).")";
+         $data[] = array($term->num_items);
+    //     $legend[] = "$term->name (".number_format($term->num_items).")";
+      }
+      if($term->num_items > $max){
+         $max = $term->num_items;
+      }
+      $total += $term->num_items;
+      $i++;
+   }
+   // convert numerical values into percentages
+   foreach($data as &$set){
+      $set[0] = ($set[0] / $total) * 100;
+   }
+   $opt[] = array(
+      data => $data,
+      axis_labels => $axis, 
+      legend => $legend,
+      size => $size, 
+      type => $type,
+ 
+      bar_width     => 10, 
+      bar_spacing   => 0, 
+      title         => $title
+   );
+//   $opt[] = $sql;
+   
+   return $opt;
+}

+ 457 - 0
modules/base/tripal_cv/feature_property.obo

@@ -0,0 +1,457 @@
+format-version: 1.2
+date: 10:06:2005 15:07
+saved-by: cjm
+default-namespace: unknown
+autogenerated-by: /Users/cjm/cvs/go-dev/go-perl/scripts/go2fmt.pl
+default-namespace: feature_property
+remark: Initially generated by Chris Mungall, sourced from FB and Rice chado. TODO: TIGR Chado; TODO: GFF3
+subsetdef: fpo_sgd "SGD and related dbs subset"
+subsetdef: fpo_gff "GFF3 specific tags"
+subsetdef: fpo_apollo "properties with fixed semantics in apollo"
+
+[Typedef]
+id: SOFP:feature_property
+name: feature_property
+namespace: feature_property
+def: "A general purpose relation between a biological feature and some value" [so:cjm]
+
+[Typedef]
+id: SOFP:aminoacid
+name: aminoacid
+namespace: feature_property
+def: "amino acid coded for by a tRNA transcript feature" [so:cjm]
+is_a: SOFP:feature_property
+domain: SO:0000253
+range: CHEBI:22477
+
+[Typedef]
+id: SOFP:anticodon
+name: anticodon
+namespace: feature_property
+def: "anticodon coded for by a tRNA transcript feature" [so:cjm]
+is_a: SOFP:feature_property
+domain: SO:0000253
+range: xsd:string
+range_def: "A 3 character string representing the anticodon using the IUPAC DNA Sequence alphabet; for example ATG. RNA seqs MUST be converted to DNA seqs" []
+comments: Note the difference between GENBANK_SOFP:anticodon and SOFP:anticodon; the former includes the base range
+
+[Typedef]
+id: SOFP:citation
+name: citation
+namespace: feature_property
+is_a: SOFP:feature_property
+domain: SO:0000110
+
+[Typedef]
+id: SOFP:comment
+name: comment
+namespace: feature_property
+def: "Annotation comments, from a human curator" []
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+
+[Typedef]
+id: SOFP:cyto_range
+name: cyto_range
+namespace: feature_property
+def: "The cytological range covered by a feature. May be auto-generated from sequence coordinates, or determined by experimental methods" [so:cjm]
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+range_def: "Must conform to naming standard for that species; typically fly-style (eg 41A2-B3) or normal (eg 14p28.1-q32.2)" [so:cjm]
+comments: If auto-derived, may be redundant with feature location
+
+[Typedef]
+id: SOFP:description
+name: description
+namespace: feature_property
+def: "Free-text description of feature" []
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+comments: Often sourced from fasta header, in which case it includes everything after the > symbol
+
+[Typedef]
+id: SOFP:dicistronic
+name: dicistronic
+namespace: feature_property
+def: "true if transcript codes for >1 non-overlapping CDSs" []
+is_a: SOFP:feature_property
+domain: SO:0000115      ! transcript_feature
+range: xsd:boolean
+comments: redundant with secondary classification term SO:0000079
+
+[Typedef]
+id: SOFP:element
+name: element
+namespace: feature_property
+def: "name of transposable element class" []
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+comment: in chado or gff, this may also be indicated by a reference to a class in an ontology of TE classes
+
+[Typedef]
+id: SOFP:encoded_symbol
+name: encoded_symbol
+namespace: feature_property
+def: "dicistronic CDS/proteins are attached to a single gene feature representing the whole cassette - in which case the symbol refers to the cassette. encoded_symbol refers to a gene in the sense of non-overlapping CDS set. cf Adh and Adhr in dmel" []
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+
+[Typedef]
+id: SOFP:evidenceGB
+name: evidenceGB
+namespace: feature_property
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+range_def: "'experimental'"
+comment: same as GENBANK_SOFP:experimental??
+
+[Typedef]
+id: SOFP:linked_to
+name: linked_to
+namespace: feature_property
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+comment: someone please define.. to do with restriction fragments?
+
+[Typedef]
+id: SOFP:missing_start_codon
+name: missing_start_codon
+namespace: feature_property
+def: "true if start of CDS is unknown" []
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:boolean
+comment: in chado, redundant with featureloc.is_{fmin,fmax}_partial
+
+[Typedef]
+id: SOFP:missing_stop_codon
+name: missing_stop_codon
+namespace: feature_property
+def: "true if end of CDS is unknown" []
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:boolean
+comment: in chado, redundant with featureloc.is_{fmin,fmax}_partial
+
+[Typedef]
+id: SOFP:na_change
+name: na_change
+namespace: feature_property
+def: "A nucleic acid modification in some variant feature relative to the reference sequence" []
+is_a: SOFP:feature_property
+domain: SO:0000109         ! sequence_variant
+range: xsd:string
+range_def: "<na_seq-REFERENCE><position><na_seq-VARIANT>"
+comment: in chado, this is redundant with featureloc.residue_info[rank=0,1]
+
+[Typedef]
+id: SOFP:non_canonical_start_codon
+name: non_canonical_start_codon
+namespace: feature_property
+def: "The sequence of the biological start codon" []
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+range_def: "3-character DNA sequence, must be different from ATG" []
+
+[Typedef]
+id: SOFP:owner
+name: owner
+namespace: feature_property
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+
+[Typedef]
+id: SOFP:pr_change
+name: pr_change
+namespace: feature_property
+def: "An amino acid modification in some variant feature relative to the reference sequence" []
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+range_def: "<aa_seq-ORIGINAL><position><aa_seq-NEW>. @ represents stop codon"
+comment: see also: na_change; in chado, this is redundant with featureloc.residue_info[rank=0,1]
+
+[Typedef]
+id: SOFP:problem
+name: problem
+namespace: feature_property
+def: "True if the annotation for this feature is problematic in some way; more details can be found in the comments property" []
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:boolean
+
+[Typedef]
+id: SOFP:readthrough_stop_codon
+name: readthrough_stop_codon
+namespace: feature_property
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:boolean
+
+[Typedef]
+id: SOFP:reported_na_change
+name: reported_na_change
+namespace: feature_property
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+range_def: "As for na_change, suffixed with |<dbxref>" []
+
+[Typedef]
+id: SOFP:reported_pr_change
+name: reported_pr_change
+namespace: feature_property
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+range_def: "As for pr_change, suffixed with |<dbxref>" []
+
+[Typedef]
+id: SOFP:sp_comment
+name: sp_comment
+namespace: feature_property
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+comment: is this deprecated? originally sourced from gadfly pep validation pipeline - of little interest to others than flybase
+
+[Typedef]
+id: SOFP:sp_status
+name: sp_status
+namespace: feature_property
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+comment: is this deprecated? originally sourced from gadfly pep validation pipeline - of little interest to others than flybase
+
+[Typedef]
+id: SOFP:status
+name: status
+namespace: feature_property
+def: "Annotation workflow status" []
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+range_def: "'not done'"
+
+[Typedef]
+id: SOFP:source
+name: source
+namespace: feature_property
+def: "Source from which feature originates - may be a computer program or analysis, or a group. Corresponds to GFF column 2" []
+subset: fpo_gff
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+
+[Typedef]
+id: SOFP:symbol
+name: symbol
+namespace: feature_property
+def: "Community symbol" []
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+
+[Typedef]
+id: SOFP:validation_flag
+name: validation_flag
+namespace: feature_property
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+range_def: "TODO: define the list of allowed values in an ontology" []
+
+[Typedef]
+id: SOFP:synonym
+name: synonym
+exact_synonym: "Alias" []
+namespace: feature_property
+def: "Historic community symbol, may have originally been symbol" []
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+comment: In chado, this is redundant with the synonym table. GFF3 uses the tag "Alias"
+
+[Typedef]
+id: SOFP:date
+name: date
+namespace: feature_property
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:date
+comment: date of what?? Annotation??
+
+[Typedef]
+id: SOFP:internal_synonym
+name: internal_synonym
+namespace: feature_property
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+comment: FB specific?
+
+[Typedef]
+id: SOFP:qseq_type
+name: qseq_type
+namespace: feature_property
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+comment: deprecated?
+
+[Typedef]
+id: SOFP:unixdate
+name: unixdate
+namespace: feature_property
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:integer
+range_def: "No of seconds since unix time zero, 1 Jan 1970" []
+comment: date of what?? Annotation??
+
+[Typedef]
+id: SOFP:gbunit
+name: gbunit
+namespace: feature_property
+def: "Name of genbank scaffold sequence intersected by the feature" []
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+range_def: "<dbxref> - taken from NCBI/EMBL/DDBJ" []
+
+[Typedef]
+id: SOFP:keywords
+name: keywords
+namespace: feature_property
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+
+[Typedef]
+id: SOFP:orf_classification
+name: orf_classification
+namespace: feature_property
+def: "gene call confidence flag"
+subset: fpo_sgd
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+range_def: "'Dubious'|'Uncharacterized'|'Verified'" []
+comment: Currently only used by SGD
+
+[Typedef]
+id: SOFP:ontology_term
+name: ontology_term
+namespace: feature_property
+def: "A term from some ontology classifying the feature" []
+subset: fpo_gff
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+range_def: "<dbxref> of a term in some ontology" []
+comment: In chado, this is redundant with the feature_cvterm table
+
+[Typedef]
+id: SOFP:protein_id
+name: protein_id
+namespace: feature_property
+def: "A protein sequence identifier" [so:sjc]
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+comment: Should really be a dbxref [so:sjc]
+
+[Typedef]
+id: SOFP:organism
+name: organism
+namespace: feature_property
+def: "The name of the organism" [so:sjc]
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+comment: In chado, this is redundant with the organism table [so:sjc]
+
+[Typedef]
+id: SOFP:mol_type
+name: mol_type
+namespace: feature_property
+def: "The type of molecule" [so:sjc]
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+comment: Presumably, this could be inferred from the SO type [so:sjc]
+
+[Typedef]
+id: SOFP:dev_stage
+name: dev_stage
+namespace: feature_property
+def: "Stage of development" [so:sjc]
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+
+[Typedef]
+id: SOFP:chromosome
+name: chromosome
+namespace: feature_property
+def: "Name of chromosome the feature occurs on(?)" [so:sjc]
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+comment: What is this for? [so:sjc]
+
+[Typedef]
+id: SOFP:map
+name: map
+namespace: feature_property
+def: "A map location" [so:sjc]
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+
+[Typedef]
+id: SOFP:finished
+name: finished
+namespace: feature_property
+def: "If the annotation of the feature is complete" [so:sjc]
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:boolean
+
+[Typedef]
+id: SOFP:Note
+name: Note
+namespace: feature_property
+def: "A GFF3 Note attribute" [so:sjc]
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+
+[Typedef]
+id: SOFP:Gap
+name: Gap
+namespace: feature_property
+def: "A GFF3 Gap cigar string" [so:sjc]
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+
+[Typedef]
+id: SOFP:score
+name: score
+namespace: feature_property
+def: "A GFF3 score" [so:sjc]
+is_a: SOFP:feature_property
+domain: SO:0000110
+range: xsd:string
+

+ 860 - 0
modules/base/tripal_cv/obo_loader.php

@@ -0,0 +1,860 @@
+<?php
+
+/** 
+ * @defgroup tripal_obo_loader Tripal Ontology Loader
+ * @ingroup tripal_cv
+ */
+ 
+/**
+*
+* @ingroup tripal_obo_loader
+*/
+function tripal_cv_load_obo_v1_2_id($obo_id,$jobid = NULL){
+
+   // get the OBO reference
+   $sql = "SELECT * FROM {tripal_cv_obo} WHERE obo_id = %d";
+   $obo = db_fetch_object(db_query($sql,$obo_id));
+
+   // if the reference is for a remote URL then run the URL processing function
+   if(preg_match("/^http:\/\//",$obo->path) or preg_match("/^ftp:\/\//",$obo->path)){
+      tripal_cv_load_obo_v1_2_url($obo->name,$obo->path,$jobid,0);
+   } 
+   // if the reference is for a local file then run the file processing function
+   else {
+      // check to see if the file is located local to Drupal
+      $dfile = $_SERVER['DOCUMENT_ROOT'] . base_path() . $obo->path; 
+      if(file_exists($dfile)){
+         tripal_cv_load_obo_v1_2_file($obo->name,$dfile,$jobid,0);
+      } 
+      // if not local to Drupal, the file must be someplace else, just use
+      // the full path provided
+      else{
+         if(file_exists($obo->path)){
+            tripal_cv_load_obo_v1_2_file($obo->name,$obo->path,$jobid,0);
+         } else {
+            print "ERROR: counld not find OBO file: '$obo->path'\n";
+         }
+      }
+   }  
+}
+/**
+*
+* @ingroup tripal_obo_loader
+*/
+function tripal_cv_load_obo_v1_2_file($obo_name,$file,$jobid = NULL,$is_new = 1){
+   $newcvs = array();
+
+   tripal_cv_load_obo_v1_2($file,$jobid,$newcvs);
+   if($is_new){
+      tripal_cv_load_obo_add_ref($obo_name,$file);
+   }
+   // update the cvtermpath table 
+   tripal_cv_load_update_cvtermpath($newcvs,$jobid);
+   print "Ontology Sucessfully loaded!\n";  
+}
+/**
+*
+* @ingroup tripal_obo_loader
+*/
+function tripal_cv_load_obo_v1_2_url($obo_name,$url,$jobid = NULL,$is_new = 1){
+
+   $newcvs = array();
+
+   // first download the OBO
+   $temp = tempnam(sys_get_temp_dir(),'obo_');
+   print "Downloading URL $url, saving to $temp\n";
+   $url_fh = fopen($url,"r");
+   $obo_fh = fopen($temp,"w");
+   while(!feof($url_fh)){
+      fwrite($obo_fh,fread($url_fh,255),255);
+   }
+   fclose($url_fh);
+   fclose($obo_fh);
+
+   // second, parse the OBO
+   tripal_cv_load_obo_v1_2($temp,$jobid,$newcvs);
+
+   // now remove the temp file
+   unlink($temp);
+
+   if($is_new){
+      tripal_cv_load_obo_add_ref($obo_name,$url);
+   }
+
+   // update the cvtermpath table 
+   tripal_cv_load_update_cvtermpath($newcvs,$jobid);
+
+   print "Ontology Sucessfully loaded!\n";
+}
+/**
+*
+* @ingroup tripal_obo_loader
+*/
+function tripal_cv_load_update_cvtermpath($newcvs,$jobid){
+
+   print "\nUpdating cvtermpath table.  This may take a while...\n";
+   foreach($newcvs as $namespace => $cvid){
+      tripal_cv_update_cvtermpath($cvid, $jobid);
+   }
+}
+/**
+*
+*/
+function tripal_cv_load_obo_add_ref($name,$path){
+   $isql = "INSERT INTO tripal_cv_obo (name,path) VALUES ('%s','%s')";
+   db_query($isql,$name,$path);
+}
+/**
+*
+* @ingroup tripal_obo_loader
+*/
+function tripal_cv_load_obo_v1_2($file,$jobid = NULL,&$newcvs) {
+
+   global $previous_db;
+
+   $header = array();
+   $obo = array();
+   
+   print "Opening File $file\n";
+
+   // set the search path
+   $previous_db = tripal_db_set_active('chado');
+
+   // make sure we have an 'internal' and a '_global' database
+   if(!tripal_cv_obo_add_db('internal')){
+      tripal_cv_obo_quiterror("Cannot add 'internal' database");
+   }
+   if(!tripal_cv_obo_add_db('_global')){
+      tripal_cv_obo_quiterror("Cannot add '_global' database");
+   }
+
+   // parse the obo file
+   tripal_cv_obo_parse($file,$obo,$header);
+
+   // add the CV for this ontology to the database
+   $defaultcv = tripal_cv_obo_add_cv($header['default-namespace'][0],'');
+   if(!$defaultcv){
+      tripal_cv_obo_quiterror('Cannot add namespace ' . $header['default-namespace'][0]);
+   }  
+   $newcvs[$header['default-namespace'][0]] = $defaultcv->cv_id;
+
+   // add any typedefs to the vocabulary first
+   $typedefs = $obo['Typedef'];
+   foreach($typedefs as $typedef){
+      tripal_cv_obo_process_term($typedef,$defaultcv,$obo,1,$newcvs);  
+   }
+
+   // next add terms to the vocabulary
+   $terms = $obo['Term'];
+   if(!tripal_cv_obo_process_terms($terms,$defaultcv,$obo,$jobid,$newcvs)){
+      tripal_cv_obo_quiterror('Cannot add terms from this ontology');
+   }
+   return tripal_cv_obo_loader_done();
+}
+/**
+*
+* @ingroup tripal_obo_loader
+*/
+function tripal_cv_obo_quiterror ($message){
+   print "ERROR: $message\n";
+   db_query("set search_path to public");  
+   exit;
+}
+/**
+*
+* @ingroup tripal_obo_loader
+*/
+function tripal_cv_obo_loader_done (){
+   // return the search path to normal
+   tripal_db_set_active($previous_db);
+   db_query("set search_path to public");  
+   return '';
+}
+/**
+*
+* @ingroup tripal_obo_loader
+*/
+function tripal_cv_obo_process_terms($terms,$defaultcv,$obo,$jobid=null,&$newcvs){
+
+   $i = 0;
+   $count = sizeof($terms);
+   $interval = intval($count * 0.01);
+   if($interval > 1){
+      $interval = 1;
+   }
+
+   foreach ($terms as $term){
+
+      // update the job status every 1% terms
+      if($jobid and $i % $interval == 0){
+         tripal_job_set_progress($jobid,intval(($i/$count)*50)); // we mulitply by 50 because parsing and loacing cvterms
+                                                                 // is only the first half.  The other half is updating
+      }                                                          // the cvtermpath table.
+
+      if(!tripal_cv_obo_process_term($term,$defaultcv,$obo,0,$newcvs)){
+         tripal_cv_obo_quiterror("Failed to process terms from the ontology");
+      }
+      $i++;
+   }
+   return 1;
+}
+
+/**
+*
+* @ingroup tripal_obo_loader
+*/
+function tripal_cv_obo_process_term($term,$defaultcv,$obo,$is_relationship=0,&$newcvs){
+
+   // add the cvterm
+   $cvterm = tripal_cv_obo_add_cvterm($term,$defaultcv,$is_relationship,1);     
+   if(!$cvterm){ 
+      tripal_cv_obo_quiterror("Cannot add the term " . $term['id'][0]);
+   }
+   if($term['namespace'][0]){
+      $newcvs[$term['namespace'][0]] = $cvterm->cv_id;
+   }
+
+   // now handle other properites
+   if(isset($term['is_anonymous'])){
+     print "WARNING: unhandled tag: is_anonymous\n";
+   }
+   if(isset($term['alt_id'])){
+      foreach($term['alt_id'] as $alt_id){
+         if(!tripal_cv_obo_add_cvterm_dbxref($cvterm,$alt_id)){
+            tripal_cv_obo_quiterror("Cannot add alternate id $alt_id");
+         }
+      }
+   }
+   if(isset($term['subset'])){
+     print "WARNING: unhandled tag: subset\n";
+   }
+   // add synonyms for this cvterm
+   if(isset($term['synonym'])){
+      if(!tripal_cv_obo_add_synonyms($term,$cvterm)){
+         tripal_cv_obo_quiterror("Cannot add synonyms");
+      }
+   }
+   // reformat the deprecated 'exact_synonym, narrow_synonym, and broad_synonym'
+   // types to be of the v1.2 standard
+   if(isset($term['exact_synonym']) or isset($term['narrow_synonym']) or isset($term['broad_synonym'])){
+      if(isset($term['exact_synonym'])){
+         foreach ($term['exact_synonym'] as $synonym){
+
+            $new = preg_replace('/^\s*(\".+?\")(.*?)$/','$1 EXACT $2',$synonym);
+            $term['synonym'][] = $new;
+         }
+      }
+      if(isset($term['narrow_synonym'])){
+         foreach ($term['narrow_synonym'] as $synonym){
+            $new = preg_replace('/^\s*(\".+?\")(.*?)$/','$1 NARROW $2',$synonym);
+            $term['synonym'][] = $new;
+         }
+      }
+      if(isset($term['broad_synonym'])){
+         foreach ($term['broad_synonym'] as $synonym){
+            $new = preg_replace('/^\s*(\".+?\")(.*?)$/','$1 BROAD $2',$synonym);
+            $term['synonym'][] = $new;
+         }
+      } 
+
+      if(!tripal_cv_obo_add_synonyms($term,$cvterm)){
+         tripal_cv_obo_quiterror("Cannot add/update synonyms");
+      }
+   }
+   // add the comment to the cvtermprop table
+   if(isset($term['comment'])){
+      $comments = $term['comment'];
+      $j = 0;
+      foreach($comments as $comment){
+         if(!tripal_cv_obo_add_cvterm_prop($cvterm,'comment',$comment,$j)){
+            tripal_cv_obo_quiterror("Cannot add/update cvterm property");
+         }
+         $j++;
+      }
+   }    
+   // add any other external dbxrefs
+   if(isset($term['xref'])){
+      foreach($term['xref'] as $xref){
+         if(!tripal_cv_obo_add_cvterm_dbxref($cvterm,$xref)){
+            tripal_cv_obo_quiterror("Cannot add/update cvterm database reference (dbxref).");
+         }
+      }
+   }
+   if(isset($term['xref_analog'])){
+      foreach($term['xref_analog'] as $xref){
+         if(!tripal_cv_obo_add_cvterm_dbxref($cvterm,$xref)){
+            tripal_cv_obo_quiterror("Cannot add/update cvterm database reference (dbxref).");
+         }
+      }
+   }
+   if(isset($term['xref_unk'])){
+      foreach($term['xref_unk'] as $xref){
+         if(!tripal_cv_obo_add_cvterm_dbxref($cvterm,$xref)){
+            tripal_cv_obo_quiterror("Cannot add/update cvterm database reference (dbxref).");
+         }
+      }
+   }
+
+   // add is_a relationships for this cvterm
+   if(isset($term['is_a'])){
+      foreach($term['is_a'] as $is_a){
+         if(!tripal_cv_obo_add_relationship($cvterm,$defaultcv,$obo,'is_a',$is_a,$is_relationship)){
+            tripal_cv_obo_quiterror("Cannot add relationship is_a: $is_a");
+         }
+      }
+   } 
+   if(isset($term['intersection_of'])){
+     print "WARNING: unhandled tag: intersection_of\n";
+   }
+   if(isset($term['union_of'])){
+     print "WARNING: unhandled tag: union_on\n";
+   }
+   if(isset($term['disjoint_from'])){
+     print "WARNING: unhandled tag: disjoint_from\n";
+   }
+   if(isset($term['relationship'])){
+      foreach($term['relationship'] as $value){
+         $rel = preg_replace('/^(.+?)\s.+?$/','\1',$value);
+         $object = preg_replace('/^.+?\s(.+?)$/','\1',$value);
+         if(!tripal_cv_obo_add_relationship($cvterm,$defaultcv,$obo,$rel,$object,$is_relationship)){
+            tripal_cv_obo_quiterror("Cannot add relationship $rel: $object");
+         }
+      }
+   }
+   if(isset($term['replaced_by'])){
+     print "WARNING: unhandled tag: replaced_by\n";
+   }
+   if(isset($term['consider'])){
+     print "WARNING: unhandled tag: consider\n";
+   }
+   if(isset($term['use_term'])){
+     print "WARNING: unhandled tag: user_term\n";
+   }
+   if(isset($term['builtin'])){
+     print "WARNING: unhandled tag: builtin\n";
+   }
+   return 1;
+}
+/**
+*
+* @ingroup tripal_obo_loader
+*/
+function tripal_cv_obo_add_db($dbname){
+
+   $db_sql = "SELECT * FROM {db} WHERE name ='%s'";
+   $db = db_fetch_object(db_query($db_sql,$dbname));
+   if(!$db){
+      if(!db_query("INSERT INTO {db} (name) VALUES ('%s')",$dbname)){
+         tripal_cv_obo_quiterror("Cannot create '$dbname' db in Chado.");
+      }      
+     $db = db_fetch_object(db_query($db_sql,$dbname));
+   }
+   return $db;
+}
+/**
+*
+* @ingroup tripal_obo_loader
+*/
+function tripal_cv_obo_add_cv($name,$comment){
+
+ // see if the CV (default-namespace) exists already in the database
+   $vocab = $name;
+   $remark = $comment;
+   $cv_sql = "SELECT * FROM {cv} WHERE name = '%s'";
+   $cv = db_fetch_object(db_query($cv_sql,$vocab));
+
+   // if the CV exists then update it, otherwise insert
+   if(!$cv){
+      $sql = "INSERT INTO {cv} (name,definition) VALUES ('%s','%s')";
+      if(!db_query($sql,$vocab,$remark)){
+         tripal_cv_obo_quiterror("Failed to create the CV record");
+      }
+      $cv = db_fetch_object(db_query($cv_sql,$vocab));
+   } else {
+      $sql = "UPDATE {cv} SET definition = '%s' WHERE name ='%s'";
+      if(!db_query($sql,$remark,$vocab)){
+         tripal_cv_obo_quiterror("Failed to update the CV record");
+      }
+      $cv = db_fetch_object(db_query($cv_sql,$vocab));
+   }
+   return $cv;
+}
+
+/**
+*
+* @ingroup tripal_obo_loader
+*/
+function tripal_cv_obo_add_relationship($cvterm,$defaultcv,$obo,$rel,$objname,$object_is_relationship=0){
+
+   // make sure the relationship cvterm exists
+   $term = array(
+      'name' => array($rel),
+      'id' => array($rel),
+      'definition' => array(''),
+      'is_obsolete' => array(0),
+   );
+   $relcvterm = tripal_cv_obo_add_cvterm($term,$defaultcv,1,0);
+   if(!$relcvterm){
+      tripal_cv_obo_quiterror("Cannot find or insert the relationship term: $rel\n");
+   }
+
+   // get the object term
+   $objterm = tripal_cv_obo_get_term($obo,$objname);
+   if(!$objterm) { 
+      tripal_cv_obo_quiterror("Could not find object term $objname\n"); 
+   }
+   $objcvterm = tripal_cv_obo_add_cvterm($objterm,$defaultcv,$object_is_relationship,1);
+   if(!$objcvterm){ 
+      tripal_cv_obo_quiterror("Cannot add/find cvterm");
+   }
+
+   // check to see if the cvterm_relationship already exists, if not add it
+   $cvrsql = "SELECT * FROM {cvterm_relationship} WHERE type_id = %d and subject_id = %d and object_id = %d";
+   if(!db_fetch_object(db_query($cvrsql,$relcvterm->cvterm_id,$cvterm->cvterm_id,$objcvterm->cvterm_id))){
+      $sql = "INSERT INTO {cvterm_relationship} ".
+             "(type_id,subject_id,object_id) VALUES (%d,%d,%d)";
+      if(!db_query($sql,$relcvterm->cvterm_id,$cvterm->cvterm_id,$objcvterm->cvterm_id)){
+         tripal_cv_obo_quiterror("Cannot add term relationship: '$cvterm->name' $rel '$objcvterm->name'");
+      }
+   }
+
+   return 1;
+}
+/**
+*
+* @ingroup tripal_obo_loader
+*/
+function tripal_cv_obo_get_term($obo,$id){
+   foreach ($obo as $type){
+      foreach ($type as $term){
+         $accession = $term['id'][0];
+         if(strcmp($accession,$id)==0){
+            return $term;
+         }
+      }
+   }
+   return;
+}
+/**
+*
+* @ingroup tripal_obo_loader
+*/
+function tripal_cv_obo_add_synonyms($term,$cvterm){
+
+   // make sure we have a 'synonym_type' vocabulary
+   $sql = "SELECT * FROM {cv} WHERE name='synonym_type'";
+   $syncv = db_fetch_object(db_query($sql));
+   if(!$syncv){
+      $sql = "INSERT INTO {cv} (name,definition) VALUES ('synonym_type','')";
+      if(!db_query($sql)){
+         tripal_cv_obo_quiterror("Failed to add the synonyms type vocabulary");
+      }
+      $syncv = db_fetch_object(db_query($sql));
+   }
+
+   // now add the synonyms
+   if(isset($term['synonym'])){
+      foreach($term['synonym'] as $synonym){
+         // separate out the synonym definition and the synonym type
+         $def = preg_replace('/^\s*"(.*)"\s*.*$/','\1',$synonym);
+         $type = strtolower(preg_replace('/^.*"\s+(.*?)\s+.*$/','\1',$synonym)); 
+
+         // make sure the synonym type exists in the 'synonym_type' vocabulary
+         $cvtsql = "
+            SELECT * 
+            FROM {cvterm} CVT
+               INNER JOIN {cv} CV ON CVT.cv_id = CV.cv_id
+            WHERE CVT.name = '%s' and CV.name = '%s'
+         ";
+         $syntype = db_fetch_object(db_query($cvtsql,$type,'synonym_type'));
+         if(!$syntype){
+            // build a 'term' object so we can add the missing term
+            $term = array(
+               'name' => array($type),
+               'id' => array("internal:$type"),
+               'definition' => array(''),
+               'is_obsolete' => array(0),
+            );
+            $syntype = tripal_cv_obo_add_cvterm($term,$syncv,0,1);
+            if(!$syntype){
+               tripal_cv_obo_quiterror("Cannot add synonym type: internal:$type");
+            }
+         }       
+
+         // make sure the synonym doesn't already exists
+         $sql = "
+            SELECT * 
+            FROM {cvtermsynonym} 
+            WHERE cvterm_id = %d and synonym = '%s'
+         ";
+         $syn = db_fetch_object(db_query($sql,$cvterm->cvterm_id,$def));
+         if(!$syn){
+            $sql = "INSERT INTO {cvtermsynonym} (cvterm_id,synonym,type_id)
+                    VALUES(%d,'%s',%d)";
+            if(!db_query($sql,$cvterm->cvterm_id,$def,$syntype->cvterm_id)){
+               tripal_cv_obo_quiterror("Failed to insert the synonym for term: $name ($def)");
+            }
+         } 
+
+         // now add the dbxrefs for the synonym if we have a comma in the middle
+         // of a description then this will cause problems when splitting os lets
+         // just change it so it won't mess up our splitting and then set it back
+         // later.
+//         $synonym = preg_replace('/(".*?),\s(.*?")/','$1,_$2',$synonym);
+//         $dbxrefs = preg_split("/, /",preg_replace('/^.*\[(.*?)\]$/','\1',$synonym));
+//         foreach($dbxrefs as $dbxref){
+//            $dbxref = preg_replace('/,_/',", ",$dbxref);
+//            if($dbxref){
+//               tripal_cv_obo_add_cvterm_dbxref($syn,$dbxref);
+//            }
+//         }
+      } 
+   }
+   return 1;
+}
+
+/**
+*
+* @ingroup tripal_obo_loader
+*/
+function tripal_cv_obo_parse($obo_file,&$obo,&$header){
+   $i = 0;
+   $in_header = 1;
+   $stanza = array();
+
+   // iterate through the lines in the OBO file and parse the stanzas
+   $fh = fopen($obo_file,'r');
+   while($line = fgets($fh)) {
+      $i++;
+
+      // remove newlines
+      $line = rtrim($line);  
+
+      // remove any special characters that may be hiding
+      $line = preg_replace('/[^(\x20-\x7F)]*/','', $line);
+
+      // skip empty lines
+      if(strcmp($line,'')==0) { continue; }
+
+      //remove comments from end of lines
+      $line = preg_replace('/^(.*?)\!.*$/','\1',$line);  // TODO: if the explamation is escaped
+
+      if(preg_match('/^\s*\[/',$line)){  // at the first stanza we're out of header
+         $in_header = 0;
+         // load the stanza we just finished reading
+         if(sizeof($stanza) > 0){
+            if(!isset($obo[$type])){
+               $obo[$type] = array();
+            }
+            if(!isset($obo[$type][$stanza['id'][0]])){
+               $obo[$type][$stanza['id'][0]] = $stanza;
+            } else {
+               array_merge($obo[$type][$stanza['id'][0]],$stanza);
+            }
+         } 
+         // get the stanza type:  Term, Typedef or Instance
+         $type = preg_replace('/^\s*\[\s*(.+?)\s*\]\s*$/','\1',$line);
+
+         // start fresh with a new array
+         $stanza = array();
+         continue;
+      }
+      // break apart the line into the tag and value but ignore any escaped colons
+      preg_replace("/\\:/","|-|-|",$line); // temporarily replace escaped colons
+      $pair = explode(":",$line,2);
+      $tag = $pair[0];
+      $value = ltrim(rtrim($pair[1]));// remove surrounding spaces
+      $tag = preg_replace("/\|-\|-\|/","\:",$tag); // return the escaped colon
+      $value = preg_replace("/\|-\|-\|/","\:",$value);
+      if($in_header){
+         if(!isset($header[$tag])){
+            $header[$tag] = array();
+         }
+         $header[$tag][] = $value;
+      } else {
+         if(!isset($stanza[$tag])){
+            $stanza[$tag] = array();
+         }  
+         $stanza[$tag][] = $value;
+      }          
+   }
+   // now add the last term in the file
+   if(sizeof($stanza) > 0){
+      if(!isset($obo[$type])){
+         $obo[$type] = array();
+      }
+      if(!isset($obo[$type][$stanza['id'][0]])){
+         $obo[$type][$stanza['id'][0]] = $stanza;
+      } else {
+         array_merge($obo[$type][$stanza['id'][0]],$stanza);
+      }
+   }
+}
+/**
+*
+* @ingroup tripal_obo_loader
+*/
+function tripal_cv_obo_add_cvterm_dbxref($cvterm,$xref){
+
+   $dbname = preg_replace('/^(.+?):.*$/','$1',$xref);
+   $accession = preg_replace('/^.+?:\s*(.*?)(\{.+$|\[.+$|\s.+$|\".+$|$)/','$1',$xref);
+   $description = preg_replace('/^.+?\"(.+?)\".*?$/','$1',$xref);
+   $dbxrefs = preg_replace('/^.+?\[(.+?)\].*?$/','$1',$xref);
+
+   if(!$accession){
+      tripal_cv_obo_quiterror();
+      watchdog('tripal_cv',"Cannot add a dbxref without an accession: '$xref'"
+         ,NULL,WATCHDOG_WARNING);
+      return 0;
+   }
+
+   // if the xref is a database link, handle that specially
+   if(strcmp($dbname,'http')==0){
+      $accession = $xref;
+      $dbname = 'URL';
+   }
+
+   // check to see if the database exists
+   $db = tripal_cv_obo_add_db($dbname);
+   if(!$db){
+      tripal_cv_obo_quiterror("Cannot find database '$dbname' in Chado.");
+   }
+
+   // now add the dbxref
+   $dbxref = tripal_cv_obo_add_dbxref($db->db_id,$accession,'',$description);
+   if(!$dbxref){ 
+      tripal_cv_obo_quiterror("Cannot find or add the database reference (dbxref)");
+   }
+
+   // finally add the cvterm_dbxref but first check to make sure it exists
+   $sql = "SELECT * from {cvterm_dbxref} WHERE cvterm_id = %d and dbxref_id = %d";
+   if(!db_fetch_object(db_query($sql,$cvterm->cvterm_id,$dbxref->dbxref_id))){            
+      $sql = "INSERT INTO {cvterm_dbxref} (cvterm_id,dbxref_id)".
+             "VALUES (%d,%d)";
+      if(!db_query($sql,$cvterm->cvterm_id,$dbxref->dbxref_id)){
+         tripal_cv_obo_quiterror("Cannot add cvterm_dbxref: $xref");
+      }
+   }
+   return 1;
+}
+/**
+*
+* @ingroup tripal_obo_loader
+*/
+function tripal_cv_obo_add_cvterm_prop($cvterm,$property,$value,$rank){
+
+   // make sure the 'cvterm_property_type' CV exists
+   $cv = tripal_cv_obo_add_cv('cvterm_property_type','');
+   if(!$cv){ 
+      tripal_cv_obo_quiterror("Cannot add/find cvterm_property_type cvterm");
+   }
+
+   // get the property type cvterm.  If it doesn't exist then we want to add it
+   $sql = "
+        SELECT * 
+        FROM {cvterm} CVT INNER JOIN {cv} CV on CVT.cv_id = CV.cv_id
+        WHERE CVT.name = '%s' and CV.name = '%s'
+   ";
+   $cvproptype = db_fetch_object(db_query($sql,$property,'cvterm_property_type'));
+   if(!$cvproptype){
+      $term = array(
+         'name' => array($property),
+         'id' => array("internal:$property"),
+         'definition' => array(''),
+         'is_obsolete' => array(0),
+      );
+      $cvproptype = tripal_cv_obo_add_cvterm($term,$cv,0,0);
+      if(!$cvproptype){  
+         tripal_cv_obo_quiterror("Cannot add cvterm property: internal:$property");
+      }
+   }
+
+
+   // remove any properties that currently exist for this term.  We'll reset them
+   if($rank == 0){
+      $sql = "DELETE FROM {cvtermprop} WHERE cvterm_id = %d";
+      db_query($sql,$cvterm->cvterm_id);
+   }
+
+   // now add the property
+   $sql = "INSERT INTO {cvtermprop} (cvterm_id,type_id,value,rank) ".
+          "VALUES (%d, %d, '%s',%d)";
+   if(!db_query($sql,$cvterm->cvterm_id,$cvproptype->cvterm_id,$value,$rank)){
+      tripal_cv_obo_quiterror("Could not add property $property for term\n");
+   }
+   return 1;
+}
+/**
+*
+* @ingroup tripal_obo_loader
+*/
+function tripal_cv_obo_add_cvterm($term,$defaultcv,$is_relationship = 0,$update = 1){
+
+   // get the term properties
+   $id = $term['id'][0];
+   $name = $term['name'][0];
+   $cvname = $term['namespace'][0];
+   $definition = preg_replace('/^\"(.*)\"/','\1',$term['def'][0]);
+   $is_obsolete = 0;
+   if(isset($term['is_obsolete'][0]) and  strcmp($term['is_obsolete'][0],'true')==0){
+     $is_obsolete = 1;
+   }
+   if(!$cvname){
+      $cvname = $defaultcv->name;
+   }
+   // make sure the CV name exists
+   $cv = tripal_cv_obo_add_cv($cvname,'');
+   if(!$cv){
+      tripal_cv_obo_quiterror("Cannot find namespace '$cvname' when adding/updating $id");
+   }
+
+   // this SQL statement will be used a lot to find a cvterm so just set it
+   // here for easy reference below.
+   $cvtermsql = "SELECT CVT.name, CVT.cvterm_id, DB.name as dbname, DB.db_id 
+                  FROM {cvterm} CVT
+                    INNER JOIN {dbxref} DBX on CVT.dbxref_id = DBX.dbxref_id
+                    INNER JOIN {db} DB on DBX.db_id = DB.db_id
+                    INNER JOIN {cv} CV on CV.cv_id = CVT.cv_id
+                  WHERE CVT.name = '%s' and DB.name = '%s'";  
+
+   // get the accession and the database from the cvterm
+   if(preg_match('/^.+?:.*$/',$id)){
+      $accession = preg_replace('/^.+?:(.*)$/','\1',$id);
+      $dbname = preg_replace('/^(.+?):.*$/','\1',$id);
+   } 
+   if($is_relationship and !$dbname){
+      $accession = $id;
+      // because this is a relationship cvterm first check to see if it 
+      // exists in the relationship ontology. If it does then return the cvterm.
+      //  If not then set the dbname to _global and we'll add it or find it there
+      $cvterm = db_fetch_object(db_query($cvtermsql,$name,'OBO_REL'));
+      if($cvterm){
+         return $cvterm;
+      } else {
+         // next check if this term is in the _global ontology.  If it is then
+         // return it no matter what the original CV
+         $dbname = '_global';
+
+         $cvterm = db_fetch_object(db_query($cvtermsql,$name,$dbname));
+         if($cvterm){
+            return $cvterm;
+         }
+      }
+   }
+   if(!$is_relationship and !$dbname){
+      tripal_cv_obo_quiterror("A database identifier is missing from the term: $id");
+   }
+
+   // check to see if the database exists. 
+   $db = tripal_cv_obo_add_db($dbname);
+   if(!$db){
+      tripal_cv_obo_quiterror("Cannot find database '$dbname' in Chado.");
+   }
+
+
+   // if the cvterm doesn't exist then add it otherwise just update it
+   $cvterm = db_fetch_object(db_query($cvtermsql,$name,$dbname));
+   if(!$cvterm){
+      // check to see if the dbxref exists if not, add it
+      $dbxref =  tripal_cv_obo_add_dbxref($db->db_id,$accession);
+      if(!$dbxref){
+         tripal_cv_obo_quiterror("Failed to find or insert the dbxref record for cvterm, $name (id: $accession), for database $dbname");
+      }
+
+      // check to see if the dbxref already has an entry in the cvterm table
+      $sql = "SELECT * FROM {cvterm} WHERE dbxref_id = %d";
+      $check = db_fetch_object(db_query($sql,$dbxref->dbxref_id));
+
+      if(!$check){
+         // now add the cvterm
+         $sql = "
+            INSERT INTO {cvterm} (cv_id, name, definition, dbxref_id, 
+               is_obsolete, is_relationshiptype) 
+            VALUES (%d,'%s','%s',%d,%d,%d)
+         ";
+         if(!db_query($sql,$cv->cv_id,$name,$definition,
+             $dbxref->dbxref_id,$is_obsolete,$is_relationship)){
+            if(!$is_relationship){
+               tripal_cv_obo_quiterror("Failed to insert the term: $name ($dbname)");
+            } else {
+               tripal_cv_obo_quiterror("Failed to insert the relationship term: $name (cv: " . $cvname . " db: $dbname)");
+            }
+         }  
+         if(!$is_relationship){
+            print "Added CV term: $name ($dbname)\n";
+         } else {
+            print "Added relationship CV term: $name ($dbname)\n";
+         }
+      }
+      elseif($check and strcmp($check->name,$name)!=0){
+         // this dbxref_id alrady exists in the database but the name is 
+         // different.  We will trust that the OBO is correct and that there
+         // has been a name change for this dbxref, so we'll update
+         $sql = "
+            UPDATE {cvterm} SET name = '%s', definition = '%s',
+               is_obsolete = %d, is_relationshiptype = %d 
+            WHERE dbxref_id = %d
+         ";
+         if(!db_query($sql,$name,$definition,$is_obsolete,$is_relationship,$dbxref->dbxref_id)){
+            if(!$is_relationship){
+               tripal_cv_obo_quiterror("Failed to update the term: $name ($dbname)");
+            } else {
+               tripal_cv_obo_quiterror("Failed to update the relationship term: $name (cv: " . $cvname . " db: $dbname)");
+            }
+         } 
+         if(!$is_relationship){
+            print "Updated CV term: $name ($dbname)\n";
+         } else {
+            print "Updated relationship CV term: $name ($dbname)\n";
+         }
+      }
+      elseif($check and strcmp($check->name,$name)==0){
+         // this entry already exists. We're good, so do nothing
+      }
+      $cvterm = db_fetch_object(db_query($cvtermsql,$name,$dbname));
+   }
+   elseif($update) { // update the cvterm
+      $sql = "
+         UPDATE {cvterm} SET name='%s', definition='%s',
+            is_obsolete = %d, is_relationshiptype = %d
+         WHERE cvterm_id = %d
+      ";
+      if(!db_query($sql,$term['name'][0],$definition,
+          $is_obsolete,$is_relationship,$cvterm->cvterm_id)){
+         tripal_cv_obo_quiterror("Failed to update the term: $name");
+      }  
+      $cvterm = db_fetch_object(db_query($cvtermsql,$name,$dbname));         
+      if(!$is_relationship){
+         print "Updated CV term: $name ($dbname)\n";
+      } else {
+         print "Updated relationship CV term: $name ($dbname)\n";
+      }
+   }
+   // return the cvterm
+   return $cvterm;
+}
+
+/**
+*
+* @ingroup tripal_obo_loader
+*/
+function tripal_cv_obo_add_dbxref($db_id,$accession,$version='',$description=''){
+
+   // check to see if the dbxref exists if not, add it
+   $dbxsql = "SELECT dbxref_id FROM {dbxref} WHERE db_id = %d and accession = '%s'";
+   $dbxref = db_fetch_object(db_query($dbxsql,$db_id,$accession));
+   if(!$dbxref){
+      $sql = "
+         INSERT INTO {dbxref} (db_id, accession, version, description)
+         VALUES (%d,'%s','%s','%s')
+      ";
+      if(!db_query($sql,$db_id,$accession,$version,$description)){
+         tripal_cv_obo_quiterror("Failed to insert the dbxref record $accession");
+      }
+      print "Added Dbxref accession: $accession\n";
+      $dbxref = db_fetch_object(db_query($dbxsql,$db_id,$accession));
+   }
+   return $dbxref;
+
+}
+

+ 457 - 0
modules/base/tripal_cv/trees.php

@@ -0,0 +1,457 @@
+<?php
+
+/**
+*
+* @ingroup tripal_cv
+*/
+function tripal_cv_show_browser() {
+  
+   $content = drupal_get_form('tripal_cv_list_form');
+   $content .= "
+      <div id=\"cv_browser\"></div>
+   ";
+   return $content;
+}
+
+
+/**
+ * Generates JSON used for generating an exapandable tree of terms from
+ * a controlled vocabulary that has associated counts. 
+ *
+ * The progammer must first create a materialized view that
+ * will generate count data for a given controlled vocabulary.  For example, the Tripal
+ * Analysis GO creates a materialized view for counting Gene Ontology assignments
+ * to features.  This view is created on install of the module and is named
+ * 'go_count_analysis'.
+ *
+ * Second, the progammer must add an HTML 'div' box to the desired page
+ * with a class name of 'tripal_cv_tree', and an id of the following format:
+ *
+ *  tripal_[module_name]_cv_tree_[unique id]
+ *
+ * where [module_name] is the name of the tripal module (e.g. tripal_analyisis_go)
+ * and [unique id] is some unique identifier that the contolling module 
+ * recognizes. This string is the $tree_id variable passed as the first argument
+ * to the function.  For example, the Tripal GO Analysis module generates 
+ * tree ids of the form:
+ * 
+ *  tripal_analysis_go_cv_tree_10_2_bp
+ * 
+ * In this case the module that will manage this tree is identified as 'tripal_analysis_go' and within
+ * the [unique id] portion contains the
+ * organism_id (e.g. 10), analysis_id (e.g. 2) and tree type (bp = biological process). 
+ *
+ * Second, the programmer must then define a hook in the controlling module for setting
+ * some options used to build the chart.  The hook has the form:  hook_cv_tree($tree_id).
+ * This hook should accept the full $tree_id as the single parameter.  For the Tripal
+ * Analysis GO module the hook is named:  tripal_analysis_go_cv_tree.  
+ *
+ * The array returned by this hook must have the following fields: 
+ *  - cv_id 
+ *      the cv_id for the controlled vocabulary
+ *  - count_mview
+ *      the name of the materialized view that contains the count data
+ *      this materialized view must have at least two columns, one with the cvterm_id
+ *      for each term and a second column containing the counts. The tree will only
+ *      be expanded to include child terms that have counts.
+ *  - cvterm_id_column
+ *      the column name in the materialized view that contains
+ *      the cvterm_ids
+ *  - count_column
+ *      the column name in the materialized view that contains the
+ *      counts
+ *  - filter
+ *      an SQL compatible WHERE clause string (whithout the word 'WHERE')
+ *      that can be used for filtering the records in the materialized view.
+ *  - label
+ *      the title for the tree
+ *
+ * Example from the tripal_analysis_go module:
+ * @code
+ *  function tripal_analysis_go_cv_tree($tree_id){
+ * 
+ *   $organism_id = preg_replace("/^tripal_analysis_go_cv_tree_(\d+)-(\d+)_(bp|cc|mf)$/","$1",$tree_id);
+ *   $analysis_id = preg_replace("/^tripal_analysis_go_cv_tree_(\d+)-(\d+)_(bp|cc|mf)$/","$2",$tree_id);
+ *   $type        = preg_replace("/^tripal_analysis_go_cv_tree_(\d+)-(\d+)_(bp|cc|mf)$/","$3",$tree_id);
+ * 
+ *   if(strcmp($type,'mf')==0){
+ *      $class = 'molecular_function';
+ *   }
+ *   if(strcmp($type,'cc')==0){
+ *      $class = 'cellular_component';
+ *   }
+ *   if(strcmp($type,'bp')==0){
+ *      $class = 'biological_process';
+ *   }
+ * 
+ *   $options = array(
+ *      cv_id            => tripal_cv_get_cv_id($class),
+ *      count_mview      => 'go_count_analysis',
+ *      cvterm_id_column => 'cvterm_id',
+ *      count_column     => 'feature_count',
+ *      filter           => "CNT.organism_id = $organism_id AND CNT.analysis_id = $analysis_id",
+ *      label            => 'Features',
+ *   );
+ *   return $options;
+ * }
+ *
+ * @endcode
+ *
+ * @param $tree_id
+ *   The unique identifier for the tree
+ *
+ * @return
+ *   JSON array needed for the jsTree package for generating expandable trees.
+ *
+ * With these three components (materialized view, a 'div' box with proper CSS class and ID, and a hook_cv_tree)
+ * a tree will be created on the page.  There is no need to call this function directly.
+ *
+ * @ingroup tripal_cv
+ */
+function tripal_cv_tree($tree_id){
+  // parse out the tripal module name from the chart_id to find out 
+  // which Tripal "hook" to call:
+  $tripal_mod = preg_replace("/^(tripal_.+?)_cv_tree_(.+)$/","$1",$tree_id);
+  if($tripal_mod){
+     $callback = $tripal_mod . "_cv_tree";
+
+     // now call the function in the module responsible for the tree.  This 
+     // should call the tripal_cv_init_cv with the proper parameters set for
+     // getting the cv_id of the vocabulary to use
+     $opt = call_user_func_array($callback,array($tree_id));
+
+     // we only need to return the cv_id for this function call.
+     $json_array[] = $opt[cv_id];
+  }
+  $json_array[] = $tree_id;
+  return drupal_json($json_array);
+}
+
+
+/**
+*
+* @ingroup tripal_cv
+*/
+function tripal_cv_update_tree() {
+   $content = array();
+   $ontology = 'sequence';
+
+   # get the id of the term to look up
+   $cv = check_plain($_REQUEST['cv']);
+   $term = check_plain($_REQUEST['term']);
+   $tree_id = check_plain($_REQUEST['tree_id']);
+
+   # get the options needed for this tree from the tripal module that 
+   # wants to create the tree
+   $tripal_mod = preg_replace("/^(tripal_.+?)_cv_tree_(.+)$/","$1",$tree_id);
+   if($tripal_mod){
+      $callback = $tripal_mod . "_cv_tree";
+      $opt = call_user_func_array($callback,array($tree_id));
+   }
+
+   # get the CV root terms
+   if(strcmp($term,'root')==0){
+      if(!$cv){
+        $cv = $opt[cv_id];
+      }
+      $content = tripal_cv_init_tree($cv,$opt[count_mview],
+         $opt[cvterm_id_column],$opt[count_column],$opt[filter],$opt[label]);
+   } 
+   # get the children terms
+   else {
+      $content = tripal_cv_get_term_children($term,$opt[count_mview],
+         $opt[cvterm_id_column],$opt[count_column],$opt[filter],$opt[label]);
+   }
+   drupal_json($content);
+}
+/**
+* Generates JSON needed for jsTree Root-level Branches
+*
+* This function returns the JSON array for the jsTree 
+*    jQuery code that builds a tree for browsing the ontology.  This function
+*    should be called to generate the root level branches of the tree.
+*
+* @ingroup tripal_cv
+*/
+function tripal_cv_init_tree($cv_id,$cnt_table = null, $fk_column = null,
+   $cnt_column = null, $filter = null, $label = null) {
+
+   // get the list of root terms for the provided CV
+   $sql = "
+      SELECT *
+      FROM {cv_root_mview} CRM
+      WHERE cv_id = %d
+   ";
+   $previous_db = tripal_db_set_active('chado');
+   $results = db_query($sql,$cv_id);
+   tripal_db_set_active($previous_db); 
+
+   // prepare the SQL statement that will allow us to pull out count 
+   // information for each term in the tree.
+   if($cnt_table){
+      if(!$filter){
+         $filter = '(1=1)'; 
+      }
+      $cnt_sql = "
+         SELECT CVT.name, CVT.cvterm_id, CNT.$cnt_column as num_items
+         FROM {$cnt_table} CNT 
+          INNER JOIN cvterm CVT on CNT.$fk_column = CVT.cvterm_id 
+         WHERE $filter AND CVT.cvterm_id = %d
+         ORDER BY $cnt_column desc
+      ";
+   }
+ 
+   while ($term = db_fetch_object($results)) {
+      $name = $term->name;
+      $count = 0;
+      if($cnt_table){
+         $previous_db = tripal_db_set_active('chado');
+         $cnt_results = db_query($cnt_sql,$term->cvterm_id);
+         tripal_db_set_active($previous_db); 
+         while($cnt = db_fetch_object($cnt_results)){
+            $count += $cnt->cnt;
+         }
+         if($count > 0){
+            $name .= " ($count $label(s))";
+         }
+      } 
+      $content[] = array(
+           'attributes' => array (
+           'id' => $term->cvterm_id,
+        ),
+        state => 'closed',
+        data => $name,
+        children => array (),
+      );
+   }
+
+   return $content;
+
+}
+/**
+*  Generates SON needed for jsTree -expanding a term to view children
+*
+*  This function returns the JSON array for the jsTree 
+*    jQuery code when expanding a term to view it's children.
+*
+* @ingroup tripal_cv_api
+*/
+function tripal_cv_get_term_children($cvterm_id,$cnt_table = null, 
+   $fk_column = null,$cnt_column = null, $filter = null, $label = null) {
+   # get the children for the term provided
+   $sql = "
+      SELECT CVTR.cvterm_relationship_id,CVTR.subject_id,
+         CVT1.name as subject_name, CVT3.name as type_name, CVTR.type_id, 
+         CVT2.name as object_name,CVTR.object_id 
+      FROM {cvterm_relationship} CVTR
+         INNER JOIN CVTerm CVT1 on CVTR.subject_id = CVT1.cvterm_id
+         INNER JOIN CVTerm CVT2 on CVTR.object_id = CVT2.cvterm_id
+         INNER JOIN CVTerm CVT3 on CVTR.type_id = CVT3.cvterm_id
+         INNER JOIN CV on CV.cv_id = CVT1.cv_id
+      WHERE CVTR.object_id = %d
+      ORDER BY CVT1.name
+   ";
+   $previous_db = tripal_db_set_active('chado');
+   $results = db_query($sql,$cvterm_id);
+   tripal_db_set_active($previous_db);
+
+
+   // prepare the SQL statement that will allow us to pull out count 
+   // information for each term in the tree.
+   if($cnt_table){
+      if(!$filter){
+         $filter = '(1=1)'; 
+      }
+      $cnt_sql = "
+         SELECT CVT.name, CVT.cvterm_id, CNT.$cnt_column as num_items
+         FROM {$cnt_table} CNT 
+          INNER JOIN cvterm CVT on CNT.$fk_column = CVT.cvterm_id 
+         WHERE $filter AND CVT.cvterm_id = %d
+         ORDER BY $cnt_column desc
+      ";
+   }
+   // populate the JSON content array
+   while ($term = db_fetch_object($results)) {
+      // count the number of items per term if requested
+      $name = $term->subject_name;
+      $count = 0;
+      if($cnt_table){
+         $previous_db = tripal_db_set_active('chado');
+         $cnt_results = db_query($cnt_sql,$term->subject_id);
+         tripal_db_set_active($previous_db); 
+         while($cnt = db_fetch_object($cnt_results)){
+            $count += $cnt->num_items;
+         }
+         if($count > 0){
+            $name .= " (".number_format($count)." $label)";
+            // check if we have any children if so then set the value
+            $previous_db = tripal_db_set_active('chado');
+            $children = db_fetch_object(db_query($sql,$term->subject_id));
+            tripal_db_set_active($previous_db);
+            $state = 'leaf';
+            if($children){
+               $state = 'closed';
+            }
+            $content[] = array(
+               'attributes' => array (
+                  'id' => $term->subject_id,
+               ),
+               state => $state,
+               data => $name,
+               children => array(),
+            );
+         }
+      } else {
+         // check if we have any children if so then set the value
+         $previous_db = tripal_db_set_active('chado');
+         $children = db_fetch_object(db_query($sql,$term->subject_id));
+         tripal_db_set_active($previous_db);
+         $state = 'leaf';
+         if($children){
+            $state = 'closed';
+         }
+         $content[] = array(
+            'attributes' => array (
+               'id' => $term->subject_id,
+            ),
+            state => $state,
+            data => $name,
+            children => array(),
+         );
+      }
+   }
+   $content[] = $cnt_sql;
+   return $content;
+}
+/**
+* 
+* @ingroup tripal_cv
+*/
+function tripal_cv_init_browser($cv_id) {
+
+   $content = "
+        <div id=\"tripal_cv_cvterm_info_box\">
+           <a href=\"#\" onclick=\"$('#tripal_cv_cvterm_info_box').hide()\" style=\"float: right\">Close [X]</a>
+           <h3>Term Information</h3>
+           <div id=\"tripal_cv_cvterm_info\"></div>
+        </div>
+        <div id=\"tripal_ajaxLoading\" style=\"display:none\">
+           <div id=\"loadingText\">Loading...</div>
+           <img src=\"$url\">
+        </div> 
+         <h3>Tree Browser</h3>
+        <div id=\"browser\"</div></div>
+   ";
+
+   drupal_json(array('update' => "$content"));
+}
+/**
+*
+* @ingroup tripal_cv
+*/
+function tripal_cv_cvterm_info($cvterm_id){
+
+   # get the id of the term to look up
+   $cv = check_plain($_REQUEST['cv']);
+   $tree_id = check_plain($_REQUEST['tree_id']);
+
+   // first get any additional information to add to the cvterm
+   if(strcmp($tree_id,'undefined')!=0){
+      $tripal_mod = preg_replace("/^(tripal_.+?)_cv_tree_(.+)$/","$1",$tree_id);
+      if($tripal_mod){
+         $callback = $tripal_mod . "_cvterm_add";
+         $opt = call_user_func_array($callback,array($cvterm_id,$tree_id));
+      }
+   }
+
+   $sql = "
+      SELECT CVT.name as cvtermname, CVT.definition, CV.name as cvname,
+         DBX.accession,DB.urlprefix,DB.db_id,DB.name as dbname
+      FROM {CVTerm} CVT
+        INNER JOIN CV on CVT.cv_id = CV.cv_id
+        INNER JOIN dbxref DBX on CVT.dbxref_id = DBX.dbxref_id
+        INNER JOIN DB on DBX.db_id = DB.db_id
+      WHERE CVT.cvterm_id = %d
+   ";
+   $previous_db = tripal_db_set_active('chado');
+   $cvterm = db_fetch_object(db_query($sql,$cvterm_id));
+   tripal_db_set_active($previous_db);
+   $sql = "
+      SELECT CVTS.synonym, CVT.name as cvname
+      FROM {cvtermsynonym} CVTS
+        INNER JOIN cvterm CVT on CVTS.type_id = CVT.cvterm_id
+      WHERE CVTS.cvterm_id = %d
+    
+   ";
+   $previous_db = tripal_db_set_active('chado');
+   $results = db_query($sql,$cvterm_id);
+   tripal_db_set_active($previous_db);
+   while($synonym = db_fetch_object($results)){
+      $synonym_rows .= "<b>$synonym->cvname:</b>  $synonym->synonym<br>";
+   }
+   $accession = $cvterm->accession;
+   if($cvterm->urlprefix){
+      $accession = "<a href=\"$cvterm->urlprefix$cvterm->accession\">$cvterm->accession</a>";
+   }
+   $content = "
+      <div id=\"cvterm\">
+      <table>
+        <tr><th>Term</th><td>$cvterm->cvtermname</td></tr>
+        <tr><th>Accession</th><td>$accession</td></tr>
+        <tr><th>Ontology</th><td>$cvterm->cvname</td></tr>
+        <tr><th>Definition</th><td>$cvterm->definition</td></tr>
+        <tr><th>Synonyms</th><td>$synonym_rows</td></tr>
+        <tr><th>Internal ID</th><td>$cvterm_id</td></tr> 
+   ";
+
+   // now add in any additional options from a hook
+   if($opt){
+      foreach ($opt as $key=>$value){
+         $content .= "<tr><th>$key</th><td>$value</td>";
+      }
+   }
+
+   // close out the information table
+   $content .= "
+      </table>
+      </div>
+   ";
+   drupal_json(array('update' => $content));
+}
+/**
+*
+* @ingroup tripal_cv
+*/
+function tripal_cv_list_form($form_state) {
+
+   // get a list of db from chado for user to choose
+    $sql = "
+      SELECT DISTINCT CV.name,CV.cv_id
+      FROM {cvterm_relationship} CVTR
+         INNER JOIN cvterm CVT on CVTR.object_id = CVT.cvterm_id
+         INNER JOIN CV on CV.cv_id = CVT.cv_id
+   ";
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $results = db_query ($sql);
+   tripal_db_set_active($previous_db);
+   $blastdbs = array();
+   $cvs[''] = '';
+   while ($cv = db_fetch_object($results)){
+      $cvs[$cv->cv_id] = $cv->name;
+   }
+
+   $form['db_options'] = array(
+      '#type' => 'value',
+      '#value' => $cvs
+   );        
+   $form['cv_list'] = array(
+      '#title' => t('CVs with relationships'),
+      '#type' => 'select',
+      '#description' => t('Choose the controlled vocabulary to browse'),
+      '#options' => $form['db_options']['#value'],
+      '#attributes' => array(
+         'onChange' => "return tripal_cv_init_browser(this)",
+      )
+   );
+   return $form;
+}

+ 517 - 0
modules/base/tripal_cv/tripal_cv.api.inc

@@ -0,0 +1,517 @@
+<?php
+
+/**
+ * @defgroup tripal_cv_api CV Module API
+ * @ingroup tripal_api
+ * @ingroup tripal_cv
+ * This module provides a set of functions to simplify working with
+ * controlled vocabularies.  Most of the API functions deal with retrieving
+ * terms or their parent vocabularies.  
+ * 
+ * However, the API also supports
+ * generation of trees for browsing a vocabulary as well as generation of 
+ * pie graphs for display of hierarchical counts of terms.  Version 0.3b of
+ * Tripal provides a feature browser and a feature summary chart uses
+ * the API functions provided here.  But in general charts and trees can be
+ * created for any controlled vocabulary.
+ *
+ */
+
+/**
+ * Purpose: To retrieve a chado controlled vocabulary object
+ *
+ * @param $select_values
+ *   An array meant to uniquely select a given controlled vocabulary
+ *
+ * @return
+ *   Chado controlled vocabulary object
+ *
+ * The controlled vocabulary is selected using tripal_core_chado select and as such the
+ * $select_values array parameter meant to uniquely identify the controlled vocab to be 
+ * returned follows the same form as when using tripal_core_chado_select directly.
+ *
+ * Example Usage:
+ * @code
+    $select_values = array(
+      'name' => 'feature_property'
+    );
+    $cv_object = tripal_cv_get_cv($select_values);
+ * @endcode
+ *  The above code selects the feature_property cv and returns the following object:
+ * @code
+    $cv_object = stdClass Object ( 
+      [cv_id] => 13
+      [name] => feature_property
+      [definition] => 
+    ); 
+ * @endcode
+ *
+ * @ingroup tripal_cv_api
+ */
+function tripal_cv_get_cv ($select_values) {
+
+  $columns = array(
+    'cv_id', 
+    'name', 
+    'definition', 
+  );
+  $results = tripal_core_chado_select('cv', $columns, $select_values);
+  if (sizeof($results) == 1) {
+    return $results[0];
+  } elseif (empty($results)) {
+    watchdog('tripal_cv', 
+      'tripal_cv_get_cv: No cv matches criteria values:%values',
+      array('%values' => print_r($select_values, TRUE)),
+      WATCHDOG_WARNING
+    );
+    return FALSE;
+  } else {
+    watchdog('tripal_cv', 
+      'tripal_cv_get_cv: 2+ cvs match criteria values:%values',
+      array('%values' => print_r($select_values, TRUE)),
+      WATCHDOG_WARNING
+    );
+  }
+  
+}
+
+// Purpose: To retrieve a chado cv object
+// @param $where_options 
+//   @code
+//        array(
+//													<column_name> => array(
+//														'type' => <type of column: INT/STRING>,
+//														'value' => <the vlaue you want to filter on>,
+//														'exact' => <if TRUE use =; if FALSE use ~>,
+//													)
+//				)
+// @endcode
+//
+// @return 
+//   Chado cv object with all fields from the chado cv table
+//
+// @ingroup tripal_cv_api
+//
+//function tripal_cv_get_cv ($where_options)
+
+/**
+ * Retrieve a cv given the cv name
+ *
+ * @param $name
+ *  The name of the cv to be returned
+ * @return 
+ *   The cv object for the specified CV name
+ *
+ * @ingroup tripal_cv_api 
+ */
+function tripal_cv_get_cv_by_name ($name) {
+	$previous_db = tripal_db_set_active('chado');	
+   $r = db_fetch_object(db_query("SELECT * FROM cv WHERE name = '%s'",$name));
+   tripal_db_set_active($previous_db);
+
+  return $r;
+}
+
+/**
+ * Retrieve the cv object for the specified CV id
+ *
+ * NOTE: This function is deprecated.
+ * @see tripal_core_chado_generate_vars
+ *
+ * @param $cv_id
+ *   The unique identifier for the cv retrieve
+ *
+ * @return
+ *   An object describing the cv
+ *
+ * @ingroup tripal_cv_api
+ */
+function tripal_cv_get_cv_by_id ($cv_id) {
+	$previous_db = tripal_db_set_active('chado');	
+   $r = db_fetch_object(db_query("SELECT * FROM cv WHERE cv_id = %d",$cv_id));
+   tripal_db_set_active($previous_db);
+
+  return $r;
+}
+
+/**
+ * Create an options array to be used in a form element which provides a list of all chado cvs
+ *
+ * @return 
+ *   An array(cv_id => name) for each cv in the chado cv table
+ *
+ * @ingroup tripal_cv_api
+ */
+function tripal_cv_get_cv_options() {
+
+  $previous_db = tripal_db_set_active('chado');
+  $result = db_query(
+    "SELECT cv_id, name FROM cv"
+  );
+  tripal_db_set_active($previous_db);
+
+  $options = array();
+  while ( $r = db_fetch_object($result) ) {
+    $options[$r->cv_id] = $r->name;
+  }
+
+  return $options;
+
+}
+
+/**
+ * To retrieve a chado controlled vocabulary term object
+ *
+ * @param $select_values
+ *   An array meant to uniquely select a given controlled vocabulary term
+ *
+ * @return
+ *   Chado controlled vocabulary term object
+ *
+ * The controlled vocabulary term is selected using tripal_core_chado select and as such the
+ * $select_values array parameter meant to uniquely identify the controlled vocab term to be 
+ * returned follows the same form as when using tripal_core_chado_select directly.
+ *
+ * Example Usage:
+ * @code
+    $select_values = array(
+      'name' => 'synonym',
+      'cv_id' => array(
+        'name' => 'feature_property'
+      )
+    );
+    $cvterm_object = tripal_cv_get_cvterm($select_values);
+ * @endcode
+ *  The above code selects the synonym cvterm from the feature_proeprty cv and returns 
+ *  the following object:
+ * @code
+    $cvterm_object = stdClass Object ( 
+      [cvterm_id] => 2099
+      [name] => synonym
+      [definition] => Historic community symbol, may have originally been symbol []
+      [is_obsolete] => 0
+      [is_relationshiptype] => 1
+      [cv_cv_id] => 13
+      [cv_name] => feature_property
+      [cv_definition] => 
+      [dbreference_dbxref_id] => 2581
+      [dbreference_accession] => synonym
+      [dbreference_description] => 
+      [dbreference_version] => 
+      [dbreference_db_db_id] => 49
+      [dbreference_db_name] => SOFP
+      [dbreference_db_description] => 
+      [dbreference_db_urlprefix] => 
+      [dbreference_db_url] => 
+    ); 
+ * @endcode
+ *
+ * @ingroup tripal_cv_api
+ */
+function tripal_cv_get_cvterm ($select_values) {
+
+  $columns = array(
+    'cvterm_id', 
+    'cv_id', 
+    'name', 
+    'definition', 
+    'dbxref_id', 
+    'is_obsolete', 
+    'is_relationshiptype'
+  );
+  $results = tripal_core_chado_select('cvterm', $columns, $select_values);
+  if (sizeof($results) == 1) {
+    // Add cv
+    $cvterm = tripal_cv_add_cv_to_object(array('cv_id'=>$results[0]->cv_id),$results[0],array());
+    unset($cvterm->cv_id);
+    
+    // Add dbxref
+    $cvterm = tripal_db_add_dbxref_to_object(array('dbxref_id'=>$cvterm->dbxref_id),$cvterm,array());
+    unset($cvterm->dbxref_id);
+    
+    return $cvterm;
+  } elseif (empty($results)) {
+    watchdog('tripal_cv', 
+      'tripal_cv_get_cvterm: No cvterm matches criteria values:%values',
+      array('%values' => print_r($select_values, TRUE)),
+      WATCHDOG_WARNING
+    );
+    return FALSE;
+  } else {
+    watchdog('tripal_cv', 
+      'tripal_cv_get_cvterm: 2+ cvterms match criteria values:%values',
+      array('%values' => print_r($select_values, TRUE)),
+      WATCHDOG_WARNING
+    );
+  }
+ 
+}
+
+/**
+ * Retrieve a chado cvterm object with a given name
+ *
+ * @param $name
+ *   the cvterm.name
+ * @param $cv_id
+ *   the cv_id of the term you are looking for
+ *
+ * @return 
+ *   cvterm object
+ *
+ * @ingroup tripal_cv_api
+ */
+function tripal_cv_get_cvterm_by_name ($name, $cv_id=0) {
+
+  if (!empty($cv_id)) {
+    $sql = "SELECT * FROM cvterm WHERE name='%s' AND cv_id=%d";
+    $previous_db = tripal_db_set_active('chado');
+    $r = db_fetch_object(db_query($sql, $name, $cv_id));
+    tripal_db_set_active($previous_db);
+  } else {
+    $sql = "SELECT * FROM cvterm WHERE name='%s'";
+    $previous_db = tripal_db_set_active('chado');
+    $r = db_fetch_object(db_query($sql, $name));
+    tripal_db_set_active($previous_db);
+  }
+  
+  return $r;
+  
+}
+
+/**
+ * Create an options array to be used in a form element
+ *   which provides a list of all chado cvterms
+ * 
+ * @param $cv_id 
+ *   The chado cv_id;
+ *   only cvterms with the supplied cv_id will be returned
+ * @return 
+ *   An array(cvterm_id => name) 
+ *   for each cvterm in the chado cvterm table where cv_id=that supplied
+ *
+ * @ingroup tripal_cv_api
+ */
+function tripal_cv_get_cvterm_options($cv_id = 0) {
+
+  $previous_db = tripal_db_set_active('chado');
+  if ($cv_id > 0) {
+  	$result = db_query(
+    	"SELECT cvterm_id, name FROM cvterm WHERE cv_id=%d", $cv_id
+  	);
+  } else {
+  	$result = db_query(
+    	"SELECT cvterm_id, name FROM cvterm"
+  	);
+  }
+  tripal_db_set_active($previous_db);
+
+  $options = array();
+  while ( $r = db_fetch_object($result) ) {
+    $options[$r->cvterm_id] = $r->name;
+  }
+
+  return $options;
+
+}
+
+/**
+ * Implements hook_chado_cvterm_schema()
+ * Purpose: To add descriptions and foreign keys to default table description
+ * Note: This array will be merged with the array from all other implementations
+ *
+ * @return
+ *    Array describing the cvterm table
+ *
+ * @ingroup tripal_schema_api 
+ */
+function tripal_cv_chado_cvterm_schema() {
+  $description = array();
+
+  $description['foreign keys']['cv'] = array(
+        'table' => 'cv',
+        'columns' => array(
+          'cv_id' => 'cv_id',
+        ),
+  );
+      
+  $description['foreign keys']['dbxref'] = array(
+        'table' => 'dbxref',
+        'columns' => array(
+          'dbxref_id' => 'dbxref_id',
+        ),
+  ); 
+
+  return $description;
+}
+
+/**
+*
+* @ingroup tripal_cv_api
+*/
+function tripal_cv_add_cv($name,$comment){
+
+ // see if the CV (default-namespace) exists already in the database
+   $vocab = $name;
+   $remark = $comment;
+   $cv_sql = "SELECT * FROM {cv} WHERE name = '%s'";
+   $cv = db_fetch_object(db_query($cv_sql,$vocab));
+
+   // if the CV exists then update it, otherwise insert
+   if(!$cv){
+      $sql = "INSERT INTO {cv} (name,definition) VALUES ('%s','%s')";
+      if(!db_query($sql,$vocab,$remark)){
+         watchdog('tripal_cv', "Failed to create the CV record",NULL,WATCHDOG_WARNING);
+         return 0;
+      }
+      $cv = db_fetch_object(db_query($cv_sql,$vocab));
+   } else {
+      $sql = "UPDATE {cv} SET definition = '%s' WHERE name ='%s'";
+      if(!db_query($sql,$remark,$vocab)){
+         watchdog('tripal_cv', "Failed to update the CV record",NULL,WATCHDOG_WARNING);
+         return 0;
+      }
+      $cv = db_fetch_object(db_query($cv_sql,$vocab));
+   }
+   return $cv;
+}
+/**
+*
+* @ingroup tripal_cv_api
+*/
+function tripal_cv_add_cvterm($term,$defaultcv,$is_relationship = 0,$update = 1){
+
+   // get the term properties
+   $id = $term['id'];
+   $name = $term['name'];
+   $cvname = $term['namespace'];
+   $definition = preg_replace('/^\"(.*)\"/','\1',$term['def']);
+   $is_obsolete = 0;
+   if(isset($term['is_obsolete']) and  strcmp($term['is_obsolete'],'true')==0){
+     $is_obsolete = 1;
+   }
+   if(!$cvname){
+      $cvname = $defaultcv->name;
+   }
+   // make sure the CV name exists
+   $cv = tripal_cv_add_cv($cvname,'');
+   if(!$cv){
+      watchdog('tripal_cv', "Cannot find namespace '$cvname' when adding/updating $id",NULL,WATCHDOG_WARNING);
+      return 0;
+   }
+
+   // this SQL statement will be used a lot to find a cvterm so just set it
+   // here for easy reference below.
+   $cvtermsql = "SELECT CVT.name, CVT.cvterm_id, DB.name as dbname, DB.db_id 
+                  FROM {cvterm} CVT
+                    INNER JOIN {dbxref} DBX on CVT.dbxref_id = DBX.dbxref_id
+                    INNER JOIN {db} DB on DBX.db_id = DB.db_id
+                    INNER JOIN {cv} CV on CV.cv_id = CVT.cv_id
+                  WHERE CVT.name = '%s' and DB.name = '%s'";  
+
+   // get the accession and the database from the cvterm
+   if(preg_match('/^.+?:.*$/',$id)){
+      $accession = preg_replace('/^.+?:(.*)$/','\1',$id);
+      $dbname = preg_replace('/^(.+?):.*$/','\1',$id);
+   } 
+   if($is_relationship and !$dbname){
+      $accession = $id;
+      // because this is a relationship cvterm first check to see if it 
+      // exists in the relationship ontology. If it does then return the cvterm.
+      //  If not then set the dbname to _global and we'll add it or find it there
+      $cvterm = db_fetch_object(db_query($cvtermsql,$name,'OBO_REL'));
+      if($cvterm){
+         return $cvterm;
+      } else {
+         // next check if this term is in the _global ontology.  If it is then
+         // return it no matter what the original CV
+         $dbname = '_global';
+
+         $cvterm = db_fetch_object(db_query($cvtermsql,$name,$dbname));
+         if($cvterm){
+            return $cvterm;
+         }
+      }
+   }
+   if(!$is_relationship and !$dbname){
+      watchdog('tripal_cv', "A database identifier is missing from the term: $id",NULL,WATCHDOG_WARNING);
+      return 0;
+   }
+
+   // check to see if the database exists. 
+   $db = tripal_db_add_db($dbname);
+   if(!$db){
+      watchdog('tripal_cv', "Cannot find database '$dbname' in Chado.",NULL,WATCHDOG_WARNING);
+      return 0;
+   }
+
+
+   // if the cvterm doesn't exist then add it otherwise just update it
+   $cvterm = db_fetch_object(db_query($cvtermsql,$name,$dbname));
+   if(!$cvterm){
+      // check to see if the dbxref exists if not, add it
+      $dbxref =  tripal_db_add_dbxref($db->db_id,$accession);
+      if(!$dbxref){
+         watchdog('tripal_cv', "Failed to find or insert the dbxref record for cvterm, $name (id: $accession), for database $dbname",NULL,WATCHDOG_WARNING);
+         return 0;
+      }
+
+      // check to see if the dbxref already has an entry in the cvterm table
+      $sql = "SELECT * FROM {cvterm} WHERE dbxref_id = %d";
+      $check = db_fetch_object(db_query($sql,$dbxref->dbxref_id));
+
+      if(!$check){
+         // now add the cvterm
+         $sql = "
+            INSERT INTO {cvterm} (cv_id, name, definition, dbxref_id, 
+               is_obsolete, is_relationshiptype) 
+            VALUES (%d,'%s','%s',%d,%d,%d)
+         ";
+         if(!db_query($sql,$cv->cv_id,$name,$definition,
+             $dbxref->dbxref_id,$is_obsolete,$is_relationship)){
+            if(!$is_relationship){
+               watchdog('tripal_cv', "Failed to insert the term: $name ($dbname)",NULL,WATCHDOG_WARNING);
+               return 0;
+            } else {
+               watchdog('tripal_cv', "Failed to insert the relationship term: $name (cv: " . $cvname . " db: $dbname)",NULL,WATCHDOG_WARNING);
+               return 0;
+            }
+         }  
+      }
+      elseif($check and strcmp($check->name,$name)==0){
+         // this entry already exists. We're good, so do nothing
+      }
+      elseif($check and strcmp($check->name,$name)!=0){
+         watchdog('tripal_cv', "The dbxref already exists in the cvterm table: $dbxref->dbxref_id ($$accession) for term $name",NULL,WATCHDOG_WARNING);
+         return 0;
+      }
+      $cvterm = db_fetch_object(db_query($cvtermsql,$name,$dbname));
+      if(!$is_relationship){
+         print "Added CV term: $name ($dbname)\n";
+      } else {
+         print "Added relationship CV term: $name ($dbname)\n";
+      }
+   }
+   elseif($update) { // update the cvterm
+      $sql = "
+         UPDATE {cvterm} SET name='%s', definition='%s',
+            is_obsolete = %d, is_relationshiptype = %d
+         WHERE cvterm_id = %d
+      ";
+      if(!db_query($sql,$term['name'],$definition,
+          $is_obsolete,$is_relationship,$cvterm->cvterm_id)){
+         watchdog('tripal_cv', "Failed to update the term: $name",NULL,WATCHDOG_WARNING);
+         return 0;
+
+      }  
+      $cvterm = db_fetch_object(db_query($cvtermsql,$name,$dbname));         
+      if(!$is_relationship){
+         print "Updated CV term: $name ($dbname)\n";
+      } else {
+         print "Updated relationship CV term: $name ($dbname)\n";
+      }
+   }
+   // return the cvterm
+   return $cvterm;
+}
+
+

+ 10 - 0
modules/base/tripal_cv/tripal_cv.info

@@ -0,0 +1,10 @@
+; $Id: tripal_cv.info,v 1.3 2009/12/05 06:11:56 ficklin Exp $
+name = Tripal CV
+description = The controlled vocabulary module for the Tripal package that integrates Drupal and GMOD chado. This module provides support for managing and viewing controlled vocabularies.
+core = 6.x
+project = tripal_cv
+package = Tripal
+version = 6.x-0.3.1b
+
+dependencies[] = tripal_core
+dependencies[] = tripal_db

+ 150 - 0
modules/base/tripal_cv/tripal_cv.install

@@ -0,0 +1,150 @@
+<?php
+
+/**
+*  Implementation of hook_install();
+*
+* @ingroup tripal_cv
+*/
+function tripal_cv_install(){
+
+   // create the module's data directory
+   tripal_create_moddir('tripal_cv');
+
+   // Add the materialized view needed to keep track of the 
+   // 
+   $previous_db = tripal_db_set_active('chado');
+   if (db_table_exists('cv_root_mview')) {
+      $sql = "DROP TABLE cv_root_mview";
+      db_query($sql);
+   }
+   tripal_db_set_active($previous_db);
+
+   // Create the MView
+   tripal_add_mview(
+      // view name
+      'cv_root_mview',
+      // module name  
+      'tripal_cv',
+      // table name
+      'cv_root_mview',
+      // table schema
+      'name character varying(1024), cvterm_id integer, cv_id integer, 
+       cv_name character varying(255)',
+      // indexed columns
+      'cvterm_id, cv_id',
+      // SQL statement that populates the view
+      'SELECT DISTINCT CVT.name,CVT.cvterm_id, CV.cv_id, CV.name
+       FROM {cvterm_relationship} CVTR
+         INNER JOIN cvterm CVT on CVTR.object_id = CVT.cvterm_id
+         INNER JOIN CV on CV.cv_id = CVT.cv_id
+       WHERE CVTR.object_id not in
+         (SELECT subject_id FROM {cvterm_relationship}) ',
+      // special index
+      ''
+   );
+
+   // create the tables that correlate OBO files/references with a chado CV
+   drupal_install_schema('tripal_cv');
+   tripal_cv_add_obo_defaults();
+}
+/**
+*  This update adds the new tripal_obo table.  This is an upgrade from
+*  Tripal version 0.2
+*
+* @ingroup tripal_cv
+*/
+
+function tripal_cv_update_6000(){
+   drupal_install_schema('tripal_cv');
+   tripal_cv_add_obo_defaults();
+   $ret = array(
+      '#finished' => 1,
+   );
+   
+   return $ret;
+}
+/**
+* Implementation of hook_uninstall()
+*
+* @ingroup tripal_cv
+*/
+function tripal_cv_uninstall(){
+
+	// remove the materialized view
+   $mview = tripal_mviews_get_mview_id('cv_root_mview');
+   if($mview){
+	   tripal_mviews_action('delete',$mview);
+	}
+
+   drupal_uninstall_schema('tripal_cv');
+}
+
+/**
+ * Implementation of hook_requirements(). Make sure 'Tripal Core' is enabled
+ * before installation
+ *
+ * @ingroup tripal_cv
+ */
+function tripal_cv_requirements($phase) {
+   $requirements = array();
+   if ($phase == 'install') {
+      if (!function_exists('tripal_create_moddir')) {
+         $requirements ['tripal_cv'] = array(
+            'title' => "tripal_cv",
+            'value' => "Required modules must be installed first before Tripal CV module can be installed",
+            'severity' => REQUIREMENT_ERROR,
+         );
+      }
+   }
+   return $requirements;
+}
+/**
+* Implementation of hook_schema().
+*
+* @ingroup tripal_cv
+*/
+function tripal_cv_schema() {
+   $schema = tripal_cv_get_schemas();
+   return $schema;
+}
+/**
+* This function simply defines all tables needed for the module to work
+* correctly.  By putting the table definitions in a separate function we 
+* can easily provide the entire list for hook_install or individual
+* tables for an update.
+*
+* @ingroup tripal_cv
+*/
+function tripal_cv_get_schemas (){  
+  $schema = array();
+
+  $schema['tripal_cv_obo'] = array(
+      'fields' => array(
+         'obo_id' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
+         'name'  => array('type' => 'varchar','length' => 255),
+         'path'   => array('type' => 'varchar','length' => 1024),
+      ),
+      'indexes' => array(
+         'obo_id' => array('obo_id'),
+       ),
+      'primary key' => array('obo_id'),
+  );
+  return $schema;
+}
+/**
+* Add's defaults to the tripal_cv_obo table
+*
+* @ingroup tripal_cv
+*/
+function tripal_cv_add_obo_defaults(){
+   // insert commonly used ontologies into the tables
+   $isql = "INSERT INTO tripal_cv_obo (name,path) VALUES ('%s','%s')";
+   db_query($isql,'Chado Feature Properties',drupal_get_path('module','tripal_cv').'/feature_property.obo');
+   db_query($isql,'Relationship Ontology','http://www.obofoundry.org/ro/ro.obo');
+   db_query($isql,'Sequence Ontology','http://song.cvs.sourceforge.net/*checkout*/song/ontology/so.obo');
+   db_query($isql,'Gene Ontology','http://www.geneontology.org/ontology/gene_ontology.obo');
+   db_query($isql,'Cell Ontology','http://obo.cvs.sourceforge.net/obo/obo/ontology/anatomy/cell_type/cell.obo?rev=HEAD');
+   db_query($isql,'Plant Structure Ontology','http://palea.cgrb.oregonstate.edu/viewsvn/Poc/trunk/ontology/OBO_format/po_anatomy.obo?view=co');  
+   db_query($isql,'Plant Growth and Development Stages Ontology','http://palea.cgrb.oregonstate.edu/viewsvn/Poc/trunk/ontology/OBO_format/po_temporal.obo?view=co');
+
+}

+ 1105 - 0
modules/base/tripal_cv/tripal_cv.module

@@ -0,0 +1,1105 @@
+<?php
+
+require_once "charts.php";
+require_once "trees.php";
+require_once "obo_loader.php";
+
+require_once "tripal_cv.api.inc";
+
+/**
+ * @defgroup tripal_cv CV Module
+ * @ingroup tripal_modules
+ */
+ 
+/**
+*
+* @ingroup tripal_cv
+*/
+function tripal_cv_init(){
+
+   // add the tripal_cv JS and CSS
+   drupal_add_css(drupal_get_path('theme', 'tripal').'/css/tripal_cv.css');
+   drupal_add_js (drupal_get_path('theme', 'tripal').'/js/tripal_cv.js');
+
+   // add the jsTree JS and CSS
+   drupal_add_css(drupal_get_path('theme', 'tripal').'/js/jsTree/source/tree_component.css');
+   drupal_add_js (drupal_get_path('theme', 'tripal').'/js/jsTree/source/_lib.js');
+   drupal_add_js (drupal_get_path('theme', 'tripal').'/js/jsTree/source/tree_component.js');
+}
+/**
+*
+* @ingroup tripal_cv
+*/
+function tripal_cv_menu() {
+   $items = array();
+
+  $items['admin/tripal/tripal_cv'] = array(
+    'title' => 'Controlled Vocabularies',
+    'description' => 'Basic Description of Tripal CV Module Functionality',
+    'page callback' => 'tripal_cv_module_description_page',
+    'access arguments' => array('administer site configuration'),
+    'type' => MENU_NORMAL_ITEM,
+  );
+
+   $items['admin/tripal/tripal_cv/cvtermpath'] = array(
+     'title' => 'Update Chado cvtermpath tables',
+     'description' => 'The Chado cvtermpath table provides lineage for terms and is  useful for quickly finding any ancestor parent of a term.  However, this table must be populated.  This page allows for populating of this table one vocabulary at a time',
+     'page callback' => 'drupal_get_form',
+     'page arguments' => array('tripal_cv_cvtermpath_form'),
+     'access arguments' => array('administer site configuration'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+
+   $items['admin/tripal/tripal_cv/edit_cv'] = array(
+     'title' => 'Update/Delete Controlled Vocabulary',
+     'description' => 'Manage controlled vocabularies/ontolgoies in Chado ',
+     'page callback' => 'tripal_cv_admin_page',
+     'access arguments' => array('administer site configuration'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+
+   $items['admin/tripal/tripal_cv/add_cv'] = array(
+     'title' => 'Add a Controlled Vocabulary',
+     'page callback' => 'drupal_get_form',
+     'page arguments' => array('tripal_cv_add_form'),
+     'access arguments' => array('access administration pages'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+
+   $items['admin/tripal/tripal_cv/add_cvterm'] = array(
+     'title' => 'Add Controlled Vocabulary Terms',
+     'description' => 'Manage controlled vocabulary/ontology terms in Chado ',
+     'page callback' => 'drupal_get_form',
+     'page arguments' => array('tripal_cv_add_cvterm_form'),
+     'access arguments' => array('administer site configuration'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+
+   $items['admin/tripal/tripal_cv/add_cvterm/js'] = array(
+     'page callback' => 'tripal_cv_add_cvterm_callback',
+     'access arguments' => array('administer site configuration'),
+     'type' => MENU_CALLBACK,
+   );
+   
+   $items['admin/tripal/tripal_cv/obo_loader'] = array(
+     'title' =>'Add/Update Ontology With OBO File',
+     'page callback' => 'drupal_get_form',
+     'page arguments' => array('tripal_cv_obo_form'),
+     'access arguments' => array('access administration pages'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+   $items['admin/tripal/tripal_cv/edit/js'] = array(
+     'title' => 'Edit Controlled Vocabularies',
+     'page callback' => 'tripal_ajax_cv_edit',
+     'access arguments' => array('access administration pages'),
+     'type' => MENU_CALLBACK,
+   );
+
+   $items['tripal_cv_chart'] = array(
+      'path' => 'tripal_cv_chart',
+      'title' => t('CV Chart'),
+      'page callback' => 'tripal_cv_chart',
+      'page arguments' => array(1),
+      'access arguments' => array('access content'),
+      'type' => MENU_CALLBACK
+   );
+
+   $items['tripal_cv_tree'] = array(
+      'path' => 'tripal_cv_tree',
+      'title' => t('CV Term Viewer'),
+      'page callback' => 'tripal_cv_tree',
+      'page arguments' => array(1),
+      'access arguments' => array('access content'),
+      'type' => MENU_CALLBACK
+   );
+   // menu items for working with the CV module tree browser
+   $items['cv_browser'] = array(
+      'title' => t('CV Relationship Browser'),
+      'page callback' => 'tripal_cv_show_browser',
+      'access arguments' => array('access chado_cv content'),
+      'type' => MENU_CALLBACK
+   );
+   $items['tripal_cv_init_browser'] = array(
+      'path' => 'tripal_cv_init_browser',
+      'title' => t('CV Browser'),
+      'page callback' => 'tripal_cv_init_browser',
+      'page arguments' => array(1),
+      'access arguments' => array('access content'),
+      'type' => MENU_CALLBACK
+   );
+   // menu item for interaction with the tree
+   $items['tripal_cv_update_tree'] = array(
+      'path' => 'tripal_cv_update_tree',
+      'title' => t('CV Tree'),
+      'page callback' => 'tripal_cv_update_tree',
+      'page arguments' => array(2,3),
+      'access arguments' => array('access content'),
+      'type' => MENU_CALLBACK
+   );
+   // menu items for working with terms
+   $items['tripal_cv_cvterm_info'] = array(
+      'path' => 'tripal_cv_cvterm_info',
+      'title' => t('CV Term Viewer'),
+      'page callback' => 'tripal_cv_cvterm_info',
+      'page arguments' => array(1),
+      'access arguments' => array('access content'),
+      'type' => MENU_CALLBACK
+   );
+
+   $items['tripal_cv_cvterm_edit'] = array(
+      'path' => 'tripal_cv_edit',
+      'title' => t('CV Term Editor'),
+      'page callback' => 'tripal_cv_cvterm_edit',
+      'page arguments' => array(1),
+      'access arguments' => array('edit chado_cv content'),
+      'type' => MENU_CALLBACK
+   );
+
+   return $items;
+}
+
+/**
+ *  The following function proves access control for users trying to
+ *  perform actions on data managed by this module
+ *
+ * @ingroup tripal_cv
+ */
+function chado_cv_access($op, $node, $account){
+  if ($op == 'create') {
+      if(!user_access('create chado_cv content', $account)){
+         return FALSE;
+      }
+   }
+   if ($op == 'update') {
+      if (!user_access('edit chado_cv content', $account)) {
+         return FALSE;
+      }
+   }
+   if ($op == 'delete') {
+      if (!user_access('delete chado_cv content', $account)) {
+         return FALSE;
+      }
+   }
+   if ($op == 'view') {
+      if (!user_access('access chado_cv content', $account)) {
+         return FALSE;
+      }
+   }
+   return NULL;
+}
+
+/**
+*  Set the permission types that the chado module uses.  Essentially we
+*  want permissionis that protect creation, editing and deleting of chado
+*  data objects
+*
+* @ingroup tripal_cv
+*/
+function tripal_cv_perm(){
+   return array(
+      'access chado_cv content',
+      'create chado_cv content',
+      'delete chado_cv content',
+      'edit chado_cv content',
+   );
+}
+
+/**
+ * Implements hook_views_api()
+ * Purpose: Essentially this hook tells drupal that there is views support for
+ *  for this module which then includes tripal_cv.views.inc where all the
+ *  views integration code is
+ *
+ * @ingroup tripal_cv
+ */
+function tripal_cv_views_api() {
+	return array('api' => 2.0);
+}
+
+/**
+ *  We need to let drupal know about our theme functions and their arguments.
+ *  We create theme functions to allow users of the module to customize the
+ *  look and feel of the output generated in this module
+ *
+ * @ingroup tripal_cv
+ */
+function tripal_cv_theme () {
+   return array(
+      'tripal_cv_cvterm_edit' => array (
+         'arguments' => array('cvterm'),
+      ),
+   );
+}
+
+/**
+ * Purpose: Provide Guidance to new Tripal Admin
+ *
+ * @return HTML Formatted text
+ *
+ * @ingroup tripal_cv
+ */
+function tripal_cv_module_description_page() {
+  $text = '';
+
+  $text = '<h3>Tripal Controlled Vocabulary Administrative Tools Quick Links</h3>';
+    $text .= '<ul>';
+      $text .= '<li>'.l('Add CV', 'admin/tripal/tripal_cv/add_cv').'</li>';
+      $text .= '<li>'.l('Update/Delete CV', 'admin/tripal/tripal_cv/edit_cv').'</li>';
+      $text .= '<li>'.l('Add/Update Ontology', 'admin/tripal/tripal_cv/obo_loader').'</li>';
+      $text .= '<li>'.l('Add CV term', 'admin/tripal/tripal_cv/add_cvterm').'</li>';
+      $text .= '<li>'.l('Term Listing', 'admin/tripal/tripal_cv/list_cvterms').'</li>';
+      $text .= '<li>'.l('Update cvtermpath', 'admin/tripal/tripal_cv/cvtermpath').'</li>';
+    $text .= '</ul>';
+    
+    
+  $text .= '<h3>Module Description:</h3>';
+  $text .= '<p>The Tripal CV (Controlled Vocabularies) Module provides 
+  	functionality for managing controlled vocabularies and the terms they are
+  	comprised of. The flexibility and extendibility of the chado schema depends
+  	on controlled vocabularies. For example, by using a controlled vocabulary for
+  	feature types the chado schema can describe features of any type, even those
+  	we have not concieved of yet.</p>';
+
+  $text .= '<h3>Setup Instructions:</h3>';
+  $text .= '<p>After installation of the controlled vocabulary module, the following tasks should be performed:</p>';
+    $text .= '<ol>';
+      $text .= '<li><p><b>Set Permissions</b>: The cv module supports the Drupal user permissions interface for 
+               controlling access to cv content and functions. These permissions include viewing, 
+               creating, editing or administering of
+               cv content. The default is that only the original site administrator has these 
+               permissions.  You can <a href="'.url('admin/user/roles').'">add roles</a> for classifying users, 
+               <a href="'.url('admin/user/user').'">assign users to roles</a> and
+               <a href="'.url('admin/user/permissions').'">assign permissions</a> for the cv content to 
+               those roles.  For a simple setup, allow anonymous users access to view organism content and 
+               allow the site administrator all other permissions.</p></li>';
+      $text .= '<li><b>Loading of Ontologies/Controlled Vocabularies</b>: You can access this loader at '.
+        l('Admin->Tripal Management->Tripal CV->Add/Update Ontology With OBO File', 'admin/tripal/tripal_cv/obo_loader')
+        .'. This loader allows you to choose from a list of common ontologies or
+        enter the URL or location to an OBO file. Even the list of common
+        ontologies is using a URL ensuring you get the most up to date ontology.</p>';
+      $text .= '<p>This loader adds a Tripal Job which then waits in a queue to
+        be launched. To launch Tripal Jobs either navidate to the root of your
+        drupal installation and execute "php sites/all/modules/tripal/tripal_core/
+        tripal_launch_jobs.php <drupal user>" or set up a cron job (See user manual
+        for more details).</p>';
+      $text .= '<p>NOTE: in some cases, community developed ontologies for your
+        data may not yet be developed. In this case, it is suggested that you begin
+        developement of an ontology using one of the online tools. You might find
+        that many researchers are trying to deal with the same data and are willing
+        to help you in this endevor. You can '.l('create a controlled vocabulary','admin/tripal/tripal_cv/add_cv').' and '
+        . l('add terms to it', 'admin/tripal/tripal_cv/add_cvterm') .' to provide functionality to your site while you are waiting
+        for the ontology to be developed.</p></li>';
+    $text .= '</ol>';
+  
+  $text .= '<h3>Features of this Module:</h3>';
+  $text .= '<p>Aside from the data loading described above, the Tripal Controlled Vocabulary (CV) module also provides the following functionality:</p>';
+    $text .= '<ul>';  
+      $text .= '<li><b>Create/Update/Delete A Controlled Vocaulbulary</b>: to create your own controlled vocabulary go to '.
+        l('Admin->Tripal Management->Tripal CV->Add a Controlled Vocabulary','admin/tripal/tripal_cv/add_cv')
+        .' and fill out the form provided.To Update/Delete a controlled vocabulary
+        go to '.l('Admin->Tripal Management->Tripal CV->Update/Delete Controlled Vocabulary', 'admin/tripal/tripal_cv/edit_cv')
+        .', select the existing controlled vocabulary you want to modify and then 
+        edit it as desired. This only modifies the name, description of a
+        controlled vocabulary. See the next section for adding, removing, editing
+        the term a controlled vocabulary contains.</li>';
+      
+      $text .= '<li><b>Create a Controlled Vocaulbulary Term</b>: To Add a term to an already existing controlled vocabulary
+        go to '.l('Admin->Tripal Management->Tripal CV->Add a Controlled Vocabulary Term','admin/tripal/tripal_cv/add_cvterm')
+        .', select the controlled vocabulary you want to add terms to and then fill
+        out the form.</li>';
+      $text .= '<li><b>Controlled Vocabulary Term Browser</b>: This module provides a '.l('basic listing','admin/tripal/tripal_cv/list_cvterms').' of controlled vocabulry terms for
+              for all vocabularies currently in chado. It does not require indexing for Drupal searching but relies on Drupal Views. 
+              <a href="http://drupal.org/project/views">Drupal Views</a> must be installed.</li>';
+      $text .= '<li><p><b>Integration with Drupal Views</b>: <a href="http://drupal.org/project/views">Drupal Views</a> is
+              a powerful tool that allows the site administrator to create lists or basic searching forms of Chado content.
+              It provides a graphical interface within Drupal to allow the site admin to directly query the Chado database
+              and create custom lists without PHP programming or customization of Tripal source code.  Views can also
+              be created to filter content that has not yet been synced with Druapl in order to protect access to non
+              published data (only works if Chado was installed using Tripal).  You can see a list of available pre-existing
+              Views <a href="'.url('admin/build/views/').'">here</a>, as well as create your own. </p></li>';
+    $text .= '</ul>';  
+  return $text;
+}
+
+///////////////////////////////////////////
+// Edit/Delete CVs
+//////////////////////////////////////////
+ 
+/**
+ * Purpose: Provides the form for Updating and Deleteing existing
+ *   chado controlled vocabularies (See chado cv table)
+ *
+  * @ingroup tripal_cv
+ */
+function tripal_cv_admin_page(){
+   $add_url = url("admin/tripal/tripal_cv/new");
+   $obo_url = url("admin/tripal/tripal_cv/obo");
+   $cvtermpath_url = url("admin/tripal/tripal_cv/cvtermpath");
+   $browser_url = url("cv_browser");
+   $output = "<a href=\"$add_url\">Add a new controlled vocabulary</a> | ";
+   $output .= "<a href=\"$browser_url\">Browse a vocabulary</a> | ";    
+   $output .= "<a href=\"$obo_url\">Add/Update Ontology With OBO File</a> | ";
+   $output .= "<a href=\"$cvtermpath_url\">Update the cvtermpath table</a> ";
+   $output .= drupal_get_form('tripal_cv_select_form');
+   $output .= '<div id="db-edit-div">Please select a vocabulary above to view or edit</div>';
+   return $output;
+}
+
+/**
+ * Purpose: Provides the actual "Select CV" form on the Update/Delete Controlled
+ *   Vocabulary page. This form also triggers the edit javascript
+ * @todo Modify this form to use Drupal AJAX
+ *
+  * @ingroup tripal_cv
+ */
+function tripal_cv_select_form(){
+
+	$previous_db = tripal_db_set_active('chado');  // use chado database
+	// get a list of db from chado for user to choose
+	$sql = "SELECT * FROM {cv} WHERE NOT name = 'tripal' ORDER BY name ";
+	$results = db_query ($sql);
+	tripal_db_set_active($previous_db); // use drupal database
+
+	$cvs = array();
+   $cvs[] = '';
+	while ($cv = db_fetch_object($results)){
+		$cvs[$cv->cv_id] = $cv->name;
+	}
+
+	$form['cvid'] = array(
+      '#title' => t('Controlled Vocabulary/Ontology Name'),
+      '#type' => 'select',
+      '#options' => $cvs,
+      '#ahah' => array(
+         'path' => 'admin/tripal/tripal_cv/edit/js',
+         'wrapper' => 'db-edit-div',
+         'effect' => 'fade',
+         'event' => 'change',
+         'method' => 'replace',
+      ),
+	);
+
+   return $form;
+}
+/**
+ * Purpose: The edit controlled vocabulary javascript
+ *
+  * @ingroup tripal_cv
+ */
+function tripal_ajax_cv_edit (){ 
+   // get the database id, build the form and then return the JSON object
+   $cvid = $_POST['cvid'];
+   $form = drupal_get_form('tripal_cv_edit_form',$cvid);
+   drupal_json(array('status' => TRUE, 'data' => $form));
+}
+
+/**
+ * Purpose: Provides a form to allow updating/deleteing of controlled vocabularies
+ *
+  * @ingroup tripal_cv
+ */
+function tripal_cv_edit_form(&$form_state = NULL,$cvid = NULL){
+
+  
+   $sql = "SELECT * FROM {cv} WHERE cv_id = %d ";
+   $previous_db = tripal_db_set_active('chado');
+   $cv = db_fetch_object(db_query($sql,$cvid));
+   tripal_db_set_active($previous_db);
+
+
+   # set the default values.  If there is a value set in the 
+   # form_state then let's use that, otherwise, we'll pull 
+   # the values from the database 
+   $default_db = $form_state['values']['name'];
+   $default_desc = $form_state['values']['description'];
+   $default_url = $form_state['values']['url'];
+   $default_urlprefix = $form_state['values']['urlprefix'];
+   if(!$default_db){
+      $default_cv = $cv->name;
+   }
+   if(!$default_desc){
+      $default_desc = $cv->definition;
+   }
+ 
+
+   $form['cvid'] = array(
+      '#type' => 'hidden',
+      '#value' => $cvid
+   );
+ 
+   $form['name']= array(
+      '#type'          => 'textfield',
+      '#title'         => t("Controlled Vocabulary name"),
+      '#description'   => t('Please enter the name for this vocabulary.'),
+      '#required'      => FALSE,
+      '#default_value' => $default_cv,
+      '#weight'        => 1
+   );
+
+   $form['definition']= array(
+      '#type'          => 'textarea',
+      '#title'         => t('Description'),
+      '#description'   => t('Please enter a description for this vocabulary'),
+      '#default_value' => $default_desc,
+      '#weight'        => 2
+   );
+
+   $form['update'] = array (
+     '#type'         => 'submit',
+     '#value'        => t('Update'),
+     '#weight'       => 5,
+     '#executes_submit_callback' => TRUE,
+   );
+   $form['delete'] = array (
+     '#type'         => 'submit',
+     '#value'        => t('Delete'),
+     '#weight'       => 6,
+     '#executes_submit_callback' => TRUE,
+   );
+
+   $form['#redirect'] = 'admin/tripal/tripal_cv';
+
+
+   return $form;
+}
+
+/**
+ * Purpose: The submit function of the update/delete controlled vocabulary form
+ *
+  * @ingroup tripal_cv
+ */
+function tripal_cv_edit_form_submit($form, &$form_state){
+
+   $name =  $form_state['values']['name'];
+   $desc =  $form_state['values']['definition'];
+   $cvid =  $form_state['values']['cvid'];
+   $op   =  $form_state['values']['op'];
+  
+   if(strcmp($op,'Update')==0){
+      $sql = "
+         UPDATE {cv} SET 
+           name = '%s',
+           definition = '%s'
+         WHERE cv_id = %d
+      ";
+      $previous_db = tripal_db_set_active('chado');
+      $db = db_query($sql,$name,$desc,$cvid);
+      tripal_db_set_active($previous_db);
+      if($db){
+        drupal_set_message("Controlled vocabulary updated");
+      } else {
+        drupal_set_message("Failed to update controlled vocabulary.");
+      }
+   } 
+   if(strcmp($op,'Delete')==0){
+      $sql = "
+         DELETE FROM {cv}
+         WHERE cv_id = %d
+      ";
+      $previous_db = tripal_db_set_active('chado');
+      $db = db_query($sql,$cvid);
+      tripal_db_set_active($previous_db);
+      if($db){
+        drupal_set_message("Controlled vocabulary deleted");
+      } else {
+        drupal_set_message("Failed to delete controlled vocabulary.");
+      }
+   }
+
+
+   return '';
+}
+
+/////////////////////////////////////
+// Add CVs
+////////////////////////////////////
+
+/**
+ * Purpose: Provides the Add controlled vocabulary form
+ *
+  * @ingroup tripal_cv
+ */
+function tripal_cv_add_form(&$form_state = NULL){
+
+
+   $form['cvid'] = array(
+      '#type' => 'hidden',
+      '#value' => $cvid
+   );
+
+
+   $form['name']= array(
+      '#type'          => 'textfield',
+      '#title'         => t("Controlled Vocabulary name"),
+      '#description'   => t('Please enter the name for this vocabulary.  This field will be ignored if an OBO file or URL is provided above'),
+      '#required'      => FALSE,
+      '#default_value' => $default_cv,
+      '#weight'        => 1
+   );
+
+   $form['definition']= array(
+      '#type'          => 'textarea',
+      '#title'         => t('Description'),
+      '#description'   => t('Please enter a description for this vocabulary'),
+      '#default_value' => $default_desc,
+      '#weight'        => 2
+   );
+
+
+   $form['add'] = array (
+     '#type'         => 'submit',
+     '#value'        => t('Add'),
+     '#weight'       => 5,
+     '#executes_submit_callback' => TRUE,
+   );
+
+   $form['#redirect'] = 'admin/tripal/tripal_cv';
+
+
+   return $form;
+}
+
+/**
+ * Purpose: The submit function for the add controlled vocabulary form
+ *
+  * @ingroup tripal_cv
+ */
+function tripal_cv_add_form_submit($form, &$form_state){
+
+   $name =  $form_state['values']['name'];
+   $desc =  $form_state['values']['definition'];
+
+   $sql = "
+      INSERT INTO {cv}
+       (name,definition)
+      VALUES 
+       ('%s','%s')
+   ";
+   $previous_db = tripal_db_set_active('chado');
+   $db = db_query($sql,$name,$desc);
+   tripal_db_set_active($previous_db);
+   if($db){
+     drupal_set_message("Controlled vocabulary added");
+   } else {
+     drupal_set_message("Failed to add controlled vocabulary.");
+   }
+
+
+   return '';
+}
+
+//////////////////////////////////////////
+// Add Controlled Vocabulary Term
+//////////////////////////////////////////
+
+/**
+ * Purpose: Provides the form that allows adding of terms to an existing
+ *   controlled vocabulary
+ *
+  * @ingroup tripal_cv
+ */
+function tripal_cv_add_cvterm_form (&$form_state) {
+	$form = array();
+	
+	$results = tripal_core_chado_select(
+		'cv', 
+		array('cv_id','name'),
+		array()
+	);
+	$cvs = array();
+  $cvs[] = '';
+	foreach ($results as $cv) {
+		$cvs[$cv->cv_id] = $cv->name;
+	}
+
+	$form['cv_id'] = array(
+      '#title' => t('Controlled Vocabulary/Ontology Name'),
+      '#type' => 'select',
+      '#options' => $cvs,
+      '#ahah' => array(
+         'path' => 'admin/tripal/tripal_cv/add_cvterm/js',
+         'wrapper' => 'cvterm-add-div',
+         'effect' => 'fade',
+         'event' => 'change',
+         'method' => 'replace',
+      ),
+      '#required' => TRUE,
+	);
+
+	$form['add_cvterm'] = array(
+		'#type'					 => 'item',
+		'#value'				 => t('Please select a vocabulary above to add a term to it'),
+		'#prefix'				 => '<div id="cvterm-add-div">',
+		'#suffix'				 => '</div>'		
+	);
+	
+	if ($form_state['values']['cv_id']) {
+
+		$form['add_cvterm'] = array(
+			'#type'					 => 'fieldset',
+			'#title'	 			 => t('Add Term to the current Controlled Vocabulary'),
+			'#prefix'				 => '<div id="cvterm-add-div">',
+			'#suffix'				 => '</div>'
+		);
+	
+  	$form['add_cvterm']['name']= array(
+    	'#type'          => 'textfield',
+    	'#title'         => t("Term Name"),
+    	'#description'   => t('Please enter the name for this vocabulary term.'),
+    	'#required'      => FALSE,
+    	'#weight'        => 1,
+    	'#required' => TRUE,
+  	);
+
+  	$form['add_cvterm']['definition']= array(
+    	'#type'          => 'textarea',
+    	'#title'         => t('Description'),
+    	'#description'   => t('Please enter a description for this term'),
+  		'#weight'        => 2
+  	);
+  
+    $form['add_cvterm']['is_relationshiptype'] = array(
+      '#type'          => 'checkbox',
+      '#title'         => t('This term describes a relationship?'),
+      '#weight'        => 3,
+    );
+
+    $form['add_cvterm']['is_obsolete'] = array(
+      '#type'          => 'checkbox',
+      '#title'         => t('This term is obsolete?'),
+      '#weight'        => 3,
+    );
+    
+	  $results = tripal_core_chado_select(
+		  'db',
+		  array('db_id', 'name'),
+		  array()
+	  );
+		$dbs = array();
+  	$dbs[] = '';
+	  foreach ($results as $db) {
+		  $dbs[$db->db_id] = $db->name;
+	  }
+		$form['add_cvterm']['db_id'] = array(
+			'#type' 				=> 'select',
+			'#title' 				=> t('Database'),
+			'#description' 	=> t('All terms must be assocated with an external database. 
+													Please select the external database to associate with
+													this term'),
+			'#options'			=> $dbs,
+			'#weight'				=> 4,
+			'#required' => TRUE,
+		);
+		
+		$form['add_cvterm']['submit'] = array(
+			'#type'					=> 'submit',
+			'#value' 				=> 'Add Term',
+			'#weight'				=> 5
+		);
+		
+	} //end of if cv selected
+
+	
+  return $form;
+}
+
+/**
+ * Purpose: Validates the input for adding a cvterm
+ *
+  * @ingroup tripal_cv
+ */
+function tripal_cv_add_cvterm_form_validate ($form, &$form_state) {
+  if (!empty($form_state['ahah_submission'])) {
+    return;
+  }
+  
+}
+
+/**
+ * Purpose: Adds terms to an existing controlled vocabulary
+ *
+  * @ingroup tripal_cv
+ */
+function tripal_cv_add_cvterm_form_submit ($form, &$form_state) {
+  if (!empty($form_state['ahah_submission'])) {
+    return;
+  }
+  
+  // Add dbxref for cvterm
+  $dbxref_insert_values = array(
+    'db_id' => $form_state['values']['db_id'],
+    'accession' => $form_state['values']['name'],
+    'description' => 'cvterm reference',
+  );
+  $dbxref_results = tripal_core_chado_select(
+    'dbxref',
+    array('dbxref_id'),
+    $dbxref_insert_values
+  );
+  if (!$dbxref_results[0]->dbxref_id) {
+    $dbxref_insert_values['version'] = '1';
+    $dbxref_success = tripal_core_chado_insert(
+      'dbxref',
+      $dbxref_insert_values
+    );
+  } else {
+    $dbxref_success = true;
+  }
+  
+  // Add cvterm
+  if ($dbxref_success) {
+    $insert_values = array(
+      'cv_id' => $form_state['values']['cv_id'],
+      'name' => $form_state['values']['name'],
+      'definition' => $form_state['values']['definition'],
+      'dbxref_id' => $dbxref_insert_values,
+      'is_obsolete' => (string) $form_state['values']['is_obsolete'],
+      'is_relationshiptype' => (string) $form_state['values']['is_relationshiptype'],
+    );
+    $success = tripal_core_chado_insert(
+      'cvterm',
+      $insert_values
+    );
+    if ($success) {
+      drupal_set_message('Successfully Added Term to Controlled Vocabulary');
+    } else {
+      drupal_set_message('Unable to add controlled vocabulary term', 'error');
+      watchdog(
+        'tripal_cv',
+        'Cvterm Insert: Unable to insert cvterm where values: %values',
+        array('%values' => print_r($insert_values,TRUE)),
+        WATCHDOG_ERROR
+      );
+    }
+  } else {
+    drupal_set_message('Unable to add database reference for controlled vocabulary term', 'error');
+    watchdog(
+      'tripal_cv',
+      'Cvterm Insert: Unable to insert dbxref for cvterm where values: %values',
+      array('%values' => print_r($dbxref_insert_values,TRUE)),
+      WATCHDOG_ERROR
+    );
+  }  
+  return;
+}
+
+/**
+ * Purpose: This function gets called when the selecting of a cv from
+ *   the select list triggers it. This function simply rebuilds the form
+ *   with new information. No elements are created here
+ *
+  * @ingroup tripal_cv
+ */
+function tripal_cv_add_cvterm_callback () {
+
+  // Retrieve the form from the cache
+  $form_state = array('storage' => NULL);
+  $form_build_id = $_POST['form_build_id'];
+  $form = form_get_cache($form_build_id, $form_state);
+	
+  // Preparing to process the form
+  $args = $form['#parameters'];
+  $form_id = array_shift($args);
+  $form_state['post'] = $form['#post'] = $_POST;
+  $form['#programmed'] = $form['#redirect'] = FALSE;
+  
+  // Sets the form_state so that the validate and submit handlers can tell
+  // when the form is submitted via AHAH
+	$form_state['ahah_submission'] = TRUE;
+
+  // Process the form with drupal_process_form. This function calls the submit 
+  // handlers, which put whatever was worthy of keeping into $form_state.
+  drupal_process_form($form_id, $form, $form_state);
+  
+  // You call drupal_rebuild_form which destroys $_POST.
+  // The form generator function is called and creates the form again but since 
+  // it knows to use $form_state, the form will be different.
+  // The new form gets cached and processed again, but because $_POST is 
+  // destroyed, the submit handlers will not be called again. 
+  $form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id);
+
+  // This is the only piece specific to your form
+  // Picks a piece of the form and renders it
+  // Specifcally the add cvterm fieldset and all contained fields
+  $add_cvterm_form = $form['add_cvterm'];
+  unset($add_cvterm_form['#prefix'], $add_cvterm_form['#suffix']);
+  $output = theme('status_messages') . drupal_render($add_cvterm_form);
+
+  // Final rendering callback.
+  drupal_json(array('status' => TRUE, 'data' => $output));
+}
+
+///////////////////////////////
+// Ontology Loader
+//////////////////////////////
+ 
+/**
+ * Purpose: Provides the form to load an already existing controlled
+ *  Vocabulary into chado
+ *
+  * @ingroup tripal_obo_loader
+ */
+function tripal_cv_obo_form(&$form_state = NULL){
+
+
+	// get a list of db from chado for user to choose
+	$sql = "SELECT * FROM {tripal_cv_obo} ORDER BY obo_id";
+	$results = db_query ($sql);
+
+
+	$obos = array();
+   $obos[] = '';
+	while ($obo = db_fetch_object($results)){
+		$obos[$obo->obo_id] = "$obo->name  | $obo->path";
+	}
+
+
+   $form['obo_existing'] = array(
+      '#type' =>'fieldset',
+      '#title' => t('Use a Saved Ontology OBO Reference')
+   );
+
+   $form['obo_new'] = array(
+      '#type' =>'fieldset',
+      '#title' => t('Use a New Ontology OBO Reference')
+   );
+
+
+   $form['obo_existing']['existing_instructions']= array(
+      '#value' => t('The Ontology OBO files listed in the drop down below have been automatically added upon 
+                     installation of the Tripal CV module or were added from a previous upload.  Select
+                     an OBO, then click the submit button to load the vocabulary into the database.  If the
+                     vocabularies already exist then the ontology will be updated.'),
+      '#weight'        => -1
+   );
+
+	$form['obo_existing']['obo_id'] = array(
+      '#title' => t('Ontology OBO File Reference'),
+      '#type' => 'select',
+      '#options' => $obos,
+      '#weight'        => 0
+	);
+
+   $form['obo_new']['path_instructions']= array(
+      '#value' => t('Provide the name and path for the OBO file.  If the vocabulary OBO file 
+                     is stored local to the server provide a file name. If the vocabulry is stored remotely, 
+                     provide a URL.  Only provide a URL or a local file, not both.'),
+      '#weight'        => 0
+   );
+
+   $form['obo_new']['obo_name']= array(
+      '#type'          => 'textfield',
+      '#title'         => t('New Vocabulary Name'),
+      '#description'   => t('Please provide a name for this vocabulary.  After upload, this name will appear in the drop down
+                             list above for use again later.'),
+      '#weight'        => 1
+   );
+
+
+
+   $form['obo_new']['obo_url']= array(
+      '#type'          => 'textfield',
+      '#title'         => t('Remote URL'),
+      '#description'   => t('Please enter a URL for the online OBO file.  The file will be downloaded and parsed.
+                             (e.g. http://www.obofoundry.org/ro/ro.obo'),
+      '#default_value' => $default_desc,
+      '#weight'        => 2
+   );
+   $form['obo_new']['obo_file']= array(
+      '#type'          => 'textfield',
+      '#title'         => t('Local File'),
+      '#description'   => t('Please enter the full system path for an OBO definition file, or a path within the Drupal
+                             installation (e.g. /sites/default/files/xyz.obo).  The path must be accessible to the
+                             server on which this Drupal instance is running.'),
+      '#default_value' => $default_desc,
+      '#weight'        => 3
+   );
+
+
+   $form['submit'] = array (
+     '#type'         => 'submit',
+     '#value'        => t('Submit'),
+     '#weight'       => 5,
+     '#executes_submit_callback' => TRUE,
+   );
+
+   $form['#redirect'] = 'admin/tripal/tripal_cv/obo';
+
+
+   return $form;
+}
+
+/**
+ * Purpose: The submit function for the load ontology form. It registers a
+ *   tripal job to run the obo_loader.php script
+ *
+   * @ingroup tripal_obo_loader
+ */
+function tripal_cv_obo_form_submit($form, &$form_state){
+   global $user;
+
+   $obo_id =  $form_state['values']['obo_id'];
+   $obo_name =  $form_state['values']['obo_name'];
+   $obo_url =  $form_state['values']['obo_url'];
+   $obo_file  = $form_state['values']['obo_file'];
+
+   $sql = "SELECT * FROM {tripal_cv_obo} WHERE obo_id = %d";
+   $obo = db_fetch_object(db_query($sql,$obo_id));
+
+   if($obo_id){ 
+      $args = array($obo_id);
+      tripal_add_job("Load OBO $obo->name",'tripal_cv',
+         "tripal_cv_load_obo_v1_2_id",$args,$user->uid);
+   } 
+   else {
+      if($obo_url){ 
+         $args = array($obo_name,$obo_url);
+         tripal_add_job("Load OBO $obo_name",'tripal_cv',
+            "tripal_cv_load_obo_v1_2_url",$args,$user->uid);
+      }
+      elseif($obo_file){
+         $args = array($obo_name,$obo_file);
+         tripal_add_job("Load OBO $obo_name",'tripal_cv',
+            "tripal_cv_load_obo_v1_2_file",$args,$user->uid);
+      } 
+   }
+
+   return '';
+}
+
+////////////////////////////////////
+// cvterm path management
+///////////////////////////////////
+
+/**
+*
+* @ingroup tripal_cv
+*/
+function tripal_cv_cvtermpath_form () {
+
+	$previous_db = tripal_db_set_active('chado');  // use chado database
+	// get a list of db from chado for user to choose
+	$sql = "SELECT * FROM {cv} WHERE NOT name = 'tripal' ORDER BY name ";
+	$results = db_query ($sql);
+	tripal_db_set_active($previous_db); // use drupal database
+
+	$cvs = array();
+   $cvs[] = '';
+	while ($cv = db_fetch_object($results)){
+		$cvs[$cv->cv_id] = $cv->name;
+	}
+
+	$form['cvid'] = array(
+      '#title' => t('Controlled Vocabulary/Ontology Name'),
+      '#type' => 'select',
+      '#options' => $cvs,
+      '#description' => t('Select a controlled vocabulary for which you would like to upate the cvtermpath.'),
+	);
+
+   $form['description'] = array(
+       '#type' => 'item',
+       '#value' => t("Submit a job to update chado cvtermpath table."),
+       '#weight' => 1,
+   );
+   $form['button'] = array(
+      '#type' => 'submit',
+      '#value' => t('Update cvtermpath'),
+      '#weight' => 2,
+   );
+   return $form;
+}
+/**
+*
+* @ingroup tripal_cv
+*/
+function tripal_cv_cvtermpath_form_validate($form, &$form_state) {
+   global $user;
+
+   $cvid =  $form_state['values']['cvid'];
+
+   // first get the controlled vocabulary name:
+   $previous_db = tripal_db_set_active('chado');
+   $cv = db_fetch_object(db_query("SELECT * FROM {cv} WHERE cv_id = %d",$cvid));
+   tripal_db_set_active($previous_db);
+
+   // Submit a job to update cvtermpath
+   $job_args = array($cvid);
+   if ($form_state['values']['op'] == t('Update cvtermpath')) {
+      tripal_add_job("Update cvtermpath: $cv->name",'tripal_cv',
+         'tripal_cv_update_cvtermpath',$job_args,$user->uid);
+   }
+}
+
+/**
+ * Update the cvtermpath table
+ *
+ * @ingroup tripal_cv
+ */
+function tripal_cv_update_cvtermpath($cvid = NULL, $job_id = NULL) {
+
+   // first get the controlled vocabulary name:
+   $previous_db = tripal_db_set_active('chado');
+   $cv = db_fetch_object(db_query("SELECT * FROM {cv} WHERE cv_id = %d",$cvid));
+   print "\nUpdating cvtermpath for $cv->name...\n";
+
+   // now fill the cvtermpath table
+   $sql = "SELECT * FROM fill_cvtermpath('%s')";
+   db_query($sql,$cv->name);         
+   tripal_db_set_active($previous_db);
+   return;
+}
+
+//////////////////////////////////////
+// @section Miscellaneous
+// @todo check to see if these functions are still needed and/or if they
+//   should be moved to the api file
+//////////////////////////////////////
+ 
+/**
+*
+* @ingroup tripal_cv
+*/
+function tripal_cv_get_cv_id($cv_name){
+
+   $sql = "
+      SELECT cv_id FROM {cv} WHERE name = '%s'
+   ";
+   $previous_db = tripal_db_set_active('chado');
+   $cv = db_fetch_object(db_query($sql,$cv_name));
+   tripal_db_set_active($previous_db);
+   return $cv->cv_id;
+}
+
+/**
+*
+* @ingroup tripal_cv
+*/
+function tripal_cv_cvterm_edit($cvterm_id){
+   $sql = "
+      SELECT CVT.name as cvtermname, CVT.definition, CV.name as cvname
+      FROM {CVTerm} CVT
+        INNER JOIN CV on CVT.cv_id = CV.cv_id
+      WHERE CVT.cvterm_id = %d
+   ";
+   $previous_db = tripal_db_set_active('chado');
+   $cvterm = db_fetch_object(db_query($sql,$cvterm_id));
+   tripal_db_set_active($previous_db);
+   return theme('tripal_cv_cvterm_edit',$cvterm);
+}
+
+/**
+*
+* @ingroup tripal_cv
+*/
+function theme_tripal_cv_cvterm_edit(&$cvterm){
+   $output = "
+      <div id=\"cvterm\">
+      <table>
+        <tr><th>Term</th><td>$cvterm->cvtermname</td></tr>
+        <tr><th>Vocabulary</th><td>$cvterm->cvname</td></tr>
+        <tr><th>Definition</th><td>$cvterm->definition</td></tr>
+      </table>
+      </div>
+   ";
+   return $output;
+}

+ 371 - 0
modules/base/tripal_cv/tripal_cv.views.inc

@@ -0,0 +1,371 @@
+<?php
+
+/**
+ *  @file
+ *  This file contains the basic functions for views integration of
+ *  chado/tripal db tables. Supplementary functions can be found in
+ *  ./views/
+ *
+ *  Documentation on views integration can be found at 
+ *  http://views2.logrus.com/doc/html/index.html.
+ */
+
+/**
+ * @defgroup tripal_cv_views Controlled Vocabulary Views Integration
+ * @ingroup views
+ * @ingroup tripal_cv
+ */
+
+require_once('views/cvterm.views.inc');
+require_once('views/cv.views.inc'); 
+/**
+ * Implements hook_views_data()
+ *
+ * Purpose: Describe chado/tripal tables & fields to views
+ *
+ * @return
+ *   a data array which follows the structure outlined in the
+ *   views2 documentation for this hook. Essentially, it's an array of table
+ *   definitions keyed by chado/tripal table name. Each table definition 
+ *   includes basic details about the table, fields in that table and
+ *   relationships between that table and others (joins)
+ *
+ * @ingroup tripal_cv_views
+ */
+function tripal_cv_views_data() {
+  $data = array();
+  
+  //Function is contained in includes/cvterm.views.inc
+  //Returns the data array for the chado cvterm table
+  $data = array_merge($data, retrieve_cvterm_views_data());
+  
+  //Function is contained in includes/cv.views.inc
+  //Returns the data array for the chado cv table
+  $data = array_merge($data, retrieve_cv_views_data());
+  
+  return $data;
+}
+ 
+/**
+ * Implements hook_views_handlers()
+ *
+ * Purpose: Register all custom handlers with views
+ *   where a handler describes either "the type of field", 
+ *   "how a field should be filtered", "how a field should be sorted"
+ *
+ * @return: An array of handler definitions
+ *
+ * @ingroup tripal_cv_views
+ */
+function tripal_cv_views_handlers() {
+ return array(
+   'info' => array(
+     'path' => drupal_get_path('module', 'tripal_cv') . '/views/handlers',
+   ),
+   'handlers' => array(
+     'views_handler_field_tf_boolean' => array(
+       'parent' => 'views_handler_field',
+     ),
+   ),
+ );
+}
+
+/**
+ *
+ * @ingroup tripal_cv_views
+ */
+function tripal_cv_views_default_views () {
+  $views = array();
+  
+  // Main default view
+  // List all cvterms based on cv
+  $view = new view;
+  $view->name = 'all_cvterms';
+  $view->description = 'A listing of all controlled vocabulary terms filtered by controlled vocabulary';
+  $view->tag = 'chado';
+  $view->view_php = '';
+  $view->base_table = 'cvterm';
+  $view->is_cacheable = FALSE;
+  $view->api_version = 2;
+  $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
+  $handler = $view->new_display('default', 'Defaults', 'default');
+  $handler->override_option('fields', array(
+    'name' => array(
+      'label' => 'Name',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'name',
+      'table' => 'cvterm',
+      'field' => 'name',
+      'relationship' => 'none',
+    ),
+    'definition' => array(
+      'label' => 'Definition',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'definition',
+      'table' => 'cvterm',
+      'field' => 'definition',
+      'relationship' => 'none',
+    ),
+    'is_obsolete' => array(
+      'label' => 'Is Obsolete',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'type' => 'yes-no',
+      'not' => 0,
+      'exclude' => 0,
+      'id' => 'is_obsolete',
+      'table' => 'cvterm',
+      'field' => 'is_obsolete',
+      'relationship' => 'none',
+    ),
+    'is_relationshiptype' => array(
+      'label' => 'Is Relationship',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'type' => 'yes-no',
+      'not' => 0,
+      'exclude' => 0,
+      'id' => 'is_relationshiptype',
+      'table' => 'cvterm',
+      'field' => 'is_relationshiptype',
+      'relationship' => 'none',
+    ),
+    'accession_link' => array(
+      'label' => 'Accession Link',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'accession_link',
+      'table' => 'dbxref',
+      'field' => 'accession_link',
+      'relationship' => 'none',
+    ),
+  ));
+  $handler->override_option('filters', array(
+    'name' => array(
+      'operator' => '=',
+      'value' => '<select cv>',
+      'group' => '0',
+      'exposed' => TRUE,
+      'expose' => array(
+        'use_operator' => 0,
+        'operator' => 'name_op',
+        'identifier' => 'cv',
+        'label' => 'Vocabulary',
+        'optional' => 0,
+        'remember' => 0,
+      ),
+      'case' => 1,
+      'id' => 'name',
+      'table' => 'cv',
+      'field' => 'name',
+      'relationship' => 'none',
+    ),
+    'name_1' => array(
+      'operator' => '~',
+      'value' => '',
+      'group' => '0',
+      'exposed' => TRUE,
+      'expose' => array(
+        'use_operator' => 0,
+        'operator' => '',
+        'identifier' => 'name',
+        'label' => 'Name Contains',
+        'optional' => 1,
+        'remember' => 0,
+      ),
+      'case' => 0,
+      'id' => 'name_1',
+      'table' => 'cvterm',
+      'field' => 'name',
+      'relationship' => 'none',
+      'values_form_type' => 'textfield',
+    ),
+    'definition' => array(
+      'operator' => 'contains',
+      'value' => '',
+      'group' => '0',
+      'exposed' => TRUE,
+      'expose' => array(
+        'use_operator' => 0,
+        'operator' => 'definition_op',
+        'identifier' => 'definition',
+        'label' => 'Definition Contains',
+        'optional' => 1,
+        'remember' => 0,
+      ),
+      'case' => 0,
+      'id' => 'definition',
+      'table' => 'cvterm',
+      'field' => 'definition',
+      'relationship' => 'none',
+    ),
+  ));
+  $handler->override_option('access', array(
+    'type' => 'perm',
+    'perm' => 'access chado_cv content',
+  ));
+  $handler->override_option('cache', array(
+    'type' => 'none',
+  ));
+  $handler->override_option('title', 'Controlled Vocabulary Terms');
+  $handler->override_option('empty', 'There are no terms associated with the selected controlled vocabulary. Please select a different vocabulary from the list above.');
+  $handler->override_option('empty_format', '1');
+  $handler->override_option('items_per_page', 50);
+  $handler->override_option('use_pager', '1');
+  $handler->override_option('style_plugin', 'table');
+  $handler->override_option('style_options', array(
+    'grouping' => '',
+    'override' => 1,
+    'sticky' => 0,
+    'order' => 'asc',
+    'columns' => array(
+      'definition' => 'definition',
+      'is_obsolete' => 'is_obsolete',
+      'is_relationshiptype' => 'is_relationshiptype',
+      'name' => 'name',
+      'accession_link' => 'accession_link',
+    ),
+    'info' => array(
+      'definition' => array(
+        'sortable' => 0,
+        'separator' => '',
+      ),
+      'is_obsolete' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'is_relationshiptype' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'name' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'accession_link' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+    ),
+    'default' => '-1',
+  ));
+  $handler = $view->new_display('page', 'Page', 'page_1');
+  $handler->override_option('path', 'admin/tripal/tripal_cv/list_cvterms');
+  $handler->override_option('menu', array(
+    'type' => 'normal',
+    'title' => 'Term Listing',
+    'description' => 'A listing of a controlled vocabulary terms for a given vocabulary',
+    'weight' => '0',
+    'name' => 'navigation',
+  ));
+  $handler->override_option('tab_options', array(
+    'type' => 'none',
+    'title' => '',
+    'description' => '',
+    'weight' => 0,
+    'name' => 'navigation',
+  ));
+  $views[$view->name] = $view;
+  
+  return $views;
+}

+ 45 - 0
modules/base/tripal_cv/views/README

@@ -0,0 +1,45 @@
+
+This directory contains all additional views code needed to integrate this module with views2.
+
+FILE/FOLDER DESCRIPTIONS
+---------------------------
+<chado table name>.views.inc:
+	contains a single function retrieve_<chado table name>_views_data()
+	which describes that table to views. This function is called by
+	tripal_cv_views_data() in ../tripal_cv.views.inc.
+	For more information on the form of this data array look up the
+	views2 documentation for hook_views_data() 
+	-http://views2.logrus.com/doc/html/index.html
+	
+handlers/
+	Each file contained within this folder defines a views handler. Only custom
+	handlers are included in this folder and each must be described in 
+	hook_views_handlers() in ../tripal_cv.views.inc.
+	A views handler does one of the following:
+		1) describe the type of a field and how it should be displayed
+		2) describe a method to sort this field
+		3) describe a method to filter this field
+
+
+STANDARDS TO FOLLOW
+---------------------------
+
+1. All table definition files should be named tablename.views.inc
+2. All handlers should be in a handlers sub-directory and follow the naming convention of
+   views handlers (ie: views_handler_field/filter/sort_handlername.inc )
+
+Views Table Definitions:
+- Please use the template files provided whenever you are describing a new table to views.
+   For any table in chado simply copy template.table_defn.views.inc to tablename.views.inc and 
+   follow the instructions listed at the top of the template file.
+- ONLY ONE TABLE DEFINITION PER FILE
+- To join a chado table to it's drupal node simply copy template.node_join.views.inc to 
+   basetablename.views.inc and replace all XXX with basetablename.
+   
+   NOTE: Creating the table definition file is not enough. You also need to call the 
+         retrieve_XXX_views_data() function from ../tripal_cv.views.inc:tripal_cv_views_data()
+         by adding the following line:
+            $data = array_merge($data, retrieve_XXX_views_data());
+         to the function and including the file directly above the function (blow the function 
+         header by adding:
+            require_once('views/XXX.views.inc');

+ 93 - 0
modules/base/tripal_cv/views/cv.views.inc

@@ -0,0 +1,93 @@
+<?php
+
+/**
+ * Prupose: this function returns a portion of the data array 
+ *   which describes the cv table, it's fields and any joins between it and other tables
+ * @see tripal_cv_views_data() --in tripal_cv.views.inc
+ *
+ * TABLE: cv
+ * @code
+ *   create table cv (
+ *       cv_id serial not null,
+ *       primary key (cv_id),
+ *       name varchar(255) not null,
+ *       definition text,
+ *       constraint cv_c1 unique (name)
+ *   );
+ * @endcode
+ *
+ * @ingroup tripal_cv_views
+ */
+function retrieve_cv_views_data() {
+  $data = array();
+
+  // Basic table definition
+  $data['cv']['table'] = array(
+    'field' => 'cv_id',
+    'title' => 'Chado CV (Controlled Vocabulary)',
+    'group' => 'Chado CV',
+    'help' => 'Controlled vocabularies existing in the Chado Database',
+    'database' => 'chado'
+  );
+
+  // Define relationships between this table and others
+  $data['cv']['table']['join'] = array(
+     'cvterm' => array(
+       'left_field' => 'cv_id',
+       'field' => 'cv_id',
+     ),
+  );
+
+  // Table Field Definitions----------------------
+  // Field: cv_id (primary key)
+  $data['cv']['cv_id'] = array(
+    'title' => t('CV ID'),
+    'help' => t('The primary key of the controlled vocabulary.'),
+    'field' => array(
+      'handler' => 'views_handler_field_numeric',
+      'click sortable' => TRUE,
+     ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_numeric',
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+  ); 
+  
+  //Field: name (varchar -255)
+  $data['cv']['name'] = array(
+    'title' => 'Vocabulary Name',
+    'field' => array(
+       'handler' => 'views_handler_field',
+       'click sortable' => TRUE,
+     ),
+     'sort' => array(
+       'handler' => 'views_handler_sort',
+     ),
+     'filter' => array(
+       'handler' => 'views_handler_filter_chado_select_string',
+     ),
+     'argument' => array(
+       'handler' => 'views_handler_argument_string',
+     ),
+  );
+
+  //Field: definition (text)
+  $data['cv']['definition'] = array(
+    'title' => 'Vocabulary Definition',
+    'field' => array(
+       'handler' => 'views_handler_field',
+       'click sortable' => TRUE,
+     ),
+     'filter' => array(
+       'handler' => 'views_handler_filter_string',
+     ),
+     'argument' => array(
+       'handler' => 'views_handler_argument_string',
+     ),
+  );
+
+  return $data;
+
+}

+ 155 - 0
modules/base/tripal_cv/views/cvterm.views.inc

@@ -0,0 +1,155 @@
+<?php
+
+/**
+ * Purpose: this function returns the portion of the data array 
+ *   which describes the cv table, it's fields and any joins between it and other tables
+ * @see tripal_cv_views_data() --in tripal_cv.views.inc
+ *
+ * BASE TABLE: cvterm
+ * @code
+ *   create table cvterm (
+ *       cvterm_id serial not null,
+ *       primary key (cvterm_id),
+ *       cv_id int not null,
+ *       foreign key (cv_id) references cv (cv_id) on delete cascade INITIALLY DEFERRED,
+ *       name varchar(1024) not null,
+ *       definition text,
+ *       dbxref_id int not null,
+ *       foreign key (dbxref_id) references dbxref (dbxref_id) on delete set null INITIALLY DEFERRED,
+ *       is_obsolete int not null default 0,
+ *       is_relationshiptype int not null default 0,
+ *       constraint cvterm_c1 unique (name,cv_id,is_obsolete),
+ *       constraint cvterm_c2 unique (dbxref_id)
+ *   );
+ * @endcode
+ *
+ * @ingroup tripal_cv_views
+ */
+function retrieve_cvterm_views_data() {
+
+  // Basic table definition
+  $data['cvterm']['table']['group'] = 'Chado CV Terms';
+  $data['cvterm']['table']['base'] = array(
+    'field' => 'cvterm_id',
+    'title' => 'Chado CV Terms (Controlled Vocabulary)',
+    'help' => 'Controlled Vocabularies (CVs) are the main way Chado controls content.',
+    'database' => 'chado'
+  );
+
+  // Define relationships between this table and others
+   $data['cvterm']['table']['join'] = array(
+     'feature' => array(
+       'left_field' => 'type_id',
+       'field' => 'cvterm_id',
+     ),
+     'library' => array(
+       'left_field' => 'type_id',
+       'field' => 'cvterm_id',
+     ),
+     'stock' => array(
+       'left_field' => 'type_id',
+       'field' => 'cvterm_id',
+     ),
+     'nd_reagent' => array(
+       'left_field' => 'type_id',
+       'field' => 'cvterm_id',
+     ),
+   );
+
+ // Table Field Definitions----------------------
+ // Field: cvterm_id (primary key)
+ $data['cvterm']['cvterm_id'] = array(
+   'title' => t('CV Term ID'),
+   'help' => t('The primary kep of controlled vocabulary terms.'),
+   'field' => array(
+     'handler' => 'views_handler_field_numeric',
+     'click sortable' => TRUE,
+    ),
+   'filter' => array(
+     'handler' => 'views_handler_filter_numeric',
+   ),
+   'sort' => array(
+     'handler' => 'views_handler_sort',
+   ),
+ );
+ 
+ //Field: cv_id (foreign key: cv)
+ //  join between cv table and this one in cv.views.inc
+ 
+ // Field: Name (varchar 1024)
+  $data['cvterm']['name'] = array(
+     'title' => 'Name',
+     'help' => 'The term name',
+     'field' => array(
+     'handler' => 'views_handler_field',
+       'click sortable' => TRUE,
+     ),
+     'sort' => array(
+       'handler' => 'views_handler_sort',
+     ),
+     'filter' => array(
+       'handler' => 'views_handler_filter_chado_select_cvterm_name',
+     ),
+     'argument' => array(
+       'handler' => 'views_handler_argument_string',
+     ),
+   );
+
+   // Field: Definition (text)
+   $data['cvterm']['definition'] = array(
+     'title' => 'Definition',
+     'help' => 'A definition of this term',
+     'field' => array(
+       'handler' => 'views_handler_field',
+       'click sortable' => TRUE,
+     ),
+     'filter' => array(
+       'handler' => 'views_handler_filter_string',
+     ),
+     'argument' => array(
+       'handler' => 'views_handler_argument_string',
+     ),
+   );
+
+   // Field: dbxref_id (foreign key: dbxref)
+   //  join between dbxref table and this one in tripal_db/views/dbxref.views.inc
+   
+   // Field: is_obsolete (integer: 1/0)
+   $data['cvterm']['is_obsolete'] = array(
+     'title' => 'Is Obsolete',
+     'help' => 'Whether this term is obsolete or not.',
+     'field' => array(
+       'handler' => 'views_handler_field_boolean',
+       'click sortable' => TRUE,
+     ),
+     'filter' => array(
+       'handler' => 'views_handler_filter_chado_boolean',
+       'label' => t('Is Obsolete?'),
+       'type' => 'yes-no',
+     ),
+     'sort' => array(
+       'handler' => 'views_handler_sort',
+     ),
+   );
+
+   // Field: is_relationshiptype (integer: 1/0)
+   $data['cvterm']['is_relationshiptype'] = array(
+     'title' => 'Is Relationship',
+     'help' => 'Whether this term describes a relationship or not.',
+     'field' => array(
+       'handler' => 'views_handler_field_boolean',
+       'click sortable' => TRUE,
+     ),
+     'filter' => array(
+       'handler' => 'views_handler_filter_chado_boolean',
+       'label' => t('Is Relationship Type?'),
+       'type' => 'yes-no',
+     ),
+     'sort' => array(
+       'handler' => 'views_handler_sort',
+     ),
+    );
+  
+  return $data;
+
+}

+ 84 - 0
modules/base/tripal_cv/views/template.node_join.views.inc

@@ -0,0 +1,84 @@
+<?php
+
+/*************************************************************************
+ * @file: THIS IS A TEMPLATE AND SHOULD NOT BE INCLUDED IN THE MODULE CODE
+ *
+ *  - simply replace all XXX with the original chado table you want to join to it's drupal nodes.
+ *    (ie: If you want to join features to their drupal nodes then XXX=feature)
+ * 
+ *  NOTE: Creating the table definition file is not enough. You also need to call the 
+ *        retrieve_XXX_views_data() function from ../tripal_cv.views.inc:tripal_cv_views_data()
+ *        by adding the following line:
+ *           $data = array_merge($data, retrieve_XXX_views_data());
+ *        to the function and including the file directly above the function (blow the function 
+ *        header by adding:
+ *           require_once('views/XXX.views.inc');
+ *
+ *  REMOVE THIS COMMENT IN THE COPY!
+ */ 
+ 
+/**
+ *  @file
+ *  This file defines the data array for a given chado table. This array
+ *  is merged into a larger array containing definitions of all tables associated
+ *  with this module in:
+ *  @see tripal_cv.views.inc --in tripal_cv_views_data()
+ *
+ *  Note: All chado tables are joined to their drupal nodes through the chado_XXX linking table. 
+ *        This file simply defines this linking table and joins the three tables together.
+ *        No modification of XXX.views.inc is needed.
+ *
+ *  Documentation on views integration can be found at 
+ *  http://views2.logrus.com/doc/html/index.html.
+ */
+ 
+/**
+ * Purpose: this function returns the portion of the data array 
+ *   which describes the chado_XXX drupal table, it's fields and any joins between it 
+ *   and other tables
+ * @see tripal_cv_views_data() --in tripal_cv.views.inc
+ *
+ * The main need for description of this table to views is to join chado data with drupal nodes
+ *
+ */
+function retrieve_chado_XXX_views_data () {
+	global $db_url;
+  $data = array();
+  
+  // if the chado database is not local to the drupal database
+  // then we need to set the database name.  This should always
+  // be 'chado'.
+  if(is_array($db_url) and array_key_exists('chado',$db_url)){
+     // return empty data array b/c if chado is external then no join to the nodetable can be made
+     return $data;
+  }
+
+  //Basic table definition-----------------------------------
+  $data['chado_XXX']['table'] = array(
+    'field' => 'nid',
+  );
+  
+  //Relationship Definitions---------------------------------
+  // Note: No joins need to be made from $data['XXX']['table']
+  
+  // Join the chado_XXX table to XXX
+  $data['chado_XXX']['table']['join']['XXX'] = array(
+  	'left_field' => 'XXX_id',
+  	'field' => 'XXX_id',
+  );
+  
+  // Join the node table to chado_XXX
+  $data['node']['table']['join']['chado_XXX'] = array(
+  	'left_field' => 'nid',
+  	'field' => 'nid',
+  );
+  
+  // Join the node table to XXX
+  $data['node']['table']['join']['XXX'] = array(
+  	'left_table' => 'chado_XXX',
+  	'left_field' => 'nid',
+  	'field' => 'nid',
+  );  
+
+	return $data;
+}

+ 211 - 0
modules/base/tripal_cv/views/template.table_defn.views.inc

@@ -0,0 +1,211 @@
+<?php
+
+/*************************************************************************
+ * @file: THIS IS A TEMPLATE AND SHOULD NOT BE INCLUDED IN THE MODULE CODE
+ *
+ *   - Every instance of XXX should be replaced with the name of your table
+ *   - If this is a base table (you want a view where every row is a row from this table)
+ *     then change $data['XXX']['table'] to $data['XXX']['table']['base'] 
+ *     and $data['XXX']['table']['database'] to $data['XXX']['table']['base']['database']
+ *   - Relationships between this table and others: YYY is the table you are trying to join to this
+ *     one. You want to join a table to this one if this table contains a foreign key to the other
+ *     table. If the join between this table and another is through a linking table
+ *     (ie: library-XXX/YYY => library_feature-XY => feature-XXX/YYY) then make the join in both
+ *     directions (ie: in the file XXX.views.inc and the file YYY.views.inc
+ *   - Create a field definition for each field in this table using the example fields already
+ *     listed. Match the type of the database field to the field definition listed below.
+ *     (ie: for a text/varchar field from the database use plain_text_field below)
+ * 
+ *  NOTE: Creating the table definition file is not enough. You also need to call the 
+ *        retrieve_XXX_views_data() function from ../tripal_cv.views.inc:tripal_cv_views_data()
+ *        by adding the following line:
+ *           $data = array_merge($data, retrieve_XXX_views_data());
+ *        to the function and including the file directly above the function (blow the function 
+ *        header by adding:
+ *           require_once('views/XXX.views.inc');
+ *
+ *  REMOVE THIS COMMENT IN THE COPY!
+ */ 
+ 
+/**
+ *  @file
+ *  This file defines the data array for a given chado table. This array
+ *  is merged into a larger array containing definitions of all tables associated
+ *  with this module in:
+ *  @see tripal_cv.views.inc --in tripal_cv_views_data()
+ *
+ *  Documentation on views integration can be found at 
+ *  http://views2.logrus.com/doc/html/index.html.
+ */
+
+/*************************************************************************
+ * Purpose: this function returns the portion of the data array 
+ *   which describes the XXX table, it's fields and any joins between it and other tables
+ * @see tripal_cv_views_data() --in tripal_cv.views.inc
+ *
+ * Table: XXX
+ * @code
+ * XXX-Copy/Paste Table SQL code here-XXX
+ * @endcode
+ */
+ function retrieve_XXX_views_data() {
+  global $db_url;
+  $data = array();
+
+  // if the chado database is not local to the drupal database
+  // then we need to set the database name.  This should always
+  // be 'chado'.
+  if(is_array($db_url) and array_key_exists('chado',$db_url)){
+    $database = 'chado';
+  }
+   
+  //Basic table definition-----------------------------------
+  $data['XXX']['table']['group'] = t('Chado XXX');
+  
+  $data['XXX']['table'] = array(
+    'field' => 'primary_id',
+    'title' => t('Chado XXX'),
+    'help' => t('Enter some user-friendly description of this tables purpose to the user.'),
+  );
+  if($database){
+     $data['XXX']['table']['database'] = $database;
+  }
+
+  
+  //Relationship Definitions---------------------------------
+  //Join: YYY => XXX
+  // Notice that this relationship tells the primary table to show it's fields to the
+  // table referencing it by a foreign key and thus the relationship is from
+  // primary table to table referenceing it (ie: cvterm => feature)
+  $data['XXX']['table']['join']['YYY'] = array(
+    'left_field' => 'foreign key in YYY table',
+    'field' => 'primary key in XXX table',
+  );  
+  
+  //Join: XXX => XY => YYY
+  // This relationship should be described in both directions
+  // in the appropriate files (ie: for feature => library 
+  // describe in both feature.views.inc and library.views.inc)
+  $data['XXX']['table']['join']['XY'] = array(
+    'left_field' => 'matching XXX key in the XY table',
+    'field' => 'primary key in XXX table',
+  );  
+  $data['XXX']['table']['join']['YYY'] = array(
+    'left_table' => 'XY',
+    'left_field' => 'matching XXX key in the XY table',
+    'field' => 'primary key in XXX table',
+  );
+  $data['XY']['table']['join']['YYY'] = array(
+    'left_field' => 'primary key in YYY table',
+    'field' => 'matching YYY key in the XY table',
+  );
+   
+  //Table Field Definitions----------------------------------
+      
+  //Field: XXX_id (primary key)
+  $data['XXX']['field_name'] = array(
+    'title' => t('XXX Primary Key'),
+    'help' => t('A unique index for every XXX.'),
+    'field' => array(
+      'handler' => 'views_handler_field_numeric',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_numeric',
+    ),
+  );
+
+  /*.......................................................
+   * Beginning of Example Field definitions
+   * Remove this section when done
+   */
+
+  //Field: plain_text_field (chado datatype)   
+  $data['XXX']['plain_text_field'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+
+  //Field: numeric_field (chado datatype)   
+  $data['XXX']['numeric_field'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_numeric',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_numeric',
+    ),
+  );
+
+  //Field: boolean_field (chado datatype)   
+  $data['XXX']['boolean_field'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_boolean',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_boolean_operator',
+    ),
+  );
+
+  //Field: unix_timestamp (chado datatype)   
+  $data['XXX']['unix_timestamp'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_date',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort_date',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_date',
+    ),
+  );
+
+  //Field: human_readable_date (chado datatype)   
+  $data['XXX']['human_readable_date'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_readble_date',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort_date',
+    ),
+  );
+   
+   /*
+    * End of Example Field definitions
+    */
+    
+  return $data;
+}

+ 333 - 0
modules/base/tripal_db/tripal_db.api.inc

@@ -0,0 +1,333 @@
+<?php
+
+/**
+ * @defgroup tripal_db_api DB Module API
+ * @ingroup tripal_api
+ * @ingroup tripal_db
+ * Provides an application programming interface (API) to manage references to external databases
+ */
+
+/**
+ * Purpose: To retrieve a chado database object
+ *
+ * @param $select_values
+ *   An array meant to uniquely select a given database
+ *
+ * @return
+ *   Chado database object
+ *
+ * The database is selected using tripal_core_chado select and as such the
+ * $select_values array parameter meant to uniquely identify the database to be 
+ * returned follows the same form as when using tripal_core_chado_select directly.
+ *
+ * Example Usage:
+ * @code
+    $select_values = array(
+      'name' => 'SOFP'
+    );
+    $db_object = tripal_db_get_db($select_values);
+ * @endcode
+ *  The above code selects the SOFP db and returns the following object:
+ * @code
+    $db_object = stdClass Object ( 
+      [db_id] => 49
+      [name] => SOFP
+      [description] => 
+      [urlprefix] => 
+      [url] =>     
+    ); 
+ * @endcode
+ *
+ * @ingroup tripal_db_api
+ */
+function tripal_db_get_db ($select_values) {
+
+  $columns = array(
+    'db_id', 
+    'name', 
+    'description',
+    'urlprefix',
+    'url'
+  );
+  $results = tripal_core_chado_select('db', $columns, $select_values);
+  if (sizeof($results) == 1) {
+    return $results[0];
+  } elseif (empty($results)) {
+    watchdog('tripal_cdb', 
+      'tripal_db_get_db: No db matches criteria values:%values',
+      array('%values' => print_r($select_values, TRUE)),
+      WATCHDOG_WARNING
+    );
+    return FALSE;
+  } else {
+    watchdog('tripal_db', 
+      'tripal_db_get_db: 2+ dbs match criteria values:%values',
+      array('%values' => print_r($select_values, TRUE)),
+      WATCHDOG_WARNING
+    );
+  }
+  
+}
+
+/**
+ * Purpose: To retrieve a chado db object
+ *
+ * @param $db_id
+ *   db.db_id
+ * @return 
+ *   Chado db object with all fields from the chado db table
+ *
+ * @ingroup tripal_db_api
+ */
+function tripal_db_get_db_by_db_id ($db_id) {
+
+	$previous_db = tripal_db_set_active('chado');
+  $r = db_fetch_object(db_query(
+    "SELECT * FROM db WHERE db_id=%d", $db_id
+  ));
+  tripal_db_set_active($previous_db);
+
+  return $r;
+}
+
+/**
+ * Purpose: To retrieve a chado db object
+ *
+ * @param $name
+ *   db.name
+ * @return 
+ *   chado db object with all fields from the chado db table
+ *
+ * @ingroup tripal_db_api
+ */
+function tripal_db_get_db_by_name ($name) {
+
+	$previous_db = tripal_db_set_active('chado');
+  $r = db_fetch_object(db_query(
+    "SELECT * FROM db WHERE name='%s'", $name
+  ));
+  tripal_db_set_active($previous_db);
+
+  return $r;
+}
+
+// Purpose: To retrieve a chado db object
+//
+// @params where_options: array(
+//													<column_name> => array(
+//														'type' => <type of column: INT/**STRING>,
+//														'value' => <the vlaue you want to filter on>,
+//														'exact' => <if TRUE use =; if FALSE use ~>,
+//													)
+//				)
+// @return chado db object with all fields from the chado db table
+//
+//function tripal_db_get_db ($where_options) {
+
+/**
+ * Purpose: Create an options array to be used in a form element
+ *   which provides a list of all chado dbs
+ *
+ * @return 
+ *   An array(db_id => name) for each db in the chado db table
+ *
+ * @ingroup tripal_db_api
+ */
+function tripal_db_get_db_options() {
+
+  $previous_db = tripal_db_set_active('chado');
+  $result = db_query(
+    "SELECT db_id, name FROM db"
+  );
+  tripal_db_set_active($previous_db);
+
+  $options = array();
+  while ( $r = db_fetch_object($result) ) {
+    $options[$r->db_id] = $r->name;
+  }
+
+  return $options;
+
+}
+
+// Purpose: To retrieve a chado dbxref object
+//
+// @param where_options: array(
+//													<column_name> => array(
+//														'type' => <type of column: INT/**STRING>,
+//														'value' => <the vlaue you want to filter on>,
+//														'exact' => <if TRUE use =; if FALSE use ~>,
+//													)
+//				)
+// @return chado dbxref object with all fields from the chado dbxref table
+//
+//function tripal_db_get_dbxref ($where_options) {
+
+/**
+ * Purpose: To retrieve a chado database reference object
+ *
+ * @param $select_values
+ *   An array meant to uniquely select a given database reference
+ *
+ * @return
+ *   Chado database reference object
+ *
+ * The database reference is selected using tripal_core_chado select and as such the
+ * $select_values array parameter meant to uniquely identify the database reference to be 
+ * returned follows the same form as when using tripal_core_chado_select directly.
+ *
+ * Example Usage:
+ * @code
+    $select_values = array(
+      'accession' => 'synonym',
+      'db_id' => array(
+        'name' => 'SOFP'
+      )
+    );
+    $dbxref_object = tripal_db_get_dbxref($select_values);
+ * @endcode
+ *  The above code selects the synonym database reference and returns the following object:
+ * @code
+    $dbxref_object = stdClass Object ( 
+      [dbxref_id] => 2581
+      [accession] => synonym
+      [description] => 
+      [version] => 
+      [db_db_id] => 49
+      [db_name] => SOFP
+      [db_description] => 
+      [db_urlprefix] => 
+      [db_url] =>     
+    ); 
+ * @endcode
+ *
+ * @ingroup tripal_db_api
+ */
+function tripal_db_get_dbxref ($select_values) {
+
+  $columns = array(
+    'dbxref_id',
+    'db_id', 
+    'accession', 
+    'description',
+    'version'
+  );
+  $results = tripal_core_chado_select('dbxref', $columns, $select_values);
+  if (sizeof($results) == 1) {
+    $dbxref = tripal_db_add_db_to_object(array('db_id' => $results[0]->db_id), $results[0], array());
+    unset($dbxref->db_id);
+
+    return $dbxref;
+  } elseif (empty($results)) {
+    watchdog('tripal_db', 
+      'tripal_db_get_dbxref: No dbxref matches criteria values:%values',
+      array('%values' => print_r($select_values, TRUE)),
+      WATCHDOG_WARNING
+    );
+    return FALSE;
+  } else {
+    watchdog('tripal_db', 
+      'tripal_db_get_dbxref: 2+ dbxrefs match criteria values:%values',
+      array('%values' => print_r($select_values, TRUE)),
+      WATCHDOG_WARNING
+    );
+  }
+
+}
+
+/**
+ * Purpose: To retrieve a chado dbxref object with a given accession
+ *
+ * @param $accession
+ *   dbxref.accession
+ * @param $db_id
+ *   dbxref.db_id
+ * @return 
+ *   chado dbxref object with all fields from the chado dbxref table
+ *
+ * @ingroup tripal_db_api
+ */
+function tripal_db_get_dbxref_by_accession ($accession, $db_id=0) {
+
+  if (!empty($db_id)) {
+	  $previous_db = tripal_db_set_active('chado');
+    $r = db_fetch_object(db_query(
+      "SELECT * FROM dbxref WHERE accession='%s' AND db_id=%d",
+      $accession, $db_id
+    ));
+    tripal_db_set_active($previous_db);
+  } else {
+	  $previous_db = tripal_db_set_active('chado');
+    $r = db_fetch_object(db_query(
+      "SELECT * FROM dbxref WHERE accession='%s'",
+      $accession
+    ));
+    tripal_db_set_active($previous_db);  
+  }
+  
+  return $r;
+}
+
+/**
+ * Implements hook_chado_dbxref_schema()
+ * Purpose: To add descriptions and foreign keys to default table description
+ * Note: This array will be merged with the array from all other implementations
+ *
+ * @return
+ *    Array describing the dbxref table
+ *
+ * @ingroup tripal_schema_api
+ */
+function tripal_db_chado_dbxref_schema() {
+  $description = array();
+
+  $description['foreign keys']['db'] = array(
+        'table' => 'db',
+        'columns' => array(
+          'db_id' => 'db_id',
+        ),
+  ); 
+
+  return $description;
+}
+/**
+*
+* @ingroup tripal_db_api
+*/
+function tripal_db_add_db($dbname){
+
+   $db_sql = "SELECT * FROM {db} WHERE name ='%s'";
+   $db = db_fetch_object(db_query($db_sql,$dbname));
+   if(!$db){
+      if(!db_query("INSERT INTO {db} (name) VALUES ('%s')",$dbname)){
+         watchdog('tripal_cv', "Cannot create '$dbname' db in Chado.",NULL,WATCHDOG_WARNING);
+         return 0;
+      }      
+     $db = db_fetch_object(db_query($db_sql,$dbname));
+   }
+   return $db;
+}
+/**
+*
+* @ingroup tripal_db_api
+*/
+function tripal_db_add_dbxref($db_id,$accession,$version='',$description=''){
+
+   // check to see if the dbxref exists if not, add it
+   $dbxsql = "SELECT dbxref_id FROM {dbxref} WHERE db_id = %d and accession = '%s'";
+   $dbxref = db_fetch_object(db_query($dbxsql,$db_id,$accession));
+   if(!$dbxref){
+      $sql = "
+         INSERT INTO {dbxref} (db_id, accession, version, description)
+         VALUES (%d,'%s','%s','%s')
+      ";
+      if(!db_query($sql,$db_id,$accession,$version,$description)){
+         watchdog('tripal_cv', "Failed to insert the dbxref record $accession",NULL,WATCHDOG_WARNING);
+         return 0;
+      }
+      print "Added Dbxref accession: $accession\n";
+      $dbxref = db_fetch_object(db_query($dbxsql,$db_id,$accession));
+   }
+   return $dbxref;
+
+}

+ 9 - 0
modules/base/tripal_db/tripal_db.info

@@ -0,0 +1,9 @@
+; $Id: tripal_db.info,v 1.2 2009/12/05 06:11:56 ficklin Exp $
+name = Tripal DB
+description = The database module for the Tripal package that integrates Drupal and GMOD chado. This module provides support for managing and viewing external databases.
+core = 6.x
+project = tripal_db
+package = Tripal
+version = 6.x-0.3.1b
+
+dependencies[] = tripal_core

+ 42 - 0
modules/base/tripal_db/tripal_db.install

@@ -0,0 +1,42 @@
+<?php
+
+/**
+*  Implementation of hook_install();
+*
+* @ingroup tripal_db
+*/
+function tripal_db_install(){
+
+   // create the module's data directory
+   tripal_create_moddir('tripal_db');
+
+}
+
+/**
+* Implementation of hook_uninstall()
+*
+* @ingroup tripal_db
+*/
+function tripal_db_uninstall(){
+
+}
+
+/**
+ * Implementation of hook_requirements(). Make sure 'Tripal Core' is enabled
+ * before installation
+ *
+ * @ingroup tripal_db
+ */
+function tripal_db_requirements($phase) {
+   $requirements = array();
+   if ($phase == 'install') {
+      if (!function_exists('tripal_create_moddir')) {
+         $requirements ['tripal_db'] = array(
+            'title' => "tripal_db",
+            'value' => "Required modules must be installed first before Tripal DB module can be installed. Please try again.",
+            'severity' => REQUIREMENT_ERROR,
+         );
+      }
+   }
+   return $requirements;
+}

+ 362 - 0
modules/base/tripal_db/tripal_db.module

@@ -0,0 +1,362 @@
+<?php
+
+require_once "tripal_db.api.inc";
+
+/**
+ * @defgroup tripal_db DB Module
+ * @ingroup tripal_modules
+ */
+ 
+/**
+*
+* @ingroup tripal_db
+*/
+function tripal_db_init(){
+
+   // add the tripal_db JS and CSS
+   drupal_add_css(drupal_get_path('theme', 'tripal').'/css/tripal_db.css');
+   drupal_add_js(drupal_get_path('theme', 'tripal').'/js/tripal_db.js');
+}
+/**
+*
+* @ingroup tripal_db
+*/
+function tripal_db_menu() {
+   $items = array();
+
+   $items['admin/tripal/tripal_db'] = array(
+     'title' => 'Databases',
+     'description' => 'Basic Description of Tripal DB Module Functionality',
+     'page callback' => 'tripal_db_module_description_page',
+     'access arguments' => array('administer site configuration'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+
+   $items['admin/tripal/tripal_db/edit_db'] = array(
+     'title' => 'Update/Delete Database References',
+     'description' => 'Manage Databases ',
+     'page callback' => 'tripal_db_admin_page',
+     'access arguments' => array('administer site configuration'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+   
+   $items['admin/tripal/tripal_db/add_db'] = array(
+     'title' => 'Add a Database',
+     'page callback' => 'drupal_get_form',
+     'page arguments' => array('tripal_db_form'),
+     'access arguments' => array('access administration pages'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+   $items['admin/tripal/tripal_db/edit/js'] = array(
+     'title' => 'Edit Databases',
+     'page callback' => 'tripal_ajax_db_edit',
+     'access arguments' => array('access administration pages'),
+     'type' => MENU_CALLBACK,
+   );
+
+   return $items;
+}
+
+/**
+*  Set the permission types that the chado module uses.  Essentially we
+*  want permissionis that protect creation, editing and deleting of chado
+*  data objects
+*
+* @ingroup tripal_db
+*/
+function tripal_db_perm(){
+   return array(
+      'access chado_db content',
+      'create chado_db content',
+      'delete chado_db content',
+      'edit chado_db content',
+   );
+}
+
+/**
+ * Implements hook_views_api()
+ * Purpose: Essentially this hook tells drupal that there is views support for
+ *  for this module which then includes tripal_db.views.inc where all the
+ *  views integration code is
+ *
+ * @ingroup tripal_db
+ */
+function tripal_db_views_api() {
+	return array('api' => 2.0);
+}
+
+/**
+ * Purpose: Provide Guidance to new Tripal Admin
+ *
+ * @return HTML Formatted text
+ *
+ * @ingroup tripal_db
+ */
+function tripal_db_module_description_page() {
+  $text = '';
+
+  $text = '<h3>Tripal External Database Administrative Tools Quick Links</h3>';
+    $text .= '<ul>';
+      $text .= '<li>'.l('Add External DB', 'admin/tripal/tripal_db/add_db').'</li>';
+      $text .= '<li>'.l('Update/Delete External DBs', 'admin/tripal/tripal_db/edit_db').'</li>';
+      $text .= '<li>'.l('Database References Listing', 'admin/tripal/tripal_db/list_dbxrefs').'</li>';
+    $text .= '</ul>';
+    
+  $text .= '<h3>Module Description:</h3>';
+  $text .= '<p>The Tripal DB Module provides functionality for linking the data in your Tripal Website with other biological websites out there. Essentially you register an enternal database with your website and then associate any of your data (usually sequence features) with that external database by providing the accession for your data in the other database. If the other database is online and you provided a URL prefix when you registered the external database with your site then there will be a link on the details page for your data that takes the user to the same record in the external database.</p>';
+
+  $text .= '<h3>Setup Instructions:</h3>';
+  $text .= '<ol>';
+  $text .= '<li><p><b>Set Permissions</b>: The feature module supports the Drupal user permissions interface for 
+               controlling access to feature content and functions. These permissions include viewing, 
+               creating, editing or administering of
+               feature content. The default is that only the original site administrator has these 
+               permissions.  You can <a href="'.url('admin/user/roles').'">add roles</a> for classifying users, 
+               <a href="'.url('admin/user/user').'">assign users to roles</a> and
+               <a href="'.url('admin/user/permissions').'">assign permissions</a> for the feature content to 
+               those roles.  For a simple setup, allow anonymous users access to view organism content and 
+               allow the site administrator all other permissions.</p></li>';
+  $text .= '<li><b>Register any external databases</b> with data pertinent to your site.</li>';
+  $text .= '<li><b>Create Database References</b>: Then as you load in your data, create database references linking your data to the external database.</li>';
+  $text .= '</ol>';
+  
+  
+  $text .= '<h3>Features of this Module:</h3>';
+  $text .= '<ul>';
+  $text .= '<li><b>Add/Register External Databases</b>:';
+  $text .= 'By entering the name and any additional details into the <a href="tripal_db/add_db">add database form</a> you register an external database with your website. This allows you to specify that a sequence feature or other data is also stored in an external database. This is escpecially useful if the external database may contain additional details not stored in yours. If the external database is online you can even provide a URL prefix which will automatically link any data in your website to theirs via a web link.</li>';
+  
+  $text .= '<li><b>Update/Delete External Databases</b>';
+  $text .= 'To edit the details of an external database record or to delete an already existing external database, go to the <a href="tripal_db/edit_db">Update/Delete DBs form</a>. This will allow you to change details or enter new details.</li>';
+
+  $text .= '<li><b>List all External Database References</b>';
+  $text .= 'If you have views installed, there will be a link to a default listing of all database references currently in your database. This listing can be accessed <a href="tripal_db/list_dbxrefs">here</a>. It requires the Drupal Module Views version 2 to be installed (<a href="http://drupal.org/project/views">Drupal Views</a>)</li>';
+  $text .= '</ul>';  
+  
+  return $text;
+}
+
+/**
+*
+*
+* @ingroup tripal_db
+*/
+function tripal_db_admin_page(){
+   $add_url = url("admin/tripal/tripal_db/add_db");
+   $output = "<a href=\"$add_url\">Add a new external database</a>"; 
+   $output .= drupal_get_form('tripal_db_select_form');
+   $output .= '<div id="db-edit-div">Please select a database above to view or edit</div>';
+   return $output;
+}
+/**
+*
+*
+* @ingroup tripal_db
+*/
+function tripal_db_select_form(){
+
+	$previous_db = tripal_db_set_active('chado');  // use chado database
+	// get a list of db from chado for user to choose
+	$sql = "SELECT * FROM {db} WHERE NOT name = 'tripal' ORDER BY name ";
+	$results = db_query ($sql);
+	tripal_db_set_active($previous_db); // use drupal database
+
+	$dbs = array();
+   $dbs[] = '';
+	while ($db = db_fetch_object($results)){
+		$dbs[$db->db_id] = $db->name;
+	}
+
+	$form['dbid'] = array(
+      '#title' => t('External Database Name'),
+      '#type' => 'select',
+      '#options' => $dbs,
+      '#ahah' => array(
+         'path' => 'admin/tripal/tripal_db/edit/js',
+         'wrapper' => 'db-edit-div',
+         'effect' => 'fade',
+         'event' => 'change',
+         'method' => 'replace',
+      ),
+	);
+
+   return $form;
+}
+/**
+*
+* @ingroup tripal_db
+*/
+function tripal_ajax_db_edit (){ 
+   // get the database id, build the form and then return the JSON object
+   $dbid = $_POST['dbid'];
+   $form = drupal_get_form('tripal_db_form',$dbid);
+   drupal_json(array('status' => TRUE, 'data' => $form));
+}
+/**
+*
+* @ingroup tripal_db
+*/
+function tripal_db_form(&$form_state = NULL,$dbid = NULL){
+
+   // get this requested database
+   if($dbid){
+      $sql = "SELECT * FROM {db} WHERE db_id = $dbid ";
+      $previous_db = tripal_db_set_active('chado');
+      $db = db_fetch_object(db_query($sql));
+      tripal_db_set_active($previous_db);
+
+
+      # set the default values.  If there is a value set in the 
+      # form_state then let's use that, otherwise, we'll pull 
+      # the values from the database 
+      $default_db = $form_state['values']['name'];
+      $default_desc = $form_state['values']['description'];
+      $default_url = $form_state['values']['url'];
+      $default_urlprefix = $form_state['values']['urlprefix'];
+      if(!$default_db){
+         $default_db = $db->name;
+      }
+      if(!$default_desc){
+         $default_desc = $db->description;
+      }
+      if(!$default_url){
+         $default_url = $db->url;
+      }
+      if(!$default_urlprefix){
+         $default_urlprefix = $db->urlprefix;
+      }
+      $action = 'Update';
+   } else {
+      $action = 'Add';
+   }
+
+   $form['dbid'] = array(
+      '#type' => 'hidden',
+      '#value' => $dbid
+   );
+
+   $form['name']= array(
+      '#type'          => 'textfield',
+      '#title'         => t("Database Name"),
+      '#description'   => t('Please enter the name for this external database.'),
+      '#required'      => TRUE,
+      '#default_value' => $default_db,
+      '#weight'        => 1
+   );
+
+   $form['description']= array(
+      '#type'          => 'textarea',
+      '#title'         => t('Description'),
+      '#description'   => t('Please enter a description for this database'),
+      '#default_value' => $default_desc,
+      '#weight'        => 2
+   );
+   $form['url']= array(
+      '#type'          => 'textfield',
+      '#title'         => t('URL'),
+      '#description'   => t('Please enter the web address for this database.'),
+      '#default_value' => $default_url,
+      '#weight'        => 3
+   );
+   $form['urlprefix']= array(
+      '#type'          => 'textfield',
+      '#title'         => t('URL prefix'),
+      '#description'   => t('Tripal can provide links to external databases when accession numbers or unique identifiers are known.  Typically, a database will provide a unique web address for each accession and the accession usually is the last component of the page address.  Please enter the web address, minus the accession number for this database.  When an accession number is present, Tripal will combine this web address with the accession and provide a link to the external site.'),
+      '#default_value' => $default_urlprefix,
+      '#weight'        => 4
+   );
+
+
+   if(strcmp($action,'Update')==0){
+      $form['update'] = array (
+        '#type'         => 'submit',
+        '#value'        => t('Update'),
+        '#weight'       => 5,
+        '#executes_submit_callback' => TRUE,
+      );
+      $form['delete'] = array (
+        '#type'         => 'submit',
+        '#value'        => t('Delete'),
+        '#weight'       => 6,
+        '#executes_submit_callback' => TRUE,
+      );
+   } else {
+      $form['add'] = array (
+        '#type'         => 'submit',
+        '#value'        => t('Add'),
+        '#weight'       => 5,
+        '#executes_submit_callback' => TRUE,
+      );
+   }
+   $form['#redirect'] = 'admin/tripal/tripal_db';
+
+
+   return $form;
+}
+/**
+*
+* @ingroup tripal_db
+*/
+function tripal_db_form_submit($form, &$form_state){
+
+   $name =  $form_state['values']['name'];
+   $desc =  $form_state['values']['description'];
+   $url  =  $form_state['values']['url'];
+   $urlp =  $form_state['values']['urlprefix'];
+   $dbid =  $form_state['values']['dbid'];
+   $op   =  $form_state['values']['op'];
+
+   if($dbid){ 
+      if(strcmp($op,'Update')==0){
+         $sql = "
+            UPDATE {db} SET 
+              name = '%s',
+              description = '%s',
+              url = '%s',
+              urlprefix = '%s'
+            WHERE db_id = %d
+         ";
+         $previous_db = tripal_db_set_active('chado');
+         $db = db_query($sql,$name,$desc,$url,$urlp,$dbid);
+         tripal_db_set_active($previous_db);
+         if($db){
+           drupal_set_message("External database updated");
+         } else {
+           drupal_set_message("Failed to update external database.");
+         }
+      } 
+      if(strcmp($op,'Delete')==0){
+         $sql = "
+            DELETE FROM {db}
+            WHERE db_id = %d
+         ";
+         $previous_db = tripal_db_set_active('chado');
+         $db = db_query($sql,$dbid);
+         tripal_db_set_active($previous_db);
+         if($db){
+           drupal_set_message("External database deleted");
+         } else {
+           drupal_set_message("Failed to delete external database.");
+         }
+      }
+   } 
+   else { 
+      $sql = "
+         INSERT INTO {db}
+          (name,description,url,urlprefix)
+         VALUES 
+          ('%s','%s','%s','%s')
+      ";
+      $previous_db = tripal_db_set_active('chado');
+      $db = db_query($sql,$name,$desc,$url,$urlp);
+      tripal_db_set_active($previous_db);
+      if($db){
+        drupal_set_message("External database added");
+      } else {
+        drupal_set_message("Failed to add external database.");
+      }
+   } 
+
+   return '';
+}
+

+ 333 - 0
modules/base/tripal_db/tripal_db.views.inc

@@ -0,0 +1,333 @@
+<?php
+
+/**
+ *  @file
+ *  This file contains the basic functions for views integration of
+ *  chado/tripal db tables. Supplementary functions can be found in
+ *  ./views/
+ *
+ *  Documentation on views integration can be found at 
+ *  http://views2.logrus.com/doc/html/index.html.
+ */
+
+/**
+ * @defgroup tripal_db_views External Database Views Integration
+ * @ingroup views
+ * @ingroup tripal_db
+ */
+
+require_once('views/dbxref.views.inc');
+require_once('views/db.views.inc'); 
+/**
+ * Implements hook_views_data()
+ * Purpose: Describe chado/tripal tables & fields to views
+ * @return: a data array which follows the structure outlined in the
+ *   views2 documentation for this hook. Essentially, it's an array of table
+ *   definitions keyed by chado/tripal table name. Each table definition 
+ *   includes basic details about the table, fields in that table and
+ *   relationships between that table and others (joins)
+ *
+ * @ingroup tripal_db_views
+ */
+function tripal_db_views_data()  {
+
+   $data = array();
+   $data = array_merge($data, retrieve_dbxref_views_data());
+   $data = array_merge($data, retrieve_db_views_data());
+   return $data;
+
+}
+
+/**
+ * Implements hook_views_handlers()
+ * Purpose: Register all custom handlers with views
+ *   where a handler describes either "the type of field", 
+ *   "how a field should be filtered", "how a field should be sorted"
+ * @return: An array of handler definitions
+ *
+ * @ingroup tripal_db_views
+ */
+function tripal_db_views_handlers() {
+ return array(
+   'info' => array(
+     'path' => drupal_get_path('module', 'tripal_db') . '/views/handlers',
+   ),
+   'handlers' => array(
+     'views_handler_field_dbxref_accession_link' => array(
+       'parent' => 'views_handler_field',
+     ),
+   ),
+ );
+}
+
+/**
+ *
+ * @ingroup tripal_db_views
+ */
+function tripal_db_views_default_views () {
+  $views = array();
+  
+  // Main default view
+  // List all cvterms based on cv
+  $view = new view;
+  $view = new view;
+  $view->name = 'all_dbxrefs';
+  $view->description = 'A listing of all database references filtered by database';
+  $view->tag = 'chado';
+  $view->view_php = '';
+  $view->base_table = 'dbxref';
+  $view->is_cacheable = FALSE;
+  $view->api_version = 2;
+  $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
+  $handler = $view->new_display('default', 'Defaults', 'default');
+  $handler->override_option('fields', array(
+    'name' => array(
+      'label' => 'Database',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'name',
+      'table' => 'db',
+      'field' => 'name',
+      'relationship' => 'none',
+    ),
+    'accession' => array(
+      'label' => 'Accession',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'accession',
+      'table' => 'dbxref',
+      'field' => 'accession',
+      'relationship' => 'none',
+    ),
+    'description' => array(
+      'label' => 'Description',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'description',
+      'table' => 'dbxref',
+      'field' => 'description',
+      'relationship' => 'none',
+    ),
+    'version' => array(
+      'label' => 'Version',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'version',
+      'table' => 'dbxref',
+      'field' => 'version',
+      'relationship' => 'none',
+    ),
+    'accession_link' => array(
+      'label' => 'External Link',
+      'alter' => array(
+        'alter_text' => 1,
+        'text' => 'link',
+        'make_link' => 1,
+        'path' => '[accession_link]',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 1,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'accession_link',
+      'table' => 'dbxref',
+      'field' => 'accession_link',
+      'relationship' => 'none',
+    ),
+  ));
+  $handler->override_option('filters', array(
+    'name' => array(
+      'operator' => '=',
+      'value' => '<select db>',
+      'group' => '0',
+      'exposed' => TRUE,
+      'expose' => array(
+        'use_operator' => 0,
+        'operator' => 'name_op',
+        'identifier' => 'db',
+        'label' => 'Database',
+        'optional' => 1,
+        'remember' => 0,
+      ),
+      'case' => 1,
+      'id' => 'name',
+      'table' => 'db',
+      'field' => 'name',
+      'relationship' => 'none',
+    ),
+    'accession' => array(
+      'operator' => 'contains',
+      'value' => '',
+      'group' => '0',
+      'exposed' => TRUE,
+      'expose' => array(
+        'use_operator' => 0,
+        'operator' => 'accession_op',
+        'identifier' => 'accession',
+        'label' => 'Accession Contains',
+        'optional' => 1,
+        'remember' => 0,
+      ),
+      'case' => 0,
+      'id' => 'accession',
+      'table' => 'dbxref',
+      'field' => 'accession',
+      'relationship' => 'none',
+    ),
+  ));
+  $handler->override_option('access', array(
+    'type' => 'perm',
+    'perm' => 'access chado_db content',
+  ));
+  $handler->override_option('cache', array(
+    'type' => 'none',
+  ));
+  $handler->override_option('title', 'Database References');
+  $handler->override_option('empty', 'There are no database references matching the above criteria. Please select a database in order to display all references to that database.');
+  $handler->override_option('empty_format', '1');
+  $handler->override_option('items_per_page', 50);
+  $handler->override_option('use_pager', '1');
+  $handler->override_option('style_plugin', 'table');
+  $handler->override_option('style_options', array(
+    'grouping' => '',
+    'override' => 1,
+    'sticky' => 0,
+    'order' => 'asc',
+    'columns' => array(
+      'name' => 'name',
+      'accession' => 'accession',
+      'description' => 'description',
+      'version' => 'version',
+    ),
+    'info' => array(
+      'name' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'accession' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'description' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+      'version' => array(
+        'sortable' => 1,
+        'separator' => '',
+      ),
+    ),
+    'default' => '-1',
+  ));
+  $handler = $view->new_display('page', 'Page', 'page_1');
+  $handler->override_option('path', 'admin/tripal/tripal_db/list_dbxrefs');
+  $handler->override_option('menu', array(
+    'type' => 'normal',
+    'title' => 'DB Reference Listing',
+    'description' => 'A listing of all database references associated with a given database',
+    'weight' => '0',
+    'name' => 'navigation',
+  ));
+  $handler->override_option('tab_options', array(
+    'type' => 'none',
+    'title' => '',
+    'description' => '',
+    'weight' => 0,
+    'name' => 'navigation',
+  ));
+  $views[$view->name] = $view;
+  
+  return $views;
+}

+ 45 - 0
modules/base/tripal_db/views/README

@@ -0,0 +1,45 @@
+
+This directory contains all additional views code needed to integrate this module with views2.
+
+FILE/FOLDER DESCRIPTIONS
+---------------------------
+<chado table name>.views.inc:
+	contains a single function retrieve_<chado table name>_views_data()
+	which describes that table to views. This function is called by
+	tripal_db_views_data() in ../tripal_db.views.inc.
+	For more information on the form of this data array look up the
+	views2 documentation for hook_views_data() 
+	-http://views2.logrus.com/doc/html/index.html
+	
+handlers/
+	Each file contained within this folder defines a views handler. Only custom
+	handlers are included in this folder and each must be described in 
+	hook_views_handlers() in ../tripal_db.views.inc.
+	A views handler does one of the following:
+		1) describe the type of a field and how it should be displayed
+		2) describe a method to sort this field
+		3) describe a method to filter this field
+
+
+STANDARDS TO FOLLOW
+---------------------------
+
+1. All table definition files should be named tablename.views.inc
+2. All handlers should be in a handlers sub-directory and follow the naming convention of
+   views handlers (ie: views_handler_field/filter/sort_handlername.inc )
+
+Views Table Definitions:
+- Please use the template files provided whenever you are describing a new table to views.
+   For any table in chado simply copy template.table_defn.views.inc to tablename.views.inc and 
+   follow the instructions listed at the top of the template file.
+- ONLY ONE TABLE DEFINITION PER FILE
+- To join a chado table to it's drupal node simply copy template.node_join.views.inc to 
+   basetablename.views.inc and replace all XXX with basetablename.
+   
+   NOTE: Creating the table definition file is not enough. You also need to call the 
+         retrieve_XXX_views_data() function from ../tripal_db.views.inc:tripal_db_views_data()
+         by adding the following line:
+            $data = array_merge($data, retrieve_XXX_views_data());
+         to the function and including the file directly above the function (blow the function 
+         header by adding:
+            require_once('views/XXX.views.inc');

+ 150 - 0
modules/base/tripal_db/views/db.views.inc

@@ -0,0 +1,150 @@
+<?php
+
+  /**
+ * Purpose: this function returns the portion of the data array 
+ *   which describes the cv table, it's fields and any joins between it and other tables
+ * @see tripal_cv_views_data() --in tripal_cv.views.inc
+ *
+ *  TABLE: db
+ * @code
+ * create table db (
+ *    db_id serial not null,
+ *    primary key (db_id),
+ *    name varchar(255) not null,
+ *    description varchar(255) null,
+ *    urlprefix varchar(255) null,
+ *    url varchar(255) null,
+ *    constraint db_c1 unique (name)
+ * );
+ * @endcode
+ *
+ * @ingroup tripal_db_views
+ */
+function retrieve_db_views_data() {
+  
+  // Basic table definition
+  $data['db']['table'] = array(
+    'field' => 'db_id',
+    'group' => 'Chado Database',
+    'title' => 'Chado Database',
+    'help' => 'Database Records existing in the Chado Database',
+    'database' => 'chado'
+  );
+  
+  // Define relationships between this table and others
+  $data['db']['table']['join'] = array(
+    'dbxref' => array(
+      'left_field' => 'db_id',
+      'field' => 'db_id',
+    ),
+    'stock' => array(
+    	'left_table' => 'dbxref',
+    	'left_field' => 'db_id',
+    	'field' => 'db_id',
+    ),
+    'cvterm' => array(
+    	'left_table' => 'dbxref',
+    	'left_field' => 'db_id',
+    	'field' => 'db_id',
+    ),
+    'feature' => array(
+    	'left_table' => 'dbxref',
+    	'left_field' => 'db_id',
+    	'field' => 'db_id',
+    ),
+  );
+  
+  // Table Field Definitions----------------------
+  // Field: db_id (primary key)
+  $data['db']['db_id'] = array(
+    'title' => t('Database ID'),
+    'help' => t('The primary key of the Database.'),
+    'field' => array(
+      'handler' => 'views_handler_field_numeric',
+      'click sortable' => TRUE,
+     ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_numeric',
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+  );
+  
+  // Field: name (varchar 255)
+  $data['db']['name'] = array(
+    'title' => 'Name',
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_chado_select_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+  
+  // Field: description (varchar 255)
+  $data['db']['description'] = array(
+    'title' => 'Description',
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+  
+  // Field: urlprefix (varchar 255)
+  $data['db']['urlprefix'] = array(
+    'title' => 'URL Prefix',
+    'help' => 'The url that when concatenated with the accession of a record in this db produces the url to the record in this database.',
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+  
+  // Field: url (varchar 255)
+  $data['db']['url'] = array(
+    'title' => 'URL',
+    'help' => 'The url to the main page of the database',
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+  
+  return $data;
+  
+}

+ 149 - 0
modules/base/tripal_db/views/dbxref.views.inc

@@ -0,0 +1,149 @@
+<?php
+
+/**
+ * Purpose: this function returns the portion of the data array 
+ *   which describes the db table, it's fields and any joins between it and other tables
+ * @see tripal_db_views_data() --in tripal_db.views.inc
+ *
+ * BASE TABLE: dbxref
+ * @code
+ * create table dbxref (
+ *    dbxref_id serial not null,
+ *    primary key (dbxref_id),
+ *    db_id int not null,
+ *    foreign key (db_id) references db (db_id) on delete cascade INITIALLY DEFERRED,
+ *    accession varchar(255) not null,
+ *    version varchar(255) not null default '',
+ *    description text,
+ *    constraint dbxref_c1 unique (db_id,accession,version)
+ * );
+ * @endcode
+ *
+ * @ingroup tripal_db_views
+ */
+function retrieve_dbxref_views_data() {
+
+  // Basic table definition
+  $data['dbxref']['table']['group'] = 'Chado Database Reference';
+  $data['dbxref']['table']['base'] = array(
+    'field' => 'dbxref_id',
+    'title' => 'Chado Database Reference',
+    'help' => 'A Reference to another database. Allows easy linking between becords in one database to those in another.',
+    'database' => 'chado'
+  );
+
+  // Define relationships between this table and others
+  $data['dbxref']['table']['join'] = array(
+    'cvterm' => array(
+      'left_field' => 'dbxref_id',
+      'field' => 'dbxref_id',
+    ),
+    'feature' => array(
+      'left_field' => 'dbxref_id',
+      'field' => 'dbxref_id',
+    ),
+    'stock' => array(
+      'left_field' => 'dbxref_id',
+      'field' => 'dbxref_id',
+    ),  
+  );
+
+  // Table Field Definitions----------------------
+  // Field: dbxref_id (primary key)
+  $data['dbxref']['dbxref_id'] = array(
+    'title' => t('Database Reference ID'),
+    'help' => t('The primary key of Database References.'),
+    'field' => array(
+      'handler' => 'views_handler_field_numeric',
+      'click sortable' => TRUE,
+     ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_numeric',
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+  );
+   
+  //Field: cv_id (foreign key: cv)
+  //  join between cv table and this one in cv.views.inc
+
+  // Field: accession (varchar 255)
+  $data['dbxref']['accession'] = array(
+    'title' => 'Accession',
+    'help' => 'The accession from the database.',
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+
+  // Field: version (varchar 255)
+  $data['dbxref']['version'] = array(
+    'title' => 'Version',
+    'help' => 'The version of the database reference.',
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+
+  // Field: description (text)
+  $data['dbxref']['description'] = array(
+    'title' => 'Description',
+    'help' => 'a description of the database reference.',
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+
+  // Calculated Field: Accession Link
+  //  uses custom field handler to pull db urlprefix and concatenate with accession
+  //  solves the problem of not being able to add urlprefix to tables which only
+  //  join to dbxref table (not db)
+  $data['dbxref']['accession_link'] = array(
+    'title' => 'Accession Link',
+    'help' => 'Provides a link to the record in the external database.',
+    'field' => array(
+      'handler' => 'views_handler_field_dbxref_accession_link',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+
+  return $data;
+
+}

+ 1 - 0
modules/base/tripal_db/views/handlers/views_handler_field_dbxref_accession_link.inc

@@ -0,0 +1 @@
+../../../tripal_core/views/handlers/views_handler_field_dbxref_accession_link.inc

+ 84 - 0
modules/base/tripal_db/views/template.node_join.views.inc

@@ -0,0 +1,84 @@
+<?php
+
+/*************************************************************************
+ * @file: THIS IS A TEMPLATE AND SHOULD NOT BE INCLUDED IN THE MODULE CODE
+ *
+ *  - simply replace all XXX with the original chado table you want to join to it's drupal nodes.
+ *    (ie: If you want to join features to their drupal nodes then XXX=feature)
+ * 
+ *  NOTE: Creating the table definition file is not enough. You also need to call the 
+ *        retrieve_XXX_views_data() function from ../tripal_db.views.inc:tripal_db_views_data()
+ *        by adding the following line:
+ *           $data = array_merge($data, retrieve_XXX_views_data());
+ *        to the function and including the file directly above the function (blow the function 
+ *        header by adding:
+ *           require_once('views/XXX.views.inc');
+ *
+ *  REMOVE THIS COMMENT IN THE COPY!
+ */ 
+ 
+/**
+ *  @file
+ *  This file defines the data array for a given chado table. This array
+ *  is merged into a larger array containing definitions of all tables associated
+ *  with this module in:
+ *  @see tripal_db.views.inc --in tripal_db_views_data()
+ *
+ *  Note: All chado tables are joined to their drupal nodes through the chado_XXX linking table. 
+ *        This file simply defines this linking table and joins the three tables together.
+ *        No modification of XXX.views.inc is needed.
+ *
+ *  Documentation on views integration can be found at 
+ *  http://views2.logrus.com/doc/html/index.html.
+ */
+ 
+/**
+ * Purpose: this function returns the portion of the data array 
+ *   which describes the chado_XXX drupal table, it's fields and any joins between it 
+ *   and other tables
+ * @see tripal_db_views_data() --in tripal_db.views.inc
+ *
+ * The main need for description of this table to views is to join chado data with drupal nodes
+ *
+ */
+function retrieve_chado_XXX_views_data () {
+	global $db_url;
+  $data = array();
+  
+  // if the chado database is not local to the drupal database
+  // then we need to set the database name.  This should always
+  // be 'chado'.
+  if(is_array($db_url) and array_key_exists('chado',$db_url)){
+     // return empty data array b/c if chado is external then no join to the nodetable can be made
+     return $data;
+  }
+
+  //Basic table definition-----------------------------------
+  $data['chado_XXX']['table'] = array(
+    'field' => 'nid',
+  );
+  
+  //Relationship Definitions---------------------------------
+  // Note: No joins need to be made from $data['XXX']['table']
+  
+  // Join the chado_XXX table to XXX
+  $data['chado_XXX']['table']['join']['XXX'] = array(
+  	'left_field' => 'XXX_id',
+  	'field' => 'XXX_id',
+  );
+  
+  // Join the node table to chado_XXX
+  $data['node']['table']['join']['chado_XXX'] = array(
+  	'left_field' => 'nid',
+  	'field' => 'nid',
+  );
+  
+  // Join the node table to XXX
+  $data['node']['table']['join']['XXX'] = array(
+  	'left_table' => 'chado_XXX',
+  	'left_field' => 'nid',
+  	'field' => 'nid',
+  );  
+
+	return $data;
+}

+ 211 - 0
modules/base/tripal_db/views/template.table_defn.views.inc

@@ -0,0 +1,211 @@
+<?php
+
+/*************************************************************************
+ * @file: THIS IS A TEMPLATE AND SHOULD NOT BE INCLUDED IN THE MODULE CODE
+ *
+ *   - Every instance of XXX should be replaced with the name of your table
+ *   - If this is a base table (you want a view where every row is a row from this table)
+ *     then change $data['XXX']['table'] to $data['XXX']['table']['base'] 
+ *     and $data['XXX']['table']['database'] to $data['XXX']['table']['base']['database']
+ *   - Relationships between this table and others: YYY is the table you are trying to join to this
+ *     one. You want to join a table to this one if this table contains a foreign key to the other
+ *     table. If the join between this table and another is through a linking table
+ *     (ie: library-XXX/YYY => library_feature-XY => feature-XXX/YYY) then make the join in both
+ *     directions (ie: in the file XXX.views.inc and the file YYY.views.inc
+ *   - Create a field definition for each field in this table using the example fields already
+ *     listed. Match the type of the database field to the field definition listed below.
+ *     (ie: for a text/varchar field from the database use plain_text_field below)
+ * 
+ *  NOTE: Creating the table definition file is not enough. You also need to call the 
+ *        retrieve_XXX_views_data() function from ../tripal_db.views.inc:tripal_db_views_data()
+ *        by adding the following line:
+ *           $data = array_merge($data, retrieve_XXX_views_data());
+ *        to the function and including the file directly above the function (blow the function 
+ *        header by adding:
+ *           require_once('views/XXX.views.inc');
+ *
+ *  REMOVE THIS COMMENT IN THE COPY!
+ */ 
+ 
+/**
+ *  @file
+ *  This file defines the data array for a given chado table. This array
+ *  is merged into a larger array containing definitions of all tables associated
+ *  with this module in:
+ *  @see tripal_db.views.inc --in tripal_db_views_data()
+ *
+ *  Documentation on views integration can be found at 
+ *  http://views2.logrus.com/doc/html/index.html.
+ */
+
+/*************************************************************************
+ * Purpose: this function returns the portion of the data array 
+ *   which describes the XXX table, it's fields and any joins between it and other tables
+ * @see tripal_db_views_data() --in tripal_db.views.inc
+ *
+ * Table: XXX
+ * @code
+ * XXX-Copy/Paste Table SQL code here-XXX
+ * @endcode
+ */
+ function retrieve_XXX_views_data() {
+  global $db_url;
+  $data = array();
+
+  // if the chado database is not local to the drupal database
+  // then we need to set the database name.  This should always
+  // be 'chado'.
+  if(is_array($db_url) and array_key_exists('chado',$db_url)){
+    $database = 'chado';
+  }
+   
+  //Basic table definition-----------------------------------
+  $data['XXX']['table']['group'] = t('Chado XXX');
+  
+  $data['XXX']['table'] = array(
+    'field' => 'primary_id',
+    'title' => t('Chado XXX'),
+    'help' => t('Enter some user-friendly description of this tables purpose to the user.'),
+  );
+  if($database){
+     $data['XXX']['table']['database'] = $database;
+  }
+
+  
+  //Relationship Definitions---------------------------------
+  //Join: YYY => XXX
+  // Notice that this relationship tells the primary table to show it's fields to the
+  // table referencing it by a foreign key and thus the relationship is from
+  // primary table to table referenceing it (ie: cvterm => feature)
+  $data['XXX']['table']['join']['YYY'] = array(
+    'left_field' => 'foreign key in YYY table',
+    'field' => 'primary key in XXX table',
+  );  
+  
+  //Join: XXX => XY => YYY
+  // This relationship should be described in both directions
+  // in the appropriate files (ie: for feature => library 
+  // describe in both feature.views.inc and library.views.inc)
+  $data['XXX']['table']['join']['XY'] = array(
+    'left_field' => 'matching XXX key in the XY table',
+    'field' => 'primary key in XXX table',
+  );  
+  $data['XXX']['table']['join']['YYY'] = array(
+    'left_table' => 'XY',
+    'left_field' => 'matching XXX key in the XY table',
+    'field' => 'primary key in XXX table',
+  );
+  $data['XY']['table']['join']['YYY'] = array(
+    'left_field' => 'primary key in YYY table',
+    'field' => 'matching YYY key in the XY table',
+  );
+   
+  //Table Field Definitions----------------------------------
+      
+  //Field: XXX_id (primary key)
+  $data['XXX']['field_name'] = array(
+    'title' => t('XXX Primary Key'),
+    'help' => t('A unique index for every XXX.'),
+    'field' => array(
+      'handler' => 'views_handler_field_numeric',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_numeric',
+    ),
+  );
+
+  /*.......................................................
+   * Beginning of Example Field definitions
+   * Remove this section when done
+   */
+
+  //Field: plain_text_field (chado datatype)   
+  $data['XXX']['plain_text_field'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+
+  //Field: numeric_field (chado datatype)   
+  $data['XXX']['numeric_field'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_numeric',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_numeric',
+    ),
+  );
+
+  //Field: boolean_field (chado datatype)   
+  $data['XXX']['boolean_field'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_boolean',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_boolean_operator',
+    ),
+  );
+
+  //Field: unix_timestamp (chado datatype)   
+  $data['XXX']['unix_timestamp'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_date',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort_date',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_date',
+    ),
+  );
+
+  //Field: human_readable_date (chado datatype)   
+  $data['XXX']['human_readable_date'] = array(
+    'title' => t('Human-Readable Name'),
+    'help' => t('Description of this field.'),
+    'field' => array(
+      'handler' => 'views_handler_field_readble_date',
+      'click sortable' => TRUE,
+     ),
+    'sort' => array(
+      'handler' => 'views_handler_sort_date',
+    ),
+  );
+   
+   /*
+    * End of Example Field definitions
+    */
+    
+  return $data;
+}

+ 709 - 0
modules/base/tripal_feature/fasta_loader.php

@@ -0,0 +1,709 @@
+<?php
+
+/**
+ * @defgroup fasta_loader FASTA Feature Loader
+ * @{
+ * Provides fasta loading functionality. Creates features based on their specification in a fasta file.
+ * @}
+ * @ingroup tripal_feature
+ */
+ 
+/**
+ *
+ *
+ * @ingroup fasta_loader
+ */
+function tripal_feature_fasta_load_form (){
+
+   $form['fasta_file']= array(
+      '#type'          => 'textfield',
+      '#title'         => t('FASTA File'),
+      '#description'   => t('Please enter the full system path for the FASTA file, or a path within the Drupal
+                             installation (e.g. /sites/default/files/xyz.obo).  The path must be accessible to the
+                             server on which this Drupal instance is running.'),
+      '#required' => TRUE,
+   );
+
+   // get the list of organisms
+   $sql = "SELECT * FROM {organism} ORDER BY genus, species";
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $org_rset = db_query($sql);
+   tripal_db_set_active($previous_db);  // now use drupal database
+   $organisms = array();
+   $organisms[''] = '';
+   while($organism = db_fetch_object($org_rset)){
+      $organisms[$organism->organism_id] = "$organism->genus $organism->species ($organism->common_name)";
+   }
+   $form['organism_id'] = array (
+     '#title'       => t('Organism'),
+     '#type'        => t('select'),
+     '#description' => t("Choose the organism to which these sequences are associated "),
+     '#required'    => TRUE,
+     '#options'     => $organisms,
+   );
+
+   $form['seqtype']= array(
+      '#type' => 'textfield',
+      '#title' => t('Sequence Type'),
+      '#required' => TRUE,
+      '#description' => t('Please enter the Sequence Ontology term that describes the sequences in the FASTA file.'),
+   );
+
+
+   // get the list of organisms
+   $sql = "SELECT L.library_id, L.name, CVT.name as type
+           FROM {library} L
+              INNER JOIN {cvterm} CVT ON L.type_id = CVT.cvterm_id
+           ORDER BY name";
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $lib_rset = db_query($sql);
+   tripal_db_set_active($previous_db);  // now use drupal database
+   $libraries = array();
+   $libraries[''] = '';
+   while($library = db_fetch_object($lib_rset)){
+      $libraries[$library->library_id] = "$library->name ($library->type)";
+   }
+//   $form['library_id'] = array (
+//     '#title'       => t('Library'),
+//     '#type'        => t('select'),
+//     '#description' => t("Choose the library to which these sequences are associated "),
+//     '#required'    => FALSE,
+//     '#options'     => $libraries,
+//     '#weight'      => 5,
+//   );
+   $form['method']= array(
+      '#type' => 'radios',
+      '#title' => 'Method',
+      '#required' => TRUE,
+      '#options' => array(
+         t('Insert only'),
+         t('Update only'),
+         t('Insert and update'),
+      ),
+      '#description' => t('Select how features in the FASTA file are handled.  
+         Select "Insert only" to insert the new features. If a feature already 
+         exists with the same name or unique name and type then it is skipped.
+         Select "Update only" to only update featues that already exist in the
+         database.  Select "Insert and Update" to insert features that do
+         not exist and upate those that do.'),
+      '#default_value' => 2,
+   );
+
+$form['match_type']= array(
+      '#type' => 'radios',
+      '#title' => 'Name Match Type',
+      '#required' => TRUE,
+      '#options' => array(
+         t('Name'),
+         t('Unique name'),
+      ),
+      '#description' => t('Feature data is stored in Chado with both a human-readable
+        name and a unique name. If the features in your FASTA file are identified using
+        a human-readable name then select the "Name" button. If your features are
+        identified using the unique name then select the "Unique name" button.  If you 
+        loaded your features first using the GFF loader then the unique name of each
+        features were indicated by the "ID=" attribute and the name by the "Name=" attribute.
+        By default, the FASTA loader will use the first word (character string
+        before the first space) as  the name for your feature. If 
+        this does not uniquely identify your feature consider specifying a regular expression in the advanced section below. 
+        Additionally, you may import both a name and a unique name for each sequence using the advanced options. 
+        When updating a sequence, the value selected here will be used to identify the sequence in the 
+        database in combination with any regular expression provided below.'),
+      '#default_value' => 1,
+   );
+
+   $form['analysis'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Analysis Used to Derive Features'),
+      '#collapsed' => TRUE
+   ); 
+   $form['analysis']['desc'] = array(
+      '#type' => 'markup',
+      '#value' => t("Why specify an analysis for a data load?  All data comes 
+         from some place, even if downloaded from Genbank. By specifying
+         analysis details for all data uploads, it allows an end user to reproduce the
+         data set, but at least indicates the source of the data."), 
+   );
+
+   // get the list of organisms
+   $sql = "SELECT * FROM {analysis} ORDER BY name";
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $org_rset = db_query($sql);
+   tripal_db_set_active($previous_db);  // now use drupal database
+   $analyses = array();
+   $analyses[''] = '';
+   while($analysis = db_fetch_object($org_rset)){
+      $analyses[$analysis->analysis_id] = "$analysis->name ($analysis->program $analysis->programversion, $analysis->sourcename)";
+   }
+   $form['analysis']['analysis_id'] = array (
+     '#title'       => t('Analysis'),
+     '#type'        => t('select'),
+     '#description' => t("Choose the analysis to which these features are associated "),
+     '#required'    => TRUE,
+     '#options'     => $analyses,
+   );
+
+   // Advanced Options
+   $form['advanced'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Advanced Options'),
+      '#collapsed' => TRUE
+   );
+   $form['advanced']['re_help']= array(
+      '#type' => 'item',
+      '#value' => t('A regular expression is an advanced method for extracting information from a string of text.  
+                     Your FASTA file may contain both a human-readable name and a unique name for each sequence.  
+                     If you want to import
+                     both the name and unique name for all sequences, then you must provide regular expressions 
+                     so that the loader knows how to separate them.  
+                     Otherwise the name and uniquename will be the same.  
+                     By default, this loader will use the first word in the definition 
+                     lines of the FASTA file
+                     as the name or unique name of the feature.'),
+   );
+   $form['advanced']['re_name']= array(
+      '#type' => 'textfield',
+      '#title' => t('Regular expression for the name'),
+      '#required' => FALSE,
+      '#description' => t('Enter the regular expression that will extract the 
+         feature name from the FASTA definition line. For example, for a 
+         defintion line with a name and unique name separated by a bar \'|\' (>seqname|uniquename), 
+         the regular expression for the name would be, "^(.*?)\|.*$".'),
+   );  
+   $form['advanced']['re_uname']= array(
+      '#type' => 'textfield',
+      '#title' => t('Regular expression for the unique name'),
+      '#required' => FALSE,
+      '#description' => t('Enter the regular expression that will extract the 
+         feature name from the FASTA definition line. For example, for a 
+         defintion line with a name and unique name separated by a bar \'|\' (>seqname|uniquename), 
+         the regular expression for the unique name would be "^.*?\|(.*)$").'),
+   );   
+ 
+
+   // Advanced database cross-reference optoins
+   $form['advanced']['db'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('External Database Reference'),
+      '#weight'=> 6,
+      '#collapsed' => TRUE
+   );
+   $form['advanced']['db']['re_accession']= array(
+      '#type' => 'textfield',
+      '#title' => t('Regular expression for the accession'),
+      '#required' => FALSE,
+      '#description' => t('Enter the regular expression that will extract the accession for the external database for each feature from the FASTA definition line.'),
+      '#weight' => 2
+   ); 
+
+  // get the list of databases
+   $sql = "SELECT * FROM {db} ORDER BY name";
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $db_rset = db_query($sql);
+   tripal_db_set_active($previous_db);  // now use drupal database
+   $dbs = array();
+   $dbs[''] = '';
+   while($db = db_fetch_object($db_rset)){
+      $dbs[$db->db_id] = "$db->name";
+   }
+   $form['advanced']['db']['db_id'] = array (
+     '#title'       => t('External Database'),
+     '#type'        => t('select'),
+     '#description' => t("Plese choose an external database for which these sequences have a cross reference."),
+     '#required'    => FALSE,
+     '#options'     => $dbs,
+     '#weight'      => 1,
+   );
+
+   $form['advanced']['relationship'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Relationships'),
+      '#weight'=> 6,
+      '#collapsed' => TRUE
+   );
+   $rels = array();
+   $rels[''] = '';
+   $rels['part_of'] = 'part of';
+   $rels['derives_from'] = 'produced by';
+
+
+   // Advanced references options
+   $form['advanced']['relationship']['rel_type']= array(
+     '#title'       => t('Relationship Type'),
+     '#type'        => t('select'),
+     '#description' => t("Use this option to create associations, or relationships between the 
+                          features of this FASTA file and existing features in the database. For 
+                          example, to associate a FASTA file of peptides to existing genes or transcript sequence, 
+                          select the type 'produced by'. For a CDS sequences select the type 'part of'"),
+     '#required'    => FALSE,
+     '#options'     => $rels,
+     '#weight'      => 5,
+   );
+   $form['advanced']['relationship']['re_subject']= array(
+      '#type' => 'textfield',
+      '#title' => t('Regular expression for the parent'),
+      '#required' => FALSE,
+      '#description' => t('Enter the regular expression that will extract the unique 
+                           name needed to identify the existing sequence for which the 
+                           relationship type selected above will apply.'),
+      '#weight' => 6
+   ); 
+   $form['advanced']['relationship']['parent_type']= array(
+      '#type' => 'textfield',
+      '#title' => t('Parent Type'),
+      '#required' => FALSE,
+      '#description' => t('Please enter the Sequence Ontology term for the parent.  For example
+                           if the FASTA file being loaded is a set of proteins that are 
+                           products of genes, then use the SO term \'gene\' or \'transcript\' or equivalent. However,
+                           this type must match the type for already loaded features.'),
+      '#weight' => 7
+   );
+
+   $form['button'] = array(
+      '#type' => 'submit',
+      '#value' => t('Import FASTA file'),
+      '#weight' => 10,
+   );
+   return $form;   
+}
+
+/**
+ *
+ *
+ * @ingroup fasta_loader
+ */
+function tripal_feature_fasta_load_form_validate($form, &$form_state){
+   $fasta_file = trim($form_state['values']['fasta_file']);
+   $organism_id  = $form_state['values']['organism_id'];
+   $type         = trim($form_state['values']['seqtype']);
+   $method       = trim($form_state['values']['method']);
+   $match_type   = trim($form_state['values']['match_type']);
+   $library_id   = $form_state['values']['library_id'];
+   $re_name      = trim($form_state['values']['re_name']);
+   $re_uname     = trim($form_state['values']['re_uname']);
+   $re_accession = trim($form_state['values']['re_accession']);
+   $db_id        = $form_state['values']['db_id'];
+   $rel_type     = $form_state['values']['rel_type'];
+   $re_subject   = trim($form_state['values']['re_subject']);
+   $parent_type   = trim($form_state['values']['parent_type']);
+
+   if($method == 0){
+      $method = 'Insert only';
+   }
+   if($method == 1){
+      $method = 'Update only';
+   }
+   if($method == 2){
+      $method = 'Insert and update';
+   }
+
+   if($match_type == 0){
+      $match_type = 'Name';
+   }
+
+   if($match_type == 1){
+      $match_type = 'Unique name';
+   }
+
+
+   if ($re_name and !$re_uname and strcmp($match_type,'Unique name')==0){
+      form_set_error('re_uname',t("You must provide a regular expression to identify the sequence unique name"));     
+   }
+
+   if (!$re_name and $re_uname and strcmp($match_type,'Name')==0){
+      form_set_error('re_name',t("You must provide a regular expression to identify the sequence name"));     
+   }
+
+   // check to see if the file is located local to Drupal
+   $dfile = $_SERVER['DOCUMENT_ROOT'] . base_path() . $fasta_file; 
+   if(!file_exists($dfile)){
+      // if not local to Drupal, the file must be someplace else, just use
+      // the full path provided
+      $dfile = $fasta_file;
+   }
+   if(!file_exists($dfile)){
+      form_set_error('fasta_file',t("Cannot find the file on the system. Check that the file exists or that the web server has permissions to read the file."));
+   }
+
+   // make sure if a relationship is specified that all fields are provided.
+   if(($rel_type or $parent_type) and !$re_subject){
+      form_set_error('re_subject',t("Please provide a regular expression for the parent"));
+   }
+   if(($rel_type or $re_subject) and !$parent_type){
+      form_set_error('parent_type',t("Please provide a SO term for the parent"));
+   }
+   if(($parent_type or $re_subject) and !$rel_type){
+      form_set_error('rel_type',t("Please select a relationship type"));
+   }
+
+
+   // make sure if a database is specified that all fields are provided
+   if($db_id and !$re_accession){
+      form_set_error('re_accession',t("Please provide a regular expression for the accession"));
+   }
+   if($re_accession and !$db_id){
+      form_set_error('db_id',t("Please select a database"));
+   }
+
+   // check to make sure the types exists
+   $cvtermsql = "SELECT CVT.cvterm_id
+                 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.name = '%s' and (CVT.name = '%s' or CVTS.synonym = '%s')";
+   $cvterm = db_fetch_object(db_query($cvtermsql,'sequence',$type,$type));
+   if(!$cvterm){
+      form_set_error('type',t("The Sequence Ontology (SO) term selected for the sequence type is not available in the database. Please check spelling or select another."));
+   }
+   if($rel_type){
+      $cvterm = db_fetch_object(db_query($cvtermsql,'sequence',$parent_type,$parent_type));
+      if(!$cvterm){
+         form_set_error('parent_type',t("The Sequence Ontology (SO) term selected for the parent relationship is not available in the database. Please check spelling or select another."));
+      }
+   }
+
+   // check to make sure the 'relationship' and 'sequence' ontologies are loaded
+   $form_state['storage']['dfile'] = $dfile;
+}
+
+/**
+ *
+ *
+ * @ingroup fasta_loader
+ */
+function tripal_feature_fasta_load_form_submit ($form, &$form_state){
+   global $user;
+
+   $dfile        = $form_state['storage']['dfile'];
+   $organism_id  = $form_state['values']['organism_id'];
+   $type         = trim($form_state['values']['seqtype']);
+   $method       = trim($form_state['values']['method']);
+   $match_type   = trim($form_state['values']['match_type']);
+   $library_id   = $form_state['values']['library_id'];
+   $re_name      = trim($form_state['values']['re_name']);
+   $re_uname     = trim($form_state['values']['re_uname']);
+   $re_accession = trim($form_state['values']['re_accession']);
+   $db_id        = $form_state['values']['db_id'];
+   $rel_type     = $form_state['values']['rel_type'];
+   $re_subject   = trim($form_state['values']['re_subject']);
+   $parent_type   = trim($form_state['values']['parent_type']);
+   $analysis_id = $form_state['values']['analysis_id'];
+
+   if($method == 0){
+      $method = 'Insert only';
+   }
+   if($method == 1){
+      $method = 'Update only';
+   }
+   if($method == 2){
+      $method = 'Insert and update';
+   }
+
+   if($match_type == 0){
+      $match_type = 'Name';
+   }
+
+   if($match_type == 1){
+      $match_type = 'Unique name';
+   }
+
+   $args = array($dfile,$organism_id,$type,$library_id,$re_name,$re_uname,
+            $re_accession,$db_id,$rel_type,$re_subject,$parent_type,$method,
+            $user->uid,$analysis_id,$match_type);
+
+   tripal_add_job("Import FASTA file: $dfile",'tripal_feature',
+      'tripal_feature_load_fasta',$args,$user->uid);
+}
+
+/**
+ *
+ *
+ * @ingroup fasta_loader
+ */
+function tripal_feature_load_fasta($dfile, $organism_id, $type,
+   $library_id, $re_name, $re_uname, $re_accession, $db_id, $rel_type,
+   $re_subject, $parent_type, $method, $uid, $analysis_id, 
+   $match_type,$job = NULL)
+{
+
+   print "Opening FASTA file $dfile\n";
+
+    
+   $lines = file($dfile,FILE_SKIP_EMPTY_LINES);
+   $i = 0;
+
+   $name = '';
+   $uname = '';
+   $residues = '';
+   $num_lines = sizeof($lines);
+   $interval = intval($num_lines * 0.01);
+   if($interval == 0){
+      $interval = 1;
+   }
+
+   foreach ($lines as $line_num => $line) {
+      $i++;  // update the line count     
+
+      // update the job status every 1% features
+      if($job and $i % $interval == 0){
+         tripal_job_set_progress($job,intval(($i/$num_lines)*100));
+      }
+
+      // 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 have a feature name then we are starting a new sequence
+         // so let's handle the previous one before moving on
+         if($name or $uname){
+           tripal_feature_fasta_loader_handle_feature($name,$uname,$db_id,
+              $accession,$subject,$rel_type,$parent_type,$analysis_id,$organism_id,$type,
+              $source,$residues,$method,$re_name,$match_type);
+           $residues = '';
+           $name = '';
+           $uname = '';
+         }
+
+         $line = preg_replace("/^>/",'',$line);
+         // get the feature name
+         if($re_name){
+            if(!preg_match("/$re_name/",$line,$matches)){
+               print "WARNING: Regular expression for the feature name finds nothing\n";
+            }
+            $name = trim($matches[1]);
+         } else {
+            // if the match_type is name and no regular expression was provided
+            // then use the first word as the name, otherwise we don't set the name
+            if(strcmp($match_type,'Name')==0){
+               preg_match("/^\s*(.*?)[\s\|].*$/",$line,$matches);
+               $name = trim($matches[1]);
+            }
+         } 
+         // get the feature unique name
+         if($re_uname){
+            if(!preg_match("/$re_uname/",$line,$matches)){
+               print "WARNING: Regular expression for the feature unique name finds nothing\n";
+            }
+            $uname = trim($matches[1]);
+         } else {
+            // if the match_type is name and no regular expression was provided
+            // then use the first word as the name, otherwise, we don't set the unqiuename
+            if(strcmp($match_type,'Unique name')==0){
+               preg_match("/^\s*(.*?)[\s\|].*$/",$line,$matches);
+               $uname = trim($matches[1]);
+            }
+         } 
+         // get the accession    
+         preg_match("/$re_accession/",$line,$matches);
+         $accession = trim($matches[1]);
+
+         // get the relationship subject
+         preg_match("/$re_subject/",$line,$matches);
+         $subject = trim($matches[1]);
+      }
+      else {
+         $residues .= trim($line);
+      }
+   }
+   // now load the last sequence in the file
+   tripal_feature_fasta_loader_handle_feature($name,$uname,$db_id,
+      $accession,$subject,$rel_type,$parent_type,$analysis_id,$organism_id,$type,
+      $source,$residues,$method,$re_name,$match_type);
+   return '';
+}
+
+/**
+ *
+ *
+ * @ingroup fasta_loader
+ */
+function tripal_feature_fasta_loader_handle_feature($name,$uname,$db_id,$accession,
+              $parent,$rel_type,$parent_type,$analysis_id,$organism_id,$type, 
+              $source,$residues,$method,$re_name,$match_type) 
+{
+   $previous_db = tripal_db_set_active('chado');
+
+   // first get the type for this sequence
+   $cvtermsql = "SELECT CVT.cvterm_id
+                 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.name = '%s' and (CVT.name = '%s' or CVTS.synonym = '%s')";
+   $cvterm = db_fetch_object(db_query($cvtermsql,'sequence',$type,$type));
+   if(!$cvterm){
+      print "ERROR: cannot find the term type: '$type'\n";
+      return 0;
+   }
+
+   // check to see if this feature already exists
+   if(strcmp($match_type,'Name')==0){
+      $cnt_sql = "SELECT count(*) as cnt FROM {feature} 
+                      WHERE organism_id = %d and name = '%s' and type_id = %d";
+      $cnt = db_fetch_object(db_query($cnt_sql,$organism_id,$name,$cvterm->cvterm_id));
+      if($cnt->cnt > 1){
+         print "ERROR: multiple features exist with the name '$name' of type '$type' for the organism.  skipping\n";
+         return 0;
+      } else {
+         $feature_sql = "SELECT * FROM {feature} 
+                      WHERE organism_id = %d and name = '%s' and type_id = %d";
+         $feature = db_fetch_object(db_query($feature_sql,$organism_id,$name,$cvterm->cvterm_id));
+      }
+   }
+   if(strcmp($match_type,'Unique name')==0){
+      $feature_sql = "SELECT * FROM {feature} 
+                      WHERE organism_id = %d and uniquename = '%s' and type_id = %d";
+      $feature = db_fetch_object(db_query($feature_sql,$organism_id,$uname,$cvterm->cvterm_id));
+   }
+
+   if(!$feature and (strcmp($method,'Insert only')==0 or strcmp($method,'Insert and update')==0)){
+       // if we have a unique name but not a name then set them to be teh same 
+       // and vice versa
+       if(!$uname){
+          $uname = $name;
+       }
+       elseif(!$name){
+          $name = $uname;
+       }
+      // now insert the feature
+      $sql = "INSERT INTO {feature} 
+                 (organism_id, name, uniquename, residues, seqlen, 
+                  md5checksum,type_id,is_analysis,is_obsolete)
+              VALUES(%d,'%s','%s','%s',%d, '%s', %d, %s, %s)";
+      $result = db_query($sql,$organism_id,$name,$uname,$residues,strlen($residues),
+                  md5($residues),$cvterm->cvterm_id,'false','false');
+      if(!$result){
+         print "ERROR: failed to insert feature '$name ($uname)'\n";
+         return 0;
+      } else {
+         print "Inserted feature $name ($uname)\n";
+      }
+      $feature = db_fetch_object(db_query($feature_sql,$organism_id,$uname,$cvterm->cvterm_id));
+   } 
+   if(!$feature and (strcmp($method,'Update only')==0 or strcmp($method,'Insert and update')==0)){
+      print "WARNING: failed to find feature '$name' ('$uname') while matching on " . strtolower($match_type) . ". Skipping\n";
+      return 0;
+   }
+
+   if($feature and (strcmp($method,'Update only')==0 or strcmp($method,'Insert and update')==0)){
+       if(strcmp($method,'Update only')==0 or strcmp($method,'Insert and update')==0){
+         if(strcmp($match_type,'Name')==0){
+            // if we're matching on the name but do not have a new unique name then we
+            // don't want to update the uniquename.  If we do have a uniquename then we 
+            // should update it.  We only get a uniquename if there was a regular expression
+            // provided for pulling it out
+            if($uname){
+               $sql = "UPDATE {feature} 
+                        SET uniquename = '%s', residues = '%s', seqlen = '%s', md5checksum = '%s'
+                        WHERE organism_id = %d and name = '%s' and type_id = %d";
+               $result = db_query($sql,$uname,$residues,strlen($residues),md5($residues),$organism_id,$name,$cvterm->cvterm_id);
+            } else {
+               $sql = "UPDATE {feature} 
+                        SET residues = '%s', seqlen = '%s', md5checksum = '%s'
+                        WHERE organism_id = %d and name = '%s' and type_id = %d";
+               $result = db_query($sql,$residues,strlen($residues),md5($residues),$organism_id,$name,$cvterm->cvterm_id);
+            }
+         } else {
+            // if we're matching on the unique name but do not have a new name then we
+            // don't want to update the name.  If we do have a name then we 
+            // should update it.  We only get a name if there was a regular expression
+            // provided for pulling it out
+            if($name){
+               $sql = "UPDATE {feature} 
+                        SET name = '%s', residues = '%s', seqlen = '%s', md5checksum = '%s'
+                        WHERE organism_id = %d and uniquename = '%s' and type_id = %d";
+               $result = db_query($sql,$name,$residues,strlen($residues),md5($residues),$organism_id,$uname,$cvterm->cvterm_id);
+            } else {
+               $sql = "UPDATE {feature} 
+                        SET residues = '%s', seqlen = '%s', md5checksum = '%s'
+                        WHERE organism_id = %d and uniquename = '%s' and type_id = %d";
+               $result = db_query($sql,$residues,strlen($residues),md5($residues),$organism_id,$uname,$cvterm->cvterm_id);
+            }
+         }
+         if(!$result){
+            print "ERROR: failed to update feature '$name ($uname)'\n";
+            return 0;
+         } else {
+            print "Updated feature $name ($uname)\n";
+         }
+      } else {
+         print "WARNING: feature already exists: '$name' ('$uname'). Skipping\n";
+      }
+   }
+   // now get the feature
+   $feature = db_fetch_object(db_query($feature_sql,$organism_id,$uname,$cvterm->cvterm_id));
+   if(!$feature){
+      print "Something bad has happened: $organism_id, $uname, $cvterm->cvterm_id\n";
+      return 0;
+   }
+
+	 // add in the analysis link
+	 if ($analysis_id) {
+	 	$analysis_link_sql = 'SELECT * FROM analysisfeature WHERE analysis_id=%d AND feature_id=%d';
+	 	$analysis_link = db_fetch_object(db_query($analysis_link_sql, $analysis_id, $feature->feature_id));
+	 	if (!$analysis_link) {
+	 		$sql = "INSERT INTO analysisfeature (analysis_id, feature_id) VALUES (%d, %d)";
+	 		$result = db_query($sql, $analysis_id, $feature->feature_id);
+		  if(!$result){
+			  print "WARNING: could not add link between analysis: ".$analysis_id." and feature: ".$feature->uniquename."\n";
+		  }
+		  $analysis_link = db_fetch_object(db_query($analysis_link_sql, $analysis_id, $feature->feature_id));
+	 	}
+	 }
+	 
+   // now add the database cross reference
+   if($db_id){
+      // check to see if this accession reference exists, if not add it
+      $dbxrefsql = "SELECT * FROM {dbxref} WHERE db_id = %s and accession = '%s'";
+      $dbxref = db_fetch_object(db_query($dbxrefsql,$db_id,$accession));
+      if(!$dbxref){
+         $sql = "INSERT INTO {dbxref} (db_id,accession) VALUES (%d,'%s')";
+         $result = db_query($sql,$db_id,$accession);
+         if(!$result){
+           print "WARNING: could not add external database acession: '$name accession: $accession'\n";
+         }
+         $dbxref = db_fetch_object(db_query($dbxrefsql,$db_id,$accession));
+      }
+
+      // check to see if the feature dbxref record exists if not, then add it 
+      $fdbxrefsql = "SELECT * FROM {feature_dbxref} WHERE feature_id = %d and dbxref_id = %d";
+      $fdbxref = db_fetch_object(db_query($fdbxrefsql,$feature->feature_id,$dbxref->dbxref_id));
+      if(!$fdbxref){
+         $sql = "INSERT INTO {feature_dbxref} (feature_id,dbxref_id) VALUES (%d,%d)";
+         $result = db_query($sql,$feature->feature_id,$dbxref->dbxref_id);
+         if(!$result){
+            print "WARNING: could not associate database cross reference with feature: '$name accession: $accession'\n";
+         } else {
+            print "Added database crossreference $name ($uname) -> $accession\n";
+         }
+      }
+   }
+
+   // now add in the relationship if one exists.  First, get the parent type for the relationship
+   // then get the parent feature 
+   if($rel_type){
+      $parentcvterm = db_fetch_object(db_query($cvtermsql,'sequence',$parent_type,$parent_type));
+      $relcvterm = db_fetch_object(db_query($cvtermsql,'relationship',$rel_type,$rel_type));
+      $parent_feature = db_fetch_object(db_query($feature_sql,$organism_id,$parent,$parentcvterm->cvterm_id));
+      if($parent_feature){
+         // check to see if the relationship already exists
+         $sql = "SELECT * FROM {feature_relationship} WHERE subject_id = %d and object_id = %d and type_id = %d";
+         $rel = db_fetch_object(db_query($sql,$feature->feature_id,$parent_feature->feature_id,$relcvterm->cvterm_id));
+         if($rel){
+            print "WARNING: relationship already exists, skipping '$uname' ($type) $rel_type '$parent' ($parent_type)\n";
+         } else {      
+            $sql = "INSERT INTO {feature_relationship} (subject_id,object_id,type_id)
+                    VALUES (%d,%d,%d)";
+            $result = db_query($sql,$feature->feature_id,$parent_feature->feature_id,$relcvterm->cvterm_id);
+            if(!$result){
+               print "WARNING: failed to insert feature relationship '$uname' ($type) $rel_type '$parent' ($parent_type)\n";
+            } else {
+               print "Inserted relationship relationship: '$uname' ($type) $rel_type '$parent' ($parent_type)\n";
+            }
+         } 
+      }
+      else {
+         print "WARNING: cannot establish relationship '$uname' ($type) $rel_type '$parent' ($parent_type): Cannot find the parent\n";
+      }
+   }
+   tripal_db_set_active($previous_db);
+}
+

+ 1191 - 0
modules/base/tripal_feature/gff_loader.php

@@ -0,0 +1,1191 @@
+<?php
+
+/**
+ * @defgroup gff3_loader GFF3 Feature Loader
+ * @{
+ * Provides gff3 loading functionality. Creates features based on their specification in a GFF3 file.
+ * @}
+ * @ingroup tripal_feature
+ */
+// TODO: The rank column on the feature_relationship table needs to be used to
+//       make sure the ordering of CDS (exons) is correct.
+
+// The entries in the GFF file are not in order so the order of the relationships
+// is not put in correctly.
+/**
+ *
+ *
+ * @ingroup gff3_loader
+ */
+function tripal_feature_gff3_load_form (){
+
+   $form['gff_file']= array(
+      '#type'          => 'textfield',
+      '#title'         => t('GFF3 File'),
+      '#description'   => t('Please enter the full system path for the GFF file, or a path within the Drupal
+                             installation (e.g. /sites/default/files/xyz.gff).  The path must be accessible to the
+                             server on which this Drupal instance is running.'),
+      '#required' => TRUE,
+      '#weight'        => 1
+   );
+   // get the list of organisms
+   $sql = "SELECT * FROM {organism} ORDER BY genus, species";
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $org_rset = db_query($sql);
+   tripal_db_set_active($previous_db);  // now use drupal database
+   $organisms = array();
+   $organisms[''] = '';
+   while($organism = db_fetch_object($org_rset)){
+      $organisms[$organism->organism_id] = "$organism->genus $organism->species ($organism->common_name)";
+   }
+   $form['organism_id'] = array (
+     '#title'       => t('Organism'),
+     '#type'        => t('select'),
+     '#description' => t("Choose the organism to which these sequences are associated "),
+     '#required'    => TRUE,
+     '#options'     => $organisms,
+   );
+   $form['import_options'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Import Options'),
+      '#weight'=> 6,
+      '#collapsed' => TRUE
+   );
+   $form['import_options']['add_only']= array(
+      '#type' => 'checkbox',
+      '#title' => t('Import only new features'),
+      '#required' => FALSE,
+      '#description' => t('The job will skip features in the GFF file that already
+                           exist in the database and import only new features.'),
+      '#weight' => 2
+   );
+   $form['import_options']['update']= array(
+      '#type' => 'checkbox',
+      '#title' => t('Import all and update'),
+      '#required' => FALSE,
+      '#default_value' => 'checked',
+      '#description' => t('Existing features will be updated and new features will be added.  Attributes 
+                           for a feature that are not present in the GFF but which are present in the 
+                           database will not be altered.'),
+      '#weight' => 3
+   );
+   $form['import_options']['refresh']= array(
+      '#type' => 'checkbox',
+      '#title' => t('Import all and replace'),
+      '#required' => FALSE,
+      '#description' => t('Existing features will be updated and feature properties not
+                           present in the GFF file will be removed.'),
+      '#weight' => 4
+   );
+   $form['import_options']['remove']= array(
+      '#type' => 'checkbox',
+      '#title' => t('Delete features'),
+      '#required' => FALSE,
+      '#description' => t('Features present in the GFF file that exist in the database
+                           will be removed rather than imported'),
+      '#weight' => 5
+   );
+
+   $form['analysis'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Analysis Used to Derive Features'),
+      '#weight'=> 6,
+      '#collapsed' => TRUE
+   ); 
+   $form['analysis']['desc'] = array(
+      '#type' => 'markup',
+      '#value' => t("Why specify an analysis for a data load?  All data comes 
+         from some place, even if downloaded from Genbank. By specifying
+         analysis details for all data uploads, it allows an end user to reproduce the
+         data set, but at least indicates the source of the data."), 
+   );
+
+   // get the list of analyses
+   $sql = "SELECT * FROM {analysis} ORDER BY name";
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $org_rset = db_query($sql);
+   tripal_db_set_active($previous_db);  // now use drupal database
+   $analyses = array();
+   $analyses[''] = '';
+   while($analysis = db_fetch_object($org_rset)){
+      $analyses[$analysis->analysis_id] = "$analysis->name ($analysis->program $analysis->programversion, $analysis->sourcename)";
+   }
+   $form['analysis']['analysis_id'] = array (
+     '#title'       => t('Analysis'),
+     '#type'        => t('select'),
+     '#description' => t("Choose the analysis to which these features are associated "),
+     '#required'    => TRUE,
+     '#options'     => $analyses,
+   );
+
+   $form['button'] = array(
+      '#type' => 'submit',
+      '#value' => t('Import GFF3 file'),
+      '#weight' => 10,
+   );
+
+
+   return $form;
+}
+
+/**
+ *
+ *
+ * @ingroup gff3_loader
+ */
+function tripal_feature_gff3_load_form_validate ($form, &$form_state){
+
+   $gff_file = $form_state['values']['gff_file'];
+   $organism_id = $form_state['values']['organism_id'];
+   $add_only = $form_state['values']['add_only'];
+   $update   = $form_state['values']['update'];
+   $refresh  = $form_state['values']['refresh'];
+   $remove   = $form_state['values']['remove'];
+
+   // check to see if the file is located local to Drupal
+   $dfile = $_SERVER['DOCUMENT_ROOT'] . base_path() . $gff_file; 
+   if(!file_exists($dfile)){
+      // if not local to Drupal, the file must be someplace else, just use
+      // the full path provided
+      $dfile = $gff_file;
+   }
+   if(!file_exists($dfile)){
+      form_set_error('gff_file',t("Cannot find the file on the system. Check that the file exists or that the web server has permissions to read the file."));
+   }
+
+   if (($add_only and ($update or $refresh or $remove)) or 
+       ($update   and ($add_only or $refresh or $remove)) or
+       ($refresh and ($update or $add_only or $remove)) or
+       ($remove and ($update or $refresh or $add_only))){
+       form_set_error('add_only',t("Please select only one checkbox from the import options section"));
+   }
+}
+
+/**
+ *
+ * @ingroup gff3_loader
+ */
+function tripal_feature_gff3_load_form_submit ($form, &$form_state){
+   global $user;
+
+   $gff_file = $form_state['values']['gff_file'];
+   $organism_id = $form_state['values']['organism_id'];
+   $add_only = $form_state['values']['add_only'];
+   $update   = $form_state['values']['update'];
+   $refresh  = $form_state['values']['refresh'];
+   $remove   = $form_state['values']['remove'];
+   $analysis_id = $form_state['values']['analysis_id'];
+
+   $args = array($gff_file,$organism_id,$analysis_id,$add_only,$update,$refresh,$remove);
+   $type = '';
+   if($add_only){
+     $type = 'import only new features';
+   }
+   if($update){
+     $type = 'import all and update';
+   }
+   if($refresh){
+     $type = 'import all and replace';
+   }
+   if($remove){
+     $type = 'delete features';
+   }
+   tripal_add_job("$type GFF3 file $gff_file",'tripal_feature',
+      'tripal_feature_load_gff3',$args,$user->uid);
+
+   return '';
+}
+
+/**
+ *
+ *
+ * @ingroup gff3_loader
+ */
+function tripal_feature_load_gff3($gff_file, $organism_id,$analysis_id,$add_only =0, 
+   $update = 0, $refresh = 0, $remove = 0, $job = NULL)
+{
+
+   // this array is used to cache all of the features in the GFF file and
+   // used to lookup parent and target relationships
+   $gff_features = array();
+ 
+   // check to see if the file is located local to Drupal
+   $dfile = $_SERVER['DOCUMENT_ROOT'] . base_path() . $gff_file; 
+   if(!file_exists($dfile)){
+      // if not local to Drupal, the file must be someplace else, just use
+      // the full path provided
+      $dfile = $gff_file;
+   }
+   if(!file_exists($dfile)){
+      print "ERROR: cannot find the file: $dfile\n";
+      return 0;
+   }
+
+   $previous_db = tripal_db_set_active('chado');
+   print "Opening $gff_file\n";
+    
+   //$lines = file($dfile,FILE_SKIP_EMPTY_LINES);
+   $fh = fopen($dfile,'r');
+   if(!$fh){
+      print "ERROR: cannot open file: $dfile\n";
+      return 0;
+   }
+   $filesize = filesize($dfile);
+
+   // get the controlled vocaubulary that we'll be using.  The
+   // default is the 'sequence' ontology
+   $sql = "SELECT * FROM cv WHERE name = '%s'";
+   $cv = db_fetch_object(db_query($sql,'sequence'));
+   if(!$cv){
+      print "ERROR:  cannot find the 'sequence' ontology\n";
+      return '';
+   }
+
+   // get the organism for which this GFF3 file belongs
+   $sql = "SELECT * FROM organism WHERE organism_id = %d";
+   $organism = db_fetch_object(db_query($sql,$organism_id));
+
+   $interval = intval($filesize * 0.01);
+   if($interval == 0){
+      $interval = 1;
+   }
+   $in_fasta = 0;
+//   foreach ($lines as $line_num => $line) {
+   $line_num = 0;
+   $num_read = 0;
+
+   while($line = fgets($fh)){
+
+      $line_num++;
+      $num_read += strlen($line);
+
+      // update the job status every 1% features
+      if($job and $num_read % $interval == 0){
+         tripal_job_set_progress($job,intval(($num_read/$filesize)*100));
+      }
+      // check to see if we have FASTA section, if so then set the variable
+      // to start parsing
+      if(preg_match('/^##FASTA/i',$line)){
+         $in_fasta = 1;
+         break;
+      }
+      // skip comments
+      if(preg_match('/^#/',$line)){
+         continue; 
+      }
+      // skip empty lines
+      if(preg_match('/^\s*$/',$line)){
+         continue; 
+      }
+      
+
+      // handle FASTA section
+      
+      // TODO: handle URL encoding
+
+      // remove URL encoding and get the columns
+      $cols = explode("\t",$line);
+      if(sizeof($cols) != 9){
+         print "ERROR: improper number of columns on line $line_num\n";
+         print_r($cols);
+         return '';
+      }
+      // get the column values
+      $landmark = $cols[0];
+      $source = $cols[1];
+      $type = $cols[2];
+      $start = $cols[3];    
+      $end = $cols[4];
+      $score = $cols[5];
+      $strand = $cols[6];
+      $phase = $cols[7];
+      $attrs = explode(";",$cols[8]);  // split by a semi-colon
+
+      // ready the start and stop for chado.  Chado expects these positions
+      // to be zero-based, so we substract 1 from the fmin
+      $fmin = $start - 1;
+      $fmax = $end;
+      if($end < $start){
+         $fmin = $end - 1;
+         $fmax = $start;
+      }
+      // format the strand for chado
+      if(strcmp($strand,'.')==0){
+         $strand = 0;
+      }
+      elseif(strcmp($strand,'+')==0){
+         $strand = 1;
+      }
+      elseif(strcmp($strand,'-')==0){
+         $strand = -1;
+      }
+      if(strcmp($phase,'.')==0){
+         $phase = '';
+      }
+
+      // get the type record
+      $cvtermsql = "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 = %d and (CVT.name = '%s' or CVTS.synonym = '%s')";
+      $cvterm = db_fetch_object(db_query($cvtermsql,$cv->cv_id,$type,$type));
+      if(!$cvterm){
+         print "ERROR: cannot find ontology term '$type' on line $line_num.\n";
+         return '';
+      }
+
+      // break apart each of the attributes
+      $tags = array();
+      $attr_name = '';
+      $attr_uniquename = '';
+      $attr_residue_info = '';
+      $attr_locgroup = 0;
+      $attr_fmin_partial = 'f';
+      $attr_fmax_partial = 'f';
+      $attr_is_obsolete = 'f';
+      $attr_is_analysis = 'f';
+      $attr_others = '';
+      $residues = '';
+      foreach($attrs as $attr){
+         $attr = rtrim($attr);
+         $attr = ltrim($attr);
+         if(strcmp($attr,'')==0){
+            continue;
+         }
+         if(!preg_match('/^[^\=]+\=[^\=]+$/',$attr)){
+            print "ERROR: attribute is not correctly formatted on line $line_num: $attr\n";
+            return '';
+         }
+
+         // break apart each tag
+         $tag = explode("=",$attr);  // split by equals sign
+         // multiple instances of an attribute are separated by commas
+         $tags[$tag[0]] = explode(",",$tag[1]);  // split by comma
+         if(strcmp($tag[0],'ID')==0){
+            $attr_uniquename = $tag[1];
+         }
+         elseif(strcmp($tag[0],'Name')==0){
+            $attr_name = $tag[1];
+         }
+         // get the list of other attributes other than those reserved ones.
+         elseif(strcmp($tag[0],'Alias')!=0        and strcmp($tag[0],'Parent')!=0 and 
+                strcmp($tag[0],'Target')!=0       and strcmp($tag[0],'Gap')!=0 and
+                strcmp($tag[0],'Derives_from')!=0 and strcmp($tag[0],'Note')!=0 and
+                strcmp($tag[0],'Dbxref')!=0       and strcmp($tag[0],'Ontology_term')!=0 and
+                strcmp($tag[0],'Is_circular')!=0){
+            $attr_others[$tag[0]] = $tag[1];
+         }
+      }
+
+      // if neither name nor uniquename are provided then generate one
+      if(!$attr_uniquename and !$attr_name){
+         if(array_key_exists('Parent',$tags)){
+            $attr_uniquename = $tags['Parent'][0]."-$type-$landmark:$fmin..$fmax";
+         } else { 
+           print "ERROR: cannot generate a uniquename for feature on line $line_num\n";
+           exit;
+         }
+         $attr_name = $attr_uniquename;
+      }
+
+      // if a name is not specified then use the unique name
+      if(strcmp($attr_name,'')==0){
+         $attr_name = $attr_uniquename;
+      }
+
+      // if an ID attribute is not specified then use the attribute name and
+      // hope for the best
+      if(!$attr_uniquename){
+         $attr_uniquename = $attr_name;
+      }
+
+      // make sure the landmark sequence exists in the database.  We don't
+      // know the type of the landmark so we'll hope that it's unique across
+      // all types. If not we'll error out.  This test is only necessary if
+      // if the landmark and the uniquename are different.  If they are the same
+      // then this is the information for the landmark
+      if(strcmp($landmark,$attr_uniquename)!=0){
+         $feature_sql = "SELECT count(*) as num_landmarks
+                         FROM {feature} 
+                         WHERE organism_id = %d and uniquename = '%s'";
+         $count = db_fetch_object(db_query($feature_sql,$organism_id,$landmark));
+         if(!$count or $count->num_landmarks == 0){
+            print "ERROR: the landmark '$landmark' cannot be found for this organism. ". 
+                  "Please add the landmark and then retry the import of this GFF3 ".
+                  "file.\n";
+            return '';
+
+         }
+         if($count->num_landmarks > 1){
+            print "ERROR: the landmark '$landmark' is not unique for this organism. ".
+                  "The features cannot be associated.\n";
+            return '';
+         }
+      }
+      
+      // if the option is to remove or refresh then we want to remove
+      // the feature from the database.
+      if($remove or $refresh){
+         print "Removing feature '$attr_uniquename'\n";
+         $sql = "DELETE FROM {feature}
+                 WHERE organism_id = %d and uniquename = '%s' and type_id = %d";
+         $result = db_query($sql,$organism->organism_id,$attr_uniquename,$cvterm->cvterm_id); 
+         if(!$result){
+            print "ERROR: cannot delete feature $attr_uniquename\n";
+         }
+         $feature = 0; 
+      }
+
+      // add or update the feature and all properties
+      if($update or $refresh or $add_only){
+    
+
+         // add/update the feature
+         print "line $line_num, ". intval(($num_read/$filesize)*100). "%. ";
+         $feature = tripal_feature_load_gff3_feature($organism,$analysis_id,$cvterm,
+            $attr_uniquename,$attr_name,$residues,$attr_is_analysis,
+            $attr_is_obsolete, $add_only,$score);
+
+         // store all of the features for use later by parent and target
+         // relationships
+         $gff_features[$feature->uniquename]['type'] = $type;
+         $gff_features[$feature->uniquename]['strand'] = $strand;
+
+         if($feature){
+
+            // add/update the featureloc if the landmark and the ID are not the same
+            // if they are the same then this entry in the GFF is probably a landmark identifier
+            if(strcmp($landmark,$attr_uniquename)!=0){
+               tripal_feature_load_gff3_featureloc($feature,$organism,
+                  $landmark,$fmin,$fmax,$strand,$phase,$attr_fmin_partial,
+                  $attr_fmax_partial,$attr_residue_info,$attr_locgroup);
+            }
+            // add any aliases for this feature
+            if(array_key_exists('Alias',$tags)){
+               tripal_feature_load_gff3_alias($feature,$tags['Alias']);
+            }
+            // add any dbxrefs for this feature
+            if(array_key_exists('Dbxref',$tags)){
+               tripal_feature_load_gff3_dbxref($feature,$tags['Dbxref']);
+            }
+            // add any ontology terms for this feature
+            if(array_key_exists('Ontology_term',$tags)){
+               tripal_feature_load_gff3_ontology($feature,$tags['Ontology_term']);
+            }
+            // add parent relationships
+            if(array_key_exists('Parent',$tags)){
+               tripal_feature_load_gff3_parents($feature,$cvterm,$tags['Parent'],$gff_features,$organism_id,$fmin);
+            }
+            // add target relationships
+            if(array_key_exists('Target',$tags)){
+               $target = explode(" ",$tags['Target'][0]);
+               $target_feature = $target[0];
+               $target_start = $target[1];
+               $target_end = $target[2];
+               $target_dir = $target[3];
+               #print "Target: $target_feature, $target_start-$target_end\n";
+               tripal_feature_load_gff3_featureloc($feature,$organism,
+                  $target_feature,$target_start,$target_end,$strand,$phase,$attr_fmin_partial,
+                  $attr_fmax_partial,$attr_residue_info,$attr_locgroup);
+            }
+            // add gap information.  This goes in simply as a property 
+            if(array_key_exists('Gap',$tags)){
+               tripal_feature_load_gff3_property($feature,'Gap',$tags['Gap'][0]);
+            }
+            // add notes. This goes in simply as a property
+            if(array_key_exists('Note',$tags)){
+               tripal_feature_load_gff3_property($feature,'Note',$tags['Note'][0]);
+            }
+            // add the Derives_from relationship (e.g. polycistronic genes).
+            if(array_key_exists('Derives_from',$tags)){
+               tripal_feature_load_gff3_derives_from($feature,$tags['Derives_from'][0],$gff_features,$organism);
+            }
+
+            // add in the GFF3_source dbxref so that GBrowse can find the feature using the source column
+            $source_ref = array('GFF_source:'.$source);
+            tripal_feature_load_gff3_dbxref($feature,$source_ref);
+
+            // add any additional attributes
+            if($attr_others){
+               foreach($attr_others as $property => $value){
+                  tripal_feature_load_gff3_property($feature,$property,$value);
+               }
+            }
+         }
+      } 
+   }
+   // 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.
+   foreach($gff_features as $parent => $details){
+      // only iterate through parents that have children
+
+      if($details['children']){
+         // get the parent
+         $values = array(
+            'uniquename' => $parent,
+            'type_id' => array(
+               'cv_id' => array(
+                  'name' => 'sequence'
+               ),
+               'name' => $details['type'],
+            ),
+            'organism_id' => $organism->organism_id,
+         );
+         $pfeature = tripal_core_chado_select('feature',array('*'),$values);
+
+         // sort the children by order of their fmin positions (values of assoc. array)
+         // if the parent is on the reverse strand then sort in reverse
+         if($details['strand'] == -1){
+            arsort($details['children']); 
+         } else {
+            asort($details['children']); 
+         }
+
+         // now iterate through the children and set their rank
+         $rank = 1;
+         print "Updating child ranks for $parent (".$details['type'].")\n";
+         foreach($details['children'] as $kfeature_id => $kfmin){
+            $match = array(
+               'object_id' => $pfeature[0]->feature_id,
+               'subject_id' => $kfeature_id,
+               'type_id' => array(
+                  'cv_id' => array(
+                     'name' => 'relationship'
+                  ),
+                  'name' => 'part_of',
+               ),
+            );
+            $values = array(
+               'rank' => $rank,          
+            );
+            tripal_core_chado_update('feature_relationship',$match,$values);
+            $rank++;
+         }
+      }
+   }
+
+   tripal_db_set_active($previous_db);
+   return '';
+}
+/**
+ *
+ *
+ * @ingroup gff3_loader
+ */
+function tripal_feature_load_gff3_derives_from($feature,$subject,$gff_features,$organism){
+
+   // first get the subject feature
+   $match = array(
+      'organism_id' => $organism->organism_id,
+      'uniquename' => $subject,
+      'type_id' => array(
+         'name' => $gff_features[$subject]['type'],
+         'cv_id' => array(
+            'name' => 'sequence'
+         ),
+      ),
+   );
+   $sfeature = tripal_core_chado_select('feature',array('*'),$match);
+   if(count($sfeature)==0){
+      print "ERROR: could not add 'Derives_from' relationship for $feature->uniquename and $subject.  Subject feature, '$subject', cannot be found\n";
+      return;
+   }
+
+   // now check to see if the relationship already exists  
+   $values = array(
+      'object_id' => $sfeature[0]->feature_id,
+      'subject_id' => $feature->feature_id,
+      'type_id' => array(
+         'cv_id' => array(
+            'name' => 'relationship'
+         ),
+         'name' => 'derives_from',
+      ),
+      'rank' => 0
+   );
+   $rel = tripal_core_chado_select('feature_relationship',array('*'),$values);
+   if(count($rel) > 0){
+      print "   Relationship already exists: $feature->uniquename derives_from $subject\n";
+      return;
+   }
+
+   // finally insert the relationship if it doesn't exist
+   $ret = tripal_core_chado_insert('feature_relationship',$values);
+   if(!$ret){
+      print "ERROR: could not add 'Derives_from' relationship for $feature->uniquename and $subject\n";
+   } else {
+      print "   Added relationship: $feature->uniquename derives_from $subject\n";
+   }
+}
+/**
+ *
+ *
+ * @ingroup gff3_loader
+ */
+function tripal_feature_load_gff3_parents($feature,$cvterm,$parents,&$gff_features,$organism_id,$fmin){
+
+   $uname = $feature->uniquename;
+   $type = $cvterm->name;
+   $rel_type = 'part_of';
+
+
+   // create these SQL statements that will be used repeatedly below.
+   $cvtermsql = "SELECT CVT.cvterm_id
+                 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.name = '%s' and (CVT.name = '%s' or CVTS.synonym = '%s')";
+
+   $feature_sql = "SELECT * FROM {feature} 
+                   WHERE organism_id = %d and uniquename = '%s' and type_id = %d";
+
+   // iterate through the parents in the list
+   foreach($parents as $parent){  
+      $parent_type = $gff_features[$parent]['type'];
+
+      // try to find the parent
+      $parentcvterm = db_fetch_object(db_query($cvtermsql,'sequence',$parent_type,$parent_type));
+      $relcvterm = db_fetch_object(db_query($cvtermsql,'relationship',$rel_type,$rel_type));
+      $parent_feature = db_fetch_object(db_query($feature_sql,$organism_id,$parent,$parentcvterm->cvterm_id));
+
+      // we want to add this feature to the child list for the parent
+      // when the loader finishes, it will go back through the parent
+      // features and rank the children by position
+      $gff_features[$parent]['children'][$feature->feature_id] = $fmin;
+
+      // if the parent exists then add the relationship otherwise print error and skip
+      if($parent_feature){
+
+         // check to see if the relationship already exists
+         $sql = "SELECT * FROM {feature_relationship} WHERE subject_id = %d and object_id = %d and type_id = %d";
+         $rel = db_fetch_object(db_query($sql,$feature->feature_id,$parent_feature->feature_id,$relcvterm->cvterm_id));
+         if($rel){
+            print "   Relationship already exists, skipping '$uname' ($type) $rel_type '$parent' ($parent_type)\n";
+         } else {  
+            // the relationship doesn't already exist, so add it.    
+            $sql = "INSERT INTO {feature_relationship} (subject_id,object_id,type_id)
+                    VALUES (%d,%d,%d)";
+            $result = db_query($sql,$feature->feature_id,$parent_feature->feature_id,$relcvterm->cvterm_id);
+            if(!$result){
+               print "WARNING: failed to insert feature relationship '$uname' ($type) $rel_type '$parent' ($parent_type)\n";
+            } else {
+               print "   Inserted relationship relationship: '$uname' ($type) $rel_type '$parent' ($parent_type)\n";
+            }
+         }          
+      }
+      else {
+         print "WARNING: cannot establish relationship '$uname' ($type) $rel_type '$parent' ($parent_type): Cannot find the parent\n";
+      }
+   }
+}
+
+/**
+ *
+ *
+ * @ingroup gff3_loader
+ */
+
+function tripal_feature_load_gff3_dbxref($feature,$dbxrefs){
+
+   // iterate through each of the dbxrefs
+   foreach($dbxrefs as $dbxref){
+
+      // get the database name from the reference.  If it doesn't exist then create one.
+      $ref = explode(":",$dbxref);
+      $dbname = $ref[0];
+      $accession = $ref[1];
+
+      // first look for the database name if it doesn't exist then create one.
+      // first check for the fully qualified URI (e.g. DB:<dbname>. If that
+      // can't be found then look for the name as is.  If it still can't be found
+      // the create the database
+      $db = tripal_core_chado_select('db',array('db_id'),array('name' => "DB:$dbname"));  
+      if(sizeof($db) == 0){
+         $db = tripal_core_chado_select('db',array('db_id'),array('name' => "$dbname"));      
+      }        
+      if(sizeof($db) == 0){
+         $ret = tripal_core_chado_insert('db',array('name' => $dbname, 
+           'description' => 'Added automatically by the GFF loader'));
+         if($ret){ 
+            print "   Added new database: $dbname\n";
+            $db = tripal_core_chado_select('db',array('db_id'),array('name' => "$dbname"));      
+         } else {
+            print "ERROR: cannot find or add the database $dbname\n";
+            return 0;
+         }
+      } 
+      $db = $db[0];
+       
+      // now check to see if the accession exists
+      $dbxref = tripal_core_chado_select('dbxref',array('dbxref_id'),array(
+         'accession' => $accession,'db_id' => $db->db_id));
+
+      // if the accession doesn't exist then we want to add it
+      if(sizeof($dbxref) == 0){
+         $ret = tripal_core_chado_insert('dbxref',array('db_id' => $db->db_id,
+            'accession' => $accession,'version' => ''));
+         $dbxref = tripal_core_chado_select('dbxref',array('dbxref_id'),array(
+            'accession' => $accession,'db_id' => $db->db_id));
+      }
+      $dbxref = $dbxref[0];
+
+      // check to see if this feature dbxref already exists
+      $fdbx = tripal_core_chado_select('feature_dbxref',array('feature_dbxref_id'),
+         array('dbxref_id' => $dbxref->dbxref_id,'feature_id' => $feature->feature_id));
+
+      // now associate this feature with the database reference if it doesn't
+      // already exist
+      if(sizeof($fdbx)==0){
+         $ret = tripal_core_chado_insert('feature_dbxref',array(
+            'feature_id' => $feature->feature_id,
+            'dbxref_id' => $dbxref->dbxref_id));
+         if($ret){
+            print "   Adding Dbxref $dbname:$accession\n";
+         } else {
+            print "ERROR: failed to insert Dbxref: $dbname:$accession\n";
+            return 0;
+         }
+      } else {
+         print "   Dbxref already associated, skipping $dbname:$accession\n";
+      }
+   }
+   return 1;
+}
+/**
+ *
+ *
+ * @ingroup gff3_loader
+ */
+function tripal_feature_load_gff3_ontology($feature,$dbxrefs){
+
+   // iterate through each of the dbxrefs
+   foreach($dbxrefs as $dbxref){
+
+      // get the database name from the reference.  If it doesn't exist then create one.
+      $ref = explode(":",$dbxref);
+      $dbname = $ref[0];
+      $accession = $ref[1];
+
+      // first look for the database name 
+      $db = tripal_core_chado_select('db',array('db_id'),array('name' => "DB:$dbname"));  
+      if(sizeof($db) == 0){
+         $db = tripal_core_chado_select('db',array('db_id'),array('name' => "$dbname"));      
+      }        
+      if(sizeof($db) == 0){
+         print "ERROR: Database, $dbname is missing for reference: $dbname:$accession\n";
+         return 0;
+      } 
+      $db = $db[0];
+       
+      // now check to see if the accession exists
+      $dbxref = tripal_core_chado_select('dbxref',array('dbxref_id'),array(
+         'accession' => $accession,'db_id' => $db->db_id));
+      if(sizeof($dbxref) == 0){
+         print "ERROR: Accession, $accession is missing for reference: $dbname:$accession\n";
+         return 0;
+      }
+      $dbxref = $dbxref[0];
+
+      // now check to see if the cvterm exists
+      $cvterm = tripal_core_chado_select('cvterm',array('cvterm_id'),array(
+         'dbxref_id' => $dbxref->dbxref_id));
+      // if it doesn't exist in the cvterm table, look for an alternate id
+      if(sizeof($cvterm) == 0){
+         $cvterm = tripal_core_chado_select('cvterm_dbxref',array('cvterm_id'),array(
+            'dbxref_id' => $dbxref->dbxref_id));
+      }
+      if(sizeof($cvterm) == 0){
+         print "ERROR: CVTerm is missing for reference: $dbname:$accession\n";
+         return 0;
+      }
+      $cvterm = $cvterm[0];
+      
+
+      // check to see if this feature cvterm already exists
+      $fcvt = tripal_core_chado_select('feature_cvterm',array('feature_cvterm_id'),
+         array('cvterm_id' => $cvterm->cvterm_id,'feature_id' => $feature->feature_id));
+
+      // now associate this feature with the cvterm if it doesn't already exist
+      if(sizeof($fcvt)==0){
+         $values = array(
+            'feature_id' => $feature->feature_id,
+            'cvterm_id' => $cvterm->cvterm_id,
+            'pub_id' => array(
+               'uniquename' => 'null',
+            ),
+         );
+         $ret = tripal_core_chado_insert('feature_cvterm',$values);
+
+         if($ret){
+            print "   Adding ontology term $dbname:$accession\n";
+         } else {
+            print "ERROR: failed to insert ontology term: $dbname:$accession\n";
+            return 0;
+         }
+      } else {
+         print "   Ontology term already associated, skipping $dbname:$accession\n";
+      }
+   }
+   return 1;
+}
+/**
+ *
+ *
+ * @ingroup gff3_loader
+ */
+function tripal_feature_load_gff3_alias($feature,$aliases){
+
+   // make sure we have a 'synonym_type' vocabulary
+   $sql = "SELECT * FROM {cv} WHERE name='synonym_type'";
+   $syncv = db_fetch_object(db_query($sql));
+   if(!$syncv){
+      $sql = "INSERT INTO {cv} (name,definition) VALUES ('synonym_type','')";
+      if(!db_query($sql)){
+         print("ERROR: Failed to add the synonyms type vocabulary");
+         return 0;
+      }
+      $syncv = db_fetch_object(db_query($sql));
+   }
+
+   // get the 'exact' cvterm, which is the type of synonym we're adding
+   $cvtsql = "
+      SELECT * FROM {cvterm} CVT
+         INNER JOIN {cv} CV ON CVT.cv_id = CV.cv_id
+      WHERE CVT.name = '%s' and CV.name = '%s'
+   ";
+   $syntype = db_fetch_object(db_query($cvtsql,'exact','synonym_type'));
+   if(!$syntype){
+      $term = array(
+         'name' => array('exact'),
+         'id' => array("internal:exact"),
+         'definition' => array(''),
+         'is_obsolete' => array(0),
+      );
+      $syntype = tripal_cv_add_cvterm($term,$syncv,0,1);
+      if(!$syntype){
+         print("Cannot add synonym type: internal:$type");
+         return 0;
+      }
+   }
+
+   // iterate through all of the aliases and add each one
+   foreach($aliases as $alias){
+      print "   Adding Alias $alias\n";
+
+      // check to see if the alias already exists in the synonym table
+      // if not, then add it
+      $synsql = "SELECT * FROM {synonym}
+                 WHERE name = '%s' and type_id = %d";
+      $synonym = db_fetch_object(db_query($synsql,$alias,$syntype->cvterm_id));
+      if(!$synonym){      
+         $sql = "INSERT INTO {synonym}
+                  (name,type_id,synonym_sgml)
+                 VALUES ('%s',%d,'%s')";
+         $result = db_query($sql,$alias,$syntype->cvterm_id,'');
+         if(!$result){
+            print "ERROR: cannot add alias $alias to synonym table\n";
+         }
+      }           
+      $synonym = db_fetch_object(db_query($synsql,$alias,$syntype->cvterm_id));
+
+
+      // check to see if we have a NULL publication in the pub table.  If not,
+      // then add one.
+      $pubsql = "SELECT * FROM {pub} WHERE uniquename = 'null'";
+      $pub = db_fetch_object(db_query($pubsql));
+      if(!$pub){
+         $sql = "INSERT INTO pub (uniquename,type_id) VALUES ('%s',
+                   (SELECT cvterm_id 
+                    FROM cvterm CVT
+                      INNER JOIN dbxref DBX on DBX.dbxref_id = CVT.dbxref_id
+                      INNER JOIN db DB on DB.db_id = DBX.db_id
+                    WHERE CVT.name = 'null' and DB.name = 'null')";
+         $result = db_query($sql,'null');
+         if(!$result){
+            print "ERROR: cannot add null publication needed for setup of alias\n";
+            return 0;
+         }
+      }
+      $pub = db_fetch_object(db_query($pubsql));
+
+      // check to see if the synonym exists in the feature_synonym table
+      // if not, then add it.
+      $synsql = "SELECT * FROM {feature_synonym}
+                 WHERE synonym_id = %d and feature_id = %d and pub_id = %d";
+      $fsyn = db_fetch_object(db_query($synsql,$synonym->synonym_id,$feature->feature_id,$pub->pub_id));
+      if(!$fsyn){      
+         $sql = "INSERT INTO {feature_synonym}
+                  (synonym_id,feature_id,pub_id)
+                 VALUES (%d,%d,%d)";
+         $result = db_query($sql,$synonym->synonym_id,$feature->feature_id,$pub->pub_id);
+         if(!$result){
+            print "ERROR: cannot add alias $alias to feature synonym table\n";
+            return 0;
+         }
+      } else {
+         print "   Synonym $alias already exists. Skipping\n";
+      }
+   }
+   return 1;
+}
+
+/**
+ *
+ *
+ * @ingroup gff3_loader
+ */
+function tripal_feature_load_gff3_feature($organism,$analysis_id,$cvterm,$uniquename,$name,
+   $residues,$is_analysis='f',$is_obsolete='f',$add_only,$score)  {
+
+   // check to see if the feature already exists
+   $feature_sql = "SELECT * FROM {feature} 
+                   WHERE organism_id = %d and uniquename = '%s' and type_id = %d";
+   $feature = db_fetch_object(db_query($feature_sql,$organism->organism_id,$uniquename,$cvterm->cvterm_id));
+
+   if(strcmp($is_obsolete,'f')==0){
+      $is_obsolete = 'false';
+   }
+   if(strcmp($is_analysis,'f')==0){
+      $is_analysis = 'false';
+   }
+
+
+   // insert the feature if it does not exist otherwise perform an update
+   if(!$feature){
+      print "Adding feature '$uniquename' ($cvterm->name)\n";
+      $isql = "INSERT INTO {feature} (organism_id, name, uniquename, residues, seqlen,
+                  md5checksum, type_id,is_analysis,is_obsolete)
+               VALUES(%d,'%s','%s','%s',%d, '%s', %d, %s, %s)";
+      $result = db_query($isql,$organism->organism_id,$name,$uniquename,$residues,strlen($residues),
+               md5($residues),$cvterm->cvterm_id,$is_analysis,$is_obsolete);
+      if(!$result){
+         print "ERROR: failed to insert feature '$uniquename' ($cvterm->name)\n";
+         return 0;
+      }
+   } 
+   elseif(!$add_only) {
+      print "Updating feature '$uniquename' ($cvterm->name)\n";
+      $usql = "UPDATE {feature} 
+               SET name = '%s', residues = '%s', seqlen = '%s', md5checksum = '%s',
+                  is_analysis = %s, is_obsolete = %s
+               WHERE organism_id = %d and uniquename = '%s' and type_id = %d";
+      $result = db_query($usql,$name,$residues,strlen($residues),md5($residues),$is_analysis,$is_obsolete,
+                   $organism_id,$uniquename,$cvterm->cvterm_id);
+      if(!$result){
+         print "ERROR: failed to update feature '$uniquename' ($cvterm->name)\n";
+         return 0;
+      }
+   }
+   else {
+      // the feature exists and we don't want to update it so return
+      // a value of 0.  This will stop all downstream property additions
+      print "Skipping existing feature: '$uniquename' ($cvterm->name).\n";
+      return 0;
+   }
+
+   // get the newly added feature
+   $feature = db_fetch_object(db_query($feature_sql,$organism->organism_id,$uniquename,$cvterm->cvterm_id));
+
+   // add the analysisfeature entry to the analysisfeature table if it doesn't already exist
+   $af_values = array(
+      'analysis_id' => $analysis_id, 
+      'feature_id' => $feature->feature_id
+   );
+   $afeature = tripal_core_chado_select('analysisfeature',array('analysisfeature_id'),$af_values,array('has_record'));
+   if(count($afeature)==0){
+      // if a score is avaialble then set that to be the significance field
+      if(strcmp($score,'.')!=0){
+        $af_values['significance'] = $score;
+      }
+      if(!tripal_core_chado_insert('analysisfeature',$af_values)){
+         print "ERROR: could not add analysisfeature record: $analysis_id, $feature->feature_id\n";
+      } else {
+         print "   Added analysisfeature record\n";
+      }
+   } else {
+      // if a score is available then set that to be the significance field
+      $new_vals = array();
+      if(strcmp($score,'.')!=0){
+        $new_vals['significance'] = $score;
+      } else {
+        $new_vals['significance'] = '__NULL__';
+      }
+      if(!$add_only){
+         $ret = tripal_core_chado_update('analysisfeature',$af_values,$new_vals);
+         if(!$ret){
+            print "ERROR: could not update analysisfeature record: $analysis_id, $feature->feature_id\n";
+         } else {
+            print "   Updated analysisfeature record\n";
+         } 
+      }
+   }
+
+   return $feature;
+}
+
+/**
+ *
+ *
+ * @ingroup gff3_loader
+ */
+function tripal_feature_load_gff3_featureloc($feature,$organism,$landmark,$fmin,
+   $fmax,$strand,$phase,$is_fmin_partial,$is_fmax_partial,$residue_info,$locgroup)
+{
+ 
+   // get the source feature
+   $sql = "SELECT * FROM {feature} 
+           WHERE organism_id = %d and uniquename = '%s'";
+   $srcfeature = db_fetch_object(db_query($sql,$organism->organism_id,$landmark));
+   if(!$srcfeature){
+      print "ERROR: cannot find landmark feature $landmark.  Cannot add the feature location record\n";
+      return 0;
+   }
+
+
+   // TODO: create an attribute that recognizes the residue_info,locgroup, is_fmin_partial and is_fmax_partial, right now these are
+   //       hardcoded to be false and 0 below.
+
+
+   // check to see if this featureloc already exists, but also keep track of the
+   // last rank value
+   $rank = 0;  
+   $exists = 0;  
+   $featureloc_sql = "SELECT FL.featureloc_id,FL.fmin,FL.fmax,F.uniquename as srcname,
+                         rank
+                      FROM {featureloc} FL
+                        INNER JOIN {feature} F on F.feature_id = FL.srcfeature_id
+                      WHERE FL.feature_id = %d
+                      ORDER BY rank ASC";
+   $recs = db_query($featureloc_sql,$feature->feature_id);
+   while ($featureloc = db_fetch_object($recs)){
+      if(strcmp($featureloc->srcname,$landmark)==0 and
+         $featureloc->fmin == $fmin and $featureloc->fmax == $fmax){
+         // this is the same featureloc, so do nothing... no need to update
+         //TODO: need more checks here
+         print "   No change to featureloc\n";
+         $exists = 1;
+      }
+      $rank = $featureloc->rank + 1;
+   }
+   if(!$exists){
+      $rank++;
+      // this feature location is new so add it
+      if(!$phase){
+          $phase = 'NULL';
+      }
+      if(strcmp($is_fmin_partial,'f')==0){
+         $is_fmin_partial = 'false';
+      }
+      elseif(strcmp($is_fmin_partial,'t')==0){
+         $is_fmin_partial = 'true';
+      }
+      if(strcmp($is_fmax_partial,'f')==0){
+         $is_fmax_partial = 'false';
+      }
+      elseif(strcmp($is_fmax_partial,'t')==0){
+         $is_fmax_partial = 'true';
+      }
+      print "   Adding featureloc $srcfeature->uniquename fmin: $fmin, fmax: $fmax, strand: $strand, phase: $phase, rank: $rank\n";
+      $fl_isql = "INSERT INTO {featureloc} 
+                    (feature_id, srcfeature_id, fmin, is_fmin_partial, fmax, is_fmax_partial,
+                     strand, phase, residue_info, locgroup, rank) 
+                 VALUES (%d,%d,%d,%s,%d,%s,%d,%s,'%s',%d,%d)";
+      $result = db_query($fl_isql,$feature->feature_id,$srcfeature->feature_id,$fmin,$is_fmin_partial,$fmax,$is_fmax_partial,
+               $strand,$phase,$residue_info,$locgroup,$rank);
+      if(!$result){
+         print "ERROR: failed to insert featureloc\n";
+         exit;
+         return 0;
+      }
+   }
+   return 1;
+}
+/**
+ *
+ *
+ * @ingroup gff3_loader
+ */
+function tripal_feature_load_gff3_property($feature,$property,$value){
+   // first make sure the cvterm exists.  If the term already exists then
+   // the function should return it of not, then add it
+   $cvt_sql = "SELECT * FROM {cvterm} CVT
+               INNER JOIN {cv} CV on CVT.cv_id = CV.cv_id
+               WHERE CV.name = '%s' and CVT.name = '%s'";
+   $cvterm = db_fetch_object(db_query($cvt_sql,'feature_property',$property));
+   if(!$cvterm){
+      $term = array(
+         'id' => "null:$property",
+         'name' => $property,
+         'namespace' => 'feature_property', 
+         'is_obsolete' => 0,
+      );
+      print "   Adding cvterm, $property\n";
+      $cvterm = (object) tripal_cv_add_cvterm($term,'feature_property',0,0);
+   }
+
+   if(!$cvterm){
+      print "ERROR: cannot add cvterm, $property\n";
+      exit;
+   }
+
+   // check to see if the property already exists for this feature
+   // if it does but the value is unique then increment the rank and add it. 
+   // if the value is not unique then don't add it.
+   $add = 1;
+   $rank = 0;
+   $sql = "SELECT rank,value FROM {featureprop} 
+           WHERE feature_id = %d and type_id = %d
+           ORDER BY rank ASC";
+   $result = db_query($sql,$feature->feature_id,$cvterm->cvterm_id);
+   while($prop = db_fetch_object($result)){
+      if(strcmp($prop->value,$value)==0){
+        $add = NULL; // don't add it, it already exists
+        print "   Property already exists, skipping\n";
+      }
+      $rank = $prop->rank + 1;
+   }
+   
+   // add the property if we pass the check above
+   if($add){
+      print "   Setting feature property. $property: $value\n";
+      $isql = "INSERT INTO {featureprop} (feature_id,type_id,value,rank)
+               VALUES (%d,%d,'%s',%d)";
+      db_query($isql,$feature->feature_id,$cvterm->cvterm_id,$value,$rank);
+   }
+}
+/*
+function tripal_feature_load_gff3_property($feature,$property,$value){
+   // first make sure the cvterm exists.  If the term already exists then
+   // the function should return it
+   $match = array(
+      'name' => $property,
+      'cv_id' => array(
+         'name' => 'feature_property',
+      ),
+   );
+   $cvterm = tripal_core_chado_select('cvterm',array('*'),$match);
+   if(sizeof($cvterm) == 0){
+      $term = array(
+         'id' => "null:$property",
+         'name' => $property,
+         'namespace' => 'feature_property', 
+         'is_obsolete' => 0,
+      );
+      print "   Adding cvterm, $property\n";
+      $cvterm = tripal_cv_add_cvterm($term,'feature_property',0,0);
+   }
+
+   if(!$cvterm){
+      print "ERROR: cannot add cvterm, $property, before adding property\n";
+      exit;
+   }
+
+   // next give the feature the property
+   tripal_core_insert_property('feature',$feature->feature_id,$property,'feature_property',$value,1);
+}
+*/
+

+ 170 - 0
modules/base/tripal_feature/indexFeatures.php

@@ -0,0 +1,170 @@
+<?php
+
+// This script can be run as a stand-alone script to sync all the features from chado to drupal
+//
+// To index a single feature
+// -i feature_id
+// -n node_id 
+//
+// To index all features
+// -i 0 
+
+$arguments = getopt("i:n:");
+
+if(isset($arguments['i'])){
+   $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['i'];
+   $nid        = $arguments['n'];
+
+   # print "\n";
+   # print "feature id is $feature_id\n";
+   # print "nid is $nid\n";
+   # print "\n";
+
+   if($feature_id > 0){ 
+      # print "indexing feature $feature_id\n";
+     // We register a shutdown function to ensure that the nodes
+     // that are indexed will have proper entries in the search_totals
+     // table.  Without these entries, the searching doesn't work
+     // properly. This function may run for quite a while since
+     // it must calculate the sum of the scores of all entries in
+     // the search_index table.  In the case of common words like
+     // 'contig', this will take quite a while
+      register_shutdown_function('search_update_totals');
+      tripal_feature_index_feature($feature_id, $nid); 
+   }
+   else{ 
+      print "indexing all features...\n";
+      tripal_features_reindex(0);
+   }
+
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_features_reindex ($max_sync,$job_id = NULL){
+   $i = 0;
+
+   // We register a shutdown function to ensure that the nodes
+   // that are indexed will have proper entries in the search_totals
+   // table.  Without these entries, the searching doesn't work
+   // properly. This function may run for quite a while since
+   // it must calculate the sum of the scores of all entries in
+   // the search_index table.  In the case of common words like
+   // 'contig', this will take quite a while
+   register_shutdown_function('search_update_totals');
+
+   // use this SQL statement to get the features that we're going to index. This
+   // SQL statement is derived from the hook_search function in the Drupal API.
+   // Essentially, this is the SQL statement that finds all nodes that need
+   // reindexing, but adjusted to include the chado_feature
+   $sql = "SELECT N.nid, N.title, CF.feature_id ".
+          "FROM {node} N ".
+          "  INNER JOIN chado_feature CF ON CF.nid = N.nid ";
+   $results = db_query($sql);
+
+   // load into ids array
+   $count = 0;
+   $chado_features = array();
+   while($chado_feature = db_fetch_object($results)){
+      $chado_features[$count] = $chado_feature;
+      $count++;
+   }
+
+   // Iterate through features that need to be indexed 
+   $interval = intval($count * 0.01);
+   if($interval >= 0){
+      $interval = 1;
+   }
+   foreach($chado_features as $chado_feature){
+
+      // update the job status every 1% features
+      if($job_id and $i % $interval == 0){
+         $prog = intval(($i/$count)*100);
+         tripal_job_set_progress($job_id,$prog);
+         print "$prog%\n";
+      }
+
+      // sync only the max requested
+      if($max_sync and $i == $max_sync){
+         return '';
+      }
+      $i++;
+
+      # tripal_feature_index_feature ($chado_feature->feature_id,$chado_feature->nid);
+      # 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
+
+      $cmd = "php " . drupal_get_path('module', 'tripal_feature') . "/indexFeatures.php ";
+      $cmd .= "-i $chado_feature->feature_id -n $chado_feature->nid ";
+
+      # print "\t$cmd\n";
+      # print "\tfeature id is $chado_feature->feature_id\n";
+      # print "\tnid is $chado_feature->nid\n";
+      # print "\n";
+
+      system($cmd);
+   }
+
+   return '';
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_index_feature ($feature_id,$nid){
+   #print "\tfeature $feature_id nid $nid\n";
+   // return if we haven't been provided with a feature_id
+   if(!$feature_id){
+      return 0;
+   }
+
+   // if we only have a feature_id then let's find a corresponding
+   // node.  If we can't find a node then return.
+   if(!$nid){
+      $nsql = "SELECT N.nid,N.title FROM {chado_feature} CF ".
+              "  INNER JOIN {node} N ON N.nid = CF.nid ".
+              "WHERE CF.feature_id = %d";
+      $node = db_fetch_object(db_query($nsql,$feature_id));
+      if(!$node){
+         return 0;
+      }
+      $node = node_load($node->nid);
+   } else {
+      $node = node_load($nid);
+   }
+
+   // node load the noad, the comments and the taxonomy and
+   // index
+   $node->build_mode = NODE_BUILD_SEARCH_INDEX;
+   $node = node_build_content($node, FALSE, FALSE);
+   $node->body = drupal_render($node->content);
+   node_invoke_nodeapi($node, 'view', FALSE, FALSE);
+//   $node->body .= module_invoke('comment', 'nodeapi', $node, 'update index');
+//   $node->body .= module_invoke('taxonomy','nodeapi', $node, 'update index');
+   //   print "$node->title: $node->body\n";
+   search_index($node->nid,'node',$node->body);
+
+   # $mem = memory_get_usage(TRUE);
+   # $mb = $mem/1048576;
+   # print "$mb mb\n";
+
+   return 1;
+}
+
+?>

+ 418 - 0
modules/base/tripal_feature/syncFeatures.php

@@ -0,0 +1,418 @@
+<?php
+
+
+# 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:");
+
+if(isset($arguments['f'])){
+   $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 ".
+         "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 ".
+         "features to be synced by a specific organism. Depending on the ".
+         "number of features in the chado database this may take a long ".
+         "time to complete. "),
+   );
+
+   $form['feature_types'] = array(
+      '#title'       => t('Feature Types'),
+      '#type'        => 'textarea',
+      '#description' => t('Enter the names of the sequence types that the ".
+         "site will support with independent pages.  Pages for these data ".
+         "types will be built automatically for features that exist in the ".
+         "chado database.  The names listed here should be spearated by ".
+         "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'),
+   );
+
+   // get the list of organisms
+   $sql = "SELECT * FROM {organism} ORDER BY genus, species";
+   $orgs = tripal_organism_get_synced(); 
+   $organisms[] = ''; 
+   foreach($orgs as $organism){
+      $organisms[$organism->organism_id] = "$organism->genus $organism->species ($organism->common_name)";
+   }
+   $form['organism_id'] = array (
+     '#title'       => t('Organism'),
+     '#type'        => t('select'),
+     '#description' => t("Choose the organism for which features will be deleted."),
+     '#options'     => $organisms,
+   );
+
+
+   $form['button'] = array(
+      '#type' => 'submit',
+      '#value' => t('Sync all Features'),
+      '#weight' => 3,
+   );
+
+   return $form;
+}
+/**
+*
+*/
+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
+}
+/**
+*
+*/
+function tripal_feature_sync_form_submit ($form, &$form_state){
+
+   global $user;
+
+   $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');
+   }
+
+   variable_set('chado_sync_feature_types',$feature_types);
+
+   tripal_add_job($title,'tripal_feature',
+         'tripal_feature_sync_features',$job_args,$user->uid);
+}
+/**
+*
+*/   
+function tripal_feature_set_urls($job_id = NULL){
+   // first get the list of features that have been synced
+   $sql = "SELECT * FROM {chado_feature}";
+   $nodes = db_query($sql);
+   while($node = db_fetch_object($nodes)){
+      // now get the feature details
+      $feature_arr = tripal_core_chado_select('feature',
+         array('feature_id','name','uniquename'),
+         array('feature_id' => $node->feature_id));
+      $feature = $feature_arr[0];
+
+      tripal_feature_set_feature_url($node,$feature);
+   }
+}
+/**
+*
+*/
+function tripal_feature_set_feature_url($node,$feature){
+
+   // determine which URL alias to use
+   $alias_type = variable_get('chado_feature_url','internal ID');
+   $aprefix = variable_get('chado_feature_accession_prefix','ID');
+   switch ($alias_type) {
+      case 'feature name':
+         $url_alias = $feature->name;
+         break;
+      case 'feature unique name':
+         $url_alias = $feature->uniquename;
+         break;
+      default:
+         $url_alias = "$aprefix$feature->feature_id";
+   }
+   print "Setting $alias_type as URL alias for $feature->name: node/$node->nid => $url_alias\n";
+   // remove any previous alias
+   db_query("DELETE FROM {url_alias} WHERE src = '%s'", "node/$node->nid");
+   // add the new alias
+   path_set_alias("node/$node->nid",$url_alias);
+}
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_sync_features ($max_sync = 0, $organism_id = NULL, 
+   $feature_types = NULL, $job_id = NULL)
+{
+   //print "Syncing features (max of $max_sync)\n";
+   $i = 0;
+
+   // 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');
+   } else {
+      $allowed_types = $feature_types;
+   }
+   $allowed_types = preg_replace("/[\s\n\r]+/"," ",$allowed_types);
+
+   print "Looking for features of type: $allowed_types\n";
+
+   $so_terms = split(' ',$allowed_types);
+   $where_cvt = "";
+   foreach ($so_terms as $term){
+      $where_cvt .= "CVT.name = '$term' OR ";
+   }
+   $where_cvt = substr($where_cvt,0,strlen($where_cvt)-3);  # strip trailing 'OR'
+
+   // get the list of organisms that are synced and only include features from
+   // those organisms
+   $orgs = tripal_organism_get_synced();
+   $where_org = "";
+   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 ";
+         }
+      } 
+      else {
+         if($org->organism_id){
+            $where_org .= "F.organism_id = $org->organism_id OR ";
+         }
+      }
+   }
+   $where_org = substr($where_org,0,strlen($where_org)-3);  # strip trailing 'OR'
+
+   // use this SQL statement to get the features that we're going to upload
+   $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' ".
+          "ORDER BY feature_id";
+
+   // get the list of features
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $results = db_query($sql);
+
+   tripal_db_set_active($previous_db);  // now use drupal database
+
+   // load into ids array
+   $count = 0;
+   $ids = array();
+   while($id = db_fetch_object($results)){
+      $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 = %d";
+
+   // Iterate through features that need to be synced
+   $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_fetch_object(db_query($sql,$feature_id))){
+        
+         # 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') . "/syncFeatures.php -f $feature_id ";
+         system($cmd);
+
+      }
+      $i++;
+   }
+
+   return '';
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_sync_feature ($feature_id){
+//   print "\tSyncing feature $feature_id\n";
+
+   $mem = memory_get_usage(TRUE);
+   $mb = $mem/1048576;
+//   print "$mb mb\n";
+
+   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','ID');
+
+   // 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 = %d";
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $feature = db_fetch_object(db_query($fsql,$feature_id));
+   tripal_db_set_active($previous_db);  // now use drupal database
+
+   // 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 = %d";
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $synonyms = db_query($synsql,$feature_id);
+   tripal_db_set_active($previous_db);  // now use drupal database
+
+   // now add these synonyms to the feature object as a single string   
+   $synstring = '';
+   while($synonym = db_fetch_object($synonyms)){
+      $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 = '%s'";
+   $cnsql = "SELECT * FROM {chado_feature} ".
+            "WHERE nid = %d";
+   $nodes = db_query($tsql,$feature->name);
+   // cycle through all nodes that may have this title
+   while($node = db_fetch_object($nodes)){
+      $feature_nid = db_fetch_object(db_query($cnsql,$node->nid));
+      if(!$feature_nid){
+         drupal_set_message(t("$feature_id: A node is present but the chado_feature entry is missing... correcting"));
+         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 = %d";
+   $nsql =  "SELECT * FROM {node} ".
+            "WHERE nid = %d";
+   $chado_feature = db_fetch_object(db_query($cfsql,$feature->feature_id));
+   if($chado_feature){
+      drupal_set_message(t("$feature_id: A chado_feature entry exists"));
+      $node = db_fetch_object(db_query($nsql,$chado_feature->nid));
+      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"));
+         $df_sql = "DELETE FROM {chado_feature} WHERE feature_id = %d";
+         db_query($df_sql,$feature_id);
+      } else {
+         drupal_set_message(t("$feature_id: A corresponding node exists"));
+         $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 = %d";
+      $organism = db_fetch_object(db_query($sql,$feature->organism_id));
+
+      drupal_set_message(t("$feature_id: Creating node $feature->name"));
+      $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()) {
+         foreach($errors as $key => $msg){
+            drupal_set_message($msg);
+         }
+         return $errors;
+      } 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"));
+   tripal_feature_set_taxonomy($node,$feature_id);
+
+   // reindex the node
+   // drupal_set_message(t("$feature_id( $node->nid): indexing"));
+   // tripal_feature_index_feature ($feature_id,$node->nid);
+
+   // set the URL alias for this node
+   tripal_feature_set_feature_url($node,$feature);
+
+
+   return '';
+}
+?>

+ 362 - 0
modules/base/tripal_feature/tripal_feature-db_references.inc

@@ -0,0 +1,362 @@
+<?php
+
+/**
+ * 
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_add_ALL_dbreferences_page($node) {
+  $output = '';
+
+  $output .= tripal_feature_implement_add_chado_properties_progress('db_references').'<br>';
+  $output .= '<b>All Database References should strictly pertain to THE CURRENT Individual</b><br>';
+  $output .= '<br><b>Current Database References</b><br>';
+  $output .= list_dbreferences_for_node($node->db_references);
+  $output .= '<br><br>';
+  $output .= drupal_get_form('tripal_feature_add_ONE_dbreference_form', $node);
+  $output .= '<br>';
+  $output .= drupal_get_form('tripal_feature_implement_add_chado_properties_navigate', 'db_references', $node->nid);
+  return $output;
+}
+
+/**
+ * Implements Hook_form()
+ * Handles adding of Database References to features
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_add_ONE_dbreference_form($form_state, $node) {
+
+  $form['nid'] = array(
+    '#type' => 'hidden',
+    '#value' => $node->nid
+  );
+
+  $form['feature_id'] = array(
+    '#type' => 'hidden',
+    '#value' => $node->feature->feature_id,
+  );
+
+  $form['add_dbreference'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Add Database References') . '<span class="form-optional" title="This field is optional">(optional)</span>',
+  );
+
+  $form['add_dbreference']['accession'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Accession'),
+    '#required' => TRUE,
+  );
+
+  $form['add_dbreference']['description'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Description of Reference') . '<span class="form-optional" title="This field is optional">+</span>',
+    '#description' => t('Optionally enter a description about the database accession.'),
+  );
+
+  $db_options = tripal_db_get_db_options();
+  $db_options[0] = 'Select a Database';
+  ksort($db_options);
+  $form['add_dbreference']['db_id'] = array(
+    '#type' => 'select',
+    '#title' => t('Database'),
+    '#options' => $db_options,
+    '#required' => TRUE,
+  );
+
+  $form['add_dbreference']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Add Database Reference')
+  );
+
+  return $form;
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_add_ONE_dbreference_form_validate($form, &$form_state) {
+
+   $db_id = $form_state['values']['db_id'];
+   $accession = $form_state['values']['accession'];
+   $description = $form_state['values']['description'];
+   $feature_id = $form_state['values']['feature_id'];
+   $nid = $form_state['values']['nid'];
+
+   // Check database is valid db_id in chado
+   $previous_db = tripal_db_set_active('chado');
+   $tmp_obj = db_fetch_object(db_query("SELECT count(*) as count FROM db WHERE db_id=%d",$db_id));
+   tripal_db_set_active($previous_db);    	
+   if ($tmp_obj->count != 1) {
+      form_set_error('database', 'The database you selected is not valid. Please choose another one.'); 
+   }
+
+   // Check Accession is unique for database
+   $previous_db = tripal_db_set_active('chado');
+   $sql = "SELECT count(*) as count FROM dbxref WHERE accession='%s' and db_id = %d";
+   $tmp_obj = db_fetch_object(db_query($sql,$accession,$db_id));
+   tripal_db_set_active($previous_db);
+
+   if ($tmp_obj->count > 0) {
+      form_set_error('accession', 'This accession has already been assigned to another feature in the selected database.'); 
+   }
+
+} 
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_add_ONE_dbreference_form_submit($form, &$form_state) {
+
+   $db_id = $form_state['values']['db_id'];
+   $accession = $form_state['values']['accession'];
+   $description = $form_state['values']['description'];
+   $feature_id = $form_state['values']['feature_id'];
+   $nid = $form_state['values']['nid'];
+
+   // create dbxref
+   $previous_db = tripal_db_set_active('chado');
+   $isql =  "INSERT INTO dbxref (db_id, accession, description) VALUES (%d, '%s', '%s')";
+   db_query($isql,$db_id,$accession,$description);
+   tripal_db_set_active($previous_db);
+
+   //create feature_dbxref
+   $dbxref = tripal_db_get_dbxref( array('db_id'=>array('type'=>'INT','value'=>$form_state['values']['db_id']), 
+ 											'accession'=>array('type'=>'STRING','exact'=>TRUE,'value'=>$form_state['values']['accession']) ) );
+
+	if (!empty($dbxref->dbxref_id)) {
+		$previous_db = tripal_db_set_active('chado');
+      $isql = "INSERT INTO feature_dbxref (feature_id, dbxref_id) VALUES (%d, %d)";
+ 	   db_query($isql,$feature_id,$dbxref->dbxref_id);
+		tripal_db_set_active($previous_db);
+    	drupal_set_message('Successfully Added Database Reference');
+      drupal_goto('node/'.$nid);
+   } else {
+ 	   drupal_set_message('Database reference NOT successfully created...','error');
+   } //end of if dbxref was created successfully
+
+}
+
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_edit_ALL_dbreferences_page($node) {
+  $output = '';
+
+  $output .= drupal_get_form('tripal_feature_edit_ALL_db_references_form', $node);
+  $output .= '<br>';
+  $output .= drupal_get_form('tripal_feature_add_ONE_dbreference_form', $node);
+  $output .= '<br>';
+  $output .= drupal_get_form('tripal_feature_implement_back_to_feature_button', $node->nid);
+
+  return $output;
+}
+
+/**                                           
+ * Implements Hook_form()                      
+ * Handles adding of DB References to Features
+ *
+ * @ingroup tripal_feature
+ */                                        
+function tripal_feature_edit_ALL_db_references_form($form_state, $node) {
+  $form = array();
+
+  $form['nid'] = array(
+    '#type' => 'hidden',
+    '#value' => $node->nid
+  );
+
+  $i=0;
+
+  $feature = $node->feature;
+  $references = tripal_feature_load_references ($feature->feature_id);
+
+  // pre populate the database options
+  $db_options = tripal_db_get_db_options();
+  $db_options[0] = 'Select a Database';
+  ksort($db_options);
+
+  if (sizeof($references) != 0) {
+     foreach ($references as $ref) {
+       $i++;
+       $form["num-$i"] = array(
+         '#type' => 'fieldset',
+         '#title' => t("Database Reference")." $i"
+       );
+
+       $form["num-$i"]["accession-$i"] = array(
+         '#type' => 'textfield',
+         '#title' => t('Accession'),
+         '#size' => 30,
+         '#required' => TRUE,
+         '#default_value' => $ref->accession
+       );
+
+       $form["num-$i"]["db_id-$i"] = array( 
+         '#type' => 'select', 
+         '#title' => t('Database'),
+         '#options' => $db_options,
+         '#required' => TRUE, 
+         '#default_value' => $ref->db_id
+       );
+
+
+       $form["num-$i"]["dbxref_id-$i"] = array(
+         '#type' => 'hidden',
+         '#value' => $ref->dbxref_id
+       );
+
+       $form["num-$i"]["delete-$i"] = array(
+         '#type' => 'submit',
+         '#value' => t("Delete"),
+         '#name' => "delete-$i",
+       );
+
+     }
+
+     $form['num_db_references'] = array(
+       '#type' => 'hidden',
+       '#value' => $i
+     );
+
+     $form["submit-edits"] = array(
+       '#type' => 'submit',
+       '#value' => t('Update All References')
+     );
+  } //end of foreach db ref
+  return $form;
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_edit_ALL_db_references_form_submit($form, &$form_state) {
+
+  $num_refs = $form_state['values']['num_db_references'];
+  $action = $form_state['clicked_button']['#value'];
+  $button = $form_state['clicked_button']['#name'];
+  $nid = $form_state['values']['nid'];
+
+  if (strcmp($action,'Update All References')==0) {
+     for ($i=1; $i<=$num_refs; $i++) {
+		 $dbxref_id = $form_state['values']["dbxref_id-$i"]; 
+		 $db_id = $form_state['values']["db_id-$i"];
+		 $accession = $form_state['values']["accession-$i"];
+       tripal_feature_update_db_reference($dbxref_id,$db_id,$accession);
+     }
+     drupal_set_message("Updated all Database References");
+     drupal_goto('node/'.$nid);
+  } 
+  elseif (strcmp($action,'Delete')==0){
+     if(preg_match('/delete-(\d+)/', $button, $matches) ) {
+        $i = $matches[1];
+	     $dbxref_id = $form_state['values']["dbxref_id-$i"]; 
+        tripal_feature_delete_db_reference($dbxref_id);
+        drupal_set_message("Deleted Database Reference");
+        drupal_goto('node/'.$nid);
+     } else {
+        drupal_set_message("Could not remove database reference: ",'error');
+     }
+  } 
+  else {
+    drupal_set_message("Unrecognized Button Pressed",'error');
+  }
+
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_update_db_reference($dbxref_id, $db_id, $accession) {
+
+  $previous_db = tripal_db_set_active('chado');
+  $sql =  "UPDATE dbxref SET db_id=%d, accession='%s' WHERE dbxref_id=%d";
+  db_query($sql,$db_id,$accession,$dbxref_id);
+  tripal_db_set_active($previous_db);
+
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_delete_db_reference($dbxref_id) {
+
+  $previous_db = tripal_db_set_active('chado');
+  db_query(
+    "DELETE FROM dbxref WHERE dbxref_id=%d",
+    $dbxref_id
+  );
+
+  db_query(
+    "DELETE FROM feature_dbxref WHERE dbxref_id=%d",
+    $dbxref_id
+  );
+  tripal_db_set_active($previous_db);
+
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function theme_tripal_feature_edit_ALL_db_references_form ($form) {
+  $output = '';
+
+  $output .= '<br><fieldset>';
+  $output .= '<legend>Edit Existing Database References<span class="form-optional" title="This field is optional">(optional)</span></legend>';
+  $output .= '<p>Below is a list of already existing database references, one per line. When entering a database reference, the accession '
+  	     .'is a unique identifier for this feature in the specified database.</p>';
+  $output .= '<table>';
+  $output .= '<tr><th>#</th><th>Database</th><th>Accession</th><th></th></tr>';
+
+  for ($i=1; $i<=$form['num_db_references']['#value']; $i++) {
+    $output .= '<tr><td>'.drupal_render($form["num-$i"]).'</td><td>'
+    	       .drupal_render($form["database-$i"]).'</td><td>'
+	       .drupal_render($form["accession-$i"]).'</td><td>'
+	       .drupal_render($form["submit-$i"]).'</td></tr>';
+  }
+
+  $output .= '</table><br>';
+  $output .= drupal_render($form);
+  $output .= '</fieldset>';
+
+  return $output;
+}
+
+/**
+ * 
+ *
+ * @ingroup tripal_feature
+ */
+function list_dbreferences_for_node($db_references) {
+
+  if (!empty($db_references) ) {
+    $output = '<table>';
+    $output .= '<tr><th>Database</th><th>Accession</th></tr>';
+
+    foreach ($db_references as $db) {
+        $output .= '<tr><td>'.$db->db_name.'</td><td>'.$db->accession.'</td></tr>';
+    } // end of foreach db reference 
+
+    $output .= '</table>'; 
+
+  } else {
+    $output = 'No Database References Added to the Current Feature';
+  }              
+
+  return $output;
+}     

+ 178 - 0
modules/base/tripal_feature/tripal_feature-delete.inc

@@ -0,0 +1,178 @@
+<?php
+
+
+function tripal_feature_delete_form (){
+   // get the list of organisms
+   $sql = "SELECT * FROM {organism} ORDER BY genus, species";
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $org_rset = db_query($sql);
+   tripal_db_set_active($previous_db);  // now use drupal database
+   $organisms = array();
+   $organisms[''] = '';
+   while($organism = db_fetch_object($org_rset)){
+      $organisms[$organism->organism_id] = "$organism->genus $organism->species ($organism->common_name)";
+   }
+   $form['desc'] = array(
+      '#type' => 'markup',
+      '#value' => t("Use one or more of the following fields to identify sets of features to be deleted."), 
+   );
+
+   $form['feature_names']= array(
+      '#type' => 'textarea',
+      '#title' => t('Feature Names'),
+      '#description' => t('Please provide a list of feature names or unique names, 
+         separated by spaces or by new lines to be delete. If you specify feature names then
+         all other options below will be ignored (except the unique checkbox).'),
+   );
+	$form['is_unique'] = array(
+      '#title' => t('Names are Unique Names'),
+      '#type' => 'checkbox',
+      '#description' => t('Select this checbox if the names listed in the feature 
+        names box above are the unique name of the feature rather than the human readable names.'),
+	);
+   $form['seq_type']= array(
+      '#type' => 'textfield',
+      '#title' => t('Sequence Type'),
+      '#description' => t('Please enter the Sequence Ontology term that describes the features to be deleted. Use in conjunction with an organism or anaylysis.'),
+   );
+
+   $form['organism_id'] = array (
+     '#title'       => t('Organism'),
+     '#type'        => t('select'),
+     '#description' => t("Choose the organism for which features will be deleted."),
+     '#options'     => $organisms,
+   );
+
+
+   // get the list of analyses
+   $sql = "SELECT * FROM {analysis} ORDER BY name";
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $org_rset = db_query($sql);
+   tripal_db_set_active($previous_db);  // now use drupal database
+   $analyses = array();
+   $analyses[''] = '';
+   while($analysis = db_fetch_object($org_rset)){
+      $analyses[$analysis->analysis_id] = "$analysis->name ($analysis->program $analysis->programversion, $analysis->sourcename)";
+   }
+//  TODO: ADD THIS BACK IN LATER
+//
+//   $form['analysis']['analysis_id'] = array (
+//     '#title'       => t('Analysis'),
+//     '#type'        => t('select'),
+//     '#description' => t("Choose the analysis for which associated features will be deleted."),
+//     '#options'     => $analyses,
+//   );
+
+   $form['button'] = array(
+      '#type' => 'submit',
+      '#value' => t('Delete Features'),
+   );
+   return $form;
+}
+
+function tripal_feature_delete_form_validate ($form, &$form_state){
+   $organism_id   = $form_state['values']['organism_id'];
+   $seq_type      = trim($form_state['values']['seq_type']);
+   $analysis_id   = $form_state['values']['analysis_id'];
+   $is_unique     = $form_state['values']['is_unique'];
+   $feature_names = $form_state['values']['feature_names'];
+
+   if (!$organism_id and !$anaysis_id and !$seq_type and !$feature_names){
+      form_set_error('feature_names',t("Please select at least one option"));
+   }
+
+   // check to make sure the types exists
+   if($seq_type){
+      $cvtermsql = "SELECT CVT.cvterm_id
+                    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.name = '%s' and (CVT.name = '%s' or CVTS.synonym = '%s')";
+      $cvterm = db_fetch_object(db_query($cvtermsql,'sequence',$seq_type,$seq_type));
+      if(!$cvterm){
+         form_set_error('seq_type',t("The Sequence Ontology (SO) term selected for the sequence type is not available in the database. Please check spelling or select another."));
+      }
+   }
+}
+
+function tripal_feature_delete_form_submit ($form, &$form_state){
+   global $user;
+
+   $organism_id   = $form_state['values']['organism_id'];
+   $seq_type      = trim($form_state['values']['seq_type']);
+   $analysis_id   = $form_state['values']['analysis_id'];
+   $is_unique     = $form_state['values']['is_unique'];
+   $feature_names = $form_state['values']['feature_names'];
+
+   $args = array($organism_id,$analysis_id,$seq_type,$is_unique,$feature_names);
+
+   tripal_add_job("Delete features",'tripal_feature',
+      'tripal_feature_delete_features',$args,$user->uid);
+}
+
+
+function tripal_feature_delete_features($organism_id,$analysis_id,$seq_type,
+   $is_unique,$feature_names,$job = NULL)
+{
+
+   global $user;
+   $match = array();   
+
+   // if feature names have been provided then handle that separately
+   if($feature_names){
+      $names = preg_split('/\s+/',$feature_names);
+      if(sizeof($names) == 1){
+         $names = $names[0];
+      }
+      if($is_unique){
+         $match['uniquename'] = $names;
+      } else {
+         $match['name'] = $names;
+      }
+      $num_deletes = tripal_core_chado_select('feature',array('count(*) as cnt'),$match);
+      print "Deleting ".$num_deletes[0]->cnt ." features\n"; 
+      tripal_core_chado_delete('feature',$match);
+   }
+
+   // if the user has provided an analysis_id then handle that separately
+   elseif($analysis_id){
+      tripal_feature_delete_by_analysis();
+   }
+   else {
+
+
+      if($organism_id){
+         $match['organism_id'] = $organism_id;
+      }
+      if($seq_type){
+         $match['type_id'] = array(
+            'name' => $seq_type,
+            'cv_id' => array(
+               'name' => 'sequence'
+            ),
+         );
+      }
+      $num_deletes = tripal_core_chado_select('feature',array('count(*) as cnt'),$match);
+      print "Deleting ".$num_deletes[0]->cnt ." features\n"; 
+      tripal_core_chado_delete('feature',$match);
+   }
+
+   print "Removing orphaned feature pages\n";
+   tripal_features_cleanup(array(),$user->uid);
+}
+
+function tripal_feature_delete_by_analysis($organism_id,$analysis_id,$seq_type,
+   $is_unique,$feature_names,$job = NULL)
+{
+
+}
+
+
+
+
+
+
+
+
+
+

+ 358 - 0
modules/base/tripal_feature/tripal_feature-properties.inc

@@ -0,0 +1,358 @@
+<?php
+
+/**
+ * Implements Hook_form()
+ * Handles adding of Properties for features
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_getfeature_add_ALL_property_page($node) {
+  $output = '';
+
+  $output .= tripal_feature_implement_add_chado_properties_progress('properties').'<br>';
+  $output .= '<br><b>Current Properties</b><br>';
+  $output .= tripal_feature_list_properties_for_node($node->properties);
+  $output .= '<br><br>';
+  $output .= drupal_get_form('tripal_feature_add_ONE_property_form', $node);
+  $output .= '<br>';
+  $output .= drupal_get_form('tripal_feature_implement_add_chado_properties_navigate', 'properties', $node->nid);
+  return $output;
+}
+
+/**
+ * 
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_add_ONE_property_form($form_state, $node) {
+  $form = array();
+  $feature_id = $node->feature->feature_id;
+
+  $form['add_properties'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Add Property'),
+  ); 
+
+  $form['prop_nid'] = array(
+    '#type' => 'hidden',
+    '#value' => $node->nid
+  );
+	
+  $prop_type_options = tripal_cv_get_cvterm_options( variable_get('chado_feature_prop_types_cv', 'null') ); 
+  $prop_type_options[0] = 'Select a Type';
+  ksort($prop_type_options);
+  $form['add_properties']['prop_type_id'] = array(
+    '#type' => 'select',
+    '#title' => t('Type of Property'),
+    '#options' => $prop_type_options,
+  );
+
+  $form['add_properties']['prop_value'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Value'),
+  );
+
+  $form['add_properties']['prop_feature_id'] = array(
+    '#type' => 'value',
+    '#value' => $feature_id,
+    '#required' => TRUE
+  );
+
+  $form['add_properties']['submit-add'] = array(
+    '#type' => 'submit',         
+    '#value' => t('Add Property')
+  );
+
+  return $form;
+}
+
+/**
+ * 
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_add_ONE_property_form_validate($form, &$form_state) {
+
+  // Only Require if Adding Property
+  if ($form_state['clicked_button']['#value'] == t('Add Property') ) {
+
+		// Check that there is a feature
+		if ( $form_state['values']['prop_feature_id'] <= 0 ) {
+			form_set_error('prop_feature_id', 'There is no associated feature.');
+		}
+		
+    // Check that Selected a type
+    if ( $form_state['values']['prop_type_id'] == 0) {
+      form_set_error('prop_type_id', 'Please select a type of property.');
+    } else {
+      // Check that type is in chado
+      $previous_db = tripal_db_set_active('chado');
+      $num_rows = db_fetch_object(db_query("SELECT count(*) as count FROM cvterm WHERE cvterm_id=%d", $form_state['values']['prop_type_id']));
+      tripal_db_set_active($previous_db);
+      if ( $num_rows->count != 1) {
+        form_set_error('prop_type_id', "The type you selected is not valid. Please choose another one. (CODE:$num_rows)");
+      } // end of if more or less than 1 row
+    } // if no prop type
+
+
+
+  } // if add Property
+
+}
+
+/**
+ * 
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_add_ONE_property_form_submit($form, &$form_state) {
+	
+  // if there is a property add it (only won't be a property if clicked next step w/ no property)
+  if ($form_state['values']['prop_type_id'] != 0) {
+    //determine the rank for this property
+    $max_rank = get_max_chado_rank('featureprop', 
+       array('feature_id'=>array('type'=>'INT','value'=>$form_state['values']['prop_feature_id']), 
+       'type_id'=>array('type'=>'INT','value'=> $form_state['values']['prop_type_id']) ));
+    if ($max_rank == -1) { $rank = 0; 
+    } else { $rank = $max_rank+1; }
+    
+    $previous_db = tripal_db_set_active('chado');
+    db_query(
+      "INSERT INTO featureprop (feature_id, type_id, value, rank) VALUES (%d, %d, '%s', %d)",
+      $form_state['values']['prop_feature_id'],
+      $form_state['values']['prop_type_id'],
+      $form_state['values']['prop_value'],
+      $rank
+    );
+    tripal_db_set_active($previous_db);
+
+    drupal_set_message("Successfully Added Property");
+
+  } //end of if property to add
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_edit_ALL_properties_page($node) {
+  $output = '';
+
+  $output .= drupal_get_form('tripal_feature_edit_ALL_properties_form', $node);
+  $output .= '<br>';
+  $output .= drupal_get_form('tripal_feature_add_ONE_property_form', $node);
+  $output .= '<br>';
+  $output .= drupal_get_form('tripal_feature_implement_back_to_feature_button', $node->nid);
+
+  return $output;
+}
+
+/**
+ * Implements Hook_form()
+ * Handles adding of Properties for features
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_edit_ALL_properties_form($form_state, $node) {
+  $form = array();
+
+  $form['nid'] = array(
+    '#type' => 'hidden',
+    '#value' => $node->nid
+  );
+
+  $i=0;
+  $feature = $node->feature;
+  $properties = tripal_feature_load_properties ($feature->feature_id);
+
+  if (sizeof($properties) != 0) {
+     foreach ($properties as $property) {
+       $i++;
+       $form["num-$i"] = array(
+         '#type' => 'fieldset',
+         '#value' => "Property $i"
+       );
+
+       $form["num-$i"]["id-$i"] = array(
+         '#type' => 'hidden',
+         '#value' => $property->featureprop_id
+       );
+
+       $prop_type_options = tripal_cv_get_cvterm_options( variable_get('chado_feature_prop_types_cv', 'null') );
+       ksort($prop_type_options);
+    
+       $default = array_search($property->type, $prop_type_options);
+
+       $form["num-$i"]["type-$i"] = array(
+         '#type' => 'select',
+         //'#title' => t('Type of Property'),
+         '#options' => $prop_type_options,
+         '#default_value' => $default 
+       );
+
+       $form["num-$i"]["value-$i"] = array(
+         '#type' => 'textfield',
+         //'#title' => t('Value'),
+         '#default_value' => $property->value
+       );
+
+       $form["num-$i"]["delete-$i"] = array(
+         '#type' => 'submit',
+         '#value' => t("Delete"),
+         '#name' => "delete-$i",
+       );
+     }//end of foreach property
+  } 
+
+  $form['num_properties'] = array(
+    '#type' => 'hidden',
+    '#value' => $i
+  );
+
+  $form["submit-edits"] = array(
+    '#type' => 'submit',
+    '#value' => t('Update All Properties')
+  );
+
+  return $form;
+}
+
+/**
+ * 
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_edit_ALL_properties_form_submit($form, &$form_state) {
+
+  if ($form_state['clicked_button']['#value'] == t('Update All Properties') ) {
+     //Update all
+     for ($i=1; $i<=$form_state['values']['num_properties']; $i++) {
+       tripal_feature_update_property($form_state['values']["id-$i"], $form_state['values']["type-$i"], $form_state['values']["value-$i"], $form_state['values']["preferred-$i"], $form_state['values']["nid"]);
+     }
+     drupal_set_message("Updated all Properties");
+     drupal_goto('node/'.$form_state['values']['nid']);
+  } elseif ( preg_match('/Delete #(\d+)/', $form_state['clicked_button']['#value'], $matches) ) {
+     $i = $matches[1];
+     tripal_feature_delete_property($form_state['values']["id-$i"], $form_state['values']["type-$i"], $form_state['values']["value-$i"]);
+     drupal_set_message("Deleted Property");
+  } else {
+    drupal_set_message("Unrecognized Button Pressed",'error');
+  }
+
+  
+}
+
+/**
+ * 
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_update_property($featureprop_id, $cvterm_id, $value, $preferred, $nid) {
+
+	$previous_db = tripal_db_set_active('chado');
+	$old_obj = db_fetch_object(db_query("SELECT * FROM featureprop WHERE featureprop_id=%d",$featureprop_id));
+	tripal_db_set_active($previous_db);
+	
+	// if they changed the type need to check rank
+	//   (if there is another property of the same type then rank needs to be increased to prevent collisions)
+	if ($cvterm_id == $old_obj->type_id) {
+	  $previous_db = tripal_db_set_active('chado');
+  	db_query(
+    	"UPDATE featureprop SET type_id=%d, value='%s' WHERE featureprop_id=%d",
+    	$cvterm_id, 
+    	$value,
+    	$featureprop_id
+  	);
+  	tripal_db_set_active($previous_db);
+  } else {
+      //determine the rank for this property
+    $max_rank = get_max_chado_rank('featureprop', 
+       array('feature_id'=>array('type'=>'INT','value'=> $old_obj->feature_id), 
+    	 'type_id'=>array('type'=>'INT','value'=> $cvterm_id ) ));
+    if ($max_rank == -1) { $rank = 0; 
+    } else { $rank = $max_rank+1; }
+	  $previous_db = tripal_db_set_active('chado');
+  	db_query(
+    	"UPDATE featureprop SET type_id=%d, value='%s', rank=%d WHERE featureprop_id=%d",
+    	$cvterm_id, 
+    	$value,
+    	$rank,
+    	$featureprop_id
+  	);
+  	tripal_db_set_active($previous_db);  	
+  }
+                                                                                                                                                                                                                          
+    module_load_include('inc', 'node', 'node.pages');                                                                                                                                                                                
+    drupal_execute('chado_feature_node_form', $node_form_state, $node);  
+
+}
+
+/**
+ * 
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_delete_property($featureprop_id) {
+
+  $previous_db = tripal_db_set_active('chado');
+  db_query(
+    "DELETE FROM featureprop WHERE featureprop_id=%d",
+    $featureprop_id
+  );
+  tripal_db_set_active($previous_db);
+
+}
+
+/**
+ * 
+ *
+ * @ingroup tripal_feature
+ */
+function theme_tripal_feature_edit_ALL_properties_form ($form) {
+  $output = '';
+
+  $output .= '<br><fieldset>';
+  $output .= '<legend>Edit Already Existing Properties<span class="form-optional" title="This field is optional">(optional)</span></legend>';
+  $output .= '<p>Below is a list of already existing properties for this feature, one property per line. The type refers to the type of '
+  	     .'property and the value is the value for that property. </p>';
+  $output .= '<table>';
+  $output .= '<tr><th>#</th><th>Type</th><th>Value</th><th></th></tr>';
+
+  for ($i=1; $i<=$form['num_properties']['#value']; $i++) {
+    $output .= '<tr><td>'.drupal_render($form["num-$i"]).'</td><td>'.drupal_render($form["type-$i"]).'</td><td>'.drupal_render($form["value-$i"]).drupal_render($form["preferred-$i"]).'</td><td>'.drupal_render($form["submit-$i"]).'</td></tr>';
+  }
+
+  $output .= '</table><br>';
+  $output .= drupal_render($form);
+  $output .= '</fieldset>';
+
+  return $output;
+}
+
+/**
+ * 
+ *
+ * @ingroup tripal_feature
+ */
+function list_properties_for_node($properties) {
+
+  if (!empty($properties)) {
+    $output = '<table>';
+    $output .= '<tr><th>Type</th><th>Value</th></tr>';
+
+
+    if (!empty($properties) ) {
+      foreach ($properties as $p) {
+        $output .= '<tr><td>'.$p->type.'</td><td>'.$p->value.'</td></tr>';
+      } // end of foreach property
+    }
+
+    $output .= '</table>';
+
+  } else {
+    $output = 'No properties exist forony the current feature';
+  }
+
+  return $output;
+}

+ 528 - 0
modules/base/tripal_feature/tripal_feature-relationships.inc

@@ -0,0 +1,528 @@
+<?php
+
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_add_ALL_relationships_page($node) {
+  $output = '';
+
+  $output .= tripal_feature_implement_add_chado_properties_progress('relationships').'<br>';
+  $output .= '<b>All Relationships should include the CURRENT Individual ('.$node->uniquename.')</b><br>';
+  $output .= '<br><b>Current Relationships</b><br>';
+  $output .= list_relationships_for_node($node->uniquename, $node->subject_relationships, $node->object_relationships);
+  $output .= '<br><br>';
+  $output .= drupal_get_form('tripal_feature_add_ONE_relationship_form', $node);
+  $output .= '<br>';
+  $output .= drupal_get_form('tripal_feature_implement_add_chado_properties_navigate', 'relationships', $node->nid);
+  return $output;
+}
+
+/**
+ * Implements Hook_form()
+ * Handles adding of Relationships to Features
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_add_ONE_relationship_form($form_state, $node) {
+
+  $feature_id = $node->feature_id;
+  $organism_id = $node->organism->organism_id;
+  $_SESSION['organism'] = $organism_id; //needed for autocomplete enter feature to work
+
+  $form['rel_nid'] = array(
+    '#type' => 'hidden', 
+    '#value' => $node->nid
+  ); 
+
+  $form['add_relationships'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Add Relationships') . '<span class="form-optional" title="This field is optional">(optional)</span>',
+  );
+
+  $form['add_relationships']['description'] = array(
+    '#type' => 'item',
+    '#value' => t('Relationships are specified as follows: (Subject) (Type of Relationship) (Object). For example, X part_of Y, where X & Y are genomic features.')
+  );
+
+  $form['add_relationships']['subject_id'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Subject'),
+    '#description' => 'The Uniquename, Name, Database Reference or Synonym of a Feature can be used here',
+  );
+
+  $cv = tripal_cv_get_cv_by_name('relationship');
+  $type_options = tripal_cv_get_cvterm_options($cv->cv_id);
+  $type_options[0] = 'Select a Type';
+  ksort($type_options);
+  $form['add_relationships']['type_id'] = array(
+    '#type' => 'select',
+    '#title' => t('Type of Relationship'),
+    '#options' => $type_options
+
+  );
+
+  $form['add_relationships']['object_id'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Object'),
+    '#description' => 'The Uniquename, Name, Database Reference or Synonym of a Feature can be used here',
+  );
+
+  $form['add_relationships']['r_description'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Notes on the relationship') . '<span class="form-optional" title="This field is optional">+</span>',
+    '#description' => t('Should not include Genotypes and Phenotypes'),
+  );
+
+  $form['add_relationships']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Add a Relationship')
+  );
+
+  $form['add_relationships']['r_feature_id'] = array(
+    '#type' => 'value',
+    '#value' => $feature_id,
+    '#required' => TRUE
+
+  );
+
+  $form['add_relationships']['r_feature_uniquename'] = array( 
+    '#type' => 'value',
+    '#value' => $node->uniquename,
+    '#required' => TRUE
+  );
+
+  return $form;
+
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_add_ONE_relationship_form_validate($form, &$form_state) {
+
+  //Require Validation if adding
+  if ($form_state['clicked_button']['#value'] == t('Add a Relationship') ) {
+
+    // check valid feature selected for subject
+    $criteria = array('unknown' => array('value'=> $form_state['values']['subject_id'], 
+      																		'columns'=>array('name','uniquename','accession','synonym') ));
+    $subject_results = get_chado_stocks($criteria,'ANY',$_SESSION['organism']);
+    if (sizeof($subject_results) > 1) {
+      $links= array();
+      for ($i=0; $i<sizeof($subject_results); $i++) { $links[] = l($i+1, "node/".$subject_results[$i]->nid); }
+      $message = "Too many stocks match '".$form_state['values']['subject_id']."'! "
+      	       	 . " Please refine your input to match ONLY ONE stock. <br>"
+		 . "To aid in this process, here are the stocks that match your initial input: "
+		 .join(', ',$links);
+      form_set_error('subject_id', $message);
+    } elseif (sizeof($subject_results) < 1) {
+      form_set_error('subject_id', "There are no stocks matching your input. Please check your input for typos and/or lookup the stock ".l('here', 'stocks'));
+    } elseif (sizeof($subject_results) == 1) {
+      $form_state['values']['subject_id'] = $subject_results[0]->stock_id;
+    }
+
+    // check valid stock selected for object
+    $criteria = array('unknown' => array('value'=> $form_state['values']['object_id'], 
+      																		'columns'=>array('name','uniquename','accession','synonym') ));
+    $object_results = get_chado_stocks($criteria,'ANY',$_SESSION['organism']);
+    if (sizeof($object_results) > 1) {
+      $links= array();
+      for ($i=0; $i<sizeof($object_results); $i++) { $links[] = l($i+1, "node/".$object_results[$i]->nid); } 
+      $message = "Too many stocks match '".$form_state['values']['object_id']."'! "
+                 . "Please refine your input to match ONLY ONE stock. <br>"
+                 . "To aid in this process, here are the stocks that match your initial input: "
+                 .join(', ',$links);
+      form_set_error('object_id', $message);
+    } elseif (sizeof($object_results) < 1) {
+      form_set_error('object_id', "There are no stocks matching your input. Please check your input for typos and/or lookup the stock ".l('here', 'stocks'));                                                                         
+    } elseif (sizeof($object_results) == 1) {
+      $form_state['values']['object_id'] = $object_results[0]->stock_id;
+    }
+
+    // check valid type selected
+    if ($form_state['values']['type_id'] == 0) {
+      form_set_error('type_id', 'Please select a type of relationship.');
+    } else {
+    	$previous_db = tripal_db_set_active('chado');
+    	$tmp_obj = db_fetch_object(db_query("SELECT count(*) as count FROM cvterm WHERE cvterm_id=%d",$form_state['values']['type_id']));
+    	tripal_db_set_active($previous_db);
+    	
+      if ($tmp_obj->count != 1) {
+        form_set_error('type_id', 'The type you selected is not valid. Please choose another one.');
+      }
+    }
+
+    // check either subject or object is the current stock
+    if ( $subject_results[0]->nid != $form_state['values']['rel_nid'] ) {
+      if ( $object_results[0]->nid != $form_state['values']['rel_nid'] ) {
+        form_set_error('subject_id', 'Either Subject or Object must be the current stock ('.$form_state['values']['r_stock_uniquename'].').');
+      }
+    }
+  } //end of require validation if adding relationship
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_add_ONE_relationship_form_submit($form, &$form_state) {
+
+  if ($form_state['values']['subject_id'] > 0) {
+    $previous_db = db_set_active('chado');
+    db_query(
+      "INSERT INTO stock_relationship (subject_id, type_id, object_id, value) VALUES (%d, %d, %d, '%s')",
+      $form_state['values']['subject_id'],
+      $form_state['values']['type_id'],
+      $form_state['values']['object_id'],
+      $form_state['values']['r_description']
+    );
+    db_set_active($previous_db);
+
+    drupal_set_message('Successfully Added Relationship.');
+  } //end of insert relationship
+
+}
+
+/** 
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_edit_ALL_relationships_page($node) {
+  $output = '';
+	
+  $output .= drupal_get_form('tripal_feature_edit_ALL_relationships_form', $node);
+  $output .= '<br>';
+  $output .= drupal_get_form('tripal_feature_add_ONE_relationship_form', $node);
+  $output .= '<br>';
+  $output .= drupal_get_form('tripal_feature_implement_back_to_feature_button', $node->nid);
+
+  return $output;
+}
+                                                      
+/**                                                 
+ * Implements Hook_form()                             
+ * Handles adding of Properties & Synonyms to Stocks  
+ *
+ * @ingroup tripal_feature
+ */                                                   
+function tripal_feature_edit_ALL_relationships_form($form_state, $node) {
+  $form = array();
+
+  $form['nid'] = array(
+    '#type' => 'hidden',
+    '#value' => $node->nid
+  );
+  
+  $form['r_feature_uniquename'] = array(
+    '#type' => 'hidden',
+    '#value' => $node->uniquename
+  );
+
+  $i=0;
+
+  $feature = $node->feature;
+  $orelationships = tripal_feature_load_relationships ($feature->feature_id,'as_object');
+  $srelationships = tripal_feature_load_relationships ($feature->feature_id,'as_subject');
+  $relationships = array_merge($orelationships,$srelationships);
+
+  if (sizeof($relationships) != 0) {
+     foreach ($relationships as $r) {
+
+       $i++;
+       $form["num-$i"] = array(
+         '#type' => 'fieldset',
+         '#title' => "Relationship $i",
+       );
+
+       $form["num-$i"]["id-$i"] = array(
+         '#type' => 'hidden',
+         '#value' => $r->stock_relationship_id
+       );
+
+       //Enter relationship specific fields
+       if ( !empty($r->subject_id) ) { 
+         $default = $r->subject_uniquename;
+         $description = l($r->subject_name, 'node/'.$r->subject_nid); 
+       } else { 
+          $default = $node->uniquename; 
+          $description = "Current Feature"; 
+       }
+       $description .= " (".$r->subject_type.")";
+       $form["num-$i"]["subject_id-$i"] = array(
+         '#type' => 'textfield',      
+         //'#title' => t('Subject'), 
+         '#required'   => TRUE,
+         '#size' => 30,
+         '#default_value' => $default,
+         '#description' => $description,
+       ); 
+
+       $cv = tripal_cv_get_cv_by_name('relationship');
+       $type_options = tripal_cv_get_cvterm_options($cv->cv_id);
+       ksort($type_options);          
+       $form["num-$i"]["type_id-$i"] = array(  
+         '#type' => 'select',    
+         //'#title' => t('Type of Relationship'), 
+         '#options' => $type_options,
+         '#required' => TRUE,
+         '#default_value' => $r->relationship_type_id
+       );
+
+       if (!empty($r->object_id) ) { 
+         $default = $r->object_uniquename;
+         $description = l($r->object_name, 'node/'.$r->object_nid);
+       } else { 
+         $default = $node->uniquename; 
+         $description = 'Current Feature'; 
+       }
+       $description .= " (".$r->object_type.")";
+       $form["num-$i"]["object_id-$i"] = array(
+         '#type' => 'textfield',          
+         //'#title' => t('Object'),      
+         '#required'   => TRUE,
+         '#size' => 30,
+         '#default_value' => $default,
+         '#description' => $description
+       );
+
+       $form["num-$i"]["delete-$i"] = array(
+         '#type' => 'submit',
+         '#value' => t("Delete"),
+         '#name' => "delete-$i",
+       );
+
+     } //end of foreach relationship
+     $form['num_relationships'] = array(
+       '#type' => 'hidden',
+       '#value' => $i
+     );
+
+     $form["submit-edits"] = array(
+       '#type' => 'submit',
+       '#value' => t('Update All Relationships')
+     );
+  } else {
+     $form["info"] = array(
+       '#type' => 'markup',
+       '#value' => t('No relationships currently exist for this feature.')
+     );
+  } 
+
+
+
+  return $form;
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_edit_ALL_relationships_form_validate($form, &$form_state) {
+
+  // Only Require if Updating Relationships
+  if ($form_state['clicked_button']['#value'] == t('Update All Relationships') ) {
+
+    for ($i=1; $i<=$form_state['values']['num_relationships']; $i++) {
+      
+      // check valid stock selected for subject
+      $criteria = array('unknown' => array('value'=>$form_state['values']["subject_id-$i"], 
+      																		'columns'=>array('name','uniquename','accession','synonym') ));
+      $subject_results = get_chado_stocks($criteria,'ANY',$_SESSION['organism']);
+      if (sizeof($subject_results) > 1) {
+        $links= array();
+        for ($j=0; $j<sizeof($subject_results); $j++) { $links[] = l($j+1, "node/".$subject_results[$j]->nid); }
+        $message = "Too many stocks match '".$form_state['values']["subject_id-$i"]."'! "
+                 . "Please refine your input to match ONLY ONE stock. <br>" 
+                 . "To aid in this process, here are the stocks that match your initial input: "
+                 .join(', ',$links);
+        form_set_error("subject_id-$i", $message);
+      } elseif (sizeof($subject_results) < 1) { 
+        form_set_error("subject_id-$i", "There are no stocks matching your input. Please check your input for typos and/or lookup the stock ".l('here', 'stocks'));
+      } elseif (sizeof($subject_results) == 1) {
+        $form_state['values']["subject_id-$i"] = $subject_results[0]->stock_id;
+      } 
+
+      // check valid stock selected for object
+      $criteria = array('unknown' => array('value'=> $form_state['values']["object_id-$i"], 
+      																		'columns'=>array('name','uniquename','accession','synonym') ));
+      $object_results = get_chado_stocks($criteria,'ANY',$_SESSION['organism']);
+      if (sizeof($object_results) > 1) {
+        $links= array();
+        for ($j=0; $j<sizeof($object_results); $j++) { $links[] = l($j+1, "node/".$object_results[$j]->nid); }
+        $message = "Too many stocks match '".$form_state['values']["object_id-$i"]."'! "
+                 . "Please refine your input to match ONLY ONE stock. <br>" 
+                 . "To aid in this process, here are the stocks that match your initial input: "
+                 .join(', ',$links);
+        form_set_error("object_id-$i", $message);
+      } elseif (sizeof($object_results) < 1) {
+        form_set_error("object_id-$i", "There are no stocks matching your input. Please check your input for typos and/or lookup the stock ".l('here', 'stocks'));
+      } elseif (sizeof($object_results) == 1) {
+        $form_state['values']["object_id-$i"] = $object_results[0]->stock_id;
+      } 
+
+      // check valid type selected
+      if ($form_state['values']["type_id-$i"] == 0) {
+        form_set_error('type_id', 'Please select a type of relationship.');
+      } else {
+    		$previous_db = tripal_db_set_active('chado');
+    		$tmp_obj = db_fetch_object(db_query("SELECT count(*) as count FROM cvterm WHERE cvterm_id=%d",$form_state['values']["type_id-$i"]));
+    		tripal_db_set_active($previous_db);
+    	
+      	if ($tmp_obj->count != 1) {
+          form_set_error("type_id-$i", 'The type you selected is not valid. Please choose another one.');
+        }
+      }
+
+      // check either subject or object is the current stock
+      if ( $subject_results[0]->nid != $form_state['values']['nid'] ) {
+        if ( $object_results[0]->nid != $form_state['values']['nid'] ) {
+          form_set_error("subject_id-$i", 'Either Subject or Object must be the current stock ('.$form_state['values']['r_stock_uniquename'].').');
+        }
+      }
+
+    } // end of for each relationship
+  } //end of if updating relationships
+
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_edit_ALL_relationships_form_submit($form, &$form_state) {
+
+  if ($form_state['clicked_button']['#value'] == t('Update Relationships') ) {
+     //Update all
+     for ($i=1; $i<=$form_state['values']['num_relationships']; $i++) {
+
+       //process stock textfields
+       tripal_feature_update_relationship(
+				$form_state['values']["id-$i"], 
+				$form_state['values']["subject_id-$i"],
+				$form_state['values']["type_id-$i"], 
+				$form_state['values']["object_id-$i"]
+			);
+     }
+     drupal_set_message("Updated all Relationships");
+     drupal_goto('node/'.$form_state['values']['nid']);
+
+  } elseif ( preg_match('/Delete #(\d+)/', $form_state['clicked_button']['#value'], $matches) ) {
+
+     $i = $matches[1];
+     tripal_feature_delete_relationship($form_state['values']["id-$i"]);
+     drupal_set_message("Deleted Relationship");
+
+  } elseif ($form_state['clicked_button']['#value'] == t('Back to Stock') ) {
+    drupal_goto('node/'.$form_state['values']['nid']);
+  } else {
+    drupal_set_message("Unrecognized Button Pressed",'error');
+  }
+
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_update_relationship ($stock_relationship_id, $subject_id, $cvterm_id, $object_id) {
+
+  $previous_db = db_set_active('chado');
+  db_query(
+    "UPDATE stock_relationship SET subject_id=%d, type_id=%d, object_id=%d WHERE stock_relationship_id=%d",
+    $subject_id,
+    $cvterm_id, 
+    $object_id,
+    $stock_relationship_id
+  );
+  db_set_active($previous_db);
+
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_delete_relationship ($stock_relationship_id) {
+
+  $previous_db = db_set_active('chado');
+  db_query(
+    "DELETE FROM stock_relationship WHERE stock_relationship_id=%d",
+    $stock_relationship_id
+  );
+  db_set_active($previous_db);
+
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function theme_tripal_feature_edit_ALL_relationships_form ($form) {
+  $output = '';
+  
+  $output .= '<br><fieldset>';
+  $output .= '<legend>Edit Already Existing Relationships<span class="form-optional" title="This field is optional">(optional)</span></legend>';
+  $output .= '<p>Each relationship for this stock is listed below, one per line. The textboxes indicating '
+  	    .'the subject and object of the relationship can contain the uniquename, name, database '
+	    .'reference or synonym of a stock of the same organism.</p>';
+  $output .= '<table>';
+  $output .= '<tr><th>#</th><th>Subject</th><th>Type</th><th>Object</th><th></th></tr>';
+
+  for ($i=1; $i<=$form['num_relationships']['#value']; $i++) {
+    $output .= '<tr><td>'.drupal_render($form["num-$i"]).'</td>'.
+      	      '<td>'.drupal_render($form["subject_id-$i"]).'</td>'.
+	            '<td>'.drupal_render($form["type_id-$i"]).'</td>'.
+     	         '<td>'.drupal_render($form["object_id-$i"]).'</td>'.
+	            '<td>'.drupal_render($form["submit-$i"]).'</td></tr>';
+  }
+
+  $output .= '</table><br>';
+  $output .= drupal_render($form);
+  $output .= '</fieldset>';
+
+  return $output;
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_list_relationships_for_node($feature_name, $subject_relationships, $object_relationships) {
+
+  if (!empty($subject_relationships) OR !empty($object_relationships) ) {
+    $output = '<table>';
+    $output .= '<tr><th>Subject</th><th>Relationship Type</th><th>Object</th></tr>';
+
+    if (!empty($subject_relationships) ) {
+      foreach ($subject_relationships as $s) {
+        $output .= '<tr><td>'.$s->subject_name.'</td><td>'.$s->relationship_type.'</td><td>'.$feature_name.'</td></tr>';
+      }
+    }
+
+    if (!empty($object_relationships) ) {
+      foreach ($object_relationships as $o) {
+        $output .= '<tr><td>'.$feature_name.'</td><td>'.$o->relationship_type.'</td><td>'.$o->object_name.'</td></tr>';
+      } // end of foreach property
+    }
+
+    $output .= '</table>';
+  } else {
+    $output = 'No Relationships For the Current Feature'; 
+  }
+
+  return $output;
+
+}

+ 222 - 0
modules/base/tripal_feature/tripal_feature-secondary_tables.inc

@@ -0,0 +1,222 @@
+<?php
+// $Id$
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_implement_back_to_feature_button($form_state, $nid) {
+  $form = array();
+
+  $form['nid'] = array(
+    '#type' => 'hidden',
+    '#value' => $nid
+  );
+
+  $form["submit-back"] = array( 
+    '#type' => 'submit',
+    '#value' => t('Back to Stock') 
+  );
+
+  return $form;
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_implement_back_to_feature_button_submit($form, $form_state) {
+  drupal_goto('node/'.$form_state['values']['nid']);
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_implement_add_chado_properties_progress($current) {
+
+    $value = '<div class="form_progress"><div class="form_progress-text">'; 
+
+    if ($current == 'main') { $value .= '<span id="form-step-current">Create Basic Feature</span>'; } 
+    else { $value .= '<span id="form-step">Create Basic Stock</span>'; }
+
+    $value .= '<span id="form-segway">  >>  </span>';
+
+    if ($current == 'properties') { $value .= '<span id="form-step-current">Add Synonyms & Properties</span>'; }
+    else { $value .= '<span id="form-step">Add Synonyms & Properties</span>'; }
+
+    $value .= '<span id="form-segway">  >>  </span>';                       
+
+    if ($current == 'db_references') { $value .= '<span id="form-step-current">Add Database References</span>'; }
+    else { $value .= '<span id="form-step">Add Database References</span>'; }
+
+    $value .= '<span id="form-segway">  >>  </span>';         
+
+    if ($current == 'relationships') { $value .= '<span id="form-step-current">Add Relationships</span>'; }
+    else { $value .= '<span id="form-step">Add Relationships</span>'; }
+
+    $value .= '</div></div>';
+
+    return $value;
+
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_implement_add_chado_properties_navigate($form_state, $step, $nid) {
+  $form = array();
+
+  $form['current_step'] = array(
+    '#type' => 'hidden',
+    '#value' => $step
+  );
+
+  // Use this field to set all the steps and the path to each form
+  // where each step is of the form name;path and each step is separated by ::
+  $steps =array(
+    'properties' => 'node/%node/properties',
+    'db_references' => 'node/%node/db_references',
+    'relationships' => 'node/%node/relationships'
+  );
+  $steps_value = array();
+  foreach ($steps as $k => $v) { $steps_value[] = $k.';'.$v; }
+  $form['steps'] = array(
+    '#type' => 'hidden',
+    '#value' => implode('::', $steps_value)
+  );
+
+  $form['first_step'] = array(
+    '#type' => 'hidden',
+    '#value' => 'properties'
+  );
+
+  $form['last_step'] = array(
+    '#type' => 'hidden',
+    '#value' => 'relationships'
+  );
+
+  $form['nid'] = array(
+    '#type' => 'hidden',
+    '#value' => $nid
+  );
+
+  if ($step != $form['first_step']['#value']) {
+    $form['submit-prev'] = array(
+      '#type' => 'submit',
+      '#value' => t('Previous Step')
+    );
+  }
+
+  if ($step != $form['last_step']['#value']) {
+    $form['submit-next'] = array( 
+      '#type' => 'submit',
+      '#value' => t('Next Step')
+    );
+  }
+
+  if ($step == $form['last_step']['#value']) {
+    $form['submit-finish'] = array(
+      '#type' => 'submit', 
+      '#value' => t('Finish')
+    );
+  }
+
+  return $form;
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_implement_add_chado_properties_navigate_submit($form, $form_state) {
+
+  $raw_steps = preg_split('/::/', $form_state['values']['steps']); 
+
+  $steps = array();
+  $current_index = 'EMPTY';
+  $i=0;
+
+  foreach ($raw_steps as $raw_step) {
+    $step = preg_split('/;/', $raw_step);
+    $steps[$i] = $step;
+    
+    if ($step[0] == $form_state['values']['current_step']) {
+      $current_index = $i;
+    }
+
+    $i++;
+  }
+  $num_steps = $i;
+  
+  if (strcmp($current_index,'EMPTY') == 0) {
+    // No Matching Step
+    drupal_set_message('Could not determine next step -'.$form_state['values']['current_step'].', please contact the administrator', 'error');
+  } elseif ($current_index == 0) {
+    $next_goto = $steps[$current_index+1][1];
+  } elseif ($current_index == ($num_steps-1)) {
+    $prev_goto = $steps[$current_index-1][1];
+    $next_goto = 'node/%node';
+  } else {
+    $prev_goto = $steps[$current_index-1][1];
+    $next_goto = $steps[$current_index+1][1];
+  }
+
+  if ($form_state['clicked_button']['#value'] == t('Previous Step') ) {
+    //replace %node
+    $prev_goto = preg_replace('/%node/', $form_state['values']['nid'], $prev_goto);
+    $_REQUEST['destination'] = $prev_goto;
+  } elseif ($form_state['clicked_button']['#value'] == t('Next Step') ) {
+    $next_goto = preg_replace('/%node/', $form_state['values']['nid'], $next_goto);
+    $_REQUEST['destination'] = $next_goto;
+  } elseif ($form_state['clicked_button']['#value'] == t('Finish') ) {
+    $next_goto = preg_replace('/%node/', $form_state['values']['nid'], $next_goto);
+    $_REQUEST['destination'] = $next_goto;
+  }
+
+}
+
+/**
+ * Implements Hook_form()
+ * Handles setting the is_obsolete property of stocks
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_is_obsolete_form($node, $stock_id) {
+
+  $form['make_obsolete'] = array(
+    '#type' => 'submit',
+    '#value' => t('Mark Stock as Obsolete')
+  );
+
+  $form['make_obsolete_stock_id'] = array(
+    '#type' => 'value',
+    '#value' => $stock_id,
+    '#required' => TRUE
+  );
+
+  return $form;
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_is_obsolete_form_submit($form, &$form_state) {
+
+  $previous_db = db_set_active('chado');
+  db_query(
+    "UPDATE stock SET is_obsolete='t' WHERE stock_id=%d",
+    $form_state['values']['make_obsolete_stock_id']
+  );
+  db_set_active($previous_db);
+
+}
+

+ 788 - 0
modules/base/tripal_feature/tripal_feature.admin.inc

@@ -0,0 +1,788 @@
+<?php
+
+/**
+ * Purpose: Provide Guidance to new Tripal Admin
+ *
+ * @return 
+ *   HTML Formatted text
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_module_description_page() {
+
+  $text .= '<h3>Tripal Feature Administrative Tools Quick Links:</h3>';
+  $text .= "<ul>
+             <li><a href=\"".url("admin/tripal/tripal_feature/configuration") . "\">Feature Configuration</a></li>
+             <li><a href=\"".url("admin/tripal/tripal_feature/fasta_loader"). "\">Import a multi-FASTA file</a></li>
+             <li><a href=\"".url("admin/tripal/tripal_feature/gff3_load"). "\">Import a GFF3 file</a></li>
+             <li><a href=\"".url("admin/tripal/tripal_feature/sync") . "\">Sync Features</a></li>
+             <li><a href=\"".url("admin/tripal/tripal_feature/delete") . "\">Delete Features</a></li>
+           </ul>";
+#             <li><a href=\"".url("admin/tripal/tripal_feature/aggregate"). "\">Feature Relationship Aggegators</a></li> 
+
+  $text .= '<h3>Module Description:</h3>';
+  $text .= '<p>This module provides an interface for the Chado feature module which stores information
+            related to genomic features.  This module provides support for bulk loading of data in
+            FASTA or GFF format, visualization of "feature" pages, editing and updating.  
+            </p>';
+
+  $text .= '<h3>Setup Instructions:</h3>';
+  $text .= '<p>After installation of the feature module.  The following tasks should be performed
+            <ol>
+              <li><p><b>Set Permissions</b>: The feature module supports the Drupal user permissions interface for 
+               controlling access to feature content and functions. These permissions include viewing, 
+               creating, editing or administering of
+               feature content. The default is that only the original site administrator has these 
+               permissions.  You can <a href="'.url('admin/user/roles').'">add roles</a> for classifying users, 
+               <a href="'.url('admin/user/user').'">assign users to roles</a> and
+               <a href="'.url('admin/user/permissions').'">assign permissions</a> for the feature content to 
+               those roles.  For a simple setup, allow anonymous users access to view organism content and 
+               allow the site administrator all other permissions.</p></li>
+
+               <li><p><b>Themeing</b>:  Before content from Chado can be visualized the Tripal base theme must
+               be installed.  This should have been done prior to this point.  But is mentioned here in the event you
+               follow the instructions below and cannot see content.  In this case, if you do not see content
+               check that Tripal theming is properly installed</p></li>
+
+               <li><p><b>Loading of Ontologies</b>:  If you
+               used Tripal to create the Chado database, then you must load ontologies before proceeding.  Visit the
+               page to <a href="'.url('admin/tripal/tripal_cv/obo_loader').'">load ontologies</a> and load at 
+               least the following ontologies:  
+               <ul>
+                  <li>Chado Feature Properties</li>
+                  <li>Relationship Ontology</li>
+                  <li>Sequence Ontology</li>
+                  <li>Gene Ontology (if loading GO terms for features)</li>
+               </ul></p></li>
+
+               <li><p><b>Create Organisms</b>:  Before adding feature data you must already have the 
+               organisms loaded in the database.  See the 
+               <a href="'.url('admin/tripal/tripal_organism').'">Tripal Organism Admin page</a> for 
+               instructions for adding and Syncing organisms.</p></li>
+
+               <li><p><b>Create Analysis</b>:  Tripal requires that feature data loaded using the Tripal loaders
+               be associated with an analyis.  This provides a grouping for the feature data and can be used
+               later to visualize data pipelines.  Before loading feature data through the FASTA or GFF loaders
+               you will need to <a href="'.url('node/add').'">create an analysis</a> for the data.</p></li>
+
+               <li><p><b>Create Referring Database Entries</b>:  If you would like to associate your feature data with an 
+               external reference database, check to ensure that the <a href="'.url('admin/tripal/tripal_db/edit_db').'">
+               database record already exists</a>.  If not you should <a href="'.url('admin/tripal/tripal_db/add_db').'">add a new database record</a> before importing
+               feature data.</p></li>
+
+               <li><p><b>Data Import</b>:  if you do not already have an existing Chado database with preloaded data 
+               then you will want
+               to import data.  You can do so using the Chado perl scripts that come with the normal 
+               <a href="http://gmod.org/wiki/Chado">distribution of Chado</a> or you can use the <a href="'.url('admin/tripal/tripal_feature/fasta_loader').'">FASTA loader</a> and 
+               <a href="'.url('admin/tripal/tripal_feature/gff3_load').'">GFF loader</a> provided here.  If you
+               created the Chado database using Tripal then you\'ll most likely want to use the Tripal loaders.  If your data
+               is not condusive for loading with these loaders you may have to write your own loaders. 
+               </p></li>
+
+               <li><p><b>Sync Features</b>:  After data is loaded you need to sync features.  This process is what
+               creates the pages for viewing online.  Not all features need be synced.  For instance, if you
+               have loaded whole genome sequence with fully defined gene models with several features to define
+               a gene and its products (e.g. gene, mRNA, CDS, 5\'UTR, 3\'UTR, etc) you probably only want to create
+               pages for genes or genes and mRNA.  You probably do not want a page for a 5\'UTR.  
+               Using the <a href="'.url('admin/tripal/tripal_feature/configuration/sync').'">Feature Sync page</a> 
+               you can sync (or create pages) for the desired feature types. </p></li>
+
+               <li><p><b>Set Feature URL</b>:  It is often convenient to have a simple URL for each feature page. 
+               For example, http://www.mygenomesite.org/[feature], where [feature] is a unique identifier for a feature page.
+               With this, people can easily include links to feature pages of interest. Use the 
+               <a href="'.url('admin/tripal/tripal_feature/configuration').'">Feature Configuration page</a> 
+               to specify whether to use the feature name, unique name or internal ID as the [feature] portion of the
+               URL.  Select the one that will guarantee a unique identifier for feature pages.</p></li>
+
+               <li><p><b>Indexing</b>:  Once all data has been loaded (including analysis data--e.g. blast, interpro, etc.)
+               you can index all feature pages for searching if you want to ues the Drupal default search mechanism. 
+               Use the <a href="'.url('admin/tripal/tripal_feature/configuration').'">Feature Configuration page</a> 
+               to either Index (for the first time) or "Reindex" (after adding new data)
+               the feature pages for searching.  Once the site is 100% indexed the pages will be searchable using Drupal\'s
+               full text searching.  You can find the percent indexed for the entire site by visiting the 
+               <a href="'.url('admin/settings/search').'">Search settings page</a>. Indexing
+               can take quite a while if you have a lot of data</p></li>
+
+               <li><p><b>Set Taxonomy</b>:  Drupal provides a mechanism for categorizing content to allow 
+               for advanced searching.  Drupal calls this "Taxonomy", but is essentially categorizing the pages.  
+               You can categorize feature pages by their type (e.g. gene, mRNA, contig, EST, etc.) and by the 
+               organism to which they belong.  This allows for filtering of search results by organism and feature type. 
+               Use the <a href="'.url('admin/tripal/tripal_feature/configuration').'">Feature Configuration page</a> to
+               set the Taxonomy.</p></li>
+            </ol>
+            </p>';
+  
+  
+  $text .= '<h3>Features of this Module:</h3>';
+  $text .= '<p>Aside from data loading and feature page setup (as described in the Setup section above), 
+            The Tripal feature module also provides the following functionality
+            <ul>
+              <li><p><b>Feature Browser:</b>  The feature browser is a tabular list of features with links to their 
+               feature pages which appears on the organism 
+               page.  It was created to provide a mechanism to allow site visitors to quickly
+               accesss feature pages when they do not know what to search for.  For sites with large numbers of features, this
+               method for finding a specific pages is inadequate, but may still be included to aid new site 
+               visitors.    This browser can be toggled on or off using the 
+               <a href="'.url('admin/tripal/tripal_feature/configuration').'">Feature Configuration page</a></p></li>
+ 
+              <li><p><b>Feature Summary:</b>  The feature summary is a pie chart that indicates the types and quantities
+              of feature types (Sequence Ontology terms) that are loaded in the database. It appears on the organism 
+              page.  The summary can be toggled on or off using the 
+              <a href="'.url('admin/tripal/tripal_feature/configuration').'">Feature Configuration page</a></p></li>             
+
+              <li><p><b>Integration with Drupal Views</b>: <a href="http://drupal.org/project/views">Drupal Views</a> is
+              a powerful tool that allows the site administrator to create lists or basic searching forms of Chado content.
+              It provides a graphical interface within Drupal to allow the site admin to directly query the Chado database
+              and create custom lists without PHP programming or customization of Tripal source code.  Views can also
+              be created to filter content that has not yet been synced with Druapl in order to protect access to non
+              published data (only works if Chado was installed using Tripal).  You can see a list of available pre-existing
+              Views <a href="'.url('admin/build/views/').'">here</a>, as well as create your own. </p></li>
+
+              <li><p><b>Basic Feature Lookup View</b>: This module provides a basic <a href="'.url('features').'">feature search 
+              tool</a> for finding or listing features in Chado. It does not require indexing for Drupal searching but relies
+              on Drupal Views.  <a href="http://drupal.org/project/views">Drupal Views</a> must be installed. </p></li>              
+
+              <li><p><b>Delete Features</b>: This module provides a <a href="'.url('admin/tripal/tripal_feature/delete').'">Delete Feature page</a>
+              for bulk deltions of features. You may delete features using a list of feature names, or for a specific organism
+              or for a specific feature type.</p></li>              
+
+            </ul>
+            </p>';
+
+   $text .= '<h3>Page Customizations</h3>';
+   $text .= '<p>There are several ways to customize the look-and-feel for the way Chado data is presented through Tripal. 
+             Below is a description of several methods.  These methods may be used in conjunction with one another to
+             provide fine-grained control. 
+             <ul>
+
+             <li><p><b>Integration with Drupal Panels</b>:  <a href="http://drupal.org/project/views">Drupal Panels</a> 
+              allows for customization of a page layout if you don\'t want to do PHP/Javascript/CSS programming.  Tripal comes with pre-set layouts for feature pages.  However, 
+              Panels become useful if you prefer a layout that is different from the pre-set layouts.  Chado content
+              is provided to Panels in the form of Drupal "blocks" which you can then place anywhere on a page using the 
+              Panel\'s GUI.</p></li>
+
+             <li><p><b>Drupal\'s Content Construction Kit (CCK)</b>: the 
+             <a href="http://drupal.org/project/cck">Content Construction Kit (CCK) </a> is a powerful way to add non-Chado content
+             to any page without need to edit template files or knowing PHP.  You must first download and install CCK.
+             With CCK, the site administartor can create a new field to appear on the page.  For example, currently,
+             the Chado publication module is not yet supported by Tripal.  Therefore, the site administrator can add a text 
+             field to the feature pages.  This content is not stored in Chado, but will appear on the feature page.  A field
+             added by CCK will also appear in the form when editing a feature to allow users to manually enter the appropriate
+             text.  If the default pre-set layout and themeing for Tripal is used, it is better to create the CCK element,
+             indicate that it is not to be shown (using the CCK interface), then manually add the new content type 
+             where desired by editing the templates (as described below).  If using Panels, the CCK field can be added to the
+             location desired using the Panels interface.</p></li>
+
+             <li><p><b>Drupal Node Templates</b>:  The Tripal packages comes with a "theme_tripal" directory that contains the
+             themeing for Chado content.    The feature module has a template file for feature "nodes" (Tripal feature pages).  This file
+             is named "node-chado_feature.tpl.php", and provides javascript, HTML and PHP code for display of the feature
+             pages.  You can edit this file to control which types of information (or which feature "blocks") are displayed for features. Be sure to 
+             copy these template to your primary theme directory for editing. Do not edit them in the "theme_tripal" directory as
+             future Tripal updates may overwrite your customizations. See the <a href="http://tripal.sourceforge.net/">Tripal website </a>
+             for instructions on how to access variables and other Chado content within the template file.</p></li>
+
+             <li><p><b>Feature "Block" Templates</b>:  In the "theme_tripal" directory is a subdirectory named "tripal_feature".
+             Inside this directory is a set of templates that control distinct types of information for features.  For example,
+             there is a "base" template for displaying of data directly from the Chado feature table, and a "references" 
+             template for showing external site references for a feature (data from the feature_dbxref table).  These templates are used both by Drupal blocks
+             for use in Drupal Panels (as described above) or for use in the default pre-set layout that the node template 
+             provides (also desribed above).  You can customize this template as you desire.  Be sure to copy the
+             template to your primary theme directory for editing. Do not edit them in the "theme_tripal" directory as
+             future Tripal updates may overwrite your customizations.  See the <a href="http://tripal.sourceforge.net/">Tripal website </a>
+             for instructions on how to access variables and other Chado content within the template files.</p></li>
+             </li>
+
+             <li><p><b>Adding Links to the "Resources" Sidebar</b>: If you use the pre-set default Tripal layout for theming, you
+             will see a "Resources" sidebar on each page.  The links that appear on the sidebar are automatically generated
+             using Javascript for all of the feature "Blocks" that appear on the page. If you want to add additional links 
+             (e.g. a dynamic link to GBrowse for the feature) and you want that link to appear in the 
+             "Resources" sidebar, simply edit the Drupal Node Template (as described above) and add the link to the 
+             section at the bottom of the template file where the resources section is found.</p></li>
+
+             </ul>
+             </p>';
+
+  return $text;
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_admin () {
+
+   // before proceeding check to see if we have any
+   // currently processing jobs. If so, we don't want
+   // to give the opportunity to sync libraries
+   $active_jobs = FALSE;
+   if(tripal_get_module_active_jobs('tripal_feature')){
+      $active_jobs = TRUE;
+   }
+   if(!$active_jobs){
+
+      get_tripal_feature_admin_form_url_set($form);
+
+      $form['browser'] = array(
+         '#type' => 'fieldset',
+         '#title' => t('Feature Browser')
+      );
+      $allowedoptions1  = array (
+        'show_feature_browser' => "Show the feature browser on the organism page. The browser loads when page loads. This may be slow for large sites.",
+        'hide_feature_browser' => "Hide the feature browser on the organism page. Disables the feature browser completely.",
+      );
+//      $allowedoptions ['allow_feature_browser'] = "Allow loading of the feature browsing through AJAX. For large sites the initial page load will be quick with the feature browser loading afterwards.";
+
+      $form['browser']['browser_desc'] = array(
+         '#type'        => 'markup',
+         '#value' => 'A feature browser can be added to an organism page to allow users to quickly '. 
+            'access a feature.  This will most likely not be the ideal mechanism for accessing feature '.
+            'information, especially for large sites, but it will alow users exploring the site (such '.
+            'as students) to better understand the data types available on the site.',
+
+      );
+      $form['browser']['feature_types'] = array(
+         '#title'       => t('Feature Types'),
+         '#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'),
+      );
+
+
+      $form['browser']['browse_features'] = array(
+         '#title' => 'Feature Browser on Organism Page',
+         '#type' => 'radios',
+         '#options' => $allowedoptions1,
+         '#default_value'=>variable_get('tripal_feature_browse_setting', 'show_feature_browser'),
+      );
+      $form['browser']['set_browse_button'] = array(
+         '#type' => 'submit',
+         '#value' => t('Set Browser'),
+         '#weight' => 2,
+      );
+
+
+      $form['feature_edit'] = array(
+         '#type' => 'fieldset',
+         '#title' => t('Feature Editing')
+      );
+
+      $form['feature_edit']['browser_desc'] = array(
+         '#type'        => 'markup',
+         '#value' => 'When editing or creating a feature, a user must provide the feature type. '.
+                     'The Sequence Ontology list is very large, therefore, to simply the list of types for the user, the following '.
+                     'textbox allows you to specify which features types can be used. This list of terms will appear in the '.
+                     'feature type drop down list of the feature creation/edit form.',
+
+      );
+      $form['feature_edit']['feature_edit_types'] = array(
+         '#title'       => t('Feature Types'),
+         '#type'        => 'textarea',
+         '#description' => t("Enter the Sequence Ontology (SO) terms for the allowed feature types when creating or editing features."),
+         '#default_value' => variable_get('chado_edit_feature_types','gene contig EST mRNA'),
+      );
+
+      $form['feature_edit']['set_feature_types'] = array(
+         '#type' => 'submit',
+         '#value' => t('Set Feature Types'),
+      );
+
+      $form['summary'] = array(
+         '#type' => 'fieldset',
+         '#title' => t('Feature Summary')
+      );
+      $allowedoptions2 ['show_feature_summary'] = "Show the feature summary on the organism page. The summary loads when page loads.";
+      $allowedoptions2 ['hide_feature_summary'] = "Hide the feature summary on the organism page. Disables the feature summary.";
+
+      $form['summary']['feature_summary'] = array(
+         '#title' => 'Feature Summary on Organism Page',
+         '#description' => 'A feature summary can be added to an organism page to allow users to see the '.
+            'type and quantity of features available for the organism.',
+         '#type' => 'radios',
+         '#options' => $allowedoptions2,
+         '#default_value'=>variable_get('tripal_feature_summary_setting', 'show_feature_summary'),
+      );
+      $form['summary']['set_summary_button'] = array(
+         '#type' => 'submit',
+         '#value' => t('Set Summary'),
+         '#weight' => 2,
+      );
+
+      get_tripal_feature_admin_form_taxonomy_set($form);
+      get_tripal_feature_admin_form_reindex_set($form);
+      get_tripal_feature_admin_form_cleanup_set($form);
+   } else {
+      $form['notice'] = array(
+         '#type' => 'fieldset',
+         '#title' => t('Feature Management Temporarily Unavailable')
+      );
+      $form['notice']['message'] = array(
+         '#value' => t('Currently, feature management jobs are waiting or ".
+            "are running. Managemment features have been hidden until these ".
+            "jobs complete.  Please check back later once these jobs have ".
+            "finished.  You can view the status of pending jobs in the Tripal ".
+            "jobs page.'),
+      );
+   }
+   return system_settings_form($form);
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_admin_validate($form, &$form_state) {
+   global $user;  // we need access to the user info
+   $job_args = array();
+
+   variable_set('chado_browser_feature_types',$form_state['values']['feature_types']);
+
+   // if the user wants to sync up the chado features then
+   // add the job to the management queue
+   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);
+         break;
+
+      case t('Reindex all feature nodes') :
+         tripal_add_job('Reindex all features','tripal_feature',
+            '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']);
+         break;
+
+      case t('Set Feature Types') :
+         variable_set('tripal_feature_type_setting',$form_state['values']['feature_edit_types']);
+         break;
+
+      case t('Set Summary') :
+         variable_set('tripal_feature_summary_setting',$form_state['values']['feature_summary']);
+         break;
+
+      case t('Set Feature URLs') :
+         variable_set('chado_feature_url',$form_state['values']['feature_url']);
+         tripal_add_job('Set Feature URLs','tripal_feature',
+            'tripal_feature_set_urls',$job_args,$user->uid);
+         break;
+   }
+}
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function get_tripal_feature_admin_form_cleanup_set(&$form) {
+   $form['cleanup'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Clean Up')
+   );
+   $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,
+   );
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function get_tripal_feature_admin_form_reindex_set(&$form) {
+   $form['reindex'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Index/Reindex')
+   );
+   $form['reindex']['description'] = array(
+       '#type' => 'item',
+       '#value' => t("Indexing or reindexing of nodes is required for Drupal's full text searching. ".
+          "Index features for the first time to allow for searching of content, and later when content for features ".
+          "is updated.  Depending on the number of features this may take ".
+          "quite a while. Click the button below to begin reindexing of ".
+          "features. "),
+       '#weight' => 1,
+   );
+   $form['reindex']['button'] = array(
+      '#type' => 'submit',
+      '#value' => t('Reindex all feature nodes'),
+      '#weight' => 2,
+   );
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function get_tripal_feature_admin_form_taxonomy_set (&$form) {
+
+
+   $form['taxonomy'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Set Taxonomy')
+   );
+
+   $form['taxonomy']['description'] = array(
+       '#type' => 'item',
+       '#value' => t("Drupal allows for assignment of \"taxonomy\" or ".
+          "catagorical terms to nodes. These terms allow for advanced ".
+          "filtering during searching."),
+       '#weight' => 1,
+   );
+   $tax_options = array (
+      'organism' => t('Organism name'),
+      'feature_type'  => t('Feature Type (e.g. EST, mRNA, etc.)'),
+      'analysis' => t('Analysis Name'),
+      'library'  => t('Library Name'),
+   );
+   $form['taxonomy']['tax_classes'] = array (
+     '#title'       => t('Available Taxonomic Classes'),
+     '#type'        => t('checkboxes'),
+     '#description' => t("Please select the class of terms to assign to ".
+        "chado features"),
+     '#required'    => FALSE,
+     '#prefix'      => '<div id="taxclass_boxes">',
+     '#suffix'      => '</div>',
+     '#options'     => $tax_options,
+     '#weight'      => 2,
+     '#default_value' => variable_get('tax_classes',''),
+   );
+   $form['taxonomy']['button'] = array(
+      '#type' => 'submit',
+      '#value' => t('Set/Reset Taxonomy for all feature nodes'),
+      '#weight' => 3,
+   );
+
+}
+
+function get_tripal_feature_admin_form_url_set (&$form) {
+
+   $form['url'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Feature URL Path')
+   );
+   $form['url']['desc'] = array (
+      '#type'        => 'markup',
+      '#value' => t('Each synced feature will have a unique URL which consists of '.
+                          'the site domain followed by a unique identifer: for '.
+                          'example http://my-tripal-site.org/ID1034, where the '.
+                          'element just after the final slash is the unique '.
+                          'identifier for the feature.'),
+
+   );
+
+   $form['url']['chado_feature_url'] = array(
+      '#title'       => t('Unique Identifier'),
+      '#type'        => 'radios',
+      '#description' => t('Choose an identifier type '.
+                          'from the list above that is guaranteed to be unique in your synced '.
+                          'dataset. If in doubt it is safest to coose the internal ID. '.
+                          'The descrpitor need not be unique amont the total dataset. '.
+                          'It only need be unique among the synced dataset.'),
+      '#required'    => FALSE,
+      '#options'     => array('internal ID' => 'internal ID (Chado feature_id)',
+                              'feature unique name' => 'feature unique name', 
+                              'feature name' => 'feature name'),
+      '#default_value' => variable_get('chado_feature_url','internal ID'),
+   );
+
+   $form['url']['chado_feature_accession_prefix'] = array (
+      '#title'       => t('ID Prefix'),
+      '#type'        => t('textfield'),
+      '#description' => t("If you choose an Internal ID above you must also enter an ID prefix.".
+                          "this prefix will be prepended to the internal ID number (e.g. ID38294). ".
+                          "if you chose to use the feature name or unique name then this prfix is not used"),
+      '#required'    => TRUE,
+      '#default_value' => variable_get('chado_feature_accession_prefix','ID'),
+   );
+
+   $form['url']['button'] = array(
+      '#type' => 'submit',
+      '#value' => t('Set Feature URLs'),
+      '#weight' => 3,
+   );
+}
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_aggregator_page(){
+   $add_url = url("admin/tripal/tripal_feature/aggregate/new");
+   $output = "<a href=\"$add_url\">Add a new aggregator</a>"; 
+   $output .= drupal_get_form('tripal_feature_aggregator_select_form');
+   $output .= '<div id="db-edit-div">Please select an aggregator base type to view or edit</div>';
+   return $output;
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_aggregator_select_form(){
+
+	// get a list of base terms from chado for user to choose
+	$sql = "SELECT DISTINCT type_id FROM {tripal_feature_relagg} ORDER BY type_id ";
+	$results = db_query ($sql);
+   
+   $sql = "SELECT * FROM {cvterm} WHERE cvterm_id = %d";
+   $previous_db = tripal_db_set_active('chado');
+	$types = array();
+   $types[] = '';
+   while($base = db_fetch_object($results)){
+      $term = db_fetch_object(db_query($sql,$base->type_id));
+		$types[$base->type_id] = $term->name;
+   }
+   tripal_db_set_active($previous_db);
+
+	$form['type_id'] = array(
+      '#title' => t('Aggregator Base Type'),
+      '#type' => 'select',
+      '#options' => $types,
+      '#ahah' => array(
+         'path' => 'admin/tripal/tripal_feature/aggregate/edit/js',
+         'wrapper' => 'db-edit-div',
+         'effect' => 'fade',
+         'event' => 'change',
+         'method' => 'replace',
+      ),
+	);
+
+   return $form;
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_aggregator_form(&$form_state = NULL,$type_id = NULL){
+
+   // get this requested database
+   if($type_id){
+      $sql = "SELECT * FROM {tripal_feature_relagg} WHERE type_id = %d ";
+      $tsql = "SELECT * FROM {cvterm} WHERE cvterm_id = %d ";
+
+      // get the default list of terms
+      $results = db_query($sql,$type_id);
+      while($type = db_fetch_object($results)){
+         $previous_db = tripal_db_set_active('chado');
+         $term = db_fetch_object(db_query($tsql,$type->rel_type_id));
+         tripal_db_set_active($previous_db);
+         $default_others .= $term->name . " ";
+      }
+      $default_base = $base->name;     
+      $action = 'Update';
+      $form['type_id'] = array(
+         '#type' => 'hidden',
+         '#value' => $type_id
+      );
+   } else {
+      $action = 'Add';
+      $form['base']= array(
+         '#type'          => 'textfield',
+         '#title'         => t('Base Feature type'),
+         '#description'   => t('Please enter the Sequence Ontology (SO) term for the base feature type for this aggregator.'),
+         '#default_value' => $default_base,
+         '#required'      => TRUE,
+         '#weight'        => 1
+      );
+   }
+
+   $form['others']= array(
+      '#type'          => 'textarea',
+      '#title'         => t('Aggregate these types if a relationship exists'),
+      '#description'   => t('Please enter the Sequence Ontology (SO) terms that should be aggregated with the base feature type listed above.  Separate each by a space or newline'),
+      '#default_value' => $default_others,
+      '#required'      => TRUE,
+      '#weight'        => 2
+   );
+ 
+   if(strcmp($action,'Update')==0){
+      $form['update'] = array (
+        '#type'         => 'submit',
+        '#value'        => t('Update'),
+        '#weight'       => 5,
+        '#executes_submit_callback' => TRUE,
+      );
+      $form['delete'] = array (
+        '#type'         => 'submit',
+        '#value'        => t('Delete'),
+        '#weight'       => 6,
+        '#executes_submit_callback' => TRUE,
+      );
+   } else {
+      $form['add'] = array (
+        '#type'         => 'submit',
+        '#value'        => t('Add'),
+        '#weight'       => 5,
+        '#executes_submit_callback' => TRUE,
+      );
+   }
+   $form['#redirect'] = 'admin/tripal/tripal_feature/aggregate';
+
+
+   return $form;
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_aggregator_form_validate($form, &$form_state){
+   $type_id  = $form_state['values']['type_id'];
+   $base     = $form_state['values']['base'];
+   $others   = $form_state['values']['others'];
+   $op      =  $form_state['values']['op'];
+
+   // split apart the feature types to be aggregated
+   $types = preg_split('/\s+/',$others);
+
+   // the SQL for finding the term
+   $tsql = "
+      SELECT * 
+      FROM {cvterm} CVT 
+         INNER JOIN {cv} CV on CVT.cv_id = CV.cv_id 
+      WHERE CVT.name = '%s' and CV.name = 'sequence'";
+
+
+   // make sure the base type exists
+   if($base){
+      $previous_db = tripal_db_set_active('chado');
+      $term = db_fetch_object(db_query($tsql,$base));
+      tripal_db_set_active($previous_db);
+      if(!$term){
+         form_set_error('base',t('The specified base type is not a valid SO term'));
+      }
+
+      // make sure this type doesn't already in the table
+      $sql = "SELECT * FROM {tripal_feature_relagg} WHERE type_id = %d ";
+      $agg = db_fetch_object(db_query($sql,$term->cvterm_id));
+      if($agg){
+         form_set_error('base',t('The specified base type is already used as a base type for another aggregator and cannot be readded.'));
+      }      
+   }
+
+   // iterate through each type to be aggregated and make sure they are valid
+   foreach($types as $type){
+      $previous_db = tripal_db_set_active('chado');
+      $term = db_fetch_object(db_query($tsql,$type));
+      tripal_db_set_active($previous_db);
+      if(!$term){
+         form_set_error('others',t('The specified type ') . $type . t(' is not a valid SO term'));
+      }
+   }   
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_aggregator_form_submit($form, &$form_state){
+
+   $type_id  = $form_state['values']['type_id'];
+   $base     = $form_state['values']['base'];
+   $others   = $form_state['values']['others'];
+   $op      =  $form_state['values']['op'];
+
+   // split apart the feature types to be aggregated
+   $types = preg_split('/\s+/',$others);
+
+   // the SQL for finding the term
+   $tsql = "
+      SELECT * 
+      FROM {cvterm} CVT 
+         INNER JOIN {cv} CV on CVT.cv_id = CV.cv_id 
+      WHERE CVT.name = '%s' and CV.name = 'sequence'";
+
+   // the SQL for deleting an aggregator
+   $dsql = "
+      DELETE FROM {tripal_feature_relagg}
+      WHERE type_id = %d
+   ";
+
+   // if this is an insert then we have a base type name.  We
+   // need to get the corresponding term id
+   if($base){
+      $previous_db = tripal_db_set_active('chado');
+      $term = db_fetch_object(db_query($tsql,$base));
+      tripal_db_set_active($previous_db);
+      $type_id = $term->cvterm_id;
+   }
+
+   if(strcmp($op,'Delete')==0){
+      $result = db_query($dsql,$type_id);
+      if($result){
+        drupal_set_message("Aggregator deleted");
+      } else {
+        drupal_set_message("Failed to delete mailing list.");
+      }
+   }
+   else {
+      // for an update, first remove all the current aggregator settings
+      // and we'll add them again
+      $result = db_query($dsql,$type_id);
+
+      // the SQL for inserting the aggregated types
+      $isql = "
+         INSERT INTO {tripal_feature_relagg}
+          (type_id,rel_type_id)
+         VALUES 
+          (%d,%d)
+      ";
+
+      // iterate through each type to be aggregated and add an entry in the table
+      foreach($types as $type){
+         $previous_db = tripal_db_set_active('chado');
+         $term = db_fetch_object(db_query($tsql,$type));
+         tripal_db_set_active($previous_db);
+         $result = db_query($isql,$type_id,$term->cvterm_id);
+      }
+      drupal_set_message("Aggregator added");
+   } 
+   return '';
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_aggregator_ajax_edit (){ 
+   // get the database id, build the form and then return the JSON object
+   $type_id = $_POST['type_id'];
+   $form = drupal_get_form('tripal_feature_aggregator_form',$type_id);
+   drupal_json(array('status' => TRUE, 'data' => $form));
+}

+ 255 - 0
modules/base/tripal_feature/tripal_feature.api.inc

@@ -0,0 +1,255 @@
+<?php
+ 
+/**
+ * Implements hook_chado_feature_schema()
+ * Purpose: To add descriptions and foreign keys to default table description
+ * Note: This array will be merged with the array from all other implementations
+ *
+ * @return
+ *    Array describing the feature table
+ *
+ * @ingroup tripal_schema_api
+ */
+function tripal_feature_chado_feature_schema() {
+  $description = array();
+
+  // Default table description in tripal_core.schema.api.inc: tripal_core_chado_feature_schema()
+
+  $description['foreign keys']['organism'] = array(
+        'table' => 'organism',
+        'columns' => array(
+          'organism_id' => 'organism_id',
+        ),
+  );
+  
+  $description['foreign keys']['dbxref'] = array(
+        'table' => 'dbxref',
+        'columns' => array(
+          'dbxref_id' => 'dbxref_id',
+        ),
+  );
+  
+  $description['foreign keys']['cvterm'] = array(
+        'table' => 'cvterm',
+        'columns' => array(
+          'type_id' => 'cvterm_id',
+        ),
+  );
+  
+  $referring_tables = array('analysisfeature',
+    'element',
+    'feature_cvterm',
+    'feature_dbxref',
+    'feature_expression',
+    'feature_genotype',
+    'feature_phenotype',
+    'feature_pub',
+    'feature_relationship',
+    'feature_synonym',
+    'featureloc',
+    'featurepos',
+    'featureprop',
+    'featurerange',
+    'library_feature',
+    'phylonode',
+    'wwwuser_feature'
+  );
+  $description['referring_tables'] = $referring_tables;
+
+  return $description;
+}
+
+/**
+ * Implements hook_chado_featureprop_schema()
+ * Purpose: To add descriptions and foreign keys to default table description
+ * Note: This array will be merged with the array from all other implementations
+ *
+ * @return
+ *    Array describing the featureprop table
+ *
+ * @ingroup tripal_schema_api
+ */
+function tripal_feature_chado_featureprop_schema() {
+  $description = array();
+
+  // Default table description in tripal_core.schema.api.inc: tripal_core_chado_featureprop_schema()
+
+  $description['foreign keys']['feature'] = array(
+        'table' => 'feature',
+        'columns' => array(
+          'feature_id' => 'feature_id',
+        ),
+  );
+  
+  $description['foreign keys']['cvterm'] = array(
+        'table' => 'cvterm',
+        'columns' => array(
+          'type_id' => 'cvterm_id',
+        ),
+  );
+  
+  $referring_tables = array('analysisfeature',
+    'featureprop_pub',
+  );
+  $description['referring_tables'] = $referring_tables;
+
+  return $description;
+}
+
+/**
+ * Implements hook_chado_featureloc_schema()
+ * Purpose: To add descriptions and foreign keys to default table description
+ * Note: This array will be merged with the array from all other implementations
+ *
+ * @return
+ *    Array describing the featureloc table
+ *
+ * @ingroup tripal_schema_api
+ */
+function tripal_feature_chado_featureloc_schema() {
+  $description = array();
+
+  // Default table description in tripal_core.schema.api.inc: tripal_core_chado_featureloc_schema()
+
+  $description['foreign keys']['feature'] = array(
+        'table' => 'feature',
+        'columns' => array(
+          'feature_id' => 'feature_id',
+          'srcfeature_id' => 'feature_id'
+        ),
+  );
+  
+  $referring_tables = array('analysisfeature',
+    'featureloc_pub',
+  );
+  $description['referring_tables'] = $referring_tables;
+
+  return $description;
+}
+
+/**
+ * Implements hook_chado_feature_dbxref_schema()
+ * Purpose: To add descriptions and foreign keys to default table description
+ * Note: This array will be merged with the array from all other implementations
+ *
+ * @return
+ *    Array describing the feature_dbxref table
+ *
+ * @ingroup tripal_schema_api
+ */
+function tripal_feature_chado_feature_dbxref_schema() {
+  $description = array();
+
+  // Default table description in tripal_core.schema.api.inc: tripal_core_chado_feature_dbxref_schema()
+
+  $description['foreign keys']['feature'] = array(
+        'table' => 'feature',
+        'columns' => array(
+          'feature_id' => 'feature_id',
+        ),
+  );
+
+  $description['foreign keys']['dbxref'] = array(
+        'table' => 'dbxref',
+        'columns' => array(
+          'dbxref_id' => 'dbxref_id',
+        ),
+  );
+
+  return $description;
+}
+/**
+ * Implements hook_chado_feature_relationship_schema()
+ * Purpose: To add descriptions and foreign keys to default table description
+ * Note: This array will be merged with the array from all other implementations
+ *
+ * @return
+ *    Array describing the feature_dbxref table
+ *
+ * @ingroup tripal_schema_api
+ */
+function tripal_feature_chado_feature_relationship_schema() {
+  $description = array();
+
+  // Default table description in tripal_core.schema.api.inc: tripal_core_chado_feature_dbxref_schema()
+
+  $description['foreign keys']['feature'] = array(
+        'table' => 'feature',
+        'columns' => array(
+          'object_id' => 'feature_id',
+          'subject_id' => 'feature_id',
+        ),
+  );
+  $description['foreign keys']['cvterm'] = array(
+        'table' => 'cvterm',
+        'columns' => array(
+          'type_id' => 'cvterm_id',
+        ),
+  );
+  return $description;
+}
+/**
+ * Implements hook_chado_feature_relationship_schema()
+ * Purpose: To add descriptions and foreign keys to default table description
+ * Note: This array will be merged with the array from all other implementations
+ *
+ * @return
+ *    Array describing the feature_dbxref table
+ *
+ * @ingroup tripal_schema_api
+ */
+function tripal_feature_chado_feature_cvterm_schema() {
+  $description = array();
+
+  // Default table description in tripal_core.schema.api.inc: tripal_core_chado_feature_dbxref_schema()
+
+  $description['foreign keys']['feature'] = array(
+        'table' => 'feature',
+        'columns' => array(
+          'feature_id' => 'feature_id',
+        ),
+  );
+  $description['foreign keys']['cvterm'] = array(
+        'table' => 'cvterm',
+        'columns' => array(
+          'type_id' => 'cvterm_id',
+        ),
+  );
+  $description['foreign keys']['pub'] = array(
+        'table' => 'pub',
+        'columns' => array(
+          'pub_id' => 'pub_id',
+        ),
+  );
+  return $description;
+}
+/**
+*
+* @ingroup tripal_schema_api
+*/
+function tripal_feature_chado_feature_synonym_schema() {
+  $description = array();
+
+  // Default table description in tripal_core.schema.api.inc: tripal_core_chado_feature_dbxref_schema()
+
+  $description['foreign keys']['feature'] = array(
+        'table' => 'feature',
+        'columns' => array(
+          'feature_id' => 'feature_id',
+        ),
+  );
+  $description['foreign keys']['synonym'] = array(
+        'table' => 'synonym',
+        'columns' => array(
+          'synonym_id' => 'synonym_id',
+        ),
+  );
+  $description['foreign keys']['pub'] = array(
+        'table' => 'pub',
+        'columns' => array(
+          'pub_id' => 'pub_id',
+        ),
+  );
+
+  return $description;
+}

+ 14 - 0
modules/base/tripal_feature/tripal_feature.info

@@ -0,0 +1,14 @@
+; $Id: tripal_feature.info,v 1.4 2009/10/01 17:52:24 ccheng Exp $
+name = Tripal Feature
+description = A module for interfacing the GMOD chado database with Drupal, providing viewing, inserting and editing of chado features.
+core = 6.x
+project = tripal_feature
+package = Tripal
+dependencies[] = search
+dependencies[] = path
+dependencies[] = tripal_core
+dependencies[] = tripal_db
+dependencies[] = tripal_cv
+dependencies[] = tripal_organism
+dependencies[] = tripal_analysis
+version = 6.x-0.3.1b

+ 195 - 0
modules/base/tripal_feature/tripal_feature.install

@@ -0,0 +1,195 @@
+<?php
+
+/**
+*  Implementation of hook_install();
+*
+* @ingroup tripal_feature
+*/
+function tripal_feature_install(){
+   // create the module's data directory
+   tripal_create_moddir('tripal_feature');
+
+   // create the tables that correlate drupal nodes with chado 
+   // features, organisms, etc....
+   drupal_install_schema('tripal_feature');
+  
+   // add the materialized view
+   tripal_feature_add_organism_count_mview();
+
+}
+/**
+*  Update for Drupal 6.x, Tripal 0.2b, Feature Module 0.2
+*  This update adjusts the materialized view by adding a 'cvterm_id' column
+*
+* @ingroup tripal_feature
+*/
+function tripal_feature_update_6000(){
+   // recreate the materialized view
+   tripal_feature_add_organism_count_mview();
+   $ret = array(
+      '#finished' => 1,
+   );
+   
+   return $ret;
+}
+
+/**
+*
+* @ingroup tripal_feature
+*/
+function tripal_feature_update_6300(){
+   // add the relationship aggregator table to the database
+   $schema = tripal_feature_get_schemas('tripal_feature_relagg');
+   $ret = array();
+   db_create_table($ret, 'tripal_feature_relagg', $schema['tripal_feature_relagg']);
+   
+   return $ret;
+}
+/**
+*
+* @ingroup tripal_feature
+*/
+function tripal_feature_add_organism_count_mview(){
+   $view_name = 'organism_feature_count';
+
+   // Drop the MView table if it exists
+   $mview_id = tripal_mviews_get_mview_id($view_name);
+   if($mview_id){
+      tripal_mviews_action("delete",$mview_id);
+   }
+
+   // Create the MView
+   tripal_add_mview(
+      // view name
+      $view_name,
+      // tripal module name
+      'tripal_feature',
+      // table name
+      $view_name,
+      // table schema definition
+      'organism_id integer, genus character varying(255), '.
+      '  species character varying(255), '.
+      '  common_name character varying(255), '.
+      '  num_features integer, cvterm_id integer, '.
+      '  feature_type character varying(255)',
+      // columns for indexing
+      'organism_id,cvterm_id,feature_type',
+      // SQL statement to populate the view
+      'SELECT O.organism_id, O.genus, O.species, O.common_name, 
+          count(F.feature_id) as num_features, 
+          CVT.cvterm_id, CVT.name as feature_type 
+       FROM {Organism} O 
+          INNER JOIN Feature F           ON O.Organism_id = F.organism_id 
+          INNER JOIN Cvterm CVT          ON F.type_id = CVT.cvterm_id 
+       GROUP BY O.Organism_id, O.genus, O.species, O.common_name,
+          CVT.cvterm_id, CVT.name',
+      // special index
+      ''
+   );
+
+   // add a job to the job queue so this view gets updated automatically next
+   // time the job facility is run
+   $mview_id = tripal_mviews_get_mview_id($view_name);
+   if($mview_id){
+      tripal_mviews_action('update',$mview_id);
+   }
+}
+/**
+* Implementation of hook_schema().
+*
+* @ingroup tripal_feature
+*/
+function tripal_feature_schema() {
+   $schema = tripal_feature_get_schemas();
+   return $schema;
+}
+/**
+* Implementation of hook_uninstall()
+*
+* @ingroup tripal_feature
+*/
+function tripal_feature_uninstall(){
+
+   // Drop the MView table if it exists
+   $mview_id = tripal_mviews_get_mview_id('organism_feature_count');
+   if($mview_id){
+      tripal_mviews_action("delete",$mview_id);
+   }
+
+   drupal_uninstall_schema('tripal_feature');
+
+   // Get the list of nodes to remove
+   $sql_feature_id = "SELECT nid, vid ".
+                 "FROM {node} ".
+                 "WHERE type='chado_feature'";
+   $result = db_query($sql_feature_id);
+   while ($node = db_fetch_object($result)) {
+      node_delete($node->nid);
+   }
+}
+
+/**
+* This function simply defines all tables needed for the module to work
+* correctly.  By putting the table definitions in a separate function we 
+* can easily provide the entire list for hook_install or individual
+* tables for an update.
+*
+* @ingroup tripal_feature
+*/
+function tripal_feature_get_schemas ($table = NULL){  
+  $schema = array();
+
+
+  if(!$table or strcmp($table,'chado_feature')==0){
+     $schema['chado_feature'] = array(
+         'fields' => array(
+            'vid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
+            'nid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
+            'feature_id' => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
+            'sync_date' => array ('type' => 'int', 'not null' => FALSE, 'description' => 'UNIX integer sync date/time'),
+         ),
+         'indexes' => array(
+            'feature_id' => array('feature_id')
+          ),
+         'unique keys' => array(
+            'nid_vid' => array('nid','vid'),
+            'vid' => array('vid')
+         ),
+         'primary key' => array('nid'),
+     );
+  }
+  if(!$table or strcmp($table,'tripal_feature_relagg')==0){
+     $schema['tripal_feature_relagg'] = array(
+        'fields' => array(
+           'type_id' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
+           'rel_type_id' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
+        ),
+        'indexes' => array(
+           'type_id' => array('type_id')
+        ),
+     );
+  }
+
+  return $schema;
+}
+
+/**
+ * Implementation of hook_requirements(). Make sure 'Tripal Core' is enabled
+ * before installation
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_requirements($phase) {
+   $requirements = array();
+   if ($phase == 'install') {
+      // make sure the core module is installed...
+      if (!function_exists('tripal_create_moddir')) {
+         $requirements ['tripal_feature'] = array(
+            'title' => "tripal_feature",
+            'value' => "error. Some required modules are just being installed. Please try again.",
+            'severity' => REQUIREMENT_ERROR,
+         );
+      }
+   }
+   return $requirements;
+}

+ 2189 - 0
modules/base/tripal_feature/tripal_feature.module

@@ -0,0 +1,2189 @@
+<?php
+
+/**
+ * @defgroup tripal_feature Feature
+ * @{
+ * Provides functions for managing chado features including creating details pages for each feature
+ * @}
+ * @ingroup tripal_modules
+ */
+ 
+require_once "tripal_feature.admin.inc";
+require_once "syncFeatures.php";
+require_once "indexFeatures.php";
+require_once "fasta_loader.php";
+require_once "gff_loader.php";
+
+require_once "tripal_feature.api.inc";
+
+require_once "tripal_feature-delete.inc";
+require_once "tripal_feature-secondary_tables.inc";
+require_once "tripal_feature-properties.inc";
+require_once "tripal_feature-relationships.inc";
+require_once "tripal_feature-db_references.inc";
+
+
+/**
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_init(){
+
+   // add the jGCharts JS and CSS
+   drupal_add_js (drupal_get_path('theme', 'tripal').'/js/tripal_feature.js'); 
+   drupal_add_js (drupal_get_path('theme', 'tripal').'/js/jgcharts/jgcharts.js'); 
+
+   drupal_add_css(drupal_get_path('theme', 'tripal').
+                                  '/css/tripal_feature.css');
+}
+
+/**
+ * Implements hook_views_api()
+ *
+ * Purpose: Essentially this hook tells drupal that there is views support for
+ *  for this module which then includes tripal_db.views.inc where all the
+ *  views integration code is
+ *
+ * @ingroup tripal_feature
+ */ 
+function tripal_feature_views_api() {
+   return array(
+      'api' => 2.0,
+   );
+}
+
+/**
+ * Display help and module information
+ *
+ * @param 
+ *   path which path of the site we're displaying help
+ * @param 
+ *   arg array that holds the current path as would be returned from arg() function
+ *
+ * @return 
+ *   help text for the path
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_help($path, $arg) {
+   $output = '';
+   switch ($path) {
+      case "admin/help#tripal_feature":
+         $output='<p>'.t("Displays links to nodes created on this date").'</p>';
+         break;
+   }
+   return $output;
+}
+
+/**
+ *  Provide information to drupal about the node types that we're creating
+ *  in this module
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_node_info() {
+   $nodes = array();
+
+   $nodes['chado_feature'] = array(
+      'name' => t('Feature'),
+      'module' => 'chado_feature',
+      'description' => t('A feature from the chado database'),
+      'has_title' => FALSE,
+      'title_label' => t('Feature'),
+      'has_body' => FALSE,
+      'body_label' => t('Feature Description'),
+      'locked' => TRUE
+   );
+   return $nodes;
+}
+
+/**
+ *  Set the permission types that the chado module uses.  Essentially we
+ *  want permissionis that protect creation, editing and deleting of chado
+ *  data objects
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_perm(){
+   return array(
+      'access chado_feature content',
+      'create chado_feature content',
+      'delete chado_feature content',
+      'edit chado_feature content',
+      'manage chado_feature aggregator',
+   );
+}
+
+/**
+ *  Set the permission types that the module uses.
+ *
+ * @ingroup tripal_feature
+ */
+function chado_feature_access($op, $node, $account) {
+	if ($op == 'create') {
+		if(!user_access('create chado_feature content', $account)){
+			return FALSE;
+      }
+	}
+
+	if ($op == 'update') {
+		if (!user_access('edit chado_feature content', $account)) {
+			return FALSE;
+		}
+	}
+	if ($op == 'delete') {
+		if (!user_access('delete chado_feature content', $account)) {
+			return FALSE;
+		}
+	}
+	if ($op == 'view') {
+		if(!user_access('access chado_feature content', $account)){
+         return FALSE;
+      }
+	}
+   return NULL;
+}
+/**
+ *  Menu items are automatically added for the new node types created
+ *  by this module to the 'Create Content' Navigation menu item.  This function
+ *  adds more menu items needed for this module.
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_menu() {
+   $items = array();
+
+   // the administative settings menu
+   $items['admin/tripal/tripal_feature'] = array(
+     'title' => 'Features',
+     'description' => 'Basic Description of Tripal Organism Module Functionality',
+     'page callback' => 'tripal_feature_module_description_page',
+     'access arguments' => array('administer site configuration'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+   $items['admin/tripal/tripal_feature/configuration'] = array(
+     'title' => 'Feature Configuration',
+     'description' => 'Settings for Chado Features',
+     'page callback' => 'drupal_get_form',
+     'page arguments' => array('tripal_feature_admin'),
+     'access arguments' => array('administer site configuration'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+   $items['admin/tripal/tripal_feature/fasta_loader'] = array(
+     'title' => 'Import a multi-FASTA file',
+     'description' => 'Load sequences from a multi-FASTA file into Chado',
+     'page callback' => 'drupal_get_form',
+     'page arguments' => array('tripal_feature_fasta_load_form'),
+     'access arguments' => array('administer site configuration'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+   $items['admin/tripal/tripal_feature/gff3_load'] = array(
+     'title' => 'Import a GFF3 file',
+     'description' => 'Import a GFF3 file into Chado',
+     'page callback' => 'drupal_get_form',
+     'page arguments' => array('tripal_feature_gff3_load_form'),
+     'access arguments' => array('access administration pages'),
+     'type' => MENU_NORMAL_ITEM,
+   );  
+
+   $items['admin/tripal/tripal_feature/delete'] = array(
+     'title' => ' Delete Features',
+     'description' => 'Delete multiple features from Chado',
+     'page callback' => 'drupal_get_form',
+     'page arguments' => array('tripal_feature_delete_form'),
+     'access arguments' => array('access administration pages'),
+     'type' => MENU_NORMAL_ITEM,
+   ); 
+
+   $items['admin/tripal/tripal_feature/sync'] = array(
+     'title' => ' Sync Features',
+     'description' => 'Sync features from Chado with Drupal',
+     'page callback' => 'drupal_get_form',
+     'page arguments' => array('tripal_feature_sync_form'),
+     'access arguments' => array('access administration pages'),
+     'type' => MENU_NORMAL_ITEM,
+   ); 
+
+  // Adding Secondary Properties
+
+
+  $items['node/%tf_node/tf_properties'] = array(       
+    'title' => t('Add Properties & Synonyms'),                         
+    'description' => t('Settings for Features'),
+    'page callback' => 'tripal_feature_add_ALL_property_page',           
+    'page arguments' => array(1), 
+    'access arguments' => array('create chado_feature content'),
+    'type' => MENU_CALLBACK
+  ); 
+
+  $items['node/%tf_node/tf_db_references'] = array(                        
+    'title' => t('Add Database References'),                   
+    'description' => t('Settings for Features'),              
+    'page callback' => 'tripal_feature_add_ALL_dbreferences_page',                         
+    'page arguments' => array(1),
+    'access arguments' => array('create chado_feature content'),
+    'type' => MENU_CALLBACK
+  ); 
+
+  $items['node/%tf_node/tf_relationships'] = array(                      
+    'title' => t('Add Relationships'),                      
+    'description' => t('Settings for Features'),               
+    'page callback' => 'tripal_feature_add_ALL_relationships_page',                          
+    'page arguments' => array(1),
+    'access arguments' => array('create chado_feature content'),
+    'type' => MENU_CALLBACK
+  );
+  //Edit/Deleting Secondary Properties-------------
+/**
+  $items['node/%tf_node/tf_edit_properties'] = array(
+    'title' => t('Edit Properties'),
+    'description' => t('Settings for Features'),
+    'page callback' => 'tripal_feature_edit_ALL_properties_page',
+    'page arguments' => array(1),
+    'access arguments' => array('edit chado_feature content'),
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 8,
+  );
+
+    $items['node/%tf_node/tf_edit_relationships'] = array(
+    'title' => t('Edit Relationships'),
+    'description' => t('Settings for Feature'), 
+    'page callback' => 'tripal_feature_edit_ALL_relationships_page',
+    'page arguments' => array(1),
+    'access arguments' => array('edit chado_feature content'),
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 9,
+  );
+*/
+  $items['node/%tf_node/tf_edit_db_references'] = array(
+    'title' => t('Edit References'),
+    'description' => t('Settings for Feature'),
+    'page callback' => 'tripal_feature_edit_ALL_dbreferences_page',
+    'page arguments' => array(1),
+    'access arguments' => array('edit chado_feature content'),
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 10,
+  );
+
+ // managing relationship aggregates
+   $items['admin/tripal/tripal_feature/aggregate'] = array(
+     'title' => 'Feature Relationship Aggegators',
+     'description' => t('Features have relationships with other features and it may be desirable to aggregate the content from one ore more child or parent feature.'),
+     'page callback' => 'tripal_feature_aggregator_page',
+     'access arguments' => array('manage chado_feature aggregator'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+
+   $items['admin/tripal/tripal_feature/aggregate/new'] = array(
+     'title' => 'Add an Aggregator',
+     'page callback' => 'drupal_get_form',
+     'page arguments' => array('tripal_feature_aggregator_form'),
+     'access arguments' => array('manage chado_feature aggregator'),
+     'type' => MENU_NORMAL_ITEM,
+   );
+   $items['admin/tripal/tripal_feature/aggregate/edit/js'] = array(
+     'title' => 'Edit an Aggegator',
+     'page callback' => 'tripal_feature_aggregator_ajax_edit',
+     'access arguments' => array('manage chado_feature aggregator'),
+     'type' => MENU_CALLBACK,
+   );
+
+   return $items;
+}
+
+/**
+ * Implements Menu wildcard_load hook
+ * Purpose: Allows the node ID of a chado feature to be dynamically 
+ *   pulled from the path. The node is loaded from this node ID
+ *   and supplied to the page as an arguement
+ *
+ * @ingroup tripal_feature
+ */
+function tf_node_load($nid) {
+  if (is_numeric($nid)) {
+    $node = node_load($nid);
+    if ($node->type == 'chado_feature') {
+      return $node;
+    }
+  }
+  return FALSE;
+}
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_block($op = 'list', $delta = 0, $edit=array()){
+   switch($op) {
+      case 'list':
+         $blocks['references']['info'] = t('Tripal Feature References');
+         $blocks['references']['cache'] = BLOCK_NO_CACHE;
+
+         $blocks['base']['info'] = t('Tripal Feature Details');
+         $blocks['base']['cache'] = BLOCK_NO_CACHE;
+
+         $blocks['sequence']['info'] = t('Tripal Feature Sequence');
+         $blocks['sequence']['cache'] = BLOCK_NO_CACHE;
+
+         $blocks['synonyms']['info'] = t('Tripal Feature Synonyms');
+         $blocks['synonyms']['cache'] = BLOCK_NO_CACHE;
+
+         $blocks['properties']['info'] = t('Tripal Feature Properties');
+         $blocks['properties']['cache'] = BLOCK_NO_CACHE;;
+
+         $blocks['featureloc_sequences']['info'] = t('Tripal Formatted Sequence');
+         $blocks['featureloc_sequences']['cache'] = BLOCK_NO_CACHE;
+
+         $blocks['alignments']['info'] = t('Tripal Feature Alignments');
+         $blocks['alignments']['cache'] = BLOCK_NO_CACHE;
+
+         $blocks['relationships']['info'] = t('Tripal Feature Relationships');
+         $blocks['relationships']['cache'] = BLOCK_NO_CACHE;
+
+         $blocks['org_feature_counts']['info'] = t('Tripal Organism Feature Counts');
+         $blocks['org_feature_counts']['cache'] = BLOCK_NO_CACHE;
+
+         $blocks['org_feature_browser']['info'] = t('Tripal Organism Feature Browser');
+         $blocks['org_feature_browser']['cache'] = BLOCK_NO_CACHE;
+         return $blocks;
+
+
+      case 'view':
+         if(user_access('access chado_feature content') and arg(0) == 'node' and is_numeric(arg(1))) {
+            $nid = arg(1);
+            $node = node_load($nid);
+ 
+            $block = array();
+            switch($delta){
+               case 'references':
+                  $block['subject'] = t('References');
+                  $block['content'] = theme('tripal_feature_references',$node);
+                  break;
+               case 'base':
+                  $block['subject'] = t('Feature Details');
+                  $block['content'] = theme('tripal_feature_base',$node);
+                  break;
+               case 'synonyms':
+                  $block['subject'] = t('Synonyms');
+                  $block['content'] = theme('tripal_feature_synonyms',$node);
+                  break;
+               case 'properties':
+                  $block['subject'] = t('Properties');
+                  $block['content'] = theme('tripal_feature_properties',$node);
+                  break;;
+               case 'sequence':
+                  $block['subject'] = t('Sequence');
+                  $block['content'] = theme('tripal_feature_sequence',$node);
+                  break;
+               case 'featureloc_sequences':
+                  $block['subject'] = t('Formatted Sequences');
+                  $block['content'] = theme('tripal_feature_featureloc_sequences',$node);
+                  break;
+               case 'alignments':
+                  $block['subject'] = t('Alignments');
+                  $block['content'] = theme('tripal_feature_featurelocs',$node);
+                  break;
+               case 'relationships':
+                  $block['subject'] = t('Relationships');
+                  $block['content'] = theme('tripal_feature_relationships',$node);
+                  break;
+               case 'org_feature_counts':
+                  $block['subject'] = t('Feature Type Summary');
+                  $block['content'] = theme('tripal_organism_feature_counts', $node);
+                  break;
+               case 'org_feature_browser':
+                  $block['subject'] = t('Feature Browser');
+                  $block['content'] = theme('tripal_organism_feature_browser', $node);
+                  break;
+               default :
+            }
+            return $block;
+         }
+
+   }
+}
+/**
+ *  When a new chado_feature node is created we also need to add information
+ *  to our chado_feature table.  This function is called on insert of a new node
+ *  of type 'chado_feature' and inserts the necessary information.
+ *
+ * @ingroup tripal_feature
+ */
+function chado_feature_insert($node){
+   // remove spaces, newlines from residues
+   $residues = preg_replace("/[\n\r\s]/","",$node->residues);
+   $obsolete = 'FALSE';
+   if($node->is_obsolete){
+      $obsolete = 'TRUE';
+   }
+   $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' => strlen($residues),
+      'is_obsolete' => $obsolete,
+      'type_id' => $type[0]->cvterm_id,
+      'md5checksum' => md5($residues)
+   );
+
+   $istatus = tripal_core_chado_insert('feature', $values);
+   if (!$istatus) {
+		drupal_set_message('Unable to add feature.', 'warning');
+		watchdog('tripal_organism', 
+			'Insert feature: Unable to create feature where values: %values', 
+			array('%values' => print_r($values, TRUE)),
+			WATCHDOG_WARNING
+		);
+	}  
+   $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 = '%s'";
+   $node_check = db_fetch_object(db_query($node_check_sql,$feature[0]->feature_id));
+   if(!$node_check){
+      // next add the item to the drupal table
+      $sql = "INSERT INTO {chado_feature} (nid, vid, feature_id, sync_date) ".
+             "VALUES (%d, %d, %d, " . time() . ")";
+      db_query($sql,$node->nid,$node->vid,$feature[0]->feature_id);
+   }
+}
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function chado_feature_update($node){
+   if($node->revision){
+      // TODO -- decide what to do about revisions
+   } else {
+      $residues = preg_replace("/[\n\r\s]/","",$node->residues);
+      $obsolete = 'FALSE';
+      if($node->is_obsolete){
+         $obsolete = 'TRUE';
+      }
+
+      // get the feature type id
+      $values = array(
+         'cv_id' => array(
+            'name' => 'sequence'
+         ),
+         'name' => $node->feature_type
+      );
+      $type = tripal_core_chado_select('cvterm',array('cvterm_id'),$values);
+
+      $feature_id = chado_get_id_for_node('feature',$node) ;
+
+      if(sizeof($type) > 0){
+         $match = array(
+            'feature_id' => $feature_id,
+         );     
+         $values = array(
+            'organism_id' => $node->organism_id,
+            'name' => $node->fname,
+            'uniquename' => $node->uniquename,
+            'residues' => $residues,
+            'seqlen' => strlen($residues),
+            'is_obsolete' => $obsolete,
+            'type_id' => $type[0]->cvterm_id,
+            'md5checksum' => md5($residues)
+         );
+         $status = tripal_core_chado_update('feature', $match,$values); 
+
+         // add the genbank synonyms
+         chado_feature_add_synonyms($node->synonyms,$feature_id);
+      }    
+      else {
+		   drupal_set_message('Unable to update feature.', 'warning');
+		   watchdog('tripal_organism', 
+			   'Update feature: Unable to update feature where values: %values', 
+			   array('%values' => print_r($values, TRUE)),
+			   WATCHDOG_WARNING
+		   );
+      }
+   }
+}
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function chado_feature_delete($node){
+
+   $feature_id  = chado_get_id_for_node('feature',$node);
+
+   // remove the drupal content  
+   $sql_del = "DELETE FROM {chado_feature} ".
+              "WHERE nid = %d ".
+              "AND vid = %d";
+   db_query($sql_del, $node->nid, $node->vid);
+   $sql_del = "DELETE FROM {node} ".
+              "WHERE nid = %d ".
+              "AND vid = %d";
+   db_query($sql_del, $node->nid, $node->vid);
+   $sql_del = "DELETE FROM {node_revisions} ".
+              "WHERE nid = %d ".
+              "AND vid = %d";
+   db_query($sql_del, $node->nid, $node->vid);
+
+
+   // Remove data from feature tables of chado database.  This will
+   // cause a cascade delete and remove all data in referencing tables
+   // for this feature
+   $previous_db = tripal_db_set_active('chado');
+   db_query("DELETE FROM {feature} WHERE feature_id = %d", $feature_id);
+   tripal_db_set_active($previous_db);
+   
+   drupal_set_message("The feature and all associated data were removed from ".
+      "chado");
+
+}
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function chado_feature_add_synonyms($synonyms,$feature_id){
+
+   // make sure we only have a single space between each synonym
+   $synonyms = preg_replace("/[\s\n\r]+/"," ",$synonyms);
+   // split the synonyms into an array based on a space as the delimieter
+   $syn_array = array();
+   $syn_array = explode(" ",$synonyms);
+
+   // use the chado database
+   $previous_db = tripal_db_set_active('chado');
+
+   // remove any old synonyms
+   $feature_syn_dsql = "DELETE FROM {feature_synonym} WHERE feature_id = %d";
+   if(!db_query($feature_syn_dsql,$feature_id)){
+      $error .= "Could not remove synonyms from feature. ";
+   }
+
+   // return if we don't have any synonmys to add
+   if(!$synonyms){
+      tripal_db_set_active($previous_db);
+      return;
+   }
+   // iterate through each synonym and add it to the database
+   foreach($syn_array as $syn){
+      // skip this item if it's empty
+      if(!$syn){ break; }
+
+      // check to see if we have this accession number already in the database
+      // if so then don't add it again. it messes up drupal if the insert fails.
+      // It is possible for the accession number to be present and not the feature
+      $synonym_sql = "SELECT synonym_id FROM {synonym} ".
+                     "WHERE name = '%s'";
+      $synonym = db_fetch_object(db_query($synonym_sql,$syn));
+      if(!$synonym){
+         $synonym_isql = "INSERT INTO {synonym} (name,synonym_sgml,type_id) ".
+                         "VALUES ('%s','%s', ".
+                         "   (SELECT cvterm_id ".
+                         "    FROM {CVTerm} CVT ".
+                         "    INNER JOIN CV ON CVT.cv_id = CV.cv_id ".
+                         "    WHERE CV.name = 'feature_property' and CVT.name = 'synonym'))";
+         if(!db_query($synonym_isql,$syn,$syn)){
+            $error .= "Could not add synonym. ";
+         }
+         // now get the synonym we just added
+         $synonym_sql = "SELECT synonym_id FROM {synonym} ".
+                        "WHERE name = '%s'";
+         $synonym = db_fetch_object(db_query($synonym_sql,$syn));
+      }
+
+      // now add in our new sysnonym
+      $feature_syn_isql = "INSERT INTO {feature_synonym} (synonym_id,feature_id,pub_id) ".
+                          "VALUES (%d,%d,1)";
+      if(!db_query($feature_syn_isql,$synonym->synonym_id,$feature_id)){
+         $error .= "Could not add synonyms to feature. ";
+      }
+   }
+
+   // return to the drupal database
+   tripal_db_set_active($previous_db);
+   return $error;
+
+}
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function chado_feature_add_gbaccession($accession,$feature_id){
+
+   // use chado database
+   $previous_db = tripal_db_set_active('chado');
+
+   // remove any old accession from genbank dbEST
+   $fdbxref_dsql = "DELETE FROM {feature_dbxref} ".
+                   "WHERE feature_id = %d and dbxref_id IN ".
+                   "   (SELECT DBX.dbxref_id FROM {dbxref} DBX ".
+                   "    INNER JOIN DB  ON DB.db_id = DBX.db_id ".
+                   "    INNER JOIN feature_dbxref FDBX ON DBX.dbxref_id = FDBX.dbxref_id ".
+                   "    WHERE DB.name = 'DB:Genbank' and FDBX.feature_id = %d)";
+   if(!db_query($fdbxref_dsql,$feature_id,$feature_id)){
+      $error .= "Could not remove accession from feature. ";
+   }
+
+   // if we don't have an accession number to add then just return
+   if(!$accession){
+      tripal_db_set_active($previous_db);
+      return;
+   }
+   // get the db_id
+   $db_sql = "SELECT db_id FROM {DB} ".
+             "WHERE name = 'DB:Genbank_est'";
+   $db = db_fetch_object(db_query($db_sql));
+
+   // check to see if we have this accession number already in the database
+   // if so then don't add it again. it messes up drupal if the insert fails.
+   // It is possible for the accession number to be present and not the feature
+   $dbxref_sql = "SELECT dbxref_id FROM {dbxref} ".
+                 "WHERE db_id = %d and accession = '%s'";
+   $dbxref = db_fetch_object(db_query($dbxref_sql,$db->db_id,$accession));
+   if(!$dbxref){
+      // add the accession number
+      $dbxref_isql = "INSERT INTO {dbxref} (db_id,accession) ".
+                     "  VALUES (%d, '%s') ";
+      if(!db_query($dbxref_isql,$db->db_id,$accession)){
+         $error .= 'Could not add accession as a database reference ';
+      }
+      // get the dbxref_id for the just added accession number
+      $dbxref_sql = "SELECT dbxref_id FROM {dbxref} ".
+                    "WHERE db_id = %d and accession = '%s'";
+      $dbxref = db_fetch_object(db_query($dbxref_sql,$db->db_id,$accession));
+   }
+
+
+   // associate the accession number with the feature
+   $feature_dbxref_isql = "INSERT INTO {feature_dbxref} (feature_id,dbxref_id) ".
+                          "  VALUES (%d, %d) ";
+   if(!db_query($feature_dbxref_isql,$feature_id,$dbxref->dbxref_id)){
+      $error .= 'Could not add feature database reference. ';
+   }
+
+   tripal_db_set_active($previous_db);
+   return $error;
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function chado_feature_form ($node,$param){
+
+   $type = node_get_types('type', $node);
+   $form = array();
+
+   $feature = $node->feature;
+
+   // add the residues to the feature object
+   $feature = tripal_core_expand_chado_vars($feature,'field','feature.residues');
+
+   // if the node has synonyms then use that as the form may be returning
+   // from an error.  Otherwise try to find synonyms from the database
+   $synonyms = $node->synonyms;
+   $feature = tripal_core_expand_chado_vars($feature,'table','feature_synonym');
+   $feature_synonyms = $feature->feature_synonym;
+   if(!$synonyms){
+      if (!is_array($feature_synonyms)) {
+         $synonyms = $feature_synonyms->synonym_id->name;
+      } 
+      elseif(is_array($feature_synonyms)) { 
+         foreach($feature_synonyms as $index => $synonym){
+            $synonyms .= $synonym->synonym_id->name ."\n";
+         }
+      }
+   }
+
+   $analyses = $node->analyses;
+   $references = $node->references;
+
+   // We need to pass above variables for preview to show
+   $form['feature'] = array(
+      '#type' => 'value',
+      '#value' => $feature
+   );
+   // This field is read when previewing a node
+   $form['synonyms'] = array(
+      '#type' => 'value',
+      '#value' => $synonyms
+   );
+   // This field is read when previewing a node
+   $form['analyses'] = array(
+      '#type' => 'value',
+      '#value' => $analyses
+   );
+   // This field is read when previewing a node
+   $form['references'] = array(
+      '#type' => 'value',
+      '#value' => $references
+   );
+
+   // keep track of the feature id if we have one.  If we do have one then
+   // this would indicate an update as opposed to an insert.
+   $form['feature_id'] = array(
+      '#type' => 'value',
+      '#value' => $feature->feature_id,
+   );
+
+   $form['title']= array(
+      '#type' => 'textfield',
+      '#title' => t('Title'),
+      '#required' => TRUE,
+      '#default_value' => $node->title,
+      '#description' => t('The title must be a unique identifier for this feature.  It is recommended to use a combination of uniquename, organism and feature type in the title as this is guranteed to be unique.'),
+      '#weight' => 1,
+      '#maxlength' => 255
+   );
+
+   $form['uniquename']= array(
+      '#type' => 'textfield',
+      '#title' => t('Unique Feature Name'),
+      '#required' => TRUE,
+      '#default_value' => $feature->uniquename,
+      '#description' => t('Enter a unique name for this feature.  This name must be unique for the organism and feature type.'),
+      '#weight' => 1,
+      '#maxlength' => 255
+   );
+
+   $form['fname']= array(
+      '#type' => 'textfield',
+      '#title' => t('Feature Name'),
+      '#required' => TRUE,
+      '#default_value' => $feature->name,
+      '#description' => t('Enter the name used by humans to refer to this feature.'),
+      '#weight' => 1,
+      '#maxlength' => 255
+   );
+
+   // get the list of supported feature types
+   $ftypes = array();
+   $ftypes[''] = '';
+   $supported_ftypes = split("[ \n]",variable_get('tripal_feature_type_setting','gene mRNA EST contig'));
+   foreach($supported_ftypes as $ftype){
+      $ftypes["$ftype"] = $ftype;
+   }
+
+   $form['feature_type'] = array (
+     '#title'       => t('Feature Type'),
+     '#type'        => t('select'),
+     '#description' => t("Choose the feature type."),
+     '#required'    => TRUE,
+     '#default_value' => $feature->type_id->name,
+     '#options'     => $ftypes,
+     '#weight'      => 2
+   );
+   // get the list of organisms
+   $sql = "SELECT * FROM {Organism} ORDER BY genus, species";
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $org_rset = db_query($sql);
+   tripal_db_set_active($previous_db);  // now use drupal database
+
+   //
+   $organisms = array();
+   $organisms[''] = '';
+   while($organism = db_fetch_object($org_rset)){
+      $organisms[$organism->organism_id] = "$organism->genus $organism->species ($organism->common_name)";
+   }
+
+   $form['organism_id'] = array (
+     '#title'       => t('Organism'),
+     '#type'        => t('select'),
+     '#description' => t("Choose the organism with which this feature is associated "),
+     '#required'    => TRUE,
+     '#default_value' => $feature->organism_id->organism_id,
+     '#options'     => $organisms,
+     '#weight'      => 3,
+   );
+
+   // Get synonyms
+   if ($synonyms) {
+      if (is_array($synonyms)) {
+         foreach ($synonyms as $synonym){
+            $syn_text .= "$synonym->name\n";
+         }
+      } else {
+         $syn_text = $synonyms;
+      }
+   }
+   $form['synonyms']= array(
+      '#type' => 'textarea',
+      '#title' => t('Synonyms'),
+      '#required' => FALSE,
+      '#default_value' => $syn_text,
+      '#description' => t('Enter alternate names (synonmys) for this feature to help in searching and identification. You may enter as many alternate names as needed separated by spaces or on different lines.'),
+      '#weight' => 5,
+   );
+
+   $form['residues']= array(
+      '#type' => 'textarea',
+      '#title' => t('Residues'),
+      '#required' => FALSE,
+      '#default_value' => $feature->residues,
+      '#description' => t('Enter the nucelotide sequences for this feature'),
+      '#weight' => 6
+   );
+
+   $checked = '';
+   if($feature->is_obsolete == 't'){
+      $checked = '1';
+   }
+   $form['is_obsolete']= array(
+      '#type' => 'checkbox',
+      '#title' => t('Is Obsolete'),
+      '#required' => FALSE,
+      '#default_value' => $checked,
+      '#description' => t('Check this box if this sequence should be retired and no longer included in further analysis.'),
+      '#weight' => 8
+   );
+   return $form;
+}
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function chado_feature_validate($node){
+   $result = 0;
+
+   // 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
+   // two sequences the same uniquename
+   if($node->feature_id){
+      $sql = "SELECT * 
+              FROM {Feature} F
+                INNER JOIN {cvterm} CVT ON F.type_id = CVT.cvterm_id
+              WHERE uniquename = '%s' 
+               AND organism_id = %d AND CVT.name = '%s' AND NOT feature_id = %d";
+      $previous_db = tripal_db_set_active('chado');
+      $result = db_fetch_object(db_query($sql, $node->uniquename,$node->organism_id,$node->feature_type,$node->feature_id));
+      tripal_db_set_active($previous_db);
+      if($result){
+         form_set_error('uniquename',t("Feature update cannot proceed. The feature name '$node->uniquename' is not unique for this organism. Please provide a unique name for this feature. "));
+      }
+   }
+
+   // 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
+   else {
+      $sql = "SELECT * 
+              FROM {Feature} F
+                INNER JOIN {cvterm} CVT ON F.type_id = CVT.cvterm_id
+              WHERE uniquename = '%s' 
+               AND organism_id = %d AND CVT.name = '%s'";
+      $previous_db = tripal_db_set_active('chado');
+      $result = db_fetch_object(db_query($sql, $node->uniquename,$node->organism_id,$node->feature_type));
+      tripal_db_set_active($previous_db);
+      if($result){
+         form_set_error('uniquename',t("Feature insert cannot proceed. The feature name '$node->uniquename' already exists for this organism. Please provide a unique name for this feature. "));
+      }
+   }
+
+   // we want to remove all characters except IUPAC nucleotide characters from the
+   // the residues. however, residues are not required so if blank then we'll skip
+   // this step
+   if($node->residues){
+      $residues = preg_replace("/[^\w]/",'',$node->residues);
+      if(!preg_match("/^[ACTGURYMKSWBDHVN]+$/i",$residues)){
+         form_set_error('residues',t("The residues in feature $node->name contains more than the nucleotide IUPAC characters. Only the following characters are allowed: A,C,T,G,U,R,Y,M,K,S,W,B,D,H,V,N: '" . $residues ."'"));
+      }
+   }
+
+   // we don't allow a genbank accession number for a contig
+   if($node->feature_type == 'contig' and $node->gbaccession){
+      form_set_error('gbaccession',t("Contigs cannot have a genbank accession number.  Please change the feature type or remove the accession number"));
+   }
+
+}
+/**
+ *  When a node is requested by the user this function is called to allow us
+ *  to add auxiliary data to the node object.
+ *
+ * @ingroup tripal_feature
+ */
+function chado_feature_load($node){
+   // get the feature details from chado
+   $feature_id = chado_get_id_for_node('feature',$node);
+
+   $values = array('feature_id' => $feature_id);
+   $feature = tripal_core_generate_chado_var('feature',$values);
+
+   $additions->feature = $feature;
+   return $additions;
+}
+/**
+ *  
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_load_organism ($organism_id){
+   // add organism details
+   $sql = "SELECT * FROM {organism} WHERE organism_id = %d";
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $organism = db_fetch_object(db_query($sql,$organism_id));
+   tripal_db_set_active($previous_db);  // now use drupal database
+   return $organism;
+}
+/**
+ *  
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_load_synonyms ($feature_id){
+
+   $sql = "SELECT S.name ".
+          "FROM {Feature_Synonym} FS ".
+          "  INNER JOIN {Synonym} S ".
+          "    ON FS.synonym_id = S.Synonym_id ".
+          "WHERE FS.feature_id = %d ".
+          "ORDER BY S.name ";
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $results = db_query($sql,$feature_id);
+   tripal_db_set_active($previous_db);  // now use drupal database
+   $synonyms = array();
+   $i=0;
+   while($synonym = db_fetch_object($results)){
+      $synonyms[$i++] = $synonym;
+   }
+   return $synonyms;
+}
+/**
+ * 
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_load_properties ($feature_id){
+
+   $sql = "SELECT CVT.name as cvname, FS.type_id, FS.value, FS.rank,
+             CVT.definition, CVT.is_obsolete, 
+             DBX.dbxref_id,DBX.accession,DB.name as dbname, 
+             DB.urlprefix, DB.description as db_description, DB.url
+           FROM {featureprop} FS 
+             INNER JOIN {cvterm} CVT ON FS.type_id = CVT.cvterm_id
+             INNER JOIN {dbxref} DBX ON CVT.dbxref_id = DBX.dbxref_id
+             INNER JOIN {db} DB      ON DB.db_id = DBX.db_id
+           WHERE FS.feature_id = %d
+           ORDER BY FS.rank ASC";
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $results = db_query($sql,$feature_id);
+   tripal_db_set_active($previous_db);  // now use drupal database
+   $i=0;
+   $properties = array();
+   while($property = db_fetch_object($results)){
+      $properties[$i++] = $property;
+   }
+   return $properties;
+}
+/**
+ *  
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_load_references ($feature_id){
+
+   $sql = "SELECT F.uniquename,F.Feature_id,DBX.accession,DB.description as dbdesc, ".
+          "   DB.db_id, DB.name as db_name, DB.urlprefix,DBX.dbxref_id ".
+          "FROM {feature} F ".
+          "  INNER JOIN {feature_dbxref} FDBX on F.feature_id = FDBX.feature_id ".
+          "  INNER JOIN {dbxref} DBX on DBX.dbxref_id = FDBX.dbxref_id ".
+          "  INNER JOIN {db} on DB.db_id = DBX.db_id ".
+          "WHERE F.feature_id = %d ".
+          "ORDER BY DB.name ";
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $results = db_query($sql,$feature_id);
+   tripal_db_set_active($previous_db);  // now use drupal database
+   $references = array();
+   $i=0;
+   while($accession = db_fetch_object($results)){
+      $references[$i++] = $accession;
+   }
+   return $references;
+}
+/**
+ *  
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_load_featurelocs ($feature_id,$side = 'as_parent',$aggregate = 1){
+
+   $sql = "SELECT 
+             F.name, F.feature_id, F.uniquename,
+             FS.name as src_name, 
+             FS.feature_id as src_feature_id, 
+             FS.uniquename as src_uniquename,
+             CVT.name as cvname, CVT.cvterm_id,
+             CVTS.name as src_cvname, CVTS.cvterm_id as src_cvterm_id,
+             FL.fmin, FL.fmax, FL.is_fmin_partial, FL.is_fmax_partial,FL.strand, 
+             FL.phase
+           FROM {featureloc} FL
+              INNER JOIN {feature} F on FL.feature_id = F.feature_id
+              INNER JOIN {feature} FS on FS.feature_id = FL.srcfeature_id
+              INNER JOIN {cvterm} CVT on F.type_id = CVT.cvterm_id
+              INNER JOIN {cvterm} CVTS on FS.type_id = CVTS.cvterm_id
+           ";
+   if(strcmp($side,'as_parent')==0){
+      $sql .= "WHERE FL.srcfeature_id = %d ";
+   }
+   if(strcmp($side,'as_child')==0){
+      $sql .= "WHERE FL.feature_id = %d ";
+   }
+
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $flresults = db_query($sql, $feature_id);
+   tripal_db_set_active($previous_db);  // now use drupal database 
+
+   // copy the results into an array
+   $i=0;
+   $featurelocs = array();
+   while($loc = db_fetch_object($flresults)){
+      // 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 = %d';
+
+      $ffeature = db_fetch_object(db_query($sql, $loc->feature_id));
+      $sfeature = db_fetch_object(db_query($sql, $loc->src_feature_id));
+      $loc->fnid = $ffeature->nid;
+      $loc->snid = $sfeature->nid;
+      // add the result to the array
+      $featurelocs[$i++] = $loc;
+   }
+
+   // Add the relationship feature locs if aggregate is turned on
+   if($aggregate and strcmp($side,'as_parent')==0){ 
+      // get the relationships for this feature without substituting any children
+      // for the parent. We want all relationships
+      $relationships = tripal_feature_get_aggregate_relationships($feature_id,0);
+      foreach($relationships as $rindex => $rel){ 
+         // get the featurelocs for each of the relationship features
+         $rel_featurelocs = tripal_feature_load_featurelocs ($rel->subject_id,'as_child',0);
+         foreach($rel_featurelocs as $findex => $rfloc){
+            $featurelocs[$i++] = $rfloc;
+         }
+      }
+   } 
+   
+   usort($featurelocs,'tripal_feature_sort_locations');
+   return $featurelocs;
+}
+/**
+ *  used to sort the feature locs by start position
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_sort_locations($a,$b){
+   return strnatcmp($a->fmin, $b->fmin); 
+}
+/**
+ *  
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_load_relationships ($feature_id,$side = 'as_subject'){
+   // get the relationships for this feature.  The query below is used for both
+   // querying the object and subject relationships
+   $sql = "SELECT 
+             FS.name as subject_name, 
+             FS.uniquename as subject_uniquename, 
+             CVTS.name as subject_type,
+             CVTS.cvterm_id as subject_type_id,
+             FR.subject_id,          
+             FR.type_id as relationship_type_id,
+             CVT.name as rel_type,     
+             FO.name as object_name, 
+             FO.uniquename as object_uniquename, 
+             CVTO.name as object_type,
+             CVTO.cvterm_id as object_type_id,
+             FR.object_id, 
+             FR.rank
+           FROM {feature_relationship} FR
+             INNER JOIN {cvterm} CVT ON FR.type_id = CVT.cvterm_id
+             INNER JOIN {feature} FS ON FS.feature_id = FR.subject_id
+             INNER JOIN {feature} FO ON FO.feature_id = FR.object_id
+             INNER JOIN {cvterm} CVTO ON FO.type_id = CVTO.cvterm_id
+             INNER JOIN {cvterm} CVTS ON FS.type_id = CVTS.cvterm_id
+   ";
+   if(strcmp($side,'as_object')==0){
+      $sql .= " WHERE FR.object_id = %d";
+   }
+   if(strcmp($side,'as_subject')==0){
+      $sql .= " WHERE FR.subject_id = %d";
+   }
+   $sql .= " ORDER BY FR.rank";
+
+   // get the relationships 
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $results = db_query($sql, $feature_id);
+   tripal_db_set_active($previous_db);  // now use drupal database
+
+
+   // iterate through the relationships, put these in an array and add
+   // in the Drupal node id if one exists
+   $i=0;
+   $nodesql = "SELECT nid FROM {chado_feature} WHERE feature_id = %d";
+   $relationships = array();
+   while($rel = db_fetch_object($results)){
+     $node = db_fetch_object(db_query($nodesql,$rel->subject_id));
+     if($node){
+        $rel->subject_nid = $node->nid;
+     }  
+     $node = db_fetch_object(db_query($nodesql,$rel->object_id));
+     if($node){
+        $rel->object_nid = $node->nid;
+     }  
+     $relationships[$i++] = $rel;      
+   }
+   return $relationships;
+}
+/**
+ *  
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_get_aggregate_types($feature_id){
+   // get the feature details
+   $sql = 'SELECT type_id FROM {feature} WHERE feature_id = %d';
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $feature = db_fetch_object(db_query($sql, $feature_id));
+   tripal_db_set_active($previous_db);  // now use drupal database
+
+   // check to see if this feature is of a type with an aggregate
+   $sql = "SELECT * FROM {tripal_feature_relagg} WHERE type_id = %d";
+   $types = array();
+   $results = db_query($sql,$feature->type_id);
+   while($agg = db_fetch_object($results)){
+      $types[] = $agg->rel_type_id;
+   }
+   
+   return $types;
+}
+/**
+ *  
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_get_aggregate_relationships($feature_id, $substitute=1,
+  $levels=0, $base_type_id=NULL, $depth=0)
+{
+
+   // we only want to recurse to as many levels deep as indicated by the 
+   // $levels variable, but only if this variable is > 0. If 0 then we 
+   // recurse until we reach the end of the relationships tree.
+   if($levels > 0 and $levels == $depth){
+      return NULL;
+   }
+
+   // first get the relationships for this feature
+   $relationships = tripal_feature_load_relationships($feature_id,'as_object');
+
+   // next, iterate through these relationships and descend, adding in those
+   // that are specified by the aggregator.
+   $i=0;
+   $new_relationships = array();
+   foreach($relationships as $rindex => $rel){
+
+      // set the base type id 
+      if(!$base_type_id){
+         $base_type_id = $rel->object_type_id;
+      } 
+      // check to see if we have an aggregator for this base type
+      $sql = "SELECT * FROM {tripal_feature_relagg} WHERE type_id = %d and rel_type_id = %d";
+      $agg = db_fetch_object(db_query($sql,$base_type_id,$rel->subject_type_id));
+      if($agg){
+         // if we're not going to substitute the resulting relationships for the
+         // parent then we need to add the parent to our list
+         if(!$substitute){
+            $new_relationships[$i++] = $rel;
+         }
+         // recurse all relationships 
+         $agg_relationships = tripal_feature_get_aggregate_relationships(
+            $rel->subject_id,$levels,$base_type_id,$depth++);
+         // if we have an aggregate defined but we have no relationships beyond
+         // this point then there's nothing we can substitute 
+         if(!$agg_relationships and $substitute){
+            $new_relationships[$i++] = $rel;
+         }
+
+         // merge all relationships into one array
+         foreach($agg_relationships as $aindex => $arel){
+            $new_relationships[$i++] = $arel;
+         }  
+      }  
+      else {
+         // if we don't have an aggregate then keep the current relationship
+         $new_relationships[$i++] = $rel;
+      }    
+   } 
+   return $new_relationships;
+}
+/**
+ *  
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_load_featureloc_sequences($feature_id,$featurelocs){
+
+   // if we don't have any featurelocs then no point in continuing
+   if(!$featurelocs){
+      return array();
+   }
+
+   // get the list of relationships (including any aggregators) and iterate
+   // through each one to find information needed to color-code the reference sequence
+   $relationships = tripal_feature_get_aggregate_relationships($feature_id);
+   if(!$relationships){
+      return array();
+   }
+
+
+   // iterate through each of the realtionships features and get their 
+   // locations
+   foreach($relationships as $rindex => $rel){
+      // get the featurelocs for each of the relationship features
+      $rel_featurelocs = tripal_feature_load_featurelocs ($rel->subject_id,'as_child',0);   
+      foreach($rel_featurelocs as $rfindex => $rel_featureloc){
+         // keep track of this unique source feature
+         $src = $rel_featureloc->src_feature_id ."-". $rel_featureloc->src_cvterm_id;
+
+         // copy over the results to the relationship object.  Since there can
+         // be more than one feature location for each relationship feature we 
+         // use the '$src' variable to keep track of these.
+         $rel->featurelocs->$src->src_uniquename = $rel_featureloc->src_uniquename;
+         $rel->featurelocs->$src->src_cvterm_id  = $rel_featureloc->src_cvterm_id;
+         $rel->featurelocs->$src->src_cvname     = $rel_featureloc->src_cvname;
+         $rel->featurelocs->$src->fmin           = $rel_featureloc->fmin;
+         $rel->featurelocs->$src->fmax           = $rel_featureloc->fmax;
+         $rel->featurelocs->$src->src_name       = $rel_featureloc->src_name;
+
+         // keep track of the individual parts for each relationship 
+         $start = $rel->featurelocs->$src->fmin;
+         $end   = $rel->featurelocs->$src->fmax;
+         $rel_locs[$src]['parts'][$start]['type']  = $rel->subject_type;
+         $rel_locs[$src]['parts'][$start]['start'] = $start;
+         $rel_locs[$src]['parts'][$start]['end']   = $end;
+      }
+   }
+
+   // the featurelocs array provided to the function contains the locations
+   // where this feature is found.   We want to get the sequence for each 
+   // location and then annotate it with the parts found from the relationships
+   // locations determiend above. 
+   $sql = "SELECT residues FROM {feature} WHERE feature_id = %d";
+   $floc_sequences = array();
+   foreach ($featurelocs as $featureloc){
+      // get the residues for this feature
+      $previous_db = tripal_db_set_active('chado');  // use chado database
+      $feature = db_fetch_object(db_query($sql,$featureloc->srcfeature_id->feature_id));
+      tripal_db_set_active($previous_db);  // now use drupal database
+
+      $src = $featureloc->srcfeature_id->feature_id ."-". $featureloc->srcfeature_id->type_id->cvterm_id;
+
+      // orient the parts to the beginning of the feature sequence
+      if (!empty($rel_locs[$src]['parts'])) {
+        $parts = $rel_locs[$src]['parts'];
+        usort($parts, 'tripal_feature_sort_rel_parts');
+        foreach ($parts as $start => $attrs){
+           $parts[$start]['start'] = $parts[$start]['start'] - $featureloc->fmin;
+           $parts[$start]['end']   = $parts[$start]['end'] - $featureloc->fmin;
+        }      
+        $floc_sequences[$src]['src'] = $src;
+        $floc_sequences[$src]['type'] = $featureloc->feature_id->type_id->name;
+        $sequence = substr($feature->residues,$featureloc->fmin-1,($featureloc->fmax - $featureloc->fmin)+1);
+        $floc_sequences[$src]['formatted_seq'] =  tripal_feature_color_sequence (
+            $sequence,$parts);
+      }
+   }
+   return $floc_sequences;
+}
+/**
+ *  
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_load_organism_feature_counts($organism){
+
+   // don't show the browser if the settings in the admin page is turned off
+   // instead return the array indicating the status of the browser
+   $show_counts = variable_get('tripal_feature_summary_setting','show_feature_summary');
+   if(strcmp($show_counts,'show_feature_summary')!=0){
+      return array ('enabled' => false );
+   }
+
+   // get the feature counts.  This is dependent on a materialized view
+   // installed with the organism module
+   $sql = "
+      SELECT OFC.num_features,OFC.feature_type,CVT.definition
+      FROM {organism_feature_count} OFC
+        INNER JOIN {cvterm} CVT on OFC.cvterm_id = CVT.cvterm_id
+      WHERE organism_id = %d 
+      ORDER BY num_features desc
+   ";
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $org_features = db_query($sql,$organism->organism_id);
+   tripal_db_set_active($previous_db);  // now use drupal database
+
+   $i=0;
+   $types = array();
+   while($type = db_fetch_object($org_features)){
+      $types[$i++] = $type;
+   }
+   return array ( 'types' => $types, 'enabled' => true );
+}
+/**
+ * 
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_load_organism_feature_browser($organism){
+
+   // don't show the browser if the settings in the admin page is turned off
+   // instead return the array indicating the status of the browser
+   $show_browser = variable_get('tripal_feature_browse_setting','show_feature_browser');
+   if(strcmp($show_browser,'show_feature_browser')!=0){
+      return array ('enabled' => false);
+   }
+
+   # 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.
+   $allowed_types = variable_get('chado_browser_feature_types','EST contig');
+   $allowed_types = preg_replace("/[\s\n\r]+/"," ",$allowed_types);
+   $so_terms = split(' ',$allowed_types);
+   $where_cvt = "";
+   foreach ($so_terms as $term){
+      $where_cvt .= "CVT.name = '$term' OR ";
+   }
+   $where_cvt = substr($where_cvt,0,strlen($where_cvt)-3);  # strip trailing 'OR'
+
+   // get the features for this organism
+   $sql  = "SELECT F.name,F.feature_id,F.uniquename,CVT.name as cvname ".
+           "FROM {feature} F ".
+              "  INNER JOIN {cvterm} CVT on F.type_id = CVT.cvterm_id ".
+            "WHERE organism_id = %s and ($where_cvt) ".
+            "ORDER BY feature_id ASC";
+
+   // the counting SQL
+   $csql  = "SELECT count(*) ".
+            "FROM {feature} F".
+            "  INNER JOIN {cvterm} CVT on F.type_id = CVT.cvterm_id ".
+            "WHERE organism_id = %s and ($where_cvt) ".
+            "GROUP BY organism_id ";
+
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $org_features = pager_query($sql,10,0,$csql,$organism->organism_id);
+   tripal_db_set_active($previous_db);  // now use drupal database
+   $pager = theme('pager');
+
+   // prepare the query that will lookup node ids
+   $sql = "SELECT nid FROM {chado_feature} ".
+           "WHERE feature_id = %d";
+   $i=0;
+   $features = array();
+   while($feature = db_fetch_object($org_features)){
+      $node = db_fetch_object(db_query($sql,$feature->feature_id));
+      $feature->nid = $node->nid;
+      $features[$i++] = $feature;
+   }
+
+   return array ( 'features' => $features, 'pager' => $pager, 'enabled' => true );
+}
+
+/**
+ *  used to sort the list of relationship objects by start position
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_sort_rel_objects($a,$b){
+   return strnatcmp($a->fmin, $b->fmin);
+}
+
+/**
+ *  used to sort the list of relationship parts by start position
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_sort_rel_parts($a,$b){
+   return strnatcmp($a['start'], $b['start']); 
+}
+
+/**
+ * 
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_color_sequence ($sequence,$parts){
+
+   $types = array();
+
+   // first get the list of types so we can create a color legend
+   foreach ($parts as $index => $child){ 
+      $type = $child['type'];
+      if(!in_array($type,$types)){
+         $types[] = $type;
+      }
+   }
+ 
+   $newseq .= "<div id=\"tripal_feature-featureloc_sequence-legend\">Legend: ";
+   foreach($types as $type){
+      $newseq .= "<span class=\"tripal_feature-featureloc_sequence-$type\">$type</span>";
+   }
+   $newseq .= "</div>";
+   
+
+   // set the background color of the rows based on the type
+   $pos = 0;
+   $newseq .= "<pre id=\"tripal_feature-featureloc_sequence\">";
+   foreach ($parts as $index => $child){
+      $type = $child['type'];
+      $start = $child['start'];
+      $end = $child['end']+1;
+      
+      $class = "class=\"tripal_feature-featureloc_sequence-$type\"";
+
+      // iterate through the sequence up to the end of the child
+      for ($i = $pos; $i < $end; $i++){ 
+    
+         // if we're at the beginning of the child sequence then set the
+         // appropriate text color
+         if($pos == $start){
+            $newseq .= "<span $class>";
+            $func = 'uc';  // nucleotides within the child should be uppercase
+         }
+         $newseq .= $sequence{$pos};
+         $seqcount++;
+
+         if($seqcount % 60 == 0){
+            $newseq .= "\n";
+         } 
+         $pos++;  
+         if($pos == $end){
+            $newseq .= "</span>";
+            $func = 'lc';
+         }
+      }
+   }
+
+   $newseq .= "</pre>";
+   return $newseq;
+}
+
+/**
+ *  This function customizes the view of the chado_feature node.  It allows
+ *  us to generate the markup.
+ *
+ * @ingroup tripal_feature
+ */
+function chado_feature_view ($node, $teaser = FALSE, $page = FALSE) {
+
+   if (!$teaser) {
+      // use drupal's default node view:
+      $node = node_prepare($node, $teaser);           
+
+      // if we're building the node for searching then
+      // we want to handle this within the module and
+      // not allow theme customization.  We don't want to
+      // index all items (such as DNA sequence).
+      if($node->build_mode == NODE_BUILD_SEARCH_INDEX){
+         $node->content['index_version'] = array(
+            '#value' => theme('tripal_feature_search_index',$node),
+            '#weight' => 1,
+         );
+      }
+      elseif($node->build_mode == NODE_BUILD_SEARCH_RESULT){
+         $node->content['index_version'] = array(
+            '#value' => theme('tripal_feature_search_results',$node),
+            '#weight' => 1,
+         );
+      }
+      else {
+         // do nothing here, let the theme derived template handle display
+      }
+   }
+
+   return $node;
+}
+
+/**
+ * Display feature information for associated organisms. This function also
+ * provides contents for indexing
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_nodeapi(&$node, $op, $teaser, $page) {
+
+   switch ($op) {
+      // Note that this function only adds feature view to an organism node.
+      // The view of a feature node is controled by the theme *.tpl file
+      case 'view':
+         switch($node->type){
+            case 'chado_organism':
+               // Show feature browser
+               $types_to_show = array('chado_organism', 'chado_library');
+               if (in_array($node->type, $types_to_show, TRUE)) {
+                  $node->content['tripal_organism_feature_counts'] = array(
+                    '#value' => theme('tripal_organism_feature_counts', $node),
+                  );
+                  $node->content['tripal_organism_feature_browser'] = array(
+                    '#value' => theme('tripal_organism_feature_browser', $node),
+                  );
+               }
+               break;
+            case 'chado_library':
+               break;
+            default:           
+         }
+         break;
+   }
+}
+
+/**
+ *  We need to let drupal know about our theme functions and their arguments.
+ *  We create theme functions to allow users of the module to customize the
+ *  look and feel of the output generated in this module
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_theme () {
+   return array(
+      'tripal_feature_search_index' => array (
+         'arguments' => array('node'),
+      ),
+      'tripal_feature_search_results' => array (
+         'arguments' => array('node'),
+      ),
+      'tripal_organism_feature_browser' => array (
+         'arguments' => array('node'=> null),
+         'template' => 'tripal_organism_feature_browser',
+      ),
+      'tripal_organism_feature_counts' => array (
+         'arguments' => array('node'=> null),
+         'template' => 'tripal_organism_feature_counts',
+      ),
+      'tripal_feature_base' => array (
+         'arguments' => array('node'=> null),
+         'template' => 'tripal_feature_base',
+      ),
+      'tripal_feature_sequence' => array (
+         'arguments' => array('node'=> null),
+         'template' => 'tripal_feature_sequence',
+      ),
+      'tripal_feature_synonyms' => array (
+         'arguments' => array('node'=> null),
+         'template' => 'tripal_feature_synonyms',
+      ),
+      'tripal_feature_featureloc_sequences' => array (
+         'arguments' => array('node'=> null),
+         'template' => 'tripal_feature_featureloc_sequences',
+      ),
+      'tripal_feature_references' => array (
+         'arguments' => array('node'=> null),
+         'template' => 'tripal_feature_references',
+      ),
+      'tripal_feature_properties' => array (
+         'arguments' => array('node'=> null),
+         'template' => 'tripal_feature_properties',
+      ),
+      'tripal_feature_featurelocs' => array (
+         'arguments' => array('node'=> null),
+         'template' => 'tripal_feature_featurelocs',
+      ),
+      'tripal_feature_relationships' => array (
+         'arguments' => array('node'=> null),
+         'template' => 'tripal_feature_relationships',
+      ),
+   );
+}
+
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_preprocess_tripal_organism_feature_counts(&$variables){
+   $organism = $variables['node']->organism;
+   $organism->feature_counts = tripal_feature_load_organism_feature_counts($organism);
+}
+
+/**
+ *  
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_preprocess_tripal_organism_feature_browser(&$variables){
+   $organism = $variables['node']->organism;
+   $organism->feature_browser = tripal_feature_load_organism_feature_browser($organism);
+}
+
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_cv_chart($chart_id){
+
+  // The CV module will create the JSON array necessary for buillding a
+  // pie chart using jgChart and Google Charts.  We have to pass to it
+  // a table that contains count information, tell it which column 
+  // contains the cvterm_id and provide a filter for getting the
+  // results we want from the table.
+  $organism_id = preg_replace("/^tripal_feature_cv_chart_(\d+)$/","$1",$chart_id);
+  $options = array(
+     count_mview      => 'organism_feature_count',
+     cvterm_id_column => 'cvterm_id',
+     count_column     => 'num_features',
+     size             => '550x200',
+     filter           => "CNT.organism_id = $organism_id",
+  );
+  return $options;
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_cv_tree($tree_id){
+
+  // The CV module will create the JSON array necessary for buillding a
+  // pie chart using jgChart and Google Charts.  We have to pass to it
+  // a table that contains count information, tell it which column 
+  // contains the cvterm_id and provide a filter for getting the
+  // results we want from the table.
+  $organism_id = preg_replace("/^tripal_feature_cv_tree_(\d+)$/","$1",$tree_id);
+  $options = array(
+     cv_id            => tripal_cv_get_cv_id('sequence'),
+     count_mview      => 'organism_feature_count',
+     cvterm_id_column => 'cvterm_id',
+     count_column     => 'num_features',
+     filter           => "CNT.organism_id = $organism_id",
+     label            => 'Features',
+  );
+  return $options;
+}
+
+/**
+ *  This function is an extension of the chado_feature_view by providing
+ *  the markup for the feature object THAT WILL BE INDEXED.
+ *
+ * @ingroup tripal_feature
+ */
+function theme_tripal_feature_search_index ($node) {
+   $feature = $node->feature;
+   $content = '';
+
+   // get the accession prefix
+   $aprefix = variable_get('chado_feature_accession_prefix','ID');
+
+   $content .= "<h1>$feature->uniquename</h1>. ";
+   $content .= "<strong>$aprefix$feature->feature_id.</strong> ";
+   $content .= "$feature->cvname ";
+   $content .= "$feature->common_name ";
+
+   // add the synonyms of this feature to the text for searching
+   $synonyms = $node->synonyms;
+   if(count($synonyms) > 0){
+      foreach ($synonyms as $result){
+         $content .= "$result->name ";
+      }
+   }
+
+   return $content;
+}
+
+/**
+ *  This function is an extension of the chado_feature_view by providing
+ *  the markup for the feature object THAT WILL BE INDEXED.
+ *
+ * @ingroup tripal_feature
+ */
+function theme_tripal_feature_search_results ($node) {
+   $feature = $node->feature;
+   $content = '';
+
+   // get the accession prefix
+   $aprefix = variable_get('chado_feature_accession_prefix','ID');
+
+   $content .= "Feature Name: <h1>$feature->uniquename</h1>. ";
+   $content .= "<strong>Accession: $aprefix$feature->feature_id.</strong>";
+   $content .= "Type: $feature->cvname. ";
+   $content .= "Organism: $feature->common_name. ";
+
+   // add the synonyms of this feature to the text for searching
+   $synonyms = $node->synonyms;
+   if(count($synonyms) > 0){
+      $content .= "Synonyms: ";
+      foreach ($synonyms as $result){
+         $content .= "$result->name, ";
+      }
+   }
+
+   return $content;
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_set_vocabulary (){
+
+   //include the file containing the required functions for adding taxonomy vocabs
+   module_load_include('inc', 'taxonomy', 'taxonomy.admin');
+
+   // get the vocabularies so that we make sure we don't recreate
+   // the vocabs that already exist
+   $vocabularies = taxonomy_get_vocabularies();
+   $ft_vid = NULL;
+   $op_vid = NULL;
+   $lb_vid = NULL;
+   $an_vid = NULL;
+
+   // These taxonomic terms are hard coded because we
+   // konw we have these relationships in the chado tables
+   // through foreign key relationships.  The tripal
+   // modules that correspond to these chado "modules" don't
+   // need to be installed for the taxonomy to work.
+   foreach($vocabularies as $vocab){
+      if($vocab->name == 'Feature Type'){
+         $ft_vid = $vocab->vid;
+      }
+      if($vocab->name == 'Organism'){
+         $op_vid = $vocab->vid;
+      }
+      if($vocab->name == 'Library'){
+         $lb_vid = $vocab->vid;
+      }
+      if($vocab->name == 'Analysis'){
+         $an_vid = $vocab->vid;
+      }
+   }
+
+   if(!$ft_vid){
+      $form_state = array();
+      $values = array(
+         'name' => t('Feature Type'),
+         'nodes' => array('chado_feature' => 'chado_feature'),
+         'description' => t('The feature type (or SO cvterm for this feature).'),
+         'help' => t('Select the term that matches the feature '),
+         'tags' => 0,
+         'hierarchy' => 1,
+         'relations' => 1,
+         'multiple' => 0,
+         'required' => 0,
+         'weight' => 1,
+      );
+      drupal_execute('taxonomy_form_vocabulary', $form_state,$values);
+      drupal_execute('taxonomy_form_vocabulary', $form_state);
+   }
+
+   if(!$op_vid){
+      $form_state = array();
+      $values = array(
+         'name' => t('Organism'),
+         'nodes' => array('chado_feature' => 'chado_feature'),
+         'description' => t('The organism to which this feature belongs.'),
+         'help' => t('Select the term that matches the feature '),
+         'tags' => 0,
+         'hierarchy' => 1,
+         'relations' => 1,
+         'multiple' => 0,
+         'required' => 0,
+         'weight' => 2,
+      );
+      drupal_execute('taxonomy_form_vocabulary', $form_state,$values);
+      drupal_execute('taxonomy_form_vocabulary', $form_state);
+   }
+
+   if(!$lb_vid){
+      $form_state = array();
+      $values = array(
+         'name' => t('Library'),
+         'nodes' => array('chado_feature' => 'chado_feature'),
+         'description' => t('Chado features associated with a library are assigned the term associated with the library'),
+         'help' => t('Select the term that matches the feature '),
+         'tags' => 0,
+         'hierarchy' => 1,
+         'relations' => 1,
+         'multiple' => 0,
+         'required' => 0,
+         'weight' => 3,
+      );
+      drupal_execute('taxonomy_form_vocabulary', $form_state, $values);
+      drupal_execute('taxonomy_form_vocabulary', $form_state);
+   }
+
+   if(!$an_vid){
+      $form_state = array();
+      $values = array(
+         'name' => t('Analysis'),
+         'nodes' => array('chado_feature' => 'chado_feature'),
+         'description' => t('Any analysis to which this feature belongs.'),
+         'help' => t('Select the term that matches the feature '),
+         'tags' => 0,
+         'hierarchy' => 1,
+         'relations' => 1,
+         'multiple' => 1,
+         'required' => 0,
+         'weight' => 4,
+      );
+      drupal_execute('taxonomy_form_vocabulary', $form_state,$values);
+      drupal_execute('taxonomy_form_vocabulary', $form_state);
+   }
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_del_vocabulary(){
+   //include the file containing the required functions for adding taxonomy vocabs
+   module_load_include('inc', 'taxonomy', 'taxonomy.admin');
+
+   // get the vocabularies
+   $vocabularies = taxonomy_get_vocabularies();
+
+   // These taxonomic terms are hard coded because we
+   // know we have these relationships in the chado tables
+   // through foreign key relationships.  The tripal
+   // modules that correspond to these chado "modules" don't
+   // need to be installed for the taxonomy to work.
+   foreach($vocabularies as $vocab){
+      if($vocab->name == 'Feature Type'){
+         taxonomy_del_vocabulary($vocab->vid);
+      }
+      if($vocab->name == 'Organism'){
+         taxonomy_del_vocabulary($vocab->vid);
+      }
+      if($vocab->name == 'Library'){
+         taxonomy_del_vocabulary($vocab->vid);
+      }
+      if($vocab->name == 'Analysis'){
+         taxonomy_del_vocabulary($vocab->vid);
+      }
+   }
+
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_features_set_taxonomy($max_sync = 0,$job_id = NULL){
+
+   // make sure our vocabularies are cleaned and reset before proceeding
+   tripal_feature_del_vocabulary();
+   tripal_feature_set_vocabulary();
+
+   // iterate through all drupal feature nodes and set the taxonomy
+   $results = db_query("SELECT * FROM {chado_feature}");
+   $nsql =  "SELECT * FROM {node} ".
+            "WHERE nid = %d";
+   $i = 0;
+
+   // load into ids array
+   $count = 0;
+   $chado_features = array();
+   while($chado_feature = db_fetch_object($results)){
+      $chado_features[$count] = $chado_feature;
+      $count++;
+   }
+
+   // Iterate through features that need to be synced
+   $interval = intval($count * 0.01);
+   foreach($chado_features as $chado_feature){
+
+      // update the job status every 1% features
+      if($job_id and $i % $interval == 0){
+         tripal_job_set_progress($job_id,intval(($i/$count)*100));
+      }
+      print "$i of $count: ";
+      $node = db_fetch_object(db_query($nsql,$chado_feature->nid));
+      tripal_feature_set_taxonomy($node,$chado_feature->feature_id);
+
+      $i++;
+   }
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_set_taxonomy ($node,$feature_id){
+
+   // iterate through the taxonomy classes that have been
+   // selected by the admin user and make sure we only set those
+   $tax_classes = variable_get('tax_classes', '');
+   $do_ft = 0;
+   $do_op = 0;
+   $do_lb = 0;
+   $do_an = 0;
+   foreach($tax_classes as $class){
+      if(strcmp($class ,'organism')==0){
+         $do_op = 1;
+      }
+      if(strcmp($class,'feature_type')==0){
+         $do_ft = 1;
+      }
+      if(strcmp($class,'library')==0){
+         $do_lb = 1;
+      }
+      if(strcmp($class,'analysis')==0){
+         $do_an = 1;
+      }
+   }
+
+
+   // get the list of vocabularies and find our two vocabularies of interest
+   $vocabularies = taxonomy_get_vocabularies();
+   $ft_vid = NULL;
+   $op_vid = NULL;
+   $lb_vid = NULL;
+   $an_vid = NULL;
+   foreach($vocabularies as $vocab){
+      if($vocab->name == 'Feature Type'){
+         $ft_vid = $vocab->vid;
+      }
+      if($vocab->name == 'Organism'){
+         $op_vid = $vocab->vid;
+      }
+      if($vocab->name == 'Library'){
+         $lb_vid = $vocab->vid;
+      }
+      if($vocab->name == 'Analysis'){
+         $an_vid = $vocab->vid;
+      }
+   }
+
+   // get the cvterm and the organism for this feature
+   $sql = "SELECT CVT.name AS cvname, O.genus, O.species ".
+          "FROM {CVTerm} CVT ".
+          "  INNER JOIN Feature F on F.type_id = CVT.cvterm_id ".
+          "  INNER JOIN Organism O ON F.organism_id = O.organism_id ".
+          "WHERE F.feature_id = $feature_id";
+   $previous_db = tripal_db_set_active('chado');  // use chado database
+   $feature = db_fetch_object(db_query($sql));
+   tripal_db_set_active($previous_db);  // now use drupal database
+
+   // Set the feature type for this feature
+   if($do_ft && $ft_vid){
+      $tags["$ft_vid"] = "$feature->cvname";
+   }
+   // Set the organism for this feature type
+   if($do_op && $op_vid){
+      $tags["$op_vid"] = "$feature->genus $feature->species";
+   }
+
+   // get the library that this feature may belong to and add it as taxonomy
+   if($do_lb && $lb_vid){
+      $sql = "SELECT L.name ".
+             "FROM {Library} L ".
+             "  INNER JOIN Library_feature LF ON LF.library_id = L.library_id ".
+             "WHERE LF.feature_id = %d ";
+      $previous_db = tripal_db_set_active('chado');  // use chado database
+      $library = db_fetch_object(db_query($sql,$feature_id));
+      tripal_db_set_active($previous_db);  // now use drupal database
+      $tags["$lb_vid"] = "$library->name";
+   }
+
+   // now add the taxonomy to the node
+   $terms['tags'] = $tags;
+   taxonomy_node_save($node,$terms);
+   //   print "Setting $node->name: " . implode(", ",$tags) . "\n";
+
+   // get the analysis that this feature may belong to and add it as taxonomy
+   // We'll add each one individually since there may be more than one analysis
+   if($do_an && $an_vid){
+      $sql = "SELECT A.name ".
+             "FROM {Analysis} A ".
+             "  INNER JOIN Analysisfeature AF ON AF.analysis_id = A.analysis_id ".
+             "WHERE AF.feature_id = $feature_id ";
+      $results = db_query($sql);
+      $previous_db = tripal_db_set_active('chado');  // use chado database
+      $analysis_terms = array();
+      while($analysis=db_fetch_object($results)){
+         $tags2["$an_vid"] = "$analysis->name";
+         $terms['tags'] = $tags2;
+         taxonomy_node_save($node,$terms);
+      }
+      tripal_db_set_active($previous_db);  // now use drupal database
+   }
+
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_features_cleanup($dummy = NULL, $job_id = NULL) {
+
+   // build the SQL statments needed to check if nodes point to valid features
+   $dsql = "SELECT * FROM {node} WHERE type = 'chado_feature' order by nid";
+   $nsql = "SELECT * FROM {node} WHERE nid = %d";
+   $csql = "SELECT * FROM {chado_feature} where nid = %d ";
+   $cfsql= "SELECT * FROM {chado_feature}";
+ 
+   // load into nodes array
+   $results = db_query($dsql);
+   $count = 0;
+   $nodes = array();
+   while($node = db_fetch_object($results)){
+      $nodes[$count] = $node;
+      $count++;
+   }
+
+   // load the chado_features into an array
+   $results = db_query($cfsql);
+   $cnodes = array();
+   while($node = db_fetch_object($results)){
+      $cnodes[$count] = $node;
+      $count++;
+   }
+   $interval = intval($count * 0.01);
+   if($interval > 1){
+      $interval = 1;
+   }
+
+   // iterate through all of the chado_feature nodes and delete those  that aren't valid
+   foreach($nodes as $nid){
+
+      // update the job status every 1% features
+      if($job_id and $i % $interval == 0){
+         tripal_job_set_progress($job_id,intval(($i/$count)*100));
+      }
+
+      // check to see if the node has a corresponding entry
+      // in the chado_feature table. If not then delete the node.
+      $feature = db_fetch_object(db_query($csql,$nid->nid));
+      if(!$feature){
+         node_delete($nid->nid);
+         $message = "Missing in chado_feature table.... DELETING: $nid->nid\n";
+         watchdog('tripal_feature',$message,array(),WATCHDOG_WARNING);
+         continue;
+      }
+
+      $i++;
+   }
+
+   // iterate through all of the chado_feature nodes and delete those  that aren't valid
+   foreach($cnodes as $nid){
+      // update the job status every 1% features
+      if($job_id and $i % $interval == 0){
+         tripal_job_set_progress($job_id,intval(($i/$count)*100));
+      }
+      $node = db_fetch_object(db_query($nsql,$nid->nid));
+      if(!$node){
+         db_query("DELETE FROM {chado_feature} WHERE nid = $nid->nid");
+         $message = "chado_feature missing node.... DELETING: $nid->nid\n";
+         watchdog('tripal_feature',$message,array(),WATCHDOG_WARNING);
+      }
+
+      $i++;
+   }
+   return '';
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_return_fasta($feature,$desc){   
+   $fasta  = ">" . variable_get('chado_feature_accession_prefix','ID') . "$feature->feature_id|$feature->name";
+   $fasta .= " $desc\n";
+   $fasta .= wordwrap($feature->residues, 50, "\n", true);
+   $fasta .= "\n\n";
+   return $fasta;
+}
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_job_describe_args($callback,$args){
+
+   $new_args = array();
+   if($callback == 'tripal_feature_load_fasta'){
+      $new_args['FASTA file'] = $args[0];
+      $organism = tripal_core_chado_select('organism',array('genus','species'),array('organism_id' => $args[1]));
+      $new_args['Organism'] = $organism[0]->genus." ". $organism[0]->species;
+      $new_args['Sequence Type'] = $args[2];
+      $new_args['Name Match Type'] = $args[14];
+      $new_args['Name RE'] = $args[4];
+      $new_args['Unique Name RE'] = $args[5];
+
+
+      // add in the relationship arguments
+      $new_args['Relationship Type'] = $args[8];
+      $new_args['Relationship Parent RE'] = $args[9];
+      $new_args['Relationship Parent Type'] = $args[10];
+
+      // add in the database reference arguments
+      if($args[7]){
+         $db = tripal_core_chado_select('db',array('name'),array('db_id' => $args[7]));
+      }
+      $new_args['Database Reference'] = $db[0]->name;
+      $new_args['Accession RE'] = $args[6];
+      $new_args['Method'] = $args[11];
+
+      // add in the analysis 
+      if($args[13]){
+         $analysis = tripal_core_chado_select('analysis',array('name'),array('analysis_id' => $args[13]));
+      }
+      $new_args['Analysis'] = $analysis[0]->name;
+   }
+   if($callback == 'tripal_feature_delete_features'){
+      if($args[0]){
+         $organism = tripal_core_chado_select('organism',array('genus','species'),array('organism_id' => $args[0]));
+         $new_args['Organism'] = $organism[0]->genus." ". $organism[0]->species;
+      } else {
+         $new_args['Organism'] = '';
+      }
+
+      if($args[1]){
+         $analysis = tripal_core_chado_select('analysis',array('name'),array('analysis_id' => $args[1]));
+         $new_args['Analysis'] = $analysis[0]->name;
+      } else {
+         $new_args['Analysis'] = '';
+      }
+      
+      $new_args['Sequence Type'] = $args[2];
+      $new_args['Is Unique Name'] = $args[3];
+      $new_args['Features Names'] = $args[4];
+      
+   }
+   if($callback == 'tripal_feature_sync_features'){
+      if($args[0]){
+         $organism = tripal_core_chado_select('organism',array('genus','species'),array('organism_id' => $args[0]));
+         $new_args['Organism'] = $organism[0]->genus." ". $organism[0]->species;
+      } else {
+         $new_args['Organism'] = '';
+      }
+      $new_args['Feature Types'] = $args[1];
+   }
+   return $new_args;
+}
+

+ 439 - 0
modules/base/tripal_feature/tripal_feature.views.inc

@@ -0,0 +1,439 @@
+<?php
+
+/**
+ *  @file
+ *  This file contains the basic functions for views integration of
+ *  chado/tripal organism tables. Supplementary functions can be found in
+ *  ./views/
+ *
+ *  Documentation on views integration can be found at 
+ *  http://views2.logrus.com/doc/html/index.html.
+ */
+
+/**
+ * @defgroup tripal_feature_views Feature Views Integration
+ * @ingroup views
+ * @ingroup tripal_feature
+ */
+ 
+/**
+ * Implements hook_views_data()
+ *
+ * Purpose: Describe chado/tripal tables & fields to views
+ *
+ * @return: a data array which follows the structure outlined in the
+ *   views2 documentation for this hook. Essentially, it's an array of table
+ *   definitions keyed by chado/tripal table name. Each table definition 
+ *   includes basic details about the table, fields in that table and
+ *   relationships between that table and others (joins)
+ *
+ * @ingroup tripal_feature_views
+ */
+require_once('views/feature.views.inc');
+require_once('views/chado_feature.views.inc');
+require_once('views/misc_tables.views.inc');
+function tripal_feature_views_data()  {
+  $data = array();
+  
+  $data = array_merge($data, retrieve_feature_views_data());
+  $data = array_merge($data, retrieve_chado_feature_views_data());
+  $data = array_merge($data, retrieve_feature_misc_tables_views_data());
+  
+  return $data;
+}
+
+/**
+ * Implements hook_views_handlers()
+ *
+ * Purpose: Register all custom handlers with views
+ *   where a handler describes either "the type of field", 
+ *   "how a field should be filtered", "how a field should be sorted"
+ *
+ * @return: An array of handler definitions
+ *
+ * @ingroup tripal_feature_views
+ */
+function tripal_feature_views_handlers() {
+ return array(
+   'info' => array(
+     'path' => drupal_get_path('module', 'tripal_feature') . '/views/handlers',
+   ),
+   'handlers' => array(
+     'views_handler_field_computed_feature_nid' => array(
+       'parent' => 'views_handler_field_numeric',
+     ),
+     'views_handler_field_readable_date' => array(
+       'parent' => 'views_handler_field',
+     ),
+     'views_handler_field_residues' => array(
+       'parent' => 'views_handler_field',
+     ),
+   ),
+ );
+}
+
+/**
+ * Implementation of hook_views_data_alter().
+ */
+function tripal_feature_views_data_alter(&$data) {
+
+  if( !(is_array($db_url) and array_key_exists('chado',$db_url)) ){
+
+    // Add featuer relationship to node
+    $data['node']['feature_chado_nid'] = array(
+      'group' => 'Feature',
+      'title' => 'Feature Node',
+      'help' => 'Links Chado Feature Fields/Data to the Nodes in the current View.',
+      'real field' => 'nid',
+      'relationship' => array(
+        'handler' => 'views_handler_relationship',
+        'title' => t('Node => Chado'),
+        'label' => t('Node => Chado'),
+        'real field' => 'nid',
+        'base' => 'chado_feature',
+        'base field' => 'nid'
+      ),
+    );
+  }
+  
+}
+
+/**
+ *
+ * @ingroup tripal_feature_views
+ */
+function tripal_feature_views_default_views () {
+  $views = array();
+  
+  // Main default view
+  // List all cvterms based on cv
+  $view = new view;
+  $view->name = 'all_features';
+  $view->description = 'A listing of all Sequence FEatures';
+  $view->tag = 'chado';
+  $view->view_php = '';
+  $view->base_table = 'feature';
+  $view->is_cacheable = FALSE;
+  $view->api_version = 2;
+  $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
+  $handler = $view->new_display('default', 'Defaults', 'default');
+  $handler->override_option('fields', array(
+    'uniquename' => array(
+      'label' => 'Unique Name',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'link_to_node' => 1,
+      'exclude' => 0,
+      'id' => 'uniquename',
+      'table' => 'feature',
+      'field' => 'uniquename',
+      'relationship' => 'none',
+    ),
+    'name' => array(
+      'label' => 'Name',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'link_to_node' => 1,
+      'exclude' => 0,
+      'id' => 'name',
+      'table' => 'feature',
+      'field' => 'name',
+      'relationship' => 'none',
+    ),
+    'common_name' => array(
+      'label' => 'Organism',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'link_to_node' => 1,
+      'exclude' => 0,
+      'id' => 'common_name',
+      'table' => 'organism',
+      'field' => 'common_name',
+      'relationship' => 'none',
+    ),
+    'name_3' => array(
+      'label' => 'Type',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'name_3',
+      'table' => 'cvterm',
+      'field' => 'name',
+      'relationship' => 'none',
+    ),
+    'accession_link' => array(
+      'label' => 'External Reference',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'exclude' => 0,
+      'id' => 'accession_link',
+      'table' => 'dbxref',
+      'field' => 'accession_link',
+      'relationship' => 'none',
+    ),
+    'name_1' => array(
+      'label' => 'Library',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'link_to_node' => 1,
+      'exclude' => 0,
+      'id' => 'name_1',
+      'table' => 'library',
+      'field' => 'name',
+      'relationship' => 'none',
+    ),
+    'name_2' => array(
+      'label' => 'Analysis',
+      'alter' => array(
+        'alter_text' => 0,
+        'text' => '',
+        'make_link' => 0,
+        'path' => '',
+        'link_class' => '',
+        'alt' => '',
+        'prefix' => '',
+        'suffix' => '',
+        'target' => '',
+        'help' => '',
+        'trim' => 0,
+        'max_length' => '',
+        'word_boundary' => 1,
+        'ellipsis' => 1,
+        'html' => 0,
+        'strip_tags' => 0,
+      ),
+      'empty' => '',
+      'hide_empty' => 0,
+      'empty_zero' => 0,
+      'link_to_node' => 1,
+      'exclude' => 0,
+      'id' => 'name_2',
+      'table' => 'analysis',
+      'field' => 'name',
+      'relationship' => 'none',
+    ),
+  ));
+  $handler->override_option('filters', array(
+    'common_name' => array(
+      'operator' => '=',
+      'value' => '<select organism>',
+      'group' => '0',
+      'exposed' => TRUE,
+      'expose' => array(
+        'use_operator' => 0,
+        'operator' => 'common_name_op',
+        'identifier' => 'organism',
+        'label' => 'Organism',
+        'optional' => 1,
+        'remember' => 0,
+      ),
+      'case' => 1,
+      'id' => 'common_name',
+      'table' => 'organism',
+      'field' => 'common_name',
+      'relationship' => 'none',
+    ),
+    'name' => array(
+      'operator' => 'contains',
+      'value' => '',
+      'group' => '0',
+      'exposed' => TRUE,
+      'expose' => array(
+        'use_operator' => 0,
+        'operator' => 'name_op',
+        'identifier' => 'name',
+        'label' => 'Name Contains',
+        'optional' => 1,
+        'remember' => 0,
+      ),
+      'case' => 0,
+      'id' => 'name',
+      'table' => 'feature',
+      'field' => 'name',
+      'relationship' => 'none',
+    ),
+    'name_1' => array(
+      'operator' => 'contains',
+      'value' => '',
+      'group' => '0',
+      'exposed' => TRUE,
+      'expose' => array(
+        'use_operator' => 0,
+        'operator' => 'name_1_op',
+        'identifier' => 'library',
+        'label' => 'Library Name Contains',
+        'optional' => 1,
+        'remember' => 0,
+      ),
+      'case' => 0,
+      'id' => 'name_1',
+      'table' => 'library',
+      'field' => 'name',
+      'relationship' => 'none',
+    ),
+    'name_2' => array(
+      'operator' => 'contains',
+      'value' => '',
+      'group' => '0',
+      'exposed' => TRUE,
+      'expose' => array(
+        'use_operator' => 0,
+        'operator' => 'name_2_op',
+        'identifier' => 'analysis',
+        'label' => 'Analysis Name Contains',
+        'optional' => 1,
+        'remember' => 0,
+      ),
+      'case' => 0,
+      'id' => 'name_2',
+      'table' => 'analysis',
+      'field' => 'name',
+      'relationship' => 'none',
+    ),
+  ));
+  $handler->override_option('access', array(
+    'type' => 'perm',
+    'perm' => 'access chado_feature content',
+  ));
+  $handler->override_option('cache', array(
+    'type' => 'none',
+  ));
+  $handler->override_option('title', 'Sequence Features');
+  $handler->override_option('empty', 'There are no features matching that criteria. Please select a different organism above.');
+  $handler->override_option('empty_format', '1');
+  $handler->override_option('items_per_page', 50);
+  $handler->override_option('style_plugin', 'table');
+  $handler = $view->new_display('page', 'Page', 'page_1');
+  $handler->override_option('path', 'features');
+  $handler->override_option('menu', array(
+    'type' => 'normal',
+    'title' => 'Sequence Features',
+    'description' => '',
+    'weight' => '0',
+    'name' => 'primary-links',
+  ));
+  $handler->override_option('tab_options', array(
+    'type' => 'none',
+    'title' => '',
+    'description' => '',
+    'weight' => 0,
+    'name' => 'navigation',
+  ));
+  $views[$view->name] = $view;
+  
+  return $views;
+}

+ 45 - 0
modules/base/tripal_feature/views/README

@@ -0,0 +1,45 @@
+
+This directory contains all additional views code needed to integrate this module with views2.
+
+FILE/FOLDER DESCRIPTIONS
+---------------------------
+<chado table name>.views.inc:
+	contains a single function retrieve_<chado table name>_views_data()
+	which describes that table to views. This function is called by
+	tripal_feature_views_data() in ../tripal_feature.views.inc.
+	For more information on the form of this data array look up the
+	views2 documentation for hook_views_data() 
+	-http://views2.logrus.com/doc/html/index.html
+	
+handlers/
+	Each file contained within this folder defines a views handler. Only custom
+	handlers are included in this folder and each must be described in 
+	hook_views_handlers() in ../tripal_feature.views.inc.
+	A views handler does one of the following:
+		1) describe the type of a field and how it should be displayed
+		2) describe a method to sort this field
+		3) describe a method to filter this field
+
+
+STANDARDS TO FOLLOW
+---------------------------
+
+1. All table definition files should be named tablename.views.inc
+2. All handlers should be in a handlers sub-directory and follow the naming convention of
+   views handlers (ie: views_handler_field/filter/sort_handlername.inc )
+
+Views Table Definitions:
+- Please use the template files provided whenever you are describing a new table to views.
+   For any table in chado simply copy template.table_defn.views.inc to tablename.views.inc and 
+   follow the instructions listed at the top of the template file.
+- ONLY ONE TABLE DEFINITION PER FILE
+- To join a chado table to it's drupal node simply copy template.node_join.views.inc to 
+   basetablename.views.inc and replace all XXX with basetablename.
+   
+   NOTE: Creating the table definition file is not enough. You also need to call the 
+         retrieve_XXX_views_data() function from ../tripal_feature.views.inc:tripal_feature_views_data()
+         by adding the following line:
+            $data = array_merge($data, retrieve_XXX_views_data());
+         to the function and including the file directly above the function (blow the function 
+         header by adding:
+            require_once('views/XXX.views.inc');

+ 83 - 0
modules/base/tripal_feature/views/chado_feature.views.inc

@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * Purpose: this function returns the portion of the data array 
+ *   which describes the chado_feature drupal table, it's fields and any joins between it and other tables
+ * @see tripal_feature_views_data() --in tripal_feature.views.inc
+ *
+ * The main need for description of this table to views is to join chado data with drupal nodes
+ *
+ * @ingroup tripal_feature_views
+ */
+function retrieve_chado_feature_views_data () {
+	global $db_url;
+  $data = array();
+  
+  // if the chado database is not local to the drupal database
+  // then we need to set the database name.  This should always
+  // be 'chado'.
+  if(is_array($db_url) and array_key_exists('chado',$db_url)){
+     // return empty data array b/c if chado is external then no join to the nodetable can be made
+     return $data;
+  }
+
+  // Basic table definition
+  $data['chado_feature']['table'] = array(
+    'field' => 'nid',
+  );
+  
+  // Note: No joins need to be made from $data['feature']['table']
+  
+  // Join the chado feature table to feature
+  $data['chado_feature']['table']['join']['feature'] = array(
+  	'left_field' => 'feature_id',
+  	'field' => 'feature_id',
+  );
+  
+  // Join the node table to chado feature
+  $data['node']['table']['join']['chado_feature'] = array(
+  	'left_field' => 'nid',
+  	'field' => 'nid',
+  );
+  
+  // Join the node table to feature
+  $data['node']['table']['join']['feature'] = array(
+  	'left_table' => 'chado_feature',
+  	'left_field' => 'nid',
+  	'field' => 'nid',
+  );
+  
+  // Add relationship between chado_feature and feature
+  $data['chado_feature']['feature_nid'] = array(
+    'group' => 'Feature',
+    'title' => 'Feature Node',
+    'help' => 'Links Chado Feature Fields/Data to the Nodes in the current View.',
+    'real field' => 'feature_id',
+    'relationship' => array(
+      'handler' => 'views_handler_relationship',
+      'title' => t('Chado => Feature'),
+      'label' => t('Chado => Feature'),
+      'real field' => 'feature_id',
+      'base' => 'feature',
+      'base field' => 'feature_id'
+    ),
+  );
+
+  // Add node relationship to feature
+  $data['chado_feature']['feature_chado_nid'] = array(
+    'group' => 'Feature',
+    'title' => 'Feature Node',
+    'help' => 'Links Chado Feature Fields/Data to the Nodes in the current View.',
+    'real field' => 'nid',
+    'relationship' => array(
+      'handler' => 'views_handler_relationship',
+      'title' => t('Chado => Node'),
+      'label' => t('Chado => Node'),
+      'real field' => 'nid',
+      'base' => 'node',
+      'base field' => 'nid'
+    ),
+  );
+  
+	return $data;
+}

+ 293 - 0
modules/base/tripal_feature/views/feature.views.inc

@@ -0,0 +1,293 @@
+<?php
+
+/**
+ * Purpose: this function returns the portion of the data array 
+ *   which describes the feature table, it's fields and any joins between it and other tables
+ * @see tripal_feature_views_data() --in tripal_feature.views.inc
+ *
+ * @todo Add better handler for is_analysis, is_obsolete: something which changes the t/f to a true boolean
+ * @todo Add support for the following tables: featureprop, featureloc, featurepos, feature_synonym, feature_relationship
+ * @todo Add join to node table within if <chado/drupal same db>; also addd if not around nid field
+ *
+ * BASE TABLE: feature
+ * @code
+ *   create table feature (
+ *        feature_id serial not null,
+ *        primary key (feature_id),
+ *        dbxref_id int,
+ *        foreign key (dbxref_id) references dbxref (dbxref_id) on delete set null INITIALLY DEFERRED,
+ *        organism_id int not null,
+ *        foreign key (organism_id) references organism (organism_id) on delete cascade INITIALLY DEFERRED,
+ *        name varchar(255),
+ *        uniquename text not null,
+ *        residues text,
+ *        seqlen int,
+ *        md5checksum char(32),
+ *        type_id int not null,
+ *        foreign key (type_id) references cvterm (cvterm_id) on delete cascade INITIALLY DEFERRED,
+ *        is_analysis boolean not null default 'false',
+ *        is_obsolete boolean not null default 'false',
+ *        timeaccessioned timestamp not null default current_timestamp,
+ *        timelastmodified timestamp not null default current_timestamp,
+ *        constraint feature_c1 unique (organism_id,uniquename,type_id)
+ *   );
+ * @endcode
+ *
+ * @ingroup tripal_feature_views
+ */
+ function retrieve_feature_views_data() {
+	global $db_url;
+	$data = array();
+	
+  // if the chado database is not local to the drupal database
+  // then we need to set the database name.  This should always
+  // be 'chado'.
+  if(is_array($db_url) and array_key_exists('chado',$db_url)){
+     $database = 'chado';
+  }
+
+	
+   // Basic table definition
+   $data['feature']['table']['group'] = 'Chado Feature';
+   $data['feature']['table']['base'] = array(
+     'field' => 'feature_id',
+     'title' => 'Chado Features',
+     'help' => 'Features are Sequence Data Records in Chado.',
+   );
+
+	if($database){
+		$data['feature']['table']['base']['database'] = $database;
+	}
+	
+
+  //Relationship Definitions---------------------------------
+  //Join: feature => nd_reagent
+  $data['feature']['table']['join']['nd_reagent'] = array(
+    'left_field' => 'feature_id',
+    'field' => 'feature_id',
+  );  
+
+  // Table Field Definitions----------------------
+  // Field: feature_id (primary key)
+  $data['feature']['feature_id'] = array(
+    'title' => 'Feature ID',
+    'help' => 'The primary key of a feature',
+    'field' => array(
+     	'handler' => 'views_handler_field_numeric',
+ 		  'click sortable' => TRUE,
+    ),
+    'filter' => array(
+     	'handler' => 'views_handler_filter_numeric',
+    ),
+    'sort' => array(
+     	'handler' => 'views_handler_sort',
+    ),
+  );
+
+  // Calculated Field: Node ID
+  //  use custom field handler to query drupal for the node ID
+  //  this is only needed if chado is in a separate database from drupal
+  if($database){
+  	$data['feature']['feature_nid'] = array(
+    	'title' => 'Node ID',
+			'help' => 'This is the node ID of this feature. It can be used as a link to the node.',
+    	'field' => array(
+      	'handler' => 'views_handler_field_computed_feature_nid',
+      ),
+  	);
+  } else {
+    // Add relationship between chado_feature and feature
+    $data['feature']['feature_nid'] = array(
+      'group' => 'Feature',
+      'title' => 'Feature Node',
+      'help' => 'Links Chado Feature Fields/Data to the Nodes in the current View.',
+      'real field' => 'feature_id',
+      'relationship' => array(
+        'handler' => 'views_handler_relationship',
+        'title' => t('Feature => Chado'),
+        'label' => t('Feature => Chado'),
+        'real field' => 'feature_id',
+        'base' => 'chado_feature',
+        'base field' => 'feature_id'
+      ),
+    );
+
+  }
+  
+  // Field: organism_id (forgeign key)
+  //  join between organism table and this one in tripal_organism/views/organism.views.inc
+  
+  // Field: dbxref_id (forgeign key)
+  //  join between dbxref table and this one in tripal_db/views/dbxref.views.inc
+  
+  // Field: type_id (forgeign key)
+  //  join between cvterm table and this one in tripal_cv/views/cvterm.views.inc
+  
+  // Field: name (varchar 255)
+  $data['feature']['name'] = array(
+    'title' => 'Name',
+    'help' => 'The human-readable, non-unique name of a feature.',
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+  // if joined to the node table add a "Link to Node" option for the field  
+  if (!$database) {
+    $data['feature']['name']['field']['handler'] = 'views_handler_field_node_optional';
+  }
+
+  // Field: unique name (text)
+  $data['feature']['uniquename'] = array(
+    'title' => 'Unique Name',
+    'help' => 'The unique name of a feature.',
+    'field' => array(
+      'handler' => 'views_handler_field',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+  // if joined to the node table add a "Link to Node" option for the field  
+  if (!$database) {
+    $data['feature']['uniquename']['field']['handler'] = 'views_handler_field_node_optional';
+  }
+
+  // Field: residues (text)
+  $data['feature']['residues'] = array(
+    'title' => 'Residues',
+    'help' => 'The sequence of a feature.',
+    'field' => array(
+      'handler' => 'views_handler_field_residues',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+  );
+
+  // Field: sequence length (integer)
+  $data['feature']['seqlen'] = array(
+    'title' => 'Sequence Length',
+    'help' => 'The length of the sequence',
+    'field' => array(
+      'handler' => 'views_handler_field_numeric',
+      'click sortable' => TRUE,
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_numeric',
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+  );
+
+  // Field: is analysis (boolean -t/f)
+  $data['feature']['is_analysis'] = array(
+    'title' => 'Is Analysis',
+    'help' => 'A boolean indicating whether this feature was annotated by means of automated analysis.',
+    'field' => array(
+       'handler' => 'views_handler_field_chado_tf_boolean',
+       'click sortable' => TRUE,
+       'label' => t('Is Analysis?'),
+       'type' => 'yes-no',
+    ),
+    'filter' => array(
+       'handler' => 'views_handler_filter_chado_boolean',
+       'label' => t('Is Analysis?'),
+       'type' => 'yes-no',
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+  );
+
+  // Field: is obsolete (boolean -t/f)
+  $data['feature']['is_obsolete'] = array(
+    'title' => 'Is Obsolete',
+    'help' => 'A boolean indicating whether this feature is obsolete.',
+    'field' => array(
+       'handler' => 'views_handler_field_chado_tf_boolean',
+       'click sortable' => TRUE,
+       'label' => t('Is Obsolete?'),
+       'type' => 'yes-no',
+    ),
+    'filter' => array(
+       'handler' => 'views_handler_filter_chado_boolean',
+       'label' => t('Is Obsolete?'),
+       'type' => 'yes-no',
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+  );
+
+  // Field: time accessioned (datetime)
+  $data['feature']['timeaccessioned'] = array(
+    'title' => 'Time Accessioned',
+    'help' => 'The date & time when this feature was accessioned (added into the database)',
+    'field' => array(
+      'handler' => 'views_handler_field_readable_date',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort_date',
+    ),
+  );
+
+  // Field: time last modified (datetime)
+  $data['feature']['timelastmodified'] = array(
+    'title' => 'Time Last Modified',
+    'help' => 'The date & time when this feature was last modified.',
+    'field' => array(
+      'handler' => 'views_handler_field_readable_date',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort_date',
+    ),
+  );
+
+  // Calculated Field: Number of Analysis' (Count -Int)
+  // Provides the number of analysis' for a given feature
+  // @see tripal_analysis/views/misc_tables.views.inc
+  
+  //Calculated Field: Number of Libraries (Count -Int)
+  // Provides the number of libraries for a given feature
+  // @see tripal_library/views/misc_tables.views.inc 
+
+	// Calculated Field: feature relationships
+	// uses a custom field handler which pulls results from the view
+	$data['feature']['relationships'] = array(
+    'title' => t('Feature Relationships'),
+    'help' => t('Relationships including the current feature.'),
+    'field' => array(
+      'title' => t('Relationships'),
+      'help' => t('Display a given type of relationships including the current feature.'),
+      'handler' => 'views_handler_field_chado_rel_by_type',
+    ),
+  );  
+  return $data;
+ }

+ 23 - 0
modules/base/tripal_feature/views/handlers/views_handler_field_computed_feature_nid.inc

@@ -0,0 +1,23 @@
+<?php
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ * @ingroup views_field_handlers
+ */
+class views_handler_field_computed_feature_nid extends views_handler_field_numeric {
+	function construct() {
+		parent::construct();
+		$this->additional_fields['feature_id'] = array('table' => 'feature', 'field' => 'feature_id');
+	}
+
+	function query() { 
+		$this->ensure_my_table();
+		$this->add_additional_fields(); 
+	}
+
+	function render($values) { 
+		return $values->feature_nid;
+	}
+} 

+ 1 - 0
modules/base/tripal_feature/views/handlers/views_handler_field_readable_date.inc

@@ -0,0 +1 @@
+../../../tripal_core/views/handlers/views_handler_field_readable_date.inc

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff