|  | @@ -369,6 +369,253 @@ function tripal_update_cvtermpath_old($cv_id, $job_id = NULL) {
 | 
	
		
			
				|  |  |    return TRUE;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * 
 | 
	
		
			
				|  |  | + * @param unknown $cv_id
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +function chado_clear_cvtermpath($cv_id) {
 | 
	
		
			
				|  |  | +  $sql = "DELETE FROM {cvtermpath} WHERE cv_id = :cv_id";
 | 
	
		
			
				|  |  | +  chado_query($sql, [':cv_id' => $cv_id]);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +function _chado_update_cvtermpath_remove_constraints() {
 | 
	
		
			
				|  |  | +  $sql = "ALTER TABLE {cvtermpath} DROP CONSTRAINT IF EXISTS cvtermpath_c1";
 | 
	
		
			
				|  |  | +  db_query($sql);
 | 
	
		
			
				|  |  | +  $sql = "ALTER TABLE {cvtermpath} DROP CONSTRAINT IF EXISTS cvtermpath_cv_id_fkey";
 | 
	
		
			
				|  |  | +  db_query($sql);
 | 
	
		
			
				|  |  | +  $sql = "ALTER TABLE {cvtermpath} DROP CONSTRAINT IF EXISTS cvtermpath_object_id_fkey";
 | 
	
		
			
				|  |  | +  db_query($sql);
 | 
	
		
			
				|  |  | +  $sql = "ALTER TABLE {cvtermpath} DROP CONSTRAINT IF EXISTS cvtermpath_pkey";
 | 
	
		
			
				|  |  | +  db_query($sql);
 | 
	
		
			
				|  |  | +  $sql = "ALTER TABLE {cvtermpath} DROP CONSTRAINT IF EXISTS cvtermpath_subject_id_fkey";
 | 
	
		
			
				|  |  | +  db_query($sql);
 | 
	
		
			
				|  |  | +  $sql = "ALTER TABLE {cvtermpath} DROP CONSTRAINT IF EXISTS cvtermpath_type_id_fkey";
 | 
	
		
			
				|  |  | +  db_query($sql);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +function _chado_update_cvtermpath_add_constraints() {
 | 
	
		
			
				|  |  | +  $sql = "ALTER TABLE {cvtermpath} ADD CONSTRAINT cvtermpath_c1 " .
 | 
	
		
			
				|  |  | +    "UNIQUE (subject_id, object_id, type_id, pathdistance)";
 | 
	
		
			
				|  |  | +  db_query($sql);
 | 
	
		
			
				|  |  | +  $sql = "ALTER TABLE {cvtermpath} ADD CONSTRAINT cvtermpath_cv_id_fkey " .
 | 
	
		
			
				|  |  | +    "FOREIGN KEY (cv_id) REFERENCES cv(cv_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED";
 | 
	
		
			
				|  |  | +  db_query($sql);
 | 
	
		
			
				|  |  | +  $sql = "ALTER TABLE {cvtermpath} ADD CONSTRAINT cvtermpath_object_id_fkey " .
 | 
	
		
			
				|  |  | +    "FOREIGN KEY (object_id) REFERENCES cvterm(cvterm_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED";
 | 
	
		
			
				|  |  | +  db_query($sql);
 | 
	
		
			
				|  |  | +  $sql = "ALTER TABLE {cvtermpath} ADD CONSTRAINT cvtermpath_pkey " .
 | 
	
		
			
				|  |  | +    "PRIMARY KEY (cvtermpath_id)";
 | 
	
		
			
				|  |  | +  db_query($sql);
 | 
	
		
			
				|  |  | +  $sql = "ALTER TABLE {cvtermpath} ADD CONSTRAINT cvtermpath_subject_id_fkey " .
 | 
	
		
			
				|  |  | +    "FOREIGN KEY (subject_id) REFERENCES cvterm(cvterm_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED";
 | 
	
		
			
				|  |  | +  db_query($sql);
 | 
	
		
			
				|  |  | +  $sql = "ALTER TABLE {cvtermpath} ADD CONSTRAINT cvtermpath_type_id_fkey " .
 | 
	
		
			
				|  |  | +    "FOREIGN KEY (type_id) REFERENCES cvterm(cvterm_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED";
 | 
	
		
			
				|  |  | +  db_query($sql);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * 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.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * @param $cvid
 | 
	
		
			
				|  |  | + *   The controlled vocabulary ID from the cv table of Chado (i.e. cv.cv_id).
 | 
	
		
			
				|  |  | + * @param $job_id
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * @ingroup tripal_chado_cv_api
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +function chado_update_cvtermpath($cv_id, TripalJob $job = NULL) {
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  // The cache is used to limit repetitive queries by storing known data.
 | 
	
		
			
				|  |  | +  $cache = [
 | 
	
		
			
				|  |  | +    'rels' => [],
 | 
	
		
			
				|  |  | +    'processed' => [],
 | 
	
		
			
				|  |  | +  ];
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  // TODO: there's a function to determine the current Chado instance.
 | 
	
		
			
				|  |  | +  // we should use that.
 | 
	
		
			
				|  |  | +  $prev_db = chado_set_active('chado');
 | 
	
		
			
				|  |  | +  $transaction = db_transaction();
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  try {
 | 
	
		
			
				|  |  | +    // Remove constraints for faster loading.
 | 
	
		
			
				|  |  | +    _chado_update_cvtermpath_remove_constraints();
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +    // Get the is_a term. The OBO importer adds this for evey vocabulary.
 | 
	
		
			
				|  |  | +    $sql = "SELECT * FROM cvterm WHERE name = :is_a and cv_id = :cv_id";
 | 
	
		
			
				|  |  | +    $args = [':is_a' => 'is_a', ':cv_id' => $cv_id];
 | 
	
		
			
				|  |  | +    $is_a = chado_query($sql, $args)->fetchObject();
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +    // First cache all the relationships for this vocaublary.
 | 
	
		
			
				|  |  | +    $sql = "
 | 
	
		
			
				|  |  | +      SELECT CVTR.subject_id, CVTR.type_id, CVTR.object_id, CVTS.name
 | 
	
		
			
				|  |  | +      FROM {cvterm_relationship} CVTR
 | 
	
		
			
				|  |  | +        INNER JOIN {cvterm} CVTO on CVTO.cvterm_id = CVTR.object_id
 | 
	
		
			
				|  |  | +        INNER JOIN {cvterm} CVTS on CVTS.cvterm_id = CVTR.subject_id
 | 
	
		
			
				|  |  | +      WHERE CVTO.cv_id = :cv_id 
 | 
	
		
			
				|  |  | +    ";
 | 
	
		
			
				|  |  | +    $rels = chado_query($sql, [':cv_id' => $cv_id]);
 | 
	
		
			
				|  |  | +    while ($rel = $rels->fetchObject()) {
 | 
	
		
			
				|  |  | +      $cache['rels'][$rel->object_id][] = [$rel->subject_id, $rel->type_id, $rel->name];
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +    // Next get the tree roots. These are terms that are in relationships as
 | 
	
		
			
				|  |  | +    // an object but never as a subject.
 | 
	
		
			
				|  |  | +    $sql = "
 | 
	
		
			
				|  |  | +      SELECT DISTINCT CVT.cvterm_id, CVT.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
 | 
	
		
			
				|  |  | +      WHERE CVT.cv_id = :cvid AND CVTR.subject_id is NULL and 
 | 
	
		
			
				|  |  | +        CVT.is_relationshiptype = 0 and CVT.is_obsolete = 0
 | 
	
		
			
				|  |  | +    ";
 | 
	
		
			
				|  |  | +    $roots = chado_query($sql, [':cvid' => $cv_id]);
 | 
	
		
			
				|  |  | +     
 | 
	
		
			
				|  |  | +    // Iterate through the tree roots.
 | 
	
		
			
				|  |  | +    while ($root = $roots->fetchObject()) {
 | 
	
		
			
				|  |  | +      $root_id =  $root->cvterm_id;
 | 
	
		
			
				|  |  | +      $root_name = $root->name;
 | 
	
		
			
				|  |  | +      $num_handled = 0;
 | 
	
		
			
				|  |  | +      
 | 
	
		
			
				|  |  | +      // Add each root as a reference to itself in the cvtermpath table.
 | 
	
		
			
				|  |  | +      $cvtermpath = new ChadoRecord('cvtermpath');
 | 
	
		
			
				|  |  | +      $cvtermpath->setValues([
 | 
	
		
			
				|  |  | +        'type_id' => $is_a->cvterm_id,
 | 
	
		
			
				|  |  | +        'object_id' => $root_id,
 | 
	
		
			
				|  |  | +        'subject_id' => $root_id,
 | 
	
		
			
				|  |  | +        'cv_id' => $cv_id,
 | 
	
		
			
				|  |  | +        'pathdistance' => 1,
 | 
	
		
			
				|  |  | +      ]);
 | 
	
		
			
				|  |  | +      if (!$cvtermpath->find()) {
 | 
	
		
			
				|  |  | +        $cvtermpath->insert();
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      
 | 
	
		
			
				|  |  | +      _chado_update_cvtermpath_root($cv_id, $root_id, $root_name, $cache, $job, $num_handled);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +    // Restore the table constraints and indexes.
 | 
	
		
			
				|  |  | +    _chado_update_cvtermpath_add_constraints();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  catch (Exception $e) {
 | 
	
		
			
				|  |  | +    $transaction->rollback();
 | 
	
		
			
				|  |  | +    chado_set_active($prev_db);
 | 
	
		
			
				|  |  | +    throw $e;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +function _chado_update_cvtermpath_root($cv_id, $root_id, $root_name, &$cache, TripalJob $job, &$num_handled, $root_depth = 0) {
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  // Mark this node as having been processed as a root node.
 | 
	
		
			
				|  |  | +  $cache['processed'][$root_id] = TRUE;
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  // An array to keep track of which terms have been visited when descending
 | 
	
		
			
				|  |  | +  // the tree. We'll use this to avoid loops.
 | 
	
		
			
				|  |  | +  $visited = [];
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  // Get this term's children and recurse.
 | 
	
		
			
				|  |  | +  $children = $cache['rels'][$root_id];
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  // If there are no children do nothing.
 | 
	
		
			
				|  |  | +  if (!$children) {
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  // Iterate through the children and descend the tree.
 | 
	
		
			
				|  |  | +  foreach ($children as $child) {
 | 
	
		
			
				|  |  | +    $child_id = $child[0];
 | 
	
		
			
				|  |  | +    $type_id = $child[1];
 | 
	
		
			
				|  |  | +    $name = $child[2];
 | 
	
		
			
				|  |  | +    _chado_update_cvtermpath_item($cv_id, $root_id, $child_id, $type_id, $cache, $visited, 1);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  // Now that we've descended the tree we can calculate how many entries we
 | 
	
		
			
				|  |  | +  // will add to the cvterm table. We only want to do this with a root_depth
 | 
	
		
			
				|  |  | +  // level of 0 because this is the top level root term.
 | 
	
		
			
				|  |  | +  if ($root_depth == 0) {
 | 
	
		
			
				|  |  | +    $num_records = 0;
 | 
	
		
			
				|  |  | +    foreach ($visited as $subject_id => $details) {
 | 
	
		
			
				|  |  | +      $depth = $details[4];
 | 
	
		
			
				|  |  | +      $num_records += $depth; 
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    print "Adding " . number_format($num_records) . " paths for root: '$root_name'\n";
 | 
	
		
			
				|  |  | +    if ($job) {
 | 
	
		
			
				|  |  | +      $job->setTotalItems($num_records);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  // Insert into the cvtermpath table.
 | 
	
		
			
				|  |  | +  _chado_update_cvtermpath_process_visited($visited, $job, $num_handled, 1);
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  // Next make each child of this node a root and recurse again.
 | 
	
		
			
				|  |  | +  foreach ($children as $child) {
 | 
	
		
			
				|  |  | +    $child_id = $child[0];
 | 
	
		
			
				|  |  | +    $type_id = $child[1];
 | 
	
		
			
				|  |  | +    $name = $child[2];
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +    // Don't use a node as a root if we've already used it once before.
 | 
	
		
			
				|  |  | +    if (array_key_exists($child_id, $cache['processed'])) {
 | 
	
		
			
				|  |  | +      continue;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +    // Process this child as a root.
 | 
	
		
			
				|  |  | +    _chado_update_cvtermpath_root($cv_id, $child_id, $name, $cache, $job, $num_handled, $root_depth + 1);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}  
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +function _chado_update_cvtermpath_item($cv_id, $root_id, $cvterm_id, $type_id, &$cache, &$visited, $depth = 1) {
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  // Have we visited this node before?  If so then this is a loop. We do not
 | 
	
		
			
				|  |  | +  // want to mark this node as having been visited before. Just return.
 | 
	
		
			
				|  |  | +  if (array_key_exists($cvterm_id, $visited)) {
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  // Indicate we have visited this node in the tree and store the cvterm
 | 
	
		
			
				|  |  | +  // path details that we need for inserting into the cvtermpath table.
 | 
	
		
			
				|  |  | +  $visited[$cvterm_id] = [$type_id, $cvterm_id, $root_id, $cv_id, $depth + 1];
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  // Get this term's children and recurse.
 | 
	
		
			
				|  |  | +  $children = $cache['rels'][$cvterm_id];
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  // If this term does not have children then return.
 | 
	
		
			
				|  |  | +  if (!$children) {
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  // If the term has children then recurse on those.
 | 
	
		
			
				|  |  | +  foreach ($children as $child) {
 | 
	
		
			
				|  |  | +    $child_id = $child[0];
 | 
	
		
			
				|  |  | +    $type_id = $child[1];
 | 
	
		
			
				|  |  | +    _chado_update_cvtermpath_item($cv_id, $root_id, $child_id, $type_id, $cache, $visited, $depth + 1);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +function _chado_update_cvtermpath_process_visited($visited, TripalJob $job, &$num_handled, $depth  = 1) {
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  foreach ($visited as $subjectid_id => $details) {
 | 
	
		
			
				|  |  | +    $num_handled++;
 | 
	
		
			
				|  |  | +    if ($job) {
 | 
	
		
			
				|  |  | +      $job->setItemsHandled($num_handled);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    list ($type_id, $subject_id, $object_id, $cv_id, $pathdistance) = $details;
 | 
	
		
			
				|  |  | +    $cvtermpath = new ChadoRecord('cvtermpath');
 | 
	
		
			
				|  |  | +    $cvtermpath->setValues([
 | 
	
		
			
				|  |  | +      'type_id' =>  $type_id,
 | 
	
		
			
				|  |  | +      'subject_id' => $subject_id,
 | 
	
		
			
				|  |  | +      'object_id' => $object_id,
 | 
	
		
			
				|  |  | +      'cv_id'  => $cv_id,
 | 
	
		
			
				|  |  | +      'pathdistance'  => $pathdistance,
 | 
	
		
			
				|  |  | +    ]);
 | 
	
		
			
				|  |  | +    if (!$cvtermpath->find()) {
 | 
	
		
			
				|  |  | +      $cvtermpath->insert();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  |   * Duplicate of fill_cvtermpath() stored procedure in Chado.
 | 
	
		
			
				|  |  |   *
 | 
	
	
		
			
				|  | @@ -382,7 +629,7 @@ function tripal_update_cvtermpath_old($cv_id, $job_id = NULL) {
 | 
	
		
			
				|  |  |   *
 | 
	
		
			
				|  |  |   * @ingroup tripal_chado_cv_api
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  | -function chado_update_cvtermpath($cv_id, $job_id = NULL){
 | 
	
		
			
				|  |  | +function chado_update_cvtermpath_orig($cv_id, $job_id = NULL){
 | 
	
		
			
				|  |  |    // TODO: there's a function to determine the current Chado instance.
 | 
	
		
			
				|  |  |    // we should use that.
 | 
	
		
			
				|  |  |    $prev_db = chado_set_active('chado');
 | 
	
	
		
			
				|  | @@ -436,8 +683,8 @@ function chado_update_cvtermpath($cv_id, $job_id = NULL){
 | 
	
		
			
				|  |  |   * @ingroup tripal_chado_cv_api
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  |  function _chado_update_cvtermpath_root_loop($rootid, $cvid, &$roots) {
 | 
	
		
			
				|  |  | -  $ttype = db_query(
 | 
	
		
			
				|  |  | -    'SELECT cv.cvterm_id 
 | 
	
		
			
				|  |  | +  $ttype = db_query('
 | 
	
		
			
				|  |  | +     SELECT cv.cvterm_id 
 | 
	
		
			
				|  |  |      FROM cvterm cv
 | 
	
		
			
				|  |  |      WHERE cv.name = :isa 
 | 
	
		
			
				|  |  |            OR cv.name = :is_a
 |