sync all resources on completion.
This commit is contained in:
parent
6a8df64691
commit
c9fc23e3a9
|
@ -10,7 +10,7 @@ type DatabaseRepository interface {
|
|||
Close() error
|
||||
GetCurrentUser() models.User
|
||||
|
||||
UpsertResource(context.Context, interface{}) error
|
||||
UpsertResource(context.Context, models.ResourceFhir) error
|
||||
//UpsertProfile(context.Context, *models.Profile) error
|
||||
//UpsertOrganziation(context.Context, *models.Organization) error
|
||||
|
||||
|
|
|
@ -46,9 +46,7 @@ func NewRepository(appConfig config.Interface, globalLogger logrus.FieldLogger)
|
|||
err = database.AutoMigrate(
|
||||
&models.User{},
|
||||
&models.Source{},
|
||||
&models.Profile{},
|
||||
&models.Organization{},
|
||||
&models.Encounter{},
|
||||
&models.ResourceFhir{},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to automigrate! - %v", err)
|
||||
|
@ -87,61 +85,17 @@ func (sr *sqliteRepository) GetCurrentUser() models.User {
|
|||
return currentUser
|
||||
}
|
||||
|
||||
func (sr *sqliteRepository) UpsertResource(ctx context.Context, resourceModel interface{}) error {
|
||||
func (sr *sqliteRepository) UpsertResource(ctx context.Context, resourceModel models.ResourceFhir) error {
|
||||
sr.logger.Infof("insert/update (%T) %v", resourceModel, resourceModel)
|
||||
|
||||
switch (resourceModel).(type) {
|
||||
case models.Encounter:
|
||||
var apiEncounter models.Encounter
|
||||
apiEncounter = resourceModel.(models.Encounter)
|
||||
if sr.gormClient.Debug().WithContext(ctx).Model(&apiEncounter).
|
||||
if sr.gormClient.Debug().WithContext(ctx).Model(&resourceModel).
|
||||
Where(models.OriginBase{
|
||||
SourceID: apiEncounter.GetSourceID(),
|
||||
SourceResourceID: apiEncounter.GetSourceResourceID(),
|
||||
SourceResourceType: apiEncounter.GetSourceResourceType(), //TODO: and UpdatedAt > old UpdatedAt
|
||||
}).Updates(&apiEncounter).RowsAffected == 0 {
|
||||
sr.logger.Infof("organization does not exist, creating: %s %s %s", apiEncounter.GetSourceID(), apiEncounter.GetSourceResourceID(), apiEncounter.GetSourceResourceType())
|
||||
return sr.gormClient.Debug().Model(&apiEncounter).Create(&apiEncounter).Error
|
||||
}
|
||||
case models.Organization:
|
||||
var apiOrganization models.Organization
|
||||
apiOrganization = (resourceModel).(models.Organization)
|
||||
if sr.gormClient.Debug().WithContext(ctx).Model(&apiOrganization).
|
||||
Where(models.OriginBase{
|
||||
SourceID: apiOrganization.GetSourceID(),
|
||||
SourceResourceID: apiOrganization.GetSourceResourceID(),
|
||||
SourceResourceType: apiOrganization.GetSourceResourceType(), //TODO: and UpdatedAt > old UpdatedAt
|
||||
}).Updates(&apiOrganization).RowsAffected == 0 {
|
||||
sr.logger.Infof("organization does not exist, creating: %s %s %s", apiOrganization.GetSourceID(), apiOrganization.GetSourceResourceID(), apiOrganization.GetSourceResourceType())
|
||||
return sr.gormClient.Debug().Model(&apiOrganization).Create(&apiOrganization).Error
|
||||
}
|
||||
case models.Profile:
|
||||
var apiProfile models.Profile
|
||||
apiProfile = (resourceModel).(models.Profile)
|
||||
if sr.gormClient.Debug().WithContext(ctx).Model(&apiProfile).
|
||||
Where(models.OriginBase{
|
||||
SourceID: apiProfile.GetSourceID(),
|
||||
SourceResourceID: apiProfile.GetSourceResourceID(),
|
||||
SourceResourceType: apiProfile.GetSourceResourceType(), //TODO: and UpdatedAt > old UpdatedAt
|
||||
}).Updates(&apiProfile).RowsAffected == 0 {
|
||||
sr.logger.Infof("profile does not exist, creating: %s %s %s", apiProfile.GetSourceID(), apiProfile.GetSourceResourceID(), apiProfile.GetSourceResourceType())
|
||||
return sr.gormClient.Debug().Model(&apiProfile).Create(&apiProfile).Error
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown model (%T) %v", resourceModel, resourceModel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sr *sqliteRepository) UpsertOrganziation(ctx context.Context, org *models.Organization) error {
|
||||
if sr.gormClient.Debug().WithContext(ctx).Model(org).
|
||||
Where(models.OriginBase{
|
||||
SourceID: org.GetSourceID(),
|
||||
SourceResourceID: org.GetSourceResourceID(),
|
||||
SourceResourceType: org.GetSourceResourceType(), //TODO: and UpdatedAt > old UpdatedAt
|
||||
}).Updates(org).RowsAffected == 0 {
|
||||
sr.logger.Infof("org does not exist, creating: %s %s %s", org.GetSourceID(), org.GetSourceResourceID(), org.GetSourceResourceType())
|
||||
return sr.gormClient.Debug().Create(org).Error
|
||||
SourceID: resourceModel.GetSourceID(),
|
||||
SourceResourceID: resourceModel.GetSourceResourceID(),
|
||||
SourceResourceType: resourceModel.GetSourceResourceType(), //TODO: and UpdatedAt > old UpdatedAt
|
||||
}).Updates(&resourceModel).RowsAffected == 0 {
|
||||
sr.logger.Infof("resource does not exist, creating: %s %s %s", resourceModel.GetSourceID(), resourceModel.GetSourceResourceID(), resourceModel.GetSourceResourceType())
|
||||
return sr.gormClient.Debug().Model(&resourceModel).Create(&resourceModel).Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
package base
|
||||
|
||||
import "github.com/samber/lo"
|
||||
|
||||
//based on algorithm - https://stackoverflow.com/a/5288547
|
||||
type DependencyGraph map[string][]string
|
||||
|
||||
func (d DependencyGraph) AddDependencies(resourceRef string, dependencyResourceRefs []string) {
|
||||
//check to see if the resourceRef already has an entry in the graph, if not, add it
|
||||
|
||||
if _, dependencyListExists := d[resourceRef]; !dependencyListExists {
|
||||
//the dependency list doesnt exist yet for this ref
|
||||
d[resourceRef] = []string{}
|
||||
}
|
||||
|
||||
for _, dependencyResourceRef := range dependencyResourceRefs {
|
||||
dependencyList, dependencyListExists := d[dependencyResourceRef]
|
||||
if !dependencyListExists {
|
||||
//the dependency list doesnt exist yet for this ref
|
||||
dependencyList = []string{}
|
||||
}
|
||||
|
||||
//add the current item to the list, then make sure the list is unique.
|
||||
dependencyList = append(dependencyList, resourceRef)
|
||||
uniqDependencyList := lo.Uniq[string](dependencyList)
|
||||
d[dependencyResourceRef] = uniqDependencyList
|
||||
}
|
||||
}
|
|
@ -6,9 +6,10 @@ import (
|
|||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/fastenhealth/gofhir-models/fhir401"
|
||||
fhirutils "github.com/fastenhealth/gofhir-models/fhir401/utils"
|
||||
"github.com/samber/lo"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/datatypes"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FHIR401Client struct {
|
||||
|
@ -40,196 +41,100 @@ func (c *FHIR401Client) GetPatient(patientId string) (fhir401.Patient, error) {
|
|||
return patient, err
|
||||
}
|
||||
|
||||
// GenerateResourceDependencyGraph
|
||||
// FHIR resources can reference/depend on other resources.
|
||||
// When storing processed models in the database, we need to make sure that we insert them in dependency order,
|
||||
// so that we can correctly update all references
|
||||
func (c *FHIR401Client) GenerateResourceDependencyGraph() {
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Process Bundles
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
func (c *FHIR401Client) ProcessBundle(bundle fhir401.Bundle) (DependencyGraph, map[string]interface{}, []string, error) {
|
||||
// this lookup dict maps resource references to API models
|
||||
resourceRefApiModelLookup := map[string]interface{}{}
|
||||
func (c *FHIR401Client) ProcessBundle(bundle fhir401.Bundle) ([]models.ResourceFhir, error) {
|
||||
|
||||
// this map contains resource references, and a list of other resources that depend on it.
|
||||
resourceRefDependencyGraph := DependencyGraph{}
|
||||
//process each entry in bundle
|
||||
wrappedResourceModels := lo.FilterMap[fhir401.BundleEntry, models.ResourceFhir](bundle.Entry, func(bundleEntry fhir401.BundleEntry, _ int) (models.ResourceFhir, bool) {
|
||||
originalResource, _ := fhirutils.MapToResource(bundleEntry.Resource, false)
|
||||
|
||||
//list of all resources
|
||||
allResources := []interface{}{}
|
||||
resourceType, resourceId := originalResource.(ResourceInterface).ResourceRef()
|
||||
|
||||
skippedResources := []string{}
|
||||
for _, bundleEntry := range bundle.Entry {
|
||||
resource, _ := fhirutils.MapToResource(bundleEntry.Resource, false)
|
||||
allResources = append(allResources, resource)
|
||||
// TODO find a way to safely/consistently get the resource updated date (and other metadata) which shoudl be added to the model.
|
||||
//if originalResource.Meta != nil && originalResource.Meta.LastUpdated != nil {
|
||||
// if parsed, err := time.Parse(time.RFC3339Nano, *originalResource.Meta.LastUpdated); err == nil {
|
||||
// patientProfile.UpdatedAt = parsed
|
||||
// }
|
||||
//}
|
||||
if resourceId == nil {
|
||||
//no resourceId present for this resource, we'll ignore it.
|
||||
return models.ResourceFhir{}, false
|
||||
}
|
||||
|
||||
switch resource.(type) {
|
||||
case fhir401.Patient:
|
||||
typedResource := resource.(fhir401.Patient)
|
||||
apiProfile, err := c.ProcessPatient(typedResource)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
wrappedResourceModel := models.ResourceFhir{
|
||||
OriginBase: models.OriginBase{
|
||||
ModelBase: models.ModelBase{},
|
||||
UserID: c.Source.UserID,
|
||||
SourceID: c.Source.ID,
|
||||
SourceResourceID: *resourceId,
|
||||
SourceResourceType: resourceType,
|
||||
},
|
||||
Payload: datatypes.JSON(bundleEntry.Resource),
|
||||
}
|
||||
resourceType, resourceId := typedResource.ResourceRef()
|
||||
resourceRef := fmt.Sprintf("%s/%s", resourceType, *resourceId)
|
||||
resourceRefApiModelLookup[resourceRef] = apiProfile
|
||||
resourceRefDependencyGraph.AddDependencies(resourceRef, []string{})
|
||||
case fhir401.Organization:
|
||||
typedResource := resource.(fhir401.Organization)
|
||||
apiOrganization, err := c.ProcessOrganization(typedResource)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
resourceType, resourceId := typedResource.ResourceRef()
|
||||
resourceRef := fmt.Sprintf("%s/%s", resourceType, *resourceId)
|
||||
resourceRefApiModelLookup[resourceRef] = apiOrganization
|
||||
resourceRefDependencyGraph.AddDependencies(resourceRef, []string{})
|
||||
case fhir401.Encounter:
|
||||
typedResource := resource.(fhir401.Encounter)
|
||||
apiEncounter, err := c.ProcessEncounter(typedResource)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
resourceType, resourceId := typedResource.ResourceRef()
|
||||
resourceRef := fmt.Sprintf("%s/%s", resourceType, *resourceId)
|
||||
resourceRefApiModelLookup[resourceRef] = apiEncounter
|
||||
resourceRefDependencyGraph.AddDependencies(resourceRef, []string{})
|
||||
default:
|
||||
typedResource := resource.(ResourceInterface)
|
||||
resourceType, resourceId := typedResource.ResourceRef()
|
||||
var resourceRef string
|
||||
if resourceId != nil {
|
||||
resourceRef = fmt.Sprintf("%s/%s", resourceType, *resourceId)
|
||||
} else {
|
||||
resourceRef = resourceType
|
||||
}
|
||||
skippedResources = append(skippedResources, resourceRef)
|
||||
}
|
||||
}
|
||||
return resourceRefDependencyGraph, resourceRefApiModelLookup, skippedResources, nil
|
||||
|
||||
return wrappedResourceModel, true
|
||||
})
|
||||
return wrappedResourceModels, nil
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Process & Generate API/Database Models
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
func (c *FHIR401Client) ProcessPatient(item fhir401.Patient) (models.Profile, error) {
|
||||
c.Logger.Debugf("item %v", item)
|
||||
patientProfile := models.Profile{
|
||||
OriginBase: models.OriginBase{
|
||||
ModelBase: models.ModelBase{},
|
||||
UserID: c.Source.UserID,
|
||||
SourceID: c.Source.ID,
|
||||
SourceResourceID: *item.Id,
|
||||
SourceResourceType: fhir401.ResourceTypePatient.Code(),
|
||||
},
|
||||
Demographics: models.Demographics{
|
||||
Address: models.Address{},
|
||||
Name: models.Name{},
|
||||
},
|
||||
}
|
||||
|
||||
if item.Meta != nil && item.Meta.LastUpdated != nil {
|
||||
if parsed, err := time.Parse(time.RFC3339Nano, *item.Meta.LastUpdated); err == nil {
|
||||
patientProfile.UpdatedAt = parsed
|
||||
}
|
||||
}
|
||||
|
||||
if item.Address != nil && len(item.Address) > 0 {
|
||||
itemAddress := item.Address[0]
|
||||
patientProfile.Demographics.Address.City = itemAddress.City
|
||||
patientProfile.Demographics.Address.Country = itemAddress.Country
|
||||
patientProfile.Demographics.Address.State = itemAddress.State
|
||||
patientProfile.Demographics.Address.Street = itemAddress.Line
|
||||
patientProfile.Demographics.Address.Zip = itemAddress.PostalCode
|
||||
|
||||
}
|
||||
patientProfile.Demographics.Dob = item.BirthDate
|
||||
|
||||
if item.Gender != nil {
|
||||
itemGenderStr := item.Gender.String()
|
||||
itemGenderCode := item.Gender.Code()
|
||||
patientProfile.Demographics.Gender = &itemGenderStr
|
||||
patientProfile.Demographics.GenderCodes = &itemGenderCode
|
||||
}
|
||||
patientProfile.Demographics.Language = item.Language
|
||||
|
||||
if item.MaritalStatus != nil {
|
||||
patientProfile.Demographics.MaritalStatus = item.MaritalStatus.Text
|
||||
if len(item.MaritalStatus.Coding) > 0 {
|
||||
patientProfile.Demographics.MaritalStatusCodes = item.MaritalStatus.Coding[0].Code
|
||||
}
|
||||
}
|
||||
if item.Name != nil && len(item.Name) > 0 {
|
||||
itemName := item.Name[0]
|
||||
if itemName.Prefix != nil && len(itemName.Prefix) > 0 {
|
||||
itemNamePrefix := itemName.Prefix[0]
|
||||
patientProfile.Demographics.Name.Prefix = &itemNamePrefix
|
||||
}
|
||||
patientProfile.Demographics.Name.Given = itemName.Given
|
||||
patientProfile.Demographics.Name.Family = itemName.Family
|
||||
|
||||
}
|
||||
|
||||
return patientProfile, nil
|
||||
}
|
||||
|
||||
func (c *FHIR401Client) ProcessOrganization(item fhir401.Organization) (models.Organization, error) {
|
||||
apiOrganization := models.Organization{
|
||||
OriginBase: models.OriginBase{
|
||||
ModelBase: models.ModelBase{},
|
||||
UserID: c.Source.UserID,
|
||||
SourceID: c.Source.ID,
|
||||
SourceResourceID: *item.Id,
|
||||
SourceResourceType: fhir401.ResourceTypeOrganization.Code(),
|
||||
},
|
||||
Address: models.Address{},
|
||||
}
|
||||
|
||||
if item.Meta != nil && item.Meta.LastUpdated != nil {
|
||||
if parsed, err := time.Parse(time.RFC3339, *item.Meta.LastUpdated); err == nil {
|
||||
apiOrganization.UpdatedAt = parsed
|
||||
}
|
||||
}
|
||||
|
||||
if item.Address != nil && len(item.Address) > 0 {
|
||||
itemAddress := item.Address[0]
|
||||
apiOrganization.Address.City = itemAddress.City
|
||||
apiOrganization.Address.Country = itemAddress.Country
|
||||
apiOrganization.Address.State = itemAddress.State
|
||||
apiOrganization.Address.Street = itemAddress.Line
|
||||
apiOrganization.Address.Zip = itemAddress.PostalCode
|
||||
}
|
||||
apiOrganization.Name = item.Name
|
||||
apiOrganization.Active = item.Active
|
||||
|
||||
return apiOrganization, nil
|
||||
}
|
||||
|
||||
//TODO
|
||||
func (c *FHIR401Client) ProcessEncounter(item fhir401.Encounter) (models.Encounter, error) {
|
||||
apiEncounter := models.Encounter{
|
||||
OriginBase: models.OriginBase{
|
||||
ModelBase: models.ModelBase{},
|
||||
UserID: c.Source.UserID,
|
||||
SourceID: c.Source.ID,
|
||||
SourceResourceID: *item.Id,
|
||||
SourceResourceType: fhir401.ResourceTypeEncounter.Code(),
|
||||
},
|
||||
Provider: models.Provider{},
|
||||
Orders: []models.Order{},
|
||||
}
|
||||
|
||||
if item.Meta != nil && item.Meta.LastUpdated != nil {
|
||||
if parsed, err := time.Parse(time.RFC3339, *item.Meta.LastUpdated); err == nil {
|
||||
apiEncounter.UpdatedAt = parsed
|
||||
}
|
||||
}
|
||||
if item.Type != nil && len(item.Type) > 0 && item.Type[0].Coding != nil && len(item.Type[0].Coding) > 0 {
|
||||
apiEncounter.VisitType = item.Type[0].Coding[0].Code
|
||||
}
|
||||
|
||||
return apiEncounter, nil
|
||||
}
|
||||
//func (c *FHIR401Client) ProcessPatient(item fhir401.Patient) (models.Profile, error) {
|
||||
// c.Logger.Debugf("item %v", item)
|
||||
// patientProfile := models.Profile{
|
||||
// OriginBase: models.OriginBase{
|
||||
// ModelBase: models.ModelBase{},
|
||||
// UserID: c.Source.UserID,
|
||||
// SourceID: c.Source.ID,
|
||||
// SourceResourceID: *item.Id,
|
||||
// SourceResourceType: fhir401.ResourceTypePatient.Code(),
|
||||
// },
|
||||
// Demographics: models.Demographics{
|
||||
// Address: models.Address{},
|
||||
// Name: models.Name{},
|
||||
// },
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
// if item.Address != nil && len(item.Address) > 0 {
|
||||
// itemAddress := item.Address[0]
|
||||
// patientProfile.Demographics.Address.City = itemAddress.City
|
||||
// patientProfile.Demographics.Address.Country = itemAddress.Country
|
||||
// patientProfile.Demographics.Address.State = itemAddress.State
|
||||
// patientProfile.Demographics.Address.Street = itemAddress.Line
|
||||
// patientProfile.Demographics.Address.Zip = itemAddress.PostalCode
|
||||
//
|
||||
// }
|
||||
// patientProfile.Demographics.Dob = item.BirthDate
|
||||
//
|
||||
// if item.Gender != nil {
|
||||
// itemGenderStr := item.Gender.String()
|
||||
// itemGenderCode := item.Gender.Code()
|
||||
// patientProfile.Demographics.Gender = &itemGenderStr
|
||||
// patientProfile.Demographics.GenderCodes = &itemGenderCode
|
||||
// }
|
||||
// patientProfile.Demographics.Language = item.Language
|
||||
//
|
||||
// if item.MaritalStatus != nil {
|
||||
// patientProfile.Demographics.MaritalStatus = item.MaritalStatus.Text
|
||||
// if len(item.MaritalStatus.Coding) > 0 {
|
||||
// patientProfile.Demographics.MaritalStatusCodes = item.MaritalStatus.Coding[0].Code
|
||||
// }
|
||||
// }
|
||||
// if item.Name != nil && len(item.Name) > 0 {
|
||||
// itemName := item.Name[0]
|
||||
// if itemName.Prefix != nil && len(itemName.Prefix) > 0 {
|
||||
// itemNamePrefix := itemName.Prefix[0]
|
||||
// patientProfile.Demographics.Name.Prefix = &itemNamePrefix
|
||||
// }
|
||||
// patientProfile.Demographics.Name.Given = itemName.Given
|
||||
// patientProfile.Demographics.Name.Family = itemName.Family
|
||||
//
|
||||
// }
|
||||
//
|
||||
// return patientProfile, nil
|
||||
//}
|
||||
|
|
|
@ -9,10 +9,8 @@ import (
|
|||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/require"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// helpers
|
||||
|
@ -70,157 +68,11 @@ func TestFHIR401Client_ProcessBundle(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
// test
|
||||
dependencyGraph, resoureceApiMap, skipped, err := client.ProcessBundle(bundle)
|
||||
log.Printf("%v", dependencyGraph)
|
||||
log.Printf("%v", resoureceApiMap)
|
||||
wrappedResourceModels, err := client.ProcessBundle(bundle)
|
||||
//log.Printf("%v", wrappedResourceModels)
|
||||
|
||||
//assert
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 8, len(skipped))
|
||||
require.Equal(t, 4, len(resoureceApiMap))
|
||||
require.Equal(t, 11, len(wrappedResourceModels))
|
||||
//require.Equal(t, "A00000000000005", profile.SourceResourceID)
|
||||
}
|
||||
|
||||
func TestFHIR401Client_ProcessPatient_Cigna_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
//setup
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||
testLogger := logrus.WithFields(logrus.Fields{
|
||||
"type": "test",
|
||||
})
|
||||
client, err := NewFHIR401Client(fakeConfig, testLogger, models.Source{
|
||||
RefreshToken: "test-refresh-token",
|
||||
AccessToken: "test-access-token",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
jsonBytes, err := readTestFixture("testdata/fixtures/401-R4/patient/cigna_syntheticuser05-patient-A00000000000005.json")
|
||||
require.NoError(t, err)
|
||||
var patient fhir401.Patient
|
||||
err = json.Unmarshal(jsonBytes, &patient)
|
||||
require.NoError(t, err)
|
||||
|
||||
// test
|
||||
profile, err := client.ProcessPatient(patient)
|
||||
|
||||
//assert
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "Patient", profile.SourceResourceType)
|
||||
require.Equal(t, "A00000000000005", profile.SourceResourceID)
|
||||
}
|
||||
|
||||
func TestFHIR401Client_ProcessPatient_Cigna_Populated(t *testing.T) {
|
||||
t.Parallel()
|
||||
//setup
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||
testLogger := logrus.WithFields(logrus.Fields{
|
||||
"type": "test",
|
||||
})
|
||||
client, err := NewFHIR401Client(fakeConfig, testLogger, models.Source{
|
||||
RefreshToken: "test-refresh-token",
|
||||
AccessToken: "test-access-token",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
jsonBytes, err := readTestFixture("testdata/fixtures/401-R4/patient/cigna_syntheticuser05-patient-ifp-A00000000000005.json")
|
||||
require.NoError(t, err)
|
||||
var patient fhir401.Patient
|
||||
err = json.Unmarshal(jsonBytes, &patient)
|
||||
require.NoError(t, err)
|
||||
|
||||
// test
|
||||
profile, err := client.ProcessPatient(patient)
|
||||
|
||||
//assert
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "Patient", profile.SourceResourceType)
|
||||
require.Equal(t, "ifp-A00000000000005", profile.SourceResourceID)
|
||||
require.Equal(t, "2022-06-20T15:45:22.043Z", profile.UpdatedAt.Format(time.RFC3339Nano))
|
||||
require.Equal(t, "2013-01-12", *profile.Demographics.Dob)
|
||||
require.Equal(t, "female", *profile.Demographics.Gender)
|
||||
require.Equal(t, "female", *profile.Demographics.GenderCodes)
|
||||
require.Equal(t, "UNK", *profile.Demographics.MaritalStatusCodes)
|
||||
require.Equal(t, "unknown", *profile.Demographics.MaritalStatus)
|
||||
require.Equal(t, "Monahan", *profile.Demographics.Name.Family)
|
||||
require.Equal(t, []string{"Felecita"}, profile.Demographics.Name.Given)
|
||||
}
|
||||
|
||||
func TestFHIR401Client_ProcessPatients_Synthea_Populated(t *testing.T) {
|
||||
t.Parallel()
|
||||
//setup
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||
testLogger := logrus.WithFields(logrus.Fields{
|
||||
"type": "test",
|
||||
})
|
||||
client, err := NewFHIR401Client(fakeConfig, testLogger, models.Source{
|
||||
RefreshToken: "test-refresh-token",
|
||||
AccessToken: "test-access-token",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
jsonBytes, err := readTestFixture("testdata/fixtures/401-R4/patient/synthea_Alesha810_Marks830_1e0a8bd3-3b82-4f17-b1d6-19043aa0db6b.json")
|
||||
require.NoError(t, err)
|
||||
var patient fhir401.Patient
|
||||
err = json.Unmarshal(jsonBytes, &patient)
|
||||
require.NoError(t, err)
|
||||
|
||||
// test
|
||||
profile, err := client.ProcessPatient(patient)
|
||||
|
||||
//assert
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "Patient", profile.SourceResourceType)
|
||||
require.Equal(t, "c088b7af-fc41-43cc-ab80-4a9ab8d47cd9", profile.SourceResourceID)
|
||||
require.Equal(t, "0001-01-01T00:00:00Z", profile.UpdatedAt.Format(time.RFC3339Nano))
|
||||
require.Equal(t, "1965-11-04", *profile.Demographics.Dob)
|
||||
require.Equal(t, "female", *profile.Demographics.Gender)
|
||||
require.Equal(t, "female", *profile.Demographics.GenderCodes)
|
||||
require.Equal(t, "S", *profile.Demographics.MaritalStatusCodes)
|
||||
require.Equal(t, "S", *profile.Demographics.MaritalStatus)
|
||||
require.Equal(t, "Marks830", *profile.Demographics.Name.Family)
|
||||
require.Equal(t, []string{"Alesha810"}, profile.Demographics.Name.Given)
|
||||
require.Equal(t, "Ms.", *profile.Demographics.Name.Prefix)
|
||||
}
|
||||
|
||||
func TestFHIR401Client_ProcessOrganizations_Cigna(t *testing.T) {
|
||||
t.Parallel()
|
||||
//setup
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||
testLogger := logrus.WithFields(logrus.Fields{
|
||||
"type": "test",
|
||||
})
|
||||
client, err := NewFHIR401Client(fakeConfig, testLogger, models.Source{
|
||||
RefreshToken: "test-refresh-token",
|
||||
AccessToken: "test-access-token",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
jsonBytes, err := readTestFixture("testdata/fixtures/401-R4/organization/cigna_syntheticuser05-organziation-ifp-51fb06f37e5ec973ce69132a9a2571f3.json")
|
||||
require.NoError(t, err)
|
||||
var org fhir401.Organization
|
||||
err = json.Unmarshal(jsonBytes, &org)
|
||||
require.NoError(t, err)
|
||||
|
||||
// test
|
||||
apiOrg, err := client.ProcessOrganization(org)
|
||||
|
||||
//assert
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "Organization", apiOrg.SourceResourceType)
|
||||
require.Equal(t, "ifp-51fb06f37e5ec973ce69132a9a2571f3", apiOrg.SourceResourceID)
|
||||
require.Equal(t, "2022-06-20T15:45:45.155Z", apiOrg.UpdatedAt.Format(time.RFC3339Nano))
|
||||
require.Equal(t, true, *apiOrg.Active)
|
||||
require.Equal(t, "SURPRISE", *apiOrg.Address.City)
|
||||
require.Equal(t, "AZ", *apiOrg.Address.State)
|
||||
require.Equal(t, []string{"13991 W GRAND AVE STE 105"}, apiOrg.Address.Street)
|
||||
require.Nil(t, apiOrg.Address.Country)
|
||||
require.Equal(t, "85374", *apiOrg.Address.Zip)
|
||||
require.Equal(t, "CIGNA MED GRP PHCY-SUN CITY WE", *apiOrg.Name)
|
||||
}
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
{
|
||||
"id": "ifp-1C9A46F07B3CADE33EFC75ABA3DC37CF",
|
||||
"meta": {
|
||||
"lastUpdated": "2022-06-20T15:45:29.685000+00:00",
|
||||
"profile": ["http://hl7.org/fhir/us/core/StructureDefinition/us-core-encounter"],
|
||||
"source": "IFP#e7zkeLCSmQFgUX0Z",
|
||||
"versionId": "1"
|
||||
},
|
||||
"class": {
|
||||
"extension": [{
|
||||
"url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason",
|
||||
"valueCode": "unknown"
|
||||
}]
|
||||
},
|
||||
"diagnosis": [{
|
||||
"condition": {
|
||||
"reference": "Condition/ifp-A00000000000005-OP0447575426-K57.80-Y-"
|
||||
},
|
||||
"use": {
|
||||
"coding": [{
|
||||
"display": "Admission diagnosis"
|
||||
}]
|
||||
}
|
||||
}],
|
||||
"identifier": [{
|
||||
"system": "http://dev.cigna.com/system/TRCR",
|
||||
"value": "TRCR-OP0447575426-1"
|
||||
}],
|
||||
"location": [{
|
||||
"location": {
|
||||
"reference": "Location/ifp-7312532"
|
||||
}
|
||||
}, {
|
||||
"location": {
|
||||
"reference": "Location/ifp-0566265"
|
||||
}
|
||||
}],
|
||||
"participant": [{
|
||||
"individual": {
|
||||
"reference": "Practitioner/ifp-50ef4542ecb5ae5eddc0dac76a10aaed"
|
||||
}
|
||||
}, {
|
||||
"individual": {
|
||||
"reference": "Practitioner/ifp-6a4d3d6fe165b6a0a79b6a976d043c2c"
|
||||
}
|
||||
}],
|
||||
"period": {
|
||||
"end": "2019-12-31",
|
||||
"start": "2019-11-07"
|
||||
},
|
||||
"reasonCode": [{
|
||||
"coding": [{
|
||||
"code": "K57.80",
|
||||
"display": "Dvtrcli of intest, part unsp, w perf and abscess w/o bleed"
|
||||
}]
|
||||
}],
|
||||
"reasonReference": [{
|
||||
"reference": "Condition/ifp-A00000000000005-OP0447575426-K57.80-Y-"
|
||||
}],
|
||||
"serviceProvider": {
|
||||
"reference": "Organization/ifp-0566265"
|
||||
},
|
||||
"serviceType": {
|
||||
"coding": [{
|
||||
"code": "302",
|
||||
"display": "Medical Outpatient"
|
||||
}]
|
||||
},
|
||||
"status": "unknown",
|
||||
"subject": {
|
||||
"reference": "Patient/ifp-A00000000000005"
|
||||
},
|
||||
"type": [{
|
||||
"extension": [{
|
||||
"url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason",
|
||||
"valueCode": "unknown"
|
||||
}]
|
||||
}],
|
||||
"resourceType": "Encounter"
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
{
|
||||
"resourceType": "Encounter",
|
||||
"id": "3476a881-bb6b-4670-b0d9-aa3eb12be267",
|
||||
"status": "finished",
|
||||
"class": {
|
||||
"system": "http://terminology.hl7.org/CodeSystem/v3-ActCode",
|
||||
"code": "AMB"
|
||||
},
|
||||
"type": [
|
||||
{
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://snomed.info/sct",
|
||||
"code": "162673000",
|
||||
"display": "General examination of patient (procedure)"
|
||||
}
|
||||
],
|
||||
"text": "General examination of patient (procedure)"
|
||||
}
|
||||
],
|
||||
"subject": {
|
||||
"reference": "urn:uuid:d7714835-c7e2-408f-9652-c8c2bdd9d2bf",
|
||||
"display": "Mr. Blair400 Grady603"
|
||||
},
|
||||
"participant": [
|
||||
{
|
||||
"individual": {
|
||||
"reference": "urn:uuid:0000016d-3a85-4cca-0000-0000000016d0",
|
||||
"display": "Dr. María Soledad68 Marrero674"
|
||||
}
|
||||
}
|
||||
],
|
||||
"period": {
|
||||
"start": "2011-08-10T08:19:16-04:00",
|
||||
"end": "2011-08-10T08:34:16-04:00"
|
||||
},
|
||||
"serviceProvider": {
|
||||
"reference": "urn:uuid:a0123c36-2436-3609-a5eb-3c3857ed711d",
|
||||
"display": "PCP8367"
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
{
|
||||
"id": "ifp-51fb06f37e5ec973ce69132a9a2571f3",
|
||||
"meta": {
|
||||
"lastUpdated": "2022-06-20T15:45:45.155000+00:00",
|
||||
"profile": ["http://hl7.org/fhir/us/carin-bb/StructureDefinition/C4BB-Organization"],
|
||||
"source": "IFP#1Hc7lo3uMBmGNP1z",
|
||||
"versionId": "1"
|
||||
},
|
||||
"active": true,
|
||||
"address": [{
|
||||
"city": "SURPRISE",
|
||||
"line": ["13991 W GRAND AVE STE 105"],
|
||||
"postalCode": "85374",
|
||||
"state": "AZ",
|
||||
"text": "13991 W GRAND AVE STE 105 SURPRISE AZ 85374"
|
||||
}],
|
||||
"identifier": [{
|
||||
"system": "http://hl7.org/fhir/sid/us-npi",
|
||||
"type": {
|
||||
"coding": [{
|
||||
"code": "npi",
|
||||
"system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/IdentifierTypeCS"
|
||||
}]
|
||||
},
|
||||
"value": "1609868678"
|
||||
}, {
|
||||
"system": "https://developer.cigna.com",
|
||||
"type": {
|
||||
"coding": [{
|
||||
"code": "provid",
|
||||
"system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/IdentifierTypeCS"
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"system": "urn:oid:2.16.840.1.113883.4.4",
|
||||
"type": {
|
||||
"coding": [{
|
||||
"code": "TAX",
|
||||
"system": "http://terminology.hl7.org/CodeSystem/v2-0203"
|
||||
}]
|
||||
}
|
||||
}],
|
||||
"name": "CIGNA MED GRP PHCY-SUN CITY WE",
|
||||
"resourceType": "Organization"
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"id": "A00000000000005",
|
||||
"link": [{
|
||||
"other": {
|
||||
"reference": "Patient/ifp-A00000000000005"
|
||||
},
|
||||
"type": "seealso"
|
||||
}, {
|
||||
"other": {
|
||||
"reference": "Patient/com-44a04ebb-7aba-4e66-99c7-12b99405f30d"
|
||||
},
|
||||
"type": "seealso"
|
||||
}],
|
||||
"resourceType": "Patient"
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
{
|
||||
"id": "ifp-A00000000000005",
|
||||
"meta": {
|
||||
"lastUpdated": "2022-06-20T15:45:22.043000+00:00",
|
||||
"profile": ["http://hl7.org/fhir/us/carin-bb/StructureDefinition/C4BB-Patient"],
|
||||
"source": "IFP#CqP06ARvJo9XS9Cl",
|
||||
"versionId": "1"
|
||||
},
|
||||
"text": {
|
||||
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><div class=\"hapiHeaderText\">Felecita <b>MONAHAN </b></div><table class=\"hapiPropertyTable\"><tbody><tr><td>Identifier</td><td>A00000000000005</td></tr><tr><td>Date of birth</td><td><span>12 January 2013</span></td></tr></tbody></table></div>",
|
||||
"status": "generated"
|
||||
},
|
||||
"birthDate": "2013-01-12",
|
||||
"gender": "female",
|
||||
"identifier": [{
|
||||
"system": "https://developer.cigna.com",
|
||||
"type": {
|
||||
"coding": [{
|
||||
"code": "um",
|
||||
"system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/IdentifierTypeCS"
|
||||
}]
|
||||
},
|
||||
"value": "A00000000000005"
|
||||
}, {
|
||||
"system": "https://developer.cigna.com",
|
||||
"type": {
|
||||
"coding": [{
|
||||
"code": "mb",
|
||||
"system": "http://hl7.org/fhir/us/carin-bb/CodeSystem/IdentifierTypeCS"
|
||||
}]
|
||||
},
|
||||
"value": "unknown"
|
||||
}],
|
||||
"maritalStatus": {
|
||||
"coding": [{
|
||||
"code": "UNK",
|
||||
"display": "unknown",
|
||||
"system": "http://terminology.hl7.org/CodeSystem/v3-NullFlavor"
|
||||
}],
|
||||
"text": "unknown"
|
||||
},
|
||||
"name": [{
|
||||
"family": "Monahan",
|
||||
"given": ["Felecita"],
|
||||
"use": "official"
|
||||
}],
|
||||
"telecom": [{
|
||||
"system": "phone",
|
||||
"use": "mobile",
|
||||
"value": "9404535496"
|
||||
}],
|
||||
"resourceType": "Patient"
|
||||
}
|
|
@ -1,201 +0,0 @@
|
|||
{
|
||||
"resourceType": "Patient",
|
||||
"id": "c088b7af-fc41-43cc-ab80-4a9ab8d47cd9",
|
||||
"text": {
|
||||
"status": "generated",
|
||||
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">Generated by <a href=\"https://github.com/synthetichealth/synthea\">Synthea</a>.Version identifier: v2.4.0-404-ge7ce2295\n . Person seed: -2282623849526784568 Population seed: 0</div>"
|
||||
},
|
||||
"extension": [
|
||||
{
|
||||
"url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race",
|
||||
"extension": [
|
||||
{
|
||||
"url": "ombCategory",
|
||||
"valueCoding": {
|
||||
"system": "urn:oid:2.16.840.1.113883.6.238",
|
||||
"code": "2106-3",
|
||||
"display": "White"
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "text",
|
||||
"valueString": "White"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity",
|
||||
"extension": [
|
||||
{
|
||||
"url": "ombCategory",
|
||||
"valueCoding": {
|
||||
"system": "urn:oid:2.16.840.1.113883.6.238",
|
||||
"code": "2186-5",
|
||||
"display": "Not Hispanic or Latino"
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "text",
|
||||
"valueString": "Not Hispanic or Latino"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName",
|
||||
"valueString": "Sandra485 Cormier289"
|
||||
},
|
||||
{
|
||||
"url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-birthsex",
|
||||
"valueCode": "F"
|
||||
},
|
||||
{
|
||||
"url": "http://hl7.org/fhir/StructureDefinition/patient-birthPlace",
|
||||
"valueAddress": {
|
||||
"city": "Tewksbury",
|
||||
"state": "Massachusetts",
|
||||
"country": "US"
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "http://synthetichealth.github.io/synthea/disability-adjusted-life-years",
|
||||
"valueDecimal": 0.7742822202801629
|
||||
},
|
||||
{
|
||||
"url": "http://synthetichealth.github.io/synthea/quality-adjusted-life-years",
|
||||
"valueDecimal": 52.22571777971984
|
||||
}
|
||||
],
|
||||
"identifier": [
|
||||
{
|
||||
"system": "https://github.com/synthetichealth/synthea",
|
||||
"value": "1e0a8bd3-3b82-4f17-b1d6-19043aa0db6b"
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
||||
"code": "MR",
|
||||
"display": "Medical Record Number"
|
||||
}
|
||||
],
|
||||
"text": "Medical Record Number"
|
||||
},
|
||||
"system": "http://hospital.smarthealthit.org",
|
||||
"value": "1e0a8bd3-3b82-4f17-b1d6-19043aa0db6b"
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
||||
"code": "SS",
|
||||
"display": "Social Security Number"
|
||||
}
|
||||
],
|
||||
"text": "Social Security Number"
|
||||
},
|
||||
"system": "http://hl7.org/fhir/sid/us-ssn",
|
||||
"value": "999-76-3236"
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
||||
"code": "DL",
|
||||
"display": "Driver's License"
|
||||
}
|
||||
],
|
||||
"text": "Driver's License"
|
||||
},
|
||||
"system": "urn:oid:2.16.840.1.113883.4.3.25",
|
||||
"value": "S99995466"
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
||||
"code": "PPN",
|
||||
"display": "Passport Number"
|
||||
}
|
||||
],
|
||||
"text": "Passport Number"
|
||||
},
|
||||
"system": "http://standardhealthrecord.org/fhir/StructureDefinition/passportNumber",
|
||||
"value": "X41129973X"
|
||||
}
|
||||
],
|
||||
"name": [
|
||||
{
|
||||
"use": "official",
|
||||
"family": "Marks830",
|
||||
"given": [
|
||||
"Alesha810"
|
||||
],
|
||||
"prefix": [
|
||||
"Ms."
|
||||
]
|
||||
}
|
||||
],
|
||||
"telecom": [
|
||||
{
|
||||
"system": "phone",
|
||||
"value": "555-664-6121",
|
||||
"use": "home"
|
||||
}
|
||||
],
|
||||
"gender": "female",
|
||||
"birthDate": "1965-11-04",
|
||||
"address": [
|
||||
{
|
||||
"extension": [
|
||||
{
|
||||
"url": "http://hl7.org/fhir/StructureDefinition/geolocation",
|
||||
"extension": [
|
||||
{
|
||||
"url": "latitude",
|
||||
"valueDecimal": 42.60588776678466
|
||||
},
|
||||
{
|
||||
"url": "longitude",
|
||||
"valueDecimal": -71.0695322588603
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"line": [
|
||||
"165 Shanahan View"
|
||||
],
|
||||
"city": "North Reading",
|
||||
"state": "Massachusetts",
|
||||
"country": "US"
|
||||
}
|
||||
],
|
||||
"maritalStatus": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://terminology.hl7.org/CodeSystem/v3-MaritalStatus",
|
||||
"code": "S",
|
||||
"display": "S"
|
||||
}
|
||||
],
|
||||
"text": "S"
|
||||
},
|
||||
"multipleBirthBoolean": false,
|
||||
"communication": [
|
||||
{
|
||||
"language": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "urn:ietf:bcp:47",
|
||||
"code": "en-US",
|
||||
"display": "English"
|
||||
}
|
||||
],
|
||||
"text": "English"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -28,11 +28,11 @@ func (c CignaClient) SyncAll(db database.DatabaseRepository) error {
|
|||
return err
|
||||
}
|
||||
|
||||
_, resourceRefApiModelLookup, _, err := c.ProcessBundle(bundle)
|
||||
wrappedResourceModels, err := c.ProcessBundle(bundle)
|
||||
|
||||
//todo, create the resources in dependency order
|
||||
|
||||
for _, apiModel := range resourceRefApiModelLookup {
|
||||
for _, apiModel := range wrappedResourceModels {
|
||||
err = db.UpsertResource(context.Background(), apiModel)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
// https://reference.humanapi.co/reference/encounters
|
||||
type Encounter struct {
|
||||
OriginBase
|
||||
|
||||
DateTime time.Time `json:"time,omitempty"` //dateTime Date (optional) The date of the encounter
|
||||
VisitType *string `json:"visitType,omitempty"` // visitType String (optional) The type of visit
|
||||
Provider Provider `json:"provider,omitempty" gorm:"serializer:json;default:'{}'"` // provider Object (optional) The provider for the encounter (see provider object)
|
||||
//Prescriptions []Prescription `json:"prescriptions,omitempty"` // prescriptions Array[Object] (optional) A list of prescriptions provided during the encounter (see link object)
|
||||
//Diagnoses []Diagnosis `json:"diagnoses,omitempty"` // diagnoses Array[Object] (optional) A list of diagnoses for the encounter where object contains a "name" field e.g. *[{"name": "Sacroiliac dysfunction"}, {"name": "Bilateral hand pain"}]
|
||||
//vitals Object (optional) Vitals captured during the encounter (e.g. {"temperature" : 95.2 [degF]","weight" : 180 [lb_av]","height" : "69 [in_us]"})
|
||||
//vitalSigns Array[Object] (optional) A list of vital signs from the encounter (see link object)
|
||||
Reasons []string `json:"reasons,omitempty" gorm:"serializer:json;default:'[]'"` // reasons Array[String] (optional) A list of reasons for the encounter (e.g. [‘Follow-up’, 'Consult’, 'DYSPHONIA', 'Back Pain’])
|
||||
Orders []Order `json:"orders,omitempty" gorm:"serializer:json;default:'{}'"` // orders Array[Object] (optional) A list of medication orders for the patient (see orders object)
|
||||
//testResults Array[Object] (optional) A list of test results for the patient (see link object)
|
||||
//plansOfCare Array[Object] (optional) A list of plans of care from the encounter (see link object)
|
||||
//medications Array[Object] (optional) A list of medications used by the patient. Objects in array can have some or many of the properties of medications. Common properties are "name", "productName", "startDate", "endDate", "instructions".
|
||||
FollowUpInstructions *string `json:"followUpInstructions,omitempty"` // followUpInstructions String (optional) Follow-up instructions
|
||||
//Organization Organization `json:"organization,omitempty"` // organization Object (optional) (See organizations object)
|
||||
//codes Array[Object] (optional) (See codes)
|
||||
}
|
||||
|
||||
type Order struct {
|
||||
Name *string `json:"name,omitempty"` // name String (optional) The name of the order
|
||||
CodeType *string `json:"codeType,omitempty"` // codeType String (optional) The code type of the order (e.g. “CPT( R )”, “Custom”)
|
||||
ExpectedDate time.Time `json:"expectedDate,omitempty"` // expectedDate Date (optional) The date the order is expected
|
||||
ExpireDate time.Time `json:"expireDate,omitempty"` // expireDate Date (optional) The date the order expires
|
||||
ProcedureCode *string `json:"procedureCode,omitempty"` // procedureCode String (optional) The procedure code of the order
|
||||
OrderType *string `json:"type,omitempty"` // type String (optional) The type of the order (e.g. “Lab”, “Procedures”)
|
||||
//Name string `codes:"codes,omitempty"` // codes Array[Object] (optional) (See codes)
|
||||
}
|
||||
|
||||
type Provider struct {
|
||||
Name *string `json:"name,omitempty"` //name String (optional) Name of the provider
|
||||
DepartmentName *string `json:"departmentName,omitempty"` //departmentName String (optional) Name of the provider department
|
||||
Address *string `json:"address,omitempty"` //address String (optional) Address of the provider
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package models
|
||||
|
||||
type Organization struct {
|
||||
OriginBase
|
||||
|
||||
Name *string `json:"name,omitempty"` //name String (optional) The name of the organization
|
||||
Address Address `json:"address,omitempty" gorm:"serializer:json;default:'{}'"` //address String (optional) Address of the provider
|
||||
Active *bool `json:"active,omitempty"`
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package models
|
||||
|
||||
//demographics Object (optional) (See demographics)
|
||||
//alcohol Object (optional) The user’s alcohol usage. See alcohol object.
|
||||
//smoking Object (optional) The user’s smoking habits. See smoking object.
|
||||
|
||||
type Profile struct {
|
||||
OriginBase
|
||||
//embedded structs
|
||||
Demographics Demographics `json:"demographics" gorm:"serializer:json;default:'{}'"`
|
||||
}
|
||||
|
||||
type Demographics struct {
|
||||
Address Address `json:"address,omitempty"` // Object (See address object)
|
||||
Dob *string `json:"dob,omitempty"` //String (optional) The user’s date of birth e.g. "04/21/1965"
|
||||
Ethnicity *string `json:"ethnicity,omitempty"` // String (optional) The ethnicity of the user e.g. "Not Hispanic of Latino"
|
||||
Gender *string `json:"gender,omitempty"` // String (optional) The user’s gender e.g. "male"
|
||||
Language *string `json:"language,omitempty"` //String (optional) The user’s primary language e.g. "eng"
|
||||
MaritalStatus *string `json:"maritalStatus,omitempty"` // String (optional) The user’s marital status (eg: “married”, “single”)
|
||||
Name Name `json:"name,omitempty"` // Object (optional) (See name object)
|
||||
Race *string `json:"race,omitempty"` // String (optional) The user’s race e.g. "White"
|
||||
EthnicityCodes *string `json:"ethnicityCodes,omitempty"` // ethnicityCodes Array[Object] (optional) CDC Race & Ethnicity and SNOMED CT Ethnicity codes: See codes
|
||||
MaritalStatusCodes *string `json:"maritalStatusCodes,omitempty"` // String (optional) SNOMED CT Marital status codes: see codes object
|
||||
GenderCodes *string `json:"genderCodes,omitempty"` //String (optional) SNOMED CT Gender codes: See codes
|
||||
}
|
||||
|
||||
type Address struct {
|
||||
City *string `json:"city,omitempty"` // (optional) City of address e.g. "SAN FRANCISCO"
|
||||
Country *string `json:"country,omitempty"` // (optional) Country of address e.g. "US"
|
||||
State *string `json:"state,omitempty"` // (optional) State of address e.g. "CA"
|
||||
Street []string `json:"street,omitempty"` // Array[String] (optional) Street of address e.g. ["156 22ND AVE NW"]
|
||||
Zip *string `json:"zip,omitempty"` // (optional) Zip of address e.g. "94123"
|
||||
}
|
||||
|
||||
type Name struct {
|
||||
Prefix *string `json:"prefix,omitempty"` // String (optional) The title of the provider e.g. "MD"
|
||||
Given []string `json:"given,omitempty"` // Array[String] Name values associated with the provider e.g. ["Travis", "R"]
|
||||
Family *string `json:"family,omitempty"` // String (optional) Family name of the provider e.g. "Liddell"
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package models
|
||||
|
||||
import "gorm.io/datatypes"
|
||||
|
||||
type ResourceFhir struct {
|
||||
OriginBase
|
||||
|
||||
//embedded data
|
||||
Payload datatypes.JSON `json:"payload" gorm:"payload"`
|
||||
}
|
|
@ -2,6 +2,7 @@ package handler
|
|||
|
||||
import (
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
@ -27,6 +28,21 @@ func CreateSource(c *gin.Context) {
|
|||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
|
||||
// after creating the source, we should do a bulk import
|
||||
sourceClient, err := hub.NewClient(providerCred.ProviderId, nil, logger, providerCred)
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while initializing hub client using source credential", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
err = sourceClient.SyncAll(databaseRepo)
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while bulk import of resources from source", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": providerCred})
|
||||
}
|
||||
|
||||
|
|
5
go.mod
5
go.mod
|
@ -16,6 +16,7 @@ require (
|
|||
github.com/stretchr/testify v1.7.1
|
||||
github.com/urfave/cli/v2 v2.11.2
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
|
||||
gorm.io/datatypes v1.0.7
|
||||
gorm.io/gorm v1.23.8
|
||||
)
|
||||
|
||||
|
@ -29,11 +30,12 @@ require (
|
|||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.10.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||
github.com/goccy/go-json v0.9.7 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.4 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kvz/logstreamer v0.0.0-20150507115422-a635b98146f0 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
|
@ -67,6 +69,7 @@ require (
|
|||
gopkg.in/ini.v1 v1.66.4 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gorm.io/driver/mysql v1.3.2 // indirect
|
||||
modernc.org/libc v1.16.8 // indirect
|
||||
modernc.org/mathutil v1.4.1 // indirect
|
||||
modernc.org/memory v1.1.1 // indirect
|
||||
|
|
Loading…
Reference in New Issue