Browse Source

Merge pull request #627 from tripal/525-tv3-obo_speed_colon

New OBO Importer
Lacey-Anne Sanderson 6 years ago
parent
commit
a1d9a57414

+ 119 - 0
tests/tripal_chado/example_files/test.obo

@@ -0,0 +1,119 @@
+format-version: 1.2
+default-namespace: tripal_obo_test
+ontology: tot
+subsetdef: test_normal "Normal nodes"
+subsetdef: test_crazy "Crazy nodes"
+
+[Term]
+id: TOT:001
+name: node01
+def: This is node 1
+subset: test_normal
+
+[Term]
+id: TOT:002
+name: node02
+is_a: TOT:001
+def: This is node 2
+subset: test_normal
+
+[Term]
+id: TOT:003
+name: node03
+is_a: TOT:001
+def: This is node 3
+subset: test_normal
+
+[Term]
+id: TOT:004
+name: node04
+is_a: TOT:001
+def: This is node 4
+subset: test_normal
+relationship: has_part TOT:011
+
+
+[Term]
+id: TOT:005
+name: node05
+is_a: TOT:001
+def: This is node 5
+subset: test_normal
+
+[Term]
+id: TOT:006
+name: node06
+is_a: TOT:003
+def: This is node 6
+subset: test_normal
+
+[Term]
+id: TOT:007
+name: node07
+is_a: TOT:003
+def: This is node 7
+subset: test_normal
+
+[Term]
+id: TOT:008
+name: node08
+is_a: TOT:007
+def: This is node 8
+subset: test_normal
+
+[Term]
+id: TOT:009
+name: node09
+is_a: TOT:004
+is_a: TOT:007
+def: This is node 9
+subset: test_normal
+
+[Term]
+id: TOT:010
+name: node10
+is_a: TOT:005
+def: This is node 10
+subset: test_normal
+
+[Term]
+id: TOT:011
+name: node11
+is_a: TOT:009
+is_a: TOT:010
+def: This is node 11 : Yo ! this comment should be ignored.
+synonym: "crazy node" EXACT []
+xref: GO:0043226
+subset: test_crazy
+comment: This is a crazy node!
+
+
+[Term]
+id: TOT:012
+name: node12
+is_a: TOT:010
+def: This is node 12
+subset: test_normal
+
+[Term]
+id: TOT:013
+name: node13
+is_a: TOT:011
+def: This is node 13
+subset: test_normal
+
+[Term]
+id: TOT:014
+name: node14
+is_obsolete: true
+def: This is node 14
+
+[Term]
+id: CHEBI:132502 ! fatty acid 18:3
+
+[Typedef]
+id: has_part
+name: has_part
+namespace: external
+xref: BFO:0000051
+is_transitive: true

+ 126 - 0
tests/tripal_chado/example_files/test.update.obo

@@ -0,0 +1,126 @@
+format-version: 1.2
+default-namespace: tripal_obo_test
+ontology: tot
+subsetdef: test_normal "Normal nodes"
+subsetdef: test_crazy "Crazy nodes"
+
+[Term]
+id: TOT:001
+name: node01
+def: This is node 1
+subset: test_normal
+
+[Term]
+id: TOT:003
+name: node03
+is_a: TOT:001
+def: This is node 3
+subset: test_normal
+
+[Term]
+id: TOT:004
+name: node04
+is_a: TOT:001
+def: This is node 4
+subset: test_normal
+relationship: has_part TOT:011
+
+
+[Term]
+id: TOT:005
+name: node05
+is_a: TOT:001
+def: This is node 5
+subset: test_normal
+
+[Term]
+id: TOT:006
+name: node06
+is_a: TOT:003
+def: This is node 6
+subset: test_normal
+
+[Term]
+id: TOT:007
+name: node07
+is_a: TOT:003
+def: This is node 7
+subset: test_normal
+
+[Term]
+id: TOT:008
+name: node08
+is_a: TOT:007
+def: This is node 8
+subset: test_normal
+is_obsolete: true
+
+[Term]
+id: TOT:009
+name: node09
+is_a: TOT:004
+is_a: TOT:007
+def: This is node 9
+subset: test_normal
+
+[Term]
+id: TOT:010
+name: node10
+is_a: TOT:005
+def: This is node 10
+subset: test_normal
+
+[Term]
+id: TOT:011
+name: node11
+is_a: TOT:009
+is_a: TOT:010
+def: This is node 11 : Yo ! this comment should be ignored.
+synonym: "crazy node" EXACT []
+xref: GO:0043226
+subset: test_crazy
+comment: This is a crazy node!
+
+
+[Term]
+id: TOT:012
+name: node12
+is_a: TOT:010
+def: This is node 12
+subset: test_normal
+
+[Term]
+id: TOT:013
+name: New name 13.
+is_a: TOT:011
+def: This is node 13
+subset: test_normal
+
+[Term]
+id: TOT:014
+name: node14
+is_obsolete: true
+def: This is node 14
+
+[Term]
+id: TOT:015
+name: node02
+def: This is a replacement for node 2
+is_a: TOT:001
+alt_id: TOT:002
+
+[Term]
+id: TOT:016
+name: node08
+def: This is a replacement for node 8
+is_a: TOT:007
+
+[Term]
+id: CHEBI:132502 ! fatty acid 18:3
+
+[Typedef]
+id: has_part
+name: has_part
+namespace: external
+xref: BFO:0000051
+is_transitive: true

+ 3 - 2
tests/tripal_chado/loaders/GFF3ImporterTest.php

@@ -123,6 +123,7 @@ class GFF3ImporterTest extends TripalTestCase {
    * The GFF importer should still create explicitly defined proteins if
    * The GFF importer should still create explicitly defined proteins if
    * skip_protein is true.
    * skip_protein is true.
    *
    *
+   * @group gff
    * @ticket 77
    * @ticket 77
    */
    */
   public function testGFFImporterLoadsExplicitProteins() {
   public function testGFFImporterLoadsExplicitProteins() {
@@ -176,11 +177,11 @@ class GFF3ImporterTest extends TripalTestCase {
 
 
   private function loadLandmarks($analysis, $organism) {
   private function loadLandmarks($analysis, $organism) {
     $landmark_file = ['file_remote' => 'https://raw.githubusercontent.com/statonlab/tripal_dev_seed/master/Fexcel_mini/sequences/empty_landmarks.fasta'];
     $landmark_file = ['file_remote' => 'https://raw.githubusercontent.com/statonlab/tripal_dev_seed/master/Fexcel_mini/sequences/empty_landmarks.fasta'];
-
+    
     $run_args = [
     $run_args = [
       'organism_id' => $organism->organism_id,
       'organism_id' => $organism->organism_id,
       'analysis_id' => $analysis->analysis_id,
       'analysis_id' => $analysis->analysis_id,
-      'seqtype' => 'scaffold',
+      'seqtype' => 'supercontig',
       'method' => 2, //default insert and update
       'method' => 2, //default insert and update
       'match_type' => 1, //unique name default
       'match_type' => 1, //unique name default
       //optional
       //optional

+ 606 - 0
tests/tripal_chado/loaders/OBOImporterTest.php

@@ -0,0 +1,606 @@
+<?php
+
+namespace Tests;
+
+use StatonLab\TripalTestSuite\DBTransaction;
+use StatonLab\TripalTestSuite\TripalTestCase;
+
+class OBOImporterTest extends TripalTestCase {
+
+  // Uncomment to auto start and rollback db transactions per test method.
+  use DBTransaction;
+
+  /**
+   * A helper function for loading any OBO.
+   *
+   * @param $name - ontology name.  This goes in the tripal_cv_obo table.
+   * @param $path - path to the OBO.  this can be a file path or a URL.
+   *
+   * @throws \Exception
+   */
+  private function loadOBO($name, $path) {
+    
+    $obo_id = db_select('public.tripal_cv_obo', 't')
+    ->fields('t', ['obo_id'])
+    ->condition('t.name', $name)
+    ->execute()
+    ->fetchField();
+    
+    if (!$obo_id) {
+      
+      $obo_id = db_insert('public.tripal_cv_obo')
+      ->fields(['name' => $name, 'path' => $path])
+      ->execute();
+    }
+    
+    $run_args = ['obo_id' => $obo_id];
+    
+    module_load_include('inc', 'tripal_chado', 'includes/TripalImporter/OBOImporter');
+    $importer = new \OBOImporter();
+    $importer->create($run_args);
+    $importer->prepareFiles();
+    $importer->run();
+  }
+  
+  
+  /**
+   * Tests that an OBO from a remote URL can be loaded.  
+   * 
+   * For this test we will use the GO Plant Slim.
+   *
+   * @group obo
+   */
+   public function testRemoteOBO() {
+
+    $name = 'core_test_goslim_plant';
+    $path = 'http://www.geneontology.org/ontology/subsets/goslim_plant.obo';
+
+    $this->loadOBO($name, $path);
+
+    // Test that we get all three vocabularies added:  biological_process,
+    // cellular_component and molecular_function.
+    $bp_cv_id = db_select('chado.cv', 'c')
+      ->fields('c', ['cv_id'])
+      ->condition('name', 'biological_process')
+      ->execute()
+      ->fetchField();
+    $this->assertNotFalse($bp_cv_id, 
+      "Missing the 'biological_process' cv record after loading the GO plant slim.");
+
+    $cc_cv_id = db_select('chado.cv', 'c')
+      ->fields('c', ['cv_id'])
+      ->condition('name', 'cellular_component')
+      ->execute()
+      ->fetchField();
+    $this->assertNotFalse($cc_cv_id,
+      "Missing the 'cellular_component' cv record after loading the GO plant slim.");
+
+    $mf_cv_id = db_select('chado.cv', 'c')
+      ->fields('c', ['cv_id'])
+      ->condition('name', 'molecular_function')
+      ->execute()
+      ->fetchField();
+    $this->assertNotFalse($mf_cv_id,
+      "Missing the 'molecular_function' cv record after loading the GO plant slim.");
+    
+    // Make sure we have a proper database record.
+    $go_db_id = db_select('chado.db', 'd')
+      ->fields('d', ['db_id'])
+      ->condition('name', 'GO')
+      ->execute()
+      ->fetchField();
+    $this->assertNotFalse($go_db_id,
+      "Missing the 'GO' database record after loading the GO plant slim.");
+  } 
+  
+  /**
+   * Tests that an OBO from a local path can be loaded.
+   *
+   * For this test we will use a test ontology.
+   *
+   * @group obo
+   */
+  public function testLocalOBO() {
+    $name = 'tripal_obo_test';
+    $path = __DIR__ . '/../example_files/test.obo';
+    
+    $this->loadOBO($name, $path);
+    
+    // Make sure we have a proper vocabulary record.
+    $tot_cv_id = db_select('chado.cv', 'c')
+      ->fields('c', ['cv_id'])
+      ->condition('name', 'tripal_obo_test')
+      ->execute()
+      ->fetchField();
+    $this->assertNotFalse($tot_cv_id,
+      "Missing the 'tripal_obo_test' cv record after loading the test.obo file");
+    
+    // Make sure we have a proper database record.
+    $tot_db_id = db_select('chado.db', 'd')
+      ->fields('d', ['db_id'])
+      ->condition('name', 'TOT')
+      ->execute()
+      ->fetchField();
+    $this->assertNotFalse($tot_db_id,
+      "Missing the 'TOT' db record after loading the test.obo file");
+    
+    return [[$tot_cv_id, $tot_db_id]];
+  }
+
+  /**
+   * Test that all nodes in our test OBO are loaded.
+   * 
+   * @group obo
+   * @dataProvider testLocalOBO
+   */
+
+  public function testCVterms($cv_id, $db_id) {
+
+    // Our test OBO has 14 nodes.
+    $nodes = [
+      ['TOT:001' => 'node01'], 
+      ['TOT:002' => 'node02'], 
+      ['TOT:003' => 'node03'], 
+      ['TOT:004' => 'node04'], 
+      ['TOT:005' => 'node05'], 
+      ['TOT:006' => 'node06'],
+      ['TOT:007' => 'node07'], 
+      ['TOT:008' => 'node08'], 
+      ['TOT:009' => 'node09'],
+      ['TOT:010' => 'node10'], 
+      ['TOT:011' => 'node11'], 
+      ['TOT:012' => 'node12'], 
+      ['TOT:013' => 'node13'],
+      ['TOT:014' => 'node14'],
+    ];
+
+    // Test that the proper records were added to identify the term.    
+    foreach ($nodes as $id => $node_name) {
+      
+      // Check that cvterm record is inserted.
+      $cvterm_id = db_select('chado.cvterm', 'cvt')
+        ->fields('cvt', ['cvterm_id'])
+        ->condition('cvt.name', $node_name)
+        ->condition('cvt.cv_id', $cv_id)
+        ->execute()
+        ->fetchField();
+      $this->assertNotFalse($cvterm_id,
+        "Missing the cvterm record with name, '$node' after loading the test.obo file");
+      
+      // Check that the dbxref record is inserted.
+      $accession = preg_replace('/TOT:/', '', $id);
+      $dbxref_id = db_select('chado.dbxref', 'dbx')
+        ->fields('dbx', ['dbxref_id'])
+        ->condition('accession', $accession)
+        ->condition('db_id', $db_id);
+      $this->assertNotFalse($cvterm_id,
+        "Missing the dbxref record forid, '$id' after loading the test.obo file");
+    }
+    
+    // Test node 11 to make sure the definition was inserted correctly.
+    // The definition for node11 has an extra colon and a comment.  The colon
+    // should not throw off the insertion of the full definition and
+    // the comment should be excluded.
+    $def = db_select('chado.cvterm', 'cvt')
+      ->fields('cvt', ['definition'])
+      ->condition('cvt.name', 'node11')
+      ->condition('cvt.cv_id', $cv_id)
+      ->execute()
+      ->fetchField();
+    $this->assertNotFalse($def,
+      "The definition for node11 was not added.");
+    $this->assertEquals('This is node 11 : Yo', $def,
+      "The definition for node11 is incorrect. it was stored as \"$def\" but should be \"def: This is node 11 : Yo\".");
+    
+    // Make sure that colons in term names don't screw up the term. This test
+    // corresponds to the term with id CHEBI:132502 in the test.obo file.
+    $exists = db_select('chado.cv', 'c')
+      ->fields('c', ['cv_id'])
+      ->condition('name', 'fatty acid 18')
+      ->execute()
+      ->fetchField();
+    $this->assertFalse($exists);
+    
+    
+    // Node14 should be marked as obsolete.
+    $sql = "
+      SELECT CVT.is_obsolete
+      FROM {cvterm} CVT
+         INNER JOIN {dbxref} DBX on DBX.dbxref_id = CVT.dbxref_id
+         INNER JOIN {db} DB on DB.db_id = DBX.db_id
+       WHERE DB.name = 'TOT' and DBX.accession = '014'
+    ";
+    $is_obsolete = chado_query($sql)->fetchField();
+    $this->assertEquals(1, $is_obsolete,
+      "The term, node14, should be marked as obsolete after loading of the test.obo file.");
+    
+    // Every vocabulary should have an is_a term added to support the is_a
+    // relationships.
+    $sql = "
+      SELECT CVT.is_relationshiptype
+      FROM {cvterm} CVT
+         INNER JOIN {dbxref} DBX on DBX.dbxref_id = CVT.dbxref_id
+         INNER JOIN {db} DB on DB.db_id = DBX.db_id
+       WHERE CVT.name = 'is_a' and DB.name = 'TOT'
+    ";
+    $is_reltype = chado_query($sql)->fetchField();
+    $this->assertNotFalse($is_reltype,
+      "The cvterm record for, is_a, should have been added during loading of the test.obo file.");
+    $this->assertEquals(1, $is_reltype, 
+      "The cvterm record, is_a, should be marked as a relationship type.");
+    
+  }
+  
+  /**
+   * Test that insertion of synonyms works.
+   * 
+   * The term 'node11' has a synonym:"crazy node" EXACT []
+   * 
+   * @group obo
+   * @dataProvider testLocalOBO
+   */
+  public function testSynonyms($cv_id, $db_id){
+     
+    $query = db_select('chado.cvtermsynonym', 'cvts');
+    $query->fields('cvts', ['synonym']);
+    $query->join('chado.cvterm', 'cvt', 'cvts.cvterm_id = cvt.cvterm_id');
+    $query->condition('cvt.name', 'node11');
+    $synonym = $query->execute()->fetchField();
+    $this->assertNotFalse($synonym,
+      "Failed to find the 'crazy node' synonym record for node 11 after loading the test.obo file.");
+    
+    $this->assertEquals("crazy node", $synonym,
+      "Failed to properly add the 'crazy node' synonym for node 11 instead the following was loaded: $synonym");
+  }
+  
+  /**
+   * Test that insertion of subset works.
+   *
+   * The term 'node11' belongs to the test_crazy subset. Everything else belongs
+   * to the test_normal subset.
+   * 
+   *
+   * @group obo
+   * @dataProvider testLocalOBO
+   */
+  public function testSubset($cv_id, $db_id) {
+    
+    $sql = "
+      SELECT CVT.name
+      FROM {cvtermprop} CVTP
+        INNER JOIN {cvterm} CVTPT on CVTPT.cvterm_id = CVTP.type_id
+        INNER JOIN {cvterm} CVT on CVT.cvterm_id = CVTP.cvterm_id
+        INNER JOIN {dbxref} DBX on CVT.dbxref_id = DBX.dbxref_id
+        INNER JOIN {db} DB on DB.db_id = DBX.db_id
+      WHERE CVTPT.name = 'Subgroup' and DB.name = 'TOT' and CVTP.value = 'test_crazy'
+    ";
+    $term_name = chado_query($sql)->fetchField();
+    $this->assertNotFalse($term_name,
+      "This cvtermprop record for the subset 'test_crazy' is missing.");
+    
+    $this->assertEquals('node11', $term_name,
+      "This cvtermprop record for the subset 'test_crazy' is assigned to term, $term_name, instead of node11.");
+  
+    $sql = "
+      SELECT count(CVT.cvterm_id)
+      FROM {cvtermprop} CVTP
+        INNER JOIN {cvterm} CVTPT on CVTPT.cvterm_id = CVTP.type_id
+        INNER JOIN {cvterm} CVT on CVT.cvterm_id = CVTP.cvterm_id
+        INNER JOIN {dbxref} DBX on CVT.dbxref_id = DBX.dbxref_id
+        INNER JOIN {db} DB on DB.db_id = DBX.db_id
+      WHERE CVTPT.name = 'Subgroup' and DB.name = 'TOT' and CVTP.value = 'test_normal'
+    ";
+    $subset_count = chado_query($sql)->fetchField();
+    
+    $this->assertNotFalse($subset_count,
+      "This cvtermprop record for the subset 'test_normal' are missing.");
+    
+    // There should be 12 terms that belong to subset 'test_normal' as node14
+    // does not belong to a subset.
+    $this->assertEquals(12, $subset_count,
+      "There are $subset_count cvtermprop record for the subset 'test_normal' but there should be 13.");
+  }
+  
+  /**
+   * Test that the insertion of xref works.
+   *
+   * The term 'node11' belongs to the test_crazy subset. Everything else belongs
+   * to the test_normal subset.
+   *
+   *
+   * @group obo
+   * @dataProvider testLocalOBO
+   */
+  public function testXref($cv_id, $db_id) {
+    
+    $sql = "
+      SELECT concat(DB2.name, ':', DBX2.accession)
+      FROM {cvterm} CVT
+        INNER JOIN {dbxref} DBX on DBX.dbxref_id = CVT.dbxref_id
+        INNER JOIN {db} on DB.db_id = DBX.db_id
+        INNER JOIN {cvterm_dbxref} CVTDBX on CVTDBX.cvterm_id = CVT.cvterm_id
+        INNER JOIN {dbxref} DBX2 on DBX2.dbxref_id = CVTDBX.dbxref_id
+        INNER JOIN {db} DB2 on DB2.db_id = DBX2.db_id
+      WHERE DB.name = 'TOT' and CVT.name = 'node11'
+      ORDER BY DBX.accession
+    ";
+    $xref_id = chado_query($sql)->fetchField();
+    $this->assertNotFalse($xref_id,
+      "This cvterm_dbxref record for the xref 'GO:0043226' is missing for node11.");
+    
+    $this->assertEquals('GO:0043226', $xref_id,
+      "This cvterm_dbxref record for node 11 is, $xref_id, instead of GO:0043226.");
+  }
+  
+  /**
+   * Test that the insertion of comments works.
+   *
+   * The term 'node11' contains a comment.
+   *
+   * @group obo
+   * @dataProvider testLocalOBO
+   */
+  public function testComment($cv_id, $db_id) {
+    
+    $sql = "
+      SELECT CVTP.value
+      FROM {cvterm} CVT
+        INNER JOIN {dbxref} DBX on DBX.dbxref_id = CVT.dbxref_id
+        INNER JOIN {db} on DB.db_id = DBX.db_id
+        INNER JOIN {cvtermprop} CVTP on CVTP.cvterm_id = CVT.cvterm_id
+        INNER JOIN {cvterm} CVTPT on CVTPT.cvterm_id = CVTP.type_id
+      WHERE DB.name = 'TOT' and CVTPT.name = 'comment' and CVT.name = 'node11'
+      ORDER BY DBX.accession
+    ";
+    $comment = chado_query($sql)->fetchField();
+    $this->assertNotFalse($xref_id,
+      "This cvterm_dbxref record for the xref 'This is a crazy node' is missing for node11.");
+    
+    $this->assertEquals('This is a crazy node', $comment,
+      "This cvterm_dbxref record for node11 is, \"$comment\", instead of \"This is a crazy node\".");
+  }
+  
+  /**
+   * Tests that the cvtermpath is properly loaded.
+   *
+   * @group obo
+   * @dataProvider testLocalOBO
+   */
+  public function testRelationships($cv_id, $db_id) {
+    $relationships = [
+      ['node02', 'is_a', 'node01'],
+      ['node03', 'is_a', 'node01'],
+      ['node04', 'is_a', 'node01'],
+      ['node04', 'has_part', 'node11'],
+      ['node05', 'is_a', 'node01'],
+      ['node06', 'is_a', 'node03'],
+      ['node07', 'is_a', 'node03'],
+      ['node08', 'is_a', 'node07'],
+      ['node09', 'is_a', 'node04'],
+      ['node09', 'is_a', 'node07'],
+      ['node10', 'is_a', 'node05'],
+      ['node11', 'is_a', 'node09'],
+      ['node11', 'is_a', 'node10'],
+      ['node12', 'is_a', 'node10'],
+      ['node13', 'is_a', 'node11'],
+    ];
+    foreach ($relationships  as $relationship) {
+      $subject = $relationship[0];
+      $type = $relationship[1];
+      $object = $relationship[2];
+      $sql = "
+        SELECT CVTR.cvterm_relationship_id
+        FROM {cvterm} CVT
+          INNER JOIN {dbxref} DBX on DBX.dbxref_id = CVT.dbxref_id
+          INNER JOIN {db} on DB.db_id = DBX.db_id
+          INNER JOIN {cvterm_relationship} CVTR on CVTR.subject_id = CVT.cvterm_id
+          INNER JOIN {cvterm} CVT2 on CVT2.cvterm_id = CVTR.object_id
+          INNER JOIN {cvterm} CVT3 on CVT3.cvterm_id = CVTR.type_id
+        WHERE DB.name = 'TOT' AND CVT2.name = :object AND 
+          CVT3.name = :type AND CVT.name = :subject
+      ";
+      $args = [':object' => $object, ':type' => $type, ':subject' => $subject];
+      $rel_id = chado_query($sql, $args)->fetchField();
+      $this->assertNotFalse($rel_id,
+        "The following relationship could not be found: $subect $type $object.");
+    }
+    
+    // Now make sure we have no more relationships than what we are supposed
+    // to have.
+    $sql = "
+      SELECT count(CVTR.cvterm_relationship_id)
+      FROM {cvterm} CVT
+        INNER JOIN {dbxref} DBX on DBX.dbxref_id = CVT.dbxref_id
+        INNER JOIN {db} on DB.db_id = DBX.db_id
+        INNER JOIN {cvterm_relationship} CVTR on CVTR.object_id = CVT.cvterm_id
+      WHERE DB.name = 'TOT' AND CVT.is_relationshiptype = 0
+    ";
+    $rel_count = chado_query($sql)->fetchField();
+    $expected = count($relationships);
+    $this->assertEquals($expected, $rel_count,
+      "There are an incorrect number of relationships. There were $rel_count found but there should be $expected.");
+  }
+
+  /**
+   * Tests that the cvtermpath is properly loaded.
+   * 
+   * @group obo
+   * @dataProvider testLocalOBO
+   */
+  public function testCVtermPath($cv_id, $db_id) {
+    
+    // For now we won't include distance or type in the check because depending
+    // how the tree was loaded and if there are multiple paths to a node
+    // then there's no guarantee we'll always get the same path. Therefore the
+    // type and pathdistance may be different (althoug not incorrect).
+    $relationships = [
+      // Node01 as root: note that the root term always has a link to itself
+      // in the cvtermpath table.
+      ['node01', 'node01'],
+      ['node01', 'node02'],
+      ['node01', 'node03'],
+      ['node01', 'node04'],
+      ['node01', 'node05'],
+      ['node01', 'node06'],
+      ['node01', 'node07'],
+      ['node01', 'node08'],
+      ['node01', 'node09'],
+      ['node01', 'node10'],
+      ['node01', 'node11'],
+      ['node01', 'node12'],
+      ['node01', 'node13'],
+      // Node03 as root.
+      ['node03', 'node04'], 
+      ['node03', 'node06'],
+      ['node03', 'node07'],
+      ['node03', 'node08'],
+      ['node03', 'node09'],
+      ['node03', 'node11'],
+      ['node03', 'node13'],
+      // Node04 as root.
+      ['node04', 'node09'],
+      ['node04', 'node11'],
+      ['node04', 'node13'],
+      // Node05 as root.
+      ['node05', 'node04'],
+      ['node05', 'node09'],
+      ['node05', 'node10'],
+      ['node05', 'node11'],
+      ['node05', 'node12'],
+      ['node05', 'node13'],
+      // Node07 as root.
+      ['node07', 'node04'],
+      ['node07', 'node08'],
+      ['node07', 'node09'],
+      ['node07', 'node11'],
+      ['node07', 'node13'],
+      // Node09 as root.
+      ['node09', 'node04'],
+      ['node09', 'node11'],
+      ['node09', 'node13'],
+      // Node10 as root.
+      ['node10', 'node04'],
+      ['node10', 'node09'],
+      ['node10', 'node11'],
+      ['node10', 'node12'],
+      ['node10', 'node13'],
+      // Node11 as root.
+      ['node11', 'node04'],
+      ['node11', 'node09'],
+      ['node11', 'node13'],
+    ];
+
+    // Populate the cvtermpath for our test OBO.
+    chado_update_cvtermpath($cv_id);
+    
+    foreach ($relationships as $relationship) {
+      $object = $relationship[0];
+      $subject = $relationship[1];
+      $sql = "
+        SELECT cvtermpath_id
+        FROM {cvtermpath} CVTP 
+          INNER JOIN {cvterm} CVTO on CVTO.cvterm_id = CVTP.object_id
+          INNER JOIN {cvterm} CVTS on CVTS.cvterm_id = CVTP.subject_id
+          INNER JOIN {cvterm} CVTT on CVTT.cvterm_id = CVTP.type_id
+        WHERE CVTP.cv_id = :cv_id and CVTO.name = :object and 
+          CVTS.name = :subject
+      ";
+      $args = [':cv_id' => $cv_id, ':object' => $object, ':subject' => $subject];
+      $cvtermpath_id = chado_query($sql, $args)->fetchField();
+      $this->assertNotFalse($cvtermpath_id,
+        "Cound not find the cvtermpath record for the relationship: $subject => $object.");
+    }
+
+    // Now make sure we have no additional entries.
+    $sql = "
+          SELECT count(cvtermpath_id)
+          FROM {cvtermpath} CVTP
+            INNER JOIN {cvterm} CVTO on CVTO.cvterm_id = CVTP.object_id
+            INNER JOIN {cvterm} CVTS on CVTS.cvterm_id = CVTP.subject_id
+            INNER JOIN {cvterm} CVTT on CVTT.cvterm_id = CVTP.type_id
+          WHERE CVTP.cv_id = :cv_id
+        ";
+    $args = [':cv_id' => $cv_id];
+    $rel_count = chado_query($sql, $args)->fetchField();
+    $expected = count($relationships);
+    $this->assertEquals($expected, $rel_count,
+      "There are an incorrect number of paths. There were $rel_count found but there should be $expected.");
+  }
+  
+  /**
+   * Tests that the EBI Lookup is properly working.
+   * 
+   * The term CHEBI:132502 should have been loaded via EBI.
+   * 
+   * @group obo
+   * @dataProvider testLocalOBO
+   */
+  public function testEBILookup($cv_id, $db_id) {
+    $sql = "
+       SELECT CVT.cvterm_id
+       FROM  {cvterm} CVT
+         INNER JOIN {dbxref} DBX on DBX.dbxref_id = CVT.dbxref_id
+         INNER JOIN {db} DB on DB.db_id = DBX.db_id
+       WHERE DB.name = 'CHEBI' and DBX.accession = '132502'
+    ";
+    $cvterm_id = chado_query($sql)->fetchField();
+    $this->assertNotFalse($cvterm_id,
+      "The term, CHEBI:132502, is not present the EBI OLS lookup must not have succeeded.");
+  }
+  
+  /**
+   * Tests when changes are made between OBO loads.
+   *
+   * Sometimes an ontology can change the names of it's terms, or set some
+   * as obsolete, etc. We need to makes sure that when changes are made and
+   * the OBO is reloaded that the terms are properly update.
+   *
+   * @group obo
+   * @dataProvider testLocalOBO
+   */
+  public function testOBOChanges($cv_id, $db_id) {
+    $name = 'tripal_obo_test_update';
+    $path = __DIR__ . '/../example_files/test.update.obo';
+    
+    $this->loadOBO($name, $path);
+    
+    // Did the name of term 13 change?
+    $sql = "
+      SELECT CVT.name
+      FROM {cvterm} CVT
+         INNER JOIN {dbxref} DBX on DBX.dbxref_id = CVT.dbxref_id
+         INNER JOIN {db} DB on DB.db_id = DBX.db_id
+       WHERE DB.name = 'TOT' and DBX.accession = '013'
+    ";
+    $name = chado_query($sql)->fetchField();
+    $this->assertEquals('New name 13.', $name,
+      "The name for node13 (TOT:013) failed to update to 'New name 13'.");
+    
+    // Node15 is new, and node02 got removed. Node15 now uses node02's name and
+    // has TOT:002 as an alt_id. So, node02 should be marked as obsolete
+    $sql = "
+      SELECT CVT.is_obsolete
+      FROM {cvterm} CVT
+         INNER JOIN {dbxref} DBX on DBX.dbxref_id = CVT.dbxref_id
+         INNER JOIN {db} DB on DB.db_id = DBX.db_id
+       WHERE DB.name = 'TOT' and DBX.accession = '002'
+    ";
+    $is_obsolete = chado_query($sql)->fetchField();
+    $this->assertEquals(1, $is_obsolete,
+      "The node02 (TOT:002) should be marked as obsolete after update.");
+    
+    // Node16 is new, and node08 is now obsolete. Node16 now uses node08's name,
+    // so, node08 should be marked as obsolete and have the word '(obsolete)'
+    // added to prevent future conflicts.
+    $sql = "
+      SELECT CVT.name
+      FROM {cvterm} CVT
+         INNER JOIN {dbxref} DBX on DBX.dbxref_id = CVT.dbxref_id
+         INNER JOIN {db} DB on DB.db_id = DBX.db_id
+       WHERE DB.name = 'TOT' and DBX.accession = '008'
+    ";
+    $name = chado_query($sql)->fetchField();
+    $this->assertEquals("node08 (obsolete)", $name,
+      "The node08 (TOT:008) should be marked as obsolete after update.");
+  }
+}

+ 4 - 4
tripal/api/tripal.DEPRECATED.api.inc

@@ -32,10 +32,10 @@
  */
  */
 function tripal_get_job_end($job) {
 function tripal_get_job_end($job) {
   tripal_report_error('tripal_deprecated', TRIPAL_NOTICE,
   tripal_report_error('tripal_deprecated', TRIPAL_NOTICE,
-    "DEPRECATED: %function has been removed from the API the end date " .
+    "DEPRECATED: %old_function has been removed from the API the end date " .
     "is now accessible via the %property property. Please update your code.",
     "is now accessible via the %property property. Please update your code.",
     array(
     array(
-      '%old_function' => 'tripal_jobs_get_end_time',
+      '%old_function' => 'tripal_get_job_end',
       '%property' => '\$job->end_time_string',
       '%property' => '\$job->end_time_string',
     )
     )
   );
   );
@@ -66,7 +66,7 @@ function tripal_get_job_end($job) {
 function tripal_get_job_start($job) {
 function tripal_get_job_start($job) {
 
 
   tripal_report_error('tripal_deprecated', TRIPAL_NOTICE,
   tripal_report_error('tripal_deprecated', TRIPAL_NOTICE,
-    "DEPRECATED: %function has been removed from the API the end date " .
+    "DEPRECATED: %old_function has been removed from the API the end date " .
     "is now accessible via the %property property. Please update your code.",
     "is now accessible via the %property property. Please update your code.",
     array(
     array(
       '%old_function' => 'tripal_get_job_start',
       '%old_function' => 'tripal_get_job_start',
@@ -104,7 +104,7 @@ function tripal_get_job_start($job) {
 function tripal_get_job_submit_date($job) {
 function tripal_get_job_submit_date($job) {
 
 
   tripal_report_error('tripal_deprecated', TRIPAL_NOTICE,
   tripal_report_error('tripal_deprecated', TRIPAL_NOTICE,
-      "DEPRECATED: %function has been removed from the API the end date " .
+      "DEPRECATED: %old_function has been removed from the API the end date " .
       "is now accessible via the %property property. Please update your code.",
       "is now accessible via the %property property. Please update your code.",
       array(
       array(
         '%old_function' => 'tripal_get_job_submit_date',
         '%old_function' => 'tripal_get_job_submit_date',

+ 0 - 2
tripal/api/tripal.notice.api.inc

@@ -63,8 +63,6 @@ define('TRIPAL_DEBUG',7);
  *   An array of options. Some available options include:
  *   An array of options. Some available options include:
  *     - print: prints the error message to the terminal screen. Useful when
  *     - print: prints the error message to the terminal screen. Useful when
  *       display is the command-line
  *       display is the command-line
- *     - drupal_set_message: set to TRUE to call drupal_set_message with the 
- *       same error.
  *     - drupal_set_message:  set to TRUE then send the message to the
  *     - drupal_set_message:  set to TRUE then send the message to the
  *       drupal_set_message function.
  *       drupal_set_message function.
  *     - watchdog:  set to FALSE to disable logging to Drupal's watchdog.
  *     - watchdog:  set to FALSE to disable logging to Drupal's watchdog.

+ 0 - 2
tripal/api/tripal.terms.api.inc

@@ -155,7 +155,6 @@ function hook_vocab_get_term($vocabulary, $accession) {
 function hook_vocab_get_terms($vocabulary, $limit = 25, $element = 0) {
 function hook_vocab_get_terms($vocabulary, $limit = 25, $element = 0) {
   // See the tripal_chado_vocab_get_terms() function for an example.
   // See the tripal_chado_vocab_get_terms() function for an example.
 }
 }
-
 /**
 /**
  * Hook used by the default term storage backend to provide children for a term.
  * Hook used by the default term storage backend to provide children for a term.
  *
  *
@@ -423,7 +422,6 @@ function tripal_get_vocabulary_root_terms($vocabulary) {
     }
     }
   }
   }
 }
 }
-
 /**
 /**
  * Retrieves the immediate children of the given term.
  * Retrieves the immediate children of the given term.
  *
  *

+ 19 - 14
tripal/includes/TripalImporter.inc

@@ -204,6 +204,11 @@ class TripalImporter {
   protected $is_prepared;
   protected $is_prepared;
 
 
 
 
+  /**
+   * Stores the last percentage that progress was reported.
+   * @var integer
+   */
+  protected $reported = 0;
 
 
   // --------------------------------------------------------------------------
   // --------------------------------------------------------------------------
   //                          CONSTRUCTORS
   //                          CONSTRUCTORS
@@ -632,37 +637,37 @@ class TripalImporter {
 
 
     if ($total_handled == 0) {
     if ($total_handled == 0) {
       $memory = number_format(memory_get_usage());
       $memory = number_format(memory_get_usage());
-      print "Percent complete: 0%. Memory: " . $memory . " bytes.\r";
+      print t("Percent complete: 0%. Memory: !memory  bytes.", ['!memory' => $memory]) . "\r";
       return;
       return;
     }
     }
 
 
     // Now see if we need to report to the user the percent done.  A message
     // Now see if we need to report to the user the percent done.  A message
     // will be printed on the command-line if the job is run there.
     // will be printed on the command-line if the job is run there.
-    $percent = sprintf("%.2f", ($this->num_handled / $this->total_items) * 100);
-    $diff = $percent - $this->prev_update;
-
-    if ($diff >= $this->interval) {
-
+    $percent = ($this->num_handled / $this->total_items) * 100;
+    $ipercent  = (int) $percent;
+    
+    // If we've reached our interval then print update info.
+    if ($ipercent > 0 and $ipercent != $this->reported and $ipercent % $this->interval == 0) {
       $memory = number_format(memory_get_usage());
       $memory = number_format(memory_get_usage());
-      print "Percent complete: " . $percent . "%. Memory: " . $memory . " bytes.\r";
+      $spercent = sprintf("%.2f", $percent);
+      print t("Percent complete: !percent %. Memory: !memory bytes.", 
+        ['!percent' => $spercent, '!memory' => $memory]) . "\r";
 
 
       // If we have a job the update the job progress too.
       // If we have a job the update the job progress too.
       if ($this->job) {
       if ($this->job) {
         $this->job->setProgress($percent);
         $this->job->setProgress($percent);
       }
       }
-
-      $this->prev_update = $diff;
+      $this->reported = $ipercent;
     }
     }
   }
   }
 
 
   /**
   /**
    * Updates the percent interval when the job progress is updated.
    * Updates the percent interval when the job progress is updated.
    *
    *
-   * Updating the job
-   * progress incurrs a database write which takes time and if it occurs to
-   * frequently can slow down the loader.  This should be a value between
-   * 0 and 100 to indicate a percent interval (e.g. 1 means update the
-   * progress every time the num_handled increases by 1%).
+   * Updating the job progress incurrs a database write which takes time 
+   * and if it occurs to frequently can slow down the loader.  This should 
+   * be a value between 0 and 100 to indicate a percent interval (e.g. 1 
+   * means update the progress every time the num_handled increases by 1%).
    *
    *
    * @param $interval
    * @param $interval
    *   A number between 0 and 100.
    *   A number between 0 and 100.

+ 38 - 14
tripal/includes/TripalJob.inc

@@ -33,13 +33,27 @@ class TripalJob {
    * 0 and 100 to indicate a percent interval (e.g. 1 means update the
    * 0 and 100 to indicate a percent interval (e.g. 1 means update the
    * progress every time the num_handled increases by 1%).
    * progress every time the num_handled increases by 1%).
    */
    */
-  private $interval;
-
+  private $interval;  
+  
+  /**
+   * The time stamp when the job begins.
+   * 
+   * @var integer
+   */
+  private $start_time;
+  
+  /**
+   * The time from when the setTotalItems is called to the present time.
+   * 
+   * @var
+   */
+  private $progress_start_time;
+  
   /**
   /**
-   * Each time the job progress is updated this variable gets set.  It is
-   * used to calculate if the $interval has passed for the next update.
+   * Stores the last percentage that progress was reported.
+   * @var integer
    */
    */
-  private $prev_update;
+  private $reported = 0;
 
 
   /**
   /**
    * Instantiates a new TripalJob object.
    * Instantiates a new TripalJob object.
@@ -277,6 +291,9 @@ class TripalJob {
    * Executes the job.
    * Executes the job.
    */
    */
   public function run() {
   public function run() {
+    
+    $this->start_time = time();
+    $this->progress_start_time = time();
 
 
     if (!$this->job) {
     if (!$this->job) {
       throw new Exception('Cannot launch job as no job is associated with this object.');
       throw new Exception('Cannot launch job as no job is associated with this object.');
@@ -296,7 +313,7 @@ class TripalJob {
       // Set the start time for this job.
       // Set the start time for this job.
       $record = new stdClass();
       $record = new stdClass();
       $record->job_id = $this->job->job_id;
       $record->job_id = $this->job->job_id;
-      $record->start_time = time();
+      $record->start_time = $this->start_time;
       $record->status = 'Running';
       $record->status = 'Running';
       $record->pid = getmypid();
       $record->pid = getmypid();
       drupal_write_record('tripal_jobs', $record, 'job_id');
       drupal_write_record('tripal_jobs', $record, 'job_id');
@@ -447,6 +464,8 @@ class TripalJob {
    *   The total number of items to process.
    *   The total number of items to process.
    */
    */
   public function setTotalItems($total_items) {
   public function setTotalItems($total_items) {
+    $this->progress_start_time = time();
+    
     $this->total_items = $total_items;
     $this->total_items = $total_items;
   }
   }
 
 
@@ -472,6 +491,7 @@ class TripalJob {
    *   The total number of items that have been processed.
    *   The total number of items that have been processed.
    */
    */
   public function setItemsHandled($total_handled) {
   public function setItemsHandled($total_handled) {
+    
     // First set the number of items handled.
     // First set the number of items handled.
     $this->num_handled = $total_handled;
     $this->num_handled = $total_handled;
 
 
@@ -483,15 +503,19 @@ class TripalJob {
 
 
     // Now see if we need to report to the user the percent done.  A message
     // Now see if we need to report to the user the percent done.  A message
     // will be printed on the command-line if the job is run there.
     // will be printed on the command-line if the job is run there.
-    $percent = sprintf("%.2f", ($this->num_handled / $this->total_items) * 100);
-    $diff = $percent - $this->prev_update;
-
-    if ($diff >= $this->interval) {
-
-      $memory = number_format(memory_get_usage());
-      print "Percent complete: " . $percent . "%. Memory: " . $memory . " bytes.\r";
-      $this->prev_update = $diff;
+    $percent = ($this->num_handled / $this->total_items) * 100;
+    $ipercent = (int) $percent;
+
+    // If we've reached our interval then print update info.
+    if ($ipercent > 0 and $ipercent != $this->reported and ($ipercent % $this->interval) == 0) {
+      $duration = (time() - $this->progress_start_time) / 60;
+      $duration = sprintf("%.2f", $duration);
+      $memory = memory_get_usage();
+      $fmemory = number_format($memory);
+      $spercent = sprintf("%d", $percent);
+      print "Percent complete: " . $spercent . "%. Memory: " . $fmemory . " bytes. Duration: " . $duration . " mins\r";
       $this->setProgress($percent);
       $this->setProgress($percent);
+      $this->reported = $ipercent;
     }
     }
   }
   }
 
 

+ 2 - 2
tripal/includes/tripal.jobs.inc

@@ -347,7 +347,7 @@ function tripal_jobs_view($job_id) {
     '#collapsed' => TRUE,
     '#collapsed' => TRUE,
     '#collapsible' => TRUE,
     '#collapsible' => TRUE,
     '#attributes' => array(
     '#attributes' => array(
-      'class' => array('collapsible', 'collapsed'),
+      'class' => array('collapsible'),
     ),
     ),
     '#attached' => array(
     '#attached' => array(
       'js' => array('misc/collapse.js', 'misc/form.js')
       'js' => array('misc/collapse.js', 'misc/form.js')
@@ -363,7 +363,7 @@ function tripal_jobs_view($job_id) {
     '#collapsed' => TRUE,
     '#collapsed' => TRUE,
     '#collapsible' => TRUE,
     '#collapsible' => TRUE,
     '#attributes' => array(
     '#attributes' => array(
-      'class' => array('collapsible', 'collapsed'),
+      'class' => array('collapsible'),
     ),
     ),
     '#attached' => array(
     '#attached' => array(
       'js' => array('misc/collapse.js', 'misc/form.js')
       'js' => array('misc/collapse.js', 'misc/form.js')

+ 140 - 107
tripal/includes/tripal.term_lookup.inc

@@ -59,6 +59,8 @@ function tripal_vocabulary_lookup_vocab_page($vocabulary) {
   drupal_set_breadcrumb($breadcrumb);
   drupal_set_breadcrumb($breadcrumb);
 
 
   $vocab = tripal_get_vocabulary_details($vocabulary);
   $vocab = tripal_get_vocabulary_details($vocabulary);
+  $vocab_table = tripal_vocabulary_get_vocab_details($vocab);
+  
   if ($vocab['description']) {
   if ($vocab['description']) {
     drupal_set_title($vocabulary . ': ' . $vocab['description']);
     drupal_set_title($vocabulary . ': ' . $vocab['description']);
   }
   }
@@ -71,58 +73,7 @@ function tripal_vocabulary_lookup_vocab_page($vocabulary) {
     drupal_set_message('The vocabulary cannot be found on this site', 'error');
     drupal_set_message('The vocabulary cannot be found on this site', 'error');
     return '';
     return '';
   }
   }
-
-  $headers = array();
-  $rows = array();
-  $vocab_name = $vocab['name'];
-  $short_name = $vocab['short_name'];
-  if ($vocab['url']) {
-    $short_name = l($vocab['short_name'], $vocab['url'], array('attributes' => array('target' => '_blank')));
-  }
-  $vocab_desc = $vocab['description'];
-  $rows[] = array(
-    array(
-      'data' => 'Short Name',
-      'header' => TRUE,
-      'width' => '20%',
-    ),
-    $short_name,
-  );
-  $rows[] = array(
-    array(
-      'data' => 'Vocabulary Name(s)',
-      'header' => TRUE,
-      'width' => '20%',
-    ),
-    $vocab_name,
-  );
-  $rows[] = array(
-    array(
-      'data' => 'Description',
-      'header' => TRUE,
-      'width' => '20%',
-    ),
-    $vocab_desc,
-  );
-  $rows[] = array(
-    array(
-      'data' => 'Number of Terms Loaded on This Site',
-      'header' => TRUE,
-      'width' => '20%',
-    ),
-    number_format($vocab['num_terms']),
-  );
-
-  $table = array(
-    'header' => $headers,
-    'rows' => $rows,
-    'attributes' => array(),
-    'sticky' => FALSE,
-    'caption' => '',
-    'colgroups' => array(),
-    'empty' => '',
-  );
-
+  
   $has_root = TRUE;
   $has_root = TRUE;
   $root_terms = tripal_get_vocabulary_root_terms($vocabulary);
   $root_terms = tripal_get_vocabulary_root_terms($vocabulary);
   // If this vocabulary doesn't have root terms then it's either not an
   // If this vocabulary doesn't have root terms then it's either not an
@@ -156,7 +107,9 @@ function tripal_vocabulary_lookup_vocab_page($vocabulary) {
     'vocab_table' => array(
     'vocab_table' => array(
       '#type' => 'item',
       '#type' => 'item',
       '#title' => 'Details',
       '#title' => 'Details',
-      '#markup' => '<p>A vocabulary is always identified by its short name and sometimes it may offer multiple sub-vocabularies with different names. Both are listed below.</p>' . theme_table($table),
+      '#markup' => '<p>A vocabulary is always identified by its short name ' . 
+        'and sometimes it may offer multiple sub-vocabularies with different ' . 
+        'names. Both are listed below.</p>' . $vocab_table,
     ),
     ),
     'vocab_browser' => array(
     'vocab_browser' => array(
       '#type' => 'item',
       '#type' => 'item',
@@ -178,6 +131,69 @@ function tripal_vocabulary_lookup_vocab_page($vocabulary) {
   return $content;
   return $content;
 }
 }
 
 
+/**
+ * Generates a table view of the vocabulary.
+ * 
+ * @param $vocab
+ *   The vocabulary array.
+ * @return 
+ *    An HTML rendered table describing the vocabulary.
+ */
+function tripal_vocabulary_get_vocab_details($vocab) {
+  $headers = array();
+  $rows = array();
+  $vocab_name = $vocab['name'];
+  $short_name = $vocab['short_name'];
+  if ($vocab['url']) {
+    $short_name = l($vocab['short_name'], $vocab['url'], array('attributes' => array('target' => '_blank')));
+  }
+  $vocab_desc = $vocab['description'];
+  $rows[] = array(
+    array(
+      'data' => 'Short Name',
+      'header' => TRUE,
+      'width' => '20%',
+    ),
+    $short_name,
+  );
+  $rows[] = array(
+    array(
+      'data' => 'Vocabulary Name(s)',
+      'header' => TRUE,
+      'width' => '20%',
+    ),
+    $vocab_name,
+  );
+  $rows[] = array(
+    array(
+      'data' => 'Description',
+      'header' => TRUE,
+      'width' => '20%',
+    ),
+    $vocab_desc,
+  );
+  $rows[] = array(
+    array(
+      'data' => 'Number of Terms Loaded on This Site',
+      'header' => TRUE,
+      'width' => '20%',
+    ),
+    number_format($vocab['num_terms']),
+  );
+  
+  $table = array(
+    'header' => $headers,
+    'rows' => $rows,
+    'attributes' => array(),
+    'sticky' => FALSE,
+    'caption' => '',
+    'colgroups' => array(),
+    'empty' => '',
+  );
+  
+  return theme_table($table);
+}
+
 /**
 /**
  * A helper function to format an array of terms into a list for the web page.
  * A helper function to format an array of terms into a list for the web page.
  *
  *
@@ -203,9 +219,6 @@ function tripal_vocabulary_lookup_term_children_format($children) {
     if ($child['accession'] != $child['name']) {
     if ($child['accession'] != $child['name']) {
       $items .= ' [' . $child['vocabulary']['short_name'] . ':' . $child['accession'] . '] ';
       $items .= ' [' . $child['vocabulary']['short_name'] . ':' . $child['accession'] . '] ';
     }
     }
-    if ($num_grand > 0) {
-      $items .= ' (' . $num_grand . ')';
-    }
     $items .= '</li>';
     $items .= '</li>';
   }
   }
   $items .= '</ul>';
   $items .= '</ul>';
@@ -257,9 +270,10 @@ function tripal_vocabulary_lookup_term_page($vocabulary, $accession) {
   $breadcrumb[] = l($vocabulary, 'cv/lookup/' . $vocabulary);
   $breadcrumb[] = l($vocabulary, 'cv/lookup/' . $vocabulary);
   drupal_set_breadcrumb($breadcrumb);
   drupal_set_breadcrumb($breadcrumb);
 
 
+  $vocab = tripal_get_vocabulary_details($vocabulary);
+  $vocab_table = tripal_vocabulary_get_vocab_details($vocab);
+  
   $term = tripal_get_term_details($vocabulary, $accession);
   $term = tripal_get_term_details($vocabulary, $accession);
-  drupal_set_title($term['name']);
-
 
 
   // If we can't find the term then just return a message.
   // If we can't find the term then just return a message.
   if (!$term) {
   if (!$term) {
@@ -272,8 +286,12 @@ function tripal_vocabulary_lookup_term_page($vocabulary, $accession) {
   $rows = array();
   $rows = array();
   $term_name = $term['name'];
   $term_name = $term['name'];
   $accession = $term['vocabulary']['short_name'] . ':' . $term['accession'];
   $accession = $term['vocabulary']['short_name'] . ':' . $term['accession'];
-  if ($term['url']) {
-    $term_name = l($term['name'], $term['url'], array('attributes' => array('target' => '_blank')));
+  drupal_set_title($accession);
+  
+  // Create a URL to point this term to it's source page, but only if the
+  // source is not this site.
+  if ($term['url'] and !preg_match('/cv\/lookup/', $term['url'])) {
+    $accession = l($accession, $term['url'], array('attributes' => array('target' => '_blank')));
   }
   }
   $rows[] = array(
   $rows[] = array(
     array(
     array(
@@ -299,63 +317,78 @@ function tripal_vocabulary_lookup_term_page($vocabulary, $accession) {
     ),
     ),
     $term['definition'],
     $term['definition'],
   );
   );
-
-
-  $table = array(
-    'header' => $headers,
-    'rows' => $rows,
-    'attributes' => array(),
-    'sticky' => FALSE,
-    'caption' => 'Term Details',
-    'colgroups' => array(),
-    'empty' => '',
-  );
-  $content = theme_table($table);
-
-
-  $rows = array();
-  $vocab_name = $term['vocabulary']['name'];
-  if ($term['vocabulary']['url']) {
-    $vocab_name = l($term['vocabulary']['name'], $term['vocabulary']['url'], array('attributes' => array('target' => '_blank')));
+  
+  // Now iterate through any other columns in the term array and add those
+  // details.
+  foreach ($term as $key => $value) {
+    if (in_array($key, ['name', 'definition', 'vocabulary', 'accession', 'url'])) {
+      continue;
+    }
+    // Convert thisto an array so we can alter it.
+    if (!is_array($value)) {
+      $new_values[] = $value;
+      $value = $new_values;
+    }
+    // If this is a relationship key then let's try to rewrite the GO 
+    // term in the relationship as a link.
+    if ($key == 'relationship') {
+      foreach ($value as $index => $v) {
+        $matches = [];
+        if (preg_match('/^(.+)\s(.+?):(.+?)$/', $v, $matches)) {
+          $rel = $matches[1];
+          $voc = $matches[2];
+          $acc = $matches[3];
+          $v = $rel . ' ' . l($voc . ':' . $acc, 'cv/lookup/' . $voc . '/' . $acc, ['attributes' => ['target' => '_blank']]);
+          $t = tripal_get_term_details($voc, $acc);
+          if ($t) {
+            $v .= ' (' . $t['name'] . ')';
+          }
+          $value[$index] = $v;
+        }
+      }
+    }
+    if (count($value) > 1) {
+      $value_str = theme_item_list([
+        'items' => $value,
+        'type' => 'ul',
+        'attributes' => [],
+        'title' => '',
+      ]);
+    }
+    else {
+      $value_str = $value[0];
+    }
+    $rows[] = array(
+      array(
+        'data' => ucfirst($key),
+        'header' => TRUE,
+        'width' => '20%',
+      ),
+      $value_str,
+    );
   }
   }
-  $short_name = $term['vocabulary']['short_name'];
-  $vocab_desc = $term['vocabulary']['description'];
-  $rows[] = array(
-    array(
-      'data' => 'Name',
-      'header' => TRUE,
-      'width' => '20%',
-    ),
-    $vocab_name,
-  );
-  $rows[] = array(
-    array(
-      'data' => 'Short Name',
-      'header' => TRUE,
-      'width' => '20%',
-    ),
-    $short_name,
-  );
-  $rows[] = array(
-    array(
-      'data' => 'Description',
-      'header' => TRUE,
-      'width' => '20%',
-    ),
-    $vocab_desc,
-  );
 
 
   $table = array(
   $table = array(
     'header' => $headers,
     'header' => $headers,
     'rows' => $rows,
     'rows' => $rows,
     'attributes' => array(),
     'attributes' => array(),
     'sticky' => FALSE,
     'sticky' => FALSE,
-    'caption' => 'Term Vocabulary details',
+    'caption' => '',
     'colgroups' => array(),
     'colgroups' => array(),
     'empty' => '',
     'empty' => '',
   );
   );
-  $content .=  theme_table($table);
-
+  $content['cvterm'] = [
+    '#type' => 'item',
+    '#title' => 'Term Details',
+    '#markup' => theme_table($table),
+  ];
+  
+  $content['vocabulary'] = [
+    '#type' => 'item',
+    '#title' => 'Vocabulary Details',
+    '#markup' => $vocab_table,
+  ];
+  
   drupal_add_js(array(
   drupal_add_js(array(
     'tripal' => array(
     'tripal' => array(
       'cv_lookup' => array(
       'cv_lookup' => array(

+ 79 - 37
tripal_chado/api/ChadoRecord.inc

@@ -170,11 +170,11 @@ class ChadoRecord {
       foreach ($col_schema as $param => $val) {
       foreach ($col_schema as $param => $val) {
         if (preg_match('/not null/i', $param) and $col_schema[$param]) {
         if (preg_match('/not null/i', $param) and $col_schema[$param]) {
           $this->required_cols[] = $column;
           $this->required_cols[] = $column;
+          // Currently all required columns are missing.
+          $this->missing_required_col[$column] = TRUE;
         }
         }
       }
       }
     }
     }
-    // Currently all required columns are missing.
-    $this->missing_required_col = $this->required_cols;
 
 
     // If a record_id was provided then lookup the record and set the values.
     // If a record_id was provided then lookup the record and set the values.
     if ($record_id) {
     if ($record_id) {
@@ -189,6 +189,7 @@ class ChadoRecord {
         }
         }
         $this->record_id = $record_id;
         $this->record_id = $record_id;
         $this->values = $values;
         $this->values = $values;
+        $this->missing_required_col = [];
       }
       }
       catch (Exception $e) {
       catch (Exception $e) {
         $message = t('ChadoRecord::_construct(). Could not find a record in table, !table, with the given !pkey: !record_id. ERROR: !error',
         $message = t('ChadoRecord::_construct(). Could not find a record in table, !table, with the given !pkey: !record_id. ERROR: !error',
@@ -276,8 +277,12 @@ class ChadoRecord {
 
 
     // Additionally, make sure we have all the required values!
     // Additionally, make sure we have all the required values!
     if (!empty($this->missing_required_col)) {
     if (!empty($this->missing_required_col)) {
-      $message = t('ChadoRecord::insert(). The columns named, "!columns", require a value for the table: "!table". You can set these values using ChadoRecord::setValues().',
-        ['!columns' => implode('", "', $this->missing_required_col), '!table' => $this->table_name]);
+      $message = t('ChadoRecord::insert(). The columns named, "!columns", ' . 
+        'require a value for the table: "!table". You can set these values ' .
+        'using ChadoRecord::setValues(). Current values: !values.',
+        ['!columns' => implode('", "', array_keys($this->missing_required_col)), 
+         '!table' => $this->table_name,
+         '!values' => print_r($this->values, TRUE)]);
       throw new Exception($message);
       throw new Exception($message);
     }
     }
 
 
@@ -293,18 +298,23 @@ class ChadoRecord {
     $sql = 'INSERT INTO {' . $this->table_name . '} (' .
     $sql = 'INSERT INTO {' . $this->table_name . '} (' .
       implode(", ", $insert_cols) . ') VALUES (' .
       implode(", ", $insert_cols) . ') VALUES (' .
       implode(", ", $insert_vals) . ')';
       implode(", ", $insert_vals) . ')';
-
+      if ($this->pkey) {
+        $sql .= ' RETURNING ' . $this->pkey;
+      }
     try {
     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.
-      // One option may be to use the `RETURNING [pkey]` keywords in the SQL statement.
-      $this->find();
+      $result = chado_query($sql, $insert_args);
+      if ($this->pkey) {
+        $record_id = $result->fetchField();
+        $this->values[$this->pkey] = $record_id;
+        $this->record_id = $record_id;
+      }
     }
     }
     catch (Exception $e) {
     catch (Exception $e) {
-      $message = t('ChadoRecord::insert(). Could not insert a record into the table, !table, with the following values: !values. ERROR: !error',
-        ['!table' => $this->table_name, '!values' => print_r($this->values, TRUE), '!error' => $e->getMessage()]);
+      $message = t('ChadoRecord::insert(). Could not insert a record into the ' . 
+        'table, !table, with the following values: !values. ERROR: !error',
+        ['!table' => $this->table_name, 
+         '!values' => print_r($this->values, TRUE), 
+         '!error' => $e->getMessage()]);
       throw new Exception($message);
       throw new Exception($message);
     }
     }
   }
   }
@@ -323,22 +333,29 @@ class ChadoRecord {
 
 
     // Make sure we have values for this record before updating.
     // Make sure we have values for this record before updating.
     if (empty($this->values)) {
     if (empty($this->values)) {
-      $message = t('ChadoRecord::update(). Could not update a record into the table, !table, without any values.',
+      $message = t('ChadoRecord::update(). Could not update a record into the ' . 
+        'table, !table, without any values.',
         ['!table' => $this->table_name]);
         ['!table' => $this->table_name]);
       throw new Exception($message);
       throw new Exception($message);
     }
     }
 
 
     // Additionally, make sure we have all the required values!
     // Additionally, make sure we have all the required values!
     if (!empty($this->missing_required_col)) {
     if (!empty($this->missing_required_col)) {
-      $message = t('ChadoRecord::update(). The columns named, "!columns", require a value for the table: "!table". You can set these values using ChadoRecord::setValues().',
-        ['!columns' => implode('", "', $this->missing_required_col), '!table' => $this->table_name]);
+      $message = t('ChadoRecord::update(). The columns named, "!columns", ' . 
+        'require a value for the table: "!table". You can set these values ' . 
+        'using ChadoRecord::setValues(). Current values: !values.',
+        ['!columns' => implode('", "', $this->missing_required_col), 
+         '!table' => $this->table_name,
+         '!values' => print_r($this->values, TRUE)]);
       throw new Exception($message);
       throw new Exception($message);
     }
     }
 
 
     // We have to have a record ID for the record to update.
     // We have to have a record ID for the record to update.
     if (!$this->record_id) {
     if (!$this->record_id) {
-      $message = t('ChadoRecord::update(). Could not update a record in the table, !table, without a record ID.',
-        ['!table' => $this->table_name]);
+      $message = t('ChadoRecord::update(). Could not update a record in the ' .
+        'table, !table, without a record ID. Current values: !values.',
+        ['!table' => $this->table_name,
+         '!values' => print_r($this->values, TRUE)]);
       throw new Exception($message);
       throw new Exception($message);
     }
     }
 
 
@@ -363,7 +380,9 @@ class ChadoRecord {
       chado_query($sql, $update_args);
       chado_query($sql, $update_args);
     }
     }
     catch (Exception $e) {
     catch (Exception $e) {
-      $message = t('ChadoRecord::update(). Could not update a record in the table, !table, with !record_id as the record ID and the following values: !values. ERROR: !error',
+      $message = t('ChadoRecord::update(). Could not update a record in the ' .
+        'table, !table, with !record_id as the record ID and the following ' .
+        'values: !values. ERROR: !error',
         ['!table' => $this->table_name,
         ['!table' => $this->table_name,
          '!record_id' => $this->record_id,
          '!record_id' => $this->record_id,
          '!values' => print_r($this->values, TRUE),
          '!values' => print_r($this->values, TRUE),
@@ -431,8 +450,11 @@ class ChadoRecord {
         $this->values[$column] = $value;
         $this->values[$column] = $value;
       }
       }
       else {
       else {
-        $message = t('ChadoRecord::setValues(). The column named, "!column", does not exist in table: "!table". Values: !values".',
-          ['!column' => $column, '!table' => $this->table_name, '!values' => print_r($values, TRUE)]);
+        $message = t('ChadoRecord::setValues(). The column named, "!column", ' . 
+          'does not exist in table: "!table". Values: !values".',
+          ['!column' => $column, 
+           '!table' => $this->table_name, 
+           '!values' => print_r($values, TRUE)]);
         throw new Exception($message);
         throw new Exception($message);
       }
       }
     }
     }
@@ -448,7 +470,7 @@ class ChadoRecord {
       }
       }
 
 
       if (in_array($rcol, array_keys($this->values)) and $this->values[$rcol] === '__NULL__') {
       if (in_array($rcol, array_keys($this->values)) and $this->values[$rcol] === '__NULL__') {
-        $this->missing_required_col[$rcol] = $rcol;
+        $this->missing_required_col[$rcol] = TRUE;
       }
       }
     }
     }
 
 
@@ -460,8 +482,13 @@ class ChadoRecord {
     // Ensure that no values are arrays.
     // Ensure that no values are arrays.
     foreach ($values as $column => $value) {
     foreach ($values as $column => $value) {
       if (is_array($value)) {
       if (is_array($value)) {
-        $message = t('ChadoRecord::setValues(). The column named, "!column", must be a single value but is currently: "!values". NOTE: we currently don\'t support expanding foreign key relationships or multiple values for a given column.',
-          ['!column' => $column, '!table' => $this->table_name, '!values' => implode('", "', $value)]);
+        $message = t('ChadoRecord::setValues(). The column named, "!column", ' . 
+          'must be a single value but is currently: "!values". NOTE: this function ' .
+          'currently does not support expanding foreign key relationships or ' .
+          'multiple values for a given column.',
+          ['!column' => $column, 
+           '!table' => $this->table_name, 
+           '!values' => implode('", "', $value)]);
         throw new Exception($message);
         throw new Exception($message);
       }
       }
     }
     }
@@ -496,27 +523,36 @@ class ChadoRecord {
 
 
     // Make sure the column is valid.
     // Make sure the column is valid.
     if (!in_array($column_name, $this->column_names)) {
     if (!in_array($column_name, $this->column_names)) {
-      $message = t('ChadoRecord::setValue(). The column named, "!column", does not exist in table: "!table".',
-        ['!column' => $column_name, '!table' => $this->table_name]);
+      $message = t('ChadoRecord::setValue(). The column named, "!column", does ' . 
+        'not exist in table: "!table".',
+        ['!column' => $column_name, 
+         '!table' => $this->table_name]);
       throw new Exception($message);
       throw new Exception($message);
     }
     }
 
 
     // Make sure that the value is not NULL if this is a required field.
     // Make sure that the value is not NULL if this is a required field.
     if (!in_array($column_name, $this->required_cols) and $value == '__NULL__') {
     if (!in_array($column_name, $this->required_cols) and $value == '__NULL__') {
-      $message = t('ChadoRecord::setValue(). The column named, "!column", requires a value for the table: "!table".',
-        ['!column' => $column_name, '!table' => $this->table_name]);
+      $message = t('ChadoRecord::setValue(). The column named, "!column", ' . 
+        'requires a value for the table: "!table".',
+        ['!column' => $column_name, 
+         '!table' => $this->table_name]);
       throw new Exception($message);
       throw new Exception($message);
     }
     }
     
     
     // Remove from the missing list if it was there.
     // Remove from the missing list if it was there.
-    elseif (isset($this->missing_required_cols[$column])) {
-      unset($this->missing_required_cols[$column]);
+    if (isset($this->missing_required_col[$column_name])) {
+      unset($this->missing_required_col[$column_name]);
     }
     }
 
 
     // Ensure that no values are arrays.
     // Ensure that no values are arrays.
     if (is_array($value)) {
     if (is_array($value)) {
-      $message = t('ChadoRecord::setValue(). The column named, "!column", must be a single value but is currently: "!values". NOTE: we currently don\'t support expanding foreign key relationships or multiple values for a given column.',
-        ['!column' => $column, '!table' => $this->table_name, '!values' => implode('", "', $value)]);
+      $message = t('ChadoRecord::setValue(). The column named, "!column", ' . 
+        'must be a single value but is currently: "!values". NOTE: this function ' . 
+        'currently does not support expanding foreign key relationships or ' .
+        'multiple values for a given column.',
+        ['!column' => $column, 
+         '!table' => $this->table_name, 
+         '!values' => implode('", "', $value)]);
       throw new Exception($message);
       throw new Exception($message);
     }
     }
 
 
@@ -533,8 +569,10 @@ class ChadoRecord {
 
 
     // Make sure the column is valid.
     // Make sure the column is valid.
     if (!in_array($column_name, $this->column_names)) {
     if (!in_array($column_name, $this->column_names)) {
-      $message = t('ChadoRecord::getValue(). The column named, "!column", does not exist in table: "!table".',
-        ['!column' => $column_name, '!table' => $this->table_name]);
+      $message = t('ChadoRecord::getValue(). The column named, "!column", ' . 
+        'does not exist in table: "!table".',
+        ['!column' => $column_name, 
+         '!table' => $this->table_name]);
       throw new Exception($message);
       throw new Exception($message);
     }
     }
 
 
@@ -565,7 +603,8 @@ class ChadoRecord {
 
 
     // Make sure we have values for this record before searching.
     // Make sure we have values for this record before searching.
     if (empty($this->values)) {
     if (empty($this->values)) {
-      $message = t('ChadoRecord::find(). Could not find a record from the table, !table, without any values.',
+      $message = t('ChadoRecord::find(). Could not find a record from ' . 
+        'the table, !table, without any values.',
         ['!table' => $this->table_name]);
         ['!table' => $this->table_name]);
       throw new Exception($message);
       throw new Exception($message);
     }
     }
@@ -581,8 +620,11 @@ class ChadoRecord {
       $results = chado_query($sql, $select_args);
       $results = chado_query($sql, $select_args);
     }
     }
     catch (Exception $e) {
     catch (Exception $e) {
-      $message = t('ChadoRecord::find(). Could not find a record in the table, !table, with the following values: !values. ERROR: !error',
-        ['!table' => $this->table_name, '!values' => print_r($this->values, TRUE), '!error' => $e->getMessage()]);
+      $message = t('ChadoRecord::find(). Could not find a record in the ' . 
+        'table, !table, with the following values: !values. ERROR: !error',
+        ['!table' => $this->table_name, 
+         '!values' => print_r($this->values, TRUE), 
+         '!error' => $e->getMessage()]);
       throw new Exception($message);
       throw new Exception($message);
     }
     }
 
 

File diff suppressed because it is too large
+ 270 - 474
tripal_chado/api/modules/tripal_chado.cv.api.inc


+ 1 - 3
tripal_chado/api/modules/tripal_chado.module.DEPRECATED.api.inc

@@ -209,9 +209,7 @@ function tripal_get_cvterm_select_options($cv_id, $rel_type = false) {
 /**
 /**
  * Duplicate of fill_cvtermpath() stored procedure in Chado.
  * Duplicate of fill_cvtermpath() stored procedure in Chado.
  *
  *
- * Identifies all of the root terms of the controlled vocabulary. These
- * root terms are then processed by calling the
- * chado_update_cvtermpath_root_loop() function on each one.
+ * Identifies all of the root terms of the controlled vocabulary. 
  *
  *
  * @param $cvid
  * @param $cvid
  *   The controlled vocabulary ID from the cv table of Chado (i.e. cv.cv_id).
  *   The controlled vocabulary ID from the cv table of Chado (i.e. cv.cv_id).

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

@@ -386,7 +386,7 @@ function chado_delete_mview($mview_id) {
 }
 }
 
 
 /**
 /**
- * Update a Materialized View.
+ * Populate a Materialized View.
  *
  *
  * @param $mview_id
  * @param $mview_id
  *   The unique identifier for the materialized view to be updated.
  *   The unique identifier for the materialized view to be updated.

+ 53 - 52
tripal_chado/files/tcontact.obo

@@ -1,150 +1,151 @@
 format-version: 1.2
 format-version: 1.2
 default-namespace: tripal_contact
 default-namespace: tripal_contact
+ontology: tcontact
 
 
 [Term]
 [Term]
-id: TContact:0000001
+id: TCONTACT:0000001
 name: Contact Type
 name: Contact Type
 
 
 
 
 [Term]
 [Term]
-id: TContact:0000002
+id: TCONTACT:0000002
 name: Collective
 name: Collective
 def: Used when a contact is a collective of individuals rather than a person.
 def: Used when a contact is a collective of individuals rather than a person.
-is_a: TContact:0000001 ! Contact Type
+is_a: TCONTACT:0000001 ! Contact Type
 
 
 [Term]
 [Term]
-id: TContact:0000003
+id: TCONTACT:0000003
 name: Person
 name: Person
-is_a: TContact:0000001 ! Contact Type
+is_a: TCONTACT:0000001 ! Contact Type
 
 
 [Term]
 [Term]
-id: TContact:0000004
+id: TCONTACT:0000004
 name: Organization
 name: Organization
-is_a: TContact:0000001 ! Contact Type
+is_a: TCONTACT:0000001 ! Contact Type
 
 
 [Term]
 [Term]
-id: TContact:0000005
+id: TCONTACT:0000005
 name: University
 name: University
-is_a: TContact:0000001 ! Contact Type
+is_a: TCONTACT:0000001 ! Contact Type
 
 
 [Term]
 [Term]
-id: TContact:0000006
+id: TCONTACT:0000006
 name: Lab
 name: Lab
-is_a: TContact:0000001 ! Contact Type
+is_a: TCONTACT:0000001 ! Contact Type
 
 
 [Term]
 [Term]
-id: TContact:0000007
+id: TCONTACT:0000007
 name: Institute
 name: Institute
-is_a: TContact:0000001 ! Contact Type
+is_a: TCONTACT:0000001 ! Contact Type
 
 
 [Term]
 [Term]
-id: TContact:0000008
+id: TCONTACT:0000008
 name: Research Group
 name: Research Group
-is_a: TContact:0000001 ! Contact Type
+is_a: TCONTACT:0000001 ! Contact Type
 
 
 [Term]
 [Term]
-id: TContact:0000009
+id: TCONTACT:0000009
 name: Department
 name: Department
-is_a: TContact:0000001 ! Contact Type
+is_a: TCONTACT:0000001 ! Contact Type
 
 
 [Term]
 [Term]
-id: TContact:0000010
+id: TCONTACT:0000010
 name: First Initials
 name: First Initials
 def: The first initials for the author including the initial for the first name and any middle names (not the initial for the last name).
 def: The first initials for the author including the initial for the first name and any middle names (not the initial for the last name).
-relationship: part_of TContact:0000003 ! Person
+relationship: part_of TCONTACT:0000003 ! Person
 
 
 [Term]
 [Term]
-id: TContact:0000011
+id: TCONTACT:0000011
 name: Surname
 name: Surname
 synonym: "family_name" EXACT []
 synonym: "family_name" EXACT []
 synonym: "last_name" EXACT []
 synonym: "last_name" EXACT []
-relationship: part_of TContact:0000003 ! Person
+relationship: part_of TCONTACT:0000003 ! Person
 
 
 [Term]
 [Term]
-id: TContact:0000012
+id: TCONTACT:0000012
 name: Given Name
 name: Given Name
 synonym: "first_name" EXACT []
 synonym: "first_name" EXACT []
-relationship: part_of TContact:0000003 ! Person
+relationship: part_of TCONTACT:0000003 ! Person
 
 
 [Term]
 [Term]
-id: TContact:0000013
+id: TCONTACT:0000013
 name: Middle Names
 name: Middle Names
 def: One or more middle names for this person.
 def: One or more middle names for this person.
-relationship: part_of TContact:0000003 ! Person
+relationship: part_of TCONTACT:0000003 ! Person
 
 
 [Term]
 [Term]
-id: TContact:0000014
+id: TCONTACT:0000014
 name: Middle Initials
 name: Middle Initials
 def: The middle initials for this person excluding the initial for the given name and the surname.
 def: The middle initials for this person excluding the initial for the given name and the surname.
-relationship: part_of TContact:0000003 ! Person
+relationship: part_of TCONTACT:0000003 ! Person
 
 
 [Term]
 [Term]
-id: TContact:0000015
+id: TCONTACT:0000015
 name: Affiliation
 name: Affiliation
 
 
 [Term]
 [Term]
-id: TContact:0000016
+id: TCONTACT:0000016
 name: Department
 name: Department
 def: The department of an institution or organization.
 def: The department of an institution or organization.
-relationship: part_of TContact:0000015 ! Affiliation
+relationship: part_of TCONTACT:0000015 ! Affiliation
 
 
 [Term]
 [Term]
-id: TContact:0000017
+id: TCONTACT:0000017
 name: Institution
 name: Institution
-relationship: part_of TContact:0000015 ! Affiliation
+relationship: part_of TCONTACT:0000015 ! Affiliation
 
 
 [Term]
 [Term]
-id: TContact:0000018
+id: TCONTACT:0000018
 name: Organization
 name: Organization
 def: A generic term for any organization.
 def: A generic term for any organization.
-relationship: part_of TContact:0000015 ! Affiliation
+relationship: part_of TCONTACT:0000015 ! Affiliation
 
 
 [Term]
 [Term]
-id: TContact:0000019
+id: TCONTACT:0000019
 name: Address
 name: Address
 
 
 [Term]
 [Term]
-id: TContact:0000020
+id: TCONTACT:0000020
 name: Address Line 1
 name: Address Line 1
-relationship: part_of TContact:0000019 ! Address
+relationship: part_of TCONTACT:0000019 ! Address
 
 
 [Term]
 [Term]
-id: TContact:0000021
+id: TCONTACT:0000021
 name: Address Line 2
 name: Address Line 2
-relationship: part_of TContact:0000019 ! Address
+relationship: part_of TCONTACT:0000019 ! Address
 
 
 [Term]
 [Term]
-id: TContact:0000022
+id: TCONTACT:0000022
 name: Address Line 3
 name: Address Line 3
-relationship: part_of TContact:0000019 ! Address
+relationship: part_of TCONTACT:0000019 ! Address
 
 
 [Term]
 [Term]
-id: TContact:0000023
+id: TCONTACT:0000023
 name: City
 name: City
-relationship: part_of TContact:0000019 ! Address
+relationship: part_of TCONTACT:0000019 ! Address
 
 
 [Term]
 [Term]
-id: TContact:0000024
+id: TCONTACT:0000024
 name: State
 name: State
-relationship: part_of TContact:0000019 ! Address
+relationship: part_of TCONTACT:0000019 ! Address
 
 
 [Term]
 [Term]
-id: TContact:0000025
+id: TCONTACT:0000025
 name: Province
 name: Province
-relationship: part_of TContact:0000019 ! Address
+relationship: part_of TCONTACT:0000019 ! Address
 
 
 [Term]
 [Term]
-id: TContact:0000026
+id: TCONTACT:0000026
 name: Country
 name: Country
-relationship: part_of TContact:0000019 ! Address
+relationship: part_of TCONTACT:0000019 ! Address
 
 
 [Term]
 [Term]
-id: TContact:0000027
+id: TCONTACT:0000027
 name: Postal Code
 name: Postal Code
-relationship: part_of TContact:0000019 ! Address
+relationship: part_of TCONTACT:0000019 ! Address
 
 
 [Term]
 [Term]
-id: TContact:0000028
+id: TCONTACT:0000028
 name: contact_description
 name: contact_description
 def: A description of the contact
 def: A description of the contact
 
 

+ 1 - 2
tripal_chado/files/tpub.obo

@@ -1,6 +1,7 @@
 format-version: 1.2
 format-version: 1.2
 default-namespace: tripal_pub
 default-namespace: tripal_pub
 subsetdef: MeSH_Publication_Type "MeSH Publication Types"
 subsetdef: MeSH_Publication_Type "MeSH Publication Types"
+ontology: tpub
 
 
 [Term]
 [Term]
 id: TPUB:0000001
 id: TPUB:0000001
@@ -742,8 +743,6 @@ def: Works consisting of or containing a substantial number of blank forms.
 is_a: TPUB:0000015 ! Publication Type
 is_a: TPUB:0000015 ! Publication Type
 subset: MeSH_Publication_Type
 subset: MeSH_Publication_Type
 
 
-subset: MeSH_Publication_Type
-
 [Term]
 [Term]
 id: TPUB:0000157
 id: TPUB:0000157
 name: Formularies
 name: Formularies

File diff suppressed because it is too large
+ 1306 - 689
tripal_chado/includes/TripalImporter/OBOImporter.inc


+ 16 - 15
tripal_chado/includes/setup/tripal_chado.chado_vx_x.inc

@@ -452,14 +452,15 @@ function tripal_chado_add_db2cv_mview_mview() {
   );
   );
 
 
   $sql = "
   $sql = "
-   SELECT DISTINCT CV.cv_id, CV.name as cvname, DB.db_id, DB.name as dbname,
-     COUNT(CVT.cvterm_id) as num_terms
-   FROM cv CV
-     INNER JOIN cvterm CVT on CVT.cv_id = CV.cv_id
-     INNER JOIN dbxref DBX on DBX.dbxref_id = CVT.dbxref_id
-     INNER JOIN db DB on DB.db_id = DBX.db_id
-   GROUP BY CV.cv_id, CV.name, DB.db_id, DB.name
-   ORDER BY DB.name
+    SELECT DISTINCT CV.cv_id, CV.name as cvname, DB.db_id, DB.name as dbname,
+      COUNT(CVT.cvterm_id) as num_terms
+    FROM cv CV
+      INNER JOIN cvterm CVT on CVT.cv_id = CV.cv_id
+      INNER JOIN dbxref DBX on DBX.dbxref_id = CVT.dbxref_id
+      INNER JOIN db DB on DB.db_id = DBX.db_id
+    WHERE CVT.is_relationshiptype = 0 and CVT.is_obsolete = 0
+    GROUP BY CV.cv_id, CV.name, DB.db_id, DB.name
+    ORDER BY DB.name
   ";
   ";
 
 
   // Create the MView
   // Create the MView
@@ -506,13 +507,13 @@ function tripal_chado_add_cv_root_mview_mview() {
   );
   );
 
 
   $sql = "
   $sql = "
-    SELECT DISTINCT CVT.name,CVT.cvterm_id, CV.cv_id, CV.name
-    FROM cvterm_relationship CVTR
-      INNER JOIN cvterm CVT on CVTR.object_id = CVT.cvterm_id
-      INNER JOIN cv CV on CV.cv_id = CVT.cv_id
-    WHERE CVTR.object_id not in
-      (SELECT subject_id FROM cvterm_relationship)
-    AND CVT.is_relationshiptype = 0
+    SELECT DISTINCT CVT.name, CVT.cvterm_id, CV.cv_id, CV.name
+    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
+    INNER JOIN cv CV on CV.cv_id = CVT.cv_id
+    WHERE CVTR.subject_id is NULL and 
+      CVT.is_relationshiptype = 0 and CVT.is_obsolete = 0
   ";
   ";
 
 
   // Create the MView
   // Create the MView

+ 39 - 0
tripal_chado/includes/setup/tripal_chado.setup.inc

@@ -71,6 +71,45 @@ function tripal_chado_prepare_drush_submit() {
  *
  *
  */
  */
 function tripal_chado_load_ontologies() {
 function tripal_chado_load_ontologies() {
+  
+  // 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',
+    'description' => 'NCI Thesaurus OBO Edition.',
+    'url' => 'http://purl.obolibrary.org/obo/ncit.owl',
+    'urlprefix' => ' http://purl.obolibrary.org/obo/{db}_{accession}',
+  ]);
+  chado_insert_cv(
+    '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',
+    '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.',
+  ));
 
 
   // Insert commonly used ontologies into the tables.
   // Insert commonly used ontologies into the tables.
   $ontologies = array(
   $ontologies = array(

+ 4 - 1
tripal_chado/includes/tripal_chado.bundle.inc

@@ -64,7 +64,7 @@ function tripal_chado_bundle_create($bundle, $storage_args) {
       throw new Exception('Cannot create content type. Problem associating type with Chado.');
       throw new Exception('Cannot create content type. Problem associating type with Chado.');
     }
     }
   }
   }
-
+  
   tripal_chado_create_bundle_table($bundle);
   tripal_chado_create_bundle_table($bundle);
 }
 }
 
 
@@ -114,6 +114,9 @@ function tripal_chado_create_bundle_table($bundle) {
     ),
     ),
   );
   );
   $chado_entity_table = chado_get_bundle_entity_table($bundle);
   $chado_entity_table = chado_get_bundle_entity_table($bundle);
+  if (!$chado_entity_table) {
+    throw new Exception('Cannot create entity linker table for chado.');
+  }
   db_create_table($chado_entity_table, $schema);
   db_create_table($chado_entity_table, $schema);
 }
 }
 
 

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

@@ -1313,7 +1313,7 @@ function tripal_chado_bundle_instances_info_custom(&$info, $entity_type, $bundle
         break;
         break;
       case 'contact':
       case 'contact':
         $default_vocab = 'tripal_contact';
         $default_vocab = 'tripal_contact';
-        $parent_term = 'TContact:0000001';
+        $parent_term = 'TCONTACT:0000001';
         $description = 'Select the type.';
         $description = 'Select the type.';
         break;
         break;
       default:
       default:

+ 2 - 7
tripal_chado/includes/tripal_chado.install.inc

@@ -85,7 +85,7 @@ function tripal_chado_load_form($form, $form_state) {
   );
   );
 
 
   $form['action_to_do'] = array(
   $form['action_to_do'] = array(
-    '#type' => 'radios',
+    '#type' => 'select',
     '#title' => 'Installation/Upgrade Action',
     '#title' => 'Installation/Upgrade Action',
     '#options' => array(
     '#options' => array(
       'Install Chado v1.3' => t('New Install of Chado v1.3 (erases all existing Chado data if Chado already exists)'),
       'Install Chado v1.3' => t('New Install of Chado v1.3 (erases all existing Chado data if Chado already exists)'),
@@ -94,7 +94,7 @@ function tripal_chado_load_form($form, $form_state) {
       'Upgrade Chado v1.11 to v1.2' => t('Upgrade existing Chado v1.11 to v1.2 (no data is lost)'),
       'Upgrade Chado v1.11 to v1.2' => t('Upgrade existing Chado v1.11 to v1.2 (no data is lost)'),
       'Install Chado v1.11' => t('New Install of Chado v1.11 (erases all existing Chado data if Chado already exists)'),
       'Install Chado v1.11' => t('New Install of Chado v1.11 (erases all existing Chado data if Chado already exists)'),
     ),
     ),
-    '#description' => t('Select an action to perform. If you want to install Chado all other Tripal modules must not be installed.'),
+    '#description' => t('Select an action to perform.'),
     '#required' => TRUE,
     '#required' => TRUE,
     '#ajax' => array(
     '#ajax' => array(
       'callback' => "tripal_chado_load_form_ajax_callback",
       'callback' => "tripal_chado_load_form_ajax_callback",
@@ -104,11 +104,6 @@ function tripal_chado_load_form($form, $form_state) {
     ),
     ),
   );
   );
 
 
-  $form['warning'] = array(
-    '#markup' => "<div><font color=\"red\">WARNING:</font>" . t('A new install of
-      Chado will remove and recreate the Chado database if it already exists.') . '</div>',
-  );
-
   $form['button'] = array(
   $form['button'] = array(
     '#type' => 'submit',
     '#type' => 'submit',
     '#value' => t('Install/Upgrade Chado'),
     '#value' => t('Install/Upgrade Chado'),

+ 18 - 4
tripal_chado/includes/tripal_chado.semweb.inc

@@ -157,6 +157,12 @@ function tripal_chado_populate_vocab_RDFS() {
     'cv_name' => 'rdfs',
     'cv_name' => 'rdfs',
     'definition' => 'A human-readable version of a resource\'s name.',
     '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.
  * Adds the Schema.org database and terms.
@@ -1831,10 +1837,10 @@ function tripal_chado_populate_vocab_SWO() {
  */
  */
 function tripal_chado_populate_vocab_TCONTACT() {
 function tripal_chado_populate_vocab_TCONTACT() {
   chado_insert_db(array(
   chado_insert_db(array(
-    'name' => 'TContact',
+    'name' => 'TCONTACT',
     'description' => 'Tripal Contact Ontology. A temporary ontology until a more formal appropriate ontology an be identified.',
     'description' => 'Tripal Contact Ontology. A temporary ontology until a more formal appropriate ontology an be identified.',
-    'url' => 'cv/lookup/TContact',
-    'urlprefix' => 'cv/lookup/TContact/{accession}',
+    'url' => 'cv/lookup/TCONTACT',
+    'urlprefix' => 'cv/lookup/TCONTACT/{accession}',
   ));
   ));
   chado_insert_cv('tripal_contact', 'Tripal Contact Ontology. A temporary ontology until a more formal appropriate ontology an be identified.');
   chado_insert_cv('tripal_contact', 'Tripal Contact Ontology. A temporary ontology until a more formal appropriate ontology an be identified.');
 }
 }
@@ -1947,7 +1953,7 @@ function tripal_chado_populate_vocab_TAXRANK() {
  */
  */
 function tripal_chado_populate_vocab_NCIT() {
 function tripal_chado_populate_vocab_NCIT() {
   chado_insert_db(array(
   chado_insert_db(array(
-    'name' => 'NCI Thesaurus OBO Edition',
+    'name' => 'NCIT',
     'description' => 'NCI Thesaurus OBO Edition.',
     'description' => 'NCI Thesaurus OBO Edition.',
     'url' => 'http://purl.obolibrary.org/obo/ncit.owl',
     'url' => 'http://purl.obolibrary.org/obo/ncit.owl',
     'urlprefix' => ' http://purl.obolibrary.org/obo/{db}_{accession}',
     'urlprefix' => ' http://purl.obolibrary.org/obo/{db}_{accession}',
@@ -2111,6 +2117,14 @@ function tripal_chado_populate_vocab_NCIT() {
     'cv_name' => 'ncit',
     'cv_name' => 'ncit',
     'definition' => 'Any genetically determined characteristic.',
     '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 ]',
+  ));
+  
 }
 }
 
 
 /**
 /**

+ 56 - 6
tripal_chado/includes/tripal_chado.vocab_storage.inc

@@ -201,6 +201,7 @@ function tripal_chado_vocab_get_terms($vocabulary, $limit = 25, $element = 0) {
   }
   }
   return $terms;
   return $terms;
 }
 }
+
 /**
 /**
  * Implements hook_vocab_get_term_children().
  * Implements hook_vocab_get_term_children().
  *
  *
@@ -235,12 +236,15 @@ function tripal_chado_vocab_get_term_children($vocabulary, $accession) {
 
 
   // Get the children.
   // Get the children.
   $sql = "
   $sql = "
-    SELECT DISTINCT subject_id
+    SELECT DISTINCT CVTR.subject_id, CVT.name
     FROM {cvterm_relationship} CVTR
     FROM {cvterm_relationship} CVTR
+      INNER JOIN {cvterm} CVT on CVTR.subject_id = CVT.cvterm_id
     WHERE object_id = :object_id
     WHERE object_id = :object_id
+    ORDER BY CVT.name
   ";
   ";
   $results = chado_query($sql, array(':object_id' => $cvterm->cvterm_id));
   $results = chado_query($sql, array(':object_id' => $cvterm->cvterm_id));
-  while($cvterm_id = $results->fetchField()) {
+  while($cvterm = $results->fetchObject()) {
+    $cvterm_id = $cvterm->subject_id;
     $match = array('cvterm_id' => $cvterm_id);
     $match = array('cvterm_id' => $cvterm_id);
     $cvterm = chado_generate_var('cvterm', $match);
     $cvterm = chado_generate_var('cvterm', $match);
     $terms[] = _tripal_chado_format_term_description($cvterm);
     $terms[] = _tripal_chado_format_term_description($cvterm);
@@ -274,7 +278,12 @@ function tripal_chado_vocab_get_term($vocabulary, $accession) {
   if (!$cvterm) {
   if (!$cvterm) {
     return FALSE;
     return FALSE;
   }
   }
-  $cvterm = chado_expand_var($cvterm, 'field', 'cvterm.definition');
+  $options = ['return_array' => TRUE];
+  $cvterm = chado_expand_var($cvterm, 'field', 'cvterm.definition', $options);
+  $cvterm = chado_expand_var($cvterm, 'table', 'cvterm_dbxref', $options);
+  $cvterm = chado_expand_var($cvterm, 'table', 'cvtermsynonym', $options);
+  $cvterm = chado_expand_var($cvterm, 'table', 'cvterm_relationship', $options);
+  $cvterm = chado_expand_var($cvterm, 'table', 'cvtermprop', $options);
 
 
   return _tripal_chado_format_term_description($cvterm);
   return _tripal_chado_format_term_description($cvterm);
 }
 }
@@ -294,16 +303,57 @@ function _tripal_chado_format_term_description($cvterm) {
   if ($sw_url) {
   if ($sw_url) {
     $sw_url = preg_replace('/{db}/', $cvterm->dbxref_id->db_id->name, $sw_url);
     $sw_url = preg_replace('/{db}/', $cvterm->dbxref_id->db_id->name, $sw_url);
     $sw_url = preg_replace('/{accession}/', '', $sw_url);
     $sw_url = preg_replace('/{accession}/', '', $sw_url);
-    $sw_url = url($sw_url, array('absolute' => TRUE));
+    $sw_url = url($sw_url, ['absolute' => TRUE]);
   }
   }
   $vocabulary = tripal_chado_vocab_get_vocabulary($cvterm->dbxref_id->db_id->name);
   $vocabulary = tripal_chado_vocab_get_vocabulary($cvterm->dbxref_id->db_id->name);
-  $term = array(
+  $term = [
     'vocabulary' => $vocabulary,
     'vocabulary' => $vocabulary,
+    'namespace'  => $cvterm->cv_id->name,
     'accession'  => $cvterm->dbxref_id->accession,
     'accession'  => $cvterm->dbxref_id->accession,
     'name'       => $cvterm->name,
     'name'       => $cvterm->name,
     'url'        => chado_get_dbxref_url($cvterm->dbxref_id),
     'url'        => chado_get_dbxref_url($cvterm->dbxref_id),
     'definition' => (isset($cvterm->definition)) ? $cvterm->definition : '',
     'definition' => (isset($cvterm->definition)) ? $cvterm->definition : '',
-  );
+  ];
+  
+  // Is this term in any relationships as the subject?
+  if (property_exists($cvterm, 'cvterm_relationship')) {
+    $relationships = $cvterm->cvterm_relationship->subject_id;
+    if ($relationships) {
+      foreach ($relationships as $relationship) {
+        $term['relationship'][] = $relationship->type_id->name . ' ' . $relationship->object_id->dbxref_id->db_id->name . ':' . $relationship->object_id->dbxref_id->accession;
+      }
+    }
+  }
+  
+  // Does this term have synonyms?
+  if (property_exists($cvterm, 'cvtermsynonym')) {
+    $synonyms = $cvterm->cvtermsynonym->cvterm_id;
+    if ($synonyms) {
+      foreach ($synonyms as $synonym) {
+        $term['synonym'][] = $synonym->synonym;
+      }
+    }
+  }
+  
+  // Does this term have any cross refs?
+  if (property_exists($cvterm, 'cvterm_dbxref')) {
+    $xrefs = $cvterm->cvterm_dbxref;
+    if ($xrefs) {
+      foreach ($xrefs as $xref) {
+        $term['cross reference'][] = $xref->dbxref_id->db_id->name . ':' . $xref->dbxref_id->accession;
+      }
+    }
+  }
+  
+  // Does this term have any properties?
+  if (property_exists($cvterm, 'cvtermprop')) {
+    $props = $cvterm->cvtermprop->cvterm_id;
+    if ($props) {
+      foreach ($props as $prop) {
+        $term[$prop->type_id->name][] = property_exists($prop, 'value') ? $prop->value : '';
+      }
+    }
+  }
   return $term;
   return $term;
 }
 }
 
 

+ 117 - 0
tripal_chado/tripal_chado.install

@@ -1708,3 +1708,120 @@ function tripal_chado_update_7331() {
     throw new DrupalUpdateException('Could not perform update: '. $error);
     throw new DrupalUpdateException('Could not perform update: '. $error);
   }
   }
 }
 }
+
+/**
+ * Adds additional vocabs for the OBO Importer.
+ */
+function tripal_chado_update_7332() {
+  try {
+    // We have to add the db and cv in case the user hasn't yet prepared Chado.
+    // If they have prepared Chado then no harm done.
+    chado_insert_db([
+      'name' => 'NCIT',
+      'description' => 'NCI Thesaurus OBO Edition.',
+      'url' => 'http://purl.obolibrary.org/obo/ncit.owl',
+      'urlprefix' => ' http://purl.obolibrary.org/obo/{db}_{accession}',
+    ]);
+    chado_insert_cv(
+      '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',
+      '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();
+    throw new DrupalUpdateException('Could not perform update: '. $error);
+  }
+}
+
+/**
+ * Renames the TContact vocabulary database entry to TCONTACT as it should be.
+ */
+function tripal_chado_update_7333() {
+  try {
+    chado_update_record('db', ['name' => 'TContact'], ['name' => 'TCONTACT']);
+  }
+  catch (\PDOException $e) {
+    $error = $e->getMessage();
+    throw new DrupalUpdateException('Could not perform update: '. $error);
+  }
+}
+
+/**
+ * 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,
+         COUNT(CVT.cvterm_id) as num_terms
+       FROM cv CV
+         INNER JOIN cvterm CVT on CVT.cv_id = CV.cv_id
+         INNER JOIN dbxref DBX on DBX.dbxref_id = CVT.dbxref_id
+         INNER JOIN db DB on DB.db_id = DBX.db_id
+       WHERE CVT.is_relationshiptype = 0 and CVT.is_obsolete = 0
+       GROUP BY CV.cv_id, CV.name, DB.db_id, DB.name
+       ORDER BY DB.name
+    ';
+    $mview_id = tripal_get_mview_id('db2cv_mview');
+    if($mview_id) {
+      $sql = "UPDATE {tripal_mviews} set query = :query WHERE mview_id = :mview_id";
+      db_query($sql, [':query' => $query, ':mview_id' => $mview_id]);
+    }
+  }
+  catch (\PDOException $e) {
+    $error = $e->getMessage();
+    throw new DrupalUpdateException('Could not perform update: '. $error);
+  }
+}
+/**
+ * SQL Fix for the cv_root_mview materialized view.
+ */
+function tripal_chado_update_7335() {
+  try {
+    $query = '
+      SELECT DISTINCT CVT.name, CVT.cvterm_id, CV.cv_id, CV.name
+      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
+      INNER JOIN cv CV on CV.cv_id = CVT.cv_id
+      WHERE CVTR.subject_id is NULL and
+        CVT.is_relationshiptype = 0 and CVT.is_obsolete = 0
+      ';
+    $mview_id = tripal_get_mview_id('cv_root_mview');
+    if($mview_id) {
+      $sql = "UPDATE {tripal_mviews} set query = :query WHERE mview_id = :mview_id";
+      db_query($sql, [':query' => $query, ':mview_id' => $mview_id]);
+    }
+  }
+  catch (\PDOException $e) {
+    $error = $e->getMessage();
+    throw new DrupalUpdateException('Could not perform update: '. $error);
+  }
+}

Some files were not shown because too many files changed in this diff