فهرست منبع

OBO Loader all functions complete. Restored chado_insert_cvterm

Stephen Ficklin 6 سال پیش
والد
کامیت
c8dd07b801

+ 7 - 1
tripal/includes/TripalImporter.inc

@@ -204,6 +204,11 @@ class TripalImporter {
   protected $is_prepared;
 
 
+  /**
+   * Stores the last percentage that progress was reported.
+   * @var integer
+   */
+  protected $reported = 0;
 
   // --------------------------------------------------------------------------
   //                          CONSTRUCTORS
@@ -644,7 +649,7 @@ class TripalImporter {
     $ipercent  = (int) $percent;
     
     // If we've reached our interval then print update info.
-    if ($ipercent > 0 and $ipercent % $this->interval == 0) {
+    if ($ipercent > 0 and $ipercent != $this->reported and $ipercent % $this->interval == 0) {
       $memory = number_format(memory_get_usage());
       $spercent = sprintf("%.2f", $percent);
       print t("Percent complete: !percent %. Memory: !memory bytes.", 
@@ -654,6 +659,7 @@ class TripalImporter {
       if ($this->job) {
         $this->job->setProgress($percent);
       }
+      $this->reported = $ipercent;
     }
   }
 

+ 4 - 1
tripal_chado/api/ChadoRecord.inc

@@ -134,7 +134,7 @@ class ChadoRecord {
   public function getSchema() {
     return $this->schema;
   }
-  
+
   /**
    * Creates a clone of this record, but without the record ID.
    * 
@@ -208,6 +208,9 @@ class ChadoRecord {
       implode(", ", $insert_vals) . ')';
     try {
       chado_query($sql, $insert_args);
+      // TODO: we can speed up inserts if we can find a way to not have to
+      // run the find(), but get the newly inserted record_id directly
+      // from the insert command.
       $this->find();
     }
     catch (Exception $e) {

+ 233 - 152
tripal_chado/api/modules/tripal_chado.cv.api.inc

@@ -924,12 +924,11 @@ function chado_insert_cv($name, $definition) {
  *      automatically updated.
  *
  * @return
- *   A cvterm object.
+ *   A cvterm object
  *
  * @ingroup tripal_chado_cv_api
  */
 function chado_insert_cvterm($term, $options = array()) {
-
   // Get the term properties.
   $id = (isset($term['id'])) ? $term['id'] : '';
   $name = '';
@@ -937,7 +936,6 @@ function chado_insert_cvterm($term, $options = array()) {
   $definition = '';
   $is_obsolete = 0;
   $accession = '';
-
   // Set Defaults.
   if (isset($term['cv_name'])) {
     $cvname = $term['cv_name'];
@@ -950,36 +948,30 @@ function chado_insert_cvterm($term, $options = array()) {
   if (array_key_exists('namespace', $term)) {
     $cvname = $term['namespace'];
   }
-
   if (isset($term['is_relationship'])) {
     $is_relationship = $term['is_relationship'];
   }
   else {
     $is_relationship = 0;
   }
-
   if (isset($term['db_name'])) {
     $dbname = $term['db_name'];
   }
   else {
     $dbname = 'internal';
   }
-
   if (isset($options['update_existing'])) {
     $update = $options['update_existing'];
   }
   else {
-    $update = TRUE;
+    $update = 1;
   }
-
   if (array_key_exists('name', $term)) {
     $name = $term['name'];
   }
   else {
     $name = $id;
   }
-
-
   if (array_key_exists('definition', $term)) {
     $definition = preg_replace('/^\"(.*)\"/', '\1', $term['definition']);
   }
@@ -991,179 +983,268 @@ function chado_insert_cvterm($term, $options = array()) {
     if (strcmp($is_obsolete, 'true') == 0) {
       $is_obsolete = 1;
     }
-    else {
-      $is_obsolete = 0;
-    }
   }
   if (!$name and !$id) {
-    tripal_report_error('tripal_cv', TRIPAL_ERROR, 
-      "Please provide an 'id' or 'name' argument to the chado_insert_cvterm() function");
-    return FALSE;
+    tripal_report_error('tripal_cv', TRIPAL_WARNING, "Cannot find cvterm without 'id' or 'name'", NULL);
+    return 0;
   }
   if (!$id) {
     $id = $name;
   }
-
   // Get the accession and the database from the cvterm id.
   if ($dbname) {
     $accession = $id;
   }
-
   if (preg_match('/^.+?:.*$/', $id)) {
     $accession = preg_replace('/^.+?:(.*)$/', '\1', $id);
     $dbname = preg_replace('/^(.+?):.*$/', '\1', $id);
   }
-
   // Check that we have a database name, give a different message if it's a
   // relationship.
   if ($is_relationship and !$dbname) {
-    tripal_report_error('tripal_cv', TRIPAL_WARNING, "A database name is not provided for this relationship term: !id", 
-      ['!id' => $id]);
-    return FALSE;
+    tripal_report_error('tripal_cv', TRIPAL_WARNING, "A database name is not provided for this relationship term: $id", NULL);
+    return 0;
   }
   if (!$is_relationship and !$dbname) {
-    tripal_report_error('tripal_cv', TRIPAL_WARNING, "A database identifier is missing from the term: !id", 
-      ['!id' => $id]);
-    return FALSE;
-  }
-
-  // Make sure the CV exists before we try to insert the cvterm. The cv table
-  // has a unique constraint on the name so we'll always get a 1 or 0 returned
-  // by the ->find function.
-  $cv = new ChadoRecord('cv');
-  $cv->setValues(['name' => $cvname]);
-  if (!$cv->find()) {
-    $cv->insert();
-  }
-  
-  // Make sure the DB exists before we try to insert the cvterm. The db table
-  // has a unique constraint on the name so we'll always get a 1 or 0 returned
-  // by the ->find function.
-  $db = new ChadoRecord('db');
-  $db->setValues(['name' => $dbname]);
-  if (!$db->find()) {
-    $db->insert();
-  }
-
-  // The cvterm table has two unique dependencies:
-  //
-  // 1) name, cv_id, is_obsolete columns must be unique
-  // 2) the dbxref_id column must be unique.
-  // 
-  // First, we'll make sure the first is unique.
-  $cvterm = new ChadoRecord('cvterm');
-  $cvterm->setValues([
+    tripal_report_error('tripal_cv', TRIPAL_WARNING, "A database identifier is missing from the term: $id", NULL);
+    return 0;
+  }
+  // Make sure the CV name exists.
+  $cv = chado_get_cv(array('name' => $cvname));
+  if (!$cv) {
+    $cv = chado_insert_cv($cvname, '');
+  }
+  if (!$cv) {
+    tripal_report_error('tripal_cv', TRIPAL_WARNING, "Cannot find namespace '$cvname' when adding/updating $id", NULL);
+    return 0;
+  }
+  // This SQL statement will be used a lot to find a cvterm so just set it
+  // here for easy reference below.  Because CV terms can change their names
+  // but accessions don't change, the following SQL finds cvterms based on
+  // their accession rather than the name.
+  $cvtermsql = "
+    SELECT CVT.name, CVT.cvterm_id, CV.cv_id, CV.name as cvname,
+      DB.name as dbname, DB.db_id, DBX.accession
+    FROM {cvterm} CVT
+      INNER JOIN {dbxref} DBX on CVT.dbxref_id = DBX.dbxref_id
+      INNER JOIN {db} DB on DBX.db_id = DB.db_id
+      INNER JOIN {cv} CV on CV.cv_id = CVT.cv_id
+    WHERE DBX.accession = :accession and DB.name = :name
+  ";
+  // Add the database. The function will just return the DB object if the
+  // database already exists.
+  $db = chado_get_db(array('name' => $dbname));
+  if (!$db) {
+    $db = chado_insert_db(array('name' => $dbname));
+  }
+  if (!$db) {
+    tripal_report_error('tripal_cv', TRIPAL_WARNING, "Cannot find database '$dbname' in Chado.", NULL);
+    return 0;
+  }
+  // The cvterm table has two unique dependencies. We need to check both.
+  // first check the (name, cv_id, is_obsolete) constraint.
+  $values = array(
     'name' => $name,
     'is_obsolete' => $is_obsolete,
-    'cv_id' => $cv->getID(),
-  ]);
-  
-  // If we found a match?
-  if ($cvterm->find()) {
-    
-    // Sometimes an OBO can define a term multiple times but with different 
-    // accessions.  If this is the case we can't do an insert or it will 
-    // violate the constraint in the cvterm table. So we'll need to add a 
-    // new dbxref and update.
-    
-    // Get the dbxref record for this cvterm so we can check if the 
-    // accession may have changed.
-    $dbxref = new ChadoRecord('dbxref');
-    $dbxref->setValues(['dbxref_id' => $cvterm->getValue('dbxref_id')]);
-    $dbxref->find();
-
-    // Check that the accession matches.  
-    $accession_change = FALSE;
-    $db_check = new ChadoRecord('db');
-    $db_check->setValues(['db_id' => $dbxref->getValue('db_id')]);
-    $db_check->find();
-    if ($dbxref->getValue('accession') != $accession or 
-        $db_check->getValue('db_id') != $db->getValue('db_id')) {
-      
-      // We need a new dbxref record.
-      $dbxref = new ChadoRecord('dbxref');
-      $dbxref->setValues([
-        'accession' => $accession,
-        'db_id' => $db->getID()
-      ]);
-      if (!$dbxref->find()) {
-        $dbxref_new->insert();
+    'cv_id' => array(
+      'name' => $cvname,
+    ),
+  );
+  $result = chado_select_record('cvterm', array('*'), $values);
+  if (count($result) == 1) {
+    $cvterm = $result[0];
+    // Get the dbxref record.
+    $values = array('dbxref_id' => $cvterm->dbxref_id);
+    $result = chado_select_record('dbxref', array('*'), $values);
+    $dbxref = $result[0];
+    if (!$dbxref) {
+      tripal_report_error('tripal_cv', TRIPAL_ERROR,
+          'Unable to access the dbxref record for the :term cvterm. Term Record: !record',
+          array(':term' => $name, '!record' => print_r($cvterm, TRUE))
+      );
+      return FALSE;
+    }
+    // Get the db.
+    $values = array('db_id' => $dbxref->db_id);
+    $result = chado_select_record('db', array('*'), $values);
+    $db_check = $result[0];
+    //     // The database name for this existing term does not match that of the
+    //     // one provided to this function.  The CV name matches otherwise we
+    //     // wouldn't have made it this far. So, let's swap the database for
+    //     // this term.
+    //     if ($db_check->name != $db->name) {
+    //       // Look to see if the correct dbxref record already exists for this
+    //       // database.
+    //       $values = array(
+    //         'db_id' => $db->db_id,
+    //         'accession' => $accession,
+    //       );
+    //       $result = chado_select_record('dbxref', array('*'), $values);
+    //       // If we already have a good dbxref then we want to update our cvterm
+    //       // to use this dbxref.
+    //       if (count($result) > 0) {
+    //         $dbxref = $result[0];
+    //         $match = array('cvterm_id' => $cvterm->cvterm_id);
+    //         $values = array('dbxref_id' => $dbxref->dbxref_id);
+    //         $success = chado_update_record('cvterm', $match, $values);
+    //         if (!$success) {
+    //           tripal_report_error('tripal_cv', TRIPAL_WARNING, "Failed to correct the dbxref id for the cvterm " .
+    //             "'$name' (id: $accession), for database $dbname", NULL);
+    //           return 0;
+    //         }
+    //       }
+    //       // If we don't have the dbxref then we want to delete our cvterm and let
+    //       // the code below recreate it with the correct info.
+    //       else {
+    //         $match = array('cvterm_id' => $cvterm->cvterm_id);
+    //         chado_delete_record('cvterm', $match);
+    //       }
+    //     }
+    // Check that the accession matches.  Sometimes an OBO can define a term
+    // multiple times but with different accessions.  If this is the case we
+    // can't do an insert or it will violate the constraint in the cvterm table.
+    // So we'll need to add the record to the cvterm_dbxref table instead.
+    if ($dbxref->accession != $accession) {
+      // Get/add the dbxref for his term.
+      $dbxref_new = chado_insert_dbxref(array(
+        'db_id' => $db->db_id,
+        'accession' => $accession
+      ));
+      if (!$dbxref_new) {
+        tripal_report_error('tripal_cv', TRIPAL_WARNING, "Failed to find or insert the dbxref record for cvterm, " .
+            "$name (id: $accession), for database $dbname", NULL);
+        return 0;
       }
-      else {
-        // If the dbxref already exists, check constraint #2.  Check to see if
-        // the dbxref is already used by another term.
-        $cvterm_check = new ChadoRecord('cvterm');
-        $cvterm_check->setValues(['dbxref_id' => $dbxref->getID()]);
-        if ($cvterm_check->find()) {
-          tripal_report_error('tripal_cv', TRIPAL_WARNING, "The dbxref is already " .
-            "used by another cvterm record: '!other_cvterm'.  It ".
-            "cannot be used for updating the existing term: '!term'",
-            ['!other_cvterm' => $cvterm_check->getValue('name'), '!term' => $cvterm->getValue('name')]);
-          return FALSE;
+      // Check to see if the cvterm_dbxref record already exists.
+      $values = array(
+        'cvterm_id' => $cvterm->cvterm_id,
+        'dbxref_id' => $dbxref_new->dbxref_id,
+        'is_for_definition' => 1,
+      );
+      $result = chado_select_record('cvterm_dbxref', array('*'), $values);
+      // if the cvterm_dbxref record does not exists then add it
+      if (count($result)==0) {
+        $options = array(
+          'return_record' => FALSE,
+        );
+        $success = chado_insert_record('cvterm_dbxref', $values, $options);
+        if (!$success) {
+          tripal_report_error('tripal_cv', TRIPAL_WARNING, "Failed to find or insert the cvterm_dbxref record for a " .
+              "duplicated cvterm:  $name (id: $accession), for database $dbname", NULL);
+          return 0;
         }
       }
-      
-      // Update the dbxref for this record. 
-      $cvterm->setValue('dbxref_id', $dbxref->getID());
-      $accession_change == TRUE;
-    }
-    
-    // If this is an update then make the changes now.
-    if ($update) {
-      $cvterm->setValue('definition', $definition);
-      $cvterm->setValue('dbxref_id', $dbxref->getID());
-      $cvterm->setValue('is_relationshiptype', $is_relationship);
-    }
-    
-    if ($accession_change or $update) {
-      $cvterm->update();
+      // Get the original cvterm with the same name and return that.
+      $result = chado_query($cvtermsql, array(':accession' => $dbxref->accession, ':name' => $dbname));
+      $cvterm = $result->fetchObject();
+      return $cvterm;
     }
+    // Continue on, we've fixed the record if the db_id did not match.
+    // We can now perform and updated if we need to.
   }
-  // If the cvterm does not already exist, then we need to create it.
-  else {
-    
-    // Save our new dbxref record that the cvterm will use.
-    $dbxref = new ChadoRecord('dbxref');
-    $dbxref->setValues([
-      'db_id' => $db->getValue('db_id'),
+  // Get the CVterm record.
+  $result = chado_query($cvtermsql, array(':accession' => $accession, ':name' => $dbname));
+  $cvterm = $result->fetchObject();
+  if (!$cvterm) {
+    // Check to see if the dbxref exists if not, add it.
+    $dbxref = chado_insert_dbxref(array(
+      'db_id' => $db->db_id,
       'accession' => $accession
-    ]);
-    if (!$dbxref->find()) {
-      $dbxref->insert();
+    ));
+    if (!$dbxref) {
+      tripal_report_error('tripal_cv', TRIPAL_WARNING, "Failed to find or insert the dbxref record for cvterm, " .
+          "$name (id: $accession), for database $dbname", NULL);
+      return 0;
     }
+    // Check to see if the dbxref already has an entry in the cvterm table.
+    $values = array('dbxref_id' => $dbxref->dbxref_id);
+    $check = chado_select_record('cvterm', array('cvterm_id'), $values);
+    if (count($check) == 0) {
+      // now add the cvterm
+      $ins_values = array(
+        'cv_id'                => $cv->cv_id,
+        'name'                 => $name,
+        'definition'           => $definition,
+        'dbxref_id'            => $dbxref->dbxref_id,
+        'is_obsolete'          => $is_obsolete,
+        'is_relationshiptype'  => $is_relationship,
+      );
+      $success = chado_insert_record('cvterm', $ins_values);
+      if (!$success) {
+        if (!$is_relationship) {
+          tripal_report_error('tripal_cv', TRIPAL_WARNING, "Failed to insert the term: $name ($dbname)", NULL);
+          return 0;
+        }
+        else {
+          tripal_report_error('tripal_cv', TRIPAL_WARNING, "Failed to insert the relationship term: $name (cv: " . $cvname . " db: $dbname)", NULL);
+          return 0;
+        }
+      }
+    }
+    // This dbxref already exists in the cvterm table.
     else {
-      // If the dbxref already exists, check constraint #2.  Check to see if 
-      // the dbxref is already used by another term.
-      $cvterm_check = new ChadoRecord('cvterm');
-      $cvterm_check->setValues(['dbxref_id' => $dbxref->getID()]);
-      if ($cvterm_check->find()) {
-        tripal_report_error('tripal_cv', TRIPAL_WARNING, "The dbxref is already " . 
-          "used by another cvterm record: '!other_cvterm'.  It ".
-          "cannot be used for a new term: '!term'",
-          ['!other_cvterm' => $cvterm_check->getValue('name'), '!term' => $cvterm->getValue('name')]);
-        return FALSE;
+      tripal_report_error('tripal_cv', TRIPAL_WARNING, "The dbxref already exists for another cvterm record: $name (cv: " . $cvname . " db: $dbname)", NULL);
+      return 0;
+    }
+    $result = chado_query($cvtermsql, array(':accession' => $accession, ':name' => $dbname));
+    $cvterm = $result->fetchObject();
+  }
+  // Update the cvterm.
+  elseif ($update) {
+    // First, basic update of the term.
+    $match = array('cvterm_id' => $cvterm->cvterm_id);
+    $upd_values = array(
+      'name'                => $name,
+      'definition'          => $definition,
+      'is_obsolete'         => $is_obsolete,
+      'is_relationshiptype' => $is_relationship,
+    );
+    $success = chado_update_record('cvterm', $match, $upd_values);
+    if (!$success) {
+      tripal_report_error('tripal_cv', TRIPAL_WARNING, "Failed to update the term: $name", NULL);
+      return 0;
+    }
+    // Second, check that the dbxref has not changed and if it has then update 
+    // it.
+    $checksql = "
+      SELECT cvterm_id
+      FROM {cvterm} CVT
+        INNER JOIN {dbxref} DBX on CVT.dbxref_id = DBX.dbxref_id
+        INNER JOIN {db} DB on DBX.db_id = DB.db_id
+        INNER JOIN {cv} CV on CV.cv_id = CVT.cv_id
+      WHERE DBX.accession = :accession and DB.name = :dbname and CVT.name = :term and CV.name = :cvname
+    ";
+    $check = chado_query($checksql, array(':accession' => $accession, ':dbname' => $dbname, ':term' => $name, ':cvname' => $cvname))->fetchObject();
+    if (!$check) {
+      // Check to see if the dbxref exists if not, add it.
+      $dbxref = chado_insert_dbxref(array(
+        'db_id' => $db->db_id,
+        'accession' => $accession
+      ));
+      if (!$dbxref) {
+        tripal_report_error('tripal_chado', TRIPAL_WARNING, "Failed to find or insert the dbxref record for cvterm, " .
+            "$name (id: $accession), for database $dbname", NULL);
+        return 0;
+      }
+      $match = array('cvterm_id' => $cvterm->cvterm_id);
+      $upd_values = array(
+        'dbxref_id' => $dbxref->dbxref_id,
+      );
+      $success = chado_update_record('cvterm', $match, $upd_values);
+      if (!$success) {
+        tripal_report_error('tripal_chado', TRIPAL_WARNING, "Failed to update the term $name with new accession $db:$accession", NULL);
+        return 0;
       }
     }
-    
-    // Add the other values to our cvterm object and then insert!
-    $cvterm->setValue('definition', $definition);
-    $cvterm->setValue('dbxref_id', $dbxref->getID());
-    $cvterm->setValue('is_relationshiptype', $is_relationship);
-    $cvterm->insert();
-  }
-  
-  // For backwards compatibility retrieve the new/updated cvterm.
-  $cvtermsql = "
-    SELECT CVT.name, CVT.cvterm_id, CV.cv_id, CV.name as cvname,
-      DB.name as dbname, DB.db_id, DBX.accession
-    FROM {cvterm} CVT
-      INNER JOIN {dbxref} DBX on CVT.dbxref_id = DBX.dbxref_id
-      INNER JOIN {db} DB on DBX.db_id = DB.db_id
-      INNER JOIN {cv} CV on CV.cv_id = CVT.cv_id
-    WHERE DBX.accession = :accession and DB.name = :name
-  ";
-  $result = chado_query($cvtermsql, array(':accession' => $accession, ':name' => $dbname));
-  return $result->fetchObject();
+    // Finally grab the updated details.
+    $result = chado_query($cvtermsql, array(':accession' => $accession, ':name' => $dbname));
+    $cvterm = $result->fetchObject();
+  }
+  else {
+    // Do nothing, we have the cvterm but we don't want to update.
+  }
+  // Return the cvterm.
+  return $cvterm;
 }
 
 

+ 415 - 314
tripal_chado/includes/TripalImporter/OBOImporter.inc

@@ -97,7 +97,7 @@ class OBOImporter extends TripalImporter {
    *
    * @var array
    */
-  private $newcvs = array();
+  private $obo_namespaces = array();
   
   
   /**
@@ -113,7 +113,20 @@ class OBOImporter extends TripalImporter {
    * @var array
    */
   private $all_dbs = [];
-
+  
+  /**
+   * When adding synonyms we need to know the cvterm_ids of the synonym types.
+   * This array holds those.
+   * 
+   * @var array
+   */
+  private $syn_types = [
+    'exact' => NULL,
+    'broad' => NULL, 
+    'narrow' => NULL,
+    'related' => NULL,
+  ];
+  
   
   // An alternative cache to the temp_obo table.
   private $termStanzaCache = [
@@ -486,6 +499,44 @@ class OBOImporter extends TripalImporter {
     $term = chado_get_cvterm(['id' => 'NCIT:C25693']);
     $this->used_terms['NCIT:C25693'] = $term->cvterm_id;
     
+    // Get the 'Comment' term that we will use for adding comments.
+    $term = chado_get_cvterm(['id' => 'rdfs:comment']);
+    $this->used_terms['rdfs:comment'] = $term->cvterm_id;
+    
+    // Make sure we have a 'synonym_type' vocabulary.
+    $syn_cv = new ChadoRecord('cv');
+    $syn_cv->setValues(['name' => 'synonym_type']);
+    $syn_cv->save();
+    $this->all_cvs['synonym_type'] = (object) $syn_cv->getValues();
+    
+    // Make sure we have a 'synonym_type' database.
+    $syn_db = new ChadoRecord('db');
+    $syn_db->setValues(['name' => 'synonym_type']);
+    $syn_db->save();
+    $this->all_dbs['synonym_type'] = (object) $syn_db->getValues();
+
+    // Make sure the synonym types exists in the 'synonym_type' vocabulary.
+    foreach (array_keys($this->syn_types) as $syn_type) {
+      $syn_dbxref = new ChadoRecord('dbxref');
+      $syn_dbxref->setValues([
+        'accession' => $syn_type,
+        'db_id' => $syn_db->getID(),
+      ]);
+      $syn_dbxref->save();
+      
+      $syn_term = new ChadoRecord('cvterm');
+      $syn_term->setValues([
+        'name' => $syn_type,
+        'definition' => '',
+        'is_obsolete' => 0,
+        'cv_id' => $syn_cv->getID(),
+        'is_relationshiptype' => 0,
+        'dbxref_id' => $syn_dbxref->getID(),
+      ]);
+      $syn_term->save();
+      $this->syn_types[$syn_type] = (object) $syn_term->getValues();
+    }
+    
     // Run the importer!
     $this->loadOBO_v1_2_id($obo);
   }
@@ -506,7 +557,7 @@ class OBOImporter extends TripalImporter {
 
     // Upate the cvtermpath table for each newly added CV.
     $this->logMessage("Updating cvtermpath table.  This may take a while...");
-    foreach ($this->newcvs as $namespace => $cv_id) {
+    foreach ($this->obo_namespaces as $namespace => $cv_id) {
       $this->logMessage("- Loading paths for @vocab", array('@vocab' => $namespace));
       tripal_update_cvtermpath($cv_id);
     }
@@ -646,19 +697,23 @@ class OBOImporter extends TripalImporter {
     $this->clearTermStanzaCache();
 
     // Parse the obo file.
+    $this->logMessage("Step 1: Preloading File $file...");
     $this->parse($file, $header);
     
     // Cache the relationships of terms.
+    $this->logMessage("Step 2: Examining relationships...");
     $this->cacheRelationships();
  
     // Add any typedefs to the vocabulary first.
+    $this->logMessage("Step 3: Loading type defs...");
     $this->processTypeDefs();
 
     // Next add terms to the vocabulary.
-    if (!$this->processTerms()) {
-      throw new Exception('Cannot add terms from this ontology');
-    }
+    $this->logMessage("Step 4: Loading terms...");
+    $this->processTerms();
+    
     // Empty the term cache.
+    $this->logMessage("Step 5: Cleanup...");
     $this->clearTermStanzaCache();
   }
 
@@ -670,7 +725,6 @@ class OBOImporter extends TripalImporter {
    */
   private function processTypeDefs() {
     
-    $this->logMessage("Step 3: Loading type defs...");
     $typedefs = $this->getCachedTermStanzas('Typedef');
     $count = $this->getCacheSize('Typedef');
     $this->setTotalItems($count);
@@ -703,7 +757,6 @@ class OBOImporter extends TripalImporter {
     $i = 0;
     $external = FALSE;
     
-    $this->logMessage("Step 4: Loading terms...");
     $terms = $this->getCachedTermStanzas('Term');
     $count = $this->getCacheSize('Term');
     $this->setTotalItems($count);
@@ -782,7 +835,7 @@ class OBOImporter extends TripalImporter {
     if (!$cv->find()) {
       $cv->insert();
     }
-    $this->newcvs[$namespace] = $cv->getID();
+    $this->obo_namespaces[$namespace] = $cv->getID();
     
     // Add the DB record if it doesn't exist.
     $db = new ChadoRecord('db');
@@ -947,54 +1000,149 @@ class OBOImporter extends TripalImporter {
    *   Set to TRUE to update the term if it exists.
    *   
    * @return
-   *   A cvterm object. 
+   *   The cvterm ID.
    */
-  private function saveTerm($stanza, $is_relationship = FALSE, $update_if_exists = FALSE) {
+  private function saveTerm($stanza, $is_relationship = FALSE) {
     
+    // Get the term ID.
     $id = $stanza['id'][0];
     
     // First check if we've already used this term.
     if ($this->used_terms[$id]) {
       return $this->used_terms[$id];
-    }
+    }    
     
-    // Convert the stanza into a term array that we can pass to
-    // chado_insert_cvterm().
-    $t = array();
-    $t['id'] = $stanza['id'][0];
-    $t['name'] = $stanza['name'][0];
-    $t['namespace'] = $stanza['namespace'][0];
-    $t['cv_name'] = $stanza['namespace'][0];
-    $t['is_relationship'] = $is_relationship;
-    $t['db_name'] = $stanza['db_name'][0];
+    // Get the term properties.
+    $id = $stanza['id'][0];
+    $name =  $stanza['name'][0];
+    $cvname = $stanza['namespace'][0];
+    $dbname = $stanza['db_name'][0];
+    $namespace = $stanza['namespace'][0];
     
-    if (array_key_exists('def', $stanza)) {
-      $t['definition'] = $stanza['def'][0];
+    // Does this term ID have both a short name and accession? If so, then
+    // separate out these components, otherwise we will use the id as both
+    // the id and accession.
+    $accession = '';
+    $matches = [];
+    if (preg_match('/^(.+?):(.*)$/', $id, $matches)) {
+      $accession = $matches[2];
     }
-    if (array_key_exists('subset', $stanza)) {
-      $t['subset'] = $stanza['subset'][0];
+    else {
+      $accession = $id;
+    }
+    
+    // Get the definition if there is on.
+    $definition = '';
+    if (array_key_exists('definition', $stanza)) {
+      $definition = preg_replace('/^\"(.*)\"/', '\1', $stanza['def'][0]);
     }
+    
+    // Set the flag if this term is obsolete.
+    $is_obsolete = 0;
     if (array_key_exists('is_obsolete', $stanza)) {
-      $t['is_obsolete'] = $stanza['is_obsolete'][0];
+      $is_obsolete = $stanza['is_obsolete'][0] == TRUE ? 1 : 0;
     }
-    $cvterm = chado_insert_cvterm($t, ['update_existing' => $update_if_exists]);
-    if (!$cvterm) {
-      throw new Exception("Cannot add the term " . $term['id'][0]);
+    
+    // Is this term borrowed from another ontology?  We can find out by checking
+    // if the namespace belongs to the list of known namespaces provided by this
+    // OBO. If not, then it's borrowed.
+    $is_borrowed = FALSE;
+    if (!array_key_exists($namespace, $this->obo_namespaces)) {
+      $is_borrowed = TRUE;
     }
     
-    // Remove any relationships that this term already has (in case it was
-    // updated) and we'll re-add them.
-    $sql = "
-      DELETE FROM {cvterm_relationship} CVTR
-      WHERE CVTR.subject_id = :cvterm_id
-    ";
-    chado_query($sql, array(':cvterm_id' => $cvterm->cvterm_id));
+    // The cvterm ChadoRecord object.
+    $cvterm = NULL;
     
-    // Save the cvterm_id for this term so we don't look it up again.
-    $this->used_terms[$id] = $cvterm->cvterm_id;
+    // Get the CV and DB objects.
+    $cv = $this->all_cvs[$cvname];
+    $db = $this->all_dbs[$dbname];
     
+    // If this is set to TRUE then we should insert the term.
+    $do_cvterm_insert = FALSE;
+    
+    // We need to locate terms using their dbxref. This is because term names
+    // can sometimes change, so we don't want to look up the term by it's name.
+    // the unique ID which is in the accession will never change.
+    $dbxref = new ChadoRecord('dbxref');
+    $dbxref->setValues([
+      'db_id' => $db->db_id,
+      'accession' => $accession
+    ]);
+    if ($dbxref->find()) {
+      // Now see if there is a cvterm for this dbxref. A dbxref can only be 
+      // used one time. If so, then update the term, but only if this term
+      // belongs to namespace created by this OBO. We don't want to update
+      // terms from other OBOs that may be borrowed by this one.
+      $cvterm = new ChadoRecord('cvterm');
+      $cvterm->setValues(['dbxref_id' => $dbxref->getID()]);
+      if ($cvterm->find()) {
+        if (!$is_borrowed) {
+          $cvterm->setValue('name', $name);
+          $cvterm->setValue('definition', $definition);
+          $cvterm->setValue('is_obsolete', $is_obsolete);
+          $cvterm->update();
+        }
+      }
+      else {
+        $do_cvterm_insert = TRUE;
+      }
+    }
+    else {
+      // The dbxref doesn't exist, so let's add it.
+      $dbxref->insert();
+      $do_cvterm_insert = TRUE;
+    }
+    
+    if ($do_cvterm_insert) {
+      // Before inserting the term let's check to see if it already exists. The
+      // cvterm table has two unique constraints, one on the dbxref and another
+      // on the name, cv_id and is_obsolete columns.  If for some reason the
+      // term exists but is assocaited with a different dbxref we'll have 
+      // a problem.  Because the is_obsolete may be set this time, but the
+      // term may not have it set from a previous load we will check for the
+      // presence of the term without the is_obsolete.
+      $cvterm = new ChadoRecord('cvterm');
+      $cvterm->setValues([
+        'cv_id' => $cv->cv_id,
+        'name' => $name,
+      ]);
+      if ($cvterm->find()) {
+        // We found the term so do an update and change the dbxref_id.
+        if (!$is_borrowed) {
+          // This is a borrowed term so just update the dbxref_id.
+          $cvterm->setvalue('dbxref_id', $dbxref->getID());
+          $cvterm->update();
+        }
+        else {
+          $cvterm->setValues([
+            $cvterm->setvalue('definition', $definition),
+            $cvterm->setvalue('dbxref_id', $dbxref->getID()),
+            $cvterm->setvalue('is_obsolete', $is_obsolete),
+          ]);
+          $cvterm->update();
+        }
+      }
+      else {
+        // The term doesnt exist, so let's just do our insert.
+        $cvterm->setValues([
+          'cv_id' => $cv->cv_id,
+          'name' => $name,
+          'definition' => $definition,
+          'dbxref_id' => $dbxref->getID(),
+          'is_relationshiptype' => $is_relationship,
+          'is_obsolete' => $is_obsolete,
+        ]);
+        $cvterm->insert();
+      }
+    }
+    
+    // Save the cvterm_id for this term so we don't look it up again.
+    $cvterm_id = $cvterm->getID();
+    $this->used_terms[$id] = $cvterm_id;
+        
     // Return the cvterm_id.
-    return $cvterm->cvterm_id;
+    return $cvterm_id;
   }
 
   /**
@@ -1013,26 +1161,66 @@ class OBOImporter extends TripalImporter {
     //
     // First things first--save the term.
     //
-    $cvterm_id = $this->saveTerm($stanza, FALSE, TRUE);
-
+    $cvterm_id = $this->saveTerm($stanza, FALSE);
+    
+    // If this term is borrowed from another ontology, then we do not want
+    // to do any further processing. We only want to process terms that
+    // belong to this one.
+    $namespace = $stanza['namespace'][0];
+    if (!array_key_exists($namespace, $this->obo_namespaces)) {
+      return;
+    }
+    
+    // If this term belongs to this OBO (not borrowed from another OBO) then
+    // remove any relationships, properties, xrefs, and synonyms that this 
+    // term alreadyn has so that they can be re-added.
+    $sql = "
+      DELETE FROM {cvterm_relationship}
+      WHERE subject_id = :cvterm_id
+    ";
+    chado_query($sql, array(':cvterm_id' => $cvterm_id));
+    
+    $sql = "
+      DELETE FROM {cvtermprop}
+      WHERE cvterm_id = :cvterm_id
+    ";
+    chado_query($sql, array(':cvterm_id' => $cvterm_id));
+    
+    $sql = "
+      DELETE FROM {cvterm_dbxref}
+      WHERE cvterm_id = :cvterm_id
+    ";
+    chado_query($sql, array(':cvterm_id' => $cvterm_id));
+    
+    $sql = "
+      DELETE FROM {cvtermsynonym} CVTSYN
+      WHERE cvterm_id = :cvterm_id
+    ";
+    chado_query($sql, array(':cvterm_id' => $cvterm_id));
+    
+    // We should never have the problem where we don't have a cvterm_id. The
+    // saveTerm() function should always return on.  But if for some unkonwn
+    // reason we don't have one then fail.
+    if (!$cvterm_id) {
+      throw new Exception(t('Missing cvterm after saving term: !term', 
+        ['!term' => print_r($stanza, TRUE)]));
+    }
     
     //
     // Handle:  alt_id
     //
     if (array_key_exists('alt_id', $stanza)) {
       foreach ($stanza['alt_id'] as $alt_id) {
-        if (!$this->addCvtermDbxref($cvterm_id, $alt_id)) {
-          throw new Exception("Cannot add alternate id $alt_id");
-        }
+        $this->addAltID($cvterm_id, $alt_id);
       }
     }
-    
+
     //
     // Handle:  synonym
     //
     if (array_key_exists('synonym', $stanza)) {
-      if (!$this->addSynonym($stanza, $cvterm_id)) {
-        throw new Exception("Cannot add synonyms");
+      foreach ($stanza['synonym'] as $synonym) {
+        $this->addSynonym($cvterm_id, $synonym);
       }
     }
 
@@ -1041,11 +1229,8 @@ class OBOImporter extends TripalImporter {
     //
     if (array_key_exists('exact_synonym', $stanza)) {
       foreach ($stanza['exact_synonym'] as $synonym) {
-        $new = preg_replace('/^\s*(\".+?\")(.*?)$/', '$1 EXACT $2', $synonym);
-        $term['synonym'][] = $new;
-        if (!$this->addSynonym($term, $cvterm_id)) {
-          throw new Exception("Cannot add/update synonyms");
-        }
+        $fixed = preg_replace('/^\s*(\".+?\")(.*?)$/', '$1 EXACT $2', $synonym);
+        $this->addSynonym($cvterm_id, $fixed);
       }
     }
     
@@ -1054,11 +1239,8 @@ class OBOImporter extends TripalImporter {
     //
     if (array_key_exists('narrow_synonym', $stanza)) {
       foreach ($stanza['narrow_synonym'] as $synonym) {
-        $new = preg_replace('/^\s*(\".+?\")(.*?)$/', '$1 NARROW $2', $synonym);
-        $term['synonym'][] = $new;
-        if (!$this->addSynonym($term, $cvterm_id)) {
-          throw new Exception("Cannot add/update synonyms");
-        }
+        $fixed = preg_replace('/^\s*(\".+?\")(.*?)$/', '$1 NARROW $2', $synonym);
+        $this->addSynonym($cvterm_id, $fixed);
       }
     }
     
@@ -1067,11 +1249,8 @@ class OBOImporter extends TripalImporter {
     //
     if (array_key_exists('broad_synonym', $stanza)) {
       foreach ($stanza['broad_synonym'] as $synonym) {
-        $new = preg_replace('/^\s*(\".+?\")(.*?)$/', '$1 BROAD $2', $synonym);
-        $term['synonym'][] = $new;
-        if (!$this->addSynonym($term, $cvterm_id)) {
-          throw new Exception("Cannot add/update synonyms");
-        }
+        $fixed = preg_replace('/^\s*(\".+?\")(.*?)$/', '$1 BROAD $2', $synonym);
+        $this->addSynonym($cvterm_id, $fixed);
       }
     }
 
@@ -1080,12 +1259,8 @@ class OBOImporter extends TripalImporter {
     //
     if (array_key_exists('comment', $stanza)) {
       $comments = $stanza['comment'];
-      $j = 0;
-      foreach ($comments as $comment) {
-        if (!$this->addCvtermProp($cvterm_id, 'comment', $comment, $j)) {
-          throw new Exception("Cannot add/update cvterm property");
-        }
-        $j++;
+      foreach ($comments as $rank => $comment) {
+        $this->addComment($cvterm_id, $comment, $rank);
       }
     }
 
@@ -1094,9 +1269,7 @@ class OBOImporter extends TripalImporter {
     //
     if (array_key_exists('xref', $stanza)) {
       foreach ($stanza['xref'] as $xref) {
-        if (!$this->addCvtermDbxref($cvterm_id, $xref)) {
-          throw new Exception("Cannot add/update cvterm database reference (dbxref).");
-        }
+        $this->addXref($cvterm_id, $xref);
       }
     }
 
@@ -1105,9 +1278,7 @@ class OBOImporter extends TripalImporter {
     //
     if (array_key_exists('xref_analog', $stanza)) {
       foreach ($stanza['xref_analog'] as $xref) {
-        if (!$this->addCvtermDbxref($cvterm_id, $xref)) {
-          throw new Exception("Cannot add/update cvterm database reference (dbxref).");
-        }
+        $this->addXref($cvterm_id, $xref);
       }
     }
     
@@ -1116,20 +1287,25 @@ class OBOImporter extends TripalImporter {
     //
     if (array_key_exists('xref_unk', $stanza)) {
       foreach ($stanza['xref_unk'] as $xref) {
-        if (!$this->addCvtermDbxref($cvterm_id, $xref)) {
-          throw new Exception("Cannot add/update cvterm database reference (dbxref).");
-        }
+        $this->addXref($cvterm_id, $xref);
       }
     }
-
+    
+    //
+    // Handle:  subset
+    //
+    if (array_key_exists('subset', $stanza)) {
+      foreach ($stanza['subset'] as $subset) {
+        $this->addSubset($cvterm_id, $subset);
+      }
+    }
+    
     //
     // Handle:  is_a
     //
     if (array_key_exists('is_a', $stanza)) {
       foreach ($stanza['is_a'] as $is_a) {
-        if (!$this->addRelationship($cvterm_id, 'is_a', $is_a)) {
-          throw new Exception("Cannot add relationship is_a: $is_a");
-        }
+        $this->addRelationship($cvterm_id, 'is_a', $is_a);
       }
     }
     
@@ -1140,23 +1316,11 @@ class OBOImporter extends TripalImporter {
       foreach ($stanza['relationship'] as $value) {
         $rel = preg_replace('/^(.+?)\s.+?$/', '\1', $value);
         $object = preg_replace('/^.+?\s(.+?)$/', '\1', $value);
-        if (!$this->addRelationship($cvterm_id, $rel, $object)) {
-          throw new Exception("Cannot add relationship $rel: $object");
-        }
-      }
-    }
-    
-    //
-    // Handle:  subset
-    //
-    if (array_key_exists('subset', $stanza)) {
-      foreach ($stanza['subset'] as $subset) {
-        if (!$this->addSubset($cvterm_id, $subset)) {
-          throw new Exception("Cannot add subset $subset");
-        }
+        $this->addRelationship($cvterm_id, $rel, $object);
       }
     }
 
+
     /**
      * The following properties are currently unsupported:
      * 
@@ -1393,96 +1557,38 @@ class OBOImporter extends TripalImporter {
   /**
    * Adds the synonyms to a term
    *
-   * @param term
-   *   An array representing the cvterm. It must have a 'synonym' key/value pair.
    * @param $cvterm_id
    *   The cvterm_id of the term to which the synonym will be added.
+   * @param $synonym
+   *   The value of the 'synonym' line of the term stanza.
    *
    * @ingroup tripal_obo_loader
    */
-  private function addSynonym($term, $cvterm_id) {
-
-    // make sure we have a 'synonym_type' vocabulary
-    $syncv = tripal_insert_cv(
-        'synonym_type',
-        'A local vocabulary added for synonym types.'
-    );
-
-    // now add the synonyms
-    if (array_key_exists('synonym', $term)) {
-      foreach ($term['synonym'] as $synonym) {
-
-        // separate out the synonym definition and the synonym type
-        $def = preg_replace('/^\s*"(.*)"\s*.*$/', '\1', $synonym);
-        // the scope will be 'EXACT', etc...
-        $scope = drupal_strtolower(preg_replace('/^.*"\s+(.*?)\s+.*$/', '\1', $synonym));
-        if (!$scope) {  // if no scope then default to 'exact'
-          $scope = 'exact';
-        }
+  private function addSynonym($cvterm_id, $synonym) {
+    $def = $synonym;
+    $syn_type = '';
 
-        // make sure the synonym type exists in the 'synonym_type' vocabulary
-        $values = array(
-          'name' => $scope,
-          'cv_id' => array(
-            'name' => 'synonym_type',
-          ),
-        );
-        $syntype = tripal_get_cvterm($values);
-
-        // if it doesn't exist then add it
-        if (!$syntype) {
-          // build a 'term' object so we can add the missing term
-          $term = array(
-            'name' => $scope,
-            'id' => "synonym_type:$scope",
-            'definition' => '',
-            'is_obsolete' => 0,
-            'cv_name' => $syncv->name,
-            'is_relationship' => FALSE
-          );
-          $syntype = tripal_insert_cvterm($term, array('update_existing' => TRUE));
-          if (!$syntype) {
-            throw new Exception("Cannot add synonym type: internal:$scope");
-          }
-        }
-
-        // make sure the synonym doesn't already exists
-        $values = array(
-          'cvterm_id' => $cvterm_id,
-          'synonym' => $def
-        );
-        $results = chado_select_record('cvtermsynonym', array('*'), $values);
-        if (count($results) == 0) {
-          $values = array(
-            'cvterm_id' => $cvterm_id,
-            'synonym' => $def,
-            'type_id' => $syntype->cvterm_id
-          );
-          $options = array('return_record' => FALSE);
-          $success = chado_insert_record('cvtermsynonym', $values, $options);
-          if (!$success) {
-            throw new Exception("Failed to insert the synonym for term: $cvterm_id ($def)");
-          }
-        }
-
-        // now add the dbxrefs for the synonym if we have a comma in the middle
-        // of a description then this will cause problems when splitting os lets
-        // just change it so it won't mess up our splitting and then set it back
-        // later.
-        /**
-        $synonym = preg_replace('/(".*?),\s(.*?")/','$1,_$2',$synonym);
-        $dbxrefs = preg_split("/, /",preg_replace('/^.*\[(.*?)\]$/','\1',$synonym));
-        foreach ($dbxrefs as $dbxref) {
-        $dbxref = preg_replace('/,_/',", ",$dbxref);
-        if ($dbxref) {
-        $this->addCvtermDbxref($syn,$dbxref);
-        }
-        }
-        */
-      }
+    // Separate out the synonym definition and type (e.g. EXACT).
+    $matches = [];
+    if (preg_match('/^\s*"(.*)"\s+(.*?)\s+.*$/', $synonym, $matches)) {
+      $def = $matches[1];
+      $syn_type = $matches[2];
     }
-
-    return TRUE;
+    
+    // Get the syn type cvterm.
+    if (!$syn_type) {  
+      $syn_type = 'exact';
+    }
+    $syn_type = $this->syn_types[$syn_type];
+        
+    // Now save the new synonym.
+    $cvtermsynonym = new ChadoRecord('cvtermsynonym');
+    $cvtermsynonym->setValues([
+      'cvterm_id' => $cvterm_id,
+      'synonym' => $def,
+      'type_id' => $syn_type->cvterm_id
+    ]);
+    $cvtermsynonym->insert();
   }
 
   /**
@@ -1508,11 +1614,10 @@ class OBOImporter extends TripalImporter {
     // The type of term:  Typedef or Term (inside the [] brackets]
     $type = '';
 
-    $this->logMessage("Step 1: Preloading File $file...");
     $filesize = filesize($obo_file);
     $this->setTotalItems($filesize);
     $this->setItemsHandled(0);
-    $this->setInterval(1);
+    $this->setInterval(5);
 
     // iterate through the lines in the OBO file and parse the stanzas
     $fh = fopen($obo_file, 'r');
@@ -1536,12 +1641,7 @@ class OBOImporter extends TripalImporter {
 
       // Remove comments from end of lines.
       $line = preg_replace('/^(.*?)\!.*$/', '\1', $line); 
-      
-      // Remove annotations surrounded by brackets. These are found
-      // in the Trait Ontology (e.g. TO:1000023 {is_inferred="true"})
-      // That construct has useful info, but it is not in the OBO 1.4 format
-      // specifications.
-      $line = preg_replace('/\{.*?\}/', '', $line);
+     
 
       // At the first stanza we're out of header.
       if (preg_match('/^\s*\[/', $line)) {
@@ -1554,6 +1654,14 @@ class OBOImporter extends TripalImporter {
         
         // Store the stanza we just finished reading.
         if (sizeof($stanza) > 0) {
+
+            // If this term has a namespace then we want to keep track of it.
+          if (array_key_exists('namespace', $stanza)) {
+            $namespace = $stanza['namespace'][0];
+            $cv = $this->all_cvs[$namespace];
+            $this->obo_namespaces[$namespace] = $cv->cv_id;
+          }
+          
           $this->addCacheTermStanza($stanza, $type);
         }
         
@@ -1593,9 +1701,20 @@ class OBOImporter extends TripalImporter {
     }
     // now add the last term in the file
     if (sizeof($stanza) > 0) {
+      // If this term has a namespace then we want to keep track of it.
+      if (array_key_exists('namespace', $stanza)) {
+        $namespace = $stanza['namespace'][0];
+        $cv = $this->all_cvs[$namespace];
+        $this->obo_namespaces[$namespace] = $cv->cv_id;
+      }
+      
       $this->addCacheTermStanza($stanza, $type);
       $this->setItemsHandled($num_read);
     }
+    
+    $message = t('Found the following namespaces: !namespaces.', 
+      ['!namespaces' => implode(', ', array_keys($this->obo_namespaces))]);
+    $this->logMessage($message);
   }
   
   /**
@@ -1606,7 +1725,6 @@ class OBOImporter extends TripalImporter {
     // Now that we have all of the terms parsed and loaded into the cache,
     // lets run through them one more time cache any terms in relationships
     // as well.
-    $this->logMessage("Step 2: Examining relationships...");
     $terms = $this->getCachedTermStanzas('Term');
     $count = $this->getCacheSize('Term');
     $this->setTotalItems($count);
@@ -1728,11 +1846,81 @@ class OBOImporter extends TripalImporter {
       'type_id' => $this->used_terms['NCIT:C25693'],
       'value' => $subset
     ]);
-    if (!$cvtermprop->find()){
-      $cvtermprop->insert();
-    }
+    $cvtermprop->insert();
     return TRUE;
   }
+  
+  /**
+   * Adds a database to Chado if it doesn't exist.
+   * 
+   * @param $dbname
+   *   The name of the database to add.
+   *   
+   * @return
+   *   A Chado database object.
+   */
+  private function addDB($dbname) {
+    // Add the database if it doesn't exist.
+    $db = NULL;
+    if (array_key_exists($dbname, $this->all_dbs)) {
+      $db = $this->all_dbs[$dbname];
+    }
+    else {
+      $db = new ChadoRecord('db');
+      $db->setValues(['name' => $dbname]);
+      $db->insert();
+      $db = (object) $db->getValues();
+      $this->all_dbs[$dbname] = $db;
+    }
+    return $db;
+  }
+  
+  /**
+   * Adds an alternative ID
+   *
+   * @param $cvterm_id
+   *   The cvterm_id of the term to which the synonym will be added.
+   * @param $alt_id
+   *   The cross refernce.  It should be of the form from the OBO specification
+   *
+   * @ingroup tripal_obo_loader
+   */
+  private function addAltID($cvterm_id, $alt_id) {
+    
+    $dbname = '';
+    $accession = '';
+    $matches = [];
+    if (preg_match('/^(.+?):(.*)$/', $alt_id, $matches)) {
+      $dbname = $matches[1];
+      $accession = $matches[2];
+    }
+    
+    if (!$accession) {
+      throw new Exception("Cannot add an Alt ID without an accession: '$alt_id'");
+    }
+
+    // Add the database if it doesn't exist.
+    $db = $this->addDB($dbname);
+    $db_id = $db->db_id;
+    
+    // Now add the dbxref if it doesn't alredy exist
+    $dbxref = new ChadoRecord('dbxref');
+    $dbxref->setValues([
+      'db_id' => $db_id,
+      'accession' => $accession
+    ]);
+    if (!$dbxref->find()) {
+      $dbxref->insert();
+    }
+    
+    // Now add the cvterm_dbxref record.
+    $cvterm_dbxref = new ChadoRecord('cvterm_dbxref');
+    $cvterm_dbxref->setValues([
+      'cvterm_id' => $cvterm_id,
+      'dbxref_id' => $dbxref->getID(),
+    ]);
+    $cvterm_dbxref->insert();
+  }
 
   /**
    * Adds a database reference to a cvterm
@@ -1744,7 +1932,7 @@ class OBOImporter extends TripalImporter {
    *
    * @ingroup tripal_obo_loader
    */
-  private function addCvtermDbxref($cvterm_id, $xref) {
+  private function addXref($cvterm_id, $xref) {
 
     $dbname = preg_replace('/^(.+?):.*$/', '$1', $xref);
     $accession = preg_replace('/^.+?:\s*(.*?)(\{.+$|\[.+$|\s.+$|\".+$|$)/', '$1', $xref);
@@ -1752,154 +1940,67 @@ class OBOImporter extends TripalImporter {
     $dbxrefs = preg_replace('/^.+?\[(.+?)\].*?$/', '$1', $xref);
 
     if (!$accession) {
-      throw new Exception("Cannot add a dbxref without an accession: '$xref'");
+      throw new Exception("Cannot add an xref without an accession: '$xref'");
     }
 
-    // if the xref is a database link, handle that specially
+    // If the xref is a database link then use a dbname of 'URL'.
+    // TODO: does this make sense to add a URL database?
     if (strcmp($dbname, 'http') == 0) {
       $accession = $xref;
       $dbname = 'URL';
     }
 
-    // add the database
-    $db = tripal_insert_db(array('name' => $dbname));
-    if (!$db) {
-      throw new Exception("Cannot find database '$dbname' in Chado.");
-    }
+    // Add the database if it doesn't exist.
+    $db = $this->addDB($dbname);
+    $db_id = $db->db_id;
 
-    // now add the dbxref
-    $dbxref = $this->addDbxref($db->db_id, $accession, '', $description);
-    if (!$dbxref) {
-      throw new Exception("Cannot find or add the database reference (dbxref)");
+    // Now add the dbxref if it doesn't alredy exist
+    $dbxref = new ChadoRecord('dbxref');
+    $dbxref->setValues([
+      'db_id' => $db_id,
+      'accession' => $accession
+    ]);
+    if (!$dbxref->find()) {
+      $dbxref->insert();
     }
-
-    // finally add the cvterm_dbxref but first check to make sure it exists
-    $values = array(
+    
+    // Now add the cvterm_dbxref record.
+    $cvterm_dbxref = new ChadoRecord('cvterm_dbxref');
+    $cvterm_dbxref->setValues([
       'cvterm_id' => $cvterm_id,
-      'dbxref_id' => $dbxref->dbxref_id,
-    );
-    $result = chado_select_record('cvterm_dbxref', array('*'), $values);
-    if (count($result) == 0) {
-      $ins_options = array('return_record' => FALSE);
-      $result = chado_insert_record('cvterm_dbxref', $values, $ins_options);
-      if (!$result) {
-        throw new Exception("Cannot add cvterm_dbxref: $xref");
-      }
-    }
-
-    return TRUE;
+      'dbxref_id' => $dbxref->getID(),
+    ]);
+    $cvterm_dbxref->insert();
   }
 
   /**
-   * Adds a property to a cvterm
+   * Adds a comment to a cvterm.
    *
    * @param $cvterm_id
    *   A cvterm_id of the term to which properties will be added
-   * @param $property
-   *   The name of the property to add
-   * @param $value
-   *   The value of the property
+   * @param $comment
+   *   The comment to add to the cvterm.
    * @param rank
-   *   The rank of the property
+   *   The rank of the comment
    *
    * @ingroup tripal_obo_loader
    */
-  private function addCvtermProp($cvterm_id, $property, $value, $rank) {
-
-    // make sure the 'cvterm_property_type' CV exists
-    $cv = tripal_insert_cv('cvterm_property_type', '');
-    if (!$cv) {
-      throw new Exception("Cannot add/find cvterm_property_type cvterm");
-    }
+  private function addComment($cvterm_id, $comment, $rank) {
 
-    // get the property type cvterm.  If it doesn't exist then we want to add it
-    $values = array(
-      'name' => $property,
-      'cv_id' => $cv->cv_id,
-    );
-    $results = chado_select_record('cvterm', array('*'), $values);
-    if (count($results) == 0) {
-      $term = array(
-        'name' => $property,
-        'id' => "internal:$property",
-        'definition' => '',
-        'is_obsolete' => 0,
-        'cv_name' => $cv->name,
-        'is_relationship' => FALSE,
-      );
-      $cvproptype = tripal_insert_cvterm($term, array('update_existing' => FALSE));
-      if (!$cvproptype) {
-        throw new Exception("Cannot add cvterm property: internal:$property");
-      }
-    }
-    else {
-      $cvproptype = $results[0];
-    }
-
-    // remove any properties that currently exist for this term.  We'll reset them
-    if ($rank == 0) {
-      $values = array('cvterm_id' => $cvterm_id);
-      $success = chado_delete_record('cvtermprop', $values);
-      if (!$success) {
-        throw new Exception("Could not remove existing properties to update property $property for term\n");
-      }
-    }
-
-    // now add the property
-    $values = array(
+    // Get the comment type id.
+    $comment_type_id = $this->used_terms['rdfs:comment'];
+    
+    // Add the comment as a property of the cvterm.
+    $cvtermprop = new ChadoRecord('cvtermprop');
+    $cvtermprop->setValues([
       'cvterm_id' => $cvterm_id,
-      'type_id' => $cvproptype->cvterm_id,
-      'value' => $value,
+      'type_id' => $comment_type_id,
+      'value' => $comment,
       'rank' => $rank,
-    );
-    $options = array('return_record' => FALSE);
-    $result = chado_insert_record('cvtermprop', $values, $options);
-    if (!$result) {
-      throw new Exception("Could not add property $property for term\n");
-    }
-    return TRUE;
-  }
-
-  /**
-   * Adds a database cross reference to a cvterm
-   *
-   * @param db_id
-   *   The database ID of the cross reference
-   * @param accession
-   *   The cross reference's accession
-   * @param $version
-   *   The version of the dbxref
-   * @param $description
-   *   The description of the cross reference
-   *
-   * @ingroup tripal_obo_loader
-   */
-  private function addDbxref($db_id, $accession, $version='', $description='') {
-
-    // check to see if the dbxref exists if not, add it
-    $values = array(
-      'db_id' => $db_id,
-      'accession' => $accession,
-    );
-    $result = chado_select_record('dbxref', array('dbxref_id'), $values);
-    if (count($result) == 0) {
-      $ins_values = array(
-        'db_id'       => $db_id,
-        'accession'   => $accession,
-        'version'     => $version,
-        'description' => $description,
-      );
-      $ins_options = array('return_record' => FALSE);
-      $result = chado_insert_record('dbxref', $ins_values, $ins_options);
-      if (!$result) {
-        throw new Exception("Failed to insert the dbxref record $accession");
-      }
-      $result = chado_select_record('dbxref', array('dbxref_id'), $values);
-    }
-    return $result[0];
+    ]);
+    $cvtermprop->insert();
   }
 
-
   /**
    * API call to Ontology Lookup Service provided by
    * https://www.ebi.ac.uk/ols/docs/api#resources-terms

+ 6 - 0
tripal_chado/includes/tripal_chado.semweb.inc

@@ -156,6 +156,12 @@ function tripal_chado_populate_vocab_RDFS() {
     'cv_name' => 'rdfs',
     'definition' => 'A human-readable version of a resource\'s name.',
   ));
+  $name = chado_insert_cvterm(array(
+    'id' => 'rdfs:comment',
+    'name' => 'comment',
+    'cv_name' => 'rdfs',
+    'definition' => 'A human-readable description of a resource\'s name.',
+  ));
 }
 /**
  * Adds the Schema.org database and terms.

+ 20 - 1
tripal_chado/tripal_chado.install

@@ -1622,7 +1622,7 @@ function tripal_chado_update_7331() {
 }
 
 /**
- * Adds the NCIT subgroup term for use by the OBO loader when setting vocabulary subsets.
+ * Adds additional vocabs for the OBO Importer.
  */
 function tripal_chado_update_7332() {
   try {
@@ -1645,6 +1645,25 @@ function tripal_chado_update_7332() {
       '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',
+      'description' => 'Resource Description Framework Schema',
+      'url' => 'https://www.w3.org/TR/rdf-schema/',
+      'urlprefix' => 'http://www.w3.org/2000/01/rdf-schema#{accession}',
+    ));
+    chado_insert_cv(
+      'rdfs',
+      'Resource Description Framework Schema'
+      );
+    $name = chado_insert_cvterm(array(
+      'id' => 'rdfs:comment',
+      'name' => 'comment',
+      'cv_name' => 'rdfs',
+      'definition' => 'A human-readable description of a resource\'s name.',
+    ));
   }
   catch (\PDOException $e) {
     $error = $e->getMessage();