Skip to content

Commit

Permalink
[candidate_parameters] Add candidate parameters query engine (#8288)
Browse files Browse the repository at this point in the history
This adds a candidate parameters query engine to deal with candidate meta-data from the candidate_parameters module for the new data query API.

It also includes a new src/Data/Query/SQLQueryEngine.php abstract class to make it easier to build query engines based on SQL statements for modules where that can be done efficiently with a single statement for any dictionary terms in the data dictionary.
  • Loading branch information
driusan authored Nov 20, 2023
1 parent f0124b3 commit 916467f
Show file tree
Hide file tree
Showing 5 changed files with 2,812 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .phan/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
"unused_variable_detection" => true,
"suppress_issue_types" => [
"PhanUnusedPublicNoOverrideMethodParameter",
// Until phan/phan#4746 is fixed
"PhanTypeMismatchArgumentInternal"
],
"analyzed_file_extensions" => ["php", "inc"],
"directory_list" => [
Expand Down
271 changes: 271 additions & 0 deletions modules/candidate_parameters/php/candidatequeryengine.class.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
<?php declare(strict_types=1);

namespace LORIS\candidate_parameters;
use LORIS\Data\Scope;
use LORIS\Data\Cardinality;
use LORIS\Data\Dictionary\DictionaryItem;

/**
* A CandidateQueryEngine providers a QueryEngine interface to query
* against general candidate data such as identifiers, DoB, Entity Type,
* etc.
*
* @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
*/
class CandidateQueryEngine extends \LORIS\Data\Query\SQLQueryEngine
{
/**
* Return a data dictionary of data types managed by this module.
* DictionaryItems are grouped into categories and a module may
* provide 0 or more categories of dictionaryitems.
*
* @return \LORIS\Data\Dictionary\Category[]
*/
public function getDataDictionary() : iterable
{
$candscope = new Scope(Scope::CANDIDATE);
$sesscope = new Scope(Scope::SESSION);

$ids = new \LORIS\Data\Dictionary\Category(
"Identifiers",
"Candidate Identifiers"
);

$ids = $ids->withItems(
[
new DictionaryItem(
"CandID",
"LORIS Candidate Identifier",
$candscope,
new \LORIS\Data\Types\IntegerType(999999),
new Cardinality(Cardinality::UNIQUE),
),
new DictionaryItem(
"PSCID",
"Project Candidate Identifier",
$candscope,
new \LORIS\Data\Types\StringType(255),
new Cardinality(Cardinality::UNIQUE),
),
]
);

$demographics = new \LORIS\Data\Dictionary\Category(
"Demographics",
"Candidate Demographics",
);
$demographics = $demographics->withItems(
[
new DictionaryItem(
"DoB",
"Date of Birth",
$candscope,
new \LORIS\Data\Types\DateType(),
new Cardinality(Cardinality::SINGLE),
),
new DictionaryItem(
"DoD",
"Date of Death",
$candscope,
new \LORIS\Data\Types\DateType(),
new Cardinality(Cardinality::OPTIONAL),
),
new DictionaryItem(
"Sex",
"Candidate's biological sex",
$candscope,
new \LORIS\Data\Types\Enumeration('Male', 'Female', 'Other'),
new Cardinality(Cardinality::SINGLE),
),
new DictionaryItem(
"EDC",
"Expected Data of Confinement",
$candscope,
new \LORIS\Data\Types\DateType(),
new Cardinality(Cardinality::OPTIONAL),
),
]
);

$meta = new \LORIS\Data\Dictionary\Category("Meta", "Other parameters");

$db = $this->loris->getDatabaseConnection();
$participantstatus_options = $db->pselectCol(
"SELECT Description FROM participant_status_options",
[]
);
$meta = $meta->withItems(
[
new DictionaryItem(
"VisitLabel",
"The study visit label",
$sesscope,
new \LORIS\Data\Types\StringType(255),
new Cardinality(Cardinality::UNIQUE),
),
new DictionaryItem(
"Project",
"The LORIS project to categorize this session",
$sesscope,
new \LORIS\Data\Types\StringType(255), // FIXME: Make an enum
new Cardinality(Cardinality::SINGLE),
),
new DictionaryItem(
"Cohort",
"The LORIS cohort used for battery selection",
$sesscope,
new \LORIS\Data\Types\StringType(255),
new Cardinality(Cardinality::SINGLE),
),
new DictionaryItem(
"Site",
"The Site at which a visit occurred",
$sesscope,
new \LORIS\Data\Types\Enumeration(...\Utility::getSiteList()),
new Cardinality(Cardinality::SINGLE),
),
new DictionaryItem(
"EntityType",
"The type of entity which this candidate represents",
$candscope,
new \LORIS\Data\Types\Enumeration('Human', 'Scanner'),
new Cardinality(Cardinality::SINGLE),
),
new DictionaryItem(
"ParticipantStatus",
"The status of the participant within the study",
$candscope,
new \LORIS\Data\Types\Enumeration(...$participantstatus_options),
new Cardinality(Cardinality::SINGLE),
),
new DictionaryItem(
"RegistrationSite",
"The site at which this candidate was initially registered",
$candscope,
new \LORIS\Data\Types\Enumeration(...\Utility::getSiteList()),
new Cardinality(Cardinality::SINGLE),
),
new DictionaryItem(
"RegistrationProject",
"The project for which this candidate was initially registered",
$candscope,
new \LORIS\Data\Types\StringType(255), // FIXME: Make an enum
new Cardinality(Cardinality::SINGLE),
),
]
);
return [$ids, $demographics, $meta];
}
/**
* {@inheritDoc}
*
* @param \Loris\Data\Dictionary\Category $cat The dictionaryItem
* category
* @param \Loris\Data\Dictionary\DictionaryItem $item The item
*
* @return string[]
*/
public function getVisitList(
\LORIS\Data\Dictionary\Category $cat,
\LORIS\Data\Dictionary\DictionaryItem $item
) : iterable {
if ($item->getScope()->__toString() !== 'session') {
return [];
}

// Session scoped variables: VisitLabel, project, site, cohort
return array_keys(\Utility::getVisitList());
}

/**
* Get the SQL field name to use to refer to a dictionary item.
*
* @param \LORIS\Data\Dictionary\DictionaryItem $item The dictionary item
*
* @return string
*/
protected function getFieldNameFromDict(
\LORIS\Data\Dictionary\DictionaryItem $item
) : string {
switch ($item->getName()) {
case 'CandID':
return 'c.CandID';
case 'PSCID':
return 'c.PSCID';
case 'Site':
$this->addTable('LEFT JOIN session s ON (s.CandID=c.CandID)');
$this->addTable('LEFT JOIN psc site ON (s.CenterID=site.CenterID)');
$this->addWhereClause("s.Active='Y'");
return 'site.Name';
case 'RegistrationSite':
$this->addTable(
'LEFT JOIN psc rsite'
. ' ON (c.RegistrationCenterID=rsite.CenterID)'
);
return 'rsite.Name';
case 'Sex':
return 'c.Sex';
case 'DoB':
return 'c.DoB';
case 'DoD':
return 'c.DoD';
case 'EDC':
return 'c.EDC';
case 'Project':
$this->addTable('LEFT JOIN session s ON (s.CandID=c.CandID)');
$this->addTable(
'LEFT JOIN Project proj ON (s.ProjectID=proj.ProjectID)'
);
$this->addWhereClause("s.Active='Y'");

return 'proj.Name';
case 'RegistrationProject':
$this->addTable(
'LEFT JOIN Project rproj'
.' ON (c.RegistrationProjectID=rproj.ProjectID)'
);
return 'rproj.Name';
case 'Cohort':
$this->addTable('LEFT JOIN session s ON (s.CandID=c.CandID)');
$this->addTable(
'LEFT JOIN cohort cohort'
.' ON (s.CohortID=cohort.CohortID)'
);
$this->addWhereClause("s.Active='Y'");

return 'cohort.title';
case 'VisitLabel':
$this->addTable('LEFT JOIN session s ON (s.CandID=c.CandID)');
$this->addWhereClause("s.Active='Y'");
return 's.Visit_label';
case 'EntityType':
return 'c.Entity_type';
case 'ParticipantStatus':
$this->addTable(
'LEFT JOIN participant_status ps ON (ps.CandID=c.CandID)'
);
$this->addTable(
'LEFT JOIN participant_status_options pso ' .
'ON (ps.participant_status=pso.ID)'
);
return 'pso.Description';
default:
throw new \DomainException("Invalid field " . $item->getName());
}
}


/**
* {@inheritDoc}
*
* @param string $fieldname A field name
*
* @return string
*/
protected function getCorrespondingKeyField($fieldname)
{
// There are no cardinality::many fields in this query engine, so this
// should never get called
throw new \Exception("Unhandled Cardinality::MANY field $fieldname");
}
}
10 changes: 10 additions & 0 deletions modules/candidate_parameters/php/module.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,14 @@ class Module extends \Module
}
return $entries;
}

/**
* {@inheritDoc}
*
* @return \LORIS\Data\Query\QueryEngine
*/
public function getQueryEngine() : \LORIS\Data\Query\QueryEngine
{
return new CandidateQueryEngine($this->loris);
}
}
Loading

0 comments on commit 916467f

Please sign in to comment.