Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding a glossary endpoint which attempts to get patient-friendly descriptions from code. #120

Merged
merged 7 commits into from
Mar 21, 2023
Next Next commit
adding a glossary endpoint which attempts to get patient-friendly des…
…criptions from code.
  • Loading branch information
AnalogJ committed Mar 20, 2023
commit 2f415c2e31a879ffd8a9aef377ad5ffa25def1b4
59 changes: 59 additions & 0 deletions backend/pkg/models/medlineplus_connect_results.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package models

import "time"

type MedlinePlusConnectResults struct {
Feed struct {
Base string `json:"base"`
Lang string `json:"lang"`
Xsi string `json:"xsi"`
Title struct {
Type string `json:"type"`
Value string `json:"_value"`
} `json:"title"`
Updated struct {
Value time.Time `json:"_value"`
} `json:"updated"`
ID struct {
Value string `json:"_value"`
} `json:"id"`
Author struct {
Name struct {
Value string `json:"_value"`
} `json:"name"`
URI struct {
Value string `json:"_value"`
} `json:"uri"`
} `json:"author"`
Subtitle struct {
Type string `json:"type"`
Value string `json:"_value"`
} `json:"subtitle"`
Category []struct {
Scheme string `json:"scheme"`
Term string `json:"term"`
} `json:"category"`
Entry []MedlinePlusConnectResultEntry `json:"entry"`
} `json:"feed"`
}

type MedlinePlusConnectResultEntry struct {
Title struct {
Value string `json:"_value"`
Type string `json:"type"`
} `json:"title"`
Link []struct {
Href string `json:"href"`
Rel string `json:"rel"`
} `json:"link"`
ID struct {
Value string `json:"_value"`
} `json:"id"`
Summary struct {
Type string `json:"type"`
Value string `json:"_value"`
} `json:"summary"`
Updated struct {
Value time.Time `json:"_value"`
} `json:"updated"`
}
112 changes: 112 additions & 0 deletions backend/pkg/web/handler/glossary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package handler

import (
"encoding/json"
"fmt"
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg"
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
"github.com/fastenhealth/gofhir-models/fhir401"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"log"
"net/http"
"net/url"
"strings"
"time"
)

func FindCodeSystem(codeSystem string) (string, error) {
log.Printf("codeSystem: %s", codeSystem)
if strings.HasPrefix(codeSystem, "2.16.840.1.113883.6.") {
return codeSystem, nil
}

//https://terminology.hl7.org/external_terminologies.html
codeSystemIds := map[string]string{
"http:https://hl7.org/fhir/sid/icd-10-cm": "2.16.840.1.113883.6.90",
"http:https://terminology.hl7.org/CodeSystem/icd9cm": "2.16.840.1.113883.6.103",
"http:https://snomed.info/sct": "2.16.840.1.113883.6.96",
"http:https://www.nlm.nih.gov/research/umls/rxnorm": "2.16.840.1.113883.6.88",
"http:https://hl7.org/fhir/sid/ndc": "2.16.840.1.113883.6.69",
"http:https://loinc.org": "2.16.840.1.113883.6.1",
"http:https://www.ama-assn.org/go/cpt": "2.16.840.1.113883.6.12",
}

if codeSystemId, ok := codeSystemIds[codeSystem]; ok {
return codeSystemId, nil
} else {
return "", fmt.Errorf("Code System not found")
}

}

// https://medlineplus.gov/medlineplus-connect/web-service/
// NOTE: max requests is 100/min
func GlossarySearchByCode(c *gin.Context) {
logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry)
//databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)

codeSystemId, err := FindCodeSystem(c.Query("code_system"))
if err != nil {
logger.Error(err)
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"error": err.Error(),
})
return
}

// Define the URL for the MedlinePlus Connect Web Service API
medlinePlusConnectEndpoint := "https://connect.medlineplus.gov/service"

// Define the query parameters
params := url.Values{
"informationRecipient.languageCode.c": []string{"en"},
"knowledgeResponseType": []string{"application/json"},
"mainSearchCriteria.v.c": []string{c.Query("code")},
"mainSearchCriteria.v.cs": []string{codeSystemId},
}

// Send the HTTP GET request to the API and retrieve the response
resp, err := http.Get(medlinePlusConnectEndpoint + "?" + params.Encode())
if err != nil {
fmt.Println("Error sending request:", err)
return
}
defer resp.Body.Close()

// Parse the JSON response into a struct
var response models.MedlinePlusConnectResults
err = json.NewDecoder(resp.Body).Decode(&response)
if err != nil {
fmt.Println("Error parsing response:", err)
return
}

//store in DB cache

//sourceCred, err := databaseRepo.GetSource(c, c.Param("sourceId"))
//if err != nil {
// logger.Errorln("An error occurred while retrieving source credential", err)
// c.JSON(http.StatusInternalServerError, gin.H{"success": false})
// return
//}

if len(response.Feed.Entry) == 0 {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "No results found"})
return
} else {
foundEntry := response.Feed.Entry[0]

dateStr := foundEntry.Updated.Value.Format(time.RFC3339)
valueSet := fhir401.ValueSet{
Title: &foundEntry.Title.Value,
Url: &foundEntry.Link[0].Href,
Description: &foundEntry.Summary.Value,
Date: &dateStr,
Publisher: &response.Feed.Author.Name.Value,
}

c.JSON(http.StatusOK, valueSet)
}
}
2 changes: 2 additions & 0 deletions backend/pkg/web/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func (ae *AppEngine) Setup(logger *logrus.Entry) *gin.Engine {
//r.Any("/database/*proxyPath", handler.CouchDBProxy)
//r.GET("/cors/*proxyPath", handler.CORSProxy)
//r.OPTIONS("/cors/*proxyPath", handler.CORSProxy)
api.GET("/glossary/code", handler.GlossarySearchByCode)

secure := api.Group("/secure").Use(middleware.RequireAuth())
{
Expand All @@ -61,6 +62,7 @@ func (ae *AppEngine) Setup(logger *logrus.Entry) *gin.Engine {
secure.GET("/resource/graph", handler.GetResourceFhirGraph)
secure.GET("/resource/fhir/:sourceId/:resourceId", handler.GetResourceFhir)
secure.POST("/resource/composition", handler.CreateResourceComposition)

}

if ae.Config.GetBool("web.allow_unsafe_endpoints") {
Expand Down