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
Prev Previous commit
Next Next commit
using caching mechanism for retrieving codes from MedlinePlus safely.
  • Loading branch information
AnalogJ committed Mar 20, 2023
commit 1c4799b2e1b1dfb3bc1f04df2bff002bfc3c6be2
5 changes: 4 additions & 1 deletion backend/pkg/database/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ type DatabaseRepository interface {
GetSourceSummary(context.Context, string) (*models.SourceSummary, error)
GetSources(context.Context) ([]models.SourceCredential, error)

//used by Client
CreateGlossaryEntry(ctx context.Context, glossaryEntry *models.Glossary) error
GetGlossaryEntry(ctx context.Context, code string, codeSystem string) (*models.Glossary, error)

//used by fasten-sources Clients
UpsertRawResource(ctx context.Context, sourceCredentials sourcePkg.SourceCredential, rawResource sourcePkg.RawResourceFhir) (bool, error)
}
25 changes: 22 additions & 3 deletions backend/pkg/database/sqlite_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,14 @@ func NewRepository(appConfig config.Interface, globalLogger logrus.FieldLogger)
}
globalLogger.Infof("Successfully connected to fasten sqlite db: %s\n", appConfig.GetString("database.location"))

deviceRepo := SqliteRepository{
fastenRepo := SqliteRepository{
AppConfig: appConfig,
Logger: globalLogger,
GormClient: database,
}

//TODO: automigrate for now
err = deviceRepo.Migrate()
err = fastenRepo.Migrate()
if err != nil {
return nil, err
}
Expand All @@ -76,7 +76,7 @@ func NewRepository(appConfig config.Interface, globalLogger logrus.FieldLogger)
return nil, fmt.Errorf("Failed to create admin user! - %v", err)
}

return &deviceRepo, nil
return &fastenRepo, nil
}

type SqliteRepository struct {
Expand All @@ -91,6 +91,7 @@ func (sr *SqliteRepository) Migrate() error {
&models.User{},
&models.SourceCredential{},
&models.ResourceFhir{},
&models.Glossary{},
)
if err != nil {
return fmt.Errorf("Failed to automigrate! - %v", err)
Expand Down Expand Up @@ -140,6 +141,24 @@ func (sr *SqliteRepository) GetCurrentUser(ctx context.Context) (*models.User, e
return &currentUser, nil
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Glossary
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

func (sr *SqliteRepository) CreateGlossaryEntry(ctx context.Context, glossaryEntry *models.Glossary) error {
record := sr.GormClient.Create(glossaryEntry)
if record.Error != nil {
return record.Error
}
return nil
}

func (sr *SqliteRepository) GetGlossaryEntry(ctx context.Context, code string, codeSystem string) (*models.Glossary, error) {
var foundGlossaryEntry models.Glossary
result := sr.GormClient.Where(models.Glossary{Code: code, CodeSystem: codeSystem}).First(&foundGlossaryEntry)
return &foundGlossaryEntry, result.Error
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Summary
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
14 changes: 14 additions & 0 deletions backend/pkg/models/glossary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package models

// Glossary contains patient friendly terms for medical concepts
// Can be retrieved by Code and CodeSystem
// Structured similar to ValueSet https://hl7.org/fhir/valueset.html
type Glossary struct {
ModelBase
Code string `json:"code" gorm:"uniqueIndex:idx_glossary_term"`
CodeSystem string `json:"code_system" gorm:"uniqueIndex:idx_glossary_term"`
Publisher string `json:"publisher"`
Title string `json:"title"`
Url string `json:"url"`
Description string `json:"description"`
}
51 changes: 41 additions & 10 deletions backend/pkg/web/handler/glossary.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg"
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
"github.com/fastenhealth/gofhir-models/fhir401"
"github.com/gin-gonic/gin"
Expand Down Expand Up @@ -47,7 +48,7 @@ func FindCodeSystem(codeSystem string) (string, error) {
// 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)
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)

codeSystemId, err := FindCodeSystem(c.Query("code_system"))
if err != nil {
Expand All @@ -58,6 +59,31 @@ func GlossarySearchByCode(c *gin.Context) {
})
return
}
if c.Query("code") == "" {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"error": "code is required",
})
return
}

//Check if the code is in the DB cache
foundGlossaryEntry, err := databaseRepo.GetGlossaryEntry(c, c.Query("code"), codeSystemId)
if err == nil {
//found in DB cache
logger.Debugf("Found code (%s %s) in DB cache", c.Query("code"), codeSystemId)
dateStr := foundGlossaryEntry.UpdatedAt.Format(time.RFC3339)
valueSet := fhir401.ValueSet{
Title: &foundGlossaryEntry.Title,
Url: &foundGlossaryEntry.Url,
Description: &foundGlossaryEntry.Description,
Date: &dateStr,
Publisher: &foundGlossaryEntry.Publisher,
}

c.JSON(http.StatusOK, valueSet)
return
}

// Define the URL for the MedlinePlus Connect Web Service API
medlinePlusConnectEndpoint := "https://connect.medlineplus.gov/service"
Expand Down Expand Up @@ -98,15 +124,6 @@ func GlossarySearchByCode(c *gin.Context) {
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
Expand All @@ -122,6 +139,20 @@ func GlossarySearchByCode(c *gin.Context) {
Publisher: &response.Feed.Author.Name.Value,
}

//store in DB cache (ignore errors)
databaseRepo.CreateGlossaryEntry(c, &models.Glossary{
ModelBase: models.ModelBase{
CreatedAt: foundEntry.Updated.Value,
UpdatedAt: foundEntry.Updated.Value,
},
Code: c.Query("code"),
CodeSystem: codeSystemId,
Publisher: response.Feed.Author.Name.Value,
Title: foundEntry.Title.Value,
Url: foundEntry.Link[0].Href,
Description: foundEntry.Summary.Value,
})

c.JSON(http.StatusOK, valueSet)
}
}