Skip to content

Commit

Permalink
Add ability to configure what valuesets to use for a new tag @pedigree
Browse files Browse the repository at this point in the history
  • Loading branch information
dconlan committed Jun 1, 2022
1 parent 7fd6627 commit 6cf209e
Show file tree
Hide file tree
Showing 8 changed files with 430 additions and 24 deletions.
106 changes: 99 additions & 7 deletions PedigreeEditorExternalModule.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,53 @@ public function validateSettings($settings){
$errors .= "Failed to get Authentication Token for fhir server at '" . $authEndpoint . "' got exception $e\n";
}
}

$keyNameLookup = [
'disorder_system' => 'Disorder System',
'disorder_valueset' => 'Disorder Valueset',
'phenotype_system' => 'Phenotype System',
'phenotype_valueset' => 'Phenotype Valueset',
'gene_system' => 'Gene System',
'gene_valueset' => 'Gene Valueset',
];
$regexNameLookup = [
'disorder_regex' => 'Disorder Regex',
'phenotype_regex' => 'Phenotype Regex',
'gene_regex' => 'Gene Regex',
];
$systemDefTerminology = $settings['system_def_terminology'];
if ($systemDefTerminology === 'CUSTOM'){
// need to check something has been entered for terminology
foreach($keyNameLookup as $setting => $name){
if (!$settings['system_'.$setting]) {
$errors .= "Custom " . $name . " is required.\n";
}
}
foreach($regexNameLookup as $setting => $name){
if (!$settings['system_'.$setting]) {
$errors .= "Custom " . $name . " is required.\n";
} else if (preg_match('/'.$settings['system_'.$setting].'/', '') === false){
$errors .= "Custom " . $name . " is not a valid regular expression.\n";
}
}
}
$projectDefTerminology = $settings['project_def_terminology'];
if ($projectDefTerminology === 'CUSTOM'){
// need to check something has been entered for terminology
foreach($keyNameLookup as $setting => $name){
if (!$settings['project_'.$setting]) {
$errors .= "Custom " . $name . " is required.\n";
}
}
foreach($regexNameLookup as $setting => $name){
if (!$settings['project_'.$setting]) {
$errors .= "Custom " . $name . " is required.\n";
} else if (preg_match('/'.$settings['project_'.$setting].'/', '') === false){
$errors .= "Custom " . $name . " is not a valid regular expression.\n";
}
}
}

return $errors;
}

Expand All @@ -70,6 +117,7 @@ function add_pedigree_to_form ($project_id, $record, $instrument, $event_id, $gr
// maybe in the future they will be exposed.
$hpoEditorPage = 'open-pedigree/localEditor.html?mode=HPO';
$sctEditorPage = 'open-pedigree/localEditor.html?mode=SCT';
$customEditorPage = 'open-pedigree/localEditor.html?mode=CUSTOM';
$editorPageLocal = true;
$hpoTag = '@PEDIGREE_HPO';
$sctTag = '@PEDIGREE_SCT';
Expand All @@ -85,7 +133,40 @@ function add_pedigree_to_form ($project_id, $record, $instrument, $event_id, $gr
$projectCompression = $this->getProjectSetting('project_compression', $project_id);

$compression = ($projectCompression) ?: $systemCompression;


$systemDefTerminology = $this->getSystemSetting('system_def_terminology');
$projectDefTerminology = $this->getProjectSetting('project_def_terminology', $project_id);
$customTerminology = array();

$defTerminology = (!$projectDefTerminology || $projectDefTerminology === 'SYSTEM') ? $systemDefTerminology
: $projectDefTerminology;

if (!$projectDefTerminology || $projectDefTerminology === 'SYSTEM'){
if ($defTerminology === 'CUSTOM'){
$customTerminology['disorderSystem'] = $this->getSystemSetting('system_disorder_system');
$customTerminology['disorderValueset'] = $this->getSystemSetting('system_disorder_valueset');
$customTerminology['disorderRegex'] = $this->getSystemSetting('system_disorder_regex');
$customTerminology['phenotypeSystem'] = $this->getSystemSetting('system_phenotype_system');
$customTerminology['phenotypeValueset'] = $this->getSystemSetting('system_phenotype_valueset');
$customTerminology['phenotypeRegex'] = $this->getSystemSetting('system_phenotype_regex');
$customTerminology['geneSystem'] = $this->getSystemSetting('system_gene_system');
$customTerminology['geneValueset'] = $this->getSystemSetting('system_gene_valueset');
$customTerminology['geneRegex'] = $this->getSystemSetting('system_gene_regex');
}
} else if ($projectDefTerminology === 'CUSTOM') {
$customTerminology['disorderSystem'] = $this->getProjectSetting('system_disorder_system', $project_id);
$customTerminology['disorderValueset'] = $this->getProjectSetting('system_disorder_valueset', $project_id);
$customTerminology['disorderRegex'] = $this->getProjectSetting('system_disorder_regex', $project_id);
$customTerminology['phenotypeSystem'] = $this->getProjectSetting('system_phenotype_system', $project_id);
$customTerminology['phenotypeValueset'] = $this->getProjectSetting('system_phenotype_valueset', $project_id);
$customTerminology['phenotypeRegex'] = $this->getProjectSetting('system_phenotype_regex', $project_id);
$customTerminology['geneSystem'] = $this->getProjectSetting('system_gene_system', $project_id);
$customTerminology['geneValueset'] = $this->getProjectSetting('system_gene_valueset', $project_id);
$customTerminology['geneRegex'] = $this->getProjectSetting('system_gene_regex', $project_id);
}



// Get the data dictionary for the current instrument in array format
try {
$dd_array = \REDCap::getDataDictionary($project_id, 'array', false, null, $instrument);
Expand All @@ -100,14 +181,14 @@ function add_pedigree_to_form ($project_id, $record, $instrument, $event_id, $gr
{
if ($field_attributes['field_type'] === 'notes'){
if (preg_match(
'/@PEDIGREE_(HPO|SCT)(=(HIDE_TEXT|SHOW_TEXT|NEVER_COMPRESS|COMPRESS_LARGE|ALWAYS_COMPRESS)(,(HIDE_TEXT|SHOW_TEXT|NEVER_COMPRESS|COMPRESS_LARGE|ALWAYS_COMPRESS))?)?/',
'/@PEDIGREE(_(HPO|SCT))?(=(HIDE_TEXT|SHOW_TEXT|NEVER_COMPRESS|COMPRESS_LARGE|ALWAYS_COMPRESS)(,(HIDE_TEXT|SHOW_TEXT|NEVER_COMPRESS|COMPRESS_LARGE|ALWAYS_COMPRESS))?)?/',
$field_attributes['field_annotation'], $matches) === 1){

$mode = $matches[1];
$mode = $matches[2] ?: $defTerminology;
$hide = $hideText;
$fCompress = $compression;
$option1 = $matches[3];
$option2 = $matches[5];
$option1 = $matches[4];
$option2 = $matches[6];
if ($option1 === $hideTextOption || $option2 === $hideTextOption){
$hide = true;
}
Expand All @@ -127,7 +208,7 @@ function add_pedigree_to_form ($project_id, $record, $instrument, $event_id, $gr
$row = array();
$row['field'] = $field_name;
$row['label'] = $field_attributes['field_label'];
$row['mode'] = $mode;
$row['mode'] = $mode ?: $defTerminology;
$row['hideText'] = $hide;
$row['compress'] = $fCompress;
$fieldsOfInterest[] = $row;
Expand All @@ -150,23 +231,33 @@ function add_pedigree_to_form ($project_id, $record, $instrument, $event_id, $gr
$this->getSystemSetting('system_compression');
$hpoEditorPage = $hpoEditorPage . '&format=' . $format;
$sctEditorPage = $sctEditorPage . '&format=' . $format;
$customEditorPage = $customEditorPage . '&format=' . $format;

if ($ontologyServer){
$hpoEditorPage = $hpoEditorPage . '&redcapTerminolgyUrl=' . $ontologyServer;
$sctEditorPage = $sctEditorPage . '&redcapTerminolgyUrl=' . $ontologyServer;
$customEditorPage = $customEditorPage . '&redcapTerminolgyUrl=' . $ontologyServer;
}
if ($customTerminology){
foreach ($customTerminology as $key=>$val){
$customEditorPage = $customEditorPage . '&'.$key .'=' . urlencode($val);
}
}
// the local url build wants to put a '?' on the end which breaks paramaters, so add one to soak the extra
$hpoEditorPage = $hpoEditorPage . '&broken=redcap';
$sctEditorPage = $sctEditorPage . '&broken=redcap';

$customEditorPage = $customEditorPage . '&broken=redcap';

$fieldsOfInterestJson = json_encode($fieldsOfInterest);
if ($editorPageLocal){
$hpoEditorUrl = $this->getLocalUrl($hpoEditorPage);
$sctEditorUrl = $this->getLocalUrl($sctEditorPage);
$customEditorUrl = $this->getLocalUrl($customEditorPage);
}
else {
$hpoEditorUrl = $hpoEditorPage;
$sctEditorUrl = $sctEditorPage;
$customEditorUrl = $customEditorPage;
}

if ($transportType == 'message'){
Expand Down Expand Up @@ -195,6 +286,7 @@ function add_pedigree_to_form ($project_id, $record, $instrument, $event_id, $gr
pedigreeEditorEM.fieldsOfInterest = {$fieldsOfInterestJson};
pedigreeEditorEM.hpoEditorPage = '{$hpoEditorUrl}';
pedigreeEditorEM.sctEditorPage = '{$sctEditorUrl}';
pedigreeEditorEM.customEditorPage = '{$customEditorUrl}';
pedigreeEditorEM.emptyIcon = '#__pedigree_empty_svg';
pedigreeEditorEM.dataIcon = '#__pedigree_with_data_svg';
pedigreeEditorEM.windowName = 'pedigreeEditor';
Expand Down
49 changes: 42 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Alternatively you can clone the git repository and generate your own distributio
```
git clone https://github.com/aehrc/redcap_pedigree_editor.git
cd redcap_pedigree_editor
git archive --format=zip --prefix=redcap_pedigree_editor_v0.3.1/ -o ../redcap_pedigree_editor_v0.3.1.zip HEAD
git archive --format=zip --prefix=redcap_pedigree_editor_v0.3.2/ -o ../redcap_pedigree_editor_v0.3.2.zip HEAD
```

This will give you a file redcap_pedigree_editor_v0.3.zip
Expand All @@ -29,12 +29,13 @@ This will give you a file redcap_pedigree_editor_v0.3.zip
representation of the diagram, add compression for large diagrams.
- v0.3 - Change to use the new GA4GH FHIR format, do terminology lookups via a web service hosted in redcap.
- v0.3.1 - Minor bug fix in open-pedigree
- v0.3.2 - Add new action tag **@PEDIGREE** which uses configurable terminology settings.

# Install the distribution

The distribution is installed by unzipping the distribution file into the `redcap/modules/` directory of the redcap installation.

This should result in the new directory `'redcap_pedigree_editor_v0.1.1'`. The external module directory name must meet a strict naming
This should result in the new directory `'redcap_pedigree_editor_v0.3.2'`. The external module directory name must meet a strict naming
convention, if the directory is missing the `'v'` before the version number then the module won't be picked up by redcap, so rename
the directory to match the form `'<module name>_v<version number>'`.

Expand All @@ -60,25 +61,56 @@ Once installed the module has a number of system wide options:
- *Compress Large Diagrams >65K* - The data is compressed if its over 65K. If its still too large after being
compressed, the diagram is stripped and the if its greater than 65K its compressed.
- *Always Compress* - The data is always compressed. If the compressed data is greater than 65K the diagram is stripped.
- *Ontology Server URL* - The URL for FHIR ontology server used to lookup disorders, phenotypes and genes. If left blank then the default *'https://genomics.ontoserver.csiro.au/fhir/'* will be used. There is a matching project setting, which allows a project to use a different ontology server.
- *Ontology Server URL* - The URL for FHIR ontology server used to lookup disorders, phenotypes and genes.
- *Authentication Type* - The authentication to use when communicating with the FHIR server. This can be either `none`
or `OAuth2 Client Credentials`. The client credentials flow uses a client id and secret to obtain an access token.
- *OAuth2 token endpoint* - The token endpoint used to obtain the access token. This is required for `Oauth2 Client Credentials` authentication type.
- *Client Id* - The client id to use to fetch an access token. This is required for `Oauth2 Client Credentials` authentication type.
- *Client Secret* - The client secret to use to fetch an access token. This is required for `Oauth2 Client Credentials` authentication type.

![Configure](documentation/pedigree_v0.3_system_settings.png)
- *Default Terminology* - This setting configures the system level default terminologies to use with the @PEDIGREE tag. It will be one of:
- *SNOMEDCT* - This uses the same terminology as the @PEDIGREE_SCT tag.
- *HPO* - This uses the same terminology as the @PEDIGREE_HPO tag.
- *Custom* - The terminology settings will be entered into additional fields.
- *Disorder Code System* - The FHIR code system to use for disorders.
- *Disorder Valueset* - The FHIR valueset to use for disorders.
- *Disorder Regex* - Regular expression used to test if a code could be a member of the phenotype code system.
- *Phenotype Code System* - The FHIR code system to use for phenotypes.
- *Phenotype Valueset* - The FHIR valueset to use for phenotypes.
- *Phenotype Regex* - Regular expression used to test if a code could be a member of the phenotype code system.
- *Gene Code System* - The FHIR code system to use for genes.
- *Gene Valueset* - The FHIR valueset to use for genes.
- *Gene Regex* - Regular expression used to test if a code could be a member of the gene code system.

![Configure](documentation/pedigree_v0.3.2_system_settings_1.png)
![Configure](documentation/pedigree_v0.3.2_system_settings_2.png)

### Project Settings

Each project can override the *FHIR Format* and *Compress Data* setting. If left blank then the system setting will be used.

![Configure](documentation/pedigree_v0.3_project_settings.png)
The project can also override the terminology to use with the @PEDIGREE action tag
- *Default Terminology* - This setting configures the system level default terminologies to use with the @PEDIGREE tag. It will be one of:
- *SNOMEDCT* - This uses the same terminology as the @PEDIGREE_SCT tag.
- *HPO* - This uses the same terminology as the @PEDIGREE_HPO tag.
- *System* - This uses the system default terminology settings.
- *Custom* - The terminology settings will be entered into additional fields.
- *Disorder Code System* - The FHIR code system to use for disorders.
- *Disorder Valueset* - The FHIR valueset to use for disorders.
- *Disorder Regex* - Regular expression used to test if a code could be a member of the phenotype code system.
- *Phenotype Code System* - The FHIR code system to use for phenotypes.
- *Phenotype Valueset* - The FHIR valueset to use for phenotypes.
- *Phenotype Regex* - Regular expression used to test if a code could be a member of the phenotype code system.
- *Gene Code System* - The FHIR code system to use for genes.
- *Gene Valueset* - The FHIR valueset to use for genes.
- *Gene Regex* - Regular expression used to test if a code could be a member of the gene code system.


![Configure](documentation/pedigree_v0.3.2_project_settings.png)

## Creating a Pedigree field
To make use of the editor a field needs to be created in the online designer and marked with one of two action tags. Only fields of type `Notes Box` are considered.
- *@PEDIGREE_HPO* - Marks a field to be a pedigree editor using the HPO and OMIM coding systems for phenotypes and disorders.
- *@PEDIGREE_SCT* - Marks a field to be a pedigree editor using the SNOMED-CT coding system for phenotypes and disorders.
- *@PEDIGREE* - Marks a field to be a pedigree editor using the default coding system for phenotypes and disorders (new to version 0.3.2).

The default 'Hide Text' and 'Compress Data' options can be overriden in the action tag by appending '=' plus a comma
separated list of options.
Expand Down Expand Up @@ -154,6 +186,9 @@ problems reloading the disorder, genes and phenotypic features fields.
|Phenotype ValueSet |http://ga4gh.org/fhir/ValueSet/phenotype |http://ga4gh.org/fhir/ValueSet/phenotype |
|--------------------|-------------------------------------------------------|-------------------------------------------------------|

In version 0.3.2 a new action tag **@PEDIGREE** was added, this will use the terminology configured as the default terminology.
The default terminology may be a custom set of terminology bindings.

# Large Data Issues
A redcap notes field can store up to 65K of character data. This should be fine if someone was typing a note, but with
adding an svg representation of the pedigree diagram as well as the verbose nature of FHIR a large diagram can hit this
Expand Down
Loading

0 comments on commit 6cf209e

Please sign in to comment.