Ver Fonte

Merge branch '7.x-3.x' into 531-t3-relationship_widget

Lacey Sanderson há 6 anos atrás
pai
commit
2ccd7ee724
36 ficheiros alterados com 2245 adições e 668 exclusões
  1. 5 4
      .github/ISSUE_TEMPLATE/feature_request.md
  2. 16 19
      .github/PULL_REQUEST_TEMPLATE.md
  3. 49 0
      docs/README.md
  4. 1 0
      docs/dev_guide.rst
  5. 296 0
      docs/dev_guide/chado.rst
  6. 1 1
      docs/user_guide.rst
  7. 11 4
      docs/user_guide/bulk_loader.rst
  8. 8 3
      docs/user_guide/example_genomics/pub_import.rst
  9. 1 0
      docs/user_guide/install_tripal.rst
  10. 7 3
      docs/user_guide/install_tripal/automating_job_execution.rst
  11. 13 0
      docs/user_guide/install_tripal/drupal_home.rst
  12. 25 20
      docs/user_guide/install_tripal/manual_install/install_drupal.rst
  13. 8 3
      docs/user_guide/install_tripal/manual_install/install_prereqs.rst
  14. 11 6
      docs/user_guide/install_tripal/manual_install/install_tripal.rst
  15. 13 6
      docs/user_guide/install_tripal/rapid_install.rst
  16. 3 3
      docs/user_guide/install_tripal/upgrade_from_tripal2.rst
  17. 6 1
      docs/user_guide/job_management.rst
  18. 5 1
      docs/user_guide/mviews.rst
  19. 5 1
      docs/user_guide/web_services.rst
  20. 137 0
      tests/tripal_chado/api/ChadoComplianceTest.php
  21. 313 0
      tests/tripal_chado/api/ChadoSchemaTest.php
  22. 93 93
      tripal/api/tripal.entities.api.inc
  23. 5 1
      tripal/includes/TripalFieldDownloaders/TripalCSVDownloader.inc
  24. 5 1
      tripal/includes/TripalFieldDownloaders/TripalTabDownloader.inc
  25. 629 0
      tripal_chado/api/ChadoSchema.inc
  26. 74 63
      tripal_chado/api/modules/tripal_chado.cv.api.inc
  27. 27 3
      tripal_chado/api/tripal_chado.schema_v1.2.api.inc
  28. 19 17
      tripal_chado/api/tripal_chado.schema_v1.3.api.inc
  29. 1 0
      tripal_chado/api/tripal_chado.variables.api.inc
  30. 169 169
      tripal_chado/includes/TripalImporter/OBOImporter.inc
  31. 95 95
      tripal_chado/includes/setup/tripal_chado.setup.inc
  32. 63 63
      tripal_chado/includes/tripal_chado.semweb.inc
  33. 19 19
      tripal_chado/tripal_chado.install
  34. 32 36
      tripal_ws/api/tripal_ws.api.inc
  35. 79 32
      tripal_ws/includes/TripalFields/remote__data/remote__data.inc
  36. 1 1
      tripal_ws/includes/TripalWebService/TripalContentService_v0_1.inc

+ 5 - 4
.github/ISSUE_TEMPLATE/feature_request.md

@@ -12,11 +12,12 @@ INSTRUCTIONS: The following template is meant to structure your feature request.
   if it's decided the feature is not a good fit for Tripal Core.
 --->
 
-<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
+<!--- Go over all the following points, and select the option in the brackets that applies to you. -->
 <!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
-- [ ] does this feature attempt to solve an existing problem with Tripal?
-- [ ] are you open to developing or collaborating on an extension module if this is not a good fit for Tripal Core (no pressure here -just good to know upfront :-) )
-- [ ] do you **Need** this feature ASAP?
+* This feature [does / does not] attempt to solve an existing problem with Tripal
+* I [am/am not] open to developing or collaborating on an extension module if this is not a good fit for Tripal Core
+<!--- (no pressure here -just good to know upfront :-) ) -->
+* This feature is [URGENT/Not Urgent]
 
 ### Description
 <!--- A clear and concise description of what you want to happen. -->

+ 16 - 19
.github/PULL_REQUEST_TEMPLATE.md

@@ -1,34 +1,31 @@
+<!--- Thank you for contributing! -->
 <!--- Provide a general summary of your changes in the Title above -->
 <!--- See our Contribution Guidelines here:
           https://github.com/tripal/tripal/blob/7.x-3.x/CONTRIBUTING.md -->
-          
-<!--- If it fixes an open issue, please add the issue link below. -->
-Issue #
 
-## Type(s) of Change(s)
-<!--- What types of changes does your code introduce? 
-         Put an `x` in all the boxes that apply: -->
-- [ ] Bug fix (non-breaking change which fixes an issue)
-- [ ] New feature (non-breaking change which adds functionality)
-- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
-- [ ] API-specific change (fix or addition to an API function)
-- [ ] Updates documentation (inline or markdown files)
+
+<!---  Please set the header below based on the PR type:
+# New Feature
+# Bux Fix
+# Documentation  --->
+
+#
+
+Issue #
 
 ## Description
 <!--- Describe your changes in detail -->
 <!--- Why is this change required? What problem does it solve? -->
 
 ## Testing?
-<!--- Please describe in detail how you tested your changes. -->
-<!--- Include details of your testing environment, tests ran to see how -->
-<!--- your change affects other areas of the code, etc. -->
+<!--- Please describe in detail how to test these changes. -->
 <!--- Reviewers will use this section to test the submission! -->
+<!--- If you've implemented PHPUnit tests, you can describe the test cases here. -->
+<!--- Unit testing guidelines: https://github.com/tripal/tripal/blob/7.x-3.x/tests/README.md -->
 
 ## Screenshots (if appropriate):
 
 ## Additional Notes (if any):
-<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
-<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
-- [ ] My code follows the code style of this project.
-- [ ] My change requires a change to the documentation.
-- [ ] I have updated the documentation accordingly.
+
+<!--- New features should include in-line code documentation. -->
+<!--- Would a user or developer guide be helpful for this feature? -->

+ 49 - 0
docs/README.md

@@ -0,0 +1,49 @@
+The Tripal documentation is written in [**Restructured Text**](http://docutils.sourceforge.net/rst.html), compiled with [Sphinx](http://www.sphinx-doc.org/en/master/usage/quickstart.html), and built/hosted with [ReadTheDocs](https://readthedocs.org/).  This directory, when compiled, is hosted at https://tripal.readthedocs.io/en/latest/
+
+For minor changes, you can simply [Edit the file using the Github editor](https://help.github.com/articles/editing-files-in-your-repository/), which will allow you to make a Pull Request.  Once approved, your changes will be reflected in the documentation automatically! 
+
+# Guide
+
+### Install Sphinx
+For minor changes, you don't need to build the documentation!  If you want to see how your changes will look on the built site, however, you will need Sphinx installed.
+
+For more information, please see the Sphinx setup guide:
+http://www.sphinx-doc.org/en/master/usage/quickstart.html
+
+
+### Building your changes
+
+For more extensive edits, or when contributing new guides, you should build the documentation locally. From the `docs` root (eg `/var/www/html/sites/all/modules/tripal/docs/`, execute `make html`.  The built site will be in `docs/_build/html/index.html`.
+
+### Tripal conventions
+Please follow these guidelines when updating our docs. Let us know if you have any questions or something isn't clear.
+
+Please place images in the same folder as the guide text file, following the convention [file_name].[n].[optional description].[extension].  For example, `configuring_page_display.3.rearrange.png` or `configuring_page_display.1.png` are both located in `docs/user_guide/` and are part of the `configuring_page_display.rst` guide.
+
+We currently use the following syntax:
+```
+Title of File (using title case)
+=================================
+
+Introduction text.
+
+Section Title
+-------------
+
+We use double backticks to indicate ``inline-code`` including file names, function and method names, paths, etc.
+
+Longer code-blocks should begin with the ``.. code-block:: [type]`` directive and should be indented at least one 
+level. There should also be a blank line before and after it as shown below.
+
+.. code-block:: sql
+  if ($needs_documentation) {
+      use $these_guidelines;
+      $contribute_docs = $appreciated;
+  }
+
+Section 1.1 Title
+^^^^^^^^^^^^^^^^^
+
+The use of appropriate sections makes reading documentation and later specific details easier. Sub sections such 
+as this one will be hidden unless the main section is already selected.
+```

+ 1 - 0
docs/dev_guide.rst

@@ -9,6 +9,7 @@ Developer's Guide
    dev_guide/introduction
    dev_guide/data_structures
    dev_guide/best_practices
+   dev_guide/chado
    dev_guide/custom_modules
    dev_guide/custom_field
    dev_guide/custom_data_loader

+ 296 - 0
docs/dev_guide/chado.rst

@@ -0,0 +1,296 @@
+Accessing Chado
+================
+
+Primarily biological data made available to Tripal is stored in the GMOD Chado
+schema. As such, you will likely need to interact with Chado at some point.
+Tripal has developed a number of API functions and classes to make this
+interaction easier and more generic.
+
+The Chado Query API
+--------------------
+
+Provides an API for querying of chado including inserting, updating, deleting and selecting from specific chado tables. There is also a generic function, ``chado_query()``, to execute and SQL statement on chado. It is ideal to use these functions to interact with chado in order to keep your module compatible with both local & external chado databases. Furthermore, it ensures connection to the chado database is taken care of for you.
+
+Generic Queries to a specifc chado table
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Selecting Records
+""""""""""""""""""
+
+``chado_select_record( [table name], [columns to select], [specify record to select], [options*] )``
+
+This function allows you to select various columns from the specified chado table. Although you can only select from a single table, you can specify the record to select using values from related tables through use of a nested array. For example, the following code shows you how to select the name and uniquename of a feature based on it's type and source organism.
+
+.. code-block:: php
+
+  $values =  array(
+    'organism_id' => array(
+      'genus' => 'Citrus',
+      'species' => 'sinensis',
+    ),
+    'type_id' => array (
+      'cv_id' => array (
+        'name' => 'sequence',
+      ),
+      'name' => 'gene',
+      'is_obsolete' => 0
+    ),
+  );
+
+  $result = chado_select_record(
+    'feature',                      // table to select from
+    array('name', 'uniquename'),    // columns to select
+    $values                         // record to select (see variable defn. above)
+  );
+
+Inserting Records
+""""""""""""""""""
+
+``chado_insert_record( [table name], [values to insert], [options*] )``
+
+This function allows you to insert a single record into a specific table. The values to insert are specified using an associative array where the keys are the column names to insert into and they point to the value to be inserted into that column. If the column is a foreign key, the key will point to an array specifying the record in the foreign table and then the primary key of that record will be inserted in the column. For example, the following code will insert a feature and for the type_id, the cvterm.cvterm_id of the cvterm record will be inserted and for the organism_id, the organism.organism_id of the organism_record will be inserted.
+
+.. code-block:: php
+
+  $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 = chado_insert_record(
+    'feature',             // table to insert into
+    $values                // values to insert
+  );
+
+Updating Records
+""""""""""""""""""
+
+``chado_update_record( [table name], [specify record to update], [values to change], [options*] )``
+
+This function allows you to update records in a specific chado table. The record(s) you wish to update are specified the same as in the select function above and the values to be update are specified the same as the values to be inserted were. For example, the following code species that a feature with a given uniquename, organism_id, and type_id (the unique constraint for the feature table) will be updated with a new name, and the type changed from a gene to an mRNA.
+
+.. code-block:: php
+
+  $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 = chado_update_record('feature',$umatch,$uvalues);
+
+Deleting Records
+"""""""""""""""""
+
+``chado_delete_record( [table name], [specify records to delete], [options*] )``
+
+This function allows you to delete records from a specific chado table. The record(s) to delete are specified the same as the record to select/update was above. For example, the following code will delete all genes from the organism Citrus sinensis.
+
+.. code-block:: php
+
+  $values =  array(
+    'organism_id' => array(
+        'genus' => 'Citrus',
+        'species' => 'sinensis',
+     ),
+    'type_id' => array (
+        'cv_id' => array (
+           'name' => 'sequence',
+        ),
+        'name' => 'gene',
+        'is_obsolete' => 0
+     ),
+  );
+  $result = chado_select_record(
+     'feature',                      // table to select from
+     $values                         // records to delete (see variable defn. above)
+  );
+
+Generic Queries for any SQL
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Often it is necessary to select from more then one table in chado or to execute other complex queries that cannot be handled efficiently by the above functions. It is for this reason that the ``chado_query( [sql string], [arguments to sub-in to the sql] )`` function was created. This function allows you to execute any SQL directly on the chado database and should be used with care. If any user input will be used in the query make sure to put a placeholder in your SQL string and then define the value in the arguments array. This will make sure that the user input is sanitized and safe through type-checking and escaping. The following code shows an example of how to use user input resulting from a form and would be called with the form submit function.
+
+.. code-block:: php
+
+  $sql = "SELECT F.name, CVT.name as type_name, ORG.common_name
+           FROM feature F
+           LEFT JOIN cvterm CVT ON F.type_id = CVT.cvterm_id
+           LEFT JOIN organism ORG ON F.organism_id = ORG.organism_id
+           WHERE
+             F.uniquename = :feature_uniquename";
+  $args = array( ':feature_uniquename' => $form_state['values']['uniquename'] );
+  $result = chado_query( $sql, $args );
+  foreach ($result as $r) { [Do something with the records here] }
+
+If you are going to need more then a couple fields, you might want to use the Chado Variables API (specifically ``chado_generate_var()``) to select all of the common fields needed including following foreign keys.
+
+Loading of Variables from chado data
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+These functions, ``chado_generate_var()`` and ``chado_expand_var()``, generate objects containing the full details of a record(s) in chado. These should be used in all theme templates.
+
+This differs from the objects returned by ``chado_select_record`` 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 ``chado_select_record`` should be used if you only case about a few columns.
+
+The initial variable is generated by the ``chado_generate_var([table], [filter criteria], [optional options])`` function. An example of how to use this function is:
+
+.. code-block:: php
+
+  $values = array(
+    'name' => 'Medtr4g030710'
+  );
+  $features = chado_generate_var('feature', $values);
+
+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.
+
+Some tables and fields are excluded by default. To have those tables & fields added to your variable you can use the ``chado_expand_var([chado variable], [type], [what to expand], [optional options])`` function. An example of how to use this function is:
+
+.. code-block:: php
+
+  // Get a chado object to be expanded
+  $values = array(
+    'name' => 'Medtr4g030710'
+  );
+
+  $features = chado_generate_var('feature', $values);
+
+  // Expand the organism node
+  $feature = chado_expand_var($feature, 'node', 'organism');
+
+  // Expand the feature.residues field
+  $feature = chado_expand_var($feature, 'field', 'feature.residues');
+
+  // Expand the feature properties (featureprop table)
+  $feature = chado_expand_var($feature, 'table', 'featureprop');
+
+
+The Chado Schema API
+--------------------
+
+The Chado Schema API provides an application programming interface (API) for describing Chado tables, accessing these descriptions and checking for compliancy of your current database to the chado schema. This API consists of the ChadoSchema class which provides methods for interacting with the Chado Schema API and a collection of supporting functions, one for each table in Chado, which describe each version of the Chado schema. Each function simply returns a Drupal style array that defines the table.
+
+Ensuring columns Tables & Columns exist
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Generally you can assume the tables and columns in the Chado schema have been unaltered. That said, there are still cases where you might want to check that specific tables and columns exist. For example, when using a custom table, it is best practice to ensure it is there before querying as it can be removed through the administrative interface.
+
+To check the existence of a specific table and column, you can use the following:
+
+.. code-block:: php
+
+  $chado_schema = new \ChadoSchema();
+
+  // Check that the organism_feature_count custom table exists.
+  $table_name = 'organism_feature_count';
+  $table_exists = $chado_schema->checkTableExists($table_name);
+
+  if ($table_exists) {
+
+    // Check that the organism_feature_count.feature_id column exists.
+    $column_name = 'feature_id';
+    $column_exists = $chado_schema->checkColumnExists($table_name, $column_name);
+
+    if ($column_exists) {
+
+      [ do your query, etc. here ]
+
+    } else { [warn the admin using tripal_repot_error()] }
+  } else { [warn the admin using tripal_repot_error()] }
+
+Checking the Schema Version
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you are using chado tables specific to a given version of Chado, it is best practice to check the chado version of the current site before querying those tables. You can use the following query to do this:
+
+.. code-block:: php
+
+  $chado_schema = new \ChadoSchema();
+  $version = $chado_schema-getVersion();
+  if ($version == '1.3') {
+    [do your chado v1.3 specific querying here]
+  } else { [warn the admin using tripal_report_error() ] }
+
+
+Retrieving a list of tables
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To retrieve a list of Chado tables, you can use the following:
+
+.. code-block:: php
+
+  $chado_schema = new \ChadoSchema();
+
+  // All Chado Tables including custom tables
+  $all_tables = $chado_schema->getTableNames(TRUE);
+
+  // All Chado Tables without custom tables
+  $all_tables = $chado_schema->getTableNames();
+
+  // Chado tables designated as Base Tables by Tripal.
+  $base_tables = $chado_schema->getBaseTables();
+
+
+Ensuring your Chado instance is compliant
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Checking compliancy of your Chado instance with the released Chado Schema is a great way to **confirm an upgrade has gone flawlessly**. Additionally, while it is not recommended, sometimes customizations to the Chado schema may be necessary. In these cases, you should **ensure backwards compatibility** through compliance checking to confirm Tripal will work as expected.
+
+Chado compliancy testing is provided with Tripal's automated PHPUnit testing. As such, to test compliancy of your specific Chado instance, you first need to install Composer. Luckily this can be as easy as:
+
+.. code-block:: bash
+
+  php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
+  php -r "if (hash_file('SHA384', 'composer-setup.php') === '544e09ee996cdf60ece3804abc52599c22b1f40f4323403c44d44fdfdd586475ca9813a858088ffbc1f233e9b180f061') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
+  php composer-setup.php
+  php -r "unlink('composer-setup.php');"
+
+Once you have Composer, you need to install PHPUnit. This is installed locally within your Tripal repository. The following bash snippet shows you how to both install composer locally and run compliance checking.
+
+.. code-block:: php
+
+  cd [DRUPAL_ROOT]/sites/all/modules/tripal
+  composer up
+
+  # Now run compliance checking
+  ./vendor/bin/phpunit --group chado-compliance
+
+Schema Definition
+^^^^^^^^^^^^^^^^^^
+
+To retrieve the schema definition for a specific table, you can execute the following:
+
+.. code-block:: php
+
+  $table_name = 'feature';
+  $chado_schema = new \ChadoSchema();
+  $table_schema = $chado_schema->getTableSchema($table_name);
+
+The resulting ``$table_schema`` variable contains a Drupal-style array describing the schema definition of the table specified by ``$table_name``. This is a great tool when trying to develop generic queries, since you can extract information about an unknown table and use it to build a query for that table. For more information on the format of this array, see `the Drupal Schema API documentation <https://api.drupal.org/api/drupal/includes%21database%21schema.inc/group/schemaapi/7.x>`_.

+ 1 - 1
docs/user_guide.rst

@@ -1,5 +1,5 @@
 User's Guide
-==============
+============
 
 
 .. toctree::

+ 11 - 4
docs/user_guide/bulk_loader.rst

@@ -1,7 +1,10 @@
-
 Bulk Loader
 ===========
 
+.. note::
+
+  Remember you must set the ``$DRUPAL_HOME`` environment variable if you want to cut-and-paste the commands below. See :doc:`./install_tripal/drupal_home`
+
 The bulk loader is a tool that Tripal provides for loading of data contained in tab delimited files. Tripal supports loading of files in standard formats (e.g. ``FASTA``, ``GFF``, ``OBO``), but Chado can support a variety of different biological data types and there are often no community standard file formats for loading these data. For example, there is no file format for importing genotype and phenotype data. Those data can be stored in the feature, stock and natural diversity tables of Chado. The Bulk Loader was introduced in Tripal v1.1 and provides a web interface for building custom data loader. In short, the site developer creates the bulk loader "template". This template can then be used and re-used for any tab delimited file that follows the format described by the template. Additionally, bulk loading templates can be exported allowing Tripal sites to share loaders with one another.  Loading templates that have been shared are available on the Tripal website here: http://tripal.info/extensions/bulk-loader-templates.
 
 The following commands can be executed to install the Tripal Bulk Loader using Drush:
@@ -20,7 +23,7 @@ To demonstrate use of the Bulk Loader, a brief example that imports a list of or
 
 .. code-block bash
 
-  cd /var/www/html/sites/default/files
+  cd $DRUPAL_HOME/sites/default/files
   wget http://tripal.info/sites/default/files/book_pages/Fragaria_0.txt
 
 
@@ -244,10 +247,14 @@ Provide the following values:
 
 * Job Name: Import of Fragaria species
 * Template: NCBI Taxonomy Importer (taxid, genus species).
-* Data File: /var/www/html/sites/default/files/Fragaria_0.txt
+* Data File: [DRUPAL_HOME]/sites/default/files/Fragaria_0.txt
 * Keep track of inserted IDs: No
 * File has a header: No
 
+.. note::
+
+  Be sure to change the [DRUPAL_HOME] token to where Drupal is installed.
+
 Click **Save**. The page then appears as follows:
 
 .. image:: ./bulk_loader.9.png
@@ -261,7 +268,7 @@ Now that we have created a job, we can submit it for execution by clicking the *
 .. code-block:: shell
 
   cd /var/www
-  drush trp-run-jobs --username=admin --root=/var/www/html
+  drush trp-run-jobs --username=admin --root=$DRUPAL_HOME
 
 After execution of the job you should see similar output to the terminal window:
 

+ 8 - 3
docs/user_guide/example_genomics/pub_import.rst

@@ -1,5 +1,9 @@
 Importing Publications
 ======================
+.. note::
+
+  Remember you must set the ``$DRUPAL_HOME`` environment variable if you want to cut-and-paste the commands below. See :doc:`../install_tripal/drupal_home`
+  
 Tripal provides an interface for automatically and manually adding publications.
 
 Manually Adding a Publication
@@ -81,7 +85,7 @@ Next, there are two ways to import these publications. The first it to manually
 
 ::
 
-  cd /var/www/html
+  cd $DRUPAL_HOME
   drush trp-import-pubs --username=administrator
 
 You should see output to the terminal that begins like this:
@@ -105,7 +109,7 @@ Some things to know about the publication importer:
 
   ::
 
-    cd /var/www/html
+    cd $DRUPAL_HOME
     drush trp-run-jobs --user=administrator
 
 The second way to import publications is to add an entry to the UNIX cron. We did this previously for the Tripal Jobs management system when we first installed Tripal. We will add another entry for importing publications. But first, now that we have imported all of the relevant pubs, we need to return to the importers list at **Tripal → Data Loaders → Chado Publication Importers** and disable the first importer we created. We do not want to run that importer again, as we've already imported all historical publications on record at PubMed. Click the edit button next to the importer named Pubs for Citrus sinensis, click the disable checkbox and then save the template. The template should now be disabled.
@@ -120,11 +124,12 @@ Now add the following line to the bottom of the crontab:
 
 ::
 
-  30 8 1,15 * *  su - www-data -c '/usr/local/drush/drush -r /var/www/html -l http://[site url] trp-import-pubs --report=[your email] > /dev/null'
+  30 8 1,15 * *  su - www-data -c '/usr/local/drush/drush -r [DRUPAL_HOME] -l http://[site url] trp-import-pubs --report=[your email] > /dev/null'
 
 Where
 
 - [site url] is the full URL of your site
 - [your email] is the email address of the user that should receive an email containing a list of publications that were imported. You can separate multiple email addresses with a comma.
+- [DRUPAL_HOME] is the directory where Drupal is installed
 
 The cron entry above will launch the importer at 8:30am on the first and fifteenth days of the month. We will run this importer twice a month in the event it fails to run (e.g. server is down) at least one time during the month.

+ 1 - 0
docs/user_guide/install_tripal.rst

@@ -8,6 +8,7 @@ Install Tripal
    :glob:
 
    ./install_tripal/pre_planning
+   ./install_tripal/drupal_home
    ./install_tripal/server_setup
    ./install_tripal/drush_installation
    ./install_tripal/rapid_install

+ 7 - 3
docs/user_guide/install_tripal/automating_job_execution.rst

@@ -1,6 +1,10 @@
 Automating Job Execution
 ========================================
 
+.. note::
+
+  Remember you must set the ``$DRUPAL_HOME`` environment variable if you want to cut-and-paste the commands below. See :doc:`./drupal_home`
+
 The Drupal cron is used to automatically execute necessary Drupal housekeeping tasks on a regular interval.  You should *always* setup the Drupal cron to ensure your site checks for updates and security issues.  To do this, we want to integrate Drupal cron with the UNIX cron facility.  The UNIX cron will automatically execute commands on set regular intervals.  First, we must get the appropriate URL for the cron by navigating to **Configuration → Cron**. On this page you will see a link that we will use for cron:
 
 .. image:: automating_job_execution.cron.png
@@ -36,9 +40,9 @@ Any job that is added to the Job's system can be run manually on the command lin
 
 .. code-block:: bash
 
-  drush trp-run-jobs --username=administrator --root=/var/www/html
+  drush trp-run-jobs --username=administrator --root=$DRUPAL_HOME
 
-Remember to change the username from **administrator** to the name of the administrator on your site and change **/var/www/html** to the location where your site installed on the server.
+Remember to change the username from **administrator** to the name of the administrator on your site.
 
 Option #2: Additional Cron Entry
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -53,7 +57,7 @@ Add this line to the crontab:
 
 .. code-block:: bash
 
-  0,5,10,15,20,25,30,35,40,45,50,55 * * * * drush trp-run-jobs --username=administrator --root=/var/www/html
+  0,5,10,15,20,25,30,35,40,45,50,55 * * * * drush trp-run-jobs --username=administrator --root=$DRUPAL_HOME
 
 Here, job execution will occur every 5 minutes.
 

+ 13 - 0
docs/user_guide/install_tripal/drupal_home.rst

@@ -0,0 +1,13 @@
+DRUPAL_HOME Variable
+====================
+An important convention in this document is the use of the ``$DRUPAL_HOME`` environment variable.  If you are new to UNIX/Linux you can learn about environment variables `here <https://www.tutorialspoint.com/unix/unix-environment.htm>`_.  Drupal is a necessary depenency of Tripal.  The setup and installation sections describe how to install Drupal.  If you follow the instructions exactly as described in this User's Guide you will install Drupal into ``/var/www/html``. However, some may desire to install Drupal elsewhere.  To ensure that all command-line examples in this guide can be cut-and-pasted you **must** set the ``$DRUPAL_HOME`` variable.  You can set the variable in the following way:
+
+  .. code-block:: bash
+
+    DRUPAL_HOME=/var/www/html
+    
+Be sure to change the path ``/var/www/html`` to the location where you have installed Drupal.  If you have never installed Drupal and you intend on following this guide step-by-step then use the command-line above to get started.
+
+.. note::
+
+  You will have to set the ``$DRUPAL_HOME`` environment variable anytime you open a new terminal window.

+ 25 - 20
docs/user_guide/install_tripal/manual_install/install_drupal.rst

@@ -33,29 +33,32 @@ We no longer need to be the postgres user so exit
 Software Installation
 ---------------------
 
-We want to install Drupal into our web document root (/var/www/html).   Before we can install Drupal we must ensure that that we are allowed to add files into the /var/www/html directory.  Select a user account that will be the owner of all web files and change the owner of the /var/www/html directory to that user:
+.. note::
+
+  Remember you must set the ``$DRUPAL_HOME`` environment variable if you want to cut-and-paste the commands below. See :doc:`../drupal_home`
+
+
+Before we can install Drupal we must ensure that that we are allowed to add files into the root directory.  Select a user account that will be the owner of all web files and change the owner of the ``$DRUPAL_HOME`` directory to that user:
 
 .. code-block:: bash
 
-  sudo chown -R [user] /var/www/html
+
+  sudo chown -R [user] $DRUPAL_HOME
 
 Substitute [user] for the name of the user that will own the web files.
 
 
 .. note::
 
-  The apache web server runs as the user 'www-data'.  For security reasons you should chose a user other than 'www-data' to be the owner of the /var/www/html directory.
+  The apache web server runs as the user 'www-data'.  For security reasons you should chose a user other than 'www-data' to be the owner of the Drupal root directory.
 
-Tripal 3.x requires version 7.x of Drupal. Drupal can be freely downloaded from the http://www.drupal.org website. At the writing of this Tutorial the most recent version of Drupal 7 is version 7.59. The software can be downloaded manually from the Drupal website through a web browser or we can use the 'wget' command to retrieve it:
+Tripal 3.x requires version 7.x of Drupal. Drupal can be freely downloaded from the http://www.drupal.org website. At the writing of this Tutorial the most recent version of Drupal 7 is version 7.59. The software can be downloaded manually from the Drupal website through a web browser or we can use the ``wget`` command to retrieve it:
 
 .. code-block:: bash
 
-  cd /var/www/html
+  cd $DRUPAL_HOME
   wget http://ftp.drupal.org/files/projects/drupal-7.59.tar.gz
 
-.. note::
-
-  If you are using older version of Ubuntu the web root directory may be /var/www rather than /var/www/html and you may need to change the path accordingly.
 
 Next, we want to install Drupal. We will use the tar command to uncompress the software:
 
@@ -78,11 +81,11 @@ If an index.html file is present (as is the case with Ubuntu installations) you
 
 .. note::
 
-  It is extremely important the the hidden file .htaccess is also moved (note the second 'mv' command above. Check to make sure this file is there
+  It is extremely important the the hidden file ``.htaccess`` is also moved (note the second ``mv`` command above. Check to make sure this file is there:
 
-.. code-block:: bash
+  .. code-block:: bash
 
-  ls -l .htaccess
+    ls -l .htaccess
 
 Configuration File
 ------------------
@@ -93,15 +96,15 @@ First navigate to the location where the configuration file should go:
 
 .. code-block:: bash
 
-  cd /var/www/html/sites/default/
+  cd $DRUPAL_HOME/sites/default/
 
-Next, copy the example configuration that already exists in the directory to be our actual configuration file by renaming it to settings.php.
+Next, copy the example configuration that already exists in the directory to be our actual configuration file by renaming it to ``settings.php``.
 
 .. code-block:: bash
 
   cp default.settings.php settings.php
 
-Now, we need to edit the configuration file to tell Drupal how to connect to our database server. To do this we'll use an easy to use text editor gedit
+Now, we need to edit the configuration file to tell Drupal how to connect to our database server. To do this we'll use an easy to use text editor **gedit**.
 
 .. code-block:: bash
 
@@ -127,17 +130,19 @@ and then insert the following array just after the above line:
   );
 
 Replace the text '********' with your database password for the user 'drupal' created previously.  Save the configuration file and close the editor.
-Files directory creation
 
-Finally, we need to create the directory where Drupal will have write-access to add files.  By default, Drupal expects to have write permission in the /var/www/html/sites/default/files directory.  Therefore, we will set group ownership of the directory to the group used by the Apache web server.  This will be the user that Drupal uses to write files.
+Files Directory Creation
+--------------------------
+
+Finally, we need to create the directory where Drupal will have write-access to add files.  By default, Drupal expects to have write permission in the ``$DRUPAL_HOME/sites/default/files`` directory.  Therefore, we will set group ownership of the directory to the group used by the Apache web server.  This will be the user that Drupal uses to write files.
 
 .. code-block:: bash
 
-  mkdir -p /var/www/html/sites/default/files
-  sudo chgrp [group] /var/www/html/sites/default/files
-  sudo chmod g+rw /var/www/html/sites/default/files
+  mkdir -p $DRUPAL_HOME/sites/default/files
+  sudo chgrp [group] $DRUPAL_HOME/sites/default/files
+  sudo chmod g+rw $DRUPAL_HOME/sites/default/files
 
-Substitute [group] for the name of the web server's group.  In Ubuntu this is www-data in CentOS this is apache.The above commands creates the directory, sets the group ownership for group. and gives read/write permissions to the group on the directory.
+Substitute [group] for the name of the web server's group.  In Ubuntu this is www-data in CentOS this is apache. The above commands creates the directory, sets the group ownership for group, and gives read/write permissions to the group on the directory.
 
 Web-based Steps
 ---------------

+ 8 - 3
docs/user_guide/install_tripal/manual_install/install_prereqs.rst

@@ -1,13 +1,18 @@
 Tripal Prerequisites
 ====================
 
+.. note::
+
+  Remember you must set the ``$DRUPAL_HOME`` environment variable if you want to cut-and-paste the commands below. See :doc:`../drupal_home`
+
+
 Tripal v3.x requires several Drupal modules. These include  `Entity <https://www.drupal.org/project/entity>`_,  `Views <https://www.drupal.org/project/views>`_, `CTools <https://www.drupal.org/project/ctools>`_, `Display Suite <https://www.drupal.org/project/ds>`_, `Field Group <https://www.drupal.org/project/field_group>`_, `Field Group Table <https://www.drupal.org/project/field_group_table>`_, `Field Formatter Class <https://www.drupal.org/project/field_formatter_class>`_ and `Field Formatter Settings <https://www.drupal.org/project/field_formatter_settings>`_ modules.   Modules can be installed using the graphical Drupal website by clicking on the Modules link in the top adminstrative menu bar.  Instructions for instaling Modules via the web-interface can be found here:  https://www.drupal.org/documentation/install/modules-themes/modules-7. However, Drush can be quicker for module installation. The following instructions will show how to install a module using the Drush command-line tool.
 
 First, install the Entity module.  We will download the current version using the drush command. On the command-line, execute the following:
 
 .. code-block:: bash
 
-  cd /var/www/html/sites/all/modules
+  cd $DRUPAL_HOME/sites/all/modules
   drush pm-download entity
 
 Typically for all module installation we should check the README for additional installation instructions. Next, enable the module using a drush command:
@@ -23,14 +28,14 @@ For basic Tripal functionality you must also enable the Views and CTools modules
   drush pm-download views ctools
   drush pm-enable views views_ui ctools
 
-Finally, Tripal works best when it can provide default display layouts.   To support default layouts you must also enable the remainig dependencies:
+Finally, Tripal works best when it can provide default display layouts.   To support default layouts you must also enable the remaining dependencies:
 
 .. code-block:: bash
 
   drush pm-download ds field_group field_group_table field_formatter_class field_formatter_settings
   drush pm-enable ds field_group field_group_table field_formatter_class field_formatter_settings
 
-Optionally, you can install the ckeditor module.  This module provides a nice WYSIWYG editor that allows you to edit text on your site using a graphical editor. Otherwise, if you need images or formatting (e.g. underline, bold, headers) you would be required to write HTML.   It is recommended that this module be installed to improve the user experience:
+Optionally, you can install the ckeditor module.  This module provides a nice WYSIWYG editor that allows you to edit text on your site using a graphical editor. Otherwise, if you need images or formatting (e.g. underline, bold, headers) you would be required to write HTML.  It is recommended that this module be installed to improve the user experience:
 
 .. code-block:: bash
 

+ 11 - 6
docs/user_guide/install_tripal/manual_install/install_tripal.rst

@@ -1,6 +1,11 @@
 Tripal Installation
 ===================
 
+.. note::
+
+  Remember you must set the ``$DRUPAL_HOME`` environment variable if you want to cut-and-paste the commands below. See :doc:`../drupal_home`
+
+
 Before installation of Tripal, you must first have a working Drupal installation.  Please see the previous section of this tutorial for step-by-step examples for server setup and Drupal installation instructions.  After installation of Tripal, you may install any Tripal extension modules you may want.
 
 Download Tripal
@@ -24,15 +29,15 @@ A bug exists in Drupal related to the bytea data type in PostgreSQL. At the writ
 
 .. code-block:: bash
 
-  cd /var/www/html
+  cd $DRUPAL_HOME
   wget --no-check-certificate https://drupal.org/files/drupal.pgsql-bytea.27.patch
   patch -p1 < drupal.pgsql-bytea.27.patch
 
-There is also a bug in the Drupal Views 3.0 code that prevents Tripal's administrative and search data views from functioning. The patch is provided within the tripal_veiws module. To apply the patch execute the following:
+There is also a bug in the Drupal Views 3.0 code that prevents Tripal's administrative and search data views from functioning. The patch is provided within the tripal_views module. To apply the patch execute the following:
 
 .. code-block:: bash
 
-  cd /var/www/html/sites/all/modules/views
+  cd $DRUPAL_HOME/sites/all/modules/views
   patch -p1 < ../tripal/tripal_chado_views/views-sql-compliant-three-tier-naming-1971160-30.patch
 
 Install Tripal
@@ -74,7 +79,7 @@ Jobs in the queue can be executed using drush to manually launch the job:
 
 .. code-block:: bash
 
-  drush trp-run-jobs --username=administrator --root=/var/www/html
+  drush trp-run-jobs --username=administrator --root=$DRUPAL_HOME
 
 As the installation of Chado proceeds, we should see output on the terminal console indicating the progress of the installation.  You should see output similar to the following:
 
@@ -108,7 +113,7 @@ To prepare the site click the button Prepare this site.   A new job is added to
 
 .. code-block:: bash
 
-  drush trp-run-jobs --username=administrator --root=/var/www/html
+  drush trp-run-jobs --username=administrator --root=$DRUPAL_HOME
 
 .. note::
 
@@ -125,4 +130,4 @@ Preparing Chado and Drupal in a previous step resulted in the automatic creation
 
 .. image:: install_tripal.install7.png
 
-Review these permissions and set them according to how you want content to be managed.   Typically, the administrator user receives all permissions, and anonymous and authenticated users receive 'View' permissions for all content types.  If you desire to create other types of users, Drupal allows you to do this by creating new types of roles.  For example, if you know that some users will be responsible for curating content, then you may add a curator role by clicking the Roles link in the top right corner of this permissions page.  After the new role is created you can return to the permission page to set the permissions accordingly.
+Review these permissions and set them according to how you want content to be managed.  Typically, the administrator user receives all permissions, and anonymous and authenticated users receive 'View' permissions for all content types.  If you desire to create other types of users, Drupal allows you to do this by creating new types of roles.  For example, if you know that some users will be responsible for curating content, then you may add a curator role by clicking the **Roles** link in the top right corner of this permissions page.  After the new role is created you can return to the permission page to set the permissions accordingly.

+ 13 - 6
docs/user_guide/install_tripal/rapid_install.rst

@@ -1,8 +1,15 @@
 Installation Method #1: Rapid Installation
 ==========================================
 
-Before installing via the rapid installation process please ensure drush is installed, and the server is setup.    Rapid Installation works with Tripal v3.0-rc2 (release candidate 2) and later.   If you are using a previous version of Tripal, please proceed to the step-by-step instructions.
+.. note::
+
+  Remember you must set the ``$DRUPAL_HOME`` environment variable if you want to cut-and-paste the commands below. See :doc:`./drupal_home`
+
+
+Before installing via the rapid installation process please ensure drush is installed, and the server is setup.   Rapid Installation works with Tripal v3.0-rc2 (release candidate 2) and later.  If you are using a previous version of Tripal, please proceed to the step-by-step instructions.
+
 Database Setup
+---------------
 
 Before we can install Tripal we must have a database ready for it.  In the server setup instructions were provided to set up a PostgreSQL database server. Now, we need to create the Drupal database. To do so we must first become the PostgreSQL user.
 
@@ -10,7 +17,7 @@ Before we can install Tripal we must have a database ready for it.  In the serve
 
   sudo su - postgres
 
-Next, create the new 'drupal' user account. This account will not be a "superuser' nor allowed to create new roles, but should be allowed to create a database.
+Next, create the new 'drupal' user account. This account will not be a "superuser" nor allowed to create new roles, but should be allowed to create a database.
 
 .. code-block:: bash
 
@@ -31,17 +38,17 @@ We no longer need to be the postgres user so exit
 Tripal Installation
 -------------------
 
-Navigate to the directory you want to create the website. For this example it will be /var/www/html (the typical home location for Ubuntu and CentOS)
+Navigate to your Drupal install directory.
 
 .. code-block:: bash
 
-  cd /var/www/html
+  cd $DRUPAL_HOME
 
 .. note::
 
   Make sure you have write permissions within this directory.
 
-Clone the tripal_install project using the git command and move the contents up one level into the web document directory:
+Clone the tripal_install project using the ``git`` command and move the contents up one level into the web document directory:
 
 .. code-block:: bash
 
@@ -70,7 +77,7 @@ From this point onward, you will be asked a series of questions in the terminal
      Administrator password: P@55w0rd
   Is this information correct? (y/n): y
 
-Next, you will be asked for the database information: database name, database  username, database  user password, host, and port.  The database name and user should match what you created in the previous section (i.e. database name = 'drupal' and database user = 'drupal').  The 'host' is the name of the server or its IP address, and the port is a numerical value that PostgreSQL uses for communication.  By default PostgreSQL uses the port 5432.  If a mistake is made you can make corrections as shown in the following screenshot:
+Next, you will be asked for the database information: database name, database  username, database  user password, host, and port.  The database name and user should match what you created in the previous section (i.e. database name = 'drupal' and database user = 'drupal').  The 'host' is the name of the server or its IP address, and the port is a numerical value that PostgreSQL uses for communication.  By default PostgreSQL uses the port 5432.  If a mistake is made you can make corrections as shown below:
 
 ::
 

+ 3 - 3
docs/user_guide/install_tripal/upgrade_from_tripal2.rst

@@ -44,7 +44,7 @@ Step 1: Upgrade Tripal
 
     drush pm-disable tripal_core
 
-4.  The Tripal modules must also be downloaded and updated. To do this, delete the old Tripal v2 modules directories, located in ``sites/all/modules`` from your Drupal root:  for example ``/var/www/html/sites/all/modules``(be sure you have a backup before removing). The following command will retrieve the Tripal 3 version:
+4.  The Tripal modules must also be downloaded and updated. To do this, delete the old Tripal v2 modules directories, located in ``sites/all/modules`` from your Drupal root:  for example ``/var/www/html/sites/all/modules`` (be sure you have a backup before removing). The following command will retrieve the Tripal 3 version:
 
   .. code-block:: bash
 
@@ -137,11 +137,11 @@ The process allows you to create Tripal 3 content types exposing the same data a
 
 4. Select the checkbox beside each Tripal v3 type you would like to create. The number of entities/pages that will be created for that content type is shown in brackets beside the name.
 
-5. Then click the "Migrate [Tripal v2 Type]" button. This will submit a Tripal job to create the requested content. Submit this job manually on the command-line as follows:
+5. Then click the "Migrate [Tripal v2 Type]" button. This will submit a Tripal job to create the requested content. Submit this job manually on the command-line as follows (note we ``cd`` to the project root at ``/var/www/html``: please navigate to wherever your site is installed):
 
   .. code-block:: bash
 
-    cd /var/www/html
+    cd $DRUPAL_HOME
     drush trp-run-jobs --user=administrator
 
 6. Now repeat 1-5 for each content type. Since this step simply creates new Tripal v3 content without touching the existing Tripal v2 content, there really is no reason not to migrate all your content types. Especially since the Tripal v3 content remains private and thus hidden from your users.

+ 6 - 1
docs/user_guide/job_management.rst

@@ -1,6 +1,11 @@
 Job Management (Tripal Daemon)
 ==============================
 
+.. note::
+
+  Remember you must set the $DRUPAL_HOME environment variable to cut-and-paste the commands below. See see :doc:`./install_tripal/drupal_home`
+
+
 The Tripal Daemon module is meant to provide a simple means of creating a robust command-line-driven, fully bootstrapped PHP Daemon. It uses the PHP-Daemon (https://github.com/shaneharter/PHP-Daemon) Library to create the Daemon (via the Libraries API) in order to not re-invent the wheel. It allows you to execute Jobs submitted to Tripal without using cron.  It provides a faster user experience for running jobs.  Prior to Tripal v3, the Tripal Daemon module was an extension module. It was integrated into the core Tripal pacakge.
 
 Features
@@ -27,7 +32,7 @@ Next, we need the `PHP-Daemon Library version 2.0 <https://github.com/shaneharte
 
 .. code-block:: shell
 
-  cd /var/www/html/sites/all/libraries
+  cd $DRUPAL_HOME/sites/all/libraries
   wget https://github.com/shaneharter/PHP-Daemon/archive/v2.0.tar.gz
   tar -zxvf v2.0.tar.gz
   mv v2.0.tar.gz PHP-Daemon

+ 5 - 1
docs/user_guide/mviews.rst

@@ -1,6 +1,10 @@
 Materialized Views
 ==================
 
+.. note::
+
+  Remember you must set the ``$DRUPAL_HOME`` environment variable if you want to cut-and-paste the commands below. See :doc:`./install_tripal/drupal_home`
+
 Chado is efficient as a data warehouse but queries can become slow depending on the type of query. To help simplify and speed up these queries, materialized views can be employed. For a materialized view, a new database table is created and then populated with the results of a pre-defined SQL query. This allows you to execute a much simpler and faster query on the materialized view when producing user pages. A side effect, however is redundant data, with the materialized view becoming stale if not updated regularly.
 
 Tripal provides a mechanism for populating and updating these materialized views. These can be found on the **Tripal → Data Storage → Chado -> Materialized Views** page.
@@ -13,7 +17,7 @@ This will submit jobs to populate the views with data. Now, run the jobs:
 
 .. code-block:: shell
 
-  cd /var/www/html
+  cd $DRUPAL_HOME
   drush trp-run-jobs --user=administrator
 
 You can now see that all views are up-to-date on the **Materialized Views Page**. The number of rows in the view table is shown.

+ 5 - 1
docs/user_guide/web_services.rst

@@ -1,6 +1,10 @@
 Web Services
 ============
 
+.. note::
+
+  Remember you must set the ``$DRUPAL_HOME`` environment variable if you want to cut-and-paste the commands below. See :doc:`./install_tripal/drupal_home`
+
 Overview
 --------
 
@@ -31,7 +35,7 @@ To enable web services, simply install the ``tripal_ws`` module, either using th
 
 .. code-block:: shell
 
-  cd /var/www/html
+  cd $DRUPAL_HOME
   drush pm-enable tripal_ws
 
 Exploring Web Services

+ 137 - 0
tests/tripal_chado/api/ChadoComplianceTest.php

@@ -0,0 +1,137 @@
+<?php
+namespace Tests\tripal_chado\api;
+
+use StatonLab\TripalTestSuite\DBTransaction;
+use StatonLab\TripalTestSuite\TripalTestCase;
+
+module_load_include('inc', 'tripal_chado', 'api/ChadoSchema');
+
+/**
+ * Tests the current Chado Database is compliant with the schema definition used by Tripal
+ */
+class ChadoComplianceTest extends TripalTestCase {
+  // Uncomment to auto start and rollback db transactions per test method.
+  use DBTransaction;
+
+  /**
+   * DataProvider, a list of all chado tables.
+   *
+   * @return array
+   */
+  public function chadoTableProvider() {
+
+    $chado_schema = new \ChadoSchema();
+    $version = $chado_schema->getVersion();
+
+    $dataset = [];
+    foreach ($chado_schema->getTableNames() as $table_name) {
+      $dataset[] = [$version, $table_name];
+    }
+
+    return $dataset;
+  }
+
+  /**
+   * Tests Compliance for a given table.
+   *
+   * The following is tested:
+   *   1. The table exists in the correct schema.
+   *   2. It has all the fields we expect.
+   *   3. Each field is the type we expect.
+   *   4. It has all the constraints we expect.
+   *   5. Each constraint consists of the columns we expect.
+   *
+   * @dataProvider chadoTableProvider
+   *
+   * @group api
+   * @group chado
+   * @group chado-compliance
+   */
+  public function testTableCompliance($schema_version, $table_name) {
+
+    // Create the ChadoSchema class to aid in testing.
+    $chado_schema = new \ChadoSchema();
+    $version = $chado_schema->getVersion();
+    $schema_name = $chado_schema->getSchemaName();
+
+    // Check #1: The table exists in the correct schema.
+    $this->assertTrue(
+      $chado_schema->checkTableExists($table_name),
+      t('"!table_name" should exist in the "!chado" schema v!version.',
+        array('!table_name' => $table_name, '!chado' => $schema_name, '!version' => $version))
+    );
+
+    // Retrieve the schema for this table.
+    $table_schema = $chado_schema->getTableSchema($table_name);
+
+    // For each column in this table...
+    foreach ($table_schema['fields'] as $column_name => $column_details) {
+
+      // Check #2: The given field exists in the table.
+      $this->assertTrue(
+        $chado_schema->checkColumnExists($table_name, $column_name),
+        t('The column "!column" must exist in "!table" for chado v!version.',
+          array('!column' => $column_name, '!table' => $table_name, '!version' => $version))
+      );
+
+      // Check #3: The field is the type we expect.
+      $this->assertTrue(
+        $chado_schema->checkColumnType($table_name, $column_name, $column_details['type']),
+        t('The column "!table.!column" must be of type "!type" for chado v!version.',
+          array('!column' => $column_name, '!table' => $table_name,
+            '!version' => $version, '!type' => $column_details['type']))
+      );
+    }
+
+    // There are three types of constraints:
+    // primary key, unique keys, and foreign keys.
+    //.......................................
+
+    // For the primary key:
+    // Check #4: The constraint exists.
+    if (isset($table_schema['primary key'][0]) AND !empty($table_schema['primary key'][0])) {
+      $pkey_column = $table_schema['primary key'][0];
+      $this->assertTrue(
+        $chado_schema->checkPrimaryKey($table_name, $pkey_column),
+        t('The column "!table.!column" must be a primary key with an associated sequence and constraint for chado v!version.',
+          array('!column' => $pkey_column, '!table' => $table_name, '!version' => $version))
+      );
+    }
+
+    // For each unique key:
+    foreach ($table_schema['unique keys'] as $constraint_name => $columns) {
+      // @debug print "Check '$constraint_name' for '$table_name': ".implode(', ', $columns).".\n";
+
+      // Check #4: The constraint exists.
+      $this->assertTrue(
+        $chado_schema->checkConstraintExists($table_name, $constraint_name, 'UNIQUE'),
+        t('The unique constraint "!name" for "!table" must exist for chado v!version.',
+          array('!name' => $constraint_name, '!table' => $table_name, '!version' => $version))
+      );
+
+      // Check #5: The constraint consists of the columns we expect.
+      // @todo
+    }
+
+    // For each foreign key:
+    foreach ($table_schema['foreign keys'] as $fk_table => $details) {
+      foreach ($details['columns'] as $base_column => $fk_column) {
+        // @debug print "Check '$table_name.$base_column =>  $fk_table.$fk_column ' foreign key.";
+
+        // Check #4: The constraint exists.
+        $constraint_name = $table_name . '_' . $base_column . '_fkey';
+        $this->assertTrue(
+          $chado_schema->checkFKConstraintExists($table_name, $base_column),
+          t('The foreign key constraint "!name" for "!table.!column" => "!fktable.!fkcolumn" must exist for chado v!version.',
+            array('!name' => $constraint_name,
+              '!table' => $table_name, '!column' => $base_column,
+              '!fktable' => $fk_table, '!fkcolumn' => $fk_column,
+              '!version' => $version))
+        );
+
+        // Check #5: The constraint consists of the columns we expect.
+        // @todo
+      }
+    }
+  }
+}

+ 313 - 0
tests/tripal_chado/api/ChadoSchemaTest.php

@@ -0,0 +1,313 @@
+<?php
+namespace Tests\tripal_chado\api;
+
+use StatonLab\TripalTestSuite\DBTransaction;
+use StatonLab\TripalTestSuite\TripalTestCase;
+use Faker\Factory;
+
+module_load_include('inc', 'tripal_chado', 'api/ChadoSchema');
+
+/**
+ * Tests the ChadoSchema class.
+ *
+ * @todo test "Check" functions in the ChadoSchema class.
+ */
+class ChadoSchemaTest extends TripalTestCase {
+  use DBTransaction;
+
+  /**
+   * Tests that the class can be initiated with or without a record specified
+   *
+   * @group api
+   * @group chado
+   * @group chado-schema
+   */
+  public function testInitClass() {
+
+    // Test with no parameters.
+    $chado_schema = new \ChadoSchema();
+    $this->assertNotNull($chado_schema);
+
+    // Test with version.
+    $chado_schema = new \ChadoSchema('1.3');
+    $this->assertNotNull($chado_schema);
+  }
+
+  /**
+   * Tests the ChadoSchema->getVersion() method.
+   *
+   * @group api
+   * @group chado
+   * @group chado-schema
+   */
+  public function testGetVersion() {
+
+    // Generate a fake version.
+    $faker = Factory::create();
+    $version = $faker->randomFloat(2, 1, 5);
+
+    // Check version can be retrieved when we set it.
+    $chado_schema = new \ChadoSchema($version);
+    $retrieved_version = $chado_schema->getVersion();
+    $this->assertEquals(
+      $version,
+      $retrieved_version,
+      t('The version retrieved via ChadoSchema->getVersion, "!ret", should equal that set, "!set"',
+        array('!ret' => $retrieved_version, '!set' => $version))
+    );
+
+    // @todo Check version can be retrieved when it's looked up?
+  }
+
+  /**
+   * Tests the ChadoSchema->getSchemaName() method.
+   *
+   * @group api
+   * @group chado
+   * @group chado-schema
+   */
+  public function testGetSchemaName() {
+
+    // Generate a fake version.
+    $faker = Factory::create();
+    $version = $faker->randomFloat(2, 1, 5);
+    $schema_name = $faker->word();
+
+    // Check the schema name can be retrieved when we set it.
+    $chado_schema = new \ChadoSchema($version, $schema_name);
+    $retrieved_schema = $chado_schema->getSchemaName();
+    $this->assertEquals(
+      $schema_name,
+      $retrieved_schema,
+      t('The schema name retrieved via ChadoSchema->getSchemaName, "!ret", should equal that set, "!set"',
+        array('!ret' => $retrieved_schema, '!set' => $schema_name))
+    );
+
+    // @todo Check schema name can be retrieved when it's looked up?
+  }
+
+  /**
+   * Tests ChadoSchema->getTableNames() method.
+   *
+   * @dataProvider knownTableProvider
+   *
+   * @group api
+   * @group chado
+   * @group chado-schema
+   */
+  public function testGetTableNames($version, $known_tables) {
+
+    // Check: Known tables for a given version are returned.
+    $chado_schema = new \ChadoSchema($version);
+    $returned_tables = $chado_schema->getTableNames();
+
+    foreach ($known_tables as $table_name) {
+      $this->assertArrayHasKey(
+        $table_name,
+        $returned_tables,
+        t('The table, "!known", should exist in the returned tables list for version !version.',
+          array(':known' => $table_name, ':version' => $version))
+      );
+    }
+  }
+
+  /**
+   * Tests ChadoSchema->getTableSchema() method.
+   *
+   * @dataProvider chadoTableProvider
+   *
+   * @group api
+   * @group chado
+   * @group chado-schema
+   */
+  public function testGetTableSchema($version, $table_name) {
+
+    // Check: a schema is returned that matches what we expect.
+    $chado_schema = new \ChadoSchema($version);
+
+    $table_schema = $chado_schema->getTableSchema($table_name);
+
+    $this->assertNotEmpty(
+      $table_schema,
+      t('Returned schema for "!table" in chado v!version should not be empty.',
+        array('!table' => $table_name, '!version' => $version))
+    );
+
+    $this->assertArrayHasKey(
+      'fields',
+      $table_schema,
+      t('The schema array for "!table" should have columns listed in an "fields" array',
+        array('!table' => $table_name))
+    );
+
+    // Instead of asserting these keys exist. Lets assert that if they do exist,
+    // they match the expected format.
+
+    if (isset($table_schema['primary key'])) {
+      $this->assertTrue(is_array($table_schema['primary key']),
+	t('The primary key of the Tripal Schema definition for "!table" must be an array.',
+          array('!table' => $table_name)));
+  
+    }
+
+    $this->assertArrayHasKey(
+      'foreign keys',
+      $table_schema,
+      t('The schema array for "!table" should have foreign keys listed in an "foreign keys" array',
+        array('!table' => $table_name))
+    );
+
+  }
+
+  /**
+   * Tests ChadoSchema->getCustomTableSchema() method.
+   *
+   * @dataProvider knownCustomTableProvider
+   *
+   * @group api
+   * @group chado
+   * @group chado-schema
+   */
+  public function testGetCustomTableSchema($table_name) {
+
+    // Check: a schema is returned that matches what we expect.
+    $chado_schema = new \ChadoSchema();
+    $table_schema = $chado_schema->getCustomTableSchema($table_name);
+
+    $this->assertNotEmpty(
+      $table_schema,
+      t('Returned schema for "!table" in chado v!version should not be empty.',
+        array('!table' => $table_name, '!version' => $version))
+    );
+
+    $this->assertArrayHasKey(
+      'fields',
+      $table_schema,
+      t('The schema array for "!table" should have columns listed in an "fields" array',
+        array('!table' => $table_name))
+    );
+
+    // NOTE: Other then ensuring fields are set, we can't test further since all other
+    // keys are technically optional and these arrays are set by admins.
+
+  }
+
+  /**
+   * Tests ChadoSchema->getBaseTables() method.
+   *
+   * @dataProvider knownBaseTableProvider
+   *
+   * @group api
+   * @group chado
+   * @group chado-schema
+   */
+  public function testGetBaseTables($version, $known_tables) {
+
+    // Check: Known base tables for a given version are returned.
+    $chado_schema = new \ChadoSchema($version);
+    $returned_tables = $chado_schema->getBaseTables();
+
+    foreach ($known_tables as $table_name) {
+
+      $found = false;
+
+      foreach ($returned_tables as $check_table ){
+
+        if ($check_table == $table_name){
+          $found = True;
+        }
+      }
+      $this->assertTrue($found, "{$table_name} was not returned by getBaseTables for Chado v {$version}");
+    }
+
+  }
+
+  /**
+   * Tests ChadoSchema->getCvtermMapping() method.
+   *
+   * @dataProvider chadoTableProvider
+   *
+   * @group api
+   * @group chado
+   * @group chado-schema
+   */
+ // public function testGetCvtermMapping($version, $table_name) {
+
+    //
+//    // Ideally we would create a new chado table + mapping and then test this pulls it out
+//    // since admin can re-map terms. However, that's more then I meant to bite off right
+//    // now...
+//
+//    // @todo Test that known terms match the tables we expect.
+//
+//    // @todo Test that a non-existent term throws an error.
+//
+//    // @todo Test that an fake unmapped term returns no mapping.
+ // }
+
+  /**
+   * Data Provider: returns known tables specific to a given chado version.
+   *
+   * @return array
+   */
+   public function knownTableProvider() {
+    // chado version, array of 3 tables specific to version.
+
+    return [
+      ['1.2', ['cell_line_relationship', 'cvprop', 'chadoprop']],
+      ['1.3', ['analysis_cvterm', 'dbprop', 'organism_pub']],
+    ];
+   }
+
+  /**
+   * Data Provider: returns known tables specific to a given chado version.
+   *
+   * @return array
+   */
+   public function knownBaseTableProvider() {
+    // chado version, array of 3 tables specific to version.
+
+    return [
+      ['1.2', ['organism', 'feature', 'stock', 'project','analysis', 'phylotree']],
+      ['1.3', ['organism', 'feature', 'stock', 'project','analysis', 'phylotree']],
+    ];
+   }
+
+  /**
+   * Data Provider: returns known custom tables specific to a given chado version.
+   *
+   * NOTE: These tables are provided by core Tripal so we should be able to
+   *  depend on them. Also, for the same reason, chado version doesn't matter.
+   *
+   * @return array
+   */
+   public function knownCustomTableProvider() {
+
+    return [
+      ['library_feature_count'],
+      ['organism_feature_count'],
+      ['tripal_gff_temp'],
+    ];
+   }
+
+  /**
+   * DataProvider, a list of all chado tables.
+   *
+   * @return array
+   */
+  public function chadoTableProvider() {
+
+    // Provide the table list for all versions.
+    $dataset = [];
+    foreach (array('1.11','1.2','1.3') as $version) {
+      $chado_schema = new \ChadoSchema();
+      $version = $chado_schema->getVersion();
+
+      foreach ($chado_schema->getTableNames() as $table_name) {
+        $dataset[] = [$version, $table_name];
+      }
+    }
+
+    return $dataset;
+  }
+}

+ 93 - 93
tripal/api/tripal.entities.api.inc

@@ -3,8 +3,8 @@
 /**
  * @file
  * Provides an application programming interface (API) for working with
- * TripalEntity content types (bundles) and their entities. 
- * 
+ * TripalEntity content types (bundles) and their entities.
+ *
  */
 
 /**
@@ -13,32 +13,32 @@
  * @{
  * Provides an application programming interface (API) for working with
  * TripalEntity content types (bundles) and their entities.
- * 
- * Bundles (Content Types): Bundles are types of content in a Drupal site.  
- * By default, Drupal provides the Basic Page and Article content types, 
- * and Drupal allows a site developer to create new content types on-the-fly 
- * using the administrative interface--no programming required.  Tripal also 
- * provides several Content Type by default. During installation of Tripal the 
- * Organism, Gene, Project, Analysis and other content types are created 
- * automatically.  The site developer can then create new content types for 
+ *
+ * Bundles (Content Types): Bundles are types of content in a Drupal site.
+ * By default, Drupal provides the Basic Page and Article content types,
+ * and Drupal allows a site developer to create new content types on-the-fly
+ * using the administrative interface--no programming required.  Tripal also
+ * provides several Content Type by default. During installation of Tripal the
+ * Organism, Gene, Project, Analysis and other content types are created
+ * automatically.  The site developer can then create new content types for
  * different biological data--again, without any programming required.
- * 
- * In order to to assist with data exchange and use of common data formats, 
- * Tripal Bundles are defined using a controlled vocabulary term (cvterm). 
- * For example, a "Gene" Bundle is defined using the Sequence Ontology term for 
- * gene whose term accession is: SO:0000704. This mapping allows Tripal to 
- * compare content across Tripal sites, and expose data to computational tools 
- * that understand these vocabularies. By default, Tripal uses Chado as its 
- * primary data storage back-end.  
- * 
+ *
+ * In order to to assist with data exchange and use of common data formats,
+ * Tripal Bundles are defined using a controlled vocabulary term (cvterm).
+ * For example, a "Gene" Bundle is defined using the Sequence Ontology term for
+ * gene whose term accession is: SO:0000704. This mapping allows Tripal to
+ * compare content across Tripal sites, and expose data to computational tools
+ * that understand these vocabularies. By default, Tripal uses Chado as its
+ * primary data storage back-end.
+ *
  * Entity: An entity is a discrete data record.  Entities are most commonly
- * seen as "pages" on a Drupal web site and are instances of a Bundle 
- * (i.e content type). When data is published on a Tripal site such as 
- * organisms, genes, germplasm, maps, etc., each record is represented by a 
- * single entity with an entity ID as its only attribute. All other 
- * information that the entity provides is made available via Fields.  
- * 
- * For more information please see: 
+ * seen as "pages" on a Drupal web site and are instances of a Bundle
+ * (i.e content type). When data is published on a Tripal site such as
+ * organisms, genes, germplasm, maps, etc., each record is represented by a
+ * single entity with an entity ID as its only attribute. All other
+ * information that the entity provides is made available via Fields.
+ *
+ * For more information please see:
  * http://tripal.info/tutorials/v3.x/developers-handbook/structure
  * @}
  *
@@ -117,15 +117,15 @@ function hook_bundle_delete($bundle) {
  * Implement this hook to define default formats for Tripal Content Types.
  *
  * @param TripalBundle $bundle
- *   A tripal content type entity with information to be used for determining 
+ *   A tripal content type entity with information to be used for determining
  *   the default title format.
  * @param array $available_tokens
  *   An array of available tokens for this particular tripal content type.
  *
  * @return array
- *   An array of potential formats. The lightest weighted format suggested by 
+ *   An array of potential formats. The lightest weighted format suggested by
  *   all modules will be chosen.
- *   Each array item should consist of a 'weight' and 'format'. See the hook 
+ *   Each array item should consist of a 'weight' and 'format'. See the hook
  *   implementation below for examples.
  *    - weight: an integer used to determine priority of suggestions.
  *        The smaller/lighter the number the higher the priority.
@@ -148,10 +148,10 @@ function hook_tripal_default_title_format($bundle, $available_tokens) {
 
   // If it's the term you are interested in then suggest a format.
   if ($term->name == 'organism') {
-    // To suggest a format, add an element to the array with a format & weight 
+    // To suggest a format, add an element to the array with a format & weight
     // key.
     $format[] = array(
-      // This is the format/pattern you suggest be used to determine the title 
+      // This is the format/pattern you suggest be used to determine the title
       // of organism pages.
       'format' => '[organism__genus] [organism__species]',
       // The weight/priority of your suggestion.
@@ -237,12 +237,12 @@ function tripal_load_entity($entity_type, $ids = FALSE, $reset = FALSE,
  * Retrieves a TripalTerm entity that matches the given arguments.
  *
  * @param $values
- *   An associative array used to match a term.  
- *   Valid keys may be: 
- *        - vocabulary: Must always be used with accession to uniquely 
- *                        identify a term.  
- *        - accession: Must always be used with vocabulary to uniquely 
- *                       identify a term. 
+ *   An associative array used to match a term.
+ *   Valid keys may be:
+ *        - vocabulary: Must always be used with accession to uniquely
+ *                        identify a term.
+ *        - accession: Must always be used with vocabulary to uniquely
+ *                       identify a term.
  *        - term_id: Can be used alone to uniquely identify a term.
  *
  * @return
@@ -284,7 +284,7 @@ function tripal_load_term_entity($values) {
  * Retrieves a TripalVocab entity that maches the given arguments.
  *
  * @param $values
- *   An associative array used to match a vocabulary.  
+ *   An associative array used to match a vocabulary.
  *   The valid keys are:
  *      - vocab_id: integer id of the vocabulary.
  *      - vocabulary: string name of vocabulary.
@@ -356,7 +356,7 @@ function tripal_load_bundle_entity($values) {
       return NULL;
     }
     $query->condition('tb.term_id', $term->id);
-    
+
   }
   $bundle = $query->execute()->fetchObject();
 
@@ -375,15 +375,15 @@ function tripal_load_bundle_entity($values) {
  * @param $details
  *   A human-readable sentence or two describing the issue.
  * @param $type
- *   A one word type indicating the type of notification. Tripal types include: 
+ *   A one word type indicating the type of notification. Tripal types include:
  *   Jobs, Fields.
  *   If no type is required please pass NULL.
  * @param $actions
- *   A serialized PHP associative array containing the link and URL for each 
+ *   A serialized PHP associative array containing the link and URL for each
  *   action.
  *   If not type is required please pass NULL.
  * @param $submitter_id
- *   A unique ID provided by the submitter for checking to make sure that the 
+ *   A unique ID provided by the submitter for checking to make sure that the
  *   notification is not added more than once.
  *
  * @ingroup tripal_entities_api
@@ -396,7 +396,7 @@ function tripal_add_notification($title, $details, $type, $actions, $submitter_i
       ->fields('tan')
       ->condition('submitter_id', $submitter_id, '=')
       ->execute()->fetchAll();
-  
+
     if (empty($dedup)) {
       $record = new stdClass;
       $record->details = $details;
@@ -439,17 +439,17 @@ function tripal_create_bundle($args, $job = NULL) {
   $accession = $args['accession'];
   $term_name = $args['term_name'];
   $storage_args = $args['storage_args'];
-  
+
   $message_args = [
     'job' => $job,
     'print' => TRUE,
     'watchdog' => TRUE,
   ];
-  
-//   tripal_report_error('tripal_entities', TRIPAL_INFO, 
+
+//   tripal_report_error('tripal_entities', TRIPAL_INFO,
 //    "Creation of a content type is performed using a database transaction. " .
 //     "If it fails or is terminated prematurely then all insertions and " .
-//     "updates are rolled back and will not be found in the database", 
+//     "updates are rolled back and will not be found in the database",
 //     [], $message_args);
 
   $transaction = db_transaction();
@@ -463,7 +463,7 @@ function tripal_create_bundle($args, $job = NULL) {
       }
       else {
         $transaction->rollback();
-        tripal_report_error('tripal_entities', TRIPAL_ERROR, 
+        tripal_report_error('tripal_entities', TRIPAL_ERROR,
           'Unable to create TripalVocab :vocab', array(':vocab' => $vocabulary), $message_args);
         return FALSE;
       }
@@ -540,17 +540,17 @@ function tripal_create_bundle($args, $job = NULL) {
         'Unable to load Tripal Bundle :name after cache clear.', array(':name' => $bundle_name), $message_args);
       return FALSE;
     }
-    
+
     // Set the bundle category
     $category = array_key_exists('bundle_category', $args) ? $args['bundle_category'] : 'Other';
-    tripal_set_bundle_variable($variable_name, $bundle->id, $category);
+    tripal_set_bundle_variable('bundle_category', $bundle->id, $category);
 
     // Attache the bundle fields.
     tripal_create_bundle_fields($bundle, $term);
 
     // Specifically commiting here since we have a fully featured bundle.
-    // Post-create hook implementations assume we have a 
-    // created bundle so we don't want to rollback if a 
+    // Post-create hook implementations assume we have a
+    // created bundle so we don't want to rollback if a
     // custom implementation causes an exception.
     unset($transaction);
 
@@ -577,10 +577,10 @@ function tripal_create_bundle($args, $job = NULL) {
 
   // Set admin access for the new bundle.
   tripal_admin_access($bundle);
-  
+
   // Report that we're done.
   tripal_report_error('tripal_entities', TRIPAL_INFO, "Done.", [], $message_args);
-  
+
   return $bundle;
 }
 
@@ -601,7 +601,7 @@ function tripal_get_content_types() {
 }
 
 /**
- * Refreshes the bundle such that new fields added by modules will be found 
+ * Refreshes the bundle such that new fields added by modules will be found
  * during cron.
  *
  * @param $bundle_name
@@ -766,13 +766,13 @@ function tripal_create_bundle_fields($bundle, $term) {
   // Allow modules to alter which fields should be attached to content
   // types they create.
   drupal_alter('bundle_instances_info', $instance_info, $bundle, $term);
-  
+
   // Get the list of existing instances
   $existing_instances = field_info_instances('TripalEntity', $bundle->name);
-  
+
   // Iterate through all of the field instances and create them.
   foreach ($instance_info as $instance_name => $details) {
-    
+
     // Make sure the instance has a term. If not, report it and skip the field.
     if (!array_key_exists('term_vocabulary', $details['settings'])) {
       tripal_report_error('tripal_fields', TRIPAL_WARNING,
@@ -785,13 +785,13 @@ function tripal_create_bundle_fields($bundle, $term) {
         'The field instance, !field, is missing the "term_accession" setting. The field instance cannot be added. Please check the field settings.',
         ['!field' => $instance_name], ['drupal_set_message' => TRUE]);
         continue;}
-    
-    // Make sure the term exists. If not, skip the field instance and 
+
+    // Make sure the term exists. If not, skip the field instance and
     // report an error.
-    $field_term_id = $details['settings']['term_vocabulary'] . ':' . $details['settings']['term_accession'];    
+    $field_term_id = $details['settings']['term_vocabulary'] . ':' . $details['settings']['term_accession'];
     $field_term = tripal_get_term_details($details['settings']['term_vocabulary'], $details['settings']['term_accession']);
     if (!$field_term) {
-      tripal_report_error('tripal_fields', TRIPAL_WARNING, 
+      tripal_report_error('tripal_fields', TRIPAL_WARNING,
         'The term, !term, for the field, !field, does not exist in the database. The  ' .
         'field instance cannot be added. Please make sure the term is correct and add it if necessary.',
         ['!term' => $field_term_id,
@@ -799,11 +799,11 @@ function tripal_create_bundle_fields($bundle, $term) {
         ['drupal_set_message' => TRUE]);
       continue;
     }
-    
+
     // Make sure the term is not used for any other existing field instance.
     $skip = FALSE;
     foreach ($existing_instances as $existing_name => $existing_instance) {
-      // If this instance term is the same as this exsiting term and the 
+      // If this instance term is the same as this exsiting term and the
       // instance name is not the same then we have a problem.
       $existing_term_id = $existing_instance['settings']['term_vocabulary'] . ':' . $existing_instance['settings']['term_accession'];
       $existing_field = field_info_field($existing_name);
@@ -816,7 +816,7 @@ function tripal_create_bundle_fields($bundle, $term) {
           ['drupal_set_message' => TRUE]);
         $skip = TRUE;
       }
-      
+
       // If the instance term is the same as this exsting term but the storage
       // types are different then we have a problem.
       $existing_storage = $existing_field['storage']['type'];
@@ -1022,7 +1022,7 @@ function tripal_set_bundle_variable($variable_name, $bundle_id, $value) {
     }
   }
 
-  // And then we need to write the new format to the tripal_bundle_variables 
+  // And then we need to write the new format to the tripal_bundle_variables
   // table.
   $record = array(
     'bundle_id' => $bundle_id,
@@ -1128,7 +1128,7 @@ function tripal_get_default_title_format($bundle) {
   if (!$format) {
     $tmp = array();
 
-    // Check which tokens are required fields and join them into a default 
+    // Check which tokens are required fields and join them into a default
     // format.
     foreach($tokens as $token) {
       if ($token['required']) {
@@ -1182,13 +1182,13 @@ function tripal_get_entity_tokens($bundle, $options = array()) {
 
   $instances = field_info_instances('TripalEntity', $bundle->name);
   foreach ($instances as $instance_name => $instance) {
-    
+
     if (!$instance['required'] and $options['required only']) {
       continue;
     }
-    
+
     $use_field = FALSE;
-   
+
     // Iterate through the TripalEntity fields and see if they have
     // sub-elements, if so, add those as tokens too.
     $field_name = $instance['field_name'];
@@ -1198,8 +1198,8 @@ function tripal_get_entity_tokens($bundle, $options = array()) {
         $field_obj = new $field_name($field, $instance);
         $element_info = $field_obj->elementInfo();
         $term_id = $instance['settings']['term_vocabulary'] . ':' . $instance['settings']['term_accession'];
-        if ($element_info and 
-            array_key_exists($term_id, $element_info) and 
+        if ($element_info and
+            array_key_exists($term_id, $element_info) and
             array_key_exists('elements', $element_info[$term_id]) and count($element_info[$term_id]['elements']) > 0) {
           $elements = $element_info[$term_id]['elements'];
           _tripal_get_entity_tokens_for_elements($instance, $field_name, $elements, $tokens, $options);
@@ -1215,7 +1215,7 @@ function tripal_get_entity_tokens($bundle, $options = array()) {
     else {
       $use_field = TRUE;
     }
-    
+
     // If we have no elements to add then just add the field as is.
     if ($use_field) {
       // Build the token from the field information.
@@ -1235,11 +1235,11 @@ function tripal_get_entity_tokens($bundle, $options = array()) {
 
 /**
  * A recursive helper function to get tokens for element sub fields.
- * 
+ *
  * @param $instance
  *   A original field instance object.
  * @param $parent
- *   The name of the parent. The first time this is called outside of 
+ *   The name of the parent. The first time this is called outside of
  *   recursion this should be the field name.
  * @param $elements
  *   The array of elements to process.
@@ -1247,21 +1247,21 @@ function tripal_get_entity_tokens($bundle, $options = array()) {
  *   The array of tokens to be added to.
  */
 function _tripal_get_entity_tokens_for_elements($instance, $parent, $elements, &$tokens, $options) {
-  
+
   // Iterate through all of the elements and add tokens for each one.
   foreach ($elements as $child_term_id => $details) {
-    
+
     // We don't need to add the entity element.
     if ($child_term_id == 'entity') {
       continue;
     }
-    
+
     // Skip elements that aren't required.
     $required = array_key_exists('required', $details) ? $details['required'] : FALSE;
     if (!$required and $options['required only']) {
       continue;
     }
-    $token = '[' . $parent . ',' . $child_term_id . ']'; 
+    $token = '[' . $parent . ',' . $child_term_id . ']';
     $label = $child_term_id;
     if (array_key_exists('name', $details)) {
       $label = $details['name'];
@@ -1280,10 +1280,10 @@ function _tripal_get_entity_tokens_for_elements($instance, $parent, $elements, &
       'field_name' => $instance['field_name'],
       'required' => $required
     ];
-    
+
     // Recurse to include sub elements
     if (array_key_exists('elements', $details)) {
-      _tripal_get_entity_tokens_for_elements($instance, $parent . ',' . $child_term_id, 
+      _tripal_get_entity_tokens_for_elements($instance, $parent . ',' . $child_term_id,
         $details['elements'], $tokens, $options);
     }
   }
@@ -1298,7 +1298,7 @@ function _tripal_get_entity_tokens_for_elements($instance, $parent, $elements, &
  * @param TripalEntity $entity
  *   The entity with field values used to find values of tokens.
  * @param TripalBundle $bundle_entity
- *   The bundle enitity containing special values sometimes needed for token 
+ *   The bundle enitity containing special values sometimes needed for token
  *   replacement.
  *
  * @return
@@ -1352,20 +1352,20 @@ function tripal_replace_entity_tokens($string, &$entity, $bundle_entity = NULL)
     module_invoke($storage['module'], 'field_storage_load', 'TripalEntity',
         $entities, FIELD_LOAD_CURRENT, $field_ids, array());
   }
-  
+
   // Now that all necessary fields are attached process the tokens.
   foreach($used_tokens as $token) {
     $token = preg_replace('/[\[\]]/', '', $token);
     $elements = explode(',', $token);
     $field_name = array_shift($elements);
     $value = '';
-    
+
     if (property_exists($entity, $field_name)) {
       $value = '';
       // Note: there is a memory leak in field_get_items() so we can't use it
       // here or bulk publishing will slowly erode memory.
       // $field_value = field_get_items('TripalEntity', $entity, $field_name);
-      if (array_key_exists('und', $entity->{$field_name}) and 
+      if (array_key_exists('und', $entity->{$field_name}) and
           array_key_exists(0, $entity->{$field_name}['und'])) {
         $value = $entity->{$field_name}['und'][0]['value'];
         // If the value is an array it means we have sub elements and we can
@@ -1373,7 +1373,7 @@ function tripal_replace_entity_tokens($string, &$entity, $bundle_entity = NULL)
         if (is_array($value) and count($elements) > 0) {
           $value = _tripal_replace_entity_tokens_for_elements($elements, $value);
         }
-      }    
+      }
     }
     // The TripalBundle__bundle_id is a special token for substituting the
     // bundle id.
@@ -1406,7 +1406,7 @@ function tripal_replace_entity_tokens($string, &$entity, $bundle_entity = NULL)
 
 /**
  * A helper function for tripal_replace_entity_tokens to get token values.
- * 
+ *
  * This helper function is used when the tokens are from subelements.
  * @param $entity
  */
@@ -1595,15 +1595,15 @@ function _tripal_get_bundle_field_element_details($elements, &$field_details) {
 
 /**
  * Is this completed? It doesn't look right and I can't find it used anywhere
- * in the existing code. 
- * 
+ * in the existing code.
+ *
  * @param $bundle_name
  *   The name of the bundle (e.g. bio_data_xx)
  * @param unknown $values
  *
  * @throws Exception
  *
- * 
+ *
  */
 function tripal_insert_entity($bundle_name, $values){
   global $user;
@@ -1654,12 +1654,12 @@ function tripal_insert_entity($bundle_name, $values){
 }
 
 /**
- * Are we keeping this? 
- * 
+ * Are we keeping this?
+ *
  * @param $bundle_name
  * @param $values
  *
- * 
+ *
  */
 function tripal_update_entity($bundle_name, $values) {
 

+ 5 - 1
tripal/includes/TripalFieldDownloaders/TripalCSVDownloader.inc

@@ -54,7 +54,7 @@ class TripalCSVDownloader extends TripalFieldDownloader {
       $field_name = $field['field_name'];
 
       // If we only have one item for this value then add it.
-      if (is_array($entity->{$field_name}['und']) and count($entity->{$field_name}['und']) == 1) {
+      if ($field_name and is_array($entity->{$field_name}['und']) and count($entity->{$field_name}['und']) == 1) {
         $value = $entity->{$field_name}['und'][0]['value'];
 
         // If the single element is not an array then this is good.
@@ -80,6 +80,10 @@ class TripalCSVDownloader extends TripalFieldDownloader {
           // TODO: What to do with fields that are arrays?
         }
       }
+      // Report the misconfigured field
+      elseif(!$field_name) {
+        tripal_report_error('tripal', TRIPAL_ERROR, 'Unable to find field name for field id: '.$field_id);
+      }
       // If we have multiple items then deal with that.
       else {
         $col[] = '';

+ 5 - 1
tripal/includes/TripalFieldDownloaders/TripalTabDownloader.inc

@@ -54,7 +54,7 @@ class TripalTabDownloader extends TripalFieldDownloader {
        $field_name = $field['field_name'];
 
        // If we only have one item for this value then add it.
-       if (is_array($entity->{$field_name}['und']) and count($entity->{$field_name}['und']) == 1) {
+       if ($field_name and is_array($entity->{$field_name}['und']) and count($entity->{$field_name}['und']) == 1) {
          $value = $entity->{$field_name}['und'][0]['value'];
 
          // If the single element is not an array then this is good.
@@ -76,6 +76,10 @@ class TripalTabDownloader extends TripalFieldDownloader {
            // TODO: What to do with fields that are arrays?
          }
        }
+       // Report the misconfigured field
+       elseif(!$field_name) {
+         tripal_report_error('tripal', TRIPAL_ERROR, 'Unable to find field name for field id: '.$field_id);
+       }
        // If we have multiple items then deal with that.
        else {
          $col[] = '';

+ 629 - 0
tripal_chado/api/ChadoSchema.inc

@@ -0,0 +1,629 @@
+<?php
+/**
+ * Provides an application programming interface (API) for describing Chado tables.
+ *
+ * If you need the Drupal-style array definition for any table, use the following:
+ * @code
+
+    $chado_schema = new \ChadoSchema();
+    $table_schema = $chado_schema->getTableSchema($table_name);
+
+ * @endcode
+ *
+ * where the variable $table contains the name of the table you want to
+ * retireve.  The getTableSchema method determines the appropriate version of
+ * Chado and uses the Drupal hook infrastructure to call the appropriate
+ * hook function to retrieve the table schema.
+ *
+ * Additionally, here are some other examples of how to use this class:
+ * @code
+
+    // Retrieve the schema array for the organism table in chado 1.2
+    $chado_schema = new \ChadoSchema('1.2');
+    $table_schema = $chado_schema->getTableSchema('organism');
+
+    // Retrieve all chado tables.
+    $chado_schema = new \ChadoSchema();
+    $tables = $chado_schema->getTableNames();
+    $base_tables = $chado_schema->getbaseTables();
+
+    // Check the feature.type_id foreign key constraint
+    $chado_schema = new \ChadoSchema();
+    $exists = $chado_schema ->checkFKConstraintExists('feature','type_id');
+
+    // Check Sequence exists
+    $chado_schema = new \ChadoSchema();
+    $exists = $chado_schema->checkSequenceExists('organism','organism_id');
+    // Or just check the primary key directly
+    $compliant = $chado_schema->checkPrimaryKey('organism');
+
+ * @endcode
+ */
+class ChadoSchema {
+
+  /**
+   * @var string
+   *   The current version for this site. E.g. "1.3".
+   */
+  protected $version = '';
+
+  /**
+   * @var string
+   *   The name of the schema chado was installed in.
+   */
+  protected $schema_name = 'chado';
+
+  /**
+   * The ChadoSchema constructor.
+   *
+   * @param string $version
+   *   The current version for this site. E.g. "1.3". If a version is not provided, the
+   *   version of the current database will be looked up.
+   */
+  public function __construct($version = NULL, $schema_name = NULL) {
+
+    // Set the version of the schema.
+    if ($version === NULL) {
+      $this->version = chado_get_version(TRUE);
+    }
+    else {
+      $this->version = $version;
+    }
+
+    // Set the name of the schema.
+    if ($schema_name === NULL) {
+      $this->schema_name = chado_get_schema_name('chado');
+    }
+    else {
+      $this->schema_name = $schema_name;
+    }
+
+    // Check functions require the chado schema be local and installed...
+    // So lets check that now...
+    if (!chado_is_local()) {
+      tripal_report_error(
+        'ChadoSchema',
+        TRIPAL_NOTICE,
+        'The ChadoSchema class requires chado be installed within the drupal database
+          in a separate schema for any compliance checking functionality.'
+      );
+    }
+    if (!chado_is_installed()) {
+      tripal_report_error(
+        'ChadoSchema',
+        TRIPAL_NOTICE,
+        'The ChadoSchema class requires chado be installed
+          for any compliance checking functionality.'
+      );
+    }
+  }
+
+  /**
+   * Returns the version number of the Chado this object references.
+   *
+   * @returns
+   *   The version of Chado
+   */
+  public function getVersion() {
+    return $this->version;
+  }
+
+  /**
+   * Retrieve the name of the PostgreSQL schema housing Chado.
+   *
+   * @return
+   *   The name of the schema.
+   */
+  public function getSchemaName() {
+    return $this->schema_name;
+  }
+
+  /**
+   * Retrieves the list of tables in the Chado schema.  By default it only returns
+   * the default Chado tables, but can return custom tables added to the
+   * Chado schema if requested.
+   *
+   * @param $include_custom
+   *   Optional.  Set as TRUE to include any custom tables created in the
+   *   Chado schema. Custom tables are added to Chado using the
+   *   tripal_chado_chado_create_table() function.
+   *
+   * @returns
+   *   An associative array where the key and value pairs are the Chado table names.
+   */
+  public function getTableNames($include_custom = FALSE) {
+
+    $tables = array();
+    if ($this->version == '1.3') {
+      $tables_v1_3 = tripal_chado_chado_get_v1_3_tables();
+      foreach ($tables_v1_3 as $table) {
+        $tables[$table] = $table;
+      }
+    }
+    if ($this->version == '1.2') {
+      $tables_v1_2 = tripal_chado_chado_get_v1_2_tables();
+      foreach ($tables_v1_2 as $table) {
+        $tables[$table] = $table;
+      }
+    }
+    if ($this->version == '1.11' or $this->version == '1.11 or older') {
+      $tables_v1_11 = tripal_chado_chado_get_v1_11_tables();
+      foreach ($tables_v1_11 as $table) {
+        $tables[$table] = $table;
+      }
+    }
+
+    // now add in the custom tables too if requested
+    if ($include_custom) {
+      $sql = "SELECT table FROM {tripal_custom_tables}";
+      $resource = db_query($sql);
+
+      foreach ($resource as $r) {
+        $tables[$r->table] = $r->table;
+      }
+    }
+
+    asort($tables);
+    return $tables;
+
+  }
+
+  /**
+   * Retrieves the chado tables Schema API array.
+   *
+   * @param $table
+   *   The name of the table to retrieve.  The function will use the appopriate
+   *   Tripal chado schema API hooks (e.g. v1.11 or v1.2).
+   *
+   * @returns
+   *   A Drupal Schema API array defining the table.
+   */
+  public function getTableSchema($table) {
+
+    // first get the chado version.
+    $v = $this->version;
+
+    // get the table array from the proper chado schema
+    $v = preg_replace("/\./", "_", $v); // reformat version for hook name
+
+    // Call the module_invoke_all.
+    $hook_name = "chado_schema_v" . $v . "_" . $table;
+    $table_arr = module_invoke_all($hook_name);
+
+    // If the module_invoke_all returned nothing then let's make sure there isn't
+    // An API call we can call directly.  The only time this occurs is
+    // during an upgrade of a major Drupal version and tripal_core is disabled.
+    if ((!$table_arr or !is_array($table_arr)) and
+          function_exists('tripal_chado_' . $hook_name)) {
+      $api_hook = "tripal_chado_" . $hook_name;
+      $table_arr = $api_hook();
+    }
+
+    // if the table_arr is empty then maybe this is a custom table
+    if (!is_array($table_arr) or count($table_arr) == 0) {
+      $table_arr = $this->getCustomTableSchema($table);
+    }
+
+    return $table_arr;
+
+  }
+
+  /**
+   * Retrieves the schema array for the specified custom table.
+   *
+   * @param $table
+   *   The name of the table to create.
+   *
+   * @return
+   *   A Drupal-style Schema API array definition of the table. Returns
+   *   FALSE on failure.
+   */
+  public function getCustomTableSchema($table) {
+
+    $sql = "SELECT schema FROM {tripal_custom_tables} WHERE table_name = :table_name";
+    $results = db_query($sql, array(':table_name' => $table));
+    $custom = $results->fetchObject();
+    if (!$custom) {
+      return FALSE;
+    }
+    else {
+      return unserialize($custom->schema);
+    }
+  }
+
+  /**
+   *  Returns all chado base tables.
+   *
+   *  Base tables are those that contain the primary record for a data type. For
+   *  example, feature, organism, stock, are all base tables.  Other tables
+   *  include linker tables (which link two or more base tables), property tables,
+   *  and relationship tables.  These provide additional information about
+   *  primary data records and are therefore not base tables.  This function
+   *  retreives only the list of tables that are considered 'base' tables.
+   *
+   *  @return
+   *    An array of base table names.
+   *
+   *  @ingroup tripal_chado_schema_api
+   */
+  function getBaseTables() {
+
+    // Initialize the base tables with those tables that are missing a type.
+    // Ideally they should have a type, but that's for a future version of Chado.
+    $base_tables = array('organism', 'project', 'analysis', 'biomaterial',
+      'eimage', 'assay');
+
+    // We'll use the cvterm table to guide which tables are base tables. Typically
+    // base tables (with a few exceptions) all have a type.  Iterate through the
+    // referring tables.
+    $schema = $this->getTableSchema('cvterm');
+    $referring = $schema['referring_tables'];
+    foreach ($referring as $tablename) {
+
+      // Ignore the cvterm tables, relationships, chadoprop tables.
+      if ($tablename == 'cvterm_dbxref' || $tablename == 'cvterm_relationship' ||
+          $tablename == 'cvtermpath' || $tablename == 'cvtermprop' || $tablename == 'chadoprop' ||
+          $tablename == 'cvtermsynonym' || preg_match('/_relationship$/', $tablename) ||
+          preg_match('/_cvterm$/', $tablename) ||
+          // Ignore prop tables
+          preg_match('/prop$/', $tablename) || preg_match('/prop_.+$/', $tablename) ||
+          // Ignore nd_tables
+          preg_match('/^nd_/', $tablename)) {
+        continue;
+      }
+      else {
+        array_push($base_tables, $tablename);
+      }
+    }
+
+    // Remove any linker tables that have snuck in.  Linker tables are those
+    // whose foreign key constraints link to two or more base table.
+    $final_list = array();
+    foreach ($base_tables as $i => $tablename) {
+      // A few tables break our rule and seems to look
+      // like a linking table, but we want to keep it as a base table.
+      if ($tablename == 'biomaterial' or $tablename == 'assay' or $tablename == 'arraydesign') {
+        $final_list[] = $tablename;
+        continue;
+      }
+
+      // Remove the phenotype table. It really shouldn't be a base table as
+      // it is meant to store individual phenotype measurements.
+      if ($tablename == 'phenotype') {
+        continue;
+      }
+      $num_links = 0;
+      $schema = $this->getTableSchema($tablename);
+      $fkeys = $schema['foreign keys'];
+      foreach ($fkeys as $fkid => $details) {
+        $fktable = $details['table'];
+        if (in_array($fktable, $base_tables)) {
+          $num_links++;
+        }
+      }
+      if ($num_links < 2) {
+        $final_list[] = $tablename;
+      }
+    }
+
+    // Now add in the cvterm table to the list.
+    $final_list[] = 'cvterm';
+
+    // Sort the tables and return the list.
+    sort($final_list);
+    return $final_list;
+
+  }
+
+  /**
+   * Get information about which Chado base table a cvterm is mapped to.
+   *
+   * Vocbulary terms that represent content types in Tripal must be mapped to
+   * Chado tables.  A cvterm can only be mapped to one base table in Chado.
+   * This function will return an object that contains the chado table and
+   * foreign key field to which the cvterm is mapped.  The 'chado_table' property
+   * of the returned object contains the name of the table, and the 'chado_field'
+   * property contains the name of the foreign key field (e.g. type_id), and the
+   * 'cvterm' property contains a cvterm object.
+   *
+   * @params
+   *   An associative array that contains the following keys:
+   *     - cvterm_id:  the cvterm ID value for the term.
+   *     - vocabulary: the short name for the vocabulary (e.g. SO, GO, PATO)
+   *     - accession:  the accession for the term.
+   *     - bundle_id:  the ID for the bundle to which a term is associated.
+   *   The 'vocabulary' and 'accession' must be used together, the 'cvterm_id' can
+   *   be used on it's own.
+   * @return
+   *   An object containing the chado_table and chado_field properties or NULL if
+   *   if no mapping was found for the term.
+   */
+  public function getCvtermMapping($params) {
+    return chado_get_cvterm_mapping($params);
+  }
+
+  /**
+   * Check that any given Chado table exists.
+   *
+   * This function is necessary because Drupal's db_table_exists() function will
+   * not look in any other schema but the one where Drupal is installed
+   *
+   * @param $table
+   *   The name of the chado table whose existence should be checked.
+   *
+   * @return
+   *   TRUE if the table exists in the chado schema and FALSE if it does not.
+   */
+  public function checkTableExists($table) {
+    return chado_table_exists($table);
+  }
+
+  /**
+   * Check that any given column in a Chado table exists.
+   *
+   * This function is necessary because Drupal's db_field_exists() will not
+   * look in any other schema but the one were Drupal is installed
+   *
+   * @param $table
+   *   The name of the chado table.
+   * @param $column
+   *   The name of the column in the chado table.
+   *
+   * @return
+   *   TRUE if the column exists for the table in the chado schema and
+   *   FALSE if it does not.
+   *
+   * @ingroup tripal_chado_schema_api
+   */
+  public function checkColumnExists($table, $column) {
+    return chado_column_exists($table, $column);
+  }
+
+  /**
+   * Check that any given column in a Chado table exists.
+   *
+   * This function is necessary because Drupal's db_field_exists() will not
+   * look in any other schema but the one were Drupal is installed
+   *
+   * @param $table
+   *   The name of the chado table.
+   * @param $column
+   *   The name of the column in the chado table.
+   * @param $type
+   *   (OPTIONAL) The PostgreSQL type to check for. If not supplied it will be
+   *   looked up via the schema (PREFERRED).
+   *
+   * @return
+   *   TRUE if the column type matches what we expect and
+   *   FALSE if it does not.
+   *
+   * @ingroup tripal_chado_schema_api
+   */
+  public function checkColumnType($table, $column, $expected_type = NULL) {
+
+    // Ensure this column exists before moving forward.
+    if (!$this->checkColumnExists($table, $column)) {
+      tripal_report_error(
+        'ChadoSchema',
+        TRIPAL_WARNING,
+        'Unable to check the type of !table!column since it doesn\'t appear to exist in your site database.',
+        array('!column' => $column, '!table' => $table)
+      );
+      return FALSE;
+    }
+
+    // Look up the type using the Schema array.
+    if ($expected_type === NULL) {
+      $schema = $this->getTableSchema($table, $column);
+
+      if (is_array($schema) AND isset($schema['fields'][$column])) {
+        $expected_type = $schema['fields'][$column]['type'];
+      }
+      else {
+        tripal_report_error(
+          'ChadoSchema',
+          TRIPAL_WARNING,
+          'Unable to check the type of !table!column due to being unable to find the schema definition.',
+          array('!column' => $column, '!table' => $table)
+        );
+        return FALSE;
+      }
+    }
+
+    // There is some flexibility in the expected type...
+    // Fix that here.
+    switch ($expected_type) {
+      case 'int':
+        $expected_type = 'integer';
+        break;
+      case 'serial':
+        $expected_type = 'integer';
+        break;
+      case 'varchar':
+        $expected_type = 'character varying';
+        break;
+      case 'datetime':
+        $expected_type = 'timestamp without time zone';
+        break;
+      case 'char':
+        $expected_type = 'character';
+        break;
+    }
+
+    // Grab the type from the current database.
+    $query = 'SELECT data_type
+              FROM information_schema.columns
+              WHERE
+                table_name = :table AND
+                column_name = :column AND
+                table_schema = :schema
+              ORDER  BY ordinal_position
+              LIMIT 1';
+    $type = db_query($query,
+      array(':table' => $table, ':column' => $column, ':schema' => $this->schema_name))->fetchField();
+
+    // Finally we do the check!
+    if ($type === $expected_type) {
+      return TRUE;
+    }
+    elseif (($expected_type == 'float') AND (($type == 'double precision') OR ($type == 'real'))) {
+      return TRUE;
+    }
+    elseif ($type == 'smallint' AND $expected_type == 'integer') {
+      return TRUE;
+    }
+    elseif ($type == 'bigint' AND $expected_type == 'integer') {
+      return TRUE;
+    }
+    else {
+      return FALSE;
+    }
+  }
+
+  /**
+   * Check that any given sequence in a Chado table exists.
+   *
+   * @param table
+   *   The name of the table the sequence is used in.
+   * @param column
+   *   The name of the column the sequence is used to populate.
+   *
+   * @return
+   *   TRUE if the seqeuence exists in the chado schema and FALSE if it does not.
+   *
+   * @ingroup tripal_chado_schema_api
+   */
+  public function checkSequenceExists($table, $column) {
+
+    $prefixed_table = $this->schema_name.'.'.$table;
+    $sequence_name = db_query('SELECT pg_get_serial_sequence(:table, :column);',
+      array(':table' => $prefixed_table, ':column' => $column))->fetchField();
+
+
+    // Remove prefixed table from sequence name
+    $sequence_name = str_replace($this->schema_name.'.', '', $sequence_name);
+
+    return chado_sequence_exists($sequence_name);
+  }
+
+  /**
+   * Check that the primary key exists, has a sequence and a constraint.
+   *
+   * @param $table
+   *   The table you want to check the primary key for.
+   * @param $column
+   *   (OPTIONAL) The name of the primary key column.
+   *
+   * @return
+   *   TRUE if the primary key meets all the requirements and false otherwise.
+   */
+  public function checkPrimaryKey($table, $column = NULL) {
+
+    // If they didn't supply the column, then we can look it up.
+    if ($column === NULL) {
+      $table_schema = $this->getTableSchema($table);
+      $column = $table_schema['primary key'][0];
+    }
+
+    // If there is no primary key then we can't check it.
+    // It neither passes nore fails validation.
+    if (empty($column)) {
+      tripal_report_error(
+        'ChadoSchema',
+        TRIPAL_NOTICE,
+        'Cannot check the validity of the primary key for "!table" since there is no record of one.',
+        array('!table' => $table)
+      );
+      return NULL;
+    }
+
+    // Check the column exists.
+    $column_exists = $this->checkColumnExists($table, $column);
+    if (!$column_exists) {
+      return FALSE;
+    }
+
+    // First check that the sequence exists.
+    $sequence_exists = $this->checkSequenceExists($table, $column);
+    if (!$sequence_exists) {
+      return FALSE;
+    }
+
+    // Next check the constraint is there.
+    $constraint_exists = chado_query(
+      "SELECT 1
+      FROM information_schema.table_constraints
+      WHERE table_name=:table AND constraint_type = 'PRIMARY KEY'",
+      array(':table' => $table))->fetchField();
+    if (!$constraint_exists) {
+      return FALSE;
+    }
+
+    return TRUE;
+  }
+
+  /**
+   * Check that the constraint exists.
+   *
+   * @param $table
+   *   The table the constraint applies to.
+   * @param $constraint_name
+   *   The name of the constraint you want to check.
+   * @param $type
+   *   The type of constraint. Should be one of "PRIMARY KEY", "UNIQUE", or "FOREIGN KEY".
+   *
+   * @return
+   *   TRUE if the constraint exists and false otherwise.
+   */
+  function checkConstraintExists($table, $constraint_name, $type) {
+
+    // Next check the constraint is there.
+    $constraint_exists = chado_query(
+      "SELECT 1
+      FROM information_schema.table_constraints
+      WHERE table_name=:table AND constraint_type = :type AND constraint_name = :name",
+      array(':table' => $table, ':name' => $constraint_name, ':type' => $type))->fetchField();
+    if (!$constraint_exists) {
+      return FALSE;
+    }
+
+    return TRUE;
+  }
+
+  /**
+   * Check the foreign key constrain specified exists.
+   *
+   * @param $base_table
+   *   The name of the table the foreign key resides in. E.g. 'feature' for
+   *     the feature.type_id => cvterm.cvterm_id foreign key.
+   * @param $base_column
+   *   The name of the column that is a foreign key in. E.g. 'type_id' for
+   *     the feature.type_id => cvterm.cvterm_id foreign key.
+   *
+   * @return
+   *   TRUE if the constraint exists and false otherwise.
+   */
+  function checkFKConstraintExists($base_table, $base_column) {
+
+
+    // Since we don't have a constraint name, we have to use the known pattern for
+    // creating these names in order to make this check.
+    // This is due to PostgreSQL not storing column information for constraints
+    // in the information_schema tables.
+    $constraint_name = $base_table . '_' . $base_column . '_fkey';
+
+    return $this->checkConstraintExists($base_table, $constraint_name, 'FOREIGN KEY');
+  }
+
+  /**
+   * A Chado-aware replacement for the db_index_exists() function.
+   *
+   * @param $table
+   *   The table to be altered.
+   * @param $name
+   *   The name of the index.
+   */
+  function checkIndexExists($table, $name) {
+    return chado_index_exists($table, $name);
+  }
+}

+ 74 - 63
tripal_chado/api/modules/tripal_chado.cv.api.inc

@@ -370,7 +370,7 @@ function tripal_update_cvtermpath_old($cv_id, $job_id = NULL) {
 }
 
 /**
- * 
+ *
  * @param unknown $cv_id
  */
 function chado_clear_cvtermpath($cv_id) {
@@ -382,7 +382,7 @@ function chado_clear_cvtermpath($cv_id) {
  * Replacement for the fill_cvtermpath() stored procedure in Chado.
  *
  * Fills the cvtermpath table of Chado with relationships between every
- * node in the ontology graph and all of it's descendents.  This was 
+ * node in the ontology graph and all of it's descendents.  This was
  * previously performed using the fill_cvtermpath() stored procedure of Chado
  * but that function cannot handle loops in the ontology graphs and results
  * in stack depth errors in PostgreSQL.
@@ -395,37 +395,37 @@ function chado_clear_cvtermpath($cv_id) {
  * @ingroup tripal_chado_cv_api
  */
 function chado_update_cvtermpath($cv_id, $job = NULL) {
-  
+
   $cv = new ChadoRecord('cv', $cv_id);
-  print "Building cvterm paths for vocabulary: " . $cv->getValue('name') ."\n"; 
+  print "Building cvterm paths for vocabulary: " . $cv->getValue('name') ."\n";
   print "Clearing the cvtermpath table for this vocabulary...\n";
-  chado_clear_cvtermpath($cv_id);  
+  chado_clear_cvtermpath($cv_id);
   print "Clearing completed.\n";
-  
+
   // The cache is used to limit repetitive queries by storing known data.
   $cache = [
     // Stores all relationships in the cvtermpath table.
     'rels' => [],
     // Stores all nodes that were visited when processing all nodes as roots.
     'roots_processed' => [],
-    // Stores all nodes that were visited when prociessing all children of 
+    // Stores all nodes that were visited when prociessing all children of
     // a single root. It gets emptied at each root.
     'nodes_processed' => [],
     // For easy lookup stores the is_a relationship.
     'is_a' => NULL,
   ];
-  
+
   // TODO: there's a function to determine the current Chado instance.
   // we should use that.
   $transaction = db_transaction();
-  
+
   try {
-    
-    // Get the is_a term. The OBO importer adds this for evey vocabulary.
-    $sql = "SELECT * FROM {cvterm} WHERE name = :is_a and cv_id = :cv_id";
-    $args = [':is_a' => 'is_a', ':cv_id' => $cv_id];
-    $cache['$is_a'] = chado_query($sql, $args)->fetchObject();
-    
+
+    // Get the is_a term. The OBO importer adds this for every vocabulary.
+    $sql = "SELECT * FROM {cvterm} WHERE (name = :is_a or name = :is_a_alt) and cv_id = :cv_id";
+    $args = [':is_a' => 'is_a', ':is_a_alt' => 'is a', ':cv_id' => $cv_id];
+    $cache['is_a'] = chado_query($sql, $args)->fetchObject();
+
     // First cache all the relationships for this vocaublary so that we
     // don't have to do repetitive queries to Chado.
     print "Retrieving relationships...\n";
@@ -434,7 +434,7 @@ function chado_update_cvtermpath($cv_id, $job = NULL) {
       FROM {cvterm_relationship} CVTR
         INNER JOIN {cvterm} CVTO on CVTO.cvterm_id = CVTR.object_id
         INNER JOIN {cvterm} CVTS on CVTS.cvterm_id = CVTR.subject_id
-      WHERE CVTO.cv_id = :cv_id 
+      WHERE CVTO.cv_id = :cv_id
       ORDER BY CVTO.name, CVTS.name
     ";
     $rels = chado_query($sql, [':cv_id' => $cv_id]);
@@ -446,12 +446,12 @@ function chado_update_cvtermpath($cv_id, $job = NULL) {
     if ($job) {
       $job->logMessage('Found !total relationships in this vocabulary.', ['!total' => $total_items]);
       $job->setTotalItems($total_items);
-      $job->logMessage('Note: Progress updates occur as each term is processed and ' . 
+      $job->logMessage('Note: Progress updates occur as each term is processed and ' .
         'some terms take longer than others.');
       $job->setProgress(0);
       $job->setInterval(1);
     }
-    
+
     // Next get the tree roots. These are terms that are in relationships as
     // an object but never as a subject.
     $sql = "
@@ -459,44 +459,44 @@ function chado_update_cvtermpath($cv_id, $job = NULL) {
       FROM {cvterm} CVT
         LEFT JOIN {cvterm_relationship} CVTR ON CVT.cvterm_id = CVTR.subject_id
         INNER JOIN {cvterm_relationship} CVTR2 ON CVT.cvterm_id = CVTR2.object_id
-      WHERE CVT.cv_id = :cvid AND CVTR.subject_id is NULL and 
+      WHERE CVT.cv_id = :cvid AND CVTR.subject_id is NULL and
         CVT.is_relationshiptype = 0 and CVT.is_obsolete = 0
     ";
     $roots = chado_query($sql, [':cvid' => $cv_id]);
-     
+
     // Iterate through the tree roots.
-    print "Processing terms...\n";    
+    print "Processing terms...\n";
     while ($root = $roots->fetchObject()) {
       $root_id =  $root->cvterm_id;
       $root_name = $root->name;
-      
+
       if ($job) {
         $job->logMessage('Processing tree root: ' . $root_name . '...');
       }
-      
+
       // Now start descending through the tree and add the relationships
       // to the cvtermpath table.
       $num_handled = 0;
       $depth = 0;
-      _chado_update_cvtermpath_root($cv_id, $root_id, $root_name, $cache, $job, 
+      _chado_update_cvtermpath_root($cv_id, $root_id, $root_name, $cache, $job,
         $num_handled, $depth);
     }
   }
   catch (Exception $e) {
-    $transaction->rollback();    
+    $transaction->rollback();
     throw $e;
   }
 }
 
 /**
- * Treats a term within the ontology as a root. 
- * 
+ * Treats a term within the ontology as a root.
+ *
  * In order to add all relationships between a term and it's descendents each
  * term gets it's turn as a "root".  The first time this function is called
  * it should be called with the actual root's of the ontology.  This function
  * will then recursively treat each child term within the tree as a root in
  * order to find all relationships.
- * 
+ *
  * @param $cv_id
  *   The vocaulary Id
  * @param $root_id
@@ -513,68 +513,74 @@ function chado_update_cvtermpath($cv_id, $job = NULL) {
  * @param $root_depth
  *   The current depth in the tree of this term.
  */
-function _chado_update_cvtermpath_root($cv_id, $root_id, $root_name, &$cache, 
+function _chado_update_cvtermpath_root($cv_id, $root_id, $root_name, &$cache,
   $job, &$num_handled, $root_depth = 0) {
-    
-    
+
+
   // Don't use a node as a root if we've already used it once before.
   if (in_array($root_id, $cache['roots_processed'])) {
     return;
   }
-    
+
   // Mark this node as having been processed as a root node.
   $cache['roots_processed'][] = $root_id;
-  
-  // For the actual tree roots we need to add a relatioship to themselves.
+
+  // For the actual tree roots we need to add a relationship to themselves.
   if ($root_depth == 0) {
-    $is_a = $cache['$is_a'];
+    $is_a = $cache['is_a'];
     $type_id = $is_a->cvterm_id;
     $depth = 1;
     _chado_update_cvtermpath_add_relationship($type_id, $root_id, $root_id, $cv_id, $depth);
   }
-  
+
+  // If there are no children do nothing.
+  if (!array_key_exists($root_id, $cache['rels'])) {
+    return;
+  }
+
   // Get this term's children and recurse.
   $children = $cache['rels'][$root_id];
-  
+
   // If there are no children do nothing.
   if (!$children) {
     return;
   }
-    
+
   // Set the job progress.
   if ($job) {
     $job->setItemsHandled($num_handled);
   }
   $num_handled++;
-  
+
   // The $path variable contains only the current path on the descent. This
   // is used for detecting loops in the graph. If we encounter a node a
   // second time while traversing a single path of the tree then we've hit
   // a loop.
   $path = [];
- 
-  // Process the children of this term.  
+
+  // Process the children of this term.
   $cache['nodes_processed'] = [];
+  $next_depth = 0;
   _chado_update_cvtermpath_process_children($cv_id, $root_id, $root_id, $path, $cache, $next_depth);
-  
+
   // Next make each child of this node a root and recurse again.
   foreach ($children as $child) {
     $child_id = $child[0];
     $child_type_id = $child[1];
     $child_name = $child[2];
 
-    // Process this child as a root. 
+    // Process this child as a root.
     $next_depth = $root_depth + 1;
-    _chado_update_cvtermpath_root($cv_id, $child_id, $child_name, $cache, 
+    _chado_update_cvtermpath_root($cv_id, $child_id, $child_name, $cache,
       $job, $num_handled, $next_depth);
   }
-}  
+}
 /**
  * Handles a single node in the tree.
- * 
+ *
  * This is a recursive function which calls itself as the tree is descended. It
  * performs a depth-first search of the tree.
- * 
+ *
  * @param $cv_id
  *   The vocaulary Id
  * @param $root_id
@@ -587,65 +593,70 @@ function _chado_update_cvtermpath_root($cv_id, $root_id, $root_name, &$cache,
  * @param $cache
  *   The cache used for lookups.
  * @param $depth
- *   The current depth in the tree. 
+ *   The current depth in the tree.
  */
-function _chado_update_cvtermpath_process_children($cv_id, $root_id, $cvterm_id, 
+function _chado_update_cvtermpath_process_children($cv_id, $root_id, $cvterm_id,
   $path, &$cache, $depth = 1) {
-  
+
   $cache['nodes_processed'][$cvterm_id] = TRUE;
-      
+
   // Have we visited this node before while on this path then we won't
   // descend further as this means we've hit a loop.
   if (in_array($cvterm_id, $path)) {
     return;
   }
-  
+
   // Add this term to the path.
   $path[] = $cvterm_id;
   //print implode('-', $path) . "\n";
-  
+
+  // If this term does not have children then return.
+  if (!array_key_exists($cvterm_id, $cache['rels'])) {
+    return;
+  }
+
   // Get this term's children and recurse.
   $children = $cache['rels'][$cvterm_id];
-  
+
   // If this term does not have children then return.
   if (!$children) {
     return;
   }
-  
+
   // If the term has children then recurse on those.
   $next_depth = $depth + 1;
   foreach ($children as $child) {
     $child_id = $child[0];
     $child_type_id = $child[1];
-         
+
     // Don't descend for children we've already seen.
     if (array_key_exists($child_id, $cache['nodes_processed'])) {
       continue;
     }
-    
+
     _chado_update_cvtermpath_add_relationship($child_type_id, $child_id, $root_id,
       $cv_id, $next_depth);
-    
+
     _chado_update_cvtermpath_process_children($cv_id, $root_id, $child_id, $path, $cache, $next_depth);
   }
 }
 
 /**
  * Inserts values into the cvtermpath table.
- * 
+ *
  * After the entire tree below the current root term is traversed, this
  * function is called and inserts all of the relationships that were found
- * into the cvtermpath table. 
- * 
+ * into the cvtermpath table.
+ *
  * @param $visited
  *   The array contaiing relationships for all visited nodes in the tree. These
  *   elements will become the entries in the cvtermpath table.
  * @param $job
  *   The TripalJob instance.
  */
-function _chado_update_cvtermpath_add_relationship($type_id, $cvterm_id, 
+function _chado_update_cvtermpath_add_relationship($type_id, $cvterm_id,
   $root_id, $cv_id, $depth) {
-  
+
   $cvtermpath = new ChadoRecord('cvtermpath');
   $cvtermpath->setValues([
     'type_id' =>  $type_id,

+ 27 - 3
tripal_chado/api/tripal_chado.schema_v1.2.api.inc

@@ -2820,6 +2820,7 @@ function tripal_chado_chado_schema_v1_2_control() {
     'primary key' => array(
       0 => 'control_id',
     ),
+    'unique keys' => array(),
     'indexes' => array(
       'control_idx1' => array(
         0 => 'type_id',
@@ -3858,6 +3859,7 @@ function tripal_chado_chado_schema_v1_2_eimage() {
     'primary key' => array(
       0 => 'eimage_id',
     ),
+    'unique keys' => array(),
     'foreign keys' => array(
     ),
     'table' => 'eimage',
@@ -6559,6 +6561,7 @@ function tripal_chado_chado_schema_v1_2_featuremap_pub() {
     'primary key' => array(
       0 => 'featuremap_pub_id',
     ),
+    'unique keys' => array(),
     'indexes' => array(
       'featuremap_pub_idx1' => array(
         0 => 'featuremap_id',
@@ -6633,6 +6636,7 @@ function tripal_chado_chado_schema_v1_2_featurepos() {
     'primary key' => array(
       0 => 'featurepos_id',
     ),
+    'unique keys' => array(),
     'indexes' => array(
       'featurepos_idx1' => array(
         0 => 'featuremap_id',
@@ -6878,6 +6882,7 @@ function tripal_chado_chado_schema_v1_2_featurerange() {
     'primary key' => array(
       0 => 'featurerange_id',
     ),
+    'unique keys' => array(),
     'indexes' => array(
       'featurerange_idx1' => array(
         0 => 'featuremap_id',
@@ -7789,6 +7794,7 @@ function tripal_chado_chado_schema_v1_2_magedocumentation() {
     'primary key' => array(
       0 => 'magedocumentation_id',
     ),
+    'unique keys' => array(),
     'indexes' => array(
       'magedocumentation_idx1' => array(
         0 => 'mageml_id',
@@ -7855,6 +7861,7 @@ function tripal_chado_chado_schema_v1_2_mageml() {
     'primary key' => array(
       0 => 'mageml_id',
     ),
+    'unique keys' => array(),
     'foreign keys' => array(
     ),
     'table' => 'mageml',
@@ -7900,6 +7907,7 @@ function tripal_chado_chado_schema_v1_2_nd_experiment() {
     'primary key' => array(
       0 => 'nd_experiment_id',
     ),
+    'unique keys' => array(),
     'foreign keys' => array(
       'cvterm' => array(
         'table' => 'cvterm',
@@ -7965,6 +7973,7 @@ function tripal_chado_chado_schema_v1_2_nd_experiment_contact() {
     'primary key' => array(
       0 => 'nd_experiment_contact_id',
     ),
+    'unique keys' => array(),
     'foreign keys' => array(
       'contact' => array(
         'table' => 'contact',
@@ -8020,6 +8029,7 @@ function tripal_chado_chado_schema_v1_2_nd_experiment_dbxref() {
     'primary key' => array(
       0 => 'nd_experiment_dbxref_id',
     ),
+    'unique keys' => array(),
     'foreign keys' => array(
       'dbxref' => array(
         'table' => 'dbxref',
@@ -8197,6 +8207,7 @@ function tripal_chado_chado_schema_v1_2_nd_experiment_project() {
     'primary key' => array(
       0 => 'nd_experiment_project_id',
     ),
+    'unique keys' => array(),
     'foreign keys' => array(
       'project' => array(
         'table' => 'project',
@@ -8252,6 +8263,7 @@ function tripal_chado_chado_schema_v1_2_nd_experiment_protocol() {
     'primary key' => array(
       0 => 'nd_experiment_protocol_id',
     ),
+    'unique keys' => array(),
     'foreign keys' => array(
       'nd_experiment' => array(
         'table' => 'nd_experiment',
@@ -8381,6 +8393,7 @@ function tripal_chado_chado_schema_v1_2_nd_experiment_stock() {
     'primary key' => array(
       0 => 'nd_experiment_stock_id',
     ),
+    'unique keys' => array(),
     'foreign keys' => array(
       'cvterm' => array(
         'table' => 'cvterm',
@@ -8445,6 +8458,7 @@ function tripal_chado_chado_schema_v1_2_nd_experiment_stock_dbxref() {
     'primary key' => array(
       0 => 'nd_experiment_stock_dbxref_id',
     ),
+    'unique keys' => array(),
     'foreign keys' => array(
       'dbxref' => array(
         'table' => 'dbxref',
@@ -8663,8 +8677,8 @@ function tripal_chado_chado_schema_v1_2_nd_geolocation() {
     'primary key' => array(
       0 => 'nd_geolocation_id',
     ),
-    'foreign keys' => array(
-    ),
+    'unique keys' => array(),
+    'foreign keys' => array(),
     'table' => 'nd_geolocation',
     'referring_tables' => array(
       0 => 'nd_experiment',
@@ -8846,6 +8860,7 @@ function tripal_chado_chado_schema_v1_2_nd_protocol_reagent() {
     'primary key' => array(
       0 => 'nd_protocol_reagent_id',
     ),
+    'unique keys' => array(),
     'foreign keys' => array(
       'cvterm' => array(
         'table' => 'cvterm',
@@ -8986,6 +9001,7 @@ function tripal_chado_chado_schema_v1_2_nd_reagent() {
     'primary key' => array(
       0 => 'nd_reagent_id',
     ),
+    'unique keys' => array(),
     'foreign keys' => array(
       'cvterm' => array(
         'table' => 'cvterm',
@@ -9044,6 +9060,7 @@ function tripal_chado_chado_schema_v1_2_nd_reagent_relationship() {
     'primary key' => array(
       0 => 'nd_reagent_relationship_id',
     ),
+    'unique keys' => array(),
     'foreign keys' => array(
       'cvterm' => array(
         'table' => 'cvterm',
@@ -10513,6 +10530,7 @@ function tripal_chado_chado_schema_v1_2_phylotree() {
     'primary key' => array(
       0 => 'phylotree_id',
     ),
+    'unique keys' => array(),
     'indexes' => array(
       'phylotree_idx1' => array(
         0 => 'phylotree_id',
@@ -11123,6 +11141,7 @@ function tripal_chado_chado_schema_v1_2_protocolparam() {
     'primary key' => array(
       0 => 'protocolparam_id',
     ),
+    'unique keys' => array(),
     'indexes' => array(
       'protocolparam_idx1' => array(
         0 => 'protocol_id',
@@ -12645,6 +12664,7 @@ function tripal_chado_chado_schema_v1_2_stock_relationship_cvterm() {
     'primary key' => array(
       0 => 'stock_relationship_cvterm_id',
     ),
+    'unique keys' => array(),
     'foreign keys' => array(
       'cvterm' => array(
         'table' => 'cvterm',
@@ -13331,6 +13351,7 @@ function tripal_chado_chado_schema_v1_2_studydesign() {
     'primary key' => array(
       0 => 'studydesign_id',
     ),
+    'unique keys' => array(),
     'indexes' => array(
       'studydesign_idx1' => array(
         0 => 'study_id',
@@ -13479,6 +13500,7 @@ function tripal_chado_chado_schema_v1_2_studyfactor() {
     'primary key' => array(
       0 => 'studyfactor_id',
     ),
+    'unique keys' => array(),
     'indexes' => array(
       'studyfactor_idx1' => array(
         0 => 'studydesign_id',
@@ -13560,6 +13582,7 @@ function tripal_chado_chado_schema_v1_2_studyfactorvalue() {
     'primary key' => array(
       0 => 'studyfactorvalue_id',
     ),
+    'unique keys' => array(),
     'indexes' => array(
       'studyfactorvalue_idx1' => array(
         0 => 'studyfactor_id',
@@ -13954,6 +13977,7 @@ function tripal_chado_chado_schema_v1_2_treatment() {
     'primary key' => array(
       0 => 'treatment_id',
     ),
+    'unique keys' => array(),
     'indexes' => array(
       'treatment_idx1' => array(
         0 => 'biomaterial_id',
@@ -14179,4 +14203,4 @@ function tripal_chado_chado_get_v1_2_tables() {
     'treatment'
    );
    return $tables;
-}
+}

+ 19 - 17
tripal_chado/api/tripal_chado.schema_v1.3.api.inc

@@ -4118,7 +4118,7 @@ function tripal_chado_chado_schema_v1_3_cvtermprop() {
       ),
     ),
     'unique keys' => array(
-      'cvterm_id_type_id_value_rank' => array(
+      'cvtermprop_cvterm_id_type_id_value_rank_key' => array(
         0 => 'cvterm_id',
         1 => 'type_id',
         2 => 'value',
@@ -7019,13 +7019,14 @@ function tripal_chado_chado_schema_v1_3_featuremap_dbxref() {
         'default' => 'true',
       ),
     ),
-    // TODO: this unique constraint is missing from the actual Chado schema.
-    // It should be included.
     'unique keys' => array(
+      /* @todo: this unique constraint is missing from the actual Chado schema.
+          It should be included.
       'feature_dbxref_c1' => array(
         0 => 'featuremap_id',
         1 => 'dbxref_id',
       ),
+      */
     ),
     'indexes' => array(
       'featuremap_dbxref_idx1' => array(
@@ -7252,13 +7253,14 @@ function tripal_chado_chado_schema_v1_3_featuremap_pub() {
         'not null' => TRUE,
       ),
     ),
-    // TODO: this unique constraint is missing from the actual Chado schema.
-    // It should be included.
     'unique keys' => array(
+      /* @todo: this unique constraint is missing from the actual Chado schema.
+          It should be included.
       'feature_pub_c1' => array(
         0 => 'featuremap_id',
         1 => 'pub_id',
       ),
+      */
     ),
     'indexes' => array(
       'featuremap_pub_idx1' => array(
@@ -9936,7 +9938,7 @@ function tripal_chado_chado_schema_v1_3_materialized_view() {
       ),
     ),
     'unique keys' => array(
-      'name' => array(
+      'materialized_view_name_key' => array(
         0 => 'name',
       ),
     ),
@@ -11123,7 +11125,7 @@ function tripal_chado_chado_schema_v1_3_nd_protocol() {
       ),
     ),
     'unique keys' => array(
-      'name' => array(
+      'nd_protocol_name_key' => array(
         0 => 'name',
       ),
     ),
@@ -13094,11 +13096,11 @@ function tripal_chado_chado_schema_v1_3_phylonode() {
       ),
     ),
     'unique keys' => array(
-      'phylotree_id_left_idx' => array(
+      'phylonode_phylotree_id_left_idx_key' => array(
         0 => 'phylotree_id',
         1 => 'left_idx',
       ),
-      'phylotree_id_right_idx' => array(
+      'phylonode_phylotree_id_right_idx_key' => array(
         0 => 'phylotree_id',
         1 => 'right_idx',
       ),
@@ -13189,7 +13191,7 @@ function tripal_chado_chado_schema_v1_3_phylonode_dbxref() {
       ),
     ),
     'unique keys' => array(
-      'phylonode_id_dbxref_id' => array(
+      'phylonode_dbxref_phylonode_id_dbxref_id_key' => array(
         0 => 'phylonode_id',
         1 => 'dbxref_id',
       ),
@@ -13262,7 +13264,7 @@ function tripal_chado_chado_schema_v1_3_phylonode_organism() {
       ),
     ),
     'unique keys' => array(
-      'phylonode_id' => array(
+      'phylonode_organism_phylonode_id_key' => array(
         0 => 'phylonode_id',
       ),
     ),
@@ -13346,7 +13348,7 @@ function tripal_chado_chado_schema_v1_3_phylonodeprop() {
       ),
     ),
     'unique keys' => array(
-      'phylonode_id_type_id_value_rank' => array(
+      'phylonodeprop_phylonode_id_type_id_value_rank_key' => array(
         0 => 'phylonode_id',
         1 => 'type_id',
         2 => 'value',
@@ -13421,7 +13423,7 @@ function tripal_chado_chado_schema_v1_3_phylonode_pub() {
       ),
     ),
     'unique keys' => array(
-      'phylonode_id_pub_id' => array(
+      'phylonode_pub_phylonode_id_pub_id_key' => array(
         0 => 'phylonode_id',
         1 => 'pub_id',
       ),
@@ -13515,7 +13517,7 @@ function tripal_chado_chado_schema_v1_3_phylonode_relationship() {
       0 => 'phylonode_relationship_id',
     ),
     'unique keys' => array(
-      'subject_id_object_id_type_id' => array(
+      'phylonode_relationship_subject_id_object_id_type_id_key' => array(
         0 => 'subject_id',
         1 => 'object_id',
         2 => 'type_id',
@@ -13768,7 +13770,7 @@ function tripal_chado_chado_schema_v1_3_phylotree_pub() {
       ),
     ),
     'unique keys' => array(
-      'phylotree_id_pub_id' => array(
+      'phylotree_pub_phylotree_id_pub_id_key' => array(
         0 => 'phylotree_id',
         1 => 'pub_id',
       ),
@@ -17676,7 +17678,7 @@ function tripal_chado_chado_schema_v1_3_studyprop() {
       0 => 'studyprop_id',
     ),
     'unique keys' => array(
-      'study_id_type_id_rank' => array(
+      'studyprop_study_id_type_id_rank_key' => array(
         0 => 'study_id',
         1 => 'type_id',
         2 => 'rank',
@@ -17756,7 +17758,7 @@ function tripal_chado_chado_schema_v1_3_studyprop_feature() {
       0 => 'studyprop_feature_id',
     ),
     'unique keys' => array(
-      'studyprop_id_feature_id' => array(
+      'studyprop_feature_studyprop_id_feature_id_key' => array(
         0 => 'studyprop_id',
         1 => 'feature_id',
       ),

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

@@ -314,6 +314,7 @@ function chado_generate_var($table, $values, $base_options = array()) {
       // 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.
+      $base_tables = chado_get_base_tables();
       if (module_exists('tripal_core') and db_table_exists('chado_' . $table)) {
         // That has a foreign key to this one ($table_desc['primary key'][0]
         // and to the node table (nid).

Diff do ficheiro suprimidas por serem muito extensas
+ 169 - 169
tripal_chado/includes/TripalImporter/OBOImporter.inc


+ 95 - 95
tripal_chado/includes/setup/tripal_chado.setup.inc

@@ -71,8 +71,8 @@ function tripal_chado_prepare_drush_submit() {
  *
  */
 function tripal_chado_load_ontologies() {
-  
-  // Before we can load ontologies we need a few terms that unfortunately 
+
+  // Before we can load ontologies we need a few terms that unfortunately
   // don't get added until later. We'll add them now so the loader works.
   chado_insert_db([
     'name' => 'NCIT',
@@ -84,15 +84,15 @@ function tripal_chado_load_ontologies() {
     'ncit',
     'The NCIt OBO Edition project aims to increase integration of the NCIt with OBO Library ontologies. NCIt is a reference terminology that includes broad coverage of the cancer domain, including cancer related diseases, findings and abnormalities. NCIt OBO Edition releases should be considered experimental.'
     );
-  
+
   $term = chado_insert_cvterm([
     'id' => 'NCIT:C25693',
     'name' => 'Subgroup',
     'cv_name' => 'ncit',
     'definition' => 'A subdivision of a larger group with members often exhibiting similar characteristics. [ NCI ]',
   ]);
-  
-  
+
+
   // Add the rdfs:comment vocabulary.
   chado_insert_db(array(
     'name' => 'rdfs',
@@ -263,7 +263,7 @@ function tripal_chado_prepare_chado($job = NULL) {
     drush_print('Populating materialized view db2cv_mview...');
     $mview_id = chado_get_mview_id('db2cv_mview');
     chado_populate_mview($mview_id);
-    
+
     drush_print("Creating common Tripal Content Types...");
     drush_print("This may take awhile if you are upgrading a site that has lots of data...");
     if ($report_progress) {
@@ -280,16 +280,16 @@ function tripal_chado_prepare_chado($job = NULL) {
 
     // Set a variable to indicate the site is prepared.
     variable_set('tripal_chado_is_prepared', TRUE);
-    
+
     if ($report_progress) {
       $job->setProgress(100);
-    }  
+    }
   }
   catch (Exception $e) {
     $job->logMessage($e);
     throw new Exception($e);
   }
-  
+
   // Clear the Drupal menu cache so that the new content types have "add" links.
   menu_cache_clear_all();
 }
@@ -298,10 +298,10 @@ function tripal_chado_prepare_chado($job = NULL) {
  * Creates the "General" category of content types.
  */
 function tripal_chado_prepare_general_types($job) {
-  
+
   //
   // Create the 'Organism' entity type. This uses the obi:organism term.
-  //  
+  //
   $args = array(
     'vocabulary' => 'OBI',
     'accession' => '0100026',
@@ -311,11 +311,11 @@ function tripal_chado_prepare_general_types($job) {
     ),
     'category' => 'General'
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
-  
+  _tripal_chado_prepare_create_bundle($args, $job);
+
   //
   // Create the 'Analysis' entity type. This uses the local:analysis term.
-  //  
+  //
   $args = array(
     'vocabulary' => 'operation',
     'accession' => '2945',
@@ -325,11 +325,11 @@ function tripal_chado_prepare_general_types($job) {
     ),
     'category' => 'General'
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
+  _tripal_chado_prepare_create_bundle($args, $job);
 
   //
   // Create the 'Project' entity type. This uses the local:project term.
-  //  
+  //
   $args = array(
     'vocabulary' => 'NCIT',
     'accession' => 'C47885',
@@ -339,12 +339,12 @@ function tripal_chado_prepare_general_types($job) {
     ),
     'category' => 'General'
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
+  _tripal_chado_prepare_create_bundle($args, $job);
+
 
-  
   //
   // Create the 'Study' entity type. This uses the local:project term.
-  //  
+  //
   $args = array(
     'vocabulary' => 'SIO',
     'accession' => '001066',
@@ -354,11 +354,11 @@ function tripal_chado_prepare_general_types($job) {
     ),
     'category' => 'General'
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
-  
+  _tripal_chado_prepare_create_bundle($args, $job);
+
   //
   // Create the 'Contact' entity type. This uses the local:contact term.
-  //  
+  //
   $args = array(
     'vocabulary' => 'local',
     'accession' => 'contact',
@@ -368,11 +368,11 @@ function tripal_chado_prepare_general_types($job) {
     ),
     'category' => 'General'
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
+  _tripal_chado_prepare_create_bundle($args, $job);
 
   //
   // Create the 'Publication' entity type.
-  //      
+  //
   $args = array(
     'vocabulary' => 'TPUB',
     'accession' => '0000002',
@@ -387,9 +387,9 @@ function tripal_chado_prepare_general_types($job) {
     // Import a publication so we get all of the properties before
     // creating the content type.
     chado_import_pub_by_dbxref('PMID:24163125');
-    
-    _tripal_chado_preapre_create_bundle($args, $job);
-    
+
+    _tripal_chado_prepare_create_bundle($args, $job);
+
     // Now remove the publication that was added above.
     $values = array(
       'dbxref_id' => array(
@@ -402,10 +402,10 @@ function tripal_chado_prepare_general_types($job) {
     $result = chado_select_record('pub_dbxref', array('pub_id'), $values);
     chado_delete_record('pub', array('pub_id' => $result[0]->pub_id));
   }
-  
+
   //
   // Create the 'Protocol' entity type.
-  //  
+  //
   $args = array(
     'vocabulary' => 'sep',
     'accession' => '00101',
@@ -415,7 +415,7 @@ function tripal_chado_prepare_general_types($job) {
     ),
     'category' => 'General'
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
+  _tripal_chado_prepare_create_bundle($args, $job);
 }
 
 /**
@@ -424,7 +424,7 @@ function tripal_chado_prepare_general_types($job) {
 function tripal_chado_prepare_genomic_types($job) {
   //
   // Create the 'Gene' entity type.
-  //  
+  //
   $args = array(
     'vocabulary' => 'SO',
     'accession' => '0000704',
@@ -435,11 +435,11 @@ function tripal_chado_prepare_genomic_types($job) {
     ),
     'category' => 'Genomic',
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
-  
+  _tripal_chado_prepare_create_bundle($args, $job);
+
   //
   // Create the 'mRNA' entity type.
-  //  
+  //
   $args = array(
     'vocabulary' => 'SO',
     'accession' => '0000234',
@@ -450,11 +450,11 @@ function tripal_chado_prepare_genomic_types($job) {
     ),
     'category' => 'Genomic',
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
-  
+  _tripal_chado_prepare_create_bundle($args, $job);
+
   //
   // Create the 'Phylogenetic tree' entity type.
-  //  
+  //
   $args = array(
     'vocabulary' => 'data',
     'accession' => '0872',
@@ -464,9 +464,9 @@ function tripal_chado_prepare_genomic_types($job) {
     ),
     'category' => 'Genomic',
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
-  
-  // Create the 'Physical Map' entity type.  
+  _tripal_chado_prepare_create_bundle($args, $job);
+
+  // Create the 'Physical Map' entity type.
   $cvterm = tripal_get_cvterm(['id' => 'rdfs:type']);
   $args = array(
     'vocabulary' => 'data',
@@ -481,9 +481,9 @@ function tripal_chado_prepare_genomic_types($job) {
     ),
     'category' => 'Genomic',
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
-  
-  // Create the 'DNA Library' entity type.  
+  _tripal_chado_prepare_create_bundle($args, $job);
+
+  // Create the 'DNA Library' entity type.
   $args = array(
     'vocabulary' => 'NCIT',
     'accession' => 'C16223',
@@ -494,9 +494,9 @@ function tripal_chado_prepare_genomic_types($job) {
     ),
     'category' => 'Genomic',
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
-  
-  // Create the 'Genome Assembly' entity type.  
+  _tripal_chado_prepare_create_bundle($args, $job);
+
+  // Create the 'Genome Assembly' entity type.
   $cvterm = tripal_get_cvterm(['id' => 'rdfs:type']);
   $args = array(
     'vocabulary' => 'operation',
@@ -511,9 +511,9 @@ function tripal_chado_prepare_genomic_types($job) {
     ),
     'category' => 'Genomic',
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
-  
-  // Create the 'Genome Annotation' entity type.  
+  _tripal_chado_prepare_create_bundle($args, $job);
+
+  // Create the 'Genome Annotation' entity type.
   $cvterm = tripal_get_cvterm(['id' => 'rdfs:type']);
   $args = array(
     'vocabulary' => 'operation',
@@ -528,9 +528,9 @@ function tripal_chado_prepare_genomic_types($job) {
     ),
     'category' => 'Genomic',
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
-  
-  // Create the 'Genome Annotation' entity type.  
+  _tripal_chado_prepare_create_bundle($args, $job);
+
+  // Create the 'Genome Annotation' entity type.
   $cvterm = tripal_get_cvterm(['id' => 'rdfs:type']);
   $args = array(
     'vocabulary' => 'local',
@@ -546,7 +546,7 @@ function tripal_chado_prepare_genomic_types($job) {
     'category' => 'Genomic',
   );
   $bundle = tripal_load_bundle_entity(['accession' => $args['vocabulary'] . ':' . $args['accession']]);
-  _tripal_chado_preapre_create_bundle($args, $job);
+  _tripal_chado_prepare_create_bundle($args, $job);
 }
 
 /**
@@ -555,7 +555,7 @@ function tripal_chado_prepare_genomic_types($job) {
 function tripal_chado_prepare_expression_types($job) {
   //
   // Create the 'biological sample' entity type.
-  //  
+  //
   $args = array(
     'vocabulary' => 'sep',
     'accession' => '00195',
@@ -566,11 +566,11 @@ function tripal_chado_prepare_expression_types($job) {
     'Expression',
   );
   $bundle = tripal_load_bundle_entity(['accession' => $args['vocabulary'] . ':' . $args['accession']]);
-  _tripal_chado_preapre_create_bundle($args, $job);
-  
+  _tripal_chado_prepare_create_bundle($args, $job);
+
   //
   // Create the 'Assay' entity type.
-  //  
+  //
   $args = array(
     'vocabulary' => 'OBI',
     'accession' => '0000070',
@@ -581,11 +581,11 @@ function tripal_chado_prepare_expression_types($job) {
     'Expression',
   );
   $bundle = tripal_load_bundle_entity(['accession' => $args['vocabulary'] . ':' . $args['accession']]);
-  _tripal_chado_preapre_create_bundle($args, $job);
-  
+  _tripal_chado_prepare_create_bundle($args, $job);
+
   //
   // Create the 'Array Design' entity type.
-  //  
+  //
   $args = array(
     'vocabulary' => 'EFO',
     'accession' => '0000269',
@@ -595,19 +595,19 @@ function tripal_chado_prepare_expression_types($job) {
     ),
     'Expression',
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
+  _tripal_chado_prepare_create_bundle($args, $job);
 }
 
 /**
  * Creates the "Germplasm/Breeding" category of content types.
  */
 function tripal_chado_prepare_germplasm_types($job) {
-  
+
   //
   // Create the 'Phenotypic Trait' entity type.
-  // 
+  //
   /**
-   * SPF:  We need a bit more testing before we add this conteont type as 
+   * SPF:  We need a bit more testing before we add this conteont type as
    * it resolves to the cvterm table. Currently, it can't be created.
   $args = array(
     'vocabulary' => 'NCIT',
@@ -619,12 +619,12 @@ function tripal_chado_prepare_germplasm_types($job) {
     ),
     'category' => 'Germplasm/Breeding',
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
+  _tripal_chado_prepare_create_bundle($args, $job);
   */
-  
+
   //
   // Create the 'Germplasm Accession' entity type.
-  //  
+  //
   $args = array(
     'vocabulary' => 'CO_010',
     'accession' => '0000044',
@@ -635,11 +635,11 @@ function tripal_chado_prepare_germplasm_types($job) {
     ),
     'category' => 'Germplasm/Breeding',
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
-  
+  _tripal_chado_prepare_create_bundle($args, $job);
+
   //
   // Create the 'Breeding Cross' entity type.
-  //  
+  //
   $args = array(
     'vocabulary' => 'CO_010',
     'accession' => '0000255',
@@ -650,11 +650,11 @@ function tripal_chado_prepare_germplasm_types($job) {
     ),
     'category' => 'Germplasm/Breeding',
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
-  
+  _tripal_chado_prepare_create_bundle($args, $job);
+
   //
   // Create the 'Germplasm Variety' entity type.
-  //  
+  //
   $args = array(
     'vocabulary' => 'CO_010',
     'accession' => '0000029',
@@ -665,11 +665,11 @@ function tripal_chado_prepare_germplasm_types($job) {
     ),
     'category' => 'Germplasm/Breeding',
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
-  
+  _tripal_chado_prepare_create_bundle($args, $job);
+
   //
   // Create the 'Germplasm Variety' entity type.
-  //  
+  //
   $args = array(
     'vocabulary' => 'CO_010',
     'accession' => '0000162',
@@ -680,17 +680,17 @@ function tripal_chado_prepare_germplasm_types($job) {
     ),
     'category' => 'Germplasm/Breeding',
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
+  _tripal_chado_prepare_create_bundle($args, $job);
 }
 
 /**
  * Creates the "Genetic" category of content types.
  */
 function tripal_chado_prepare_genetic_types($job) {
-  
+
   //
   // Create the 'Genetic Map' entity type.
-  //  
+  //
   $cvterm = tripal_get_cvterm(['id' => 'rdfs:type']);
   $args = array(
     'vocabulary' => 'data',
@@ -705,11 +705,11 @@ function tripal_chado_prepare_genetic_types($job) {
     ),
     'category' => 'Genetic',
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
-  
+  _tripal_chado_prepare_create_bundle($args, $job);
+
+  //
+  // Create the 'QTL' entity type.
   //
-  // Create the 'QTL' entity type. 
-  //  
   $args = array(
     'vocabulary' => 'SO',
     'accession' => '0000771',
@@ -720,11 +720,11 @@ function tripal_chado_prepare_genetic_types($job) {
     ),
     'category' => 'Genetic',
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
-  
+  _tripal_chado_prepare_create_bundle($args, $job);
+
   //
   // Create the 'Sequence Variant' entity type.
-  //  
+  //
   $args = array(
     'vocabulary' => 'SO',
     'accession' => '0001060',
@@ -735,8 +735,8 @@ function tripal_chado_prepare_genetic_types($job) {
     ),
     'category' => 'Genetic',
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
-  
+  _tripal_chado_prepare_create_bundle($args, $job);
+
   //
   // Create the 'Genetic Marker' entity type.
   //
@@ -750,9 +750,9 @@ function tripal_chado_prepare_genetic_types($job) {
     ),
     'category' => 'Genetic',
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
-  
-  
+  _tripal_chado_prepare_create_bundle($args, $job);
+
+
   //
   // Create the 'Heritable Phenotypic Marker' entity type.
   //
@@ -766,17 +766,17 @@ function tripal_chado_prepare_genetic_types($job) {
     ),
     'category' => 'Genetic',
   );
-  _tripal_chado_preapre_create_bundle($args, $job);
+  _tripal_chado_prepare_create_bundle($args, $job);
 }
 /**
  * A helper function to consolidate the  code used to create a bundle.
  */
-function _tripal_chado_preapre_create_bundle($args, $job) {
-  
+function _tripal_chado_prepare_create_bundle($args, $job) {
+
   $bundle = tripal_load_bundle_entity(['accession' => $args['vocabulary'] . ':' . $args['accession']]);
   if (!$bundle) {
     drush_print("Creating " . $args['term_name'] . "...");
-    if (!tripal_create_bundle($args, $job)) {      
+    if (!tripal_create_bundle($args, $job)) {
       $msg = t('Error encountered creating !type Content Type.', ['!type' => $args['term_name']]);
       throw new Exception($msg);
     }

+ 63 - 63
tripal_chado/includes/tripal_chado.semweb.inc

@@ -262,7 +262,7 @@ function tripal_chado_populate_vocab_SCHEMA() {
     'cv_name' => 'schema',
     'definition' => 'A page devoted to a single item, such as a particular product or hotel.',
   ));
-  
+
 }
 
 /**
@@ -283,7 +283,7 @@ function tripal_chado_populate_vocab_SEP() {
     'definition' => 'A biological sample analysed by a particular technology.',
   ));
   chado_associate_semweb_term(NULL, 'biomaterial_id', $term);
-  
+
   $term = tripal_insert_cvterm([
     'id' => 'sep:00101',
     'name' => 'protocol',
@@ -370,20 +370,20 @@ function tripal_chado_populate_vocab_SIO() {
     'cv_name' => 'SIO',
     'definition' => 'an email address is an identifier to send mail to particular electronic mailbox.',
   ));
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'SIO:001007',
     'name' => 'assay',
     'cv_name' => 'SIO',
     'definition' => 'An assay is an investigative (analytic) procedure in ' .
-      'laboratory medicine, pharmacology, environmental biology, and ' . 
-      'molecular biology for qualitatively assessing or quantitatively ' . 
-      'measuring the presence or amount or the functional activity of a ' . 
+      'laboratory medicine, pharmacology, environmental biology, and ' .
+      'molecular biology for qualitatively assessing or quantitatively ' .
+      'measuring the presence or amount or the functional activity of a ' .
       'target entity (the analyte) which can be a drug or biochemical ' .
       'substance or a cell in an organism or organic sample.',
   ));
   chado_associate_semweb_term(NULL, 'assay_id', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'SIO:010054',
     'name' => 'cell line',
@@ -391,7 +391,7 @@ function tripal_chado_populate_vocab_SIO() {
     'definition' => 'A cell line is a collection of genetically identifical cells.',
   ));
   chado_associate_semweb_term(NULL, 'cell_line_id', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'SIO:001066',
     'name' => 'study',
@@ -399,7 +399,7 @@ function tripal_chado_populate_vocab_SIO() {
     'definition' => 'A study is a process that realizes the steps of a study design.',
   ));
   chado_associate_semweb_term(NULL, 'study_id', $term);
-  
+
 }
 
 /**
@@ -413,7 +413,7 @@ function tripal_chado_populate_vocab_SO() {
     'urlprefix' => 'http://www.sequenceontology.org/browser/current_svn/term/{db}:{accession}',
   ));
   chado_insert_cv('sequence', 'The sequence ontology.');
-  
+
   $term = chado_get_cvterm(['cv_id' => ['name' => 'sequence'], 'name' => 'sequence_feature']);
   chado_associate_semweb_term(NULL, 'feature_id', $term);
 }
@@ -518,14 +518,14 @@ function tripal_chado_populate_vocab_EDAM() {
   chado_insert_cv(
     'EDAM',
     'EDAM is an ontology of well established, familiar concepts that are ' .
-    'prevalent within bioinformatics, including types of data and data ' . 
-    'identifiers, data formats, operations and topics. EDAM is a simple ' . 
+    'prevalent within bioinformatics, including types of data and data ' .
+    'identifiers, data formats, operations and topics. EDAM is a simple ' .
     'ontology - essentially a set of terms with synonyms and definitions - ' .
-    'organised into an intuitive hierarchy for convenient use by curators, ' . 
-    'software developers and end-users. EDAM is suitable for large-scale ' . 
-    'semantic annotations and categorization of diverse bioinformatics ' . 
-    'resources. EDAM is also suitable for diverse application including ' . 
-    'for example within workbenches and workflow-management systems, ' . 
+    'organised into an intuitive hierarchy for convenient use by curators, ' .
+    'software developers and end-users. EDAM is suitable for large-scale ' .
+    'semantic annotations and categorization of diverse bioinformatics ' .
+    'resources. EDAM is also suitable for diverse application including ' .
+    'for example within workbenches and workflow-management systems, ' .
     'software distributions, and resource registries.'
   );
 
@@ -705,14 +705,14 @@ function tripal_chado_populate_vocab_EDAM() {
     'cv_name' => 'EDAM',
     'definition' => 'Visualise, format or render a molecular sequence or sequences such as a sequence alignment, possibly with sequence features or properties shown.',
   ));
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'operation:0525',
     'name' => 'genome assembly',
     'cv_name' => 'EDAM',
     'definition' => '',
   ));
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'operation:0362',
     'name' => 'Genome annotation ',
@@ -735,7 +735,7 @@ function tripal_chado_populate_vocab_EFO() {
     'efo',
     'The Experimental Factor Ontology (EFO) provides a systematic description of many experimental variables available in EBI databases, and for external projects such as the NHGRI GWAS catalogue. It combines parts of several biological ontologies, such as anatomy, disease and chemical compounds. The scope of EFO is to support the annotation, analysis and visualization of data handled by many groups at the EBI and as the core ontology for OpenTargets.org'
   );
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'EFO:0000548',
     'name' => 'instrument',
@@ -743,7 +743,7 @@ function tripal_chado_populate_vocab_EFO() {
     'definition' => 'An instrument is a device which provides a mechanical or electronic function.',
   ));
   chado_associate_semweb_term('protocol', 'hardwaredescription', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'EFO:0000269',
     'name' => 'array design',
@@ -751,7 +751,7 @@ function tripal_chado_populate_vocab_EFO() {
     'definition' => 'An instrument design which describes the design of the array.',
   ));
   chado_associate_semweb_term(NULL, 'arraydesign_id', $term);
-   
+
   $term = chado_insert_cvterm(array(
     'id' => 'EFO:0005522',
     'name' => 'substrate type',
@@ -773,7 +773,7 @@ function tripal_chado_populate_vocab_EFO() {
     'definition' => '',
   ));
   chado_associate_semweb_term('arraydesign', 'manufacturer_id', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'EFO:0000269',
     'name' => 'assay design',
@@ -858,13 +858,13 @@ function tripal_chado_populate_vocab_OBI() {
   ));
   chado_associate_semweb_term(NULL, 'organism_id', $term);
   chado_associate_semweb_term('biomaterial', 'taxon_id', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'OBI:0000070',
     'name' => 'assay',
     'cv_name' => 'obi',
     'definition' => 'A planned process with the objective to produce information about the material entity that is the evaluant, by physically examining it or its proxies.',
-  ));  
+  ));
 }
 
 /**
@@ -1184,7 +1184,7 @@ function tripal_chado_populate_vocab_LOCAL() {
     'cv_name' => 'local',
   ));
   chado_associate_semweb_term(NULL, 'is_current', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'local:is_internal',
     'name' => 'is_internal',
@@ -1192,7 +1192,7 @@ function tripal_chado_populate_vocab_LOCAL() {
     'cv_name' => 'local',
   ));
   chado_associate_semweb_term(NULL, 'is_internal', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'local:miniref',
     'name' => 'Mini-ref',
@@ -1201,7 +1201,7 @@ function tripal_chado_populate_vocab_LOCAL() {
   ));
   chado_associate_semweb_term('pub', 'miniref', $term);
 
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'local:array_batch_identifier',
     'name' => 'Array Batch Identifier',
@@ -1209,7 +1209,7 @@ function tripal_chado_populate_vocab_LOCAL() {
     'cv_name' => 'local',
   ));
   chado_associate_semweb_term('assay', 'arraybatchidentifier', $term);
-  
+
   //-----------------------------
   // Relationship Terms
   //-----------------------------
@@ -1332,7 +1332,7 @@ function tripal_chado_populate_vocab_LOCAL() {
 
   //--------------
   // Project Terms
-  //-------------- 
+  //--------------
   // Insert cvterm 'Project Description' into cvterm table of chado
   // database. This CV term is used to keep track of the project
   // description in the projectprop table.
@@ -1342,7 +1342,7 @@ function tripal_chado_populate_vocab_LOCAL() {
     'cv_name' => 'project_property',
     'db_name' => 'local'
   ));
-  
+
   chado_insert_cvterm(array(
     'name' => 'Project Type',
     'definition'  => 'A type of project',
@@ -1387,7 +1387,7 @@ function tripal_chado_populate_vocab_LOCAL() {
     'db_name' => 'local'
   ));
   chado_associate_semweb_term(NULL, 'library_id', $term);
-  
+
   // Insert cvterm 'library_description' into cvterm table of chado
   // database. This CV term is used to keep track of the library
   // description in the libraryprop table.
@@ -1640,7 +1640,7 @@ function tripal_chado_populate_vocab_LOCAL() {
     '(Random House Kernerman Webster\'s College Dictionary, © 2010 K ' .
     'Dictionaries Ltd).',
     'cv_name' => 'local',
-  )); 
+  ));
 
   $term = chado_insert_cvterm(array(
     'id' => 'local:source_data',
@@ -1680,7 +1680,7 @@ function tripal_chado_populate_vocab_LOCAL() {
     'biomaterials via the biomaterialrelationship table.',
     'cv_name' => 'local',
   ));
-  
+
   //
   // Terms for arraydesign table
   //
@@ -1747,8 +1747,8 @@ function tripal_chado_populate_vocab_LOCAL() {
     'cv_name' => 'local',
   ));
   chado_associate_semweb_term('arraydesign', 'num_sub_rows', $term);
-  
-  
+
+
   //
   // Terms for Study
   //
@@ -1758,14 +1758,14 @@ function tripal_chado_populate_vocab_LOCAL() {
     'cv_name' => 'study_property',
     'db_name' => 'local'
   ));
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'local:Genome Project',
     'name' => 'Genome Project',
     'definition' => 'A project for whole genome analysis that can include assembly and annotation.',
     'cv_name' => 'local',
   ));
-  
+
 }
 /**
  * Adds the Systems Biology Ontology database and terms.
@@ -1805,7 +1805,7 @@ function tripal_chado_populate_vocab_SBO() {
 }
 
 /**
- * Adds the "Bioinformatics operations, data types, formats, identifiers and 
+ * Adds the "Bioinformatics operations, data types, formats, identifiers and
  * topics" database and terms.
  */
 function tripal_chado_populate_vocab_SWO() {
@@ -1852,11 +1852,11 @@ function tripal_chado_populate_vocab_TPUB() {
 
   chado_insert_db(array(
     'name' => 'TPUB',
-    'description' => 'Tripal Publiation Ontology. A temporary ontology until a more formal appropriate ontology an be identified.',
+    'description' => 'Tripal Publication Ontology. A temporary ontology until a more formal appropriate ontology an be identified.',
     'url' => 'cv/lookup/TPUB',
     'urlprefix' => 'cv/lookup/TPUB/{accession}',
   ));
-  chado_insert_cv('tripal_pub', 'Tripal Publiation Ontology. A temporary ontology until a more formal appropriate ontology an be identified.');
+  chado_insert_cv('tripal_pub', 'Tripal Publication Ontology. A temporary ontology until a more formal appropriate ontology an be identified.');
 
   // make sure we have our supported databases
   chado_insert_db(
@@ -1962,7 +1962,7 @@ function tripal_chado_populate_vocab_NCIT() {
     'ncit',
     'The NCIt OBO Edition project aims to increase integration of the NCIt with OBO Library ontologies. NCIt is a reference terminology that includes broad coverage of the cancer domain, including cancer related diseases, findings and abnormalities. NCIt OBO Edition releases should be considered experimental.'
   );
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C25164',
     'name' => 'Date',
@@ -1972,7 +1972,7 @@ function tripal_chado_populate_vocab_NCIT() {
   chado_associate_semweb_term('assay', 'assaydate', $term);
   chado_associate_semweb_term('acquisition', 'acquisitiondate', $term);
   chado_associate_semweb_term('quantification', 'quantificationdate', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C48036',
     'name' => 'Operator',
@@ -1980,7 +1980,7 @@ function tripal_chado_populate_vocab_NCIT() {
     'definition' => 'A person that operates some apparatus or machine',
   ));
   chado_associate_semweb_term(NULL, 'operator_id', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C45378',
     'name' => 'Technology Platform',
@@ -1988,7 +1988,7 @@ function tripal_chado_populate_vocab_NCIT() {
     'definition' => 'The specific version (manufacturer, model, etc.) of a technology that is used to carry out a laboratory or computational experiment.',
   ));
   chado_associate_semweb_term('arraydesign', 'platformtype_id', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C25712',
     'name' => 'Value',
@@ -1996,7 +1996,7 @@ function tripal_chado_populate_vocab_NCIT() {
     'definition' => 'A numerical quantity measured or assigned or computed.',
   ));
   chado_associate_semweb_term(NULL, 'value', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C44170',
     'name' => 'Channel',
@@ -2004,7 +2004,7 @@ function tripal_chado_populate_vocab_NCIT() {
     'definition' => 'An independent acquisition scheme, i.e., a route or conduit through which flows data consisting of one particular measurement using one particular parameter.',
   ));
   chado_associate_semweb_term(NULL, 'channel_id', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C48697',
     'name' => 'Controlled Vocabulary',
@@ -2012,7 +2012,7 @@ function tripal_chado_populate_vocab_NCIT() {
     'definition' => 'A set of terms that are selected and defined based on the requirements set out by the user group, usually a set of vocabulary is chosen to promote consistency across data collection projects. [ NCI ]',
   ));
   chado_associate_semweb_term(NULL, 'cv_id', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C45559',
     'name' => 'Term',
@@ -2020,7 +2020,7 @@ function tripal_chado_populate_vocab_NCIT() {
     'definition' => 'A word or expression used for some particular thing. [ NCI ]',
   ));
   chado_associate_semweb_term(NULL, 'cvterm_id', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C80488',
     'name' => 'Expression',
@@ -2028,7 +2028,7 @@ function tripal_chado_populate_vocab_NCIT() {
     'definition' => 'A combination of symbols that represents a value. [ NCI ]',
   ));
   chado_associate_semweb_term(NULL, 'expression_id', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C16977',
     'name' => 'Phenotype',
@@ -2036,7 +2036,7 @@ function tripal_chado_populate_vocab_NCIT() {
     'definition' => 'The assemblage of traits or outward appearance of an individual. It is the product of interactions between genes and between genes and the environment. [ NCI ]',
   ));
   chado_associate_semweb_term(NULL, 'phenotype_id', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C16631',
     'name' => 'Genotype',
@@ -2044,7 +2044,7 @@ function tripal_chado_populate_vocab_NCIT() {
     'definition' => 'The genetic constitution of an organism or cell, as distinct from its expressed features or phenotype. [ NCI ]',
   ));
   chado_associate_semweb_term(NULL, 'genotype_id', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C25341',
     'name' => 'Location',
@@ -2052,7 +2052,7 @@ function tripal_chado_populate_vocab_NCIT() {
     'definition' => 'A position, site, or point in space where something can be found. [ NCI ]',
   ));
   chado_associate_semweb_term(NULL, 'nd_geolocation_id', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C802',
     'name' => 'Reagent',
@@ -2060,7 +2060,7 @@ function tripal_chado_populate_vocab_NCIT() {
     'definition' => 'Any natural or synthetic substance used in a chemical or biological reaction in order to produce, identify, or measure another substance. [ NCI ]',
   ));
   chado_associate_semweb_term(NULL, 'nd_reagent_id', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C16551',
     'name' => 'Environment',
@@ -2068,7 +2068,7 @@ function tripal_chado_populate_vocab_NCIT() {
     'definition' => 'The totality of surrounding conditions. [ NCI ]',
   ));
   chado_associate_semweb_term(NULL, 'environment_id', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C42765',
     'name' => 'Tree Node',
@@ -2076,7 +2076,7 @@ function tripal_chado_populate_vocab_NCIT() {
     'definition' => 'A term that refers to any individual item or entity in a hierarchy or pedigree. [ NCI ]',
   ));
   chado_associate_semweb_term(NULL, 'phylonode_id', $term);
-     
+
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C15320',
     'name' => 'Study Design',
@@ -2084,10 +2084,10 @@ function tripal_chado_populate_vocab_NCIT() {
     'definition' => 'A plan detailing how a study will be performed in order to represent the phenomenon under examination, to answer the research questions that have been asked, and defining the methods of data analysis. Study design is driven by research hypothesis being posed, study subject/population/sample available, logistics/resources: technology, support, networking, collaborative support, etc. [ NCI ]',
   ));
   chado_associate_semweb_term(NULL, 'studydesign_id', $term);
-  
+
   // The Company term is missing for the Tripal Contact ontology, but is
   // useful for the arraydesign.manufacturer which is an FK to Contact.
-  // It seems better to use a term from a curated ontology than to add to 
+  // It seems better to use a term from a curated ontology than to add to
   // Tripal Contact.
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C54131',
@@ -2095,7 +2095,7 @@ function tripal_chado_populate_vocab_NCIT() {
     'cv_name' => 'ncit',
     'definition' => 'Any formal business entity for profit, which may be a corporation, a partnership, association or individual proprietorship.',
   ));
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C47885',
     'name' => 'Project',
@@ -2103,28 +2103,28 @@ function tripal_chado_populate_vocab_NCIT() {
     'definition' => 'Any specifically defined piece of work that is undertaken or attempted to meet a single requirement.',
   ));
   chado_associate_semweb_term(NULL, 'project_id', $term);
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C16223',
     'name' => 'DNA Library',
     'cv_name' => 'ncit',
     'definition' => 'A collection of DNA molecules that have been cloned in vectors.',
   ));
-    
+
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C85496',
     'name' => 'Trait',
     'cv_name' => 'ncit',
     'definition' => 'Any genetically determined characteristic.',
   ));
-  
+
   $term = chado_insert_cvterm(array(
     'id' => 'NCIT:C25693',
     'name' => 'Subgroup',
     'cv_name' => 'ncit',
     'definition' => 'A subdivision of a larger group with members often exhibiting similar characteristics. [ NCI ]',
   ));
-  
+
 }
 
 /**

+ 19 - 19
tripal_chado/tripal_chado.install

@@ -30,7 +30,7 @@ function tripal_chado_install() {
     ";
     chado_query($sql);
   }
-  
+
   tripal_insert_variable('bundle_category', 'Bundles can be categorized to allow for grouping');
 }
 
@@ -624,7 +624,7 @@ function tripal_chado_chado_bundle_schema() {
       ),
       'base_type_id' => array(
         'description' => 'If a property table is used for a linker, and if the base table requires a type_id then this is the type that should be used on insert of new records in the base table.',
-        'size' => 'big',        
+        'size' => 'big',
         'type' => 'int',
       )
     ),
@@ -1394,11 +1394,11 @@ function tripal_chado_update_7321() {
   try {
     chado_insert_db(array(
       'name' => 'TPUB',
-      'description' => 'Tripal Publiation Ontology. A temporary ontology until a more formal appropriate ontology an be identified.',
+      'description' => 'Tripal Publication Ontology. A temporary ontology until a more formal appropriate ontology an be identified.',
       'url' => '/cv/lookup/TPUB',
       'urlprefix' => '/cv/lookup/TPUB/{accession}',
     ));
-    chado_insert_cv('tripal_pub', 'Tripal Publiation Ontology. A temporary ontology until a more formal appropriate ontology an be identified.');
+    chado_insert_cv('tripal_pub', 'Tripal Publication Ontology. A temporary ontology until a more formal appropriate ontology an be identified.');
 
     chado_insert_db(array(
       'name' => 'rdf',
@@ -1439,7 +1439,7 @@ function tripal_chado_update_7322() {
   try {
     chado_insert_db(array(
       'name' => 'TPUB',
-      'description' => 'Tripal Publiation Ontology. A temporary ontology until a more formal appropriate ontology an be identified.',
+      'description' => 'Tripal Publication Ontology. A temporary ontology until a more formal appropriate ontology an be identified.',
       'url' => 'cv/lookup/TPUB',
       'urlprefix' => 'cv/lookup/TPUB/{accession}',
     ));
@@ -1578,7 +1578,7 @@ function tripal_chado_update_7328() {
     tripal_chado_add_cv_root_mview_mview();
     $mview_id = chado_get_mview_id($mv_name);
     chado_populate_mview($mview_id);
-  } 
+  }
   catch (\PDOException $e) {
     $error = $e->getMessage();
     throw new DrupalUpdateException('Could not perform update: '. $error);
@@ -1608,7 +1608,7 @@ function tripal_chado_update_7329() {
 function tripal_chado_update_7330() {
   try {
     tripal_insert_variable('bundle_category', 'Bundles can be categorized to allow for grouping');
-    
+
     $bundles = [
       'General' => [
         // Organism
@@ -1624,7 +1624,7 @@ function tripal_chado_update_7330() {
         // Publication
         'TPUB:0000002',
         // Protocol
-        'sep:00101',        
+        'sep:00101',
       ],
       'Genomic' => [
         // Gene
@@ -1642,7 +1642,7 @@ function tripal_chado_update_7330() {
         // Genome Annotation
         'operation:0362',
         // Genome Project
-        'local:Genome Project',                
+        'local:Genome Project',
       ],
       'Genetic' => [
         // Genetic Map
@@ -1654,7 +1654,7 @@ function tripal_chado_update_7330() {
         // Genetic Marker
         'SO:0001645',
         // Heritable Phenotypic Marker
-        'SO:0001500',        
+        'SO:0001500',
       ],
       'Germplasm/Breeding' => [
         // Germplasm Accession
@@ -1664,14 +1664,14 @@ function tripal_chado_update_7330() {
         // Cutlivar
         'CO_010:0000029',
         // Recombinant Inbred Line
-        'CO_010:0000162',        
+        'CO_010:0000162',
       ],
       'Expression' => [
         // Biological Sample
         'sep:00195',
         // Assay
         'OBI:0000070',
-        // Array Design 
+        // Array Design
         'EFO:0000269',
       ]
     ];
@@ -1681,8 +1681,8 @@ function tripal_chado_update_7330() {
         if ($bundle) {
           tripal_set_bundle_variable('bundle_category', $bundle->id, $category);
         }
-      }      
-    }    
+      }
+    }
   }
   catch (\PDOException $e) {
     $error = $e->getMessage();
@@ -1726,15 +1726,15 @@ function tripal_chado_update_7332() {
       'ncit',
       'The NCIt OBO Edition project aims to increase integration of the NCIt with OBO Library ontologies. NCIt is a reference terminology that includes broad coverage of the cancer domain, including cancer related diseases, findings and abnormalities. NCIt OBO Edition releases should be considered experimental.'
     );
-    
+
     $term = chado_insert_cvterm([
       'id' => 'NCIT:C25693',
       'name' => 'Subgroup',
       'cv_name' => 'ncit',
       'definition' => 'A subdivision of a larger group with members often exhibiting similar characteristics. [ NCI ]',
     ]);
-    
-    
+
+
     // Add the rdfs:comment vocabulary.
     chado_insert_db(array(
       'name' => 'rdfs',
@@ -1776,7 +1776,7 @@ function tripal_chado_update_7333() {
  * SQL Fix for the db2cv_mview materialized view.
  */
 function tripal_chado_update_7334() {
-  
+
   try {
     $query = '
        SELECT DISTINCT CV.cv_id, CV.name as cvname, DB.db_id, DB.name as dbname,
@@ -1824,4 +1824,4 @@ function tripal_chado_update_7335() {
     $error = $e->getMessage();
     throw new DrupalUpdateException('Could not perform update: '. $error);
   }
-}
+}

+ 32 - 36
tripal_ws/api/tripal_ws.api.inc

@@ -60,7 +60,7 @@ function hook_tripal_ws_value(&$items, $field, $instance) {
  *
  * @return
  *   A list of TripalWebService names.
- * 
+ *
  * @ingroup tripal_ws_api
  */
 function tripal_get_web_services() {
@@ -91,19 +91,15 @@ function tripal_get_web_services() {
  *
  * @return
  *   TRUE if the field type class file was found, FALSE otherwise.
- * 
+ *
  * @ingroup tripal_ws_api
  */
 function tripal_load_include_web_service_class($class) {
-
   $modules = module_list(TRUE);
   foreach ($modules as $module) {
-    $file_path = realpath(".") . '/' . drupal_get_path('module', $module) . '/includes/TripalWebService/' . $class . '.inc';
-    if (file_exists($file_path)) {
-      module_load_include('inc', $module, 'includes/TripalWebService/' . $class);
-      if (class_exists($class)) {
-        return TRUE;
-      }
+    module_load_include('inc', $module, 'includes/TripalWebService/' . $class);
+    if (class_exists($class)) {
+      return TRUE;
     }
   }
   return FALSE;
@@ -124,7 +120,7 @@ function tripal_load_include_web_service_class($class) {
  *
  * @return
  *   TRUE if the site is successfully added, FALSE otherwise.
- * 
+ *
  * @ingroup tripal_ws_api
  */
 function tripal_add_site($name, $url, $version, $description) {
@@ -180,7 +176,7 @@ function tripal_add_site($name, $url, $version, $description) {
  *
  * @return
  *   TRUE if the record was successfully deleted, FALSE otherwise.
- * 
+ *
  * @ingroup tripal_ws_api
  */
 function tripal_remove_site($record_id) {
@@ -196,7 +192,7 @@ function tripal_remove_site($record_id) {
 
 /**
  * Constructs a URL for a remote Tripal web service.
- * 
+ *
  * @param $remote_site
  *   A remote Tripal site object.
  * @param $path
@@ -205,8 +201,8 @@ function tripal_remove_site($record_id) {
  *   leave this paramter empty.
  * @param $query
  *   An query string to appear after the ? in a URL.
- *   
- * @return  
+ *
+ * @return
  *   The full URL within the content service.
  */
 function tripal_build_remote_content_url($remote_site, $path = '', $query = '') {
@@ -215,12 +211,12 @@ function tripal_build_remote_content_url($remote_site, $path = '', $query = '')
   $ws_url = $remote_site->url;
   $ws_url = trim($ws_url, '/');
   $ws_url .= '/web-services/content/' . $ws_version . '/' . $path;
-  
+
   // Build the Query and make and substitions needed.
   if ($query) {
     $ws_url = $ws_url . '?' . $query;
   }
-  
+
   return $ws_url;
 }
 
@@ -238,7 +234,7 @@ function tripal_build_remote_content_url($remote_site, $path = '', $query = '')
  *
  * @return
  *   The JSON response formatted in a PHP array or FALSE if a problem occured.
- * 
+ *
  * @ingroup tripal_ws_api
  */
 function tripal_get_remote_content($site_id, $path = '', $query = '') {
@@ -262,7 +258,7 @@ function tripal_get_remote_content($site_id, $path = '', $query = '') {
     _tripal_report_ws_error($data);
     return $data;
   }
-  
+
   // Make the remote query.
   $ws_url = tripal_build_remote_content_url($remote_site, $path, $query);
   $data = drupal_http_request($ws_url);
@@ -287,7 +283,7 @@ function tripal_get_remote_content($site_id, $path = '', $query = '') {
 
   // We got a response, so convert it to a PHP array.
   $data = drupal_json_decode($data->data);
-  
+
   // Check if there was a Tripal Web Services error.
   if (array_key_exists('error', $data)) {
     _tripal_report_ws_error($data);
@@ -298,12 +294,12 @@ function tripal_get_remote_content($site_id, $path = '', $query = '') {
 
 /**
  * A helper function for reporting an error when retrieving remote content.
- * 
+ *
  * @param $data
  *   A data array containing at a minimum the 'error' key containing the
  *   error message.
  */
-function _tripal_report_ws_error($data) {  
+function _tripal_report_ws_error($data) {
   $error = '</pre>' . print_r($data['error'], TRUE) . '</pre>';
   tripal_report_error('tripal_ws', TRIPAL_ERROR,
     'Tripal remote web services reports the following error: !error.',
@@ -323,7 +319,7 @@ function _tripal_report_ws_error($data) {
  * @return
  *   The JSON-LD context mapping array, or FALSE if the context could not
  *   be retrieved.
- * 
+ *
  * @ingroup tripal_ws_api
  */
 function tripal_get_remote_context($context_url, $cache_id) {
@@ -374,7 +370,7 @@ function tripal_get_remote_context($context_url, $cache_id) {
  *   the Class (i.e. content type).
  * @return
  *   The JSON-LD context mapping array.
- * 
+ *
  * @ingroup tripal_ws_api
  */
 function tripal_get_remote_content_context($site_id, $context_url, $bundle_accession, $field_accession = '') {
@@ -392,7 +388,7 @@ function tripal_get_remote_content_context($site_id, $context_url, $bundle_acces
  *
  * @param $site_id
  *   The numeric site ID for the remote Tripal site.
- * 
+ *
  * @ingroup tripal_ws_api
  */
 function tripal_clear_remote_cache($site_id) {
@@ -410,7 +406,7 @@ function tripal_clear_remote_cache($site_id) {
  * @return
  *   The vocabulary of a remote Tripal web service, or FALSE if an error
  *   occured.
- * 
+ *
  * @ingroup tripal_ws_api
  */
 function tripal_get_remote_API_doc($site_id) {
@@ -498,7 +494,7 @@ function tripal_get_remote_API_doc($site_id) {
  * @return
  *    An array of fake entity objects where the key is the entity_id and
  *    the value is the object.
- * 
+ *
  * @ingroup tripal_ws_api
  */
 function tripal_load_remote_entities($remote_entity_ids, $site_id, $bundle_accession, $field_ids) {
@@ -531,7 +527,7 @@ function tripal_load_remote_entities($remote_entity_ids, $site_id, $bundle_acces
     '&fields=' . urlencode(implode(",", $field_ids));
 
   $results = tripal_get_remote_content($site_id, $bundle_accession, $query);
-  
+
   // If we encountered an error just return;
   if (array_key_exists('error', $results)) {
     return FALSE;
@@ -591,7 +587,7 @@ function tripal_load_remote_entities($remote_entity_ids, $site_id, $bundle_acces
  *   be added to the entity returned.
  * @return
  *    A fake entity object.
- * 
+ *
  * @ingroup tripal_ws_api
  */
 function tripal_load_remote_entity($remote_entity_id, $site_id, $bundle_accession, $field_ids) {
@@ -601,7 +597,7 @@ function tripal_load_remote_entity($remote_entity_id, $site_id, $bundle_accessio
 
   // Get the remote entity and create the fake entity.
   $remote_entity = tripal_get_remote_content($site_id, $bundle_accession . '/' . $remote_entity_id);
-  
+
   // If we encountered an error just return;
   if (array_key_exists('error', $results)) {
     return FALSE;
@@ -700,7 +696,7 @@ function tripal_load_remote_entity($remote_entity_id, $site_id, $bundle_accessio
  *   The field array as returned by web services.
  * @param $context
  *   The web service JSON-LD context for the bundle to which the field belongs.
- * 
+ *
  * @ingroup tripal_ws_api
  */
 function _tripal_update_remote_entity_field($field_data, $context, $depth = 0) {
@@ -764,7 +760,7 @@ function _tripal_update_remote_entity_field($field_data, $context, $depth = 0) {
  * @return
  *   An array similar to that returned by the field_info_field function
  *   of Drupal for local fields.
- * 
+ *
  * @ingroup tripal_ws_api
  */
 function tripal_get_remote_field_info($site_id, $bundle_accession, $field_accession){
@@ -806,7 +802,7 @@ function tripal_get_remote_field_info($site_id, $bundle_accession, $field_access
  * @return
  *   An array similar to that returned by the field_info_instance function
  *   of Drupal for local fields.
- * 
+ *
  * @ingroup tripal_ws_api
  */
 function tripal_get_remote_field_instance_info($site_id, $bundle_accession, $field_accession){
@@ -851,7 +847,7 @@ function tripal_get_remote_field_instance_info($site_id, $bundle_accession, $fie
  * @return
  *   A PHP array corresponding to the Hydra Class stanza (i.e. a content type).
  *   Returns NULL if the class ID cannot be found.
- * 
+ *
  * @ingroup tripal_ws_api
  */
 function tripal_get_remote_content_doc($site_id, $bundle_accession){
@@ -888,7 +884,7 @@ function tripal_get_remote_content_doc($site_id, $bundle_accession){
  *   A PHP array corresponding to the Hydra property stanza (field) that
  *   belongs to the given Class (i.e. a content type).  Retruns NULL if the
  *   property cannot be found.
- * 
+ *
  * @ingroup tripal_ws_api
  */
 function tripal_get_remote_field_doc($site_id, $bundle_accession, $field_accession){
@@ -926,7 +922,7 @@ function tripal_get_remote_field_doc($site_id, $bundle_accession, $field_accessi
  * @return
  *   An array of field downloader class names that are compoatible with the
  *   field and which exist on this site.
- * 
+ *
  * @ingroup tripal_ws_api
  */
 function tripal_get_remote_field_formatters($site_id, $bundle_accession, $field_accession) {
@@ -947,4 +943,4 @@ function tripal_get_remote_field_formatters($site_id, $bundle_accession, $field_
     }
   }
   return $flist;
-}
+}

+ 79 - 32
tripal_ws/includes/TripalFields/remote__data/remote__data.inc

@@ -320,7 +320,7 @@ class remote__data extends WebServicesField {
       ->execute()->fetchAll();
 
     foreach ($sites as $site) {
-      $rows[$site->id] =$site->name;
+      $rows[$site->id] = $site->name;
     }
 
     $element['data_info']['remote_site'] = array(
@@ -333,43 +333,44 @@ class remote__data extends WebServicesField {
     $element['data_info']['query'] = array(
       '#type' => 'textarea',
       '#title' => 'Query to Execute',
-      '#description' => 'Enter the query that will retreive the remote records. ' .
-        'If the full URL to the content web service is ' .
-        'https://[tripal_site]/web-services/content/v0.1/. Then this field should ' .
-        'contain the text immediately after the content/v0.1 portion of the URL. ' .
-        'For information about building web services queries see the ' .
-        'online documentation at ' . l('The Tripal v3 User\'s Guide', 'http://tripal.info/tutorials/v3.x/web-services') . '. ' .
-        'For example, suppose this field is attached to an ' .
-        'Organism content type on the local site, and you want to retrieve a ' .
-        'field for the same organism on a remote Tripal site then you will ' .
-        'want to query on the genus and species. Also, you want the genus and ' .
-        'species to match the organism that this field is attached to. You can ' .
-        'use tokens to do this (see the "Available Tokesn" fieldset below). ' .
-        'For this example, the query text should be ' .
-        'Organism?genus=[taxrank__genus]&species=[taxrank__species].',
+      '#description' => 'Enter the query that will retreive the remote records. ',
       '#default_value' => $this->instance['settings']['data_info']['query'],
-      '#rows' => 5,
-      '#required' => TRUE
-    );
-    $element['data_info']['rd_field_name'] = array(
-      '#type' => 'textfield',
-      '#title' => 'Field to Display',
-      '#description' => 'The results returned by the query should match
-        entities (or records) from the selected remote site.  That entity
-        will have multiple fields. Only one remote field can be shown by
-        this field. Please enter the name of the field you would like
-        to display.  Some fields have "subfields".  You can display a subfield
-        rather than the entire field by entering a comma-separated sequence
-        of subfields.  For example, for relationships, you may only want to
-        show the "clause", therefore, the entry here would be: realtionship,clause.',
-      '#default_value' => $this->instance['settings']['data_info']['rd_field_name'],
+      '#rows' => 3,
       '#required' => TRUE
     );
+    $element['data_info']['query_instructions'] = [
+      '#type' => 'fieldset',
+      '#title' => 'Query to Execute Instructions',
+      '#description' => 'If the full URL to the remote tripal content web '.
+        'service is "https://[tripal_site]/web-services/content/v0.1/". Then '.
+        'this field should contain the text immediately after the '.
+        '"content/v0.1" portion of the URL. For information about building '.
+        'web services queries see the online documentation at '.
+        l('The Tripal v3 User\'s Guide', 'http://tripal.info/tutorials/v3.x/web-services').
+        '. For example, suppose this field is attached to an Organism content '.
+        'type on the local site, and you want to retrieve a field for the '.
+        'same organism on a remote Tripal site. To retrieve the matching '.
+        'record, you will want to query on the genus and species, since it '.
+        'is unique and, you want them to match the organism for each specific '.
+        'local organism page. You can use tokens to do this (see the '.
+        '"Available Tokens" fieldset below). For this example, the full '.
+        'remote web service endpoint would be '.
+        '"https://[tripal_site]/web-services/content/v0.1/Organism" '.
+        'and the query text should be '.
+        '"Organism?genus=[taxrank__genus]&species=[taxrank__species]".',
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE
+    ];
+    $element['data_info']['query_instructions']['instructions'] = [
+      '#type' => 'markup',
+      '#markup' => '',      
+    ];
+    
     $element['data_info']['token_display']['tokens'] = array(
       '#type' => 'hidden',
       '#value' => serialize($tokens)
     );
-
+    
     $element['data_info']['token_display'] = array(
       '#type' => 'fieldset',
       '#title' => 'Available Tokens',
@@ -377,11 +378,57 @@ class remote__data extends WebServicesField {
       '#collapsible' => TRUE,
       '#collapsed' => TRUE
     );
-
+    
     $element['data_info']['token_display']['content'] = array(
       '#type' => 'item',
       '#markup' => theme_token_list($tokens),
     );
+    
+    $element['data_info']['rd_field_name'] = array(
+      '#type' => 'textfield',
+      '#title' => 'Field to Display',
+      '#description' => 'Enter the key from the results returned by the "Query to Execute" that should be displayed. See the example below for more details.',
+      '#default_value' => $this->instance['settings']['data_info']['rd_field_name'],
+      '#required' => TRUE
+    );
+    
+    $element['data_info']['rd_field_name_instructions'] = [
+      '#type' => 'fieldset',
+      '#title' => 'Field to Display Instructions',
+      '#description' => '',
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE
+    ];
+    $element['data_info']['rd_field_name_instructions']['insructions'] = [
+      '#type' => 'markup',
+      '#markup' => 'The query from a Tripal web service response is always
+       in JSON array arranged in key/value pairs.  The key is the name of a
+       controlled vocabulary term.  
+       <br><br>Suppose you want to query details about an organism.
+       Consider the following JSON result 
+       <pre style="height: 200px; overflow: auto;">   
+        "@type": "organism",
+        "label": "Anopheles gambiae",
+        "ItemPage": "http://demo.tripal.info/3.x/bio_data/642",
+        "type": "Organism",
+        "abbreviation": "A.gambiae",
+        "genus": "Anopheles",
+        "species": "gambiae",
+        "common_name": "mosquito",
+        "database_cross_reference": "http://demo.tripal.info/3.x/web-services/content/v0.1/Organism/642/database+cross+reference",
+        "equivalent_name": "Anopheles gambiae sensu stricto",
+        "division": "Invertebrates",
+        "mitochondrial_genetic_code_name": "Invertebrate Mitochondrial",
+        "synonym": "Anopheles gambiae S",
+        "genetic_code": "1",
+        "lineage": "cellular organisms; Eukaryota; Opisthokonta; Metazoa; Eumetazoa; Bilateria; Protostomia; Ecdysozoa; Panarthropoda; Arthropoda; Mandibulata; Pancrustacea; Hexapoda; Insecta; Dicondylia; Pterygota; Neoptera; Holometabola; Diptera; Nematocera; Culicomorpha; Culicoidea; Culicidae; Anophelinae; Anopheles; Cellia; Pyretophorus; gambiae species complex",
+        "genetic_code_name": "Standard",
+        "genbank_common_name": "African malaria mosquito"
+       </pre> 
+       To display the "common_name" from the JSON above you would enter the word
+       "common_name" in the Field to Display textbox.
+      ',
+    ];
 
     $element['data_info']['description'] = array(
       '#type' => 'textarea',

+ 1 - 1
tripal_ws/includes/TripalWebService/TripalContentService_v0_1.inc

@@ -420,7 +420,7 @@ class TripalContentService_v0_1 extends TripalWebService {
         $i++;
       }
       if ($expfield) {
-        $resource = $response;
+        $this->resource = $response;
       }
       else {
         //$this->resource->addProperty($key, $response);

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff