adding support for Composition resource type (Custom grouping) (#20)
This commit is contained in:
parent
ba333fce42
commit
f903c38b55
|
@ -58,3 +58,5 @@ vendor
|
|||
fasten.db
|
||||
test.go
|
||||
/.couchdb
|
||||
|
||||
config.dev.yaml
|
||||
|
|
|
@ -1 +1,12 @@
|
|||
package pkg
|
||||
|
||||
const (
|
||||
ContextKeyTypeConfig string = "CONFIG"
|
||||
ContextKeyTypeDatabase string = "REPOSITORY"
|
||||
ContextKeyTypeLogger string = "LOGGER"
|
||||
|
||||
ContextKeyTypeAuthUsername string = "AUTH_USERNAME"
|
||||
ContextKeyTypeAuthToken string = "AUTH_TOKEN"
|
||||
|
||||
FhirResourceTypeComposition string = "Composition"
|
||||
)
|
||||
|
|
|
@ -25,6 +25,7 @@ type DatabaseRepository interface {
|
|||
AddResourceAssociation(ctx context.Context, source *models.SourceCredential, resourceType string, resourceId string, relatedSource *models.SourceCredential, relatedResourceType string, relatedResourceId string) error
|
||||
RemoveResourceAssociation(ctx context.Context, source *models.SourceCredential, resourceType string, resourceId string, relatedSource *models.SourceCredential, relatedResourceType string, relatedResourceId string) error
|
||||
GetFlattenedResourceGraph(ctx context.Context) ([]*models.ResourceFhir, []*models.ResourceFhir, error)
|
||||
AddResourceComposition(ctx context.Context, compositionTitle string, resources []*models.ResourceFhir) error
|
||||
//UpsertProfile(context.Context, *models.Profile) error
|
||||
//UpsertOrganziation(context.Context, *models.Organization) error
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// SourceCredential: interface.go
|
||||
// Source: interface.go
|
||||
|
||||
// Package mock_database is a generated GoMock package.
|
||||
package mock_database
|
||||
|
@ -8,7 +8,8 @@ import (
|
|||
context "context"
|
||||
reflect "reflect"
|
||||
|
||||
models "github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
models "github.com/fastenhealth/fasten-sources/clients/models"
|
||||
models0 "github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
|
@ -35,6 +36,34 @@ func (m *MockDatabaseRepository) EXPECT() *MockDatabaseRepositoryMockRecorder {
|
|||
return m.recorder
|
||||
}
|
||||
|
||||
// AddResourceAssociation mocks base method.
|
||||
func (m *MockDatabaseRepository) AddResourceAssociation(ctx context.Context, source *models0.SourceCredential, resourceType, resourceId string, relatedSource *models0.SourceCredential, relatedResourceType, relatedResourceId string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "AddResourceAssociation", ctx, source, resourceType, resourceId, relatedSource, relatedResourceType, relatedResourceId)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// AddResourceAssociation indicates an expected call of AddResourceAssociation.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) AddResourceAssociation(ctx, source, resourceType, resourceId, relatedSource, relatedResourceType, relatedResourceId interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddResourceAssociation", reflect.TypeOf((*MockDatabaseRepository)(nil).AddResourceAssociation), ctx, source, resourceType, resourceId, relatedSource, relatedResourceType, relatedResourceId)
|
||||
}
|
||||
|
||||
// AddResourceComposition mocks base method.
|
||||
func (m *MockDatabaseRepository) AddResourceComposition(ctx context.Context, compositionTitle string, resources []*models0.ResourceFhir) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "AddResourceComposition", ctx, compositionTitle, resources)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// AddResourceComposition indicates an expected call of AddResourceComposition.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) AddResourceComposition(ctx, compositionTitle, resources interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddResourceComposition", reflect.TypeOf((*MockDatabaseRepository)(nil).AddResourceComposition), ctx, compositionTitle, resources)
|
||||
}
|
||||
|
||||
// Close mocks base method.
|
||||
func (m *MockDatabaseRepository) Close() error {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -49,8 +78,22 @@ func (mr *MockDatabaseRepositoryMockRecorder) Close() *gomock.Call {
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockDatabaseRepository)(nil).Close))
|
||||
}
|
||||
|
||||
// CreateSource mocks base method.
|
||||
func (m *MockDatabaseRepository) CreateSource(arg0 context.Context, arg1 *models0.SourceCredential) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CreateSource", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// CreateSource indicates an expected call of CreateSource.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) CreateSource(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSource", reflect.TypeOf((*MockDatabaseRepository)(nil).CreateSource), arg0, arg1)
|
||||
}
|
||||
|
||||
// CreateUser mocks base method.
|
||||
func (m *MockDatabaseRepository) CreateUser(arg0 context.Context, arg1 *models.User) error {
|
||||
func (m *MockDatabaseRepository) CreateUser(arg0 context.Context, arg1 *models0.User) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CreateUser", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
|
@ -62,3 +105,211 @@ func (mr *MockDatabaseRepositoryMockRecorder) CreateUser(arg0, arg1 interface{})
|
|||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUser", reflect.TypeOf((*MockDatabaseRepository)(nil).CreateUser), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetCurrentUser mocks base method.
|
||||
func (m *MockDatabaseRepository) GetCurrentUser(arg0 context.Context) *models0.User {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetCurrentUser", arg0)
|
||||
ret0, _ := ret[0].(*models0.User)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetCurrentUser indicates an expected call of GetCurrentUser.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) GetCurrentUser(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentUser", reflect.TypeOf((*MockDatabaseRepository)(nil).GetCurrentUser), arg0)
|
||||
}
|
||||
|
||||
// GetFlattenedResourceGraph mocks base method.
|
||||
func (m *MockDatabaseRepository) GetFlattenedResourceGraph(ctx context.Context) ([]*models0.ResourceFhir, []*models0.ResourceFhir, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetFlattenedResourceGraph", ctx)
|
||||
ret0, _ := ret[0].([]*models0.ResourceFhir)
|
||||
ret1, _ := ret[1].([]*models0.ResourceFhir)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
||||
// GetFlattenedResourceGraph indicates an expected call of GetFlattenedResourceGraph.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) GetFlattenedResourceGraph(ctx interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFlattenedResourceGraph", reflect.TypeOf((*MockDatabaseRepository)(nil).GetFlattenedResourceGraph), ctx)
|
||||
}
|
||||
|
||||
// GetPatientForSources mocks base method.
|
||||
func (m *MockDatabaseRepository) GetPatientForSources(ctx context.Context) ([]models0.ResourceFhir, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetPatientForSources", ctx)
|
||||
ret0, _ := ret[0].([]models0.ResourceFhir)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetPatientForSources indicates an expected call of GetPatientForSources.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) GetPatientForSources(ctx interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPatientForSources", reflect.TypeOf((*MockDatabaseRepository)(nil).GetPatientForSources), ctx)
|
||||
}
|
||||
|
||||
// GetResourceBySourceId mocks base method.
|
||||
func (m *MockDatabaseRepository) GetResourceBySourceId(arg0 context.Context, arg1, arg2 string) (*models0.ResourceFhir, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetResourceBySourceId", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(*models0.ResourceFhir)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetResourceBySourceId indicates an expected call of GetResourceBySourceId.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) GetResourceBySourceId(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetResourceBySourceId", reflect.TypeOf((*MockDatabaseRepository)(nil).GetResourceBySourceId), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// GetResourceBySourceType mocks base method.
|
||||
func (m *MockDatabaseRepository) GetResourceBySourceType(arg0 context.Context, arg1, arg2 string) (*models0.ResourceFhir, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetResourceBySourceType", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(*models0.ResourceFhir)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetResourceBySourceType indicates an expected call of GetResourceBySourceType.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) GetResourceBySourceType(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetResourceBySourceType", reflect.TypeOf((*MockDatabaseRepository)(nil).GetResourceBySourceType), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// GetSource mocks base method.
|
||||
func (m *MockDatabaseRepository) GetSource(arg0 context.Context, arg1 string) (*models0.SourceCredential, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetSource", arg0, arg1)
|
||||
ret0, _ := ret[0].(*models0.SourceCredential)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetSource indicates an expected call of GetSource.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) GetSource(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSource", reflect.TypeOf((*MockDatabaseRepository)(nil).GetSource), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetSourceSummary mocks base method.
|
||||
func (m *MockDatabaseRepository) GetSourceSummary(arg0 context.Context, arg1 string) (*models0.SourceSummary, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetSourceSummary", arg0, arg1)
|
||||
ret0, _ := ret[0].(*models0.SourceSummary)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetSourceSummary indicates an expected call of GetSourceSummary.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) GetSourceSummary(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSourceSummary", reflect.TypeOf((*MockDatabaseRepository)(nil).GetSourceSummary), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetSources mocks base method.
|
||||
func (m *MockDatabaseRepository) GetSources(arg0 context.Context) ([]models0.SourceCredential, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetSources", arg0)
|
||||
ret0, _ := ret[0].([]models0.SourceCredential)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetSources indicates an expected call of GetSources.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) GetSources(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSources", reflect.TypeOf((*MockDatabaseRepository)(nil).GetSources), arg0)
|
||||
}
|
||||
|
||||
// GetSummary mocks base method.
|
||||
func (m *MockDatabaseRepository) GetSummary(ctx context.Context) (*models0.Summary, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetSummary", ctx)
|
||||
ret0, _ := ret[0].(*models0.Summary)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetSummary indicates an expected call of GetSummary.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) GetSummary(ctx interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSummary", reflect.TypeOf((*MockDatabaseRepository)(nil).GetSummary), ctx)
|
||||
}
|
||||
|
||||
// GetUserByUsername mocks base method.
|
||||
func (m *MockDatabaseRepository) GetUserByUsername(arg0 context.Context, arg1 string) (*models0.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetUserByUsername", arg0, arg1)
|
||||
ret0, _ := ret[0].(*models0.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetUserByUsername indicates an expected call of GetUserByUsername.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) GetUserByUsername(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByUsername", reflect.TypeOf((*MockDatabaseRepository)(nil).GetUserByUsername), arg0, arg1)
|
||||
}
|
||||
|
||||
// ListResources mocks base method.
|
||||
func (m *MockDatabaseRepository) ListResources(arg0 context.Context, arg1 models0.ListResourceQueryOptions) ([]models0.ResourceFhir, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ListResources", arg0, arg1)
|
||||
ret0, _ := ret[0].([]models0.ResourceFhir)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ListResources indicates an expected call of ListResources.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) ListResources(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListResources", reflect.TypeOf((*MockDatabaseRepository)(nil).ListResources), arg0, arg1)
|
||||
}
|
||||
|
||||
// Migrate mocks base method.
|
||||
func (m *MockDatabaseRepository) Migrate() error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Migrate")
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Migrate indicates an expected call of Migrate.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) Migrate() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Migrate", reflect.TypeOf((*MockDatabaseRepository)(nil).Migrate))
|
||||
}
|
||||
|
||||
// RemoveResourceAssociation mocks base method.
|
||||
func (m *MockDatabaseRepository) RemoveResourceAssociation(ctx context.Context, source *models0.SourceCredential, resourceType, resourceId string, relatedSource *models0.SourceCredential, relatedResourceType, relatedResourceId string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "RemoveResourceAssociation", ctx, source, resourceType, resourceId, relatedSource, relatedResourceType, relatedResourceId)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// RemoveResourceAssociation indicates an expected call of RemoveResourceAssociation.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) RemoveResourceAssociation(ctx, source, resourceType, resourceId, relatedSource, relatedResourceType, relatedResourceId interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveResourceAssociation", reflect.TypeOf((*MockDatabaseRepository)(nil).RemoveResourceAssociation), ctx, source, resourceType, resourceId, relatedSource, relatedResourceType, relatedResourceId)
|
||||
}
|
||||
|
||||
// UpsertRawResource mocks base method.
|
||||
func (m *MockDatabaseRepository) UpsertRawResource(ctx context.Context, sourceCredentials models.SourceCredential, rawResource models.RawResourceFhir) (bool, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpsertRawResource", ctx, sourceCredentials, rawResource)
|
||||
ret0, _ := ret[0].(bool)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// UpsertRawResource indicates an expected call of UpsertRawResource.
|
||||
func (mr *MockDatabaseRepositoryMockRecorder) UpsertRawResource(ctx, sourceCredentials, rawResource interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertRawResource", reflect.TypeOf((*MockDatabaseRepository)(nil).UpsertRawResource), ctx, sourceCredentials, rawResource)
|
||||
}
|
||||
|
|
|
@ -6,8 +6,10 @@ import (
|
|||
"fmt"
|
||||
"github.com/dominikbraun/graph"
|
||||
sourceModel "github.com/fastenhealth/fasten-sources/clients/models"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/glebarez/sqlite"
|
||||
"github.com/google/uuid"
|
||||
|
@ -121,10 +123,10 @@ func (sr *SqliteRepository) GetUserByUsername(ctx context.Context, username stri
|
|||
}
|
||||
|
||||
func (sr *SqliteRepository) GetCurrentUser(ctx context.Context) *models.User {
|
||||
username := ctx.Value("AUTH_USERNAME")
|
||||
username := ctx.Value(pkg.ContextKeyTypeAuthUsername)
|
||||
if username == nil {
|
||||
ginCtx := ctx.(*gin.Context)
|
||||
username = ginCtx.MustGet("AUTH_USERNAME")
|
||||
username = ginCtx.MustGet(pkg.ContextKeyTypeAuthUsername)
|
||||
}
|
||||
|
||||
var currentUser models.User
|
||||
|
@ -134,7 +136,7 @@ func (sr *SqliteRepository) GetCurrentUser(ctx context.Context) *models.User {
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// User
|
||||
// Summary
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func (sr *SqliteRepository) GetSummary(ctx context.Context) (*models.Summary, error) {
|
||||
|
@ -205,7 +207,7 @@ func (sr *SqliteRepository) UpsertRawResource(ctx context.Context, sourceCredent
|
|||
//create associations
|
||||
//note: we create the association in the related_resources table **before** the model actually exists.
|
||||
|
||||
if rawResource.ReferencedResources != nil {
|
||||
if rawResource.ReferencedResources != nil && len(rawResource.ReferencedResources) > 0 {
|
||||
for _, referencedResource := range rawResource.ReferencedResources {
|
||||
parts := strings.Split(referencedResource, "/")
|
||||
if len(parts) != 2 {
|
||||
|
@ -227,7 +229,17 @@ func (sr *SqliteRepository) UpsertRawResource(ctx context.Context, sourceCredent
|
|||
}
|
||||
}
|
||||
|
||||
sr.Logger.Infof("insert/update (%v) %v", rawResource.SourceResourceType, rawResource.SourceResourceID)
|
||||
return sr.UpsertResource(ctx, wrappedResourceModel)
|
||||
|
||||
}
|
||||
|
||||
//this method will upsert a resource, however it will not create associations.
|
||||
func (sr *SqliteRepository) UpsertResource(ctx context.Context, wrappedResourceModel *models.ResourceFhir) (bool, error) {
|
||||
wrappedResourceModel.UserID = sr.GetCurrentUser(ctx).ID
|
||||
wrappedResourceModel.RelatedResourceFhir = nil
|
||||
cachedResourceRaw := wrappedResourceModel.ResourceRaw
|
||||
|
||||
sr.Logger.Infof("insert/update (%v) %v", wrappedResourceModel.SourceResourceType, wrappedResourceModel.SourceResourceID)
|
||||
|
||||
createResult := sr.GormClient.WithContext(ctx).Where(models.OriginBase{
|
||||
SourceID: wrappedResourceModel.GetSourceID(),
|
||||
|
@ -240,7 +252,7 @@ func (sr *SqliteRepository) UpsertRawResource(ctx context.Context, sourceCredent
|
|||
} else if createResult.RowsAffected == 0 {
|
||||
//at this point, wrappedResourceModel contains the data found in the database.
|
||||
// check if the database resource matches the new resource.
|
||||
if wrappedResourceModel.ResourceRaw.String() != string(rawResource.ResourceRaw) {
|
||||
if wrappedResourceModel.ResourceRaw.String() != string(cachedResourceRaw) {
|
||||
updateResult := createResult.Omit("RelatedResourceFhir.*").Updates(wrappedResourceModel)
|
||||
return updateResult.RowsAffected > 0, updateResult.Error
|
||||
} else {
|
||||
|
@ -251,34 +263,6 @@ func (sr *SqliteRepository) UpsertRawResource(ctx context.Context, sourceCredent
|
|||
//resource was created
|
||||
return createResult.RowsAffected > 0, createResult.Error
|
||||
}
|
||||
|
||||
//return results.RowsAffected > 0, results.Error
|
||||
|
||||
//if sr.GormClient.Debug().WithContext(ctx).
|
||||
// Where(models.OriginBase{
|
||||
// SourceID: wrappedResourceModel.GetSourceID(),
|
||||
// SourceResourceID: wrappedResourceModel.GetSourceResourceID(),
|
||||
// SourceResourceType: wrappedResourceModel.GetSourceResourceType(), //TODO: and UpdatedAt > old UpdatedAt
|
||||
// }).Updates(wrappedResourceModel).RowsAffected == 0 {
|
||||
// sr.Logger.Infof("resource does not exist, creating: %s %s %s", wrappedResourceModel.GetSourceID(), wrappedResourceModel.GetSourceResourceID(), wrappedResourceModel.GetSourceResourceType())
|
||||
// return sr.GormClient.Debug().Create(wrappedResourceModel).Error
|
||||
//}
|
||||
//return nil
|
||||
}
|
||||
|
||||
func (sr *SqliteRepository) UpsertResource(ctx context.Context, resourceModel *models.ResourceFhir) error {
|
||||
sr.Logger.Infof("insert/update (%T) %v", resourceModel, resourceModel)
|
||||
|
||||
if sr.GormClient.WithContext(ctx).
|
||||
Where(models.OriginBase{
|
||||
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.Create(resourceModel).Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sr *SqliteRepository) ListResources(ctx context.Context, queryOptions models.ListResourceQueryOptions) ([]models.ResourceFhir, error) {
|
||||
|
@ -407,6 +391,7 @@ func (sr *SqliteRepository) GetFlattenedResourceGraph(ctx context.Context) ([]*m
|
|||
}
|
||||
|
||||
//Generate Graph
|
||||
// TODO optimization: eventually cache the graph in a database/storage, and update when new resources are added.
|
||||
g := graph.New(resourceVertexId, graph.Directed(), graph.Acyclic(), graph.Rooted())
|
||||
|
||||
//add vertices to the graph (must be done first)
|
||||
|
@ -477,7 +462,7 @@ func (sr *SqliteRepository) GetFlattenedResourceGraph(ctx context.Context) ([]*m
|
|||
continue
|
||||
}
|
||||
|
||||
if strings.ToLower(resource.SourceResourceType) == "condition" {
|
||||
if strings.ToLower(resource.SourceResourceType) == "condition" || strings.ToLower(resource.SourceResourceType) == strings.ToLower(pkg.FhirResourceTypeComposition) {
|
||||
conditionList = append(conditionList, resource)
|
||||
} else if strings.ToLower(resource.SourceResourceType) == "encounter" {
|
||||
encounterList = append(encounterList, resource)
|
||||
|
@ -507,6 +492,10 @@ func (sr *SqliteRepository) GetFlattenedResourceGraph(ctx context.Context) ([]*m
|
|||
for ndx, _ := range encounterList {
|
||||
// this is a "root" encounter, which is not related to any condition, we should add it to the Unknown encounters list
|
||||
flattenRelatedResourcesFn(encounterList[ndx])
|
||||
|
||||
//sort all related resources (by date, desc)
|
||||
encounterList[ndx].RelatedResourceFhir = utils.SortResourcePtrListByDate(encounterList[ndx].RelatedResourceFhir)
|
||||
|
||||
}
|
||||
|
||||
// Step 4: find all encounters referenced by the root conditions, populate them, then add them to the condition as RelatedResourceFhir
|
||||
|
@ -520,10 +509,13 @@ func (sr *SqliteRepository) GetFlattenedResourceGraph(ctx context.Context) ([]*m
|
|||
flattenRelatedResourcesFn(relatedResourceFhir)
|
||||
conditionList[ndx].RelatedResourceFhir = append(conditionList[ndx].RelatedResourceFhir, relatedResourceFhir)
|
||||
}
|
||||
|
||||
//sort all related resources (by date, desc)
|
||||
conditionList[ndx].RelatedResourceFhir = utils.SortResourcePtrListByDate(conditionList[ndx].RelatedResourceFhir)
|
||||
}
|
||||
|
||||
//TODO: sort conditionList by date
|
||||
|
||||
conditionList = utils.SortResourcePtrListByDate(conditionList)
|
||||
encounterList = utils.SortResourcePtrListByDate(encounterList)
|
||||
return conditionList, encounterList, nil
|
||||
}
|
||||
|
||||
|
@ -674,6 +666,155 @@ func (sr *SqliteRepository) RemoveResourceAssociation(ctx context.Context, sourc
|
|||
}).Error
|
||||
}
|
||||
|
||||
// AddResourceComposition
|
||||
// this will group resources together into a "Composition" -- primarily to group related Encounters & Conditions into one semantic root.
|
||||
// algorithm:
|
||||
// - find source for each resource
|
||||
// - (validate) ensure the current user and the source for each resource matches
|
||||
// - check if there is a Composition resource Type already.
|
||||
// - if Composition type already exists:
|
||||
// - update "relatesTo" field with additional data.
|
||||
// - else:
|
||||
// - Create a Composition resource type (populated with "relatesTo" references to all provided Resources)
|
||||
// - add AddResourceAssociation for all resources linked to the Composition resource
|
||||
// - store the Composition resource
|
||||
func (sr *SqliteRepository) AddResourceComposition(ctx context.Context, compositionTitle string, resources []*models.ResourceFhir) error {
|
||||
currentUser := sr.GetCurrentUser(ctx)
|
||||
|
||||
//generate placeholder source
|
||||
placeholderSource := models.SourceCredential{UserID: currentUser.ID, SourceType: "manual", ModelBase: models.ModelBase{ID: uuid.MustParse("00000000-0000-0000-0000-000000000000")}}
|
||||
|
||||
existingCompositionResources := []*models.ResourceFhir{}
|
||||
rawResourceLookupTable := map[string]*models.ResourceFhir{}
|
||||
|
||||
//find the source for each resource we'd like to merge. (for ownership verification)
|
||||
sourceLookup := map[uuid.UUID]*models.SourceCredential{}
|
||||
for _, resource := range resources {
|
||||
if resource.SourceResourceType == pkg.FhirResourceTypeComposition {
|
||||
//skip, Composition resources don't have a valid SourceCredential
|
||||
existingCompositionResources = append(existingCompositionResources, resource)
|
||||
|
||||
//compositions may include existing resources, make sure we handle these
|
||||
for _, related := range resource.RelatedResourceFhir {
|
||||
rawResourceLookupTable[fmt.Sprintf("%s/%s", related.SourceResourceType, related.SourceResourceID)] = related
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if _, sourceOk := sourceLookup[resource.SourceID]; !sourceOk {
|
||||
//source has not been added yet, lets query for it.
|
||||
sourceCred, err := sr.GetSource(ctx, resource.SourceID.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not find source %s", resource.SourceID.String())
|
||||
}
|
||||
sourceLookup[resource.SourceID] = sourceCred
|
||||
}
|
||||
|
||||
rawResourceLookupTable[fmt.Sprintf("%s/%s", resource.SourceResourceType, resource.SourceResourceID)] = resource
|
||||
}
|
||||
|
||||
// (validate) ensure the current user and the source for each resource matches
|
||||
for _, source := range sourceLookup {
|
||||
if source.UserID != currentUser.ID {
|
||||
return fmt.Errorf("source must be owned by the current user: %s vs %s", source.UserID, currentUser.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// - check if there is a Composition resource Type already.
|
||||
var compositionResource *models.ResourceFhir
|
||||
|
||||
if len(existingCompositionResources) > 0 {
|
||||
//- if Composition type already exists in this set
|
||||
// - update "relatesTo" field with additional data.
|
||||
compositionResource = existingCompositionResources[0]
|
||||
|
||||
//unassociated all existing composition resources.
|
||||
for _, existingCompositionResource := range existingCompositionResources[1:] {
|
||||
for _, relatedResource := range existingCompositionResource.RelatedResourceFhir {
|
||||
if err := sr.RemoveResourceAssociation(
|
||||
ctx,
|
||||
&placeholderSource,
|
||||
existingCompositionResource.SourceResourceType,
|
||||
existingCompositionResource.SourceResourceID,
|
||||
sourceLookup[relatedResource.SourceID],
|
||||
relatedResource.SourceResourceType,
|
||||
relatedResource.SourceResourceID,
|
||||
); err != nil {
|
||||
//ignoring errors, could be due to duplicate edges
|
||||
return fmt.Errorf("an error occurred while removing resource association: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
//remove this resource
|
||||
err := sr.GormClient.WithContext(ctx).Delete(existingCompositionResource)
|
||||
if err.Error != nil {
|
||||
return fmt.Errorf("an error occurred while removing resource: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
//- else:
|
||||
// - Create a Composition resource type (populated with "relatesTo" references to all provided Resources)
|
||||
compositionResource = &models.ResourceFhir{
|
||||
OriginBase: models.OriginBase{
|
||||
UserID: placeholderSource.UserID, //
|
||||
SourceID: placeholderSource.ID, //Empty SourceID expected ("0000-0000-0000-0000")
|
||||
SourceResourceType: pkg.FhirResourceTypeComposition,
|
||||
SourceResourceID: uuid.New().String(),
|
||||
},
|
||||
SortDate: nil, //TOOD: figoure out the sortDate by looking for the earliest sort date for all nested resources
|
||||
SortTitle: &compositionTitle,
|
||||
RelatedResourceFhir: resources,
|
||||
}
|
||||
}
|
||||
|
||||
// - Generate an "updated" RawResource json blob
|
||||
rawCompositionResource := models.ResourceComposition{
|
||||
Title: compositionTitle,
|
||||
RelatesTo: []models.ResourceCompositionRelatesTo{},
|
||||
}
|
||||
|
||||
for relatedResourceKey, _ := range rawResourceLookupTable {
|
||||
rawCompositionResource.RelatesTo = append(rawCompositionResource.RelatesTo, models.ResourceCompositionRelatesTo{
|
||||
Target: models.ResourceCompositionRelatesToTarget{
|
||||
TargetReference: models.ResourceCompositionRelatesToTargetReference{
|
||||
Reference: relatedResourceKey,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
rawResourceJson, err := json.Marshal(rawCompositionResource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
compositionResource.ResourceRaw = rawResourceJson
|
||||
|
||||
//store the Composition resource
|
||||
_, err = sr.UpsertResource(ctx, compositionResource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// - add AddResourceAssociation for all resources linked to the Composition resource
|
||||
for _, resource := range rawResourceLookupTable {
|
||||
if err := sr.AddResourceAssociation(
|
||||
ctx,
|
||||
&placeholderSource,
|
||||
compositionResource.SourceResourceType,
|
||||
compositionResource.SourceResourceID,
|
||||
sourceLookup[resource.SourceID],
|
||||
resource.SourceResourceType,
|
||||
resource.SourceResourceID,
|
||||
); err != nil {
|
||||
//ignoring errors, could be due to duplicate edges
|
||||
sr.Logger.Warnf("an error occurred while creating resource association: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SourceCredential
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -1,8 +1,21 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
sourceModels "github.com/fastenhealth/fasten-sources/clients/models"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg"
|
||||
mock_config "github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config/mock"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/google/uuid"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"golang.org/x/net/context"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -15,3 +28,233 @@ func TestSourceCredentialInterface(t *testing.T) {
|
|||
require.Implements(t, (*sourceModels.DatabaseRepository)(nil), repo, "should implement the DatabaseRepository interface from fasten-sources")
|
||||
require.Implements(t, (*DatabaseRepository)(nil), repo, "should implement the DatabaseRepository interface")
|
||||
}
|
||||
|
||||
// Define the suite, and absorb the built-in basic suite
|
||||
// functionality from testify - including a T() method which
|
||||
// returns the current testing context
|
||||
type RepositoryTestSuite struct {
|
||||
suite.Suite
|
||||
MockCtrl *gomock.Controller
|
||||
TestDatabase *os.File
|
||||
}
|
||||
|
||||
// BeforeTest has a function to be executed right before the test starts and receives the suite and test names as input
|
||||
func (suite *RepositoryTestSuite) BeforeTest(suiteName, testName string) {
|
||||
suite.MockCtrl = gomock.NewController(suite.T())
|
||||
|
||||
dbFile, err := ioutil.TempFile("", fmt.Sprintf("%s.*.db", testName))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
suite.TestDatabase = dbFile
|
||||
|
||||
}
|
||||
|
||||
// AfterTest has a function to be executed right after the test finishes and receives the suite and test names as input
|
||||
func (suite *RepositoryTestSuite) AfterTest(suiteName, testName string) {
|
||||
suite.MockCtrl.Finish()
|
||||
os.Remove(suite.TestDatabase.Name())
|
||||
}
|
||||
|
||||
// In order for 'go test' to run this suite, we need to create
|
||||
// a normal test function and pass our suite to suite.Run
|
||||
func TestRepositoryTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(RepositoryTestSuite))
|
||||
|
||||
}
|
||||
|
||||
func (suite *RepositoryTestSuite) TestNewRepository() {
|
||||
//setup
|
||||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
|
||||
//test
|
||||
_, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()))
|
||||
|
||||
//assert
|
||||
require.NoError(suite.T(), err)
|
||||
}
|
||||
|
||||
func (suite *RepositoryTestSuite) TestCreateUser() {
|
||||
//setup
|
||||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()))
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
//test
|
||||
userModel := &models.User{
|
||||
Username: "test_username",
|
||||
Password: "testpassword",
|
||||
Email: "test@test.com",
|
||||
}
|
||||
dbRepo.CreateUser(context.Background(), userModel)
|
||||
|
||||
//assert
|
||||
require.NotEmpty(suite.T(), userModel.ID)
|
||||
}
|
||||
|
||||
func (suite *RepositoryTestSuite) TestCreateUser_WithExitingUser_ShouldFail() {
|
||||
//setup
|
||||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()))
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
//test
|
||||
userModel := &models.User{
|
||||
Username: "test_username",
|
||||
Password: "testpassword",
|
||||
Email: "test@test.com",
|
||||
}
|
||||
err = dbRepo.CreateUser(context.Background(), userModel)
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
userModel2 := &models.User{
|
||||
Username: "test_username",
|
||||
Password: "testpassword",
|
||||
Email: "test@test.com",
|
||||
}
|
||||
err = dbRepo.CreateUser(context.Background(), userModel2)
|
||||
//assert
|
||||
require.Error(suite.T(), err)
|
||||
}
|
||||
|
||||
//TODO: ensure user's cannot specify the ID when creating a user.
|
||||
func (suite *RepositoryTestSuite) TestCreateUser_WithUserProvidedId_ShouldBeReplaced() {
|
||||
//setup
|
||||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()))
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
//test
|
||||
userProvidedId := uuid.New()
|
||||
userModel := &models.User{
|
||||
ModelBase: models.ModelBase{
|
||||
ID: userProvidedId,
|
||||
},
|
||||
Username: "test_username",
|
||||
Password: "testpassword",
|
||||
Email: "test@test.com",
|
||||
}
|
||||
dbRepo.CreateUser(context.Background(), userModel)
|
||||
|
||||
//assert
|
||||
require.NotEmpty(suite.T(), userModel.ID)
|
||||
require.NotEqual(suite.T(), userProvidedId.String(), userModel.ID.String())
|
||||
}
|
||||
|
||||
func (suite *RepositoryTestSuite) TestGetUserByUsername() {
|
||||
//setup
|
||||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()))
|
||||
require.NoError(suite.T(), err)
|
||||
userModel := &models.User{
|
||||
Username: "test_username",
|
||||
Password: "testpassword",
|
||||
Email: "test@test.com",
|
||||
}
|
||||
err = dbRepo.CreateUser(context.Background(), userModel)
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
//test
|
||||
userModelResult, err := dbRepo.GetUserByUsername(context.Background(), "test_username")
|
||||
|
||||
//assert
|
||||
require.NoError(suite.T(), err)
|
||||
require.Equal(suite.T(), userModel.ID, userModelResult.ID)
|
||||
}
|
||||
|
||||
func (suite *RepositoryTestSuite) TestGetUserByUsername_WithInvalidUsername() {
|
||||
//setup
|
||||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()))
|
||||
require.NoError(suite.T(), err)
|
||||
userModel := &models.User{
|
||||
Username: "test_username",
|
||||
Password: "testpassword",
|
||||
Email: "test@test.com",
|
||||
}
|
||||
err = dbRepo.CreateUser(context.Background(), userModel)
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
//test
|
||||
_, err = dbRepo.GetUserByUsername(context.Background(), "userdoesntexist")
|
||||
|
||||
//assert
|
||||
require.Error(suite.T(), err)
|
||||
}
|
||||
|
||||
func (suite *RepositoryTestSuite) TestGetCurrentUser_WithContextBackgroundAuthUser() {
|
||||
//setup
|
||||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()))
|
||||
require.NoError(suite.T(), err)
|
||||
userModel := &models.User{
|
||||
Username: "test_username",
|
||||
Password: "testpassword",
|
||||
Email: "test@test.com",
|
||||
}
|
||||
err = dbRepo.CreateUser(context.Background(), userModel)
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
//test
|
||||
userModelResult := dbRepo.GetCurrentUser(context.WithValue(context.Background(), pkg.ContextKeyTypeAuthUsername, "test_username"))
|
||||
|
||||
//assert
|
||||
require.NotNil(suite.T(), userModelResult)
|
||||
require.Equal(suite.T(), userModelResult.Username, "test_username")
|
||||
}
|
||||
|
||||
func (suite *RepositoryTestSuite) TestGetCurrentUser_WithGinContextBackgroundAuthUser() {
|
||||
//setup
|
||||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()))
|
||||
require.NoError(suite.T(), err)
|
||||
userModel := &models.User{
|
||||
Username: "test_username",
|
||||
Password: "testpassword",
|
||||
Email: "test@test.com",
|
||||
}
|
||||
err = dbRepo.CreateUser(context.Background(), userModel)
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
//test
|
||||
ginContext := gin.Context{}
|
||||
ginContext.Set(pkg.ContextKeyTypeAuthUsername, "test_username")
|
||||
userModelResult := dbRepo.GetCurrentUser(&ginContext)
|
||||
|
||||
//assert
|
||||
require.NotNil(suite.T(), userModelResult)
|
||||
require.Equal(suite.T(), userModelResult.Username, "test_username")
|
||||
}
|
||||
|
||||
func (suite *RepositoryTestSuite) TestGetCurrentUser_WithContextBackgroundAuthUserAndNoUserExists_ShouldThrowError() {
|
||||
//setup
|
||||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()))
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
//test
|
||||
userModelResult := dbRepo.GetCurrentUser(context.WithValue(context.Background(), pkg.ContextKeyTypeAuthUsername, "test_username"))
|
||||
|
||||
//assert
|
||||
require.Nil(suite.T(), userModelResult)
|
||||
}
|
||||
|
||||
//TODO - merging multiple Compositions is broken
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package models
|
||||
|
||||
//this model is based on FHIR401 Resource Composition - https://github.com/fastenhealth/gofhir-models/blob/main/fhir401/composition.go
|
||||
type ResourceComposition struct {
|
||||
Title string `bson:"title" json:"title"`
|
||||
RelatesTo []ResourceCompositionRelatesTo `bson:"relatesTo,omitempty" json:"relatesTo,omitempty"`
|
||||
}
|
||||
|
||||
type ResourceCompositionRelatesTo struct {
|
||||
Target ResourceCompositionRelatesToTarget `bson:"target,omitempty" json:"target,omitempty"`
|
||||
}
|
||||
|
||||
type ResourceCompositionRelatesToTarget struct {
|
||||
TargetReference ResourceCompositionRelatesToTargetReference `bson:"targetReference,omitempty" json:"targetReference,omitempty"`
|
||||
}
|
||||
|
||||
type ResourceCompositionRelatesToTargetReference struct {
|
||||
Reference string `bson:"reference,omitempty" json:"reference,omitempty"`
|
||||
}
|
|
@ -8,8 +8,8 @@ import (
|
|||
type ResourceFhir struct {
|
||||
OriginBase
|
||||
|
||||
SortDate time.Time `json:"sort_date" gorm:"sort_date"`
|
||||
SortTitle string `json:"sort_title" gorm:"sort_title"`
|
||||
SortDate *time.Time `json:"sort_date" gorm:"sort_date"`
|
||||
SortTitle *string `json:"sort_title" gorm:"sort_title"`
|
||||
|
||||
//embedded data
|
||||
ResourceRaw datatypes.JSON `json:"resource_raw" gorm:"resource_raw"`
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"sort"
|
||||
)
|
||||
|
||||
//default sort ASC (a, b, c, d, e...)
|
||||
func SortResourcePtrListByTitle(resourceList []*models.ResourceFhir) []*models.ResourceFhir {
|
||||
sort.SliceStable(resourceList, func(i, j int) bool {
|
||||
if resourceList[i].SortTitle != nil && resourceList[j].SortTitle != nil {
|
||||
return (*resourceList[i].SortTitle) < (*resourceList[j].SortTitle)
|
||||
} else if resourceList[i].SortTitle != nil {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
return resourceList
|
||||
}
|
||||
|
||||
func SortResourceListByTitle(resourceList []models.ResourceFhir) []models.ResourceFhir {
|
||||
sort.SliceStable(resourceList, func(i, j int) bool {
|
||||
if resourceList[i].SortTitle != nil && resourceList[j].SortTitle != nil {
|
||||
return (*resourceList[i].SortTitle) < (*resourceList[j].SortTitle)
|
||||
} else if resourceList[i].SortTitle != nil {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
return resourceList
|
||||
}
|
||||
|
||||
//default sort DESC (today, yesterday, 2 days ago, 3 days ago...)
|
||||
func SortResourcePtrListByDate(resourceList []*models.ResourceFhir) []*models.ResourceFhir {
|
||||
sort.SliceStable(resourceList, func(i, j int) bool {
|
||||
if resourceList[i].SortDate != nil && resourceList[j].SortDate != nil {
|
||||
return (*resourceList[i].SortDate).After(*resourceList[j].SortDate)
|
||||
} else if resourceList[i].SortDate != nil {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
return resourceList
|
||||
}
|
||||
|
||||
func SortResourceListByDate(resourceList []models.ResourceFhir) []models.ResourceFhir {
|
||||
sort.SliceStable(resourceList, func(i, j int) bool {
|
||||
if resourceList[i].SortDate != nil && resourceList[j].SortDate != nil {
|
||||
return (*resourceList[i].SortDate).After(*resourceList[j].SortDate)
|
||||
} else if resourceList[i].SortDate != nil {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
return resourceList
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSortResourceListByTitle(t *testing.T) {
|
||||
//setup
|
||||
a := "a"
|
||||
g := "g"
|
||||
p := "p"
|
||||
z := "z"
|
||||
resources := []*models.ResourceFhir{
|
||||
{
|
||||
SortTitle: &a,
|
||||
},
|
||||
{
|
||||
SortTitle: &g,
|
||||
},
|
||||
{
|
||||
SortTitle: &z,
|
||||
},
|
||||
{
|
||||
SortTitle: &p,
|
||||
},
|
||||
}
|
||||
|
||||
//test
|
||||
resources = SortResourcePtrListByTitle(resources)
|
||||
|
||||
//assert
|
||||
require.Equal(t, "a", *resources[0].SortTitle)
|
||||
require.Equal(t, "g", *resources[1].SortTitle)
|
||||
require.Equal(t, "p", *resources[2].SortTitle)
|
||||
require.Equal(t, "z", *resources[3].SortTitle)
|
||||
}
|
||||
|
||||
func TestSortResourceListByDate(t *testing.T) {
|
||||
//setup
|
||||
a := time.Now().Add(time.Hour * -24)
|
||||
g := time.Now().Add(time.Hour * -48)
|
||||
p := time.Now().Add(time.Hour * -72)
|
||||
z := time.Now().Add(time.Hour * -100)
|
||||
resources := []*models.ResourceFhir{
|
||||
{
|
||||
SortDate: &a,
|
||||
},
|
||||
{
|
||||
SortDate: &g,
|
||||
},
|
||||
{
|
||||
SortDate: &z,
|
||||
},
|
||||
{
|
||||
SortDate: &p,
|
||||
},
|
||||
}
|
||||
|
||||
//test
|
||||
resources = SortResourcePtrListByDate(resources)
|
||||
|
||||
//assert
|
||||
require.Equal(t, a, *resources[0].SortDate)
|
||||
require.Equal(t, g, *resources[1].SortDate)
|
||||
require.Equal(t, p, *resources[2].SortDate)
|
||||
require.Equal(t, z, *resources[3].SortDate)
|
||||
}
|
|
@ -2,6 +2,7 @@ package handler
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/auth"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
|
@ -11,8 +12,8 @@ import (
|
|||
)
|
||||
|
||||
func AuthSignup(c *gin.Context) {
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
appConfig := c.MustGet("CONFIG").(config.Interface)
|
||||
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
|
||||
appConfig := c.MustGet(pkg.ContextKeyTypeConfig).(config.Interface)
|
||||
|
||||
var user models.User
|
||||
if err := c.ShouldBindJSON(&user); err != nil {
|
||||
|
@ -32,8 +33,8 @@ func AuthSignup(c *gin.Context) {
|
|||
}
|
||||
|
||||
func AuthSignin(c *gin.Context) {
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
appConfig := c.MustGet("CONFIG").(config.Interface)
|
||||
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
|
||||
appConfig := c.MustGet(pkg.ContextKeyTypeConfig).(config.Interface)
|
||||
|
||||
var user models.User
|
||||
if err := c.ShouldBindJSON(&user); err != nil {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"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/fastenhealth-onprem/backend/pkg/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
|
@ -10,8 +12,8 @@ import (
|
|||
)
|
||||
|
||||
func ListResourceFhir(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry)
|
||||
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
|
||||
|
||||
listResourceQueryOptions := models.ListResourceQueryOptions{}
|
||||
if len(c.Query("sourceResourceType")) > 0 {
|
||||
|
@ -29,6 +31,12 @@ func ListResourceFhir(c *gin.Context) {
|
|||
|
||||
wrappedResourceModels, err := databaseRepo.ListResources(c, listResourceQueryOptions)
|
||||
|
||||
if c.Query("sortBy") == "title" {
|
||||
wrappedResourceModels = utils.SortResourceListByTitle(wrappedResourceModels)
|
||||
} else {
|
||||
wrappedResourceModels = utils.SortResourceListByDate(wrappedResourceModels)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while retrieving resources", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
|
@ -40,8 +48,8 @@ func ListResourceFhir(c *gin.Context) {
|
|||
|
||||
//this endpoint retrieves a specific resource by its ID
|
||||
func GetResourceFhir(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry)
|
||||
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
|
||||
|
||||
resourceId := strings.Trim(c.Param("resourceId"), "/")
|
||||
sourceId := strings.Trim(c.Param("sourceId"), "/")
|
||||
|
@ -56,55 +64,27 @@ func GetResourceFhir(c *gin.Context) {
|
|||
c.JSON(http.StatusOK, gin.H{"success": true, "data": wrappedResourceModel})
|
||||
}
|
||||
|
||||
func ReplaceResourceAssociation(c *gin.Context) {
|
||||
func CreateResourceComposition(c *gin.Context) {
|
||||
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry)
|
||||
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
|
||||
|
||||
resourceAssociation := models.ResourceAssociation{}
|
||||
if err := c.ShouldBindJSON(&resourceAssociation); err != nil {
|
||||
logger.Errorln("An error occurred while parsing posted resource association data", err)
|
||||
type jsonPayload struct {
|
||||
Resources []*models.ResourceFhir `json:"resources"`
|
||||
Title string `json:"title"`
|
||||
}
|
||||
var payload jsonPayload
|
||||
if err := c.ShouldBindJSON(&payload); err != nil {
|
||||
logger.Errorln("An error occurred while parsing posted resources & title", err)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
|
||||
sourceCred, err := databaseRepo.GetSource(c, resourceAssociation.SourceID)
|
||||
err := databaseRepo.AddResourceComposition(c, payload.Title, payload.Resources)
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while retrieving source", err)
|
||||
logger.Errorln("An error occurred while creating resource group (composition)", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
|
||||
if len(resourceAssociation.OldRelatedSourceID) > 0 {
|
||||
oldRelatedSourceCred, err := databaseRepo.GetSource(c, resourceAssociation.OldRelatedSourceID)
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while retrieving old related source", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
|
||||
err = databaseRepo.RemoveResourceAssociation(c, sourceCred, resourceAssociation.SourceResourceType, resourceAssociation.SourceResourceID, oldRelatedSourceCred, resourceAssociation.OldRelatedSourceResourceType, resourceAssociation.OldRelatedSourceResourceID)
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while deleting resource association", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
newRelatedSourceCred, err := databaseRepo.GetSource(c, resourceAssociation.NewRelatedSourceID)
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while retrieving new related source", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
|
||||
err = databaseRepo.AddResourceAssociation(c, sourceCred, resourceAssociation.SourceResourceType, resourceAssociation.SourceResourceID, newRelatedSourceCred, resourceAssociation.NewRelatedSourceResourceType, resourceAssociation.NewRelatedSourceResourceID)
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while associating resource", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
}
|
||||
|
||||
|
@ -114,8 +94,8 @@ func ReplaceResourceAssociation(c *gin.Context) {
|
|||
// find the PredecessorMap
|
||||
// - filter to only vertices that are "Condition" or "Encounter" and are "root" nodes (have no edges directed to this node)
|
||||
func GetResourceFhirGraph(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry)
|
||||
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
|
||||
|
||||
conditionResourceList, encounterResourceList, err := databaseRepo.GetFlattenedResourceGraph(c)
|
||||
if err != nil {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/fastenhealth/fasten-sources/clients/factory"
|
||||
sourceModels "github.com/fastenhealth/fasten-sources/clients/models"
|
||||
sourcePkg "github.com/fastenhealth/fasten-sources/pkg"
|
||||
"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/gin-gonic/gin"
|
||||
|
@ -15,8 +16,8 @@ import (
|
|||
)
|
||||
|
||||
func CreateSource(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry)
|
||||
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
|
||||
|
||||
sourceCred := models.SourceCredential{}
|
||||
if err := c.ShouldBindJSON(&sourceCred); err != nil {
|
||||
|
@ -45,8 +46,8 @@ func CreateSource(c *gin.Context) {
|
|||
}
|
||||
|
||||
func SourceSync(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry)
|
||||
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
|
||||
|
||||
logger.Infof("Get SourceCredential Credentials: %v", c.Param("sourceId"))
|
||||
|
||||
|
@ -67,8 +68,8 @@ func SourceSync(c *gin.Context) {
|
|||
}
|
||||
|
||||
func CreateManualSource(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry)
|
||||
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
|
||||
|
||||
// single file
|
||||
file, err := c.FormFile("file")
|
||||
|
@ -135,12 +136,12 @@ func CreateManualSource(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": summary})
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": summary, "source": manualSourceCredential})
|
||||
}
|
||||
|
||||
func GetSource(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry)
|
||||
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
|
||||
|
||||
sourceCred, err := databaseRepo.GetSource(c, c.Param("sourceId"))
|
||||
if err != nil {
|
||||
|
@ -152,8 +153,8 @@ func GetSource(c *gin.Context) {
|
|||
}
|
||||
|
||||
func GetSourceSummary(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry)
|
||||
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
|
||||
|
||||
sourceSummary, err := databaseRepo.GetSourceSummary(c, c.Param("sourceId"))
|
||||
if err != nil {
|
||||
|
@ -165,8 +166,8 @@ func GetSourceSummary(c *gin.Context) {
|
|||
}
|
||||
|
||||
func ListSource(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry)
|
||||
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
|
||||
|
||||
sourceCreds, err := databaseRepo.GetSources(c)
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg"
|
||||
mock_config "github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config/mock"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Define the suite, and absorb the built-in basic suite
|
||||
// functionality from testify - including a T() method which
|
||||
// returns the current testing context
|
||||
type SourceHandlerTestSuite struct {
|
||||
suite.Suite
|
||||
MockCtrl *gomock.Controller
|
||||
TestDatabase *os.File
|
||||
|
||||
AppConfig *mock_config.MockInterface
|
||||
AppRepository database.DatabaseRepository
|
||||
}
|
||||
|
||||
// BeforeTest has a function to be executed right before the test starts and receives the suite and test names as input
|
||||
func (suite *SourceHandlerTestSuite) BeforeTest(suiteName, testName string) {
|
||||
suite.MockCtrl = gomock.NewController(suite.T())
|
||||
|
||||
dbFile, err := ioutil.TempFile("", fmt.Sprintf("%s.*.db", testName))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
suite.TestDatabase = dbFile
|
||||
|
||||
appConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
appConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
appConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
suite.AppConfig = appConfig
|
||||
|
||||
appRepo, err := database.NewRepository(suite.AppConfig, logrus.WithField("test", suite.T().Name()))
|
||||
suite.AppRepository = appRepo
|
||||
|
||||
appRepo.CreateUser(context.Background(), &models.User{
|
||||
Username: "test_username",
|
||||
Password: "test",
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// AfterTest has a function to be executed right after the test finishes and receives the suite and test names as input
|
||||
func (suite *SourceHandlerTestSuite) AfterTest(suiteName, testName string) {
|
||||
suite.MockCtrl.Finish()
|
||||
os.Remove(suite.TestDatabase.Name())
|
||||
}
|
||||
|
||||
// In order for 'go test' to run this suite, we need to create
|
||||
// a normal test function and pass our suite to suite.Run
|
||||
func TestSourceHandlerTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(SourceHandlerTestSuite))
|
||||
}
|
||||
|
||||
func (suite *SourceHandlerTestSuite) TestCreateManualSourceHandler() {
|
||||
//setup
|
||||
w := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(w)
|
||||
ctx.Set(pkg.ContextKeyTypeLogger, logrus.WithField("test", suite.T().Name()))
|
||||
ctx.Set(pkg.ContextKeyTypeDatabase, suite.AppRepository)
|
||||
ctx.Set(pkg.ContextKeyTypeConfig, suite.AppConfig)
|
||||
ctx.Set(pkg.ContextKeyTypeAuthUsername, "test_username")
|
||||
|
||||
//test
|
||||
file, err := os.Open("testdata/Tania553_Harris789_545c2380-b77f-4919-ab5d-0f615f877250.json")
|
||||
require.NoError(suite.T(), err)
|
||||
defer file.Close()
|
||||
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
part, _ := writer.CreateFormFile("file", filepath.Base(file.Name()))
|
||||
io.Copy(part, file)
|
||||
writer.Close()
|
||||
|
||||
req, err := http.NewRequest("POST", "/source/manual", body)
|
||||
require.NoError(suite.T(), err)
|
||||
req.Header.Add("Content-Type", writer.FormDataContentType())
|
||||
ctx.Request = req
|
||||
|
||||
CreateManualSource(ctx)
|
||||
|
||||
//assert
|
||||
require.Equal(suite.T(), http.StatusOK, w.Code)
|
||||
|
||||
type ResponseWrapper struct {
|
||||
Data struct {
|
||||
UpdatedResources []string `json:"UpdatedResources"`
|
||||
TotalResources int `json:"TotalResources"`
|
||||
} `json:"data"`
|
||||
Success bool `json:"success"`
|
||||
Source models.SourceCredential `json:"source"`
|
||||
}
|
||||
var respWrapper ResponseWrapper
|
||||
err = json.Unmarshal(w.Body.Bytes(), &respWrapper)
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
require.Equal(suite.T(), true, respWrapper.Success)
|
||||
require.Equal(suite.T(), "manual", string(respWrapper.Source.SourceType))
|
||||
require.Equal(suite.T(), 196, respWrapper.Data.TotalResources)
|
||||
summary, err := suite.AppRepository.GetSourceSummary(ctx, respWrapper.Source.ID.String())
|
||||
require.NoError(suite.T(), err)
|
||||
require.Equal(suite.T(), map[string]interface{}{
|
||||
"count": int64(5),
|
||||
"resource_type": "Condition",
|
||||
"source_id": respWrapper.Source.ID.String(),
|
||||
}, summary.ResourceTypeCounts[3])
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
@ -8,8 +9,8 @@ import (
|
|||
)
|
||||
|
||||
func GetSummary(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry)
|
||||
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
|
||||
|
||||
summary, err := databaseRepo.GetSummary(c)
|
||||
if err != nil {
|
||||
|
|
16198
backend/pkg/web/handler/testdata/Tania553_Harris789_545c2380-b77f-4919-ab5d-0f615f877250.json
vendored
Normal file
16198
backend/pkg/web/handler/testdata/Tania553_Harris789_545c2380-b77f-4919-ab5d-0f615f877250.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -3,6 +3,7 @@ package handler
|
|||
import (
|
||||
"github.com/fastenhealth/fasten-sources/clients/factory"
|
||||
sourcePkg "github.com/fastenhealth/fasten-sources/pkg"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -17,9 +18,10 @@ These Endpoints are only available when Fasten is deployed with allow_unsafe_end
|
|||
*/
|
||||
|
||||
func UnsafeRequestSource(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
appConfig := c.MustGet("CONFIG").(config.Interface)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry)
|
||||
appConfig := c.MustGet(pkg.ContextKeyTypeConfig).(config.Interface)
|
||||
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
|
||||
|
||||
//safety check incase this function is called in another way
|
||||
if !appConfig.GetBool("web.allow_unsafe_endpoints") {
|
||||
c.JSON(http.StatusServiceUnavailable, gin.H{"success": false})
|
||||
|
@ -79,3 +81,18 @@ func UnsafeRequestSource(c *gin.Context) {
|
|||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": resp})
|
||||
}
|
||||
|
||||
func UnsafeResourceGraph(c *gin.Context) {
|
||||
appConfig := c.MustGet(pkg.ContextKeyTypeConfig).(config.Interface)
|
||||
//safety check incase this function is called in another way
|
||||
if !appConfig.GetBool("web.allow_unsafe_endpoints") {
|
||||
c.JSON(http.StatusServiceUnavailable, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
|
||||
//!!!!!!INSECURE!!!!!!S
|
||||
//We're setting the username to a user provided value, this is insecure, but required for calling databaseRepo fns
|
||||
c.Set(pkg.ContextKeyTypeAuthUsername, c.Param("username"))
|
||||
|
||||
GetResourceFhirGraph(c)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func ConfigMiddleware(appConfig config.Interface) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Set("CONFIG", appConfig)
|
||||
c.Set(pkg.ContextKeyTypeConfig, appConfig)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -16,7 +17,7 @@ func RepositoryMiddleware(appConfig config.Interface, globalLogger logrus.FieldL
|
|||
|
||||
//TODO: determine where we can call defer deviceRepo.Close()
|
||||
return func(c *gin.Context) {
|
||||
c.Set("REPOSITORY", deviceRepo)
|
||||
c.Set(pkg.ContextKeyTypeDatabase, deviceRepo)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package middleware
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"io"
|
||||
|
@ -51,7 +52,7 @@ func LoggerMiddleware(logger *logrus.Entry) gin.HandlerFunc {
|
|||
path := c.Request.URL.Path
|
||||
blw := &responseBodyLogWriter{body: &bytes.Buffer{}, ResponseWriter: c.Writer}
|
||||
c.Writer = blw
|
||||
c.Set("LOGGER", logger)
|
||||
c.Set(pkg.ContextKeyTypeLogger, logger)
|
||||
start := time.Now()
|
||||
c.Next()
|
||||
stop := time.Since(start)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/auth"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -11,7 +12,7 @@ import (
|
|||
|
||||
func RequireAuth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
appConfig := c.MustGet("CONFIG").(config.Interface)
|
||||
appConfig := c.MustGet(pkg.ContextKeyTypeConfig).(config.Interface)
|
||||
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
authHeaderParts := strings.Split(authHeader, " ")
|
||||
|
@ -38,8 +39,8 @@ func RequireAuth() gin.HandlerFunc {
|
|||
}
|
||||
|
||||
//todo, is this shared between all sessions??
|
||||
c.Set("AUTH_TOKEN", tokenString)
|
||||
c.Set("AUTH_USERNAME", claim.Subject)
|
||||
c.Set(pkg.ContextKeyTypeAuthToken, tokenString)
|
||||
c.Set(pkg.ContextKeyTypeAuthUsername, claim.Subject)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ func (ae *AppEngine) Setup(logger *logrus.Entry) *gin.Engine {
|
|||
secure.GET("/resource/fhir", handler.ListResourceFhir)
|
||||
secure.GET("/resource/graph", handler.GetResourceFhirGraph)
|
||||
secure.GET("/resource/fhir/:sourceId/:resourceId", handler.GetResourceFhir)
|
||||
secure.POST("/resource/association", handler.ReplaceResourceAssociation)
|
||||
secure.POST("/resource/composition", handler.CreateResourceComposition)
|
||||
}
|
||||
|
||||
if ae.Config.GetBool("web.allow_unsafe_endpoints") {
|
||||
|
@ -80,6 +80,7 @@ func (ae *AppEngine) Setup(logger *logrus.Entry) *gin.Engine {
|
|||
{
|
||||
//http://localhost:9090/api/raw/test@test.com/436d7277-ad56-41ce-9823-44e353d1b3f6/Patient/smart-1288992
|
||||
unsafe.GET("/:username/:sourceId/*path", handler.UnsafeRequestSource)
|
||||
unsafe.GET("/:username/graph", handler.UnsafeResourceGraph)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ const routes: Routes = [
|
|||
{ path: 'dashboard', component: DashboardComponent, canActivate: [ IsAuthenticatedAuthGuard] },
|
||||
{ path: 'source/:source_id', component: SourceDetailComponent, canActivate: [ IsAuthenticatedAuthGuard] },
|
||||
{ path: 'source/:source_id/resource/:resource_id', component: ResourceDetailComponent, canActivate: [ IsAuthenticatedAuthGuard] },
|
||||
{ path: 'source/:source_id/resource/:resource_type/:resource_id', component: ResourceDetailComponent, canActivate: [ IsAuthenticatedAuthGuard] },
|
||||
{ path: 'sources', component: MedicalSourcesComponent, canActivate: [ IsAuthenticatedAuthGuard] },
|
||||
{ path: 'sources/callback/:source_type', component: MedicalSourcesComponent, canActivate: [ IsAuthenticatedAuthGuard] },
|
||||
|
||||
|
|
|
@ -160,6 +160,10 @@ export class ReportLabsObservationComponent implements OnInit {
|
|||
|
||||
let referenceRanges = []
|
||||
|
||||
//sort observations
|
||||
this.observations = this.observations.sort((a, b) => a.sort_date > b.sort_date ? -1 : a.sort_date < b.sort_date ? 1 : 0)
|
||||
|
||||
|
||||
for(let observation of this.observations){
|
||||
//get label
|
||||
this.barChartLabels.push(
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<div class="card card-dashboard-seven mb-3">
|
||||
<div class="card-header tx-medium">
|
||||
<div class="row cursor-pointer" routerLink="/source/{{condition?.source_id}}/resource/{{condition?.source_resource_id}}">
|
||||
<div class="row cursor-pointer" routerLink="/source/{{conditionDisplayModel?.source_id}}/resource/{{conditionDisplayModel?.source_resource_id}}">
|
||||
<!-- Condition Header -->
|
||||
<div class="col-6">
|
||||
{{condition | fhirPath: "Condition.code.text.first()":"Condition.code.coding.display.first()"}}
|
||||
{{conditionDisplayModel?.sort_title ? conditionDisplayModel?.sort_title : (conditionGroup | fhirPath: "Condition.code.text.first()":"Condition.code.coding.display.first()")}}
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{{condition | fhirPath: "Condition.onsetPeriod.start":"Condition.onsetDateTime" | date }} - {{condition | fhirPath: "Condition.onsetPeriod.end" | date}}
|
||||
{{conditionGroup | fhirPath: "Condition.onsetPeriod.start":"Condition.onsetDateTime" | date }} - {{conditionGroup | fhirPath: "Condition.onsetPeriod.end" | date}}
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- card-header -->
|
||||
|
@ -14,28 +14,20 @@
|
|||
|
||||
<div class="row">
|
||||
<!-- Condition Details -->
|
||||
|
||||
<!-- {{conditionDisplayModel | json}}-->
|
||||
<div class="col-6 mb-2">
|
||||
|
||||
<div class="row pl-3">
|
||||
<div *ngIf="involvedInCare.length > 0" class="row pl-3">
|
||||
<div class="col-12 mt-3 mb-2 tx-indigo">
|
||||
<p>Involved in Care</p>
|
||||
</div>
|
||||
<ng-container *ngFor="let careTeamEntry of careTeams | keyvalue">
|
||||
<ng-container *ngFor="let practitioner of involvedInCare">
|
||||
<div class="col-6">
|
||||
<strong>{{careTeamEntry.value | fhirPath: "CareTeam.participant.member.display"}}</strong>
|
||||
<strong>{{practitioner.displayName}}</strong>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{{careTeamEntry.value | fhirPath: "CareTeam.participant.role.text"}}
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngFor="let practitionerEntry of practitioners | keyvalue">
|
||||
<div class="col-6">
|
||||
<strong>{{practitionerEntry.value | fhirPath: "Practitioner.name.family"}}, {{practitionerEntry.value | fhirPath: "Practitioner.name.given"}}</strong>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{{practitionerEntry.value | fhirPath: "Practitioner.name.prefix"}}
|
||||
{{practitioner.role}}
|
||||
<!-- TODO: add email address link here -->
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
|
@ -48,12 +40,12 @@
|
|||
<!-- </div>-->
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="condition.related_resources.length > 0">
|
||||
<ng-container *ngIf="conditionGroup.related_resources.length > 0">
|
||||
<a class="cursor-pointer tx-indigo" (click)="collapse.toggle()">show all</a>
|
||||
<div #collapse="ngbCollapse" [ngbCollapse]="true">
|
||||
|
||||
<ul>
|
||||
<li class="cursor-pointer tx-indigo" *ngFor="let resource of condition.related_resources" routerLink="/source/{{resource?.source_id}}/resource/{{resource?.source_resource_id}}">Resource: {{resource.source_resource_type}}/{{resource.source_resource_id}}</li>
|
||||
<li class="cursor-pointer tx-indigo" *ngFor="let resourceEntry of resourcesLookup | keyvalue" [routerLink]="resourceEntry.key">{{resourceEntry.value.source_resource_type}} {{resourceEntry.value.sort_title ? '- '+resourceEntry.value.sort_title : '' }} </li>
|
||||
</ul>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
@ -62,50 +54,53 @@
|
|||
</div>
|
||||
<div class="col-6 bg-gray-100">
|
||||
<div class="row">
|
||||
<ng-container *ngFor="let encounter of condition.related_resources | filter:'source_resource_type':'Encounter'">
|
||||
|
||||
<ng-container *ngFor="let encounter of encounters">
|
||||
|
||||
<div routerLink="/source/{{encounter?.source_id}}/resource/{{encounter?.source_resource_id}}" class="col-6 mt-3 mb-2 tx-indigo">
|
||||
<strong>{{encounter | fhirPath: "Encounter.period.start" | date}}</strong>
|
||||
<strong>{{encounter.period_start | date}}</strong>
|
||||
</div>
|
||||
<div routerLink="/source/{{encounter?.source_id}}/resource/{{encounter?.source_resource_id}}" class="col-6 mt-3 mb-2 tx-indigo">
|
||||
<small>{{encounter | fhirPath: "Encounter.location.first().location.display"}}</small>
|
||||
<small>{{encounter.location_display }}</small>
|
||||
</div>
|
||||
|
||||
<div *ngIf="encounter.related_resources | filter:'source_resource_type':'MedicationRequest' as medications" class="col-12 mt-2 mb-2">
|
||||
<div *ngIf="encounter?.related_resources?.MedicationRequest || encounter?.related_resources?.Medication" class="col-12 mt-2 mb-2">
|
||||
<strong>Medications:</strong>
|
||||
<ul>
|
||||
<li routerLink="/source/{{medication?.source_id}}/resource/{{medication?.source_resource_id}}" *ngFor="let medication of medications">
|
||||
{{medication | fhirPath: "MedicationRequest.medicationReference.display":"MedicationRequest.medicationCodeableConcept.text"}}
|
||||
<li routerLink="/source/{{medication?.source_id}}/resource/{{medication?.source_resource_id}}" *ngFor="let medication of encounter?.related_resources?.MedicationRequest">
|
||||
{{medication.display }}
|
||||
</li>
|
||||
<li routerLink="/source/{{medication?.source_id}}/resource/{{medication?.source_resource_id}}" *ngFor="let medication of encounter?.related_resources?.Medication">
|
||||
{{medication.title}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div *ngIf="encounter.related_resources | filter:'source_resource_type':'Procedure' as procedures" class="col-12 mt-2 mb-2">
|
||||
<div *ngIf="encounter?.related_resources?.Procedure as procedures" class="col-12 mt-2 mb-2">
|
||||
<strong>Procedures:</strong>
|
||||
<ul>
|
||||
<li routerLink="/source/{{procedure?.source_id}}/resource/{{procedure?.source_resource_id}}" *ngFor="let procedure of procedures">
|
||||
{{procedure | fhirPath: "Procedure.code.text"}}
|
||||
{{procedure.display}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div *ngIf="encounter.related_resources | filter:'source_resource_type':'DiagnosticReport' as diagnosticReports" class="col-12 mt-2 mb-2">
|
||||
<div *ngIf="encounter?.related_resources?.DiagnosticReport as diagnosticReports" class="col-12 mt-2 mb-2">
|
||||
<strong>Tests and Examinations:</strong>
|
||||
<ul>
|
||||
<li routerLink="/source/{{diagnosticReport?.source_id}}/resource/{{diagnosticReport?.source_resource_id}}" *ngFor="let diagnosticReport of diagnosticReports">
|
||||
{{diagnosticReport | fhirPath: "DiagnosticReport.code.text":"DiagnosticReport.code.coding.display"}}
|
||||
{{diagnosticReport.title}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div *ngIf="encounter.related_resources | filter:'source_resource_type':'Device' as devices" class="col-12 mt-2 mb-2">
|
||||
<div *ngIf="encounter?.related_resources?.Device as devices" class="col-12 mt-2 mb-2">
|
||||
<strong>Device:</strong>
|
||||
<ul>
|
||||
<li routerLink="/source/{{device?.source_id}}/resource/{{device?.source_resource_id}}" *ngFor="let device of devices">
|
||||
{{device | fhirPath: "Device.code.text"}}
|
||||
{{device.model}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -1,52 +1,137 @@
|
|||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {ResourceFhir} from '../../models/fasten/resource_fhir';
|
||||
import {CareTeamModel} from '../../../lib/models/resources/care-team-model';
|
||||
import {PractitionerModel} from '../../../lib/models/resources/practitioner-model';
|
||||
import {EncounterModel} from '../../../lib/models/resources/encounter-model';
|
||||
import {fhirModelFactory} from '../../../lib/models/factory';
|
||||
import {fhirVersions, ResourceType} from '../../../lib/models/constants';
|
||||
import {MedicationModel} from '../../../lib/models/resources/medication-model';
|
||||
import {ProcedureModel} from '../../../lib/models/resources/procedure-model';
|
||||
import {DeviceModel} from '../../../lib/models/resources/device-model';
|
||||
import {DiagnosticReportModel} from '../../../lib/models/resources/diagnostic-report-model';
|
||||
import {FastenDisplayModel} from '../../../lib/models/fasten/fasten-display-model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-report-medical-history-condition',
|
||||
templateUrl: './report-medical-history-condition.component.html',
|
||||
styleUrls: ['./report-medical-history-condition.component.scss']
|
||||
})
|
||||
|
||||
export class ReportMedicalHistoryConditionComponent implements OnInit {
|
||||
|
||||
|
||||
@Input() condition: ResourceFhir
|
||||
/*
|
||||
* conditionGroup is either a Condition or Composite object
|
||||
*
|
||||
* Condition
|
||||
├─ related_resources
|
||||
│ ├─ Encounter1
|
||||
│ │ ├─ Location
|
||||
│ ├─ Encounter2
|
||||
│ │ ├─ Observation
|
||||
│ ├─ Observation
|
||||
│ ├─ Location
|
||||
*
|
||||
* or
|
||||
* Composite
|
||||
├─ related_resources
|
||||
│ ├─ Condition2
|
||||
│ ├─ Condition
|
||||
│ │ ├─ related_resources
|
||||
│ │ │ ├─ Encounter1
|
||||
│ │ │ │ ├─ Location
|
||||
│ │ │ ├─ Encounter2
|
||||
│ │ │ │ ├─ Observation
|
||||
│ │ │ ├─ Observation
|
||||
│ │ │ ├─ Location
|
||||
|
||||
careTeams: {[careTeamId: string]: ResourceFhir} = {}
|
||||
practitioners: {[practitionerId: string]: ResourceFhir} = {}
|
||||
encounters: {[encounterId: string]: ResourceFhir} = {}
|
||||
*
|
||||
* */
|
||||
@Input() conditionGroup: ResourceFhir
|
||||
|
||||
conditionDisplayModel: FastenDisplayModel
|
||||
|
||||
//lookup table for all resources
|
||||
resourcesLookup: {[name:string]: FastenDisplayModel} = {}
|
||||
|
||||
involvedInCare: {displayName: string, role?: string, email?: string}[] = []
|
||||
encounters: EncounterModel[] = []
|
||||
// medications: {[encounterResourceId: string]: MedicationModel[]} = {}
|
||||
// procedures: {[encounterResourceId: string]: ProcedureModel[]} = {}
|
||||
// diagnosticReports: {[encounterResourceId: string]: DiagnosticReportModel[]} = {}
|
||||
// device: {[encounterResourceId: string]: DeviceModel[]} = {}
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
for(let resource of this.condition.related_resources){
|
||||
this.recExtractResources(resource)
|
||||
//add resources to the lookup table, ensure uniqueness.
|
||||
this.conditionDisplayModel = this.recExtractResources(this.conditionGroup)
|
||||
|
||||
|
||||
//loop though all resources, process display data
|
||||
for(let resourceId in this.resourcesLookup){
|
||||
let resource = this.resourcesLookup[resourceId]
|
||||
|
||||
switch(resource.source_resource_type){
|
||||
case ResourceType.CareTeam:
|
||||
for(let participant of (resource as CareTeamModel).participants){
|
||||
this.involvedInCare.push({
|
||||
displayName: participant.display,
|
||||
role: participant.role
|
||||
})
|
||||
}
|
||||
break
|
||||
case ResourceType.Practitioner:
|
||||
this.involvedInCare.push({
|
||||
displayName: `${(resource as PractitionerModel).name?.family }, ${(resource as PractitionerModel).name?.given}`,
|
||||
role: `${(resource as PractitionerModel).name?.prefix || (resource as PractitionerModel).name?.suffix}`
|
||||
})
|
||||
break
|
||||
case ResourceType.Encounter:
|
||||
this.encounters.push(resource as EncounterModel)
|
||||
break
|
||||
}
|
||||
|
||||
// console.log("EXTRACTED CARETEAM", this.careTeams)
|
||||
// console.log("EXTRACTED practitioners", this.practitioners)
|
||||
// console.log("EXTRACTED encounters", this.encounters)
|
||||
}
|
||||
}
|
||||
|
||||
recExtractResources(resource: ResourceFhir){
|
||||
if(resource.source_resource_type == "CareTeam"){
|
||||
this.careTeams[this.genResourceId(resource)] = resource
|
||||
} else if (resource.source_resource_type == "Practitioner"){
|
||||
this.practitioners[this.genResourceId(resource)] = resource
|
||||
} else if (resource.source_resource_type == "Encounter"){
|
||||
this.encounters[this.genResourceId(resource)] = resource
|
||||
/*
|
||||
This function flattens all resources
|
||||
*/
|
||||
recExtractResources(resource: ResourceFhir): FastenDisplayModel{
|
||||
let resourceId = this.genResourceId(resource)
|
||||
let resourceDisplayModel: FastenDisplayModel = this.resourcesLookup[resourceId]
|
||||
|
||||
//ensure display model is populated
|
||||
if(!resourceDisplayModel){
|
||||
try{
|
||||
resourceDisplayModel = fhirModelFactory(resource.source_resource_type as ResourceType, resource)
|
||||
this.resourcesLookup[resourceId] = resourceDisplayModel
|
||||
}catch(e){
|
||||
console.error(e) //failed to parse a model
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(!resource.related_resources){
|
||||
return
|
||||
}
|
||||
return resourceDisplayModel
|
||||
} else {
|
||||
for(let relatedResource of resource.related_resources){
|
||||
this.recExtractResources(relatedResource)
|
||||
resourceDisplayModel.related_resources[relatedResource.source_resource_type] = resourceDisplayModel.related_resources[relatedResource.source_resource_type] || []
|
||||
|
||||
let relatedResourceDisplayModel = this.recExtractResources(relatedResource)
|
||||
if(relatedResourceDisplayModel){
|
||||
resourceDisplayModel.related_resources[relatedResource.source_resource_type].push(relatedResourceDisplayModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
return resourceDisplayModel
|
||||
}
|
||||
|
||||
genResourceId(relatedResource: ResourceFhir): string {
|
||||
return `${relatedResource.source_id}/${relatedResource.source_resource_type}/${relatedResource.source_resource_id}`
|
||||
return `/source/${relatedResource.source_id}/resource/${relatedResource.source_resource_type}/${relatedResource.source_resource_id}`
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,9 +3,53 @@
|
|||
<button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross click')"><span aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<tree-root [nodes]="nodes" [options]="options" (moveNode)="onResourceMoved($event)"></tree-root>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="alert alert-info" role="alert">
|
||||
<strong>Create Group</strong> If you have Conditions & Encounters from multiple healthcare providers that are related,
|
||||
you can group them together by using the checkboxes below. You can also provide a new name for this new group.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">Group Title</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" placeholder="provide a name for your new group" [(ngModel)]="compositionTitle" [disabled]="(selectedResources | keyvalue)?.length == 0" >
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<tree-root [nodes]="nodes" [options]="options">
|
||||
|
||||
<ng-template #treeNodeWrapperTemplate let-node let-index="index">
|
||||
<div class="node-wrapper" [style.padding-left]="node.getNodePadding()">
|
||||
<tree-node-checkbox *ngIf="node.data.show_checkbox"
|
||||
(click)="onResourceCheckboxClick($event, node)"
|
||||
[node]="node"></tree-node-checkbox>
|
||||
<tree-node-expander [node]="node"></tree-node-expander>
|
||||
<div class="node-content-wrapper"
|
||||
[class.node-content-wrapper-active]="node.isActive"
|
||||
[class.node-content-wrapper-focused]="node.isFocused"
|
||||
(click)="node.mouseAction('click', $event)"
|
||||
(dblclick)="node.mouseAction('dblClick', $event)"
|
||||
(contextmenu)="node.mouseAction('contextMenu', $event)"
|
||||
(treeDrop)="node.onDrop($event)"
|
||||
[treeAllowDrop]="node.allowDrop"
|
||||
[treeDrag]="node"
|
||||
[treeDragEnabled]="node.allowDrag()">
|
||||
|
||||
<tree-node-content [node]="node" [index]="index"></tree-node-content>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</tree-root>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-indigo" [disabled]="!compositionTitle" (click)="onMergeResourcesClick()">Merge Selected</button>
|
||||
<button type="button" class="btn btn-outline-dark" (click)="activeModal.close('Close click')">Close</button>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -3,14 +3,16 @@ import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
|||
import {ResourceFhir} from '../../models/fasten/resource_fhir';
|
||||
import {FastenApiService} from '../../services/fasten-api.service';
|
||||
import * as fhirpath from 'fhirpath';
|
||||
import {ITreeOptions} from '@circlon/angular-tree-component';
|
||||
|
||||
class RelatedNode {
|
||||
name: string
|
||||
resourceType: string
|
||||
resourceId: string
|
||||
sourceId: string
|
||||
draggable: boolean
|
||||
source_resource_type: string
|
||||
source_resource_id: string
|
||||
source_id: string
|
||||
children: RelatedNode[]
|
||||
show_checkbox: boolean
|
||||
resource: ResourceFhir
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
@ -21,9 +23,8 @@ class RelatedNode {
|
|||
export class ReportMedicalHistoryEditorComponent implements OnInit {
|
||||
|
||||
@Input() conditions: ResourceFhir[] = []
|
||||
@Input() encounters: ResourceFhir[] = []
|
||||
|
||||
assignedEncounters: {[name: string]: ResourceFhir} = {}
|
||||
resourceLookup: {[name: string]: ResourceFhir} = {}
|
||||
compositionTitle: string = ""
|
||||
|
||||
nodes = [
|
||||
// {
|
||||
|
@ -49,70 +50,62 @@ export class ReportMedicalHistoryEditorComponent implements OnInit {
|
|||
// ]
|
||||
// }
|
||||
];
|
||||
options = {
|
||||
allowDrag: (node) => {return node.data.draggable},
|
||||
allowDrop: (element, { parent, index }) => {
|
||||
// return true / false based on element, to.parent, to.index. e.g.
|
||||
return parent.data.resourceType == "Condition";
|
||||
},
|
||||
|
||||
options: ITreeOptions = {
|
||||
allowDrag: false,
|
||||
allowDrop: false,
|
||||
}
|
||||
|
||||
selectedResources:{ [id:string]: ResourceFhir} = {}
|
||||
constructor(
|
||||
public activeModal: NgbActiveModal,
|
||||
private fastenApi: FastenApiService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
console.log("ngOnInit STATUS", this.conditions)
|
||||
this.nodes = this.generateNodes(this.conditions)
|
||||
}
|
||||
|
||||
|
||||
onResourceMoved($event) {
|
||||
onResourceCheckboxClick($event, node:{data:RelatedNode}){
|
||||
let key = `${node.data.source_id}/${node.data.source_resource_type}/${node.data.source_resource_id}`
|
||||
if($event.target.checked){
|
||||
this.selectedResources[key] = node.data.resource
|
||||
if(!this.compositionTitle){
|
||||
this.compositionTitle = node.data.resource.sort_title
|
||||
}
|
||||
} else {
|
||||
//delete this key (unselected)
|
||||
delete this.selectedResources[key]
|
||||
}
|
||||
console.log("selected resources", this.selectedResources)
|
||||
}
|
||||
|
||||
this.fastenApi.replaceResourceAssociation({
|
||||
onMergeResourcesClick() {
|
||||
|
||||
source_id: $event.to.parent.sourceId,
|
||||
source_resource_type: $event.to.parent.resourceType,
|
||||
source_resource_id: $event.to.parent.resourceId,
|
||||
let resources: ResourceFhir[] = []
|
||||
for(let key in this.selectedResources){
|
||||
resources.push(this.selectedResources[key])
|
||||
}
|
||||
|
||||
new_related_source_id: $event.node.sourceId,
|
||||
new_related_source_resource_type: $event.node.resourceType,
|
||||
new_related_source_resource_id: $event.node.resourceId,
|
||||
|
||||
|
||||
}).subscribe(results => {
|
||||
this.fastenApi.createResourceComposition(this.compositionTitle, resources).subscribe(results => {
|
||||
console.log(results)
|
||||
})
|
||||
this.activeModal.close()
|
||||
},(err) => {})
|
||||
}
|
||||
|
||||
|
||||
|
||||
generateNodes(resouceFhirList: ResourceFhir[]): RelatedNode[] {
|
||||
let relatedNodes = resouceFhirList.map((resourceFhir) => { return this.recGenerateNode(resourceFhir) })
|
||||
|
||||
//create an unassigned encounters "condition"
|
||||
if(this.encounters.length > 0){
|
||||
let unassignedCondition = {
|
||||
name: "[Unassigned Encounters]",
|
||||
resourceType: "Condition",
|
||||
resourceId: "UNASSIGNED",
|
||||
sourceId: "UNASSIGNED",
|
||||
draggable: false,
|
||||
children: []
|
||||
for(let relatedNode of relatedNodes){
|
||||
if(relatedNode.source_id == 'UNASSIGNED' && relatedNode.source_resource_type == 'Condition' && relatedNode.source_resource_id == 'UNASSIGNED'){
|
||||
//this is a placeholder for the Unassigned resources. This resource cannot be merged, but all child resources can be, so lets set them to true
|
||||
for(let unassignedEncounters of relatedNode.children){
|
||||
unassignedEncounters.show_checkbox = true
|
||||
}
|
||||
|
||||
for(let encounter of this.encounters){
|
||||
let encounterId = `${encounter.source_id}/${encounter.source_resource_type}/${encounter.source_resource_id}`
|
||||
if(!this.assignedEncounters[encounterId]){
|
||||
this.assignedEncounters[encounterId] = encounter
|
||||
unassignedCondition.children.push(this.recGenerateNode(encounter))
|
||||
}
|
||||
}
|
||||
|
||||
if(unassignedCondition.children.length > 0){
|
||||
//only add the unassigned condition block if the subchildren list is populated.
|
||||
relatedNodes.push(unassignedCondition)
|
||||
} else {
|
||||
relatedNode.show_checkbox = true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,39 +115,48 @@ export class ReportMedicalHistoryEditorComponent implements OnInit {
|
|||
|
||||
recGenerateNode(resourceFhir: ResourceFhir): RelatedNode {
|
||||
let relatedNode = {
|
||||
sourceId: resourceFhir.source_id,
|
||||
name: `[${resourceFhir.source_resource_type}/${resourceFhir.source_resource_id}]`,
|
||||
resourceId: resourceFhir.source_resource_id,
|
||||
resourceType: resourceFhir.source_resource_type,
|
||||
draggable: resourceFhir.source_resource_type == "Encounter" || resourceFhir.source_resource_type == "Condition",
|
||||
show_checkbox: false,
|
||||
source_id: resourceFhir.source_id,
|
||||
name: `[${resourceFhir.source_resource_type}/${resourceFhir.source_resource_id.length > 10 ? resourceFhir.source_resource_id.substring(0, 10)+ '...' : resourceFhir.source_resource_id}] `,
|
||||
source_resource_id: resourceFhir.source_resource_id,
|
||||
source_resource_type: resourceFhir.source_resource_type,
|
||||
children: [],
|
||||
resource: resourceFhir
|
||||
}
|
||||
|
||||
switch (resourceFhir.source_resource_type) {
|
||||
case "Condition":
|
||||
relatedNode.name += ` ${fhirpath.evaluate(resourceFhir.resource_raw, "Condition.onsetPeriod.start")} ${fhirpath.evaluate(resourceFhir.resource_raw, "Condition.code.text.first()")}`
|
||||
relatedNode.name += resourceFhir.sort_title || ` ${fhirpath.evaluate(resourceFhir.resource_raw, "Condition.onsetPeriod.start")} ${fhirpath.evaluate(resourceFhir.resource_raw, "Condition.code.text.first()")}`
|
||||
if(resourceFhir.sort_date){
|
||||
relatedNode.name += ` - ${new Date(resourceFhir.sort_date).toLocaleDateString("en-US")}`
|
||||
}
|
||||
break
|
||||
case "Encounter":
|
||||
relatedNode.name += ` ${fhirpath.evaluate(resourceFhir.resource_raw, "Encounter.period.start")} ${fhirpath.evaluate(resourceFhir.resource_raw, "Encounter.location.first().location.display")}`
|
||||
relatedNode.name += resourceFhir.sort_title ||` ${fhirpath.evaluate(resourceFhir.resource_raw, "Encounter.period.start")} ${fhirpath.evaluate(resourceFhir.resource_raw, "Encounter.location.first().location.display")}`
|
||||
if(resourceFhir.sort_date){
|
||||
relatedNode.name += ` - ${new Date(resourceFhir.sort_date).toLocaleDateString("en-US")}`
|
||||
}
|
||||
break
|
||||
case "CareTeam":
|
||||
relatedNode.name += ` ${fhirpath.evaluate(resourceFhir.resource_raw, "CareTeam.participant.member.display")}`
|
||||
relatedNode.name += resourceFhir.sort_title || ` ${fhirpath.evaluate(resourceFhir.resource_raw, "CareTeam.participant.member.display")}`
|
||||
break
|
||||
case "Location":
|
||||
relatedNode.name += ` ${fhirpath.evaluate(resourceFhir.resource_raw, "Location.name")}`
|
||||
relatedNode.name += resourceFhir.sort_title || ` ${fhirpath.evaluate(resourceFhir.resource_raw, "Location.name")}`
|
||||
break
|
||||
case "Organization":
|
||||
relatedNode.name += ` ${fhirpath.evaluate(resourceFhir.resource_raw, "Organization.name")}`
|
||||
relatedNode.name += resourceFhir.sort_title || ` ${fhirpath.evaluate(resourceFhir.resource_raw, "Organization.name")}`
|
||||
break
|
||||
case "Practitioner":
|
||||
relatedNode.name += ` ${fhirpath.evaluate(resourceFhir.resource_raw, "Practitioner.name.family")}`
|
||||
relatedNode.name += resourceFhir.sort_title || ` ${fhirpath.evaluate(resourceFhir.resource_raw, "Practitioner.name.family")}`
|
||||
break
|
||||
case "MedicationRequest":
|
||||
relatedNode.name += ` ${fhirpath.evaluate(resourceFhir.resource_raw, "MedicationRequest.medicationReference.display")}`
|
||||
relatedNode.name += resourceFhir.sort_title || ` ${fhirpath.evaluate(resourceFhir.resource_raw, "MedicationRequest.medicationReference.display")}`
|
||||
break
|
||||
default:
|
||||
relatedNode.name += resourceFhir.sort_title
|
||||
}
|
||||
|
||||
this.assignedEncounters[`${resourceFhir.source_id}/${resourceFhir.source_resource_type}/${resourceFhir.source_resource_id}`] = resourceFhir
|
||||
this.resourceLookup[`${resourceFhir.source_id}/${resourceFhir.source_resource_type}/${resourceFhir.source_resource_id}`] = resourceFhir
|
||||
|
||||
if(!resourceFhir.related_resources){
|
||||
return relatedNode
|
||||
|
|
|
@ -43,6 +43,7 @@ import { ReportMedicalHistoryConditionComponent } from './report-medical-history
|
|||
import { ReportLabsObservationComponent } from './report-labs-observation/report-labs-observation.component';
|
||||
import { ChartsModule } from 'ng2-charts';
|
||||
import { LoadingSpinnerComponent } from './loading-spinner/loading-spinner.component';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -50,6 +51,7 @@ import { LoadingSpinnerComponent } from './loading-spinner/loading-spinner.compo
|
|||
BrowserModule,
|
||||
NgxDatatableModule,
|
||||
NgbModule,
|
||||
FormsModule,
|
||||
MomentModule,
|
||||
TreeModule,
|
||||
ChartsModule
|
||||
|
|
|
@ -8,6 +8,9 @@ export class ResourceFhir {
|
|||
resource_raw: IResourceRaw
|
||||
related_resources?: ResourceFhir[] = []
|
||||
|
||||
sort_title: string = ""
|
||||
sort_date: Date = null
|
||||
|
||||
constructor(object?: any) {
|
||||
return Object.assign(this, object)
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
</div>
|
||||
|
||||
<!-- Condition List -->
|
||||
<app-report-medical-history-condition *ngFor="let condition of conditions; let i = index" [condition]="condition"></app-report-medical-history-condition>
|
||||
<app-report-medical-history-condition *ngFor="let condition of conditions; let i = index" [conditionGroup]="condition"></app-report-medical-history-condition>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #emptyReport>
|
||||
|
|
|
@ -52,7 +52,7 @@ export class MedicalHistoryComponent implements OnInit {
|
|||
},
|
||||
source_id: 'UNASSIGNED',
|
||||
source_resource_id: 'UNASSIGNED',
|
||||
source_resource_type: 'UNASSIGNED',
|
||||
source_resource_type: 'Condition',
|
||||
related_resources: this.unassigned_encounters
|
||||
} as any)
|
||||
}
|
||||
|
@ -65,9 +65,10 @@ export class MedicalHistoryComponent implements OnInit {
|
|||
}
|
||||
|
||||
openEditorRelated(): void {
|
||||
const modalRef = this.modalService.open(ReportMedicalHistoryEditorComponent);
|
||||
const modalRef = this.modalService.open(ReportMedicalHistoryEditorComponent, {
|
||||
size: 'xl',
|
||||
});
|
||||
modalRef.componentInstance.conditions = this.conditions;
|
||||
modalRef.componentInstance.encounters = this.unassigned_encounters;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import {FastenApiService} from '../../services/fasten-api.service';
|
|||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {ResourceFhir} from '../../models/fasten/resource_fhir';
|
||||
import {fhirModelFactory} from '../../../lib/models/factory';
|
||||
import {ResourceType} from '../../../lib/models/constants';
|
||||
|
||||
@Component({
|
||||
selector: 'app-resource-detail',
|
||||
|
@ -29,7 +30,7 @@ export class ResourceDetailComponent implements OnInit {
|
|||
this.sourceName = "unknown" //TODO popualte this
|
||||
|
||||
try{
|
||||
let parsed = fhirModelFactory(resourceFhir["source_resource_type"], resourceFhir["resource_raw"])
|
||||
let parsed = fhirModelFactory(resourceFhir["source_resource_type"] as ResourceType, resourceFhir)
|
||||
console.log("Successfully parsed model", parsed)
|
||||
} catch (e) {
|
||||
console.log("FAILED TO PARSE", resourceFhir)
|
||||
|
|
|
@ -148,8 +148,12 @@ export class FastenApiService {
|
|||
);
|
||||
}
|
||||
|
||||
replaceResourceAssociation(resourceAssociation: ResourceAssociation): Observable<any> {
|
||||
return this._httpClient.post<any>(`${GetEndpointAbsolutePath(globalThis.location, environment.fasten_api_endpoint_base)}/secure/resource/association`, resourceAssociation)
|
||||
//this method allows a user to manually group related FHIR resources together (conditions, encounters, etc).
|
||||
createResourceComposition(title: string, resources: ResourceFhir[]){
|
||||
return this._httpClient.post<any>(`${GetEndpointAbsolutePath(globalThis.location, environment.fasten_api_endpoint_base)}/secure/resource/composition`, {
|
||||
"resources": resources,
|
||||
"title": title,
|
||||
})
|
||||
.pipe(
|
||||
map((response: ResponseWrapper) => {
|
||||
console.log("RESPONSE", response)
|
||||
|
|
|
@ -1,4 +1,37 @@
|
|||
|
||||
export enum ResourceType {
|
||||
AdverseEvent = "AdverseEvent",
|
||||
AllergyIntolerance = "AllergyIntolerance",
|
||||
Appointment = "Appointment",
|
||||
Binary = "Binary",
|
||||
CarePlan = "CarePlan",
|
||||
CareTeam = "CareTeam",
|
||||
Condition = "Condition",
|
||||
Composition = "Composition",
|
||||
Coverage = "Coverage",
|
||||
Device = "Device",
|
||||
DiagnosticReport = "DiagnosticReport",
|
||||
DocumentReference = "DocumentReference",
|
||||
Encounter = "Encounter",
|
||||
Goal = "Goal",
|
||||
Immunization = "Immunization",
|
||||
Location = "Location",
|
||||
Medication = "Medication",
|
||||
MedicationDispense = "MedicationDispense",
|
||||
MedicationRequest = "MedicationRequest",
|
||||
Observation = "Observation",
|
||||
Organization = "Organization",
|
||||
Patient = "Patient",
|
||||
Practitioner = "Practitioner",
|
||||
PractitionerRole = "PractitionerRole",
|
||||
Procedure = "Procedure",
|
||||
Provenance = "Provenance",
|
||||
RelatedPerson = "RelatedPerson",
|
||||
ResearchStudy = "ResearchStudy",
|
||||
ServiceRequest = "ServiceRequest",
|
||||
Specimen = "Specimen",
|
||||
}
|
||||
|
||||
|
||||
export enum fhirVersions{
|
||||
DSTU2 = "DSTU2",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {fhirVersions} from './constants';
|
||||
import {fhirVersions, ResourceType} from './constants';
|
||||
import {AdverseEventModel} from './resources/adverse-event-model';
|
||||
import {AllergyIntoleranceModel} from './resources/allergy-intolerance-model';
|
||||
import {AppointmentModel} from './resources/appointment-model';
|
||||
|
@ -22,126 +22,144 @@ import {PractitionerRoleModel} from './resources/practitioner-role-model';
|
|||
import {ProcedureModel} from './resources/procedure-model';
|
||||
import {RelatedPersonModel} from './resources/related-person-model';
|
||||
import {ResearchStudyModel} from './resources/research-study-model';
|
||||
import {BinaryModel} from './resources/binary-model';
|
||||
import {FastenOptions} from './fasten/fasten-options';
|
||||
import {FastenDisplayModel} from './fasten/fasten-display-model';
|
||||
import {MedicationRequestModel} from './resources/medication-request-model';
|
||||
|
||||
export function fhirModelFactory(modelName: string, fhirResource: any, fhirVersion: fhirVersions = fhirVersions.R4):any {
|
||||
switch (modelName) {
|
||||
case "AdverseEvent": {
|
||||
return new AdverseEventModel(fhirResource, fhirVersion)
|
||||
}
|
||||
case "AllergyIntolerance": {
|
||||
return new AllergyIntoleranceModel(fhirResource, fhirVersion)
|
||||
}
|
||||
case "Appointment": {
|
||||
return new AppointmentModel(fhirResource, fhirVersion)
|
||||
}
|
||||
case "Binary": {
|
||||
return new BinaryModel(fhirResource, fhirVersion)
|
||||
}
|
||||
case "CarePlan": {
|
||||
return new CarePlanModel(fhirResource, fhirVersion)
|
||||
}
|
||||
case "CareTeam": {
|
||||
return new CareTeamModel(fhirResource, fhirVersion)
|
||||
}
|
||||
// case "Claim": {
|
||||
// return new ClaimModel(fhirResource, fhirVersion)
|
||||
// }
|
||||
// case "ClaimResponse": {
|
||||
// return new ClaimResponseModel(fhirResource, fhirVersion)
|
||||
// }
|
||||
case "Condition": {
|
||||
return new ConditionModel(fhirResource, fhirVersion)
|
||||
}
|
||||
// case "Coverage": {
|
||||
// return new CoverageModel(fhirResource, fhirVersion)
|
||||
// }
|
||||
case "Device": {
|
||||
return new DeviceModel(fhirResource, fhirVersion)
|
||||
}
|
||||
case "DiagnosticReport": {
|
||||
return new DiagnosticReportModel(fhirResource, fhirVersion)
|
||||
}
|
||||
case "DocumentReference": {
|
||||
return new DocumentReferenceModel(fhirResource, fhirVersion)
|
||||
}
|
||||
case "Encounter": {
|
||||
return new EncounterModel(fhirResource, fhirVersion)
|
||||
}
|
||||
// case "ExplanationOfBenefit": {
|
||||
// return new ExplanationOfBenefitModel(fhirResource, fhirVersion)
|
||||
// }
|
||||
// case "FamilyMemberHistory": {
|
||||
// return new FamilyMemberHistoryModel(fhirResource, fhirVersion)
|
||||
// }
|
||||
case "Goal": {
|
||||
return new GoalModel(fhirResource, fhirVersion)
|
||||
}
|
||||
case "Immunization": {
|
||||
return new ImmunizationModel(fhirResource, fhirVersion)
|
||||
}
|
||||
case "Location": {
|
||||
return new LocationModel(fhirResource, fhirVersion)
|
||||
}
|
||||
case "Medication": {
|
||||
return new MedicationModel(fhirResource, fhirVersion)
|
||||
}
|
||||
// case "MedicationAdministration": {
|
||||
// return new MedicationAdministrationModel(fhirResource, fhirVersion)
|
||||
// }
|
||||
case "MedicationDispense": {
|
||||
return new MedicationDispenseModel(fhirResource, fhirVersion)
|
||||
}
|
||||
// case "MedicationKnowledge": {
|
||||
// return new MedicationKnowledgeModel(fhirResource, fhirVersion)
|
||||
// }
|
||||
// case "MedicationOrder": {
|
||||
// return new MedicationOrderModel(fhirResource, fhirVersion)
|
||||
// }
|
||||
// case "MedicationRequest": {
|
||||
// return new MedicationRequestModel(fhirResource, fhirVersion)
|
||||
// }
|
||||
// case "MedicationStatement": {
|
||||
// return new MedicationStatementModel(fhirResource, fhirVersion)
|
||||
// }
|
||||
case "Observation": {
|
||||
return new ObservationModel(fhirResource, fhirVersion)
|
||||
}
|
||||
case "Organization": {
|
||||
return new OrganizationModel(fhirResource, fhirVersion)
|
||||
}
|
||||
case "Patient": {
|
||||
return new PatientModel(fhirResource, fhirVersion)
|
||||
}
|
||||
case "Practitioner": {
|
||||
return new PractitionerModel(fhirResource, fhirVersion)
|
||||
}
|
||||
case "PractitionerRole": {
|
||||
return new PractitionerRoleModel(fhirResource, fhirVersion)
|
||||
}
|
||||
case "Procedure": {
|
||||
return new ProcedureModel(fhirResource, fhirVersion)
|
||||
}
|
||||
// case "Questionnaire": {
|
||||
// return new QuestionnaireModel(fhirResource, fhirVersion)
|
||||
// }
|
||||
// case "QuestionnaireResponse": {
|
||||
// return new QuestionnaireResponseModel(fhirResource, fhirVersion)
|
||||
// }
|
||||
// case "ReferralRequest": {
|
||||
// return new ReferralRequestModel(fhirResource, fhirVersion)
|
||||
// }
|
||||
case "RelatedPerson": {
|
||||
return new RelatedPersonModel(fhirResource, fhirVersion)
|
||||
}
|
||||
case "ResearchStudy": {
|
||||
return new ResearchStudyModel(fhirResource, fhirVersion)
|
||||
}
|
||||
// case "ResourceCategory": {
|
||||
// return new ResourceCategoryModel(fhirResource, fhirVersion)
|
||||
// import {BinaryModel} from './resources/binary-model';
|
||||
|
||||
export function fhirModelFactory(modelResourceType: ResourceType, fhirResourceWrapper: any, fhirVersion: fhirVersions = fhirVersions.R4, fastenOptions?: FastenOptions): FastenDisplayModel {
|
||||
|
||||
let resourceModel: FastenDisplayModel
|
||||
switch (modelResourceType) {
|
||||
case "AdverseEvent":
|
||||
resourceModel = new AdverseEventModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
case "AllergyIntolerance":
|
||||
resourceModel = new AllergyIntoleranceModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
case "Appointment":
|
||||
resourceModel = new AppointmentModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
|
||||
// case "Binary": {
|
||||
// resourceModel = new BinaryModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
// }
|
||||
case "CarePlan":
|
||||
resourceModel = new CarePlanModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
case "CareTeam":
|
||||
resourceModel = new CareTeamModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
// case "Claim":
|
||||
// resourceModel = new ClaimModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
// break
|
||||
// case "ClaimResponse":
|
||||
// resourceModel = new ClaimResponseModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
// break
|
||||
case "Condition":
|
||||
resourceModel = new ConditionModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
case "Composition":
|
||||
resourceModel = new ConditionModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
// case "Coverage":
|
||||
// resourceModel = new CoverageModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
// break
|
||||
case "Device":
|
||||
resourceModel = new DeviceModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
case "DiagnosticReport":
|
||||
resourceModel = new DiagnosticReportModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
case "DocumentReference":
|
||||
resourceModel = new DocumentReferenceModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
case "Encounter":
|
||||
resourceModel = new EncounterModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
// case "ExplanationOfBenefit":
|
||||
// resourceModel = new ExplanationOfBenefitModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
// break
|
||||
// case "FamilyMemberHistory":
|
||||
// resourceModel = new FamilyMemberHistoryModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
//break
|
||||
case "Goal":
|
||||
resourceModel = new GoalModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
case "Immunization":
|
||||
resourceModel = new ImmunizationModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
case "Location":
|
||||
resourceModel = new LocationModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
case "Medication":
|
||||
resourceModel = new MedicationModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
// case "MedicationAdministration":
|
||||
// resourceModel = new MedicationAdministrationModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
// break
|
||||
case "MedicationDispense":
|
||||
resourceModel = new MedicationDispenseModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
// case "MedicationKnowledge":
|
||||
// resourceModel = new MedicationKnowledgeModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
// break
|
||||
// case "MedicationOrder":
|
||||
// resourceModel = new MedicationOrderModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
// break
|
||||
case "MedicationRequest":
|
||||
resourceModel = new MedicationRequestModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
// case "MedicationStatement":
|
||||
// resourceModel = new MedicationStatementModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
// break
|
||||
case "Observation":
|
||||
resourceModel = new ObservationModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
case "Organization":
|
||||
resourceModel = new OrganizationModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
case "Patient":
|
||||
resourceModel = new PatientModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
case "Practitioner":
|
||||
resourceModel = new PractitionerModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
case "PractitionerRole":
|
||||
resourceModel = new PractitionerRoleModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
case "Procedure":
|
||||
resourceModel = new ProcedureModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
// case "Questionnaire":
|
||||
// resourceModel = new QuestionnaireModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
// break
|
||||
// case "QuestionnaireResponse":
|
||||
// resourceModel = new QuestionnaireResponseModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
// break
|
||||
// case "ReferralRequest":
|
||||
// resourceModel = new ReferralRequestModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
// break
|
||||
case "RelatedPerson":
|
||||
resourceModel = new RelatedPersonModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
case "ResearchStudy":
|
||||
resourceModel = new ResearchStudyModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
break
|
||||
// case "ResourceCategory":
|
||||
// resourceModel = new ResourceCategoryModel(fhirResourceWrapper.resource_raw, fhirVersion, fastenOptions)
|
||||
// break
|
||||
default: {
|
||||
throw new Error("Unknown resource data structure")
|
||||
throw new Error("Ignoring Unknown resource data structure:" + modelResourceType)
|
||||
}
|
||||
}
|
||||
|
||||
//transfer data from wrapper to the display model.
|
||||
resourceModel.source_resource_id = fhirResourceWrapper.source_resource_id
|
||||
resourceModel.source_id = fhirResourceWrapper.source_id
|
||||
resourceModel.sort_title = fhirResourceWrapper.sort_title
|
||||
resourceModel.sort_date = fhirResourceWrapper.sort_date
|
||||
|
||||
return resourceModel
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import { FastenDisplayModel } from './fasten-display-model';
|
||||
|
||||
describe('FastenDisplayModel', () => {
|
||||
it('should create an instance', () => {
|
||||
expect(new FastenDisplayModel()).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
import {FastenOptions} from './fasten-options';
|
||||
import {ResourceType} from '../constants';
|
||||
|
||||
export class FastenDisplayModel {
|
||||
source_resource_type: ResourceType | undefined
|
||||
source_resource_id: string | undefined
|
||||
source_id: string | undefined
|
||||
sort_title: string | undefined
|
||||
sort_date: Date | undefined
|
||||
|
||||
related_resources: {[ modelResourceType: string]: FastenDisplayModel[]} = {}
|
||||
|
||||
constructor(options?: FastenOptions) {}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { FastenOptions } from './fasten-options';
|
||||
|
||||
describe('FastenOptions', () => {
|
||||
it('should create an instance', () => {
|
||||
expect(new FastenOptions()).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,2 @@
|
|||
export class FastenOptions {
|
||||
}
|
|
@ -20,7 +20,7 @@ describe('AdverseEventModel', () => {
|
|||
// expected.hasEventType = true
|
||||
expected.date = "2017-01-29T12:34:56+00:00"
|
||||
expected.seriousness = new CodableConceptModel({ coding: [ Object({ system: 'http://terminology.hl7.org/CodeSystem/adverse-event-seriousness', code: 'Non-serious', display: 'Non-serious' }) ] })
|
||||
expected.hasSeriousness = true
|
||||
expected.has_seriousness = true
|
||||
expected.actuality = 'actual'
|
||||
expected.event = new CodableConceptModel({
|
||||
"coding": [
|
||||
|
@ -32,7 +32,7 @@ describe('AdverseEventModel', () => {
|
|||
],
|
||||
"text": "This was a mild rash on the left forearm"
|
||||
})
|
||||
expected.hasEvent = true
|
||||
expected.has_event = true
|
||||
|
||||
expect(new AdverseEventModel(fixture)).toEqual(expected);
|
||||
});
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class AdverseEventModel {
|
||||
export class AdverseEventModel extends FastenDisplayModel {
|
||||
subject: ReferenceModel | undefined
|
||||
description: string | undefined
|
||||
eventType: string | undefined
|
||||
hasEventType: boolean | undefined
|
||||
event_type: string | undefined
|
||||
has_event_type: boolean | undefined
|
||||
date: string | undefined
|
||||
seriousness: CodableConceptModel | undefined
|
||||
hasSeriousness: boolean | undefined
|
||||
has_seriousness: boolean | undefined
|
||||
actuality: string | undefined
|
||||
event: CodableConceptModel | undefined
|
||||
hasEvent: boolean | undefined
|
||||
has_event: boolean | undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.AdverseEvent
|
||||
this.resourceDTO(fhirResource, fhirVersion || fhirVersions.R4);
|
||||
}
|
||||
|
||||
|
@ -25,20 +29,20 @@ export class AdverseEventModel {
|
|||
this.date = _.get(fhirResource, 'date');
|
||||
let seriousness = _.get(fhirResource, 'seriousness', [])
|
||||
this.seriousness = new CodableConceptModel(seriousness);
|
||||
this.hasSeriousness = hasValue(seriousness);
|
||||
this.has_seriousness = hasValue(seriousness);
|
||||
};
|
||||
|
||||
stu3DTO(fhirResource:any){
|
||||
this.description = _.get(fhirResource, 'description');
|
||||
this.eventType = _.get(fhirResource, 'type', []);
|
||||
this.hasEventType = hasValue(this.eventType);
|
||||
this.event_type = _.get(fhirResource, 'type', []);
|
||||
this.has_event_type = hasValue(this.event_type);
|
||||
};
|
||||
|
||||
r4DTO(fhirResource:any){
|
||||
this.actuality = _.get(fhirResource, 'actuality');
|
||||
let event = _.get(fhirResource, 'event', [])
|
||||
this.event = new CodableConceptModel(event);
|
||||
this.hasEvent = hasValue(event);
|
||||
this.has_event = hasValue(event);
|
||||
};
|
||||
|
||||
resourceDTO(fhirResource: any, fhirVersion: fhirVersions){
|
||||
|
|
|
@ -13,8 +13,8 @@ describe('AllergyIntoleranceModel', () => {
|
|||
let expected = new AllergyIntoleranceModel({})
|
||||
expected.title = 'Cashew nuts'
|
||||
expected.status = 'Confirmed'
|
||||
expected.recordedDate = '2014-10-09T14:58:00+11:00'
|
||||
expected.substanceCoding = [
|
||||
expected.recorded_date = '2014-10-09T14:58:00+11:00'
|
||||
expected.substance_coding = [
|
||||
{
|
||||
"system": "http://www.nlm.nih.gov/research/umls/rxnorm",
|
||||
"code": "1160593",
|
||||
|
@ -35,8 +35,8 @@ describe('AllergyIntoleranceModel', () => {
|
|||
let expected = new AllergyIntoleranceModel({})
|
||||
expected.title = 'Penicillin G'
|
||||
expected.status = 'Unconfirmed'
|
||||
expected.recordedDate = '2010-03-01'
|
||||
expected.substanceCoding = []
|
||||
expected.recorded_date = '2010-03-01'
|
||||
expected.substance_coding = []
|
||||
// expected.asserter = { reference: 'Patient/example' }
|
||||
// expected.note = [{ text: 'The criticality is high becasue of the observed anaphylactic reaction when challenged with cashew extract.' }]
|
||||
// expected.type = ''
|
||||
|
@ -51,8 +51,8 @@ describe('AllergyIntoleranceModel', () => {
|
|||
let expected = new AllergyIntoleranceModel({})
|
||||
expected.title = 'No Known Allergy (situation)'
|
||||
expected.status = 'Confirmed'
|
||||
expected.recordedDate = '2015-08-06T15:37:31-06:00'
|
||||
expected.substanceCoding = []
|
||||
expected.recorded_date = '2015-08-06T15:37:31-06:00'
|
||||
expected.substance_coding = []
|
||||
// expected.asserter = { reference: 'Patient/example' }
|
||||
// expected.note = [{ text: 'The criticality is high becasue of the observed anaphylactic reaction when challenged with cashew extract.' }]
|
||||
// expected.type = 'allergy'
|
||||
|
@ -69,8 +69,8 @@ describe('AllergyIntoleranceModel', () => {
|
|||
let expected = new AllergyIntoleranceModel({})
|
||||
expected.title = "ALLERGENIC EXTRACT, PENICILLIN"
|
||||
expected.status = 'unconfirmed'
|
||||
expected.recordedDate = "2010-03-01"
|
||||
expected.substanceCoding = [
|
||||
expected.recorded_date = "2010-03-01"
|
||||
expected.substance_coding = [
|
||||
{
|
||||
"system": "http://www.nlm.nih.gov/research/umls/rxnorm",
|
||||
"code": "314422",
|
||||
|
@ -91,8 +91,8 @@ describe('AllergyIntoleranceModel', () => {
|
|||
let expected = new AllergyIntoleranceModel({})
|
||||
expected.title = 'PENICILLINS'
|
||||
expected.status = 'confirmed'
|
||||
expected.recordedDate = '2008-02-22T06:00:00.000Z'
|
||||
expected.substanceCoding = [
|
||||
expected.recorded_date = '2008-02-22T06:00:00.000Z'
|
||||
expected.substance_coding = [
|
||||
{
|
||||
"system": 'http://hl7.org/fhir/ndfrt' ,
|
||||
"code": 'N0000005840' ,
|
||||
|
@ -116,8 +116,8 @@ describe('AllergyIntoleranceModel', () => {
|
|||
let expected = new AllergyIntoleranceModel({})
|
||||
expected.title = 'Cashew nuts'
|
||||
expected.status = 'confirmed'
|
||||
expected.recordedDate = '2014-10-09T14:58:00+11:00'
|
||||
expected.substanceCoding = [
|
||||
expected.recorded_date = '2014-10-09T14:58:00+11:00'
|
||||
expected.substance_coding = [
|
||||
{
|
||||
"system": "http://www.nlm.nih.gov/research/umls/rxnorm",
|
||||
"code": "1160593",
|
||||
|
@ -138,8 +138,8 @@ describe('AllergyIntoleranceModel', () => {
|
|||
let expected = new AllergyIntoleranceModel({})
|
||||
expected.title = 'Fish - dietary (substance)'
|
||||
expected.status = 'confirmed'
|
||||
expected.recordedDate = '2015-08-06T15:37:31-06:00'
|
||||
expected.substanceCoding = []
|
||||
expected.recorded_date = '2015-08-06T15:37:31-06:00'
|
||||
expected.substance_coding = []
|
||||
// expected.asserter = {reference: 'Patient/example'}
|
||||
// expected.note = []
|
||||
// expected.type = 'allergy'
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import {CodingModel} from '../datatypes/coding-model';
|
||||
import * as _ from "lodash";
|
||||
import {fhirVersions} from '../constants'
|
||||
import {fhirVersions, ResourceType} from '../constants'
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class AllergyIntoleranceModel {
|
||||
export class AllergyIntoleranceModel extends FastenDisplayModel {
|
||||
|
||||
title: string | undefined
|
||||
status: string | undefined
|
||||
recordedDate: string | undefined
|
||||
substanceCoding: CodingModel[] | undefined
|
||||
recorded_date: string | undefined
|
||||
substance_coding: CodingModel[] | undefined
|
||||
// reaction: string | undefined
|
||||
asserter: ReferenceModel | undefined
|
||||
note: { text: string }[] | undefined
|
||||
|
@ -16,7 +18,9 @@ export class AllergyIntoleranceModel {
|
|||
category: string[] | undefined
|
||||
patient: ReferenceModel | undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.AllergyIntolerance
|
||||
this.resourceDTO(fhirResource, fhirVersion || fhirVersions.R4);
|
||||
}
|
||||
|
||||
|
@ -34,8 +38,8 @@ export class AllergyIntoleranceModel {
|
|||
_.get(fhirResource, 'substance.coding[0].display') ||
|
||||
_.get(fhirResource, 'substance.text', '');
|
||||
this.status = _.get(fhirResource, 'status', '');
|
||||
this.recordedDate = _.get(fhirResource, 'recordedDate');
|
||||
this.substanceCoding = _.get(fhirResource, 'substance.coding', []);
|
||||
this.recorded_date = _.get(fhirResource, 'recordedDate');
|
||||
this.substance_coding = _.get(fhirResource, 'substance.coding', []);
|
||||
this.asserter = _.get(fhirResource, 'reporter');
|
||||
this.note = []
|
||||
this.category = _.get(fhirResource, 'category') ? [_.get(fhirResource, 'category')] : [];
|
||||
|
@ -48,11 +52,11 @@ export class AllergyIntoleranceModel {
|
|||
stu3DTO(fhirResource: any) {
|
||||
this.title = _.get(fhirResource, 'code.coding.0.display');
|
||||
this.status = _.get(fhirResource, 'verificationStatus');
|
||||
this.recordedDate = _.get(fhirResource, 'assertedDate');
|
||||
this.recorded_date = _.get(fhirResource, 'assertedDate');
|
||||
let substanceCoding = _.get(fhirResource, 'reaction', []).filter((item: any) =>
|
||||
_.get(item, 'substance.coding'),
|
||||
);
|
||||
this.substanceCoding = _.get(substanceCoding, '0.substance.coding', []);
|
||||
this.substance_coding = _.get(substanceCoding, '0.substance.coding', []);
|
||||
|
||||
this.note = _.get(fhirResource, 'note');
|
||||
};
|
||||
|
@ -60,11 +64,11 @@ export class AllergyIntoleranceModel {
|
|||
r4DTO(fhirResource: any) {
|
||||
this.title = _.get(fhirResource, 'code.coding.0.display');
|
||||
this.status = _.get(fhirResource, 'verificationStatus.coding[0].display');
|
||||
this.recordedDate = _.get(fhirResource, 'recordedDate');
|
||||
this.recorded_date = _.get(fhirResource, 'recordedDate');
|
||||
let substanceCoding = _.get(fhirResource, 'reaction', []).filter((item: any) =>
|
||||
_.get(item, 'substance.coding'),
|
||||
);
|
||||
this.substanceCoding = _.get(substanceCoding, '0.substance.coding', []);
|
||||
this.substance_coding = _.get(substanceCoding, '0.substance.coding', []);
|
||||
|
||||
this.note = _.get(fhirResource, 'note');
|
||||
};
|
||||
|
|
|
@ -1,25 +1,30 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {CodingModel} from '../datatypes/coding-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class AppointmentModel extends FastenDisplayModel {
|
||||
|
||||
export class AppointmentModel {
|
||||
description: string|undefined
|
||||
status: string|undefined
|
||||
start: string|undefined
|
||||
typeCoding: string|undefined
|
||||
type_coding: string|undefined
|
||||
comment: string|undefined
|
||||
participant: string|undefined
|
||||
participantPatient: string|undefined
|
||||
participantPractitioner: string|undefined
|
||||
participantLocation: string|undefined
|
||||
minutesDuration: string|undefined
|
||||
participant_patient: string|undefined
|
||||
participant_practitioner: string|undefined
|
||||
participant_location: string|undefined
|
||||
minutes_duration: string|undefined
|
||||
reason: string|undefined
|
||||
cancelationReason: string|undefined
|
||||
serviceCategory: string|undefined
|
||||
cancelation_reason: string|undefined
|
||||
service_category: string|undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.Appointment
|
||||
this.resourceDTO(fhirResource, fhirVersion || fhirVersions.R4);
|
||||
}
|
||||
|
||||
|
@ -28,7 +33,7 @@ export class AppointmentModel {
|
|||
this.description = _.get(fhirResource, 'description');
|
||||
this.status = _.get(fhirResource, 'status');
|
||||
this.start = _.get(fhirResource, 'start');
|
||||
this.typeCoding = _.get(fhirResource, 'type.coding');
|
||||
this.type_coding = _.get(fhirResource, 'type.coding');
|
||||
this.comment = _.get(fhirResource, 'comment');
|
||||
this.participant = _.get(fhirResource, 'participant');
|
||||
// const {
|
||||
|
@ -36,20 +41,20 @@ export class AppointmentModel {
|
|||
// participantPractitioner,
|
||||
// participantLocation,
|
||||
// } = prepareParticipantData(participant);
|
||||
this.minutesDuration = _.get(fhirResource, 'minutesDuration');
|
||||
this.minutes_duration = _.get(fhirResource, 'minutesDuration');
|
||||
this.reason = _.get(fhirResource, 'reason', []);
|
||||
};
|
||||
|
||||
stu3DTO(fhirResource:any) {
|
||||
this.serviceCategory = _.get(fhirResource, 'serviceCategory', []);
|
||||
this.typeCoding = _.get(fhirResource, 'appointmentType.coding');
|
||||
this.service_category = _.get(fhirResource, 'serviceCategory', []);
|
||||
this.type_coding = _.get(fhirResource, 'appointmentType.coding');
|
||||
};
|
||||
|
||||
r4DTO(fhirResource:any) {
|
||||
this.reason = _.get(fhirResource, 'reasonCode', []);
|
||||
this.cancelationReason = _.get(fhirResource, 'cancelationReason', []);
|
||||
this.serviceCategory = _.get(fhirResource, 'serviceCategory', []);
|
||||
this.typeCoding = _.get(fhirResource, 'appointmentType.coding');
|
||||
this.cancelation_reason = _.get(fhirResource, 'cancelationReason', []);
|
||||
this.service_category = _.get(fhirResource, 'serviceCategory', []);
|
||||
this.type_coding = _.get(fhirResource, 'appointmentType.coding');
|
||||
};
|
||||
|
||||
resourceDTO(fhirResource:any, fhirVersion:fhirVersions){
|
||||
|
|
|
@ -14,9 +14,9 @@ describe('CarePlanModel', () => {
|
|||
// expected.expiry = "completed"
|
||||
// expected.category = "completed"
|
||||
expected.goals = [ { reference: '#goal' } ]
|
||||
expected.hasGoals = true
|
||||
expected.has_goals = true
|
||||
expected.addresses = [ { reference: "Condition/f201", display: '?????' } ]
|
||||
expected.hasAddresses = true
|
||||
expected.has_addresses = true
|
||||
expected.activity = [
|
||||
{
|
||||
title: '64915003',
|
||||
|
@ -26,10 +26,10 @@ describe('CarePlanModel', () => {
|
|||
]
|
||||
}
|
||||
]
|
||||
expected.hasActivity = true
|
||||
expected.has_activity = true
|
||||
expected.subject = {"reference": "Patient/f001", display: 'P. van de Heuvel'}
|
||||
expected.periodStart = "2011-06-26"
|
||||
expected.periodEnd = "2011-06-27"
|
||||
expected.period_start = "2011-06-26"
|
||||
expected.period_end = "2011-06-27"
|
||||
// expected.basedOn
|
||||
|
||||
// expected.title = 'Cashew nuts'
|
||||
|
@ -58,19 +58,19 @@ describe('CarePlanModel', () => {
|
|||
// expected.expiry = "completed"
|
||||
// expected.category = "completed"
|
||||
expected.goals = [ { reference: '#goal' } ]
|
||||
expected.hasGoals = true
|
||||
expected.has_goals = true
|
||||
expected.addresses = [ { reference: '#p1', "display": "pregnancy" } ]
|
||||
expected.hasAddresses = true
|
||||
expected.has_addresses = true
|
||||
expected.activity = [
|
||||
{ title: undefined, hasCategories: false, categories: [ ] },
|
||||
{ title: 'First Antenatal encounter', hasCategories: true, categories: [ { system: 'http://example.org/mySystem', code: '1an' } ] },
|
||||
{ title: 'Follow-up Antenatal encounter', hasCategories: true, categories: [ { system: 'http://example.org/mySystem', code: 'an' } ] },
|
||||
{ title: 'Delivery', hasCategories: true, categories: [ { system: 'http://example.org/mySystem', code: 'del' } ] }
|
||||
]
|
||||
expected.hasActivity = true
|
||||
expected.has_activity = true
|
||||
expected.subject = {display: 'Eve Everywoman', reference: "Patient/1"}
|
||||
expected.periodStart = '2013-01-01'
|
||||
expected.periodEnd = '2013-10-01'
|
||||
expected.period_start = '2013-01-01'
|
||||
expected.period_end = '2013-10-01'
|
||||
|
||||
expect(new CarePlanModel(fixture)).toEqual(expected);
|
||||
});
|
||||
|
@ -82,20 +82,20 @@ describe('CarePlanModel', () => {
|
|||
// expected.expiry = "completed"
|
||||
expected.category = [{ text: 'Weight management plan' }]
|
||||
expected.goals = [ { reference: 'Goal/example' } ]
|
||||
expected.hasGoals = true
|
||||
expected.has_goals = true
|
||||
expected.addresses = [
|
||||
{ reference: '#p1', display: 'obesity' }
|
||||
]
|
||||
expected.hasAddresses = true
|
||||
expected.has_addresses = true
|
||||
expected.activity = [
|
||||
{ title: '3141-9', hasCategories: true, categories: [ Object({ system: 'http://loinc.org', code: '3141-9', display: 'Weight Measured' }), Object({ system: 'http://snomed.info/sct', code: '27113001', display: 'Body weight' }) ] }
|
||||
|
||||
]
|
||||
expected.hasActivity = true
|
||||
expected.has_activity = true
|
||||
expected.description = 'Manage obesity and weight loss'
|
||||
expected.subject = {display: 'Peter James Chalmers', reference: 'Patient/example'}
|
||||
// expected.periodStart = '2013-01-01'
|
||||
expected.periodEnd = '2017-06-01'
|
||||
expected.period_end = '2017-06-01'
|
||||
expected.author = { reference: 'Practitioner/example', display: 'Dr Adam Careful' }
|
||||
expect(new CarePlanModel(fixture)).toEqual(expected);
|
||||
});
|
||||
|
|
|
@ -1,29 +1,33 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class CarePlanModel {
|
||||
export class CarePlanModel extends FastenDisplayModel {
|
||||
|
||||
status: string | undefined
|
||||
expiry: string | undefined
|
||||
category: any[] | undefined
|
||||
hasCategory: boolean | undefined
|
||||
has_category: boolean | undefined
|
||||
goals: ReferenceModel[] | undefined
|
||||
hasGoals: boolean | undefined
|
||||
has_goals: boolean | undefined
|
||||
addresses: ReferenceModel[] | undefined
|
||||
hasAddresses: boolean | undefined
|
||||
has_addresses: boolean | undefined
|
||||
activity: any
|
||||
hasActivity: boolean | undefined
|
||||
basedOn: string | undefined
|
||||
partOf: string | undefined
|
||||
has_activity: boolean | undefined
|
||||
based_on: string | undefined
|
||||
part_of: string | undefined
|
||||
intent: string | undefined
|
||||
description: string | undefined
|
||||
subject: ReferenceModel | undefined
|
||||
periodStart: string | undefined
|
||||
periodEnd: string | undefined
|
||||
period_start: string | undefined
|
||||
period_end: string | undefined
|
||||
author: ReferenceModel | undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.CarePlan
|
||||
this.resourceDTO(fhirResource, fhirVersion || fhirVersions.R4);
|
||||
}
|
||||
|
||||
|
@ -31,22 +35,22 @@ export class CarePlanModel {
|
|||
this.status = _.get(fhirResource, 'status', '');
|
||||
this.expiry = _.get(fhirResource, 'expiry');
|
||||
this.category = _.get(fhirResource, 'category');
|
||||
this.hasCategory = Array.isArray(_.get(fhirResource, 'category.0.coding'));
|
||||
this.has_category = Array.isArray(_.get(fhirResource, 'category.0.coding'));
|
||||
this.goals = _.get(fhirResource, 'goal');
|
||||
this.hasGoals = Array.isArray(this.goals);
|
||||
this.has_goals = Array.isArray(this.goals);
|
||||
this.addresses = _.get(fhirResource, 'addresses')
|
||||
this.hasAddresses = Array.isArray(this.addresses);
|
||||
this.has_addresses = Array.isArray(this.addresses);
|
||||
this.description = _.get(fhirResource, 'description');
|
||||
this.subject = _.get(fhirResource, 'subject');
|
||||
this.periodStart = _.get(fhirResource, 'period.start');
|
||||
this.periodEnd = _.get(fhirResource, 'period.end');
|
||||
this.period_start = _.get(fhirResource, 'period.start');
|
||||
this.period_end = _.get(fhirResource, 'period.end');
|
||||
this.author = _.get(fhirResource, 'author');
|
||||
};
|
||||
|
||||
dstu2DTO(fhirResource: any){
|
||||
this.activity = _.get(fhirResource, 'activity');
|
||||
this.hasActivity = Array.isArray(this.activity);
|
||||
this.activity = !this.hasActivity
|
||||
this.has_activity = Array.isArray(this.activity);
|
||||
this.activity = !this.has_activity
|
||||
? this.activity
|
||||
: this.activity.map((item: any) => {
|
||||
const categories = _.get(item, 'detail.category.coding');
|
||||
|
@ -62,8 +66,8 @@ export class CarePlanModel {
|
|||
|
||||
stu3DTO(fhirResource: any) {
|
||||
let activity = _.get(fhirResource, 'activity');
|
||||
this.hasActivity = Array.isArray(activity);
|
||||
this.activity = !this.hasActivity
|
||||
this.has_activity = Array.isArray(activity);
|
||||
this.activity = !this.has_activity
|
||||
? activity
|
||||
: activity.map((item: any) => {
|
||||
const categories = [
|
||||
|
@ -78,15 +82,15 @@ export class CarePlanModel {
|
|||
categories,
|
||||
};
|
||||
});
|
||||
this.basedOn = _.get(fhirResource, 'basedOn', []);
|
||||
this.partOf = _.get(fhirResource, 'partOf', []);
|
||||
this.based_on = _.get(fhirResource, 'basedOn', []);
|
||||
this.part_of = _.get(fhirResource, 'partOf', []);
|
||||
this.intent = _.get(fhirResource, 'intent', []);
|
||||
};
|
||||
|
||||
r4DTO(fhirResource: any) {
|
||||
this.activity = _.get(fhirResource, 'activity');
|
||||
this.hasActivity = Array.isArray(this.activity);
|
||||
this.activity = !this.hasActivity
|
||||
this.has_activity = Array.isArray(this.activity);
|
||||
this.activity = !this.has_activity
|
||||
? this.activity
|
||||
: this.activity.map((item: any) => {
|
||||
const categories = [
|
||||
|
|
|
@ -15,7 +15,7 @@ describe('CareTeamModel', () => {
|
|||
expected.name = "Peter James Charlmers Care Plan for Inpatient Encounter"
|
||||
expected.status = "active"
|
||||
// expected.periodStart
|
||||
expected.periodEnd = "2013-01-01"
|
||||
expected.period_end = "2013-01-01"
|
||||
// expected.participants
|
||||
expected.category = [
|
||||
{ coding: [
|
||||
|
@ -30,7 +30,7 @@ describe('CareTeamModel', () => {
|
|||
expected.encounter = {
|
||||
"reference": "Encounter/example"
|
||||
}
|
||||
expected.managingOrganization = { reference: 'Organization/f001' }
|
||||
expected.managing_organization = { reference: 'Organization/f001' }
|
||||
expected.participants = [
|
||||
{ display: 'Peter James Chalmers', role: undefined, periodStart: undefined, periodEnd: undefined },
|
||||
{ display: 'Dorothy Dietition', role: undefined, periodStart: undefined, periodEnd: '2013-01-01' }
|
||||
|
|
|
@ -1,20 +1,25 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {CodableConceptModel} from '../datatypes/codable-concept-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class CareTeamModel extends FastenDisplayModel {
|
||||
|
||||
export class CareTeamModel {
|
||||
name: string | undefined
|
||||
status: string | undefined
|
||||
periodStart: string | undefined
|
||||
periodEnd: string | undefined
|
||||
period_start: string | undefined
|
||||
period_end: string | undefined
|
||||
participants: any[] | undefined
|
||||
category: CodableConceptModel[] | undefined
|
||||
subject: ReferenceModel | undefined
|
||||
encounter: ReferenceModel | undefined
|
||||
managingOrganization: ReferenceModel | undefined
|
||||
managing_organization: ReferenceModel | undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.CareTeam
|
||||
this.resourceDTO(fhirResource, fhirVersion || fhirVersions.R4)
|
||||
}
|
||||
|
||||
|
@ -22,17 +27,17 @@ export class CareTeamModel {
|
|||
// Default value for title - "Care team"
|
||||
this.name = _.get(fhirResource, 'name', 'Care team');
|
||||
this.status = _.get(fhirResource, 'status');
|
||||
this.periodStart = _.get(fhirResource, 'period.start');
|
||||
this.periodEnd = _.get(fhirResource, 'period.end');
|
||||
this.period_start = _.get(fhirResource, 'period.start');
|
||||
this.period_end = _.get(fhirResource, 'period.end');
|
||||
this.category = _.get(fhirResource, 'category');
|
||||
this.subject = _.get(fhirResource, 'subject');
|
||||
this.managingOrganization =
|
||||
this.managing_organization =
|
||||
_.get(fhirResource, 'managingOrganization[0]') ||
|
||||
_.get(fhirResource, 'managingOrganization');
|
||||
|
||||
this.participants = _.get(fhirResource, 'participant', []).map((item: any) => {
|
||||
const display = _.get(item, 'member.display');
|
||||
const role = _.get(item, 'role.text') || _.get(item, 'role.coding.0.display');
|
||||
const role = _.get(item, 'role.text') || _.get(item, 'role[0].text') || _.get(item, 'role.coding.0.display');
|
||||
const periodStart = _.get(item, 'period.start');
|
||||
const periodEnd = _.get(item, 'period.end');
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import { CompositionModel } from './composition-model';
|
||||
|
||||
describe('CompositionModel', () => {
|
||||
it('should create an instance', () => {
|
||||
expect(new CompositionModel()).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {CodableConceptModel} from '../datatypes/codable-concept-model';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
import * as _ from "lodash";
|
||||
|
||||
export class CompositionModel extends FastenDisplayModel {
|
||||
|
||||
title: string | undefined
|
||||
relates_to: string | undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.Composition
|
||||
|
||||
this.title = _.get(fhirResource, 'title')
|
||||
this.relates_to = _.get(fhirResource, 'relatesTo', []).map((relatesTo) => {
|
||||
return relatesTo.target?.targetReference?.reference
|
||||
})
|
||||
}
|
||||
}
|
|
@ -11,12 +11,12 @@ describe('ConditionModel', () => {
|
|||
it('should parse example1.json', () => {
|
||||
let fixture = require("../../fixtures/r4/resources/condition/example1.json")
|
||||
let expected = new ConditionModel({})
|
||||
expected.codeText = 'Burn of ear'
|
||||
expected.severityText = 'Severe'
|
||||
expected.code_text = 'Burn of ear'
|
||||
expected.severity_text = 'Severe'
|
||||
// expected.hasAsserter: boolean | undefined
|
||||
// expected.asserter: string | undefined
|
||||
expected.hasBodySite = true
|
||||
expected.bodySite = [new CodableConceptModel({
|
||||
expected.has_body_site = true
|
||||
expected.body_site = [new CodableConceptModel({
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://snomed.info/sct",
|
||||
|
@ -26,22 +26,22 @@ describe('ConditionModel', () => {
|
|||
],
|
||||
"text": "Left Ear"
|
||||
})]
|
||||
expected.clinicalStatus = 'active'
|
||||
expected.clinical_status = 'active'
|
||||
// expected.dateRecorded: string | undefined
|
||||
expected.onsetDateTime = '2012-05-24'
|
||||
expected.onset_datetime = '2012-05-24'
|
||||
|
||||
expect(new ConditionModel(fixture)).toEqual(expected);
|
||||
});
|
||||
it('should parse example2.json', () => {
|
||||
let fixture = require("../../fixtures/r4/resources/condition/example2.json")
|
||||
let expected = new ConditionModel({})
|
||||
expected.codeText = 'Asthma'
|
||||
expected.severityText = 'Mild'
|
||||
expected.code_text = 'Asthma'
|
||||
expected.severity_text = 'Mild'
|
||||
// expected.hasAsserter: boolean | undefined
|
||||
// expected.asserter: string | undefined
|
||||
expected.hasBodySite = false
|
||||
expected.has_body_site = false
|
||||
// expected.bodySite
|
||||
expected.clinicalStatus = 'active'
|
||||
expected.clinical_status = 'active'
|
||||
// expected.dateRecorded: string | undefined
|
||||
// expected.onsetDateTime = '2012-05-24'
|
||||
|
||||
|
@ -50,15 +50,15 @@ describe('ConditionModel', () => {
|
|||
it('should parse example3.json', () => {
|
||||
let fixture = require("../../fixtures/r4/resources/condition/example3.json")
|
||||
let expected = new ConditionModel({})
|
||||
expected.codeText = 'Fever'
|
||||
expected.severityText = 'Mild'
|
||||
expected.hasAsserter = true
|
||||
expected.code_text = 'Fever'
|
||||
expected.severity_text = 'Mild'
|
||||
expected.has_asserter = true
|
||||
expected.asserter = { reference: 'Practitioner/f201' }
|
||||
expected.hasBodySite = true
|
||||
expected.bodySite = [new CodableConceptModel({ text: '', coding: [ Object({ system: 'http://snomed.info/sct', code: '38266002', display: 'Entire body as a whole' }) ] })]
|
||||
expected.clinicalStatus = 'resolved'
|
||||
expected.dateRecorded = '2013-04-04'
|
||||
expected.onsetDateTime = '2013-04-02'
|
||||
expected.has_body_site = true
|
||||
expected.body_site = [new CodableConceptModel({ text: '', coding: [ Object({ system: 'http://snomed.info/sct', code: '38266002', display: 'Entire body as a whole' }) ] })]
|
||||
expected.clinical_status = 'resolved'
|
||||
expected.date_recorded = '2013-04-04'
|
||||
expected.onset_datetime = '2013-04-02'
|
||||
|
||||
expect(new ConditionModel(fixture)).toEqual(expected);
|
||||
});
|
||||
|
|
|
@ -1,54 +1,59 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class ConditionModel {
|
||||
codeText: string | undefined
|
||||
severityText: string | undefined
|
||||
hasAsserter: boolean | undefined
|
||||
export class ConditionModel extends FastenDisplayModel {
|
||||
|
||||
code_text: string | undefined
|
||||
severity_text: string | undefined
|
||||
has_asserter: boolean | undefined
|
||||
asserter: ReferenceModel | undefined
|
||||
hasBodySite: boolean | undefined
|
||||
bodySite: CodableConceptModel[] | undefined
|
||||
clinicalStatus: string | undefined
|
||||
dateRecorded: string | undefined
|
||||
onsetDateTime: string | undefined
|
||||
has_body_site: boolean | undefined
|
||||
body_site: CodableConceptModel[] | undefined
|
||||
clinical_status: string | undefined
|
||||
date_recorded: string | undefined
|
||||
onset_datetime: string | undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.Condition
|
||||
this.resourceDTO(fhirResource, fhirVersion || fhirVersions.R4);
|
||||
}
|
||||
|
||||
|
||||
commonDTO(fhirResource:any){
|
||||
this.codeText =
|
||||
this.code_text =
|
||||
_.get(fhirResource, 'code.coding.0.display') ||
|
||||
_.get(fhirResource, 'code.text') ||
|
||||
_.get(fhirResource, 'code.coding.0.code');
|
||||
this.severityText =
|
||||
this.severity_text =
|
||||
_.get(fhirResource, 'severity.coding.0.display') ||
|
||||
_.get(fhirResource, 'severity.text');
|
||||
this.onsetDateTime = _.get(fhirResource, 'onsetDateTime');
|
||||
this.hasAsserter = _.has(fhirResource, 'asserter');
|
||||
this.onset_datetime = _.get(fhirResource, 'onsetDateTime');
|
||||
this.has_asserter = _.has(fhirResource, 'asserter');
|
||||
this.asserter = _.get(fhirResource, 'asserter');
|
||||
this.hasBodySite = !!_.get(fhirResource, 'bodySite.0.coding.0.display');
|
||||
this.has_body_site = !!_.get(fhirResource, 'bodySite.0.coding.0.display');
|
||||
let bodySite = _.get(fhirResource, 'bodySite')
|
||||
if(bodySite){
|
||||
this.bodySite = bodySite.map((body:any) => new CodableConceptModel(body))
|
||||
this.body_site = bodySite.map((body:any) => new CodableConceptModel(body))
|
||||
}
|
||||
};
|
||||
dstu2DTO(fhirResource:any){
|
||||
this.clinicalStatus = _.get(fhirResource, 'clinicalStatus');
|
||||
this.dateRecorded = _.get(fhirResource, 'dateRecorded');
|
||||
this.clinical_status = _.get(fhirResource, 'clinicalStatus');
|
||||
this.date_recorded = _.get(fhirResource, 'dateRecorded');
|
||||
};
|
||||
|
||||
stu3DTO(fhirResource:any){
|
||||
this.clinicalStatus = _.get(fhirResource, 'clinicalStatus');
|
||||
this.dateRecorded = _.get(fhirResource, 'assertedDate');
|
||||
this.clinical_status = _.get(fhirResource, 'clinicalStatus');
|
||||
this.date_recorded = _.get(fhirResource, 'assertedDate');
|
||||
};
|
||||
|
||||
r4DTO(fhirResource:any){
|
||||
this.clinicalStatus = _.get(fhirResource, 'clinicalStatus.coding.0.code');
|
||||
this.dateRecorded = _.get(fhirResource, 'recordedDate');
|
||||
this.clinical_status = _.get(fhirResource, 'clinicalStatus.coding.0.code');
|
||||
this.date_recorded = _.get(fhirResource, 'recordedDate');
|
||||
};
|
||||
|
||||
resourceDTO(fhirResource:any, fhirVersion:fhirVersions){
|
||||
|
|
|
@ -1,55 +1,60 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class DeviceModel extends FastenDisplayModel {
|
||||
|
||||
export class DeviceModel {
|
||||
model: string | undefined
|
||||
status: string | undefined
|
||||
hasExpiry: boolean | undefined
|
||||
getExpiry: string | undefined
|
||||
getTypeCoding: string | undefined
|
||||
hasTypeCoding: boolean | undefined
|
||||
getUdi: string | undefined
|
||||
udiCarrierAIDC: string | undefined
|
||||
udiCarrierHRF: string | undefined
|
||||
has_expiry: boolean | undefined
|
||||
get_expiry: string | undefined
|
||||
get_type_coding: string | undefined
|
||||
has_type_coding: boolean | undefined
|
||||
get_udi: string | undefined
|
||||
udi_carrier_aidc: string | undefined
|
||||
udi_carrier_hrf: string | undefined
|
||||
safety: string | undefined
|
||||
hasSafety: boolean | undefined
|
||||
has_safety: boolean | undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.Device
|
||||
this.resourceDTO(fhirResource, fhirVersion || fhirVersions.R4);
|
||||
}
|
||||
|
||||
|
||||
commonDTO(fhirResource:any){
|
||||
this.model = _.get(fhirResource, 'model', 'Device');
|
||||
this.model = _.get(fhirResource, 'model') || _.get(fhirResource,"code.text", 'Device');
|
||||
this.status = _.get(fhirResource, 'status', '');
|
||||
this.getTypeCoding = _.get(fhirResource, 'type.coding');
|
||||
this.hasTypeCoding = Array.isArray(this.getTypeCoding);
|
||||
this.get_type_coding = _.get(fhirResource, 'type.coding');
|
||||
this.has_type_coding = Array.isArray(this.get_type_coding);
|
||||
};
|
||||
|
||||
dstu2DTO(fhirResource:any){
|
||||
this.getUdi = _.get(fhirResource, 'udi');
|
||||
this.hasExpiry = _.has(fhirResource, 'expiry');
|
||||
this.getExpiry = _.get(fhirResource, 'expiry');
|
||||
this.get_udi = _.get(fhirResource, 'udi');
|
||||
this.has_expiry = _.has(fhirResource, 'expiry');
|
||||
this.get_expiry = _.get(fhirResource, 'expiry');
|
||||
};
|
||||
|
||||
stu3DTO(fhirResource:any){
|
||||
this.getUdi = _.get(fhirResource, 'udi.name');
|
||||
this.hasExpiry = _.has(fhirResource, 'expirationDate');
|
||||
this.getExpiry = _.get(fhirResource, 'expirationDate');
|
||||
this.get_udi = _.get(fhirResource, 'udi.name');
|
||||
this.has_expiry = _.has(fhirResource, 'expirationDate');
|
||||
this.get_expiry = _.get(fhirResource, 'expirationDate');
|
||||
this.safety = _.get(fhirResource, 'safety', []);
|
||||
this.hasSafety = hasValue(this.safety);
|
||||
this.has_safety = hasValue(this.safety);
|
||||
};
|
||||
|
||||
r4DTO(fhirResource:any){
|
||||
this.getUdi = _.get(fhirResource, 'udiCarrier.deviceIdentifier');
|
||||
this.hasExpiry = _.has(fhirResource, 'expirationDate');
|
||||
this.getExpiry = _.get(fhirResource, 'expirationDate');
|
||||
this.udiCarrierAIDC = _.get(fhirResource, 'udiCarrier.carrierAIDC');
|
||||
this.udiCarrierHRF = _.get(fhirResource, 'udiCarrier.carrierHRF');
|
||||
this.get_udi = _.get(fhirResource, 'udiCarrier.deviceIdentifier');
|
||||
this.has_expiry = _.has(fhirResource, 'expirationDate');
|
||||
this.get_expiry = _.get(fhirResource, 'expirationDate');
|
||||
this.udi_carrier_aidc = _.get(fhirResource, 'udiCarrier.carrierAIDC');
|
||||
this.udi_carrier_hrf = _.get(fhirResource, 'udiCarrier.carrierHRF');
|
||||
this.safety = _.get(fhirResource, 'safety', []);
|
||||
this.hasSafety = hasValue(this.safety);
|
||||
this.has_safety = hasValue(this.safety);
|
||||
};
|
||||
|
||||
resourceDTO(fhirResource:any, fhirVersion:fhirVersions){
|
||||
|
|
|
@ -16,12 +16,12 @@ describe('DiagnosticReportModel', () => {
|
|||
expected.title = 'Complete blood count (hemogram) panel - Blood by Automated count'
|
||||
expected.status = 'final'
|
||||
// expected.effectiveDateTime: string | undefined
|
||||
expected.categoryCoding = [
|
||||
expected.category_coding = [
|
||||
{ system: 'http://snomed.info/sct', code: '252275004', display: 'Haematology test' },
|
||||
{ system: 'http://hl7.org/fhir/v2/0074', code: 'HM' }
|
||||
]
|
||||
expected.hasCategoryCoding = true
|
||||
expected.hasPerformer = true
|
||||
expected.has_category_coding = true
|
||||
expected.has_performer = true
|
||||
expected.conclusion = 'Core lab'
|
||||
expected.performer = { reference: 'Organization/f001', display: 'Burgers University Medical Centre' }
|
||||
expected.issued = '2013-05-15T19:32:52+01:00'
|
||||
|
|
|
@ -1,21 +1,26 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {CodingModel} from '../datatypes/coding-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class DiagnosticReportModel extends FastenDisplayModel {
|
||||
|
||||
export class DiagnosticReportModel {
|
||||
title: string | undefined
|
||||
status: string | undefined
|
||||
effectiveDateTime: string | undefined
|
||||
categoryCoding: CodingModel[] | undefined
|
||||
hasCategoryCoding: boolean | undefined
|
||||
hasPerformer: boolean | undefined
|
||||
effective_datetime: string | undefined
|
||||
category_coding: CodingModel[] | undefined
|
||||
has_category_coding: boolean | undefined
|
||||
has_performer: boolean | undefined
|
||||
conclusion: string | undefined
|
||||
performer: ReferenceModel | undefined
|
||||
issued: string | undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.DiagnosticReport
|
||||
this.resourceDTO(fhirResource, fhirVersion || fhirVersions.R4);
|
||||
}
|
||||
|
||||
|
@ -25,19 +30,19 @@ export class DiagnosticReportModel {
|
|||
_.get(fhirResource, 'code.display') ||
|
||||
_.get(fhirResource, 'code.coding.0.display', null);
|
||||
this.status = _.get(fhirResource, 'status', '');
|
||||
this.effectiveDateTime = _.get(fhirResource, 'effectiveDateTime');
|
||||
this.categoryCoding = _.get(fhirResource, 'category.coding');
|
||||
this.hasCategoryCoding = Array.isArray(this.categoryCoding);
|
||||
this.effective_datetime = _.get(fhirResource, 'effectiveDateTime');
|
||||
this.category_coding = _.get(fhirResource, 'category.coding');
|
||||
this.has_category_coding = Array.isArray(this.category_coding);
|
||||
this.conclusion = _.get(fhirResource, 'conclusion');
|
||||
this.issued = _.get(fhirResource, 'issued');
|
||||
};
|
||||
|
||||
dstu2DTO(fhirResource:any){
|
||||
this.hasPerformer = _.has(fhirResource, 'performer');
|
||||
this.has_performer = _.has(fhirResource, 'performer');
|
||||
this.performer = _.get(fhirResource, 'performer');
|
||||
};
|
||||
stu3DTO(fhirResource:any){
|
||||
this.hasPerformer = _.has(fhirResource, 'performer.0.actor.display');
|
||||
this.has_performer = _.has(fhirResource, 'performer.0.actor.display');
|
||||
this.performer = _.get(fhirResource, 'performer.0.actor');
|
||||
};
|
||||
|
||||
|
@ -46,9 +51,9 @@ export class DiagnosticReportModel {
|
|||
if (!this.performer) {
|
||||
this.performer = _.get(fhirResource, 'performer.0');
|
||||
}
|
||||
this.hasPerformer = !!this.performer;
|
||||
this.categoryCoding = _.get(fhirResource, 'category.coding');
|
||||
this.hasCategoryCoding = Array.isArray(this.categoryCoding);
|
||||
this.has_performer = !!this.performer;
|
||||
this.category_coding = _.get(fhirResource, 'category.coding');
|
||||
this.has_category_coding = Array.isArray(this.category_coding);
|
||||
};
|
||||
|
||||
resourceDTO(fhirResource:any, fhirVersion: fhirVersions){
|
||||
|
|
|
@ -15,10 +15,10 @@ describe('DocumentReferenceModel', () => {
|
|||
expected.description = 'Physical'
|
||||
expected.status = 'current'
|
||||
// expected.docStatus: string | undefined
|
||||
expected.typeCoding = { system: 'http://loinc.org', code: '34108-1', display: 'Outpatient Note' }
|
||||
expected.type_coding = { system: 'http://loinc.org', code: '34108-1', display: 'Outpatient Note' }
|
||||
// expected.classCoding: string | undefined
|
||||
expected.createdAt = '2005-12-24T09:43:41+11:00'
|
||||
expected.securityLabelCoding = { system: 'http://terminology.hl7.org/CodeSystem/v3-Confidentiality', code: 'V', display: 'very restricted' }
|
||||
expected.created_at = '2005-12-24T09:43:41+11:00'
|
||||
expected.security_label_coding = { system: 'http://terminology.hl7.org/CodeSystem/v3-Confidentiality', code: 'V', display: 'very restricted' }
|
||||
expected.context = {
|
||||
eventCoding: { system: 'http://ihe.net/xds/connectathon/eventCodes', code: 'T-D8200', display: 'Arm' },
|
||||
facilityTypeCoding: { system: 'http://www.ihe.net/xds/connectathon/healthcareFacilityTypeCodes', code: 'Outpatient', display: 'Outpatient' },
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {CodingModel} from '../datatypes/coding-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class DocumentReferenceModel extends FastenDisplayModel {
|
||||
|
||||
export class DocumentReferenceModel {
|
||||
description: string | undefined
|
||||
status: string | undefined
|
||||
docStatus: string | undefined
|
||||
typeCoding: CodingModel | undefined
|
||||
classCoding: CodingModel | undefined
|
||||
createdAt: string | undefined
|
||||
securityLabelCoding: CodingModel | undefined
|
||||
doc_status: string | undefined
|
||||
type_coding: CodingModel | undefined
|
||||
class_coding: CodingModel | undefined
|
||||
created_at: string | undefined
|
||||
security_label_coding: CodingModel | undefined
|
||||
content: {
|
||||
url: string
|
||||
isUrlBinaryResourceReference: boolean
|
||||
|
@ -26,7 +29,9 @@ export class DocumentReferenceModel {
|
|||
periodEnd: string
|
||||
} | undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.DocumentReference
|
||||
this.resourceDTO(fhirResource, fhirVersion || fhirVersions.R4);
|
||||
}
|
||||
|
||||
|
@ -34,10 +39,10 @@ export class DocumentReferenceModel {
|
|||
commonDTO(fhirResource:any){
|
||||
this.description = _.get(fhirResource, 'description');
|
||||
this.status = _.get(fhirResource, 'status');
|
||||
this.typeCoding = _.get(fhirResource, 'type.coding[0]');
|
||||
this.classCoding = _.get(fhirResource, 'class.coding[0]');
|
||||
this.createdAt = _.get(fhirResource, 'created');
|
||||
this.securityLabelCoding = _.get(fhirResource, 'securityLabel[0].coding[0]');
|
||||
this.type_coding = _.get(fhirResource, 'type.coding[0]');
|
||||
this.class_coding = _.get(fhirResource, 'class.coding[0]');
|
||||
this.created_at = _.get(fhirResource, 'created');
|
||||
this.security_label_coding = _.get(fhirResource, 'securityLabel[0].coding[0]');
|
||||
const eventCoding = _.get(fhirResource, 'context.event[0].coding[0]');
|
||||
const facilityTypeCoding = _.get(
|
||||
fhirResource,
|
||||
|
@ -59,18 +64,18 @@ export class DocumentReferenceModel {
|
|||
};
|
||||
|
||||
dstu2DTO(fhirResource:any) {
|
||||
this.docStatus =
|
||||
this.doc_status =
|
||||
_.get(fhirResource, 'docStatus.coding[0].display') ||
|
||||
_.get(fhirResource, 'docStatus.coding[0].code');
|
||||
};
|
||||
|
||||
stu3DTO(fhirResource:any){
|
||||
this.docStatus = _.get(fhirResource, 'docStatus');
|
||||
this.doc_status = _.get(fhirResource, 'docStatus');
|
||||
};
|
||||
|
||||
r4DTO(fhirResource:any){
|
||||
this.classCoding = _.get(fhirResource, 'category.coding[0]');
|
||||
this.createdAt = _.get(fhirResource, 'date');
|
||||
this.class_coding = _.get(fhirResource, 'category.coding[0]');
|
||||
this.created_at = _.get(fhirResource, 'date');
|
||||
};
|
||||
|
||||
contentDTO(fhirResource: any, fhirVersion: fhirVersions){
|
||||
|
|
|
@ -15,8 +15,8 @@ describe('EncounterModel', () => {
|
|||
// hasParticipant: boolean | undefined
|
||||
// locationDisplay: string | undefined
|
||||
// encounterType: string | undefined
|
||||
expected.resourceClass = 'inpatient encounter'
|
||||
expected.resourceStatus = 'in-progress'
|
||||
expected.resource_class = 'inpatient encounter'
|
||||
expected.resource_status = 'in-progress'
|
||||
// participant
|
||||
|
||||
expect(new EncounterModel(fixture)).toEqual(expected);
|
||||
|
@ -25,13 +25,13 @@ describe('EncounterModel', () => {
|
|||
it('should parse example2.json', () => {
|
||||
let fixture = require("../../fixtures/r4/resources/encounter/example2.json")
|
||||
let expected = new EncounterModel({})
|
||||
expected.periodEnd = '2015-01-17T16:30:00+10:00'
|
||||
expected.periodStart = '2015-01-17T16:00:00+10:00'
|
||||
expected.hasParticipant = true
|
||||
expected.locationDisplay = 'Client\'s home'
|
||||
expected.period_end = '2015-01-17T16:30:00+10:00'
|
||||
expected.period_start = '2015-01-17T16:00:00+10:00'
|
||||
expected.has_participant = true
|
||||
expected.location_display = 'Client\'s home'
|
||||
// encounterType: string | undefined
|
||||
expected.resourceClass = 'home health'
|
||||
expected.resourceStatus = 'finished'
|
||||
expected.resource_class = 'home health'
|
||||
expected.resource_status = 'finished'
|
||||
expected.participant = [
|
||||
{
|
||||
display: undefined,
|
||||
|
@ -49,11 +49,11 @@ describe('EncounterModel', () => {
|
|||
let expected = new EncounterModel({})
|
||||
// expected.periodEnd = '2015-01-17T16:30:00+10:00'
|
||||
// expected.periodStart = '2015-01-17T16:00:00+10:00'
|
||||
expected.hasParticipant = true
|
||||
expected.locationDisplay = 'Encounter'
|
||||
expected.encounterType = [ { coding: [ Object({ system: 'http://snomed.info/sct', code: '11429006', display: 'Consultation' }) ] } ]
|
||||
expected.resourceClass = 'ambulatory'
|
||||
expected.resourceStatus = 'finished'
|
||||
expected.has_participant = true
|
||||
expected.location_display = 'Encounter'
|
||||
expected.encounter_type = [ { coding: [ Object({ system: 'http://snomed.info/sct', code: '11429006', display: 'Consultation' }) ] } ]
|
||||
expected.resource_class = 'ambulatory'
|
||||
expected.resource_status = 'finished'
|
||||
expected.participant = [
|
||||
{ display: undefined, reference: Object({ reference: 'Practitioner/f201' }), text: undefined, periodStart: undefined }
|
||||
]
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {CodingModel} from '../datatypes/coding-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class EncounterModel {
|
||||
periodEnd: string | undefined
|
||||
periodStart: string | undefined
|
||||
hasParticipant: boolean | undefined
|
||||
locationDisplay: string | undefined
|
||||
encounterType: CodableConceptModel[] | undefined
|
||||
resourceClass: string | undefined
|
||||
resourceStatus: string | undefined
|
||||
export class EncounterModel extends FastenDisplayModel {
|
||||
|
||||
period_end: string | undefined
|
||||
period_start: string | undefined
|
||||
has_participant: boolean | undefined
|
||||
location_display: string | undefined
|
||||
encounter_type: CodableConceptModel[] | undefined
|
||||
resource_class: string | undefined
|
||||
resource_status: string | undefined
|
||||
participant: {
|
||||
display?: string,
|
||||
reference: ReferenceModel,
|
||||
|
@ -19,25 +22,27 @@ export class EncounterModel {
|
|||
periodStart?:string
|
||||
}[] | undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.Encounter
|
||||
this.resourceDTO(fhirResource, fhirVersion || fhirVersions.R4);
|
||||
}
|
||||
|
||||
commonDTO(fhirResource:any){
|
||||
this.resourceStatus = _.get(fhirResource, 'status');
|
||||
this.locationDisplay = _.get(
|
||||
this.resource_status = _.get(fhirResource, 'status');
|
||||
this.location_display = _.get(
|
||||
fhirResource,
|
||||
'location[0].location.display',
|
||||
'Encounter',
|
||||
);
|
||||
this.encounterType = _.get(fhirResource, 'type');
|
||||
this.hasParticipant = _.has(fhirResource, 'participant');
|
||||
this.encounter_type = _.get(fhirResource, 'type');
|
||||
this.has_participant = _.has(fhirResource, 'participant');
|
||||
};
|
||||
|
||||
dstu2DTO(fhirResource:any){
|
||||
this.periodEnd = _.get(fhirResource, 'period.end');
|
||||
this.periodStart = _.get(fhirResource, 'period.start');
|
||||
this.resourceClass = _.get(fhirResource, 'class');
|
||||
this.period_end = _.get(fhirResource, 'period.end');
|
||||
this.period_start = _.get(fhirResource, 'period.start');
|
||||
this.resource_class = _.get(fhirResource, 'class');
|
||||
this.participant = _.get(fhirResource, 'participant', []).map((item: any) => {
|
||||
let periodStart = _.get(item, 'period.start');
|
||||
periodStart = new Date(periodStart).toLocaleString();
|
||||
|
@ -52,11 +57,11 @@ export class EncounterModel {
|
|||
};
|
||||
|
||||
stu3DTO(fhirResource:any){
|
||||
this.periodEnd = _.get(fhirResource, 'period.end');
|
||||
this.periodStart = _.get(fhirResource, 'period.start');
|
||||
this.period_end = _.get(fhirResource, 'period.end');
|
||||
this.period_start = _.get(fhirResource, 'period.start');
|
||||
|
||||
|
||||
this.resourceClass = _.get(fhirResource, 'class.display');
|
||||
this.resource_class = _.get(fhirResource, 'class.display');
|
||||
this.participant = _.get(fhirResource, 'participant', []).map((item: any) => {
|
||||
let periodStart = _.get(item, 'period.start');
|
||||
const reference = _.get(item, 'individual', {});
|
||||
|
@ -70,10 +75,10 @@ export class EncounterModel {
|
|||
};
|
||||
|
||||
r4DTO(fhirResource:any){
|
||||
this.periodEnd = _.get(fhirResource, 'period.end');
|
||||
this.periodStart = _.get(fhirResource, 'period.start');
|
||||
this.period_end = _.get(fhirResource, 'period.end');
|
||||
this.period_start = _.get(fhirResource, 'period.start');
|
||||
|
||||
this.resourceClass = _.get(fhirResource, 'class.display');
|
||||
this.resource_class = _.get(fhirResource, 'class.display');
|
||||
this.participant = _.get(fhirResource, 'participant', []).map((item: any) => {
|
||||
let periodStart = _.get(item, 'period.start');
|
||||
const reference = _.get(item, 'individual', {});
|
||||
|
|
|
@ -15,23 +15,23 @@ describe('GoalModel', () => {
|
|||
|
||||
// expected.title: string | undefined
|
||||
expected.status = 'on-hold'
|
||||
expected.hasStatus = true
|
||||
expected.startDate = '2015-04-05'
|
||||
expected.hasCategory = true
|
||||
expected.has_status = true
|
||||
expected.start_date = '2015-04-05'
|
||||
expected.has_category = true
|
||||
expected.category = [
|
||||
new CodableConceptModel({ coding: [ Object({ system: 'http://terminology.hl7.org/CodeSystem/goal-category', code: 'dietary' }) ] })
|
||||
]
|
||||
// expected.hasUdi: boolean | undefined
|
||||
// expected.udi: string | undefined
|
||||
expected.addresses = [{ display: 'obesity condition' }]
|
||||
expected.hasAddresses = true
|
||||
expected.has_addresses = true
|
||||
// expected.author: string | undefined
|
||||
expected.description = 'Target weight is 160 to 180 lbs.'
|
||||
// expected.outcomeReference: string | undefined
|
||||
// expected.achievementStatus: string | undefined
|
||||
expected.priority = { system: 'http://terminology.hl7.org/CodeSystem/goal-priority', code: 'high-priority', display: 'High Priority' }
|
||||
expected.subject = { reference: 'Patient/example', display: 'Peter James Chalmers' }
|
||||
expected.statusDate = '2016-02-14'
|
||||
expected.status_date = '2016-02-14'
|
||||
|
||||
expect(new GoalModel(fixture)).toEqual(expected);
|
||||
});
|
||||
|
@ -42,17 +42,17 @@ describe('GoalModel', () => {
|
|||
|
||||
// expected.title: string | undefined
|
||||
expected.status = 'completed'
|
||||
expected.hasStatus = true
|
||||
expected.startDate = '2015-04-05'
|
||||
expected.hasCategory = false
|
||||
expected.has_status = true
|
||||
expected.start_date = '2015-04-05'
|
||||
expected.has_category = false
|
||||
// expected.hasUdi: boolean | undefined
|
||||
// expected.udi: string | undefined
|
||||
// expected.addresses = [{ display: 'obesity condition' }]
|
||||
expected.hasAddresses = false
|
||||
expected.has_addresses = false
|
||||
// expected.author: string | undefined
|
||||
expected.description = 'Stop smoking'
|
||||
// expected.outcomeReference: string | undefined
|
||||
expected.achievementStatus = { system: 'http://terminology.hl7.org/CodeSystem/goal-achievement', code: 'achieved', display: 'Achieved' }
|
||||
expected.achievement_status = { system: 'http://terminology.hl7.org/CodeSystem/goal-achievement', code: 'achieved', display: 'Achieved' }
|
||||
// expected.priority = { system: 'http://terminology.hl7.org/CodeSystem/goal-priority', code: 'high-priority', display: 'High Priority' }
|
||||
expected.subject = { reference: 'Patient/example', display: 'Peter James Chalmers' }
|
||||
// expected.statusDate = '2016-02-14'
|
||||
|
|
|
@ -1,51 +1,55 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {CodingModel} from '../datatypes/coding-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class GoalModel {
|
||||
export class GoalModel extends FastenDisplayModel {
|
||||
|
||||
title: string | undefined
|
||||
status: string | undefined
|
||||
hasStatus: boolean | undefined
|
||||
startDate: string | undefined
|
||||
hasCategory: boolean | undefined
|
||||
has_status: boolean | undefined
|
||||
start_date: string | undefined
|
||||
has_category: boolean | undefined
|
||||
category: CodableConceptModel[] | undefined
|
||||
hasUdi: boolean | undefined
|
||||
has_udi: boolean | undefined
|
||||
udi: string | undefined
|
||||
addresses: any[] | undefined
|
||||
hasAddresses: boolean | undefined
|
||||
has_addresses: boolean | undefined
|
||||
author: string | undefined
|
||||
description: string | undefined
|
||||
outcomeReference: string | undefined
|
||||
achievementStatus: CodingModel | undefined
|
||||
outcome_reference: string | undefined
|
||||
achievement_status: CodingModel | undefined
|
||||
priority: CodingModel | undefined
|
||||
subject: ReferenceModel | undefined
|
||||
statusDate: string | undefined
|
||||
status_date: string | undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.Goal
|
||||
this.resourceDTO(fhirResource, fhirVersion || fhirVersions.R4);
|
||||
}
|
||||
|
||||
commonDTO(fhirResource: any){
|
||||
this.title = _.get(fhirResource, 'note[0].text', 'Goal');
|
||||
this.status = _.get(fhirResource, 'status', '');
|
||||
this.hasStatus = _.has(fhirResource, 'status');
|
||||
this.startDate = _.get(fhirResource, 'startDate');
|
||||
this.has_status = _.has(fhirResource, 'status');
|
||||
this.start_date = _.get(fhirResource, 'startDate');
|
||||
let category = _.get(fhirResource, 'category');
|
||||
if(category){
|
||||
this.category = category.map((cat:any) => new CodableConceptModel(cat))
|
||||
}
|
||||
this.hasCategory = Array.isArray(this.category);
|
||||
this.hasUdi = _.has(fhirResource, 'udi');
|
||||
this.has_category = Array.isArray(this.category);
|
||||
this.has_udi = _.has(fhirResource, 'udi');
|
||||
this.udi = _.get(fhirResource, 'udi');
|
||||
this.addresses = _.get(fhirResource, 'addresses');
|
||||
this.hasAddresses = Array.isArray(this.addresses);
|
||||
this.has_addresses = Array.isArray(this.addresses);
|
||||
this.author = _.get(fhirResource, 'author');
|
||||
this.priority = _.get(fhirResource, 'priority.coding[0]');
|
||||
this.subject = _.get(fhirResource, 'subject');
|
||||
this.statusDate = _.get(fhirResource, 'statusDate');
|
||||
this.status_date = _.get(fhirResource, 'statusDate');
|
||||
};
|
||||
dstu2DTO(fhirResource:any){
|
||||
this.description = _.get(fhirResource, 'description');
|
||||
|
@ -53,13 +57,13 @@ export class GoalModel {
|
|||
stu3DTO(fhirResource:any){
|
||||
this.description = _.get(fhirResource, 'description.text', null);
|
||||
this.title = _.get(fhirResource, 'statusReason');
|
||||
this.outcomeReference = _.get(fhirResource, 'outcomeReference');
|
||||
this.outcome_reference = _.get(fhirResource, 'outcomeReference');
|
||||
};
|
||||
r4DTO(fhirResource:any){
|
||||
this.description = _.get(fhirResource, 'description.text', null);
|
||||
this.status = _.get(fhirResource, 'lifecycleStatus', '');
|
||||
this.hasStatus = _.has(fhirResource, 'lifecycleStatus');
|
||||
this.achievementStatus = _.get(fhirResource, 'achievementStatus.coding[0]');
|
||||
this.has_status = _.has(fhirResource, 'lifecycleStatus');
|
||||
this.achievement_status = _.get(fhirResource, 'achievementStatus.coding[0]');
|
||||
};
|
||||
|
||||
resourceDTO(fhirResource: any, fhirVersion:fhirVersions){
|
||||
|
|
|
@ -12,20 +12,20 @@ describe('ImmunizationModel', () => {
|
|||
|
||||
expected.title = 'Fluvax (Influenza)'
|
||||
expected.status = 'completed'
|
||||
expected.providedDate = '2013-01-10'
|
||||
expected.provided_date = '2013-01-10'
|
||||
// manufacturerText: string | undefined
|
||||
expected.hasLotNumber = true
|
||||
expected.lotNumber = 'AAJN11K'
|
||||
expected.lotNumberExpirationDate = '2015-02-15'
|
||||
expected.hasDoseQuantity = true
|
||||
expected.doseQuantity = { value: 5, system: 'http://unitsofmeasure.org', code: 'mg' }
|
||||
expected.has_lot_number = true
|
||||
expected.lot_number = 'AAJN11K'
|
||||
expected.lot_number_expiration_date = '2015-02-15'
|
||||
expected.has_dose_quantity = true
|
||||
expected.dose_quantity = { value: 5, system: 'http://unitsofmeasure.org', code: 'mg' }
|
||||
// requester: string | undefined
|
||||
// reported: string | undefined
|
||||
// performer: string | undefined
|
||||
expected.route = [ { system: 'http://terminology.hl7.org/CodeSystem/v3-RouteOfAdministration', code: 'IM', display: 'Injection, intramuscular' }]
|
||||
expected.hasRoute = true
|
||||
expected.has_route = true
|
||||
expected.site = [{ system: 'http://terminology.hl7.org/CodeSystem/v3-ActSite', code: 'LA', display: 'left arm' } ]
|
||||
expected.hasSite = true
|
||||
expected.has_site = true
|
||||
expected.patient = { reference: 'Patient/example' }
|
||||
expected.note = [ { text: 'Notes on adminstration of vaccine' } ]
|
||||
|
||||
|
|
|
@ -1,31 +1,35 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {CodingModel} from '../datatypes/coding-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class ImmunizationModel {
|
||||
export class ImmunizationModel extends FastenDisplayModel {
|
||||
|
||||
title: string | undefined
|
||||
status: string | undefined
|
||||
providedDate: string | undefined
|
||||
manufacturerText: string | undefined
|
||||
hasLotNumber: boolean | undefined
|
||||
lotNumber: string | undefined
|
||||
lotNumberExpirationDate: string | undefined
|
||||
hasDoseQuantity: boolean | undefined
|
||||
doseQuantity: CodingModel | undefined
|
||||
provided_date: string | undefined
|
||||
manufacturer_text: string | undefined
|
||||
has_lot_number: boolean | undefined
|
||||
lot_number: string | undefined
|
||||
lot_number_expiration_date: string | undefined
|
||||
has_dose_quantity: boolean | undefined
|
||||
dose_quantity: CodingModel | undefined
|
||||
requester: string | undefined
|
||||
reported: string | undefined
|
||||
performer: string | undefined
|
||||
route: CodingModel[] | undefined
|
||||
hasRoute: boolean | undefined
|
||||
has_route: boolean | undefined
|
||||
site: CodingModel[] | undefined
|
||||
hasSite: boolean | undefined
|
||||
has_site: boolean | undefined
|
||||
patient: ReferenceModel | undefined
|
||||
note: any[] | undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.Immunization
|
||||
this.resourceDTO(fhirResource, fhirVersion || fhirVersions.R4);
|
||||
}
|
||||
|
||||
|
@ -35,19 +39,19 @@ export class ImmunizationModel {
|
|||
_.get(fhirResource, 'vaccineCode.text') ||
|
||||
_.get(fhirResource, 'vaccineCode.coding[0].display', 'Immunization');
|
||||
this.status = _.get(fhirResource, 'status', null);
|
||||
this.providedDate = _.get(fhirResource, 'date', null);
|
||||
this.provided_date = _.get(fhirResource, 'date', null);
|
||||
this.reported = _.get(fhirResource, 'reported') && ' - self reported';
|
||||
this.manufacturerText = _.get(fhirResource, 'manufacturer.display');
|
||||
this.hasLotNumber = _.has(fhirResource, 'lotNumber');
|
||||
this.lotNumber = _.get(fhirResource, 'lotNumber');
|
||||
this.lotNumberExpirationDate = _.get(fhirResource, 'expirationDate');
|
||||
this.hasDoseQuantity = _.has(fhirResource, 'doseQuantity');
|
||||
this.doseQuantity = _.get(fhirResource, 'doseQuantity');
|
||||
this.manufacturer_text = _.get(fhirResource, 'manufacturer.display');
|
||||
this.has_lot_number = _.has(fhirResource, 'lotNumber');
|
||||
this.lot_number = _.get(fhirResource, 'lotNumber');
|
||||
this.lot_number_expiration_date = _.get(fhirResource, 'expirationDate');
|
||||
this.has_dose_quantity = _.has(fhirResource, 'doseQuantity');
|
||||
this.dose_quantity = _.get(fhirResource, 'doseQuantity');
|
||||
this.requester = _.get(fhirResource, 'requester');
|
||||
this.route = _.get(fhirResource, 'route.coding');
|
||||
this.hasRoute = Array.isArray(this.route);
|
||||
this.has_route = Array.isArray(this.route);
|
||||
this.site = _.get(fhirResource, 'site.coding');
|
||||
this.hasSite = Array.isArray(this.site);
|
||||
this.has_site = Array.isArray(this.site);
|
||||
this.patient = _.get(fhirResource, 'patient');
|
||||
this.note = _.get(fhirResource, 'note');
|
||||
};
|
||||
|
@ -60,7 +64,7 @@ export class ImmunizationModel {
|
|||
|
||||
r4DTO(fhirResource:any){
|
||||
this.performer = _.get(fhirResource, 'performer.actor');
|
||||
this.providedDate =
|
||||
this.provided_date =
|
||||
_.get(fhirResource, 'occurrenceDateTime') ||
|
||||
_.get(fhirResource, 'occurrenceString');
|
||||
};
|
||||
|
|
|
@ -17,7 +17,7 @@ describe('LocationModel', () => {
|
|||
expectedLocation.status = 'active'
|
||||
expectedLocation.description = 'Second floor of the Old South Wing, formerly in use by Psychiatry'
|
||||
expectedLocation.mode = "instance"
|
||||
expectedLocation.managingOrganization = {"reference": "Organization/f001"}
|
||||
expectedLocation.managing_organization = {"reference": "Organization/f001"}
|
||||
expectedLocation.address = new AddressModel({
|
||||
city: "Den Burg",
|
||||
line: ['Galapagosweg 91, Building A'],
|
||||
|
@ -31,7 +31,7 @@ describe('LocationModel', () => {
|
|||
{system: 'url', value: 'http://sampleorg.com/southwing', use: 'work'}
|
||||
]
|
||||
expectedLocation.type = []
|
||||
expectedLocation.physicalType = new CodableConceptModel({
|
||||
expectedLocation.physical_type = new CodableConceptModel({
|
||||
text: '',
|
||||
coding: [
|
||||
{
|
||||
|
@ -52,7 +52,7 @@ describe('LocationModel', () => {
|
|||
expectedLocation.status = 'active'
|
||||
expectedLocation.description = "Ambulance provided by Burgers University Medical Center"
|
||||
expectedLocation.mode = "kind"
|
||||
expectedLocation.managingOrganization = {"reference": "Organization/f001"}
|
||||
expectedLocation.managing_organization = {"reference": "Organization/f001"}
|
||||
expectedLocation.telecom = [
|
||||
{system: 'phone', value: '2329', use: 'mobile'}
|
||||
]
|
||||
|
@ -66,7 +66,7 @@ describe('LocationModel', () => {
|
|||
}
|
||||
]
|
||||
})]
|
||||
expectedLocation.physicalType = new CodableConceptModel({
|
||||
expectedLocation.physical_type = new CodableConceptModel({
|
||||
text: '',
|
||||
coding: [
|
||||
{
|
||||
|
@ -90,7 +90,7 @@ describe('LocationModel', () => {
|
|||
expectedLocation.status = 'active'
|
||||
expectedLocation.description = "Second floor of the Old South Wing, formerly in use by Psychiatry"
|
||||
expectedLocation.mode = "instance"
|
||||
expectedLocation.managingOrganization = {"reference": "Organization/f001"}
|
||||
expectedLocation.managing_organization = {"reference": "Organization/f001"}
|
||||
expectedLocation.telecom = [
|
||||
{
|
||||
"system": "phone",
|
||||
|
@ -121,7 +121,7 @@ describe('LocationModel', () => {
|
|||
"postalCode": "9105 PZ",
|
||||
"country": "NLD"
|
||||
})
|
||||
expectedLocation.physicalType = new CodableConceptModel({
|
||||
expectedLocation.physical_type = new CodableConceptModel({
|
||||
text: '',
|
||||
coding: [
|
||||
{
|
||||
|
|
|
@ -3,20 +3,25 @@ import {AddressModel} from '../datatypes/address-model';
|
|||
import {TelecomModel} from '../datatypes/telecom-model';
|
||||
import {CodableConceptModel} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class LocationModel extends FastenDisplayModel {
|
||||
|
||||
export class LocationModel {
|
||||
name: string
|
||||
status: string
|
||||
description: string
|
||||
address: AddressModel
|
||||
telecom: TelecomModel[]
|
||||
type: CodableConceptModel[]
|
||||
physicalType: CodableConceptModel
|
||||
physical_type: CodableConceptModel
|
||||
mode: string
|
||||
managingOrganization: ReferenceModel
|
||||
managing_organization: ReferenceModel
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.Location
|
||||
|
||||
this.name = _.get(fhirResource, 'name');
|
||||
this.status = _.get(fhirResource, 'status');
|
||||
|
@ -24,9 +29,9 @@ export class LocationModel {
|
|||
this.address = new AddressModel(_.get(fhirResource, 'address'));
|
||||
this.telecom = _.get(fhirResource, 'telecom');
|
||||
this.type = (_.get(fhirResource, 'type') || []).map((_type: any) => new CodableConceptModel(_type));
|
||||
this.physicalType = new CodableConceptModel(_.get(fhirResource, 'physicalType'));
|
||||
this.physical_type = new CodableConceptModel(_.get(fhirResource, 'physicalType'));
|
||||
this.mode = _.get(fhirResource, 'mode');
|
||||
this.managingOrganization = _.get(fhirResource, 'managingOrganization');
|
||||
this.managing_organization = _.get(fhirResource, 'managingOrganization');
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,31 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {CodingModel} from '../datatypes/coding-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class MedicationDispenseModel {
|
||||
medicationTitle: string|undefined
|
||||
medicationCoding: string|undefined
|
||||
typeCoding: string|undefined
|
||||
hasDosageInstruction: boolean|undefined
|
||||
dosageInstruction: any[] | undefined
|
||||
dosageInstructionData: any[]|undefined
|
||||
whenPrepared: string|undefined
|
||||
export class MedicationDispenseModel extends FastenDisplayModel {
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
medication_title: string|undefined
|
||||
medication_coding: string|undefined
|
||||
type_coding: string|undefined
|
||||
has_dosage_instruction: boolean|undefined
|
||||
dosage_instruction: any[] | undefined
|
||||
dosage_instruction_data: any[]|undefined
|
||||
when_prepared: string|undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.MedicationDispense
|
||||
this.resourceDTO(fhirResource, fhirVersion || fhirVersions.R4);
|
||||
}
|
||||
|
||||
|
||||
commonDTO(fhirResource:any){
|
||||
this.typeCoding = _.get(fhirResource, 'type.coding.0');
|
||||
this.whenPrepared = _.get(fhirResource, 'whenPrepared');
|
||||
this.type_coding = _.get(fhirResource, 'type.coding.0');
|
||||
this.when_prepared = _.get(fhirResource, 'whenPrepared');
|
||||
};
|
||||
|
||||
dstu2DTO(fhirResource:any){
|
||||
|
@ -46,10 +51,10 @@ export class MedicationDispenseModel {
|
|||
});
|
||||
};
|
||||
|
||||
this.medicationTitle = _.get(fhirResource, 'medicationReference.display');
|
||||
this.dosageInstruction = _.get(fhirResource, 'dosageInstruction', []);
|
||||
this.hasDosageInstruction = Array.isArray(this.dosageInstruction) && this.dosageInstruction.length > 0;
|
||||
this.dosageInstructionData = prepareDosageInstructionData(this.dosageInstruction || []);
|
||||
this.medication_title = _.get(fhirResource, 'medicationReference.display');
|
||||
this.dosage_instruction = _.get(fhirResource, 'dosageInstruction', []);
|
||||
this.has_dosage_instruction = Array.isArray(this.dosage_instruction) && this.dosage_instruction.length > 0;
|
||||
this.dosage_instruction_data = prepareDosageInstructionData(this.dosage_instruction || []);
|
||||
};
|
||||
|
||||
stu3DTO(fhirResource:any){
|
||||
|
@ -74,14 +79,14 @@ export class MedicationDispenseModel {
|
|||
return data;
|
||||
});
|
||||
};
|
||||
this.medicationTitle =
|
||||
this.medication_title =
|
||||
_.get(fhirResource, 'medicationReference.display') ||
|
||||
_.get(fhirResource, 'contained[0].code.coding[0].display');
|
||||
this.medicationCoding = _.get(fhirResource, 'contained[0].code.coding[0]');
|
||||
this.dosageInstruction = _.get(fhirResource, 'dosageInstruction', []);
|
||||
this.hasDosageInstruction =
|
||||
Array.isArray(this.dosageInstruction) && this.dosageInstruction.length > 0;
|
||||
this.dosageInstructionData = prepareDosageInstructionData(this.dosageInstruction);
|
||||
this.medication_coding = _.get(fhirResource, 'contained[0].code.coding[0]');
|
||||
this.dosage_instruction = _.get(fhirResource, 'dosageInstruction', []);
|
||||
this.has_dosage_instruction =
|
||||
Array.isArray(this.dosage_instruction) && this.dosage_instruction.length > 0;
|
||||
this.dosage_instruction_data = prepareDosageInstructionData(this.dosage_instruction);
|
||||
};
|
||||
|
||||
r4DTO(fhirResource:any){
|
||||
|
@ -106,14 +111,14 @@ export class MedicationDispenseModel {
|
|||
return data;
|
||||
});
|
||||
};
|
||||
this.medicationTitle =
|
||||
this.medication_title =
|
||||
_.get(fhirResource, 'medicationReference.display') ||
|
||||
_.get(fhirResource, 'contained[0].code.coding[0].display');
|
||||
this.medicationCoding = _.get(fhirResource, 'contained[0].code.coding[0]');
|
||||
this.dosageInstruction = _.get(fhirResource, 'dosageInstruction', []);
|
||||
this.hasDosageInstruction =
|
||||
Array.isArray(this.dosageInstruction) && this.dosageInstruction.length > 0;
|
||||
this.dosageInstructionData = prepareDosageInstructionData(this.dosageInstruction);
|
||||
this.medication_coding = _.get(fhirResource, 'contained[0].code.coding[0]');
|
||||
this.dosage_instruction = _.get(fhirResource, 'dosageInstruction', []);
|
||||
this.has_dosage_instruction =
|
||||
Array.isArray(this.dosage_instruction) && this.dosage_instruction.length > 0;
|
||||
this.dosage_instruction_data = prepareDosageInstructionData(this.dosage_instruction);
|
||||
};
|
||||
|
||||
resourceDTO(fhirResource:any, fhirVersion: fhirVersions){
|
||||
|
|
|
@ -1,25 +1,30 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {CodingModel} from '../datatypes/coding-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class MedicationModel extends FastenDisplayModel {
|
||||
|
||||
export class MedicationModel {
|
||||
title: CodingModel|undefined
|
||||
isBrand: string|undefined
|
||||
is_brand: string|undefined
|
||||
manufacturer: string|undefined
|
||||
hasProductForm:boolean|undefined
|
||||
productForm: string|undefined
|
||||
productIngredient: string|undefined
|
||||
hasProductIngredient: boolean|undefined
|
||||
hasProduct: boolean|undefined
|
||||
packageCoding: string|undefined
|
||||
hasPackageCoding: boolean|undefined
|
||||
hasImages: boolean|undefined
|
||||
has_product_form:boolean|undefined
|
||||
product_form: string|undefined
|
||||
product_ingredient: string|undefined
|
||||
has_product_ingredient: boolean|undefined
|
||||
has_product: boolean|undefined
|
||||
package_coding: string|undefined
|
||||
has_package_coding: boolean|undefined
|
||||
has_images: boolean|undefined
|
||||
images: string|undefined
|
||||
status: string|undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.Medication
|
||||
this.resourceDTO(fhirResource, fhirVersion || fhirVersions.R4);
|
||||
}
|
||||
|
||||
|
@ -32,46 +37,46 @@ export class MedicationModel {
|
|||
};
|
||||
|
||||
dstu2DTO(fhirResource:any){
|
||||
this.productForm = _.get(fhirResource, 'product.form.coding', []);
|
||||
this.hasProductForm = Array.isArray(this.productForm) && this.productForm.length > 0;
|
||||
this.productIngredient = _.get(fhirResource, 'product.ingredient', []);
|
||||
this.hasProductIngredient =
|
||||
Array.isArray(this.productIngredient) && this.productIngredient.length > 0;
|
||||
this.hasProduct = this.hasProductForm || this.hasProductIngredient;
|
||||
this.packageCoding = _.get(fhirResource, 'package.container.coding', []);
|
||||
this.hasPackageCoding =
|
||||
Array.isArray(this.packageCoding) && this.packageCoding.length > 0;
|
||||
this.isBrand = _.get(fhirResource, 'isBrand');
|
||||
this.product_form = _.get(fhirResource, 'product.form.coding', []);
|
||||
this.has_product_form = Array.isArray(this.product_form) && this.product_form.length > 0;
|
||||
this.product_ingredient = _.get(fhirResource, 'product.ingredient', []);
|
||||
this.has_product_ingredient =
|
||||
Array.isArray(this.product_ingredient) && this.product_ingredient.length > 0;
|
||||
this.has_product = this.has_product_form || this.has_product_ingredient;
|
||||
this.package_coding = _.get(fhirResource, 'package.container.coding', []);
|
||||
this.has_package_coding =
|
||||
Array.isArray(this.package_coding) && this.package_coding.length > 0;
|
||||
this.is_brand = _.get(fhirResource, 'isBrand');
|
||||
};
|
||||
|
||||
stu3DTO(fhirResource:any){
|
||||
this.productForm = _.get(fhirResource, 'form.coding', []);
|
||||
this.hasProductForm = Array.isArray(this.productForm) && this.productForm.length > 0;
|
||||
this.productIngredient = _.get(fhirResource, 'ingredient', []);
|
||||
this.hasProductIngredient = Array.isArray(this.productIngredient) && this.productIngredient.length > 0;
|
||||
this. hasProduct = this.hasProductForm || this.hasProductIngredient;
|
||||
this. packageCoding = _.get(fhirResource, 'package.container.coding', []);
|
||||
this. hasPackageCoding =
|
||||
Array.isArray(this.packageCoding) && this.packageCoding.length > 0;
|
||||
this.product_form = _.get(fhirResource, 'form.coding', []);
|
||||
this.has_product_form = Array.isArray(this.product_form) && this.product_form.length > 0;
|
||||
this.product_ingredient = _.get(fhirResource, 'ingredient', []);
|
||||
this.has_product_ingredient = Array.isArray(this.product_ingredient) && this.product_ingredient.length > 0;
|
||||
this. has_product = this.has_product_form || this.has_product_ingredient;
|
||||
this. package_coding = _.get(fhirResource, 'package.container.coding', []);
|
||||
this. has_package_coding =
|
||||
Array.isArray(this.package_coding) && this.package_coding.length > 0;
|
||||
this. images = _.get(fhirResource, 'image', []);
|
||||
this. hasImages = true;
|
||||
this. has_images = true;
|
||||
if (
|
||||
!Array.isArray(this.images) ||
|
||||
this.images.filter(item => !!item.url).length === 0
|
||||
) {
|
||||
this.hasImages = false;
|
||||
this.has_images = false;
|
||||
}
|
||||
this. isBrand = _.get(fhirResource, 'isBrand');
|
||||
this. is_brand = _.get(fhirResource, 'isBrand');
|
||||
};
|
||||
|
||||
r4DTO(fhirResource:any){
|
||||
this.productForm = _.get(fhirResource, 'form.coding', []);
|
||||
this.hasProductForm = Array.isArray(this.productForm) && this.productForm.length > 0;
|
||||
this.productIngredient = _.get(fhirResource, 'ingredient', []);
|
||||
this.hasProductIngredient =
|
||||
Array.isArray(this.productIngredient) && this.productIngredient.length > 0;
|
||||
this.product_form = _.get(fhirResource, 'form.coding', []);
|
||||
this.has_product_form = Array.isArray(this.product_form) && this.product_form.length > 0;
|
||||
this.product_ingredient = _.get(fhirResource, 'ingredient', []);
|
||||
this.has_product_ingredient =
|
||||
Array.isArray(this.product_ingredient) && this.product_ingredient.length > 0;
|
||||
this.status = _.get(fhirResource, 'status');
|
||||
this.hasProduct = this.hasProductForm || this.hasProductIngredient;
|
||||
this.has_product = this.has_product_form || this.has_product_ingredient;
|
||||
};
|
||||
|
||||
resourceDTO(fhirResource:any, fhirVersion: fhirVersions){
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import { MedicationRequestModel } from './medication-request-model';
|
||||
|
||||
describe('MedicationRequestModel', () => {
|
||||
it('should create an instance', () => {
|
||||
expect(new MedicationRequestModel()).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,40 @@
|
|||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {CodingModel} from '../datatypes/coding-model';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
import * as _ from "lodash";
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
|
||||
export class MedicationRequestModel extends FastenDisplayModel {
|
||||
|
||||
display: string|undefined
|
||||
medication_reference: ReferenceModel|undefined
|
||||
medication_codeable_concept: CodingModel|undefined
|
||||
reason_code: string|undefined
|
||||
dosage_instruction: string|undefined
|
||||
has_dosage_instruction: boolean|undefined
|
||||
requester: string|undefined
|
||||
created: string|undefined
|
||||
intent: string|undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.MedicationRequest
|
||||
|
||||
this.medication_reference = _.get(fhirResource, 'medicationReference');
|
||||
this.medication_codeable_concept = _.get(
|
||||
fhirResource,
|
||||
'medicationCodeableConcept.coding.0',
|
||||
);
|
||||
this.reason_code = _.get(fhirResource, 'reasonCode');
|
||||
this.dosage_instruction = _.get(fhirResource, 'dosageInstruction');
|
||||
this.has_dosage_instruction =
|
||||
Array.isArray(this.dosage_instruction) && this.dosage_instruction.length > 0;
|
||||
this.requester =
|
||||
_.get(fhirResource, 'requester.agent') || _.get(fhirResource, 'requester');
|
||||
this.created = _.get(fhirResource, 'authoredOn');
|
||||
this.intent = _.get(fhirResource, 'intent');
|
||||
|
||||
this.display = _.get(fhirResource, 'medicationCodeableConcept.text') || this.medication_codeable_concept?.display || _.get(fhirResource, 'medicationCodeableConcept.text') || this.medication_reference?.display
|
||||
}
|
||||
}
|
|
@ -1,47 +1,51 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {CodingModel} from '../datatypes/coding-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
|
||||
export class ObservationModel {
|
||||
export class ObservationModel extends FastenDisplayModel {
|
||||
|
||||
effectiveDate: string
|
||||
codeCodingDisplay: string
|
||||
codeText: string
|
||||
valueQuantityValue: string
|
||||
valueQuantityUnit: string
|
||||
effective_date: string
|
||||
code_coding_display: string
|
||||
code_text: string
|
||||
value_quantity_value: string
|
||||
value_quantity_unit: string
|
||||
status: string
|
||||
valueCodeableConceptText: string
|
||||
valueCodeableConceptCodingDisplay: string
|
||||
valueCodeableConceptCoding: string
|
||||
valueQuantityValueNumber: string
|
||||
value_codeable_concept_text: string
|
||||
value_codeable_concept_coding_display: string
|
||||
value_codeable_concept_coding: string
|
||||
value_quantity_value_number: string
|
||||
subject: string
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
this.effectiveDate = _.get(fhirResource, 'effectiveDateTime');
|
||||
this.codeCodingDisplay = _.get(fhirResource, 'code.coding.0.display');
|
||||
this.codeText = _.get(fhirResource, 'code.text', '');
|
||||
this.valueQuantityValue = _.get(fhirResource, 'valueQuantity.value', '');
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.Observation
|
||||
this.effective_date = _.get(fhirResource, 'effectiveDateTime');
|
||||
this.code_coding_display = _.get(fhirResource, 'code.coding.0.display');
|
||||
this.code_text = _.get(fhirResource, 'code.text', '');
|
||||
this.value_quantity_value = _.get(fhirResource, 'valueQuantity.value', '');
|
||||
// const issued = _.get(fhirResource, 'issued', '');
|
||||
this.valueQuantityUnit = _.get(fhirResource, 'valueQuantity.unit', '');
|
||||
this.value_quantity_unit = _.get(fhirResource, 'valueQuantity.unit', '');
|
||||
this.status = _.get(fhirResource, 'status', '');
|
||||
this.valueCodeableConceptText = _.get(
|
||||
this.value_codeable_concept_text = _.get(
|
||||
fhirResource,
|
||||
'valueCodeableConcept.text',
|
||||
);
|
||||
this.valueCodeableConceptCodingDisplay = _.get(
|
||||
this.value_codeable_concept_coding_display = _.get(
|
||||
fhirResource,
|
||||
'valueCodeableConcept.coding[0].display',
|
||||
);
|
||||
this.valueCodeableConceptCoding = _.get(
|
||||
this.value_codeable_concept_coding = _.get(
|
||||
fhirResource,
|
||||
'valueCodeableConcept.coding',
|
||||
[],
|
||||
);
|
||||
|
||||
this.valueQuantityValueNumber = this.valueQuantityValue;
|
||||
this.value_quantity_value_number = this.value_quantity_value;
|
||||
|
||||
this.subject = _.get(fhirResource, 'subject');
|
||||
}
|
||||
|
|
|
@ -1,17 +1,22 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {CodingModel} from '../datatypes/coding-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class OrganizationModel extends FastenDisplayModel {
|
||||
|
||||
export class OrganizationModel {
|
||||
identifier: string|undefined
|
||||
name: string|undefined
|
||||
addresses: string|undefined
|
||||
telecom: string|undefined
|
||||
typeCodings: any[]|undefined
|
||||
type_codings: any[]|undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.Organization
|
||||
this.resourceDTO(fhirResource, fhirVersion || fhirVersions.R4);
|
||||
}
|
||||
|
||||
|
@ -23,11 +28,11 @@ export class OrganizationModel {
|
|||
this.telecom = _.get(fhirResource, 'telecom');
|
||||
};
|
||||
dstu2DTO(fhirResource:any){
|
||||
this.typeCodings = _.get(fhirResource, 'type.coding');
|
||||
this.type_codings = _.get(fhirResource, 'type.coding');
|
||||
};
|
||||
stu3DTO(fhirResource:any){
|
||||
let typeCodings = _.get(fhirResource, 'type', []).map((type: { coding: any; }) => type.coding);
|
||||
this.typeCodings = _.flatten(typeCodings)
|
||||
this.type_codings = _.flatten(typeCodings)
|
||||
};
|
||||
|
||||
resourceDTO(fhirResource:any, fhirVersion:fhirVersions){
|
||||
|
|
|
@ -1,46 +1,52 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {CodingModel} from '../datatypes/coding-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class PatientModel extends FastenDisplayModel {
|
||||
|
||||
export class PatientModel {
|
||||
id: string|undefined
|
||||
patientName: string|undefined
|
||||
patientBirthDate: string|undefined
|
||||
patientGender: string|undefined
|
||||
patientContact: string|undefined
|
||||
patientAddress: string|undefined
|
||||
patientPhones: string|undefined
|
||||
communicationLanguage: string|undefined
|
||||
hasCommunicationLanguage: boolean|undefined
|
||||
hasPatientPhones: boolean|undefined
|
||||
patient_name: string|undefined
|
||||
patient_birthdate: string|undefined
|
||||
patient_gender: string|undefined
|
||||
patient_contact: string|undefined
|
||||
patient_address: string|undefined
|
||||
patient_phones: string|undefined
|
||||
communication_language: string|undefined
|
||||
has_communication_language: boolean|undefined
|
||||
has_patient_phones: boolean|undefined
|
||||
active: string|undefined
|
||||
activeStatus: string|undefined
|
||||
isDeceased: boolean|undefined
|
||||
deceasedDate: string|undefined
|
||||
active_status: string|undefined
|
||||
is_deceased: boolean|undefined
|
||||
deceased_date: string|undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.Patient
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
this.id = this.getId(fhirResource);
|
||||
this.patientName = this.getNames(fhirResource);
|
||||
this.patientBirthDate = this.getBirthDate(fhirResource);
|
||||
this.patientGender = this.getGender(fhirResource);
|
||||
this.patientContact = _.get(fhirResource, 'contact[0]');
|
||||
this.patientAddress = _.get(fhirResource, 'address[0]');
|
||||
this.patientPhones = _.get(fhirResource, 'telecom', []).filter(
|
||||
this.patient_name = this.getNames(fhirResource);
|
||||
this.patient_birthdate = this.getBirthDate(fhirResource);
|
||||
this.patient_gender = this.getGender(fhirResource);
|
||||
this.patient_contact = _.get(fhirResource, 'contact[0]');
|
||||
this.patient_address = _.get(fhirResource, 'address[0]');
|
||||
this.patient_phones = _.get(fhirResource, 'telecom', []).filter(
|
||||
(telecom: any) => telecom.system === 'phone',
|
||||
);
|
||||
let communicationLanguage = _.get(fhirResource, 'communication', [])
|
||||
.filter((item: any) => Boolean(_.get(item, 'language.coding', null)))
|
||||
.map((item: any) => item.language.coding);
|
||||
this.communicationLanguage = _.get(communicationLanguage, '0', []);
|
||||
this.hasCommunicationLanguage = !_.isEmpty(communicationLanguage);
|
||||
this.hasPatientPhones = !_.isEmpty(this.patientPhones);
|
||||
this.communication_language = _.get(communicationLanguage, '0', []);
|
||||
this.has_communication_language = !_.isEmpty(communicationLanguage);
|
||||
this.has_patient_phones = !_.isEmpty(this.patient_phones);
|
||||
this.active = _.get(fhirResource, 'active', false);
|
||||
this.activeStatus = this.active ? 'active' : 'inactive';
|
||||
this.active_status = this.active ? 'active' : 'inactive';
|
||||
let deceasedBoolean = _.get(fhirResource, 'deceasedBoolean', false);
|
||||
this.deceasedDate = _.get(fhirResource, 'deceasedDateTime');
|
||||
this.isDeceased = deceasedBoolean || this.deceasedDate;
|
||||
this.deceased_date = _.get(fhirResource, 'deceasedDateTime');
|
||||
this.is_deceased = deceasedBoolean || this.deceased_date;
|
||||
|
||||
}
|
||||
getId(fhirResource: any) {
|
||||
|
|
|
@ -1,24 +1,29 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {CodingModel} from '../datatypes/coding-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class PractitionerModel extends FastenDisplayModel {
|
||||
|
||||
export class PractitionerModel {
|
||||
identifier: string|undefined
|
||||
name: string|undefined
|
||||
name: any|undefined
|
||||
gender: string|undefined
|
||||
status: string|undefined
|
||||
isContactData: boolean|undefined
|
||||
is_contact_data: boolean|undefined
|
||||
contactData: {
|
||||
name: string,
|
||||
relationship: string
|
||||
}|undefined
|
||||
telecom: string|undefined
|
||||
address: string|undefined
|
||||
birthDate: string|undefined
|
||||
birthdate: string|undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.Practitioner
|
||||
this.resourceDTO(fhirResource, fhirVersion || fhirVersions.R4);
|
||||
}
|
||||
|
||||
|
@ -28,8 +33,8 @@ export class PractitionerModel {
|
|||
this.identifier = _.get(fhirResource, 'identifier', '');
|
||||
this.gender = _.get(fhirResource, 'gender', '');
|
||||
this.status = _.get(fhirResource, 'active') === true ? 'active' : '';
|
||||
this.isContactData = _.has(fhirResource, 'contact[0]');
|
||||
this.birthDate = _.get(fhirResource, 'birthDate');
|
||||
this.is_contact_data = _.has(fhirResource, 'contact[0]');
|
||||
this.birthdate = _.get(fhirResource, 'birthDate');
|
||||
this.contactData = {
|
||||
name: _.get(fhirResource, 'contact[0].name'),
|
||||
relationship: _.get(fhirResource, 'contact[0].relationship[0].text'),
|
||||
|
|
|
@ -1,17 +1,23 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {CodingModel} from '../datatypes/coding-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class PractitionerRoleModel extends FastenDisplayModel {
|
||||
|
||||
export class PractitionerRoleModel {
|
||||
codes: string|undefined
|
||||
status: string|undefined
|
||||
specialties: string|undefined
|
||||
organization: string|undefined
|
||||
practitioner: string|undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.PractitionerRole
|
||||
|
||||
this.resourceDTO(fhirResource, fhirVersion || fhirVersions.R4);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,48 +1,54 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {CodingModel} from '../datatypes/coding-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class ProcedureModel extends FastenDisplayModel {
|
||||
|
||||
export class ProcedureModel {
|
||||
display: string|undefined;
|
||||
status: string|undefined;
|
||||
hasPerformedDateTime: boolean|undefined;
|
||||
performedDateTime: string|undefined;
|
||||
performedPeriodStart: string|undefined;
|
||||
performedPeriodEnd: string|undefined;
|
||||
hasPerformedPeriod: boolean|undefined;
|
||||
hasCoding: boolean|undefined;
|
||||
has_performed_datetime: boolean|undefined;
|
||||
performed_datetime: string|undefined;
|
||||
performed_period_start: string|undefined;
|
||||
performed_period_end: string|undefined;
|
||||
has_performed_period: boolean|undefined;
|
||||
has_coding: boolean|undefined;
|
||||
coding: string|undefined;
|
||||
category: string|undefined;
|
||||
locationReference: string|undefined;
|
||||
hasPerformerData: boolean|undefined;
|
||||
location_reference: string|undefined;
|
||||
has_performer_data: boolean|undefined;
|
||||
performer: string|undefined;
|
||||
hasReasonCode: boolean|undefined;
|
||||
reasonCode: string|undefined;
|
||||
hasNote: boolean|undefined;
|
||||
has_reason_code: boolean|undefined;
|
||||
reason_code: string|undefined;
|
||||
has_note: boolean|undefined;
|
||||
note: string|undefined;
|
||||
outcome: string|undefined;
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.Procedure
|
||||
|
||||
this.display =
|
||||
_.get(fhirResource, 'code.coding[0].display') ||
|
||||
_.get(fhirResource, 'code.text');
|
||||
this.status = _.get(fhirResource, 'status', '');
|
||||
this.hasPerformedDateTime = _.has(fhirResource, 'performedDateTime');
|
||||
this.performedDateTime = _.get(fhirResource, 'performedDateTime');
|
||||
this.performedPeriodStart = _.get(fhirResource, 'performedPeriod.start');
|
||||
this.performedPeriodEnd = _.get(fhirResource, 'performedPeriod.end');
|
||||
this.hasPerformedPeriod = !!(this.performedPeriodStart || this.performedPeriodEnd);
|
||||
this.hasCoding = _.has(fhirResource, 'code.coding');
|
||||
this.has_performed_datetime = _.has(fhirResource, 'performedDateTime');
|
||||
this.performed_datetime = _.get(fhirResource, 'performedDateTime');
|
||||
this.performed_period_start = _.get(fhirResource, 'performedPeriod.start');
|
||||
this.performed_period_end = _.get(fhirResource, 'performedPeriod.end');
|
||||
this.has_performed_period = !!(this.performed_period_start || this.performed_period_end);
|
||||
this.has_coding = _.has(fhirResource, 'code.coding');
|
||||
this.coding = _.get(fhirResource, 'code.coding', []);
|
||||
this.category = _.get(fhirResource, 'category.coding[0]');
|
||||
this.locationReference = _.get(fhirResource, 'location');
|
||||
this.hasPerformerData = _.has(fhirResource, 'performer.0.actor.display');
|
||||
this.location_reference = _.get(fhirResource, 'location');
|
||||
this.has_performer_data = _.has(fhirResource, 'performer.0.actor.display');
|
||||
this.performer = _.get(fhirResource, 'performer', []);
|
||||
this.hasReasonCode = _.has(fhirResource, 'reasonCode');
|
||||
this.reasonCode = _.get(fhirResource, 'reasonCode', []);
|
||||
this.hasNote = _.has(fhirResource, 'note');
|
||||
this.has_reason_code = _.has(fhirResource, 'reasonCode');
|
||||
this.reason_code = _.get(fhirResource, 'reasonCode', []);
|
||||
this.has_note = _.has(fhirResource, 'note');
|
||||
this.note = _.get(fhirResource, 'note', []);
|
||||
this.outcome = _.get(fhirResource, 'outcome');
|
||||
|
||||
|
|
|
@ -1,28 +1,34 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {CodingModel} from '../datatypes/coding-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class RelatedPersonModel extends FastenDisplayModel {
|
||||
|
||||
export class RelatedPersonModel {
|
||||
patient: string|undefined
|
||||
name: string|undefined
|
||||
birthDate: string|undefined
|
||||
birthdate: string|undefined
|
||||
gender: string|undefined
|
||||
address: string|undefined
|
||||
relatedPersonTelecom: string|undefined
|
||||
related_person_telecom: string|undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.RelatedPerson
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
this.resourceDTO(fhirResource, fhirVersion || fhirVersions.R4);
|
||||
}
|
||||
|
||||
|
||||
commonDTO(fhirResource:any){
|
||||
this.patient = _.get(fhirResource, 'patient');
|
||||
this.birthDate = _.get(fhirResource, 'birthDate');
|
||||
this.birthdate = _.get(fhirResource, 'birthDate');
|
||||
this.gender = _.get(fhirResource, 'gender');
|
||||
this.address = _.get(fhirResource, 'address[0]');
|
||||
this.relatedPersonTelecom = _.get(fhirResource, 'telecom', []).filter(
|
||||
this.related_person_telecom = _.get(fhirResource, 'telecom', []).filter(
|
||||
(telecom: any) => telecom.system === 'phone',
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,30 +1,36 @@
|
|||
import {fhirVersions} from '../constants';
|
||||
import {fhirVersions, ResourceType} from '../constants';
|
||||
import * as _ from "lodash";
|
||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||
import {ReferenceModel} from '../datatypes/reference-model';
|
||||
import {CodingModel} from '../datatypes/coding-model';
|
||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||
import {FastenOptions} from '../fasten/fasten-options';
|
||||
|
||||
export class ResearchStudyModel extends FastenDisplayModel {
|
||||
|
||||
export class ResearchStudyModel {
|
||||
title:string|undefined
|
||||
status:string|undefined
|
||||
categoryCoding:string|undefined
|
||||
focusCoding:string|undefined
|
||||
protocolReference:string|undefined
|
||||
partOfReference:string|undefined
|
||||
category_coding:string|undefined
|
||||
focus_coding:string|undefined
|
||||
protocol_reference:string|undefined
|
||||
part_of_reference:string|undefined
|
||||
contacts:string|undefined
|
||||
keywordConcepts:string|undefined
|
||||
keyword_concepts:string|undefined
|
||||
period:string|undefined
|
||||
enrollmentReferences:string|undefined
|
||||
sponsorReference:string|undefined
|
||||
principalInvestigatorReference:string|undefined
|
||||
siteReferences:string|undefined
|
||||
enrollment_references:string|undefined
|
||||
sponsor_reference:string|undefined
|
||||
principal_investigator_reference:string|undefined
|
||||
site_references:string|undefined
|
||||
comments:string|undefined
|
||||
description:string|undefined
|
||||
arms:string|undefined
|
||||
location:string|undefined
|
||||
primaryPurposeType:string|undefined
|
||||
primary_purpose_type:string|undefined
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||
super(fastenOptions)
|
||||
this.source_resource_type = ResourceType.ResearchStudy
|
||||
|
||||
constructor(fhirResource: any, fhirVersion?: fhirVersions) {
|
||||
this.resourceDTO(fhirResource, fhirVersion || fhirVersions.R4);
|
||||
}
|
||||
|
||||
|
@ -32,24 +38,24 @@ export class ResearchStudyModel {
|
|||
commonDTO(fhirResource:any) {
|
||||
this.title = _.get(fhirResource, 'title', 'Research Study');
|
||||
this.status = _.get(fhirResource, 'status');
|
||||
this.categoryCoding = _.get(fhirResource, 'category[0].coding[0]');
|
||||
this.focusCoding = _.get(fhirResource, 'focus[0].coding[0]');
|
||||
this.protocolReference = _.get(fhirResource, 'protocol');
|
||||
this.partOfReference = _.get(fhirResource, 'partOf');
|
||||
this.category_coding = _.get(fhirResource, 'category[0].coding[0]');
|
||||
this.focus_coding = _.get(fhirResource, 'focus[0].coding[0]');
|
||||
this.protocol_reference = _.get(fhirResource, 'protocol');
|
||||
this.part_of_reference = _.get(fhirResource, 'partOf');
|
||||
this.contacts = _.get(fhirResource, 'contact', []).map((contact: any) => {
|
||||
const name = _.get(contact, 'name');
|
||||
const telecoms = _.get(contact, 'telecom');
|
||||
return { name, telecoms };
|
||||
});
|
||||
this.keywordConcepts = _.get(fhirResource, 'keyword', []);
|
||||
this.keyword_concepts = _.get(fhirResource, 'keyword', []);
|
||||
this.period = _.get(fhirResource, 'period', {});
|
||||
this.enrollmentReferences = _.get(fhirResource, 'enrollment', []);
|
||||
this.sponsorReference = _.get(fhirResource, 'sponsor');
|
||||
this.principalInvestigatorReference = _.get(
|
||||
this.enrollment_references = _.get(fhirResource, 'enrollment', []);
|
||||
this.sponsor_reference = _.get(fhirResource, 'sponsor');
|
||||
this.principal_investigator_reference = _.get(
|
||||
fhirResource,
|
||||
'principalInvestigator',
|
||||
);
|
||||
this.siteReferences = _.get(fhirResource, 'site', []);
|
||||
this.site_references = _.get(fhirResource, 'site', []);
|
||||
this.comments = _.get(fhirResource, 'note', []);
|
||||
this.description = _.get(fhirResource, 'description');
|
||||
this.arms = _.get(fhirResource, 'arm', []).map((arm: any) => {
|
||||
|
@ -62,7 +68,7 @@ export class ResearchStudyModel {
|
|||
|
||||
r4DTO(fhirResource:any) {
|
||||
this.location = _.get(fhirResource, 'location');
|
||||
this.primaryPurposeType = _.get(fhirResource, 'primaryPurposeType');
|
||||
this.primary_purpose_type = _.get(fhirResource, 'primaryPurposeType');
|
||||
|
||||
};
|
||||
|
||||
|
|
4
go.mod
4
go.mod
|
@ -5,7 +5,7 @@ go 1.18
|
|||
require (
|
||||
github.com/analogj/go-util v0.0.0-20210417161720-39b497cca03b
|
||||
github.com/dominikbraun/graph v0.15.0
|
||||
github.com/fastenhealth/fasten-sources v0.0.11
|
||||
github.com/fastenhealth/fasten-sources v0.0.14
|
||||
github.com/gin-gonic/gin v1.8.1
|
||||
github.com/glebarez/sqlite v1.5.0
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2
|
||||
|
@ -17,6 +17,7 @@ require (
|
|||
github.com/urfave/cli/v2 v2.11.2
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
|
||||
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17
|
||||
golang.org/x/net v0.2.0
|
||||
gorm.io/datatypes v1.0.7
|
||||
gorm.io/gorm v1.24.1
|
||||
)
|
||||
|
@ -62,7 +63,6 @@ require (
|
|||
github.com/subosito/gotenv v1.3.0 // indirect
|
||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
golang.org/x/net v0.2.0 // indirect
|
||||
golang.org/x/oauth2 v0.2.0 // indirect
|
||||
golang.org/x/sys v0.2.0 // indirect
|
||||
golang.org/x/term v0.2.0 // indirect
|
||||
|
|
4
go.sum
4
go.sum
|
@ -74,8 +74,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
|||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fastenhealth/fasten-sources v0.0.11 h1:GuU/fEs7yMtFY+Pe0j9z4XzJQtEZ3Yx3pDNqzsAWnOk=
|
||||
github.com/fastenhealth/fasten-sources v0.0.11/go.mod h1:NBh0n2OBwJC10CHqkIx4WYQcDBRywJU4TAR8rSfR2Ts=
|
||||
github.com/fastenhealth/fasten-sources v0.0.14 h1:+SGn/gzhC9MU2OzZy8hn9giiuOs6lJgIKPReY8STe5U=
|
||||
github.com/fastenhealth/fasten-sources v0.0.14/go.mod h1:NBh0n2OBwJC10CHqkIx4WYQcDBRywJU4TAR8rSfR2Ts=
|
||||
github.com/fastenhealth/gofhir-models v0.0.4 h1:Q//StwNXGfK+WAS2DckGBHAP1R4cHMRZEF/sLGgmR04=
|
||||
github.com/fastenhealth/gofhir-models v0.0.4/go.mod h1:xB8ikGxu3bUq2b1JYV+CZpHqBaLXpOizFR0eFBCunis=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
|
|
Loading…
Reference in New Issue