From 05b13677a6ec2c0cd62d6f80c0a1abc17d05b40f Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Tue, 30 Aug 2022 19:55:27 -0700 Subject: [PATCH] working serialization, storage in DB. --- backend/pkg/database/sqlite_repository.go | 39 +++++++--- .../hub/internal/fhir/base/fhir401_client.go | 78 ++++++++++++++++++- backend/pkg/models/medical_profile.go | 39 ++++++++++ 3 files changed, 141 insertions(+), 15 deletions(-) create mode 100644 backend/pkg/models/medical_profile.go diff --git a/backend/pkg/database/sqlite_repository.go b/backend/pkg/database/sqlite_repository.go index 2ae5af91..6e08d07c 100644 --- a/backend/pkg/database/sqlite_repository.go +++ b/backend/pkg/database/sqlite_repository.go @@ -43,11 +43,11 @@ func NewRepository(appConfig config.Interface, globalLogger logrus.FieldLogger) globalLogger.Infof("Successfully connected to scrutiny sqlite db: %s\n", appConfig.GetString("web.database.location")) //TODO: automigrate for now - err = database.AutoMigrate(&models.User{}) - if err != nil { - return nil, fmt.Errorf("Failed to automigrate! - %v", err) - } - err = database.AutoMigrate(&models.ProviderCredential{}) + err = database.AutoMigrate( + &models.User{}, + &models.Source{}, + &models.Profile{}, + ) if err != nil { return nil, fmt.Errorf("Failed to automigrate! - %v", err) } @@ -79,18 +79,35 @@ func (sr *sqliteRepository) Close() error { } func (sr *sqliteRepository) GetCurrentUser() models.User { - return models.User{Model: gorm.Model{ID: 1}} + var currentUser models.User + sr.gormClient.Model(models.User{}).First(¤tUser) + + return currentUser +} + +// UpsertSourceResource Create or Update record in database +func (sr *sqliteRepository) UpsertProfile(ctx context.Context, profile models.Profile) error { + if sr.gormClient.Debug().WithContext(ctx).Model(&profile). + Where(models.OriginBase{ + SourceID: profile.GetSourceID(), + SourceResourceID: profile.GetSourceResourceID(), + SourceResourceType: profile.GetSourceResourceType(), //TODO: and UpdatedAt > old UpdatedAt + }).Updates(&profile).RowsAffected == 0 { + sr.logger.Infof("profile does not exist, creating: %s %s %s", profile.GetSourceID(), profile.GetSourceResourceID(), profile.GetSourceResourceType()) + return sr.gormClient.Debug().Create(&profile).Error + } + return nil } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ProviderCredentials //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -func (sr *sqliteRepository) CreateProviderCredentials(ctx context.Context, providerCreds *models.ProviderCredential) error { +func (sr *sqliteRepository) CreateSource(ctx context.Context, providerCreds *models.Source) error { providerCreds.UserID = sr.GetCurrentUser().ID if sr.gormClient.WithContext(ctx).Model(&providerCreds). - Where(models.ProviderCredential{ + Where(models.Source{ UserID: providerCreds.UserID, ProviderId: providerCreds.ProviderId, PatientId: providerCreds.PatientId}).Updates(&providerCreds).RowsAffected == 0 { @@ -99,11 +116,11 @@ func (sr *sqliteRepository) CreateProviderCredentials(ctx context.Context, provi return nil } -func (sr *sqliteRepository) GetProviderCredentials(ctx context.Context) ([]models.ProviderCredential, error) { +func (sr *sqliteRepository) GetSources(ctx context.Context) ([]models.Source, error) { - var providerCredentials []models.ProviderCredential + var providerCredentials []models.Source results := sr.gormClient.WithContext(ctx). - Where(models.ProviderCredential{UserID: sr.GetCurrentUser().ID}). + Where(models.Source{UserID: sr.GetCurrentUser().ID}). Find(&providerCredentials) return providerCredentials, results.Error diff --git a/backend/pkg/hub/internal/fhir/base/fhir401_client.go b/backend/pkg/hub/internal/fhir/base/fhir401_client.go index 413d101f..4828a245 100644 --- a/backend/pkg/hub/internal/fhir/base/fhir401_client.go +++ b/backend/pkg/hub/internal/fhir/base/fhir401_client.go @@ -6,16 +6,18 @@ import ( "github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models" "github.com/fastenhealth/gofhir-models/fhir401" "github.com/sirupsen/logrus" + "log" "net/http" + "time" ) type FHIR401Client struct { - BaseClient + *BaseClient } -func NewFHIR401Client(appConfig config.Interface, globalLogger logrus.FieldLogger, credentials models.ProviderCredential, testHttpClient ...*http.Client) (FHIR401Client, error) { - return FHIR401Client{ - NewBaseClient(appConfig, globalLogger, credentials, testHttpClient...), +func NewFHIR401Client(appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (*FHIR401Client, error) { + return &FHIR401Client{ + NewBaseClient(appConfig, globalLogger, source, testHttpClient...), }, nil } @@ -37,3 +39,71 @@ func (c *FHIR401Client) GetPatient(patientId string) (*fhir401.Patient, error) { err := c.GetRequest(fmt.Sprintf("Patient/%s", patientId), &patient) return &patient, err } + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Process & Generate API/Database Models +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +func (c *FHIR401Client) ProcessPatients(patients []fhir401.Patient) ([]models.Profile, error) { + profiles := []models.Profile{} + for _, item := range patients { + log.Printf("item", 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.RFC3339, *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 + + } + profiles = append(profiles, patientProfile) + } + + return profiles, nil +} diff --git a/backend/pkg/models/medical_profile.go b/backend/pkg/models/medical_profile.go new file mode 100644 index 00000000..afdc2bd3 --- /dev/null +++ b/backend/pkg/models/medical_profile.go @@ -0,0 +1,39 @@ +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" +}