-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1486 from reynoldtan/tv4g2-issue1482-chadoCvtermA…
…utocomplete Chado CV Term Autocomplete Functionality
- Loading branch information
Showing
3 changed files
with
241 additions
and
1 deletion.
There are no files selected for viewing
108 changes: 108 additions & 0 deletions
108
tripal_chado/src/Controller/ChadoCVTermAutocompleteController.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
<?php | ||
|
||
namespace Drupal\tripal_chado\Controller; | ||
|
||
use Drupal\Core\Controller\ControllerBase; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\JsonResponse; | ||
|
||
/** | ||
* Controller routines for the chado CV Term autocomplete. | ||
*/ | ||
class ChadoCVTermAutocompleteController extends ControllerBase { | ||
/** | ||
* Contoller method, autocomplete cvterm name. | ||
* | ||
* @param Request request | ||
* @param int $cv | ||
* @TODO: Limit the match of term to a CV. | ||
* @param int $count | ||
* Desired number of matching names to suggest. | ||
* Default to 5 items. | ||
* | ||
* @return Json Object | ||
* Matching cvterm rows where each row is formatted as string: | ||
* cvterm.name (db.name:dbxref.accession) and is the value for | ||
* the object keys label and value. | ||
*/ | ||
public function handleAutocomplete(Request $request, int $count = 5) { | ||
// Array to hold matching cvterm names. | ||
$response = null; | ||
|
||
if ($request->query->get('q')) { | ||
// Get typed in string input from the URL. | ||
$string = trim($request->query->get('q')); | ||
|
||
if (strlen($string) > 1 && $count > 0) { | ||
// Proceed to autocomplete when string is at least 2 characters | ||
// long and result count is set to a value greater than 0. | ||
|
||
// Transform string as a search keyword pattern. | ||
$keyword = '%' . strtolower($string) . '%'; | ||
|
||
// Query cvterm (joins: dbxref - accession and db - dn name) for names matching | ||
// the keyword pattern and return each row in the format specified. | ||
// Tables indicate schema sequence number #1 to use default schema. | ||
$sql = sprintf(" | ||
SELECT ct.name AS term, db.name AS dbname, dx.accession | ||
FROM {1:cvterm} AS ct | ||
LEFT JOIN {1:dbxref} AS dx USING(dbxref_id) | ||
LEFT JOIN {1:db} USING(db_id) | ||
WHERE ct.name LIKE :keyword ORDER BY ct.name ASC LIMIT %d | ||
", $count); | ||
|
||
// Prepare Chado database connection and execute sql query by providing value | ||
// for :keyword placeholder text. | ||
$connection = \Drupal::service('tripal_chado.database'); | ||
$results = $connection->query($sql, [':keyword' => $keyword]); | ||
|
||
// Compose response result. | ||
if ($results) { | ||
foreach ($results as $record) { | ||
$term = $record->term . ' (' . $record->dbname . ':' . $record->accession . ')'; | ||
$response[] = [ | ||
'value' => $term, // Value returned and value displayed by textfield. | ||
'label' => $term // Value shown in the list of options. | ||
]; | ||
} | ||
} | ||
} | ||
} | ||
|
||
return new JsonResponse($response); | ||
} | ||
|
||
/** | ||
* Fetch the cvterm.cvterm_id given a cvterm name (db.name:dbxref.accession) | ||
* value returned by the handler method above. | ||
* | ||
* @param string $term | ||
* String value returned by authocomplete handler method. | ||
* | ||
* @return integer | ||
* Id number corresponding to chado.cvterm_id field of the matching term | ||
* or 0 if no match was found. | ||
*/ | ||
public static function getCVtermId(string $term): int { | ||
$id = 0; | ||
|
||
if (strlen($term) > 0) { | ||
$sql = " | ||
SELECT ct.cvterm_id FROM {1:cvterm} AS ct | ||
LEFT JOIN {1:dbxref} AS dx USING(dbxref_id) | ||
LEFT JOIN {1:db} USING(db_id) | ||
WHERE CONCAT(ct.name, ' (', db.name, ':', dx.accession, ')') = :term | ||
LIMIT 1 | ||
"; | ||
|
||
$connection = \Drupal::service('tripal_chado.database'); | ||
$result = $connection->query($sql, [':term' => $term]); | ||
|
||
if($result) { | ||
$id = $result->fetchField(); | ||
} | ||
} | ||
|
||
return $id; | ||
} | ||
} |
122 changes: 122 additions & 0 deletions
122
tripal_chado/tests/src/Functional/Controller/ChadoTableCvtermAutocompleteTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
<?php | ||
|
||
namespace Drupal\Tests\tripal_chado\Functional; | ||
|
||
use Drupal\Tests\tripal_chado\Functional\ChadoTestBrowserBase; | ||
use Drupal\tripal_chado\Controller\ChadoCVTermAutocompleteController; | ||
use Symfony\Component\HttpFoundation\Request; | ||
|
||
/** | ||
* Test autocomplete cvterm name. | ||
*/ | ||
class ChadoTableCvtermAutocompleteTest extends ChadoTestBrowserBase { | ||
/** | ||
* Registered user with access content privileges. | ||
* | ||
* @var \Drupal\user\Entity\User | ||
*/ | ||
private $registered_user; | ||
|
||
|
||
/** | ||
* Test autocomplete cvterm name. | ||
*/ | ||
public function testAutocompleteCvterm() { | ||
// Setup registered user. | ||
$this->registered_user = $this->drupalCreateUser( | ||
['access content'], | ||
); | ||
|
||
$this->drupalLogin($this->registered_user); | ||
|
||
// Ensure we see all logging in tests. | ||
\Drupal::state()->set('is_a_test_environment', TRUE); | ||
|
||
// Create a new test schema for us to use. | ||
$connection = $this->createTestSchema(ChadoTestBrowserBase::PREPARE_TEST_CHADO); | ||
|
||
// Test Handler: | ||
$autocomplete = new ChadoCVTermAutocompleteController(); | ||
// Prepare a Request:$request entry. Search for null term | ||
// and suggest at least 5 items. | ||
$request = Request::create( | ||
'chado/cvterm/autocomplete/5', | ||
'GET', | ||
['q' => 'null'] | ||
); | ||
|
||
$suggest = $autocomplete->handleAutocomplete($request) | ||
->getContent(); | ||
|
||
// Find if null cvterm was suggested. | ||
$null_found = 0; | ||
foreach(json_decode($suggest) as $item) { | ||
if (str_contains($item->value, 'null')) { | ||
$null_found++; | ||
} | ||
} | ||
|
||
$this->assertTrue($null_found > 0); | ||
|
||
|
||
// Test Get Id. | ||
// Each item in the result for term null should have | ||
// an integer value which is the cvterm id number. | ||
foreach(json_decode($suggest) as $item) { | ||
// ChadoCVTermAutocompleteController::getCVtermId() | ||
$id = $autocomplete->getCVtermId($item->value); | ||
|
||
$this->assertNotNull($id); | ||
$this->assertIsInt($id); | ||
} | ||
|
||
|
||
// Test limit. | ||
$request = Request::create( | ||
'chado/cvterm/autocomplete/5', | ||
'GET', | ||
['q' => 'ul'] | ||
); | ||
// This will return > 6 terms ie. n[ul]l, vocab[ul]ary, pop[ul]ation, form[ul]a etc. | ||
// but should only suggest exactly 6 items. | ||
|
||
$suggest = $autocomplete->handleAutocomplete($request, 6) | ||
->getContent(); | ||
|
||
$this->assertEquals(count(json_decode($suggest)), 6); | ||
|
||
|
||
// Test exact term and 1 suggestion (exact match). | ||
$query = $connection->select('1:cvterm', 'c'); | ||
$query->condition('c.name', 'null', '='); | ||
$query->fields('c', ['cvterm_id']); | ||
$null_cvterm_id = $query->execute()->fetchField(); | ||
|
||
$request = Request::create( | ||
'chado/cvterm/autocomplete/5', | ||
'GET', | ||
['q' => 'null'] | ||
); | ||
|
||
$suggest = $autocomplete->handleAutocomplete($request, 1) | ||
->getContent(); | ||
|
||
foreach(json_decode($suggest) as $item) { | ||
// ChadoCVTermAutocompleteController::getCVtermId() | ||
$id = $autocomplete->getCVtermId($item->value); | ||
|
||
$this->assertNotNull($id); | ||
$this->assertIsInt($id); | ||
$this->assertEquals($id, $null_cvterm_id); | ||
} | ||
|
||
|
||
// Test invalid values as id passed to GetId. | ||
// Not found | ||
$not_ids = [0, 'lorem.ipsum', 'null', '@$#%', 'null (abc:xyz)', ' ', '.']; | ||
foreach($not_ids as $i) { | ||
$id = $autocomplete->getCVtermId($i); | ||
$this->assertEquals($id, 0); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters