Skip to content

Commit 18e5e76

Browse files
committed
Add support for loinc fhir server, change storage format to not include display
1 parent f84cb8e commit 18e5e76

3 files changed

Lines changed: 270 additions & 72 deletions

File tree

FhirOntologyAutocompleteExternalModule.php

Lines changed: 133 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,16 @@ public function validateSettings($settings)
149149
}
150150
}
151151
$headers = ['User-Agent: Redcap'];
152+
$authType = $settings['authentication_type'];
153+
if ($authType === 'basic') {
154+
$authUser = $settings['basic_user_id'];
155+
$authPassword = $settings['basic_user_password'];
156+
$headers[] = 'Authorization: Basic ' . base64_encode($authUser . ':' . $authPassword);
157+
}
152158
$metadata = $this->httpGet($fhirUrl . '/metadata', $headers);
153159
if ($metadata === FALSE) {
154160
$errors .= "Failed to get metadata for fhir server at '" . $fhirUrl . "'\n";
155161
}
156-
$authType = $settings['authentication_type'];
157162
if ($authType === 'cc') {
158163
$authEndpoint = $settings['cc_token_endpoint'];
159164
$clientId = $settings['cc_client_id'];
@@ -215,6 +220,17 @@ public function getFhirServerUri()
215220
return $ontologyServer;
216221
}
217222

223+
public function hasSnomedSupport()
224+
{
225+
return $this->getSystemSetting('snomed_support');
226+
}
227+
228+
public function hasLoincSupport()
229+
{
230+
return $this->getSystemSetting('loinc_support');
231+
}
232+
233+
218234
/**
219235
* Search API with a search term for a given ontology
220236
* Returns array of results with Notation as key and PrefLabel as value.
@@ -227,9 +243,9 @@ public function searchOntology($valueset_id, $search_term, $result_limit)
227243

228244
// Build URL to call
229245
$headers = ['User-Agent: Redcap'];
230-
$authToken = $this->getAuthToken();
231-
if ($authToken !== false) {
232-
$headers[] = 'Authorization: Bearer ' . $this->getAuthToken();
246+
$authHeader = $this->getAuthHeader();
247+
if ($authHeader !== false) {
248+
$headers[] = $authHeader;
233249
}
234250
// Base URL + “/ValueSet/$expand?identifier=VS_ID&filter=SEARCH_TERM”
235251
// need to escape the $expand in the url!
@@ -256,7 +272,7 @@ public function searchOntology($valueset_id, $search_term, $result_limit)
256272
}
257273
// Determine the value
258274
// need to add the system as codes are not unique in SCT
259-
$this_value = $this_item['code'] . "|" . $this_item['display'] . "|" . $this_item['system'];
275+
$this_value = $this_item['code'] . "|" . $this_item['system'];
260276

261277
// Add to array
262278
$results[$this_value] = $this_item['display'];
@@ -322,6 +338,18 @@ public function getOnlineDesignerSection()
322338
{
323339

324340
$findValueSetService_url = $this->getUrl('FindValueSetService.php', false, true);
341+
$loincSupport = $this->hasLoincSupport();
342+
$implicitSearchOptions = '';
343+
if ($this->hasSnomedSupport()){
344+
$implicitSearchOptions = $implicitSearchOptions .
345+
'<option value="refset">SnomedCT Refset</option>\n' .
346+
'<option value="isa">SnomedCT isa implicit valueset</option>\n';
347+
}
348+
if ($loincSupport === "ontoserver" || $loincSupport === "filter"){
349+
$implicitSearchOptions = $implicitSearchOptions .
350+
'<option value="loinc_answer">LOINC implicit answer set</option>';
351+
}
352+
325353
$onlineDesignerHtml = <<<EOD
326354
<script type="text/javascript">
327355
@@ -352,6 +380,14 @@ function move_selected_valueset(event){
352380
return false;
353381
}
354382
383+
function manual_valuset_update(event){
384+
selected_valueset = $('#fhir_value_set').val();
385+
if (selected_valueset){
386+
update_ontology_selection('FHIR', selected_valueset);
387+
}
388+
}
389+
390+
355391
function JSON_STRING(data){
356392
this.data = data;
357393
}
@@ -483,18 +519,16 @@ function show_selected_valueset(event){
483519
class='x-form-text x-form-field' style='padding-right:0;height:22px;width:330px;max-width:330px;'>
484520
<option value=""> -- choose search criteria -- </option>
485521
<option value="name">ValueSet Name</option>
486-
<option value="codesystem">By CodeSystem(Name)</option>
487-
<option value="refset">SnomedCT Refset</option>
488-
<option value="isa">SnomedCT isa implicit valueset</option>
489-
<option value="loinc_answer">LOINC implicit answer set</option>
522+
<option value="codesystem">By CodeSystem(Title)</option>
523+
{$implicitSearchOptions}
490524
</select><br>
491525
<div class="ui-front">
492526
<input id="fhir_valueset_search" class="x-form-text x-form-field" size="25">
493527
<span id="fhir_valueset_search_code" style="display:none"></span>
494528
<a class="ui-button ui-widget ui-corner-all" href="#" onclick="move_selected_valueset(event)">Select</a>
495529
</div>
496530
497-
<input id="fhir_value_set" class="x-form-text x-form-field" size="25" type="text" readonly="readonly">
531+
<input id="fhir_value_set" class="x-form-text x-form-field" size="25" type="text" onchange="manual_valuset_update(event)">
498532
<a class="ui-button ui-widget ui-corner-all" href="#" onclick="show_selected_valueset(event)">Show Details</a>
499533
500534
<div id="fhir_valueset_dialog" title="ValueSet Details">
@@ -543,11 +577,11 @@ public function getLabelForValue($category, $value)
543577

544578
public function isValidValuesetQueryType($type)
545579
{
580+
$loincSupport = $this->hasLoincSupport();
546581
if ('name' === $type ||
547582
'codesystem' === $type ||
548-
'refset' === $type ||
549-
'isa' === $type ||
550-
'loinc_answer' === $type) {
583+
(('refset' === $type || 'isa' === $type) && $this->hasSnomedSupport()) ||
584+
(('loinc_answer' === $type) && ('ontoserver' === $loincSupport || 'filter' == $loincSupport))) {
551585
return true;
552586
}
553587
return false;
@@ -556,6 +590,8 @@ public function isValidValuesetQueryType($type)
556590
public function findValueSet($type, $query)
557591
{
558592
$contentType = 'application/x-www-form-urlencoded';
593+
$snomedSupport = $this->hasSnomedSupport();
594+
$loincSupport = $this->hasLoincSupport();
559595
if ($type === 'name') {
560596
$method = "GET";
561597
$params = ['name' => $query, '_summary' => true, '_count' => 20];
@@ -575,14 +611,14 @@ public function findValueSet($type, $query)
575611
};
576612
} elseif ($type === 'codesystem') {
577613
$method = "GET";
578-
$params = ['name' => $query, '_elements' => 'name,valueSet', '_count' => 20];
614+
$params = ['title' => $query, '_elements' => 'title,valueSet', '_count' => 20];
579615
$url = "/CodeSystem";
580616
$processFunction = function ($data) {
581617
$result = [];
582618
if (isset($data['entry'])) {
583619
foreach ($data['entry'] as $this_entry) {
584620
if (isset($this_entry['resource']['valueSet'])) {
585-
$result[] = ['label' => $this_entry['resource']['name'], 'value' => $this_entry['resource']['valueSet']];
621+
$result[] = ['label' => $this_entry['resource']['title'], 'value' => $this_entry['resource']['valueSet']];
586622
}
587623
}
588624
}
@@ -593,6 +629,9 @@ public function findValueSet($type, $query)
593629
return $result;
594630
};
595631
} elseif ($type === 'refset') {
632+
if (!$snomedSupport){
633+
return []; // snomed implicit valuesets not supported
634+
}
596635
$method = "GET";
597636
$params = ['filter' => $query, 'url' => 'http://snomed.info/sct?fhir_vs=refset', '_count' => 20];
598637
$url = '/ValueSet/$expand';
@@ -610,6 +649,9 @@ public function findValueSet($type, $query)
610649
return $result;
611650
};
612651
} elseif ($type === 'isa') {
652+
if (!$snomedSupport){
653+
return []; // snomed implicit valuesets not supported
654+
}
613655
$method = "GET";
614656
$params = ['filter' => $query, 'url' => 'http://snomed.info/sct?fhir_vs', '_count' => 20];
615657
$url = '/ValueSet/$expand';
@@ -627,53 +669,80 @@ public function findValueSet($type, $query)
627669
return $result;
628670
};
629671
} elseif ($type === 'loinc_answer') {
630-
$method = "POST";
631-
$contentType = "application/json";
632-
$postData = [
633-
"resourceType" => "Parameters",
634-
"parameter" => [
635-
["name" => "filter", "valueString" => $query],
636-
["name" => "_count", "valueInteger" => 20],
637-
["name" => "valueSet",
638-
"resource" => [
639-
"resourceType" => "ValueSet",
640-
"compose" => [
641-
"include" => [[
642-
"system" => "http://loinc.org",
643-
"filter" => [
644-
["property" => "parent",
645-
"op" => "=",
646-
"value" => "LL"]
647-
]
648-
]]
672+
if ('ontoserver' === $loincSupport){
673+
$method = "POST";
674+
$contentType = "application/json";
675+
$postData = [
676+
"resourceType" => "Parameters",
677+
"parameter" => [
678+
["name" => "filter", "valueString" => $query],
679+
["name" => "_count", "valueInteger" => 20],
680+
["name" => "valueSet",
681+
"resource" => [
682+
"resourceType" => "ValueSet",
683+
"compose" => [
684+
"include" => [[
685+
"system" => "http://loinc.org",
686+
"filter" => [
687+
["property" => "parent",
688+
"op" => "=",
689+
"value" => "LL"]
690+
]
691+
]]
692+
]
649693
]
650694
]
651695
]
652-
]
653-
];
654-
$postData = json_encode($postData, JSON_UNESCAPED_SLASHES);
655-
656-
$url = '/ValueSet/$expand';
657-
$processFunction = function ($data) {
658-
$result = [];
659-
if (isset($data['expansion']) && isset($data['expansion']['contains'])) {
660-
foreach ($data['expansion']['contains'] as $this_entry) {
661-
$result[] = ['label' => $this_entry['display'], 'value' => 'http://loinc.org/vs/' . $this_entry['code']];
696+
];
697+
$postData = json_encode($postData, JSON_UNESCAPED_SLASHES);
698+
699+
$url = '/ValueSet/$expand';
700+
$processFunction = function ($data) {
701+
$result = [];
702+
if (isset($data['expansion']) && isset($data['expansion']['contains'])) {
703+
foreach ($data['expansion']['contains'] as $this_entry) {
704+
$result[] = ['label' => $this_entry['display'], 'value' => 'http://loinc.org/vs/' . $this_entry['code']];
705+
}
706+
}
707+
if (empty($result)) {
708+
$result[] = ['label' => 'No matches found', 'value' => '__NMF__'];
662709
}
663-
}
664-
if (empty($result)) {
665-
$result[] = ['label' => 'No matches found', 'value' => '__NMF__'];
666-
}
667710

668-
return $result;
669-
};
711+
return $result;
712+
};
713+
}
714+
elseif ('filter' === $loincSupport){
715+
$method = "GET";
716+
$params = ['filter' => $query, 'url' => 'http://loinc.org/vs', '_count' => 100];
717+
$url = '/ValueSet/$expand';
718+
$processFunction = function ($data) {
719+
$result = [];
720+
if (isset($data['expansion']) && isset($data['expansion']['contains'])) {
721+
foreach ($data['expansion']['contains'] as $this_entry) {
722+
if (substr($this_entry['code'], 0, 2 ) === 'LL'){
723+
$result[] = ['label' => $this_entry['display'], 'value' => 'http://loinc.org/vs/' . $this_entry['code']];
724+
}
725+
if (count($result) >= 20){
726+
break;
727+
}
728+
}
729+
}
730+
if (empty($result)) {
731+
$result[] = ['label' => 'No matches found', 'value' => '__NMF__'];
732+
}
733+
return $result;
734+
};
735+
}
736+
else {
737+
return []; // loinc not supported
738+
}
670739
} else {
671740
return ['error' => "Unknown search type $type"];
672741
}
673742
$headers = ['User-Agent: Redcap'];
674-
$authToken = $this->getAuthToken();
675-
if ($authToken !== false) {
676-
$headers[] = 'Authorization: Bearer ' . $this->getAuthToken();
743+
$authHeader = $this->getAuthHeader();
744+
if ($authHeader !== false) {
745+
$headers[] = $authHeader;
677746
}
678747
if ('GET' === $method) {
679748
$fullUrl = $this->getFhirServerUri() . $url . '?' . http_build_query($params);
@@ -693,9 +762,9 @@ public function getValueSetInfo($valueSet)
693762
$params = ["url" => $valueSet, "count" => 10];
694763
$fullUrl = $this->getFhirServerUri() . '/ValueSet/$expand?' . http_build_query($params);
695764
$headers = ['User-Agent: Redcap'];
696-
$authToken = $this->getAuthToken();
697-
if ($authToken !== false) {
698-
$headers[] = 'Authorization: Bearer ' . $this->getAuthToken();
765+
$authHeader = $this->getAuthHeader();
766+
if ($authHeader !== false) {
767+
$headers[] = $authHeader;
699768
}
700769
return $this->httpGet($fullUrl, $headers);
701770
}
@@ -798,15 +867,21 @@ public function httpPost($fullUrl, $postData, $contentType, $headers)
798867
}
799868

800869

801-
public function getAuthToken()
870+
public function getAuthHeader()
802871
{
803872
$authType = $this->getSystemSetting('authentication_type');
804873
if ($authType === 'cc') {
805874
$authEndpoint = $this->getSystemSetting('cc_token_endpoint');
806875
$clientId = $this->getSystemSetting('cc_client_id');
807876
$clientSecret = $this->getSystemSetting('cc_client_secret');
808877

809-
return $this->getClientCredentialsToken($authEndpoint, $clientId, $clientSecret);
878+
$authToken = $this->getClientCredentialsToken($authEndpoint, $clientId, $clientSecret);
879+
return 'Authorization: Bearer ' . $authToken;
880+
}
881+
elseif ($authType === 'basic') {
882+
$userId = $this->getSystemSetting('basic_user_id');
883+
$userPassword = $this->getSystemSetting('basic_user_password');
884+
return 'Authorization: Basic ' . base64_encode($userId . ':' . $userPassword);
810885
}
811886
return false;
812887
}

0 commit comments

Comments
 (0)