From 2061684aed9a9c78f5383f6da354bd5d0b5b3c67 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Thu, 23 Nov 2023 08:51:01 -0800 Subject: [PATCH] WIP adding medical history timeline view. (#325) --- backend/pkg/database/gorm_repository_graph.go | 102 ++++----- .../database/gorm_repository_graph_test.go | 192 ++++++++++++++++ backend/pkg/database/interface.go | 2 +- .../database/testdata/epic_fhircamila.ndjson | 58 +++++ backend/pkg/models/resource_graph_options.go | 2 +- backend/pkg/web/handler/resource_fhir.go | 27 +-- backend/pkg/web/server.go | 2 +- .../fhir-resource/fhir-resource.component.ts | 16 +- .../fhir/resources/binary/binary.component.ts | 1 + .../diagnostic-report.component.ts | 21 +- .../location/location.component.html | 20 ++ .../location/location.component.scss | 0 .../location/location.component.spec.ts | 23 ++ .../resources/location/location.component.ts | 77 +++++++ .../observation/observation.component.html | 28 +++ .../observation/observation.component.scss | 0 .../observation/observation.component.spec.ts | 23 ++ .../observation/observation.component.ts | 154 +++++++++++++ .../organization/organization.component.html | 19 ++ .../organization/organization.component.scss | 0 .../organization.component.spec.ts | 23 ++ .../organization/organization.component.ts | 77 +++++++ .../components/footer/footer.component.html | 2 +- .../report-labs-observation.component.ts | 24 +- ...t-medical-history-condition.component.html | 3 - ...ical-history-timeline-panel.component.html | 157 ++++++++++++++ ...ical-history-timeline-panel.component.scss | 205 ++++++++++++++++++ ...l-history-timeline-panel.component.spec.ts | 23 ++ ...edical-history-timeline-panel.component.ts | 38 ++++ frontend/src/app/components/shared.module.ts | 71 +++--- .../models/fasten/resource-graph-response.ts | 7 - .../medical-history.component.html | 24 +- .../medical-history.component.scss | 23 ++ .../medical-history.component.ts | 169 +++++++++------ .../src/app/services/fasten-api.service.ts | 13 +- .../resources/diagnostic-report-model.ts | 12 +- .../lib/models/resources/encounter-model.ts | 3 + .../lib/models/resources/observation-model.ts | 17 +- .../models/resources/organization-model.ts | 7 +- 39 files changed, 1450 insertions(+), 215 deletions(-) create mode 100644 backend/pkg/database/gorm_repository_graph_test.go create mode 100644 backend/pkg/database/testdata/epic_fhircamila.ndjson create mode 100644 frontend/src/app/components/fhir/resources/location/location.component.html create mode 100644 frontend/src/app/components/fhir/resources/location/location.component.scss create mode 100644 frontend/src/app/components/fhir/resources/location/location.component.spec.ts create mode 100644 frontend/src/app/components/fhir/resources/location/location.component.ts create mode 100644 frontend/src/app/components/fhir/resources/observation/observation.component.html create mode 100644 frontend/src/app/components/fhir/resources/observation/observation.component.scss create mode 100644 frontend/src/app/components/fhir/resources/observation/observation.component.spec.ts create mode 100644 frontend/src/app/components/fhir/resources/observation/observation.component.ts create mode 100644 frontend/src/app/components/fhir/resources/organization/organization.component.html create mode 100644 frontend/src/app/components/fhir/resources/organization/organization.component.scss create mode 100644 frontend/src/app/components/fhir/resources/organization/organization.component.spec.ts create mode 100644 frontend/src/app/components/fhir/resources/organization/organization.component.ts create mode 100644 frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.html create mode 100644 frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.scss create mode 100644 frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.spec.ts create mode 100644 frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.ts diff --git a/backend/pkg/database/gorm_repository_graph.go b/backend/pkg/database/gorm_repository_graph.go index 1ee372d3..21b843a0 100644 --- a/backend/pkg/database/gorm_repository_graph.go +++ b/backend/pkg/database/gorm_repository_graph.go @@ -29,17 +29,10 @@ func (rp *VertexResourcePlaceholder) ID() string { // Retrieve a list of all fhir resources (vertex), and a list of all associations (edge) // Generate a graph // return list of root nodes, and their flattened related resources. -func (gr *GormRepository) GetFlattenedResourceGraph(ctx context.Context, graphType pkg.ResourceGraphType, options models.ResourceGraphOptions) (map[string][]*models.ResourceBase, *models.ResourceGraphMetadata, error) { +func (gr *GormRepository) GetFlattenedResourceGraph(ctx context.Context, graphType pkg.ResourceGraphType, options models.ResourceGraphOptions) (map[string][]*models.ResourceBase, error) { currentUser, currentUserErr := gr.GetCurrentUser(ctx) if currentUserErr != nil { - return nil, nil, currentUserErr - } - - //initialize the graph results metadata - resourceGraphMetadata := models.ResourceGraphMetadata{ - TotalElements: 0, - PageSize: 20, //TODO: replace this with pkg.DefaultPageSize - Page: options.Page, + return nil, currentUserErr } // Get list of all (non-reciprocal) relationships @@ -52,18 +45,13 @@ func (gr *GormRepository) GetFlattenedResourceGraph(ctx context.Context, graphTy }). Find(&relatedResourceRelationships) if result.Error != nil { - return nil, nil, result.Error + return nil, result.Error } + log.Printf("found %d related resources", len(relatedResourceRelationships)) //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()) - - //// Get list of all resources TODO - REPLACED THIS - //wrappedResourceModels, err := gr.ListResources(ctx, models.ListResourceQueryOptions{}) - //if err != nil { - // return nil, err - //} + g := graph.New(resourceVertexId, graph.Directed(), graph.Rooted()) //add vertices to the graph (must be done first) //we don't want to request all resources from the database, so we will create a placeholder vertex for each resource. @@ -105,7 +93,7 @@ func (gr *GormRepository) GetFlattenedResourceGraph(ctx context.Context, graphTy &resourcePlaceholder, ) if err != nil { - return nil, nil, fmt.Errorf("an error occurred while adding vertex: %v", err) + return nil, fmt.Errorf("an error occurred while adding vertex: %v", err) } } @@ -142,7 +130,7 @@ func (gr *GormRepository) GetFlattenedResourceGraph(ctx context.Context, graphTy // } adjacencyMap, err := g.AdjacencyMap() if err != nil { - return nil, nil, fmt.Errorf("error while generating AdjacencyMap: %v", err) + return nil, fmt.Errorf("error while generating AdjacencyMap: %v", err) } // For a directed graph, PredecessorMap is the complement of AdjacencyMap. This is because in a directed graph, only @@ -151,12 +139,12 @@ func (gr *GormRepository) GetFlattenedResourceGraph(ctx context.Context, graphTy // ie. "empty" verticies in this map are "root" nodes. predecessorMap, err := g.PredecessorMap() if err != nil { - return nil, nil, fmt.Errorf("error while generating PredecessorMap: %v", err) + return nil, fmt.Errorf("error while generating PredecessorMap: %v", err) } // Doing this in one massive function, because passing graph by reference is difficult due to generics. - // Step 1: use predecessorMap to find all "root" resources (eg. MedicalHistory - encounters and conditions). store those nodes in their respective lists. + // Step 1: use predecessorMap to find all "root" resources (eg. MedicalHistory - encounters and EOB). store those nodes in their respective lists. resourcePlaceholderListDictionary := map[string][]*VertexResourcePlaceholder{} sources, _, sourceFlattenLevel := getSourcesAndSinksForGraphType(graphType) @@ -201,11 +189,10 @@ func (gr *GormRepository) GetFlattenedResourceGraph(ctx context.Context, graphTy // Step 2: now that we've created a relationship graph using placeholders, we need to determine which page of resources to return // and look up the actual resources from the database. - resourceListDictionary, totalElements, err := gr.InflateResourceGraphAtPage(resourcePlaceholderListDictionary, options.Page) + resourceListDictionary, err := gr.InflateSelectedResourcesInResourceGraph(currentUser, resourcePlaceholderListDictionary, options) if err != nil { - return nil, nil, fmt.Errorf("error while paginating & inflating resource graph: %v", err) + return nil, fmt.Errorf("error while paginating & inflating resource graph: %v", err) } - resourceGraphMetadata.TotalElements = totalElements // Step 3: define a function. When given a resource, should find all related resources, flatten the heirarchy and set the RelatedResourceFhir list flattenRelatedResourcesFn := func(resource *models.ResourceBase) { @@ -220,22 +207,30 @@ func (gr *GormRepository) GetFlattenedResourceGraph(ctx context.Context, graphTy resource.RelatedResource = []*models.ResourceBase{} + //make sure we don't keep traversing the same node over and over again + visited := map[string]bool{ + vertexId: true, + } + //get all the resource placeholders associated with this node //TODO: handle error? graph.DFS(g, vertexId, func(relatedVertexId string) bool { + relatedResourcePlaceholder, _ := g.Vertex(relatedVertexId) //skip the current resourcePlaceholder if it's referenced in this list. + //skip any "visted" nodes //also skip the current resourcePlaceholder if its a Binary resourcePlaceholder (which is a special case) - if vertexId != resourceVertexId(relatedResourcePlaceholder) && relatedResourcePlaceholder.ResourceType != "Binary" { + if _, hasVisited := visited[resourceVertexId(relatedResourcePlaceholder)]; !hasVisited && relatedResourcePlaceholder.ResourceType != "Binary" { relatedResource, err := gr.GetResourceByResourceTypeAndId(ctx, relatedResourcePlaceholder.ResourceType, relatedResourcePlaceholder.ResourceID) if err != nil { - gr.Logger.Warnf("ignoring, cannot safely handle error which occurred while getting related resource: %v", err) - return true + gr.Logger.Warnf("ignoring, cannot safely handle error which occurred while getting related resource (%s/%s): %v", relatedResourcePlaceholder.ResourceType, relatedResourcePlaceholder.ResourceID, err) + return false } resource.RelatedResource = append( resource.RelatedResource, relatedResource, ) + visited[resourceVertexId(relatedResourcePlaceholder)] = true } return false }) @@ -288,36 +283,50 @@ func (gr *GormRepository) GetFlattenedResourceGraph(ctx context.Context, graphTy // Step 5: return the populated resource list dictionary - return resourceListDictionary, &resourceGraphMetadata, nil + return resourceListDictionary, nil } -// LoadResourceGraphAtPage - this function will take a dictionary of placeholder "sources" graph and load the actual resources from the database, for a specific page +// InflateSelectedResourcesInResourceGraph - this function will take a dictionary of placeholder "sources" graph and load the selected resources (and their descendants) from the database. // - first, it will load all the "source" resources (eg. Encounter, Condition, etc) // - sort the root resources by date, desc // - use the page number + page size to determine which root resources to return // - return a dictionary of "source" resource lists -func (gr *GormRepository) InflateResourceGraphAtPage(resourcePlaceholderListDictionary map[string][]*VertexResourcePlaceholder, page int) (map[string][]*models.ResourceBase, int, error) { - totalElements := 0 - // Step 3a: since we cant calulate the sort order until the resources are loaded, we need to load all the root resources first. +func (gr *GormRepository) InflateSelectedResourcesInResourceGraph(currentUser *models.User, resourcePlaceholderListDictionary map[string][]*VertexResourcePlaceholder, options models.ResourceGraphOptions) (map[string][]*models.ResourceBase, error) { + + // Step 3a: group the selected resources by type, so we only need to do 1 query per type + selectedResourceIdsByResourceType := map[string][]models.OriginBase{} + + for _, resourceId := range options.ResourcesIds { + if _, ok := selectedResourceIdsByResourceType[resourceId.SourceResourceType]; !ok { + selectedResourceIdsByResourceType[resourceId.SourceResourceType] = []models.OriginBase{} + } + selectedResourceIdsByResourceType[resourceId.SourceResourceType] = append(selectedResourceIdsByResourceType[resourceId.SourceResourceType], resourceId) + } + + // Step 3b: query the database for all the selected resources //TODO: maybe its more performant to query each resource by type/id/source, since they are indexed already? rootWrappedResourceModels := []models.ResourceBase{} - for resourceType, _ := range resourcePlaceholderListDictionary { - // resourcePlaceholderListDictionary contains top level resource types (eg. Encounter, Condition, etc) + for resourceType, _ := range selectedResourceIdsByResourceType { + // selectedResourceIdsByResourceType contains selected resources grouped by ty[e types (eg. Encounter, Condition, etc) + //convert these to a list of interface{} for the query selectList := [][]interface{}{} - for ndx, _ := range resourcePlaceholderListDictionary[resourceType] { + for ndx, _ := range selectedResourceIdsByResourceType[resourceType] { + + selectedResource := selectedResourceIdsByResourceType[resourceType][ndx] + selectList = append(selectList, []interface{}{ - resourcePlaceholderListDictionary[resourceType][ndx].UserID, - resourcePlaceholderListDictionary[resourceType][ndx].SourceID, - resourcePlaceholderListDictionary[resourceType][ndx].ResourceType, - resourcePlaceholderListDictionary[resourceType][ndx].ResourceID, + currentUser.ID, + selectedResource.SourceID, + selectedResource.SourceResourceType, + selectedResource.SourceResourceID, }) } tableName, err := databaseModel.GetTableNameByResourceType(resourceType) if err != nil { - return nil, totalElements, err + return nil, err } var tableWrappedResourceModels []models.ResourceBase gr.GormClient. @@ -332,13 +341,7 @@ func (gr *GormRepository) InflateResourceGraphAtPage(resourcePlaceholderListDict //sort rootWrappedResourceModels = utils.SortResourceListByDate(rootWrappedResourceModels) - //calculate total elements - totalElements = len(rootWrappedResourceModels) - - //paginate (by calculating window for the slice) - rootWrappedResourceModels = utils.PaginateResourceList(rootWrappedResourceModels, page, 20) //todo: replace size with pkg.ResourceListPageSize - - // Step 3b: now that we have the root resources, lets generate a dictionary of resource lists, keyed by resource type + // Step 3c: now that we have the selected root resources, lets generate a dictionary of resource lists, keyed by resource type resourceListDictionary := map[string][]*models.ResourceBase{} for ndx, _ := range rootWrappedResourceModels { resourceType := rootWrappedResourceModels[ndx].SourceResourceType @@ -349,7 +352,7 @@ func (gr *GormRepository) InflateResourceGraphAtPage(resourcePlaceholderListDict } // Step 4: return the populated resource list dictionary - return resourceListDictionary, totalElements, nil + return resourceListDictionary, nil } // We need to support the following types of graphs: @@ -445,11 +448,10 @@ func getSourcesAndSinksForGraphType(graphType pkg.ResourceGraphType) ([][]string switch graphType { case pkg.ResourceGraphTypeMedicalHistory: sources = [][]string{ - {"condition", "composition"}, {"encounter", "explanationofbenefit"}, } sinks = [][]string{ - {"location", "device", "organization", "practitioner", "medication", "patient", "coverage"}, //resources that are shared across multiple conditions + {"condition", "composition", "location", "device", "organization", "practitioner", "medication", "patient", "coverage"}, //resources that are shared across multiple conditions {"binary"}, } sourceFlattenRelated = map[string]bool{ diff --git a/backend/pkg/database/gorm_repository_graph_test.go b/backend/pkg/database/gorm_repository_graph_test.go new file mode 100644 index 00000000..49613ddc --- /dev/null +++ b/backend/pkg/database/gorm_repository_graph_test.go @@ -0,0 +1,192 @@ +package database + +import ( + "context" + "fmt" + "github.com/fastenhealth/fasten-onprem/backend/pkg" + mock_config "github.com/fastenhealth/fasten-onprem/backend/pkg/config/mock" + "github.com/fastenhealth/fasten-onprem/backend/pkg/event_bus" + "github.com/fastenhealth/fasten-onprem/backend/pkg/models" + sourceFactory "github.com/fastenhealth/fasten-sources/clients/factory" + sourcePkg "github.com/fastenhealth/fasten-sources/pkg" + "github.com/golang/mock/gomock" + "github.com/google/uuid" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "io/ioutil" + "log" + "os" + "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 RepositoryGraphTestSuite 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 *RepositoryGraphTestSuite) 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 *RepositoryGraphTestSuite) AfterTest(suiteName, testName string) { + suite.MockCtrl.Finish() + os.Remove(suite.TestDatabase.Name()) + os.Remove(suite.TestDatabase.Name() + "-shm") + os.Remove(suite.TestDatabase.Name() + "-wal") +} + +// 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 TestRepositoryGraphTestSuite(t *testing.T) { + suite.Run(t, new(RepositoryGraphTestSuite)) +} + +func (suite *RepositoryGraphTestSuite) TestGetFlattenedResourceGraph() { + //setup + fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) + fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() + fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() + fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() + dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) + 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) + require.NotEmpty(suite.T(), userModel.ID) + require.NotEqual(suite.T(), uuid.Nil, userModel.ID) + authContext := context.WithValue(context.Background(), pkg.ContextKeyTypeAuthUsername, "test_username") + + testSourceCredential := models.SourceCredential{ + ModelBase: models.ModelBase{ + ID: uuid.New(), + }, + UserID: userModel.ID, + } + err = dbRepo.CreateSource(authContext, &testSourceCredential) + require.NoError(suite.T(), err) + + bundleFile, err := os.Open("./testdata/Abraham100_Heller342_262b819a-5193-404a-9787-b7f599358035.json") + require.NoError(suite.T(), err) + + testLogger := logrus.WithFields(logrus.Fields{ + "type": "test", + }) + + manualClient, err := sourceFactory.GetSourceClient(sourcePkg.FastenLighthouseEnvSandbox, sourcePkg.SourceTypeManual, authContext, testLogger, &testSourceCredential) + + summary, err := manualClient.SyncAllBundle(dbRepo, bundleFile, sourcePkg.FhirVersion401) + require.NoError(suite.T(), err) + require.Equal(suite.T(), 198, summary.TotalResources) + require.Equal(suite.T(), 234, len(summary.UpdatedResources)) + + //test + options := models.ResourceGraphOptions{ + ResourcesIds: []models.OriginBase{ + { + SourceID: testSourceCredential.ID, + SourceResourceType: "Encounter", + SourceResourceID: "d9a7c76f-dfe4-41c6-9924-82a405613f44", + }, + }, + } + + flattenedGraph, err := dbRepo.GetFlattenedResourceGraph(authContext, pkg.ResourceGraphTypeMedicalHistory, options) + require.NoError(suite.T(), err) + require.NotNil(suite.T(), flattenedGraph) + //validated using http://clinfhir.com/bundleVisualizer.html + + require.Equal(suite.T(), 1, len(flattenedGraph["Encounter"])) + require.Equal(suite.T(), 27, len(flattenedGraph["Encounter"][0].RelatedResource)) + +} + +// Bug: the Epic enounter graph was not consistently return the same related resources +func (suite *RepositoryGraphTestSuite) TestGetFlattenedResourceGraph_NDJson() { + //setup + fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) + fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() + fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() + fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() + dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) + 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) + require.NotEmpty(suite.T(), userModel.ID) + require.NotEqual(suite.T(), uuid.Nil, userModel.ID) + authContext := context.WithValue(context.Background(), pkg.ContextKeyTypeAuthUsername, "test_username") + + testSourceCredential := models.SourceCredential{ + ModelBase: models.ModelBase{ + ID: uuid.New(), + }, + UserID: userModel.ID, + } + err = dbRepo.CreateSource(authContext, &testSourceCredential) + require.NoError(suite.T(), err) + + bundleFile, err := os.Open("./testdata/epic_fhircamila.ndjson") + require.NoError(suite.T(), err) + + testLogger := logrus.WithFields(logrus.Fields{ + "type": "test", + }) + + manualClient, err := sourceFactory.GetSourceClient(sourcePkg.FastenLighthouseEnvSandbox, sourcePkg.SourceTypeManual, authContext, testLogger, &testSourceCredential) + + summary, err := manualClient.SyncAllBundle(dbRepo, bundleFile, sourcePkg.FhirVersion401) + require.NoError(suite.T(), err) + require.Equal(suite.T(), 58, summary.TotalResources) + require.Equal(suite.T(), 58, len(summary.UpdatedResources)) + + //test + options := models.ResourceGraphOptions{ + ResourcesIds: []models.OriginBase{ + { + SourceID: testSourceCredential.ID, + SourceResourceType: "Encounter", + SourceResourceID: "eGmO0h.1.UQQrExl4bfM7OQ3", + }, + }, + } + + flattenedGraph, err := dbRepo.GetFlattenedResourceGraph(authContext, pkg.ResourceGraphTypeMedicalHistory, options) + require.NoError(suite.T(), err) + require.NotNil(suite.T(), flattenedGraph) + //validated using http://clinfhir.com/bundleVisualizer.html + + require.Equal(suite.T(), 1, len(flattenedGraph["Encounter"])) + //REGRESSION: in some cases the flattened graph was not correctly returning 7 related, instead only retuning 1 or 4. Bug in Graph generation + + for ndx, found := range flattenedGraph["Encounter"][0].RelatedResource { + suite.T().Logf("ndx: %d, found: %s/%s", ndx, found.SourceResourceType, found.SourceResourceID) + } + require.Equal(suite.T(), 7, len(flattenedGraph["Encounter"][0].RelatedResource)) + +} diff --git a/backend/pkg/database/interface.go b/backend/pkg/database/interface.go index d42a4101..c061ff06 100644 --- a/backend/pkg/database/interface.go +++ b/backend/pkg/database/interface.go @@ -28,7 +28,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 FindResourceAssociationsByTypeAndId(ctx context.Context, source *models.SourceCredential, resourceType string, resourceId string) ([]models.RelatedResource, error) - GetFlattenedResourceGraph(ctx context.Context, graphType pkg.ResourceGraphType, options models.ResourceGraphOptions) (map[string][]*models.ResourceBase, *models.ResourceGraphMetadata, error) + GetFlattenedResourceGraph(ctx context.Context, graphType pkg.ResourceGraphType, options models.ResourceGraphOptions) (map[string][]*models.ResourceBase, error) AddResourceComposition(ctx context.Context, compositionTitle string, resources []*models.ResourceBase) error //UpsertProfile(context.Context, *models.Profile) error //UpsertOrganziation(context.Context, *models.Organization) error diff --git a/backend/pkg/database/testdata/epic_fhircamila.ndjson b/backend/pkg/database/testdata/epic_fhircamila.ndjson new file mode 100644 index 00000000..33e7169d --- /dev/null +++ b/backend/pkg/database/testdata/epic_fhircamila.ndjson @@ -0,0 +1,58 @@ +{"resourceType":"AllergyIntolerance","id":"eARZpey6BWRZxRZkRpc8OFJ46j3QOFrduk77hYQKWRQnp6GRlpYc2I3BfUN6pz4Anz77ow8.GJh54fUVfm3O8Vw3","clinicalStatus":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical","version":"4.0.0","code":"active","display":"Active"}]},"verificationStatus":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/allergyintolerance-verification","version":"4.0.0","code":"unconfirmed","display":"Unconfirmed"}]},"code":{"coding":[{"system":"http://snomed.info/sct","code":"1631000175102","display":"Patient not asked"}],"text":"Not on File"},"patient":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"}} +{"contentType":"text/html","data":"PGRpdiBjbGFzcz0iZm10Q29udiIgc3R5bGU9ImxpbmUtaGVpZ2h0OiBub3JtYWw7IGZvbnQtZmFtaWx5OiBBcmlhbDsiPg0KDQo8ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBsZWZ0OyBtYXgtd2lkdGg6IDEwMCU7IiBkYXRhLXBhcmFncmFwaD0iMSI+PHNwYW4gc3R5bGU9ImZvbnQtd2VpZ2h0OiBib2xkOyBmb250LXNpemU6IDE0cHQ7IGZvbnQtZmFtaWx5OiAnU0VHT0UgVUknOyBjb2xvcjogIzAwMDAwMDsiPlN1YmplY3RpdmU8L3NwYW4+PC9kaXY+DQo8ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBsZWZ0OyBtYXgtd2lkdGg6IDEwMCU7IiBkYXRhLXBhcmFncmFwaD0iMSI+PHNwYW4gc3R5bGU9ImZvbnQtd2VpZ2h0OiBib2xkOyBmb250LXNpemU6IDExcHQ7IGZvbnQtZmFtaWx5OiAnU0VHT0UgVUknOyBjb2xvcjogIzAwMDAwMDsiPlBhdGllbnQgSUQ6PC9zcGFuPjxzcGFuIHN0eWxlPSJmb250LXdlaWdodDogYm9sZDsgZm9udC1zaXplOiAxMXB0OyBmb250LWZhbWlseTogJ1NFR09FIFVJJzsgY29sb3I6ICMwMDAwQTA7IHdoaXRlLXNwYWNlOiBwcmUtd3JhcDsiPiA8L3NwYW4+PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTogMTFwdDsgZm9udC1mYW1pbHk6ICdTRUdPRSBVSSc7IGNvbG9yOiAjMDAwMDAwOyI+Q2FtaWxhIE1hcmlhIExvcGV6PC9zcGFuPjxzcGFuIHN0eWxlPSJmb250LXNpemU6IDExcHQ7IGZvbnQtZmFtaWx5OiAnU0VHT0UgVUknOyBjb2xvcjogIzAwMDAwMDsgd2hpdGUtc3BhY2U6IHByZS13cmFwOyI+IGlzIGEgPC9zcGFuPjxzcGFuIHN0eWxlPSJmb250LXNpemU6IDExcHQ7IGZvbnQtZmFtaWx5OiAnU0VHT0UgVUknOyBjb2xvcjogIzAwMDAwMDsiPjM1IHkuby48L3NwYW4+PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTogMTFwdDsgZm9udC1mYW1pbHk6ICdTRUdPRSBVSSc7IGNvbG9yOiAjMDAwMDAwOyB3aGl0ZS1zcGFjZTogcHJlLXdyYXA7Ij4gPC9zcGFuPjxzcGFuIHN0eWxlPSJmb250LXNpemU6IDExcHQ7IGZvbnQtZmFtaWx5OiAnU0VHT0UgVUknOyBjb2xvcjogIzAwMDAwMDsiPmZlbWFsZS48L3NwYW4+PC9kaXY+DQo8ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBsZWZ0OyBtYXgtd2lkdGg6IDEwMCU7IHdpZG93czogMTsgb3JwaGFuczogMTsiIGRhdGEtcGFyYWdyYXBoPSIxIj48c3BhbiBzdHlsZT0iZm9udC1zaXplOiAxMXB0OyBmb250LWZhbWlseTogJ1NFR09FIFVJJzsgY29sb3I6ICMwMDAwMDA7Ij4mbmJzcDs8L3NwYW4+PC9kaXY+DQo8ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBsZWZ0OyBtYXgtd2lkdGg6IDEwMCU7IHdpZG93czogMTsgb3JwaGFuczogMTsiIGRhdGEtcGFyYWdyYXBoPSIxIj48c3BhbiBzdHlsZT0iZm9udC13ZWlnaHQ6IGJvbGQ7IGZvbnQtc2l6ZTogMTFwdDsgZm9udC1mYW1pbHk6ICdTRUdPRSBVSSc7IGNvbG9yOiAjMDAwMDAwOyI+Q2hpZWYgQ29tcGxhaW50Ojwvc3Bhbj48L2Rpdj4NCjxkaXYgc3R5bGU9InRleHQtYWxpZ246IGxlZnQ7IG1heC13aWR0aDogMTAwJTsgd2lkb3dzOiAxOyBvcnBoYW5zOiAxOyIgZGF0YS1wYXJhZ3JhcGg9IjEiPjxzcGFuIHN0eWxlPSJmb250LXNpemU6IDExcHQ7IGZvbnQtZmFtaWx5OiAnU0VHT0UgVUknOyBjb2xvcjogIzAwMDAwMDsiPk5vIGNoaWVmIGNvbXBsYWludCBvbiBmaWxlLjwvc3Bhbj48L2Rpdj4NCjxkaXYgc3R5bGU9InRleHQtYWxpZ246IGxlZnQ7IG1heC13aWR0aDogMTAwJTsgd2lkb3dzOiAxOyBvcnBoYW5zOiAxOyIgZGF0YS1wYXJhZ3JhcGg9IjEiPjxzcGFuIHN0eWxlPSJmb250LXNpemU6IDExcHQ7IGZvbnQtZmFtaWx5OiAnU0VHT0UgVUknOyBjb2xvcjogIzAwMDAwMDsiPiZuYnNwOzwvc3Bhbj48L2Rpdj4NCjxkaXYgc3R5bGU9InRleHQtYWxpZ246IGxlZnQ7IG1heC13aWR0aDogMTAwJTsiIGRhdGEtcGFyYWdyYXBoPSIxIj48c3BhbiBzdHlsZT0iZm9udC1zaXplOiAxMnB0OyBmb250LWZhbWlseTogQXJpYWw7IGNvbG9yOiAjMDAwMDAwOyI+SFBJPC9zcGFuPjwvZGl2Pg0KPGRpdiBzdHlsZT0idGV4dC1hbGlnbjogbGVmdDsgbWF4LXdpZHRoOiAxMDAlOyIgZGF0YS1wYXJhZ3JhcGg9IjEiPjxzcGFuIHN0eWxlPSJmb250LXNpemU6IDExcHQ7IGZvbnQtZmFtaWx5OiAnU0VHT0UgVUknOyBjb2xvcjogIzAwMDAwMDsiPiZuYnNwOzwvc3Bhbj48L2Rpdj4NCjxkaXYgc3R5bGU9InRleHQtYWxpZ246IGxlZnQ7IG1heC13aWR0aDogMTAwJTsiIGRhdGEtcGFyYWdyYXBoPSIxIj48c3BhbiBzdHlsZT0iZm9udC1zaXplOiAxMXB0OyBmb250LWZhbWlseTogJ1NFR09FIFVJJzsgY29sb3I6ICMwMDAwMDA7Ij5UaGUgZm9sbG93aW5nIHBvcnRpb25zIG9mIHRoZSBwYXRpZW50JiMzOTtzIGhpc3Rvcnkgd2VyZSByZXZpZXdlZCBpbiB0aGlzIGVuY291bnRlciBhbmQgdXBkYXRlZCBhcyBhcHByb3ByaWF0ZTo8L3NwYW4+PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTogMTFwdDsgZm9udC1mYW1pbHk6ICdTRUdPRSBVSSc7IGNvbG9yOiAjMDAwMDAwOyB3aGl0ZS1zcGFjZTogcHJlLXdyYXA7Ij4gPC9zcGFuPjxzcGFuIHN0eWxlPSJmb250LXNpemU6IDExcHQ7IGZvbnQtZmFtaWx5OiAnU0VHT0UgVUknOyBjb2xvcjogIzAwMDAwMDsiPlZhY2NpbmUgYWRtaW5pc3RyYXRpb248L3NwYW4+PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTogMTFwdDsgZm9udC1mYW1pbHk6ICdTRUdPRSBVSSc7IGNvbG9yOiAjMDAwMDAwOyB3aGl0ZS1zcGFjZTogcHJlLXdyYXA7Ij4gPC9zcGFuPjwvZGl2Pg0KPGRpdiBzdHlsZT0idGV4dC1hbGlnbjogbGVmdDsgbWF4LXdpZHRoOiAxMDAlOyIgZGF0YS1wYXJhZ3JhcGg9IjEiPjxzcGFuIHN0eWxlPSJmb250LXNpemU6IDExcHQ7IGZvbnQtZmFtaWx5OiAnU0VHT0UgVUknOyBjb2xvcjogIzAwMDAwMDsgd2hpdGUtc3BhY2U6IHByZS13cmFwOyI+IDwvc3Bhbj48L2Rpdj4NCjxkaXYgc3R5bGU9InRleHQtYWxpZ246IGxlZnQ7IG1heC13aWR0aDogMTAwJTsiIGRhdGEtcGFyYWdyYXBoPSIxIj48c3BhbiBzdHlsZT0iZm9udC1zaXplOiAxMXB0OyBmb250LWZhbWlseTogJ1NFR09FIFVJJzsgY29sb3I6ICMwMDAwMDA7Ij4mbmJzcDs8L3NwYW4+PC9kaXY+DQo8ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBsZWZ0OyBtYXgtd2lkdGg6IDEwMCU7IiBkYXRhLXBhcmFncmFwaD0iMSI+PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTogMTJwdDsgZm9udC1mYW1pbHk6IEFyaWFsOyBjb2xvcjogIzAwMDAwMDsiPlJldmlldyBvZiBTeXN0ZW1zPC9zcGFuPjwvZGl2Pg0KPGRpdiBzdHlsZT0idGV4dC1hbGlnbjogbGVmdDsgbWF4LXdpZHRoOiAxMDAlOyIgZGF0YS1wYXJhZ3JhcGg9IjEiPjxzcGFuIHN0eWxlPSJmb250LXNpemU6IDExcHQ7IGZvbnQtZmFtaWx5OiAnU0VHT0UgVUknOyBjb2xvcjogIzAwMDAwMDsiPiZuYnNwOzwvc3Bhbj48L2Rpdj4NCjxkaXYgc3R5bGU9InRleHQtYWxpZ246IGxlZnQ7IG1heC13aWR0aDogMTAwJTsiIGRhdGEtcGFyYWdyYXBoPSIxIj48c3BhbiBzdHlsZT0iZm9udC13ZWlnaHQ6IGJvbGQ7IGZvbnQtc2l6ZTogMTRwdDsgZm9udC1mYW1pbHk6ICdTRUdPRSBVSSc7IGNvbG9yOiAjMDAwMDAwOyI+T2JqZWN0aXZlPC9zcGFuPjwvZGl2Pg0KPGRpdiBzdHlsZT0idGV4dC1hbGlnbjogbGVmdDsgbWF4LXdpZHRoOiAxMDAlOyIgZGF0YS1wYXJhZ3JhcGg9IjEiPjxzcGFuIHN0eWxlPSJmb250LXNpemU6IDEycHQ7IGZvbnQtZmFtaWx5OiBBcmlhbDsgY29sb3I6ICMwMDAwMDA7Ij5QaHlzaWNhbCBFeGFtPC9zcGFuPjwvZGl2Pg0KPGRpdiBzdHlsZT0idGV4dC1hbGlnbjogbGVmdDsgbWF4LXdpZHRoOiAxMDAlOyIgZGF0YS1wYXJhZ3JhcGg9IjEiPjxzcGFuIHN0eWxlPSJmb250LXNpemU6IDExcHQ7IGZvbnQtZmFtaWx5OiAnU0VHT0UgVUknOyBjb2xvcjogIzAwMDAwMDsiPiZuYnNwOzwvc3Bhbj48L2Rpdj4NCjxkaXYgc3R5bGU9InRleHQtYWxpZ246IGxlZnQ7IG1heC13aWR0aDogMTAwJTsiIGRhdGEtcGFyYWdyYXBoPSIxIj48c3BhbiBzdHlsZT0iZm9udC13ZWlnaHQ6IGJvbGQ7IGZvbnQtc2l6ZTogMTRwdDsgZm9udC1mYW1pbHk6ICdTRUdPRSBVSSc7IGNvbG9yOiAjMDAwMDAwOyI+QXNzZXNzbWVudC9QbGFuPC9zcGFuPjwvZGl2Pg0KPGRpdiBzdHlsZT0idGV4dC1hbGlnbjogbGVmdDsgbWF4LXdpZHRoOiAxMDAlOyB3aWRvd3M6IDE7IG9ycGhhbnM6IDE7IiBkYXRhLXBhcmFncmFwaD0iMSI+PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTogMTFwdDsgZm9udC1mYW1pbHk6ICdTRUdPRSBVSSc7IGNvbG9yOiAjMDAwMDAwOyI+VGhlcmUgYXJlIG5vIGRpYWdub3NlcyBsaW5rZWQgdG8gdGhpcyBlbmNvdW50ZXIuPC9zcGFuPjwvZGl2PjwvZGl2Pg0K","id":"eXGCC0U1rMIkqT43GlAEfsA3","resourceType":"Binary"} +{"contentType":"text/html","data":"IDxicj5ObyBmaW5kaW5ncy4gUmVmZXIgdG8gY2FyZGlvbG9neS4=","id":"eatrFYJ-c5OxFaXGGKhRmtxnlc-Z0vWhRnWj621bo99g3","resourceType":"Binary"} +{"contentType":"text/html","data":"JiMxODM7IExlZnQgdmVudHJpY2xlIGVqZWN0aW9uIGZyYWN0aW9uIGlzIG5vcm1hbC48YnI+JiMxODM7IFNlcHRhbCB3YWxsIGhhcyBhYm5vcm1hbCBtb3Rpb24gY29uc2lzdGVudCB3aXRoIHBvc3Qtb3BlcmF0aXZlIHN0YXR1cy48YnI+Jm5ic3A7PGJyPkhlcmUgaXMgdGhlIGludGVycHJldGF0aW9uIHN1bW1hcnkgdGV4dC48YnI+PGJyPkxlZnQgVmVudHJpY2xlPGJyPkVqZWN0aW9uIGZyYWN0aW9uIGlzIG5vcm1hbC4gQ2F2aXR5IGlzIG5vcm1hbC4gRW5kLWRpYXN0b2xpYyB2b2x1bWUgYXBwZWFycyBtaWxkbHkgaW5jcmVhc2VkLiBOb3JtYWwgd2FsbCB0aGlja25lc3Mgb2JzZXJ2ZWQuIE1pZC1jYXZpdHkgb2JzdHJ1Y3Rpb24gaXMgcHJlc2VudC4gU2hhcGUgaXMgbm9ybWFsLiBBYm5vcm1hbCBzZXB0YWwgbW90aW9uIGNvbnNpc3RlbnQgd2l0aCBwb3N0LW9wZXJhdGl2ZSBzdGF0ZS4gTm9ybWFsIGZyYWN0aW9uYWwgc2hvcnRlbmluZy4gTm9ybWFsIGxlZnQgdmVudHJpY2xlIGRpYXN0b2xpYyBmdW5jdGlvbi4gVGhlIGZpbmRpbmdzIGFyZSBjb25zaXN0ZW50IHdpdGggZGlsYXRlZCBjYXJkaW9teW9wYXRoeS4=","id":"ey9R3HQvzT5D6lNvbJeLdyTF10pM-Yzpp1nsL-VLVkJc3","resourceType":"Binary"} +{"contentType":"text/html","data":"UGF0aWVudCBwcmVzZW50cyB3aXRoIGhpc3Rvcnkgb2YgY2hlc3QgcGFpbi4gTm8gcmliIGZyYWN0dXJlcyBvciBsZXNpb25zIDxicj5mb3VuZDxicj4=","id":"eatrFYJ-c5OxFaXGGKhRmt-kbn-1xHl8SNrcCB4fHVps3","resourceType":"Binary"} +{"contentType":"text/rtf","data":"e1xydGYxXGVwaWNWMTA3MDBcYW5zaVxzcGx0cGdwYXJcamV4cGFuZFxub3hsYXR0b3llbiANClxkZWZsYW5nMFxkZWZsYW5nZmUwXGRlZmYxXHBhcGVydzEyMjQwXHBhcGVyaDE1ODQwXG1hcmdsMTgwMFxtYXJncjE4MDBcbWFyZ3QxNDQwXG1hcmdiMTQ0MFx3aWRvd2N0cmwgDQp7XGZvbnR0YmwgDQp7XGYxXGZjaGFyc2V0MFxmbmlsIFNFR09FIFVJO30NCntcZjJcZmNoYXJzZXQwXGZuaWwgQXJpYWw7fX0NCntcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUxNjA7XHJlZDBcZ3JlZW4wXGJsdWUwO30NClxzZWN0ZCANClxwYXJkXGx0cnBhciANClxwbGFpblxsdHJjaFxiXGZzMjggU3ViamVjdGl2ZVxiMFxmczIyXHBhciANClxiIFBhdGllbnQgSUQ6XGNmMSAgXGIwXGNmMCBDYW1pbGEgTWFyaWEgTG9wZXogaXMgYSAzNSB5Lm8uIGZlbWFsZS5ccGFyIA0KXG5vd2lkY3RscGFyIA0KXHBhciANClxiXGNmMiBDaGllZiBDb21wbGFpbnQ6XHBhciANClxiMFxjZjAgTm8gY2hpZWYgY29tcGxhaW50IG9uIGZpbGUuXHBhciANClxwYXIgDQpcd2lkY3RscGFyIA0KXGYyXGZzMjQgSFBJXGYxXGZzMjJccGFyIA0KXHBhciANClRoZSBmb2xsb3dpbmcgcG9ydGlvbnMgb2YgdGhlIHBhdGllbnQncyBoaXN0b3J5IHdlcmUgcmV2aWV3ZWQgaW4gdGhpcyBlbmNvdW50ZXIgYW5kIHVwZGF0ZWQgYXMgYXBwcm9wcmlhdGU6IFZhY2NpbmUgYWRtaW5pc3RyYXRpb24gXHBhciANCiBccGFyIA0KXHBhciANClxmMlxmczI0IFJldmlldyBvZiBTeXN0ZW1zXGYxXGZzMjJccGFyIA0KXHBhciANClxiXGZzMjggT2JqZWN0aXZlXGIwXGZzMjJccGFyIA0KXGYyXGZzMjQgUGh5c2ljYWwgRXhhbVxmMVxmczIyXHBhciANClxwYXIgDQpcYlxmczI4IEFzc2Vzc21lbnQvUGxhblx1bFxmczIyXHBhciANClxub3dpZGN0bHBhciANClxiMFx1bDAgVGhlcmUgYXJlIG5vIGRpYWdub3NlcyBsaW5rZWQgdG8gdGhpcyBlbmNvdW50ZXIuXHBhcn0=","id":"ftlnC8mfyjUVrXEpeEsj12f1.H.Tu5gtgXv1gF4e3F4Y4","resourceType":"Binary"} +{"resourceType":"CareTeam","id":"elXzftb61zxJqvM4xV3jTzA3","status":"active","category":[{"coding":[{"system":"http://loinc.org","code":"LA28865-6","display":"Longitudinal care-coordination focused care team"}],"text":"Longitudinal care-coordination focused care team"}],"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"participant":[{"role":[{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.4.836982.1050","code":"9","display":"Family Medicine"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.4.698084.5655","code":"1","display":"General"}],"text":"Family Medicine"}],"member":{"reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3","type":"Practitioner","display":"Physician"}}]} +{"resourceType":"Condition","id":"euDDjG6i1-OkhZii5APxOiw3","clinicalStatus":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/condition-clinical","version":"4.0.0","code":"active","display":"Active"}],"text":"Active"},"verificationStatus":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/condition-ver-status","version":"4.0.0","code":"confirmed","display":"Confirmed"}],"text":"Confirmed"},"category":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/condition-category","code":"problem-list-item","display":"Problem List Item"}],"text":"Problem List Item"}],"severity":{"text":"Medium"},"code":{"coding":[{"system":"http://hl7.org/fhir/sid/icd-10-cm","code":"E28.2"},{"system":"http://snomed.info/sct","code":"69878008"}],"text":"Polycystic ovaries"},"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"onsetPeriod":{"start":"2005-09-20","end":"2005-09-20"},"recordedDate":"2019-05-28"} +{"category":[{"coding":[{"code":"care-plan-problem","display":"Care Plan Problem","system":"http://open.epic.com/FHIR/StructureDefinition/condition-category"}],"text":"Care Plan Problem"}],"clinicalStatus":{"coding":[{"code":"active","display":"Active","system":"http://terminology.hl7.org/CodeSystem/condition-clinical","version":"4.0.0"}],"text":"Active"},"code":{"text":"Pain - Adult"},"extension":[{"url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-asserter","valueReference":{"display":"NURSING","type":"PractitionerRole"}},{"url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-asserter","valueReference":{"display":"INTERDISCIPLINARY","type":"PractitionerRole"}},{"url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-asserter","valueReference":{"display":"PHYSICAL THERAPY","type":"PractitionerRole"}},{"url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-asserter","valueReference":{"display":"RESPIRATORY THERAPY","type":"PractitionerRole"}},{"url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-asserter","valueReference":{"display":"SOCIAL WORK","type":"PractitionerRole"}}],"id":"e-oL436hofxVBDhMRXD9mrg3","onsetDateTime":"2023-06-02","recordedDate":"2023-06-02","recorder":{"display":"Robert Z","reference":"Practitioner/eHP1iZcoohQAKIcKyP6CDvA3"},"resourceType":"Condition","subject":{"display":"Lopez, Camila Maria","reference":"Patient/erXuFYUfucBZaryVksYEcMg3"}} +{"category":[{"coding":[{"code":"care-plan-problem","display":"Care Plan Problem","system":"http://open.epic.com/FHIR/StructureDefinition/condition-category"}],"text":"Care Plan Problem"}],"clinicalStatus":{"coding":[{"code":"active","display":"Active","system":"http://terminology.hl7.org/CodeSystem/condition-clinical","version":"4.0.0"}],"text":"Active"},"code":{"text":"Discharge Planning"},"extension":[{"url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-asserter","valueReference":{"display":"NURSING","type":"PractitionerRole"}},{"url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-asserter","valueReference":{"display":"INTERDISCIPLINARY","type":"PractitionerRole"}},{"url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-asserter","valueReference":{"display":"RESPIRATORY THERAPY","type":"PractitionerRole"}},{"url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-asserter","valueReference":{"display":"SOCIAL WORK","type":"PractitionerRole"}}],"id":"e7.CXqjHOcMA8Gxl2sOOs3w3","onsetDateTime":"2023-06-02","recordedDate":"2023-06-02","recorder":{"display":"Robert Z","reference":"Practitioner/eHP1iZcoohQAKIcKyP6CDvA3"},"resourceType":"Condition","subject":{"display":"Lopez, Camila Maria","reference":"Patient/erXuFYUfucBZaryVksYEcMg3"}} +{"category":[{"coding":[{"code":"care-plan-problem","display":"Care Plan Problem","system":"http://open.epic.com/FHIR/StructureDefinition/condition-category"}],"text":"Care Plan Problem"}],"clinicalStatus":{"coding":[{"code":"active","display":"Active","system":"http://terminology.hl7.org/CodeSystem/condition-clinical","version":"4.0.0"}],"text":"Active"},"code":{"text":"Safety Adult - Fall"},"extension":[{"url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-asserter","valueReference":{"display":"NURSING","type":"PractitionerRole"}},{"url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-asserter","valueReference":{"display":"INTERDISCIPLINARY","type":"PractitionerRole"}},{"url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-asserter","valueReference":{"display":"RESPIRATORY THERAPY","type":"PractitionerRole"}},{"url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-asserter","valueReference":{"display":"SOCIAL WORK","type":"PractitionerRole"}}],"id":"e1tKlHlRqm9EXhpS9iLx4xA3","onsetDateTime":"2023-06-02","recordedDate":"2023-06-02","recorder":{"display":"Robert Z","reference":"Practitioner/eHP1iZcoohQAKIcKyP6CDvA3"},"resourceType":"Condition","subject":{"display":"Lopez, Camila Maria","reference":"Patient/erXuFYUfucBZaryVksYEcMg3"}} +{"category":[{"coding":[{"code":"care-plan-problem","display":"Care Plan Problem","system":"http://open.epic.com/FHIR/StructureDefinition/condition-category"}],"text":"Care Plan Problem"}],"clinicalStatus":{"coding":[{"code":"active","display":"Active","system":"http://terminology.hl7.org/CodeSystem/condition-clinical","version":"4.0.0"}],"text":"Active"},"code":{"text":"Chronic Conditions and Co-morbidities"},"id":"eshaiIZ7f0Z.ptm.6wNwjrA3","onsetDateTime":"2023-06-02","recordedDate":"2023-06-02","recorder":{"display":"Robert Z","reference":"Practitioner/eHP1iZcoohQAKIcKyP6CDvA3"},"resourceType":"Condition","subject":{"display":"Lopez, Camila Maria","reference":"Patient/erXuFYUfucBZaryVksYEcMg3"}} +{"category":[{"coding":[{"code":"care-plan-problem","display":"Care Plan Problem","system":"http://open.epic.com/FHIR/StructureDefinition/condition-category"}],"text":"Care Plan Problem"}],"clinicalStatus":{"coding":[{"code":"active","display":"Active","system":"http://terminology.hl7.org/CodeSystem/condition-clinical","version":"4.0.0"}],"text":"Active"},"code":{"text":"Infection - Adult"},"extension":[{"url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-asserter","valueReference":{"display":"NURSING","type":"PractitionerRole"}},{"url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-asserter","valueReference":{"display":"INTERDISCIPLINARY","type":"PractitionerRole"}},{"url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-asserter","valueReference":{"display":"RESPIRATORY THERAPY","type":"PractitionerRole"}},{"url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-asserter","valueReference":{"display":"SOCIAL WORK","type":"PractitionerRole"}}],"id":"enxs1cxkwzPxvOqZcUj4IVQ3","onsetDateTime":"2023-06-02","recordedDate":"2023-06-02","recorder":{"display":"Robert Z","reference":"Practitioner/eHP1iZcoohQAKIcKyP6CDvA3"},"resourceType":"Condition","subject":{"display":"Lopez, Camila Maria","reference":"Patient/erXuFYUfucBZaryVksYEcMg3"}} +{"resourceType":"DiagnosticReport","id":"ebLG6RMUaZyb2xk2DuX.kd4g8mNRffn9ftcwqSmnhrwM3","identifier":[{"use":"official","type":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0203","code":"PLAC","display":"Placer Identifier"}],"text":"Placer Identifier"},"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.798268","value":"1066905"}],"basedOn":[{"reference":"ServiceRequest/egf4KkhXmgeVWImXEEYH.CEE1ZgJG5SQy2vBX1HgvBCs3","type":"ServiceRequest"}],"status":"final","category":[{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.798268.30","code":"Lab"}],"text":"Lab"},{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0074","code":"LAB","display":"Laboratory"}],"text":"Laboratory"}],"code":{"coding":[{"system":"http://loinc.org","code":"4548-4"},{"system":"urn:oid:2.16.840.1.113883.6.12","code":"83036","display":"PR GLYCOSYLATED HEMOGLOBIN TEST"},{"system":"urn:oid:2.16.840.1.113883.6.96","code":"43396009","display":"HEMOGLOBIN A1C"}],"text":"Hemoglobin A1c"},"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"encounter":{"reference":"Encounter/elMz2mwjsRvKnZiR.0ceTUg3","identifier":{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"27558"},"display":"Office Visit"},"effectiveDateTime":"2019-05-28T14:22:00Z","issued":"2019-05-28T14:22:00Z","performer":[{"reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3","type":"Practitioner","display":"Physician"}],"result":[{"reference":"Observation/eyPMWgv2u2RUfsV4p1lLKuUtqyPs2-QNi2zKvbTsFYtRByc6B.cSi1iVU5V2HOpX23","display":"Component (1): HEMOGLOBIN A1C, POC"}]} +{"resourceType":"DiagnosticReport","id":"eeIUePPFhkOlGgtsbPko4a4tPlKY9045CYysh7Ryulnc3","identifier":[{"use":"official","type":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0203","code":"PLAC","display":"Placer Identifier"}],"text":"Placer Identifier"},"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.798268","value":"1066912"},{"use":"official","type":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0203","code":"FILL","display":"Filler Identifier"}],"text":"Filler Identifier"},"value":"161"}],"basedOn":[{"reference":"ServiceRequest/ePsXywWuaF47D-FPsxcnz3BOk1-JNF41RJJEMQu.2TCA3","type":"ServiceRequest"}],"status":"final","category":[{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.798268.30","code":"Imaging"}],"text":"Imaging"},{"coding":[{"system":"http://loinc.org","code":"LP29684-5","display":"Radiology"}],"text":"Radiology"}],"code":{"coding":[{"system":"http://loinc.org","code":"36643-5"},{"system":"urn:oid:2.16.840.1.113883.6.12","code":"71020","display":"HC XR CHEST 2V"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.52000","code":"7000029"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.72","code":"IMG36"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.155","code":"RPID2503"}],"text":"X-ray Chest 2 Views"},"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"encounter":{"reference":"Encounter/eoIRZgvu9RhrwOzDlZkRbSg3","identifier":{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"29541"},"display":"Hospital Outpatient Visit"},"effectiveDateTime":"2023-06-02T20:43:46Z","issued":"2023-07-25T13:27:19Z","performer":[{"reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3","type":"Practitioner","display":"Physician"},{"reference":"Practitioner/elpRiy0AYgjhdjAhTBJ3Aiw3","type":"Practitioner","display":"Np"},{"reference":"Organization/etOhDRFmHqAJ3CBmvHCeF9VYgSiH5bYCNkDSOho2TXb43","type":"Organization","display":"IMAGING"}],"resultsInterpreter":[{"reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3","type":"Practitioner","display":"Physician"}],"result":[{"reference":"Observation/eWQ91GjryHtrlYhSNxK1L7mI-fgFwPAedom.YQ4JDeQk3","display":"Narrative"},{"reference":"Observation/eWQ91GjryHtrlYhSNxK1L7hOQbiABlro0a6oo2bIh68I3","display":"Impression"}],"conclusionCode":[{"coding":[{"system":"urn:oid:2.16.840.1.113883.6.103","code":"786.50","display":"Ischemic chest pain (CMS/HCC)"}],"text":"Ischemic chest pain"}],"presentedForm":[{"contentType":"text/html","url":"Binary/eatrFYJ-c5OxFaXGGKhRmt-kbn-1xHl8SNrcCB4fHVps3","title":"Narrative"},{"contentType":"text/html","url":"Binary/eatrFYJ-c5OxFaXGGKhRmtxnlc-Z0vWhRnWj621bo99g3","title":"Impression"}]} +{"resourceType":"DiagnosticReport","id":"ejRFFAyoWd-DvE0lHi4UEUVA9qTH9PxfCLhEetlq04Ck3","identifier":[{"use":"official","type":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0203","code":"PLAC","display":"Placer Identifier"}],"text":"Placer Identifier"},"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.798268","value":"1066915"},{"use":"official","type":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0203","code":"FILL","display":"Filler Identifier"}],"text":"Filler Identifier"},"value":"162"}],"basedOn":[{"reference":"ServiceRequest/e6t1HtRj0Za8M9Y0u5nU2tu3idJ7P66B2HzlMtddZ6Wc3","type":"ServiceRequest"}],"status":"final","category":[{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.798268.30","code":"Echo"}],"text":"Echocardiography"},{"coding":[{"system":"http://loinc.org","code":"LP29708-2","display":"Cardiology"}],"text":"Cardiology"}],"code":{"coding":[{"system":"urn:oid:2.16.840.1.113883.6.12","code":"93306","display":"HC TRANSTHORACIC ECHO"}],"text":"Transthoracic Echo (TTE) Complete"},"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"encounter":{"reference":"Encounter/ej567ARSFdaF4ooZVunIlIg3","identifier":{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"29542"},"display":"Hospital Outpatient Visit"},"effectiveDateTime":"2023-06-02T20:50:25Z","issued":"2023-07-25T13:26:44Z","performer":[{"reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3","type":"Practitioner","display":"Physician"},{"reference":"Practitioner/elpRiy0AYgjhdjAhTBJ3Aiw3","type":"Practitioner","display":"Np"},{"reference":"Organization/eVUDU8oWh9.KM7oI8Mu9jlqn1C3d5ozeHAXDHuaPYsvU3","type":"Organization","display":"CPACS"}],"resultsInterpreter":[{"reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3","type":"Practitioner","display":"Physician"}],"result":[{"reference":"Observation/epvZIWrCStn-QxWdd6yDL2rsODx.M3VAMdHGGYcFbGkQ3","display":"Narrative"},{"reference":"Observation/eUnZeZkXn.qk2e15Fa5GSJrQuuYKhh55JdwnpB0Cppeo3","display":"Myocardial Finding: Left Ventricle"}],"conclusionCode":[{"coding":[{"system":"urn:oid:2.16.840.1.113883.6.103","code":"786.50","display":"Ischemic chest pain (CMS/HCC)"}],"text":"Ischemic chest pain"}],"presentedForm":[{"contentType":"text/html","url":"Binary/ey9R3HQvzT5D6lNvbJeLdyTF10pM-Yzpp1nsL-VLVkJc3","title":"Narrative"}]} +{"resourceType":"DiagnosticReport","id":"e1xW6IyYA20h9CHX4rXDK8wRZibvNskG.0jb.xlYR.OI3","identifier":[{"use":"official","type":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0203","code":"PLAC","display":"Placer Identifier"}],"text":"Placer Identifier"},"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.798268","value":"1159966"}],"basedOn":[{"reference":"ServiceRequest/enf3AGjDevbeKPagusE3V1s5OQotZY-b9J5pspEt6PfU3","type":"ServiceRequest"}],"status":"final","category":[{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.798268.30","code":"Path,Cyt"}],"text":"Pathology and Cytology"},{"coding":[{"system":"http://loinc.org","code":"LP7839-6","display":"Pathology"}],"text":"Pathology"}],"code":{"coding":[{"system":"urn:oid:2.16.840.1.113883.6.12","code":"88305","display":"PR SURG PATH,LEVEL IV"}],"text":"Specimen to pathology"},"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"encounter":{"reference":"Encounter/eMT95RPaM-HxLc3NeIA5qlQ3","identifier":{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"29544"},"display":"Office Visit"},"effectiveDateTime":"2023-06-02T21:02:00Z","issued":"2023-06-02T21:02:00Z","performer":[{"reference":"Practitioner/e2qocqJm-DdjHLS0Not4qjA3","type":"Practitioner","display":"Historical"}]} +{"resourceType":"DocumentReference","id":"eNreR3hAkvySSHIcq2Lrxlw3","identifier":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.727879","value":"196763"},{"system":"urn:oid:1.2.840.114350.1.72.3.15","value":"1.2.840.114350.1.13.0.1.7.2.727879_196763"}],"status":"current","docStatus":"final","type":{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.4.737880.5010","code":"1","display":"Progress Notes"},{"system":"urn:oid:1.2.840.114350.1.72.727879.69848980","code":"1","display":"Progress Notes"},{"system":"http://loinc.org","code":"11506-3","display":"Provider-unspecified Progress note","userSelected":true}],"text":"Progress Notes"},"category":[{"coding":[{"system":"http://hl7.org/fhir/us/core/CodeSystem/us-core-documentreference-category","code":"clinical-note","display":"Clinical Note"}],"text":"Clinical Note"}],"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"date":"2023-05-21T16:55:00Z","author":[{"reference":"Practitioner/eUXiD4IyBLJFoR2VHOjfTdg3","type":"Practitioner","display":"Physician"}],"authenticator":{"extension":[{"valueDateTime":"2023-05-21T16:57:01Z","url":"http://open.epic.com/FHIR/StructureDefinition/extension/clinical-note-authentication-instant"}],"reference":"Practitioner/eUXiD4IyBLJFoR2VHOjfTdg3","type":"Practitioner","display":"Physician"},"custodian":{"identifier":{"system":"urn:ietf:rfc:3986","value":"urn:oid:fhir"},"display":"Epic USCDI on FHIR"},"content":[{"attachment":{"contentType":"text/html","url":"Binary/eXGCC0U1rMIkqT43GlAEfsA3"},"format":{"system":"http://ihe.net/fhir/ValueSet/IHE.FormatCode.codesystem","code":"urn:ihe:iti:xds:2017:mimeTypeSufficient","display":"mimeType Sufficient"}},{"attachment":{"contentType":"text/rtf","url":"Binary/ftlnC8mfyjUVrXEpeEsj12f1.H.Tu5gtgXv1gF4e3F4Y4"},"format":{"system":"http://ihe.net/fhir/ValueSet/IHE.FormatCode.codesystem","code":"urn:ihe:iti:xds:2017:mimeTypeSufficient","display":"mimeType Sufficient"}}],"context":{"extension":[{"valueCodeableConcept":{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.4.836982.1040","code":"1","display":"Physician"},{"system":"urn:oid:2.16.840.1.113883.11.19465","code":"207Q00000X","display":"Family Medicine Physician"}],"text":"Physician"},"url":"http://open.epic.com/FHIR/StructureDefinition/extension/clinical-note-author-provider-type"}],"encounter":[{"reference":"Encounter/exfbAtPCSLnf8s.193aMUnQ3","identifier":{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"29539"},"display":"Office Visit"}],"period":{"start":"2023-05-21T16:53:00Z"}}} +{"resourceType":"DocumentReference","id":"ewtIzA62-DkL21MnJY6OyRj8UF8vh7UuasqvGB1pc5CY3","identifier":[{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.798268","value":"1066912"}],"status":"current","type":{"coding":[{"system":"http://loinc.org","code":"18748-4","display":"Diagnostic imaging study"}],"text":"Diagnostic imaging study"},"category":[{"coding":[{"system":"http://hl7.org/fhir/us/core/CodeSystem/us-core-documentreference-category","code":"clinical-note","display":"Clinical Note"}],"text":"Clinical Note"}],"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"date":"2023-06-02T20:43:46Z","author":[{"reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3","type":"Practitioner","display":"Physician"}],"custodian":{"display":"Epic USCDI on FHIR"},"content":[{"attachment":{"contentType":"text/html","url":"Binary/eatrFYJ-c5OxFaXGGKhRmtxnlc-Z0vWhRnWj621bo99g3"},"format":{"system":"http://ihe.net/fhir/ValueSet/IHE.FormatCode.codesystem","code":"urn:ihe:iti:xds:2017:mimeTypeSufficient","display":"mimeType Sufficient"}}],"context":{"encounter":[{"reference":"Encounter/eoIRZgvu9RhrwOzDlZkRbSg3","identifier":{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"29541"},"display":"Hospital Outpatient Visit"}],"period":{"start":"2023-06-02T20:43:46Z"}}} +{"resourceType":"DocumentReference","id":"ewtIzA62-DkL21MnJY6OyRhIhI5asLLt7L5RQtNUW6qY3","identifier":[{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.798268","value":"1066912"}],"status":"current","type":{"coding":[{"system":"http://loinc.org","code":"18748-4","display":"Diagnostic imaging study"}],"text":"Diagnostic imaging study"},"category":[{"coding":[{"system":"http://hl7.org/fhir/us/core/CodeSystem/us-core-documentreference-category","code":"clinical-note","display":"Clinical Note"}],"text":"Clinical Note"}],"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"date":"2023-06-02T20:43:46Z","author":[{"reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3","type":"Practitioner","display":"Physician"}],"custodian":{"display":"Epic USCDI on FHIR"},"content":[{"attachment":{"contentType":"text/html","url":"Binary/eatrFYJ-c5OxFaXGGKhRmt-kbn-1xHl8SNrcCB4fHVps3"},"format":{"system":"http://ihe.net/fhir/ValueSet/IHE.FormatCode.codesystem","code":"urn:ihe:iti:xds:2017:mimeTypeSufficient","display":"mimeType Sufficient"}}],"context":{"encounter":[{"reference":"Encounter/eoIRZgvu9RhrwOzDlZkRbSg3","identifier":{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"29541"},"display":"Hospital Outpatient Visit"}],"period":{"start":"2023-06-02T20:43:46Z"}}} +{"resourceType":"DocumentReference","id":"euYCTLWovmnCqF5RhHJi3Z25sdrk5joaPj1R8zBg5PYM3","identifier":[{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.798268","value":"1066915"}],"status":"current","type":{"coding":[{"system":"http://loinc.org","code":"18748-4","display":"Diagnostic imaging study"}],"text":"Diagnostic imaging study"},"category":[{"coding":[{"system":"http://hl7.org/fhir/us/core/CodeSystem/us-core-documentreference-category","code":"clinical-note","display":"Clinical Note"}],"text":"Clinical Note"}],"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"date":"2023-06-02T20:50:25Z","author":[{"reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3","type":"Practitioner","display":"Physician"}],"custodian":{"display":"Epic USCDI on FHIR"},"content":[{"attachment":{"contentType":"text/html","url":"Binary/ey9R3HQvzT5D6lNvbJeLdyTF10pM-Yzpp1nsL-VLVkJc3"},"format":{"system":"http://ihe.net/fhir/ValueSet/IHE.FormatCode.codesystem","code":"urn:ihe:iti:xds:2017:mimeTypeSufficient","display":"mimeType Sufficient"}}],"context":{"encounter":[{"reference":"Encounter/ej567ARSFdaF4ooZVunIlIg3","identifier":{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"29542"},"display":"Hospital Outpatient Visit"}],"period":{"start":"2023-06-02T20:50:25Z"}}} +{"resourceType":"Encounter","id":"eGmO0h.1.UQQrExl4bfM7OQ3","identifier":[{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"29545"}],"status":"unknown","class":{"system":"urn:oid:1.2.840.114350.1.72.1.7.7.10.696784.13260","code":"10","display":"Surgery Log"},"type":[{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.698084.30","code":"51","display":"Surgery"}],"text":"Surgery"}],"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"participant":[{"individual":{"reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3","type":"Practitioner","display":"Physician"}}],"period":{"start":"2023-06-06T16:20:00Z","end":"2023-06-06T17:35:00Z"},"location":[{"location":{"reference":"Location/efR3SIdpRKF9BFBM5qakt9F5X1mWxjiAGu2hNTKbqOdI3","display":"EMH Operating Room"}}],"partOf":{"reference":"Encounter/eoK8nLRcEypNjtns4dgnF3Q3","identifier":{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"29547"},"display":"Emergency Department"}} +{"resourceType":"Encounter","id":"eMT95RPaM-HxLc3NeIA5qlQ3","identifier":[{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"29544"}],"status":"in-progress","class":{"system":"urn:oid:1.2.840.114350.1.72.1.7.7.10.696784.13260","code":"13","display":"Support OP Encounter"},"type":[{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.698084.30","code":"101","display":"Office Visit"}],"text":"Office Visit"}],"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"participant":[{"individual":{"reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3","type":"Practitioner","display":"Physician"}}],"period":{"start":"2023-06-02","end":"2023-06-02"},"location":[{"location":{"reference":"Location/e4W4rmGe9QzuGm2Dy4NBqVc0KDe6yGld6HW95UuN-Qd03","display":"EMC Family Medicine"}}]} +{"resourceType":"Encounter","id":"ej567ARSFdaF4ooZVunIlIg3","identifier":[{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"29542"}],"status":"in-progress","class":{"system":"urn:oid:1.2.840.114350.1.72.1.7.7.10.696784.13260","code":"4","display":"HOV"},"type":[{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.698084.10110","code":"102","display":"Outpatient"}],"text":"Outpatient"},{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.698084.30","code":"3","display":"Hospital Encounter"}],"text":"Hospital Encounter"},{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.808267","code":"10023","display":"Transthoracic Echocardiogram Complete"}],"text":"Transthoracic Echocardiogram Complete"}],"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"participant":[{"type":[{"coding":[{"system":"http://hl7.org/fhir/v3/ParticipationType","code":"REF","display":"referrer"}],"text":"referrer"}],"individual":{"reference":"Practitioner/elpRiy0AYgjhdjAhTBJ3Aiw3","type":"Practitioner","display":"Np"}}],"period":{"start":"2023-06-02T20:49:58Z"},"account":[{"identifier":{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.726582","value":"1000501"},"display":"LOPEZ,CAMILA MARIA"}],"hospitalization":{"extension":[{"valueDateTime":"2023-06-02T20:49:00Z","url":"http://open.epic.com/FHIR/StructureDefinition/extension/observation-datetime"}]},"location":[{"location":{"reference":"Location/exzJSfs-2YYqMPilpdzkcdMVzw6TB9WJfeqFRVe.cpSo3","display":"EMC Cardiology Procedures"}},{"location":{"reference":"Location/exzJSfs-2YYqMPilpdzkcdMVzw6TB9WJfeqFRVe.cpSo3","display":"EMC Cardiology Procedures"}},{"location":{"reference":"Location/eQSOrLdbVrMRc68KBcTBG4A3","display":"Epic Hospital System"}}]} +{"resourceType":"Encounter","id":"eoIRZgvu9RhrwOzDlZkRbSg3","identifier":[{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"29541"}],"status":"in-progress","class":{"system":"urn:oid:1.2.840.114350.1.72.1.7.7.10.696784.13260","code":"4","display":"HOV"},"type":[{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.698084.10110","code":"102","display":"Outpatient"}],"text":"Outpatient"},{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.698084.30","code":"3","display":"Hospital Encounter"}],"text":"Hospital Encounter"},{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.808267","code":"1053001","display":"X-Ray Exam"}],"text":"X-Ray Exam"}],"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"participant":[{"type":[{"coding":[{"system":"http://hl7.org/fhir/v3/ParticipationType","code":"REF","display":"referrer"}],"text":"referrer"}],"individual":{"reference":"Practitioner/elpRiy0AYgjhdjAhTBJ3Aiw3","type":"Practitioner","display":"Np"}}],"period":{"start":"2023-06-02T20:41:08Z"},"account":[{"identifier":{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.726582","value":"1000501"},"display":"LOPEZ,CAMILA MARIA"}],"hospitalization":{"extension":[{"valueDateTime":"2023-06-02T20:41:00Z","url":"http://open.epic.com/FHIR/StructureDefinition/extension/observation-datetime"}]},"location":[{"location":{"reference":"Location/e6.GHPEJ3Jrk18e14wi2AJ5vFRqvLa0ZB6qH40N4UgB03","display":"EMH X-Ray Imaging"}},{"location":{"reference":"Location/e6.GHPEJ3Jrk18e14wi2AJ5vFRqvLa0ZB6qH40N4UgB03","display":"EMH X-Ray Imaging"}},{"location":{"reference":"Location/eQSOrLdbVrMRc68KBcTBG4A3","display":"Epic Hospital System"}}]} +{"resourceType":"Encounter","id":"exfbAtPCSLnf8s.193aMUnQ3","identifier":[{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"29539"}],"status":"finished","class":{"system":"urn:oid:1.2.840.114350.1.72.1.7.7.10.696784.13260","code":"13","display":"Support OP Encounter"},"type":[{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.698084.30","code":"101","display":"Office Visit"}],"text":"Office Visit"}],"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"participant":[{"type":[{"text":"losAuthorizingPhysician"}],"individual":{"reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3","type":"Practitioner","display":"Physician"}}],"period":{"start":"2023-05-21","end":"2023-05-21"},"location":[{"location":{"reference":"Location/e4W4rmGe9QzuGm2Dy4NBqVc0KDe6yGld6HW95UuN-Qd03","display":"EMC Family Medicine"}}]} +{"resourceType":"Encounter","id":"elMz2mwjsRvKnZiR.0ceTUg3","identifier":[{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"27558"}],"status":"in-progress","class":{"system":"urn:oid:1.2.840.114350.1.72.1.7.7.10.696784.13260","code":"13","display":"Support OP Encounter"},"type":[{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.698084.30","code":"101","display":"Office Visit"}],"text":"Office Visit"}],"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"participant":[{"individual":{"reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3","type":"Practitioner","display":"Physician"}}],"period":{"start":"2019-05-28","end":"2019-05-28"},"location":[{"location":{"reference":"Location/e4W4rmGe9QzuGm2Dy4NBqVc0KDe6yGld6HW95UuN-Qd03","display":"EMC Family Medicine"}}]} +{"resourceType":"Encounter","id":"eEyT8ihlF3x9EsGhmSjNsaA3","identifier":[{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"27547"}],"status":"in-progress","class":{"system":"urn:oid:1.2.840.114350.1.72.1.7.7.10.696784.13260","code":"13","display":"Support OP Encounter"},"type":[{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.698084.30","code":"101","display":"Office Visit"}],"text":"Office Visit"}],"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"period":{"start":"2019-05-24","end":"2019-05-24"},"location":[{"location":{"reference":"Location/e4W4rmGe9QzuGm2Dy4NBqVc0KDe6yGld6HW95UuN-Qd03","display":"EMC Family Medicine"}}]} +{"resourceType":"Goal","id":"e3hwZL.Ra85rtejrMitqu8w3","extension":[{"valueString":"NURSING","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"},{"valueString":"INTERDISCIPLINARY","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"},{"valueString":"RESPIRATORY THERAPY","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"},{"valueString":"SOCIAL WORK","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"}],"lifecycleStatus":"active","category":[{"coding":[{"system":"http://open.epic.com/FHIR/StructureDefinition/goal-category","code":"inpatient-care-plan-goal","display":"Inpatient Care Plan Goal"}],"text":"Inpatient Care Plan Goal"}],"description":{"text":"Verbalizes/displays adequate comfort level or baseline comfort level"},"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"startDate":"2023-06-02","addresses":[{"reference":"Condition/e-oL436hofxVBDhMRXD9mrg3","display":"Pain - Adult"}]} +{"resourceType":"Goal","id":"eQkMeW6kwaLlhQWHCjq6qiQ3","extension":[{"valueString":"NURSING","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"},{"valueString":"INTERDISCIPLINARY","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"},{"valueString":"RESPIRATORY THERAPY","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"},{"valueString":"SOCIAL WORK","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"}],"lifecycleStatus":"active","category":[{"coding":[{"system":"http://open.epic.com/FHIR/StructureDefinition/goal-category","code":"inpatient-care-plan-goal","display":"Inpatient Care Plan Goal"}],"text":"Inpatient Care Plan Goal"}],"description":{"text":"Absence of infection during hospitalization"},"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"startDate":"2023-06-02","addresses":[{"reference":"Condition/enxs1cxkwzPxvOqZcUj4IVQ3","display":"Infection - Adult"}]} +{"resourceType":"Goal","id":"ebkef6fRYMQQ2wIkoURNGAA3","extension":[{"valueString":"NURSING","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"},{"valueString":"INTERDISCIPLINARY","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"},{"valueString":"RESPIRATORY THERAPY","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"},{"valueString":"SOCIAL WORK","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"}],"lifecycleStatus":"active","category":[{"coding":[{"system":"http://open.epic.com/FHIR/StructureDefinition/goal-category","code":"inpatient-care-plan-goal","display":"Inpatient Care Plan Goal"}],"text":"Inpatient Care Plan Goal"}],"description":{"text":"Absence of fever/infection during anticipated neutropenic period"},"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"startDate":"2023-06-02","addresses":[{"reference":"Condition/enxs1cxkwzPxvOqZcUj4IVQ3","display":"Infection - Adult"}]} +{"resourceType":"Goal","id":"ed-oONVmWCMix2WPAWmM8cg3","extension":[{"valueString":"NURSING","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"},{"valueString":"INTERDISCIPLINARY","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"},{"valueString":"RESPIRATORY THERAPY","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"},{"valueString":"SOCIAL WORK","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"}],"lifecycleStatus":"active","category":[{"coding":[{"system":"http://open.epic.com/FHIR/StructureDefinition/goal-category","code":"inpatient-care-plan-goal","display":"Inpatient Care Plan Goal"}],"text":"Inpatient Care Plan Goal"}],"description":{"text":"Free from fall injury"},"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"startDate":"2023-06-02","addresses":[{"reference":"Condition/e1tKlHlRqm9EXhpS9iLx4xA3","display":"Safety Adult - Fall"}]} +{"resourceType":"Goal","id":"ee47OnbFatGYF-t1YFS8qvA3","extension":[{"valueString":"NURSING","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"},{"valueString":"INTERDISCIPLINARY","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"},{"valueString":"RESPIRATORY THERAPY","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"},{"valueString":"SOCIAL WORK","url":"http://open.epic.com/FHIR/StructureDefinition/extension/multiple-expressedby"}],"lifecycleStatus":"active","category":[{"coding":[{"system":"http://open.epic.com/FHIR/StructureDefinition/goal-category","code":"inpatient-care-plan-goal","display":"Inpatient Care Plan Goal"}],"text":"Inpatient Care Plan Goal"}],"description":{"text":"Discharge to home or other facility with appropriate resources"},"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"startDate":"2023-06-02","addresses":[{"reference":"Condition/e7.CXqjHOcMA8Gxl2sOOs3w3","display":"Discharge Planning"}]} +{"resourceType":"Goal","id":"eGv1hgf8wfgd-eV1xuhoW1w3","lifecycleStatus":"active","category":[{"coding":[{"system":"http://open.epic.com/FHIR/StructureDefinition/goal-category","code":"inpatient-care-plan-goal","display":"Inpatient Care Plan Goal"}],"text":"Inpatient Care Plan Goal"}],"description":{"text":"Patient's chronic conditions and co-morbidity symptoms are monitored and maintained or improved"},"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"startDate":"2023-06-02","expressedBy":{"display":"INTERDISCIPLINARY"},"addresses":[{"reference":"Condition/eshaiIZ7f0Z.ptm.6wNwjrA3","display":"Chronic Conditions and Co-morbidities"}]} +{"resourceType":"Immunization","id":"eEAE--XrkYnFv04FWNZG8Aw3","identifier":[{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.768076","value":"29421"}],"status":"completed","vaccineCode":{"coding":[{"system":"http://hl7.org/fhir/sid/cvx","code":"115"},{"system":"http://hl7.org/fhir/sid/ndc","code":"58160-842-11"}],"text":"Tdap"},"patient":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"encounter":{"reference":"Encounter/exfbAtPCSLnf8s.193aMUnQ3","identifier":{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"29539"},"display":"Office Visit"},"occurrenceDateTime":"2023-05-21","primarySource":true,"manufacturer":{"display":"Sanofi Pasteur"},"lotNumber":"1","site":{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.768076.4040","code":"7","display":"Left deltoid"}],"text":"Left deltoid"},"route":{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.768076.4030","code":"2","display":"Intramuscular"}],"text":"Intramuscular"},"doseQuantity":{"value":0.5,"unit":"mL","system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.768076.4019","code":"1"},"performer":[{"function":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0443","code":"AP","display":"Administering Provider"}],"text":"Administering Provider"},"actor":{"reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3","type":"Practitioner","display":"Physician"}}]} +{"id":"e4W4rmGe9QzuGm2Dy4NBqVc0KDe6yGld6HW95UuN-Qd03","mode":"instance","name":"EMC Family Medicine","resourceType":"Location"} +{"id":"e6.GHPEJ3Jrk18e14wi2AJ5vFRqvLa0ZB6qH40N4UgB03","mode":"instance","name":"EMH X-Ray Imaging","resourceType":"Location"} +{"id":"eQSOrLdbVrMRc68KBcTBG4A3","mode":"instance","name":"EHS Service Area","resourceType":"Location"} +{"id":"el.Nc4rfzXa7Ml9v6gin1Pw3","mode":"instance","name":"EHS Hospital","resourceType":"Location"} +{"id":"exzJSfs-2YYqMPilpdzkcdMVzw6TB9WJfeqFRVe.cpSo3","mode":"instance","name":"EMC Cardiology Procedures","resourceType":"Location"} +{"id":"efR3SIdpRKF9BFBM5qakt9F5X1mWxjiAGu2hNTKbqOdI3","mode":"instance","name":"EMH Operating Room","resourceType":"Location"} +{"code":{"coding":[{"code":"100899","system":"urn:oid:2.16.840.1.113883.6.253"},{"code":"25990002150316","system":"urn:oid:2.16.840.1.113883.6.68"},{"code":"49431","system":"urn:oid:2.16.840.1.113883.6.162"},{"code":"4124","system":"http://www.nlm.nih.gov/research/umls/rxnorm"},{"code":"11636","system":"http://www.nlm.nih.gov/research/umls/rxnorm"},{"code":"630734","system":"http://www.nlm.nih.gov/research/umls/rxnorm"},{"code":"748794","system":"http://www.nlm.nih.gov/research/umls/rxnorm"},{"code":"748798","system":"http://www.nlm.nih.gov/research/umls/rxnorm"},{"code":"840781","system":"http://www.nlm.nih.gov/research/umls/rxnorm"}],"text":"drospirenone-ethinyl estradiol 3-0.02 MG per tablet"},"form":{"coding":[{"code":"TABS","display":"tablet","system":"urn:oid:1.2.840.114350.1.13.0.1.7.4.698288.310"}],"text":"tablet"},"id":"ej-bgums0x4N6E0.QqIjJb3kGhOQBVK3lIYjcil9mb9j-ukNrz6P2y90AiDm3R20Y3","identifier":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.698288","use":"usual","value":"70784"}],"ingredient":[{"itemCodeableConcept":{"coding":[{"code":"100899","system":"urn:oid:2.16.840.1.113883.6.253"},{"code":"25990002150316","system":"urn:oid:2.16.840.1.113883.6.68"},{"code":"49431","system":"urn:oid:2.16.840.1.113883.6.162"},{"code":"4124","system":"http://www.nlm.nih.gov/research/umls/rxnorm"},{"code":"11636","system":"http://www.nlm.nih.gov/research/umls/rxnorm"},{"code":"630734","system":"http://www.nlm.nih.gov/research/umls/rxnorm"},{"code":"748794","system":"http://www.nlm.nih.gov/research/umls/rxnorm"},{"code":"748798","system":"http://www.nlm.nih.gov/research/umls/rxnorm"},{"code":"840781","system":"http://www.nlm.nih.gov/research/umls/rxnorm"}],"text":"drospirenone-ethinyl estradiol 3-0.02 MG per tablet"}}],"resourceType":"Medication"} +{"resourceType":"MedicationRequest","id":"ePDJ.zsf3Jfg2.MKkAMgW9EcIbsaiB.y-OTrPS5v2h8Q3","identifier":[{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.798268","value":"1066899"}],"status":"active","intent":"order","category":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/medicationrequest-category","code":"community","display":"Community"}],"text":"Community"}],"medicationReference":{"reference":"Medication/ej-bgums0x4N6E0.QqIjJb3kGhOQBVK3lIYjcil9mb9j-ukNrz6P2y90AiDm3R20Y3","display":"drospirenone-ethinyl estradiol 3-0.02 MG per tablet"},"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"encounter":{"reference":"Encounter/elMz2mwjsRvKnZiR.0ceTUg3","identifier":{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"27558"},"display":"Office Visit"},"authoredOn":"2019-05-28","requester":{"reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3","type":"Practitioner","display":"Physician"},"recorder":{"reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3","type":"Practitioner","display":"Physician"},"reasonCode":[{"coding":[{"system":"http://snomed.info/sct","code":"69878008","display":"Polycystic ovaries (disorder)"},{"system":"http://hl7.org/fhir/sid/icd-9-cm/diagnosis","code":"256.4","display":"Polycystic ovaries"},{"system":"http://hl7.org/fhir/sid/icd-10-cm","code":"E28.2","display":"Polycystic ovaries"}],"text":"Polycystic ovaries"}],"courseOfTherapyType":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/medicationrequest-course-of-therapy","code":"continuous","display":"Continuous long term therapy"}],"text":"Continuous long term therapy"},"dosageInstruction":[{"text":"Take 1 tablet by mouth 1 (one) time each day., Starting Tue 5/28/2019, Until Wed 5/27/2020, Normal","patientInstruction":"Take 1 tablet by mouth 1 (one) time each day.","timing":{"repeat":{"boundsPeriod":{"start":"2019-05-28"},"count":365,"timeOfDay":["09:00:00"]},"code":{"text":"1 (one) time each day"}},"asNeededBoolean":false,"route":{"coding":[{"system":"http://snomed.info/sct","code":"260548002","display":"Oral (qualifier value)"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.4.798268.7025","code":"15","display":"Oral"}],"text":"Oral"},"method":{"coding":[{"system":"http://snomed.info/sct","code":"419652001","display":"Take"}],"text":"Take"},"doseAndRate":[{"type":{"coding":[{"system":"http://epic.com/CodeSystem/dose-rate-type","code":"calculated","display":"calculated"}],"text":"calculated"},"doseQuantity":{"value":1,"unit":"tablet","system":"http://unitsofmeasure.org","code":"{tbl}"}},{"type":{"coding":[{"system":"http://epic.com/CodeSystem/dose-rate-type","code":"admin-amount","display":"admin-amount"}],"text":"admin-amount"},"doseQuantity":{"value":1,"unit":"tablet","system":"http://unitsofmeasure.org","code":"{tbl}"}},{"type":{"coding":[{"system":"http://epic.com/CodeSystem/dose-rate-type","code":"ordered","display":"ordered"}],"text":"ordered"},"doseQuantity":{"value":1,"unit":"tablet","system":"http://unitsofmeasure.org","code":"{tbl}"}}]}],"dispenseRequest":{"validityPeriod":{"start":"2019-05-28"},"numberOfRepeatsAllowed":12,"quantity":{"value":28,"unit":"tablet"},"expectedSupplyDuration":{"value":28,"unit":"Day","system":"http://unitsofmeasure.org","code":"d"}}} +{"basedOn":[{"display":"XR CHEST 2 VIEWS","reference":"ServiceRequest/ePsXywWuaF47D-FPsxcnz3BOk1-JNF41RJJEMQu.2TCA3"}],"category":[{"coding":[{"code":"imaging","display":"Imaging","system":"http://terminology.hl7.org/CodeSystem/observation-category"}],"text":"Imaging"},{"coding":[{"code":"Imaging","display":"Imaging","system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.798268.30"}],"text":"Imaging"}],"code":{"text":"Narrative"},"effectiveDateTime":"2023-06-02T20:43:46Z","encounter":{"display":"Hospital Outpatient Visit","identifier":{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","use":"usual","value":"29541"},"reference":"Encounter/eoIRZgvu9RhrwOzDlZkRbSg3"},"id":"eWQ91GjryHtrlYhSNxK1L7mI-fgFwPAedom.YQ4JDeQk3","performer":[{"display":"Physician","reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3"}],"resourceType":"Observation","status":"final","subject":{"display":"Lopez, Camila Maria","reference":"Patient/erXuFYUfucBZaryVksYEcMg3"},"valueString":"Patient presents with history of chest pain. No rib fractures or lesions \r\nfound\r\n\r\n"} +{"basedOn":[{"display":"TRANSTHORACIC ECHO (TTE) COMPLETE","reference":"ServiceRequest/e6t1HtRj0Za8M9Y0u5nU2tu3idJ7P66B2HzlMtddZ6Wc3"}],"category":[{"coding":[{"code":"imaging","display":"Imaging","system":"http://terminology.hl7.org/CodeSystem/observation-category"}],"text":"Imaging"},{"coding":[{"code":"Echo","display":"Echocardiography","system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.798268.30"}],"text":"Echocardiography"}],"code":{"text":"Narrative"},"effectiveDateTime":"2023-06-02T20:50:25Z","encounter":{"display":"Hospital Outpatient Visit","identifier":{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","use":"usual","value":"29542"},"reference":"Encounter/ej567ARSFdaF4ooZVunIlIg3"},"id":"epvZIWrCStn-QxWdd6yDL2rsODx.M3VAMdHGGYcFbGkQ3","performer":[{"display":"Physician","reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3"}],"resourceType":"Observation","status":"final","subject":{"display":"Lopez, Camila Maria","reference":"Patient/erXuFYUfucBZaryVksYEcMg3"},"valueString":"· Left ventricle ejection fraction is normal.\r\n· Septal wall has abnormal motion consistent with post-operative status.\r\n \r\nHere is the interpretation summary text.\r\n\r\n\r\nLeft Ventricle\r\nEjection fraction is normal. Cavity is normal. End-diastolic volume appears mildly increased. Normal wall thickness observed. Mid-cavity obstruction is present. Shape is normal. Abnormal septal motion consistent with post-operative state. Normal fractional shortening. Normal left ventricle diastolic function. The findings are consistent with dilated cardiomyopathy."} +{"basedOn":[{"display":"XR CHEST 2 VIEWS","reference":"ServiceRequest/ePsXywWuaF47D-FPsxcnz3BOk1-JNF41RJJEMQu.2TCA3"}],"category":[{"coding":[{"code":"imaging","display":"Imaging","system":"http://terminology.hl7.org/CodeSystem/observation-category"}],"text":"Imaging"},{"coding":[{"code":"Imaging","display":"Imaging","system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.798268.30"}],"text":"Imaging"}],"code":{"text":"Impression"},"effectiveDateTime":"2023-06-02T20:43:46Z","encounter":{"display":"Hospital Outpatient Visit","identifier":{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","use":"usual","value":"29541"},"reference":"Encounter/eoIRZgvu9RhrwOzDlZkRbSg3"},"id":"eWQ91GjryHtrlYhSNxK1L7hOQbiABlro0a6oo2bIh68I3","performer":[{"display":"Physician","reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3"}],"resourceType":"Observation","status":"final","subject":{"display":"Lopez, Camila Maria","reference":"Patient/erXuFYUfucBZaryVksYEcMg3"},"valueString":" \r\nNo findings. Refer to cardiology."} +{"basedOn":[{"display":"HEMOGLOBIN A1C","reference":"ServiceRequest/egf4KkhXmgeVWImXEEYH.CEE1ZgJG5SQy2vBX1HgvBCs3"}],"category":[{"coding":[{"code":"laboratory","display":"Laboratory","system":"http://terminology.hl7.org/CodeSystem/observation-category"}],"text":"Laboratory"},{"coding":[{"code":"Lab","display":"Lab","system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.798268.30"}],"text":"Lab"}],"code":{"coding":[{"code":"4548-4","display":"Hemoglobin A1c/Hemoglobin.total in Blood","system":"http://loinc.org"},{"code":"1558024","display":"Hemoglobin A1C","system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.768282"}],"text":"Hemoglobin A1C"},"effectiveDateTime":"2019-05-28T14:22:00Z","encounter":{"display":"Office Visit","identifier":{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","use":"usual","value":"27558"},"reference":"Encounter/elMz2mwjsRvKnZiR.0ceTUg3"},"id":"eyPMWgv2u2RUfsV4p1lLKuUtqyPs2-QNi2zKvbTsFYtRByc6B.cSi1iVU5V2HOpX23","issued":"2019-05-28T14:22:41Z","resourceType":"Observation","specimen":{"display":"Specimen","reference":"Specimen/e.n21kE9PMQ94vOo1lkT2XkN1av86guPxYsFU20qTQ1M3"},"status":"final","subject":{"display":"Lopez, Camila Maria","reference":"Patient/erXuFYUfucBZaryVksYEcMg3"},"valueQuantity":{"value":5.1}} +{"active":true,"alias":["CPACS"],"id":"eVUDU8oWh9.KM7oI8Mu9jlqn1C3d5ozeHAXDHuaPYsvU3","name":"CPACS","resourceType":"Organization"} +{"active":true,"alias":["IMG"],"id":"etOhDRFmHqAJ3CBmvHCeF9VYgSiH5bYCNkDSOho2TXb43","name":"IMAGING","resourceType":"Organization"} +{"active":true,"address":[{"city":"VERONA","line":["123 Anywhere St."],"postalCode":"53593","state":"Wisconsin"}],"id":"enRyWnSP963FYDpoks4NHOA3","identifier":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.61","type":{"text":"EPIC"},"use":"usual","value":"1627363736"}],"name":"EHS Service Area","resourceType":"Organization"} +{"id":"erXuFYUfucBZaryVksYEcMg3","extension":[{"url":"http://open.epic.com/FHIR/StructureDefinition/extension/legal-sex","valueCodeableConcept":{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.10.698084.130.657370.19999000","code":"female","display":"female"}]}},{"extension":[{"url":"ombCategory","valueCoding":{"system":"urn:oid:2.16.840.1.113883.6.238","code":"2131-1","display":"Other Race"}},{"url":"text","valueString":"Other"}],"url":"http://hl7.org/fhir/us/core/StructureDefinition/us-core-race"},{"extension":[{"url":"text","valueString":"Unknown"}],"url":"http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity"}],"identifier":[{"use":"usual","type":{"text":"EPIC"},"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.0","value":"E4007"},{"use":"usual","type":{"text":"EXTERNAL"},"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.698084","value":"Z6129"},{"use":"usual","type":{"text":"FHIR"},"system":"http://open.epic.com/FHIR/StructureDefinition/patient-dstu2-fhir-id","value":"TnOZ.elPXC6zcBNFMcFA7A5KZbYxo2.4T-LylRk4GoW4B"},{"use":"usual","type":{"text":"FHIR STU3"},"system":"http://open.epic.com/FHIR/StructureDefinition/patient-fhir-id","value":"erXuFYUfucBZaryVksYEcMg3"},{"use":"usual","type":{"text":"INTERNAL"},"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.698084","value":" Z6129"},{"use":"usual","type":{"text":"EPI"},"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.14","value":"203713"},{"use":"usual","type":{"text":"MYCHARTLOGIN"},"system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.878082.110","value":"FHIRCAMILA"},{"use":"usual","type":{"text":"WPRINTERNAL"},"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.878082","value":"736"}],"active":true,"name":[{"use":"official","text":"Camila Maria Lopez","family":"Lopez","given":["Camila","Maria"]},{"use":"usual","text":"Camila Maria Lopez","family":"Lopez","given":["Camila","Maria"]}],"telecom":[{"system":"phone","value":"469-555-5555","use":"home"},{"system":"phone","value":"469-888-8888","use":"work"},{"system":"email","value":"knixontestemail@epic.com","rank":1}],"gender":"female","birthDate":"1987-09-12","deceasedBoolean":false,"address":[{"use":"home","line":["3268 West Johnson St.","Apt 117"],"city":"GARLAND","district":"DALLAS","state":"TX","postalCode":"75043","country":"US","period":{"start":"2019-05-24"}},{"use":"old","line":["3268 West Johnson St.","Apt 117"],"city":"GARLAND","district":"DALLAS","state":"TX","postalCode":"75043","country":"US"}],"maritalStatus":{"text":"Married"},"communication":[{"language":{"coding":[{"system":"urn:ietf:bcp:47","code":"en","display":"English"}],"text":"English"},"preferred":true}],"generalPractitioner":[{"reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3","type":"Practitioner","display":"Physician"}],"managingOrganization":{"reference":"Organization/enRyWnSP963FYDpoks4NHOA3","display":"EHS Service Area"},"resourceType":"Patient"} +{"active":true,"id":"eHP1iZcoohQAKIcKyP6CDvA3","identifier":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.697780","type":{"text":"INTERNAL"},"use":"usual","value":" 5157"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.697780","type":{"text":"EXTERNAL"},"use":"usual","value":"5157"}],"name":[{"family":"Z","given":["Robert"],"text":"Robert Z","use":"usual"}],"resourceType":"Practitioner"} +{"active":true,"gender":"male","id":"eM5CWtq15N0WJeuCet5bJlQ3","identifier":[{"system":"http://hl7.org/fhir/sid/us-npi","type":{"text":"NPI"},"use":"usual","value":"1627363736"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.697780","type":{"text":"INTERNAL"},"use":"usual","value":" FAMMD"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.697780","type":{"text":"EXTERNAL"},"use":"usual","value":"FAMMD"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.6","type":{"text":"PROVID"},"use":"usual","value":"1000"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.7","type":{"text":"EPIC"},"use":"usual","value":"207Q00000X"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.60","type":{"text":"NPI"},"use":"usual","value":"1627363736"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.63","type":{"text":"Epic"},"use":"usual","value":"6011"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.836982","type":{"text":"INTERNAL"},"use":"usual","value":" E1000"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.836982","type":{"text":"EXTERNAL"},"use":"usual","value":"E1000"}],"name":[{"_family":{"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/data-absent-reason","valueCode":"masked"}]},"given":["Physician"],"text":"Physician","use":"usual"}],"photo":[{"url":"Binary/esQeLhUcHfUSQnyrAJ7GV7A3"}],"resourceType":"Practitioner"} +{"active":true,"id":"elpRiy0AYgjhdjAhTBJ3Aiw3","identifier":[{"system":"http://hl7.org/fhir/sid/us-npi","type":{"text":"NPI"},"use":"usual","value":"1400103994"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.6","type":{"text":"PROVID"},"use":"usual","value":"400103"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.7","type":{"text":"EPIC"},"use":"usual","value":"207Q00000X"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.60","type":{"text":"NPI"},"use":"usual","value":"1400103994"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.63","type":{"text":"Epic"},"use":"usual","value":"TEMP400103"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.836982","type":{"text":"INTERNAL"},"use":"usual","value":" E400103"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.836982","type":{"text":"EXTERNAL"},"use":"usual","value":"E400103"}],"name":[{"_family":{"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/data-absent-reason","valueCode":"masked"}]},"given":["Np"],"text":"Np","use":"usual"}],"resourceType":"Practitioner"} +{"active":true,"id":"e2qocqJm-DdjHLS0Not4qjA3","identifier":[{"system":"http://hl7.org/fhir/sid/us-npi","type":{"text":"NPI"},"use":"usual","value":"1101299992"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.7","type":{"text":"EPIC"},"use":"usual","value":"208D00000X"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.60","type":{"text":"NPI"},"use":"usual","value":"1101299992"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.63","type":{"text":"Epic"},"use":"usual","value":"830"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.836982","type":{"text":"INTERNAL"},"use":"usual","value":" E1012"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.836982","type":{"text":"EXTERNAL"},"use":"usual","value":"E1012"}],"name":[{"_family":{"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/data-absent-reason","valueCode":"masked"}]},"given":["Historical"],"text":"Historical","use":"usual"}],"resourceType":"Practitioner"} +{"active":true,"gender":"male","id":"eUXiD4IyBLJFoR2VHOjfTdg3","identifier":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.697780","type":{"text":"INTERNAL"},"use":"usual","value":" FAMMD"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.697780","type":{"text":"EXTERNAL"},"use":"usual","value":"FAMMD"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.6","type":{"text":"PROVID"},"use":"usual","value":"1000"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.7","type":{"text":"EPIC"},"use":"usual","value":"207Q00000X"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.60","type":{"text":"NPI"},"use":"usual","value":"1627363736"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.5.737384.63","type":{"text":"Epic"},"use":"usual","value":"6011"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.836982","type":{"text":"INTERNAL"},"use":"usual","value":" E1000"},{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.836982","type":{"text":"EXTERNAL"},"use":"usual","value":"E1000"}],"name":[{"_family":{"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/data-absent-reason","valueCode":"masked"}]},"given":["Physician"],"text":"Physician","use":"usual"}],"resourceType":"Practitioner"} +{"resourceType":"Procedure","id":"e0ZpcfJHKvPE0nQfie.r1utQLnHC473E7POoGnZIh7yj3wWsggKlPmbROlo9Q5vi-3","identifier":[{"use":"usual","type":{"text":"ORD"},"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.798268","value":"1066915"},{"use":"usual","type":{"text":"EAP"},"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.696580","value":"104443"},{"use":"usual","type":{"text":"OPE"},"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.798069","value":"88"}],"status":"completed","category":{"coding":[{"system":"http://snomed.info/sct","code":"103693007","display":"Diagnostic procedure"}],"text":"Ordered Procedures"},"code":{"coding":[{"system":"http://www.ama-assn.org/go/cpt","code":"93306","display":"HC TRANSTHORACIC ECHO"}],"text":"TRANSTHORACIC ECHO (TTE) COMPLETE"},"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"encounter":{"reference":"Encounter/ej567ARSFdaF4ooZVunIlIg3","identifier":{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"29542"},"display":"Hospital Outpatient Visit"},"performedDateTime":"2023-06-02T20:50:25Z","asserter":{"reference":"Practitioner/elpRiy0AYgjhdjAhTBJ3Aiw3","type":"Practitioner","display":"Np"},"performer":[{"actor":{"reference":"Practitioner/eM5CWtq15N0WJeuCet5bJlQ3","type":"Practitioner","display":"Physician"},"onBehalfOf":{"reference":"Organization/eVUDU8oWh9.KM7oI8Mu9jlqn1C3d5ozeHAXDHuaPYsvU3","type":"Organization","display":"CPACS"}}],"reasonCode":[{"coding":[{"system":"http://hl7.org/fhir/sid/icd-9-cm/diagnosis","code":"786.50","display":"Ischemic chest pain (CMS/HCC)"}],"text":"Ischemic chest pain"}],"report":[{"reference":"DiagnosticReport/ejRFFAyoWd-DvE0lHi4UEUVA9qTH9PxfCLhEetlq04Ck3","type":"DiagnosticReport","display":"TRANSTHORACIC ECHO (TTE) COMPLETE"}]} +{"resourceType":"Procedure","id":"ea8l39YGDvGqR.Rw4MVH4p6am4lpdDPM2QFz0gpZXtbnV8a5ab-hpVbV1IBMzJ7D53","identifier":[{"use":"usual","type":{"text":"ORL"},"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.798276","value":"1505"},{"use":"usual","type":{"text":"EAP"},"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.696580","value":"14493"},{"use":"usual","type":{"text":"OPE"},"system":"urn:oid:1.2.840.114350.1.13.0.1.7.2.798069","value":"90"}],"status":"completed","category":{"coding":[{"system":"http://snomed.info/sct","code":"387713003","display":"Surgical procedure"}],"text":"Surgical Procedures"},"code":{"coding":[{"system":"http://www.ama-assn.org/go/cpt","code":"44950","display":"PR APPENDECTOMY"}],"text":"Removal of appendix (APPENDECTOMY)"},"subject":{"reference":"Patient/erXuFYUfucBZaryVksYEcMg3","display":"Lopez, Camila Maria"},"encounter":{"reference":"Encounter/eGmO0h.1.UQQrExl4bfM7OQ3","identifier":{"use":"usual","system":"urn:oid:1.2.840.114350.1.13.0.1.7.3.698084.8","value":"29545"},"display":"Surgery"},"performedPeriod":{"start":"2023-06-06T13:56:00Z","end":"2023-06-06T14:56:00Z"},"recorder":{"reference":"Practitioner/eHP1iZcoohQAKIcKyP6CDvA3","type":"Practitioner","display":"Robert Z"},"location":{"reference":"Location/el.Nc4rfzXa7Ml9v6gin1Pw3","display":"EHS Hospital"},"bodySite":[{"extension":[{"valueCodeableConcept":{"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.4.798276.2010","code":"4","display":"N/A"}],"text":"N/A"},"url":"http://nictiz.nl/fhir/StructureDefinition/BodySite-Qualifier"}],"coding":[{"system":"urn:oid:1.2.840.114350.1.13.0.1.7.4.798276.2015","code":"10","display":"Abdomen"}],"text":"Abdomen"}]} diff --git a/backend/pkg/models/resource_graph_options.go b/backend/pkg/models/resource_graph_options.go index 24a7dd8a..5b2fd75e 100644 --- a/backend/pkg/models/resource_graph_options.go +++ b/backend/pkg/models/resource_graph_options.go @@ -1,5 +1,5 @@ package models type ResourceGraphOptions struct { - Page int + ResourcesIds []OriginBase `json:"resource_ids"` } diff --git a/backend/pkg/web/handler/resource_fhir.go b/backend/pkg/web/handler/resource_fhir.go index 71bc1293..534220db 100644 --- a/backend/pkg/web/handler/resource_fhir.go +++ b/backend/pkg/web/handler/resource_fhir.go @@ -76,7 +76,7 @@ func ListResourceFhir(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"success": true, "data": wrappedResourceModels}) } -//this endpoint retrieves a specific resource by its ID +// this endpoint retrieves a specific resource by its ID func GetResourceFhir(c *gin.Context) { logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry) databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository) @@ -129,18 +129,20 @@ func GetResourceFhirGraph(c *gin.Context) { graphType := strings.Trim(c.Param("graphType"), "/") - graphOptions := models.ResourceGraphOptions{} - if len(c.Query("page")) > 0 { - pageNumb, err := strconv.Atoi(c.Query("page")) - if err != nil { - logger.Errorln("An error occurred while calculating page number", err) - c.JSON(http.StatusInternalServerError, gin.H{"success": false}) - return - } - graphOptions.Page = pageNumb + var graphOptions models.ResourceGraphOptions + if err := c.ShouldBindJSON(&graphOptions); err != nil { + logger.Errorln("An error occurred while parsing resource graph query options", err) + c.JSON(http.StatusBadRequest, gin.H{"success": false}) + return } - resourceListDictionary, resourceListMetadata, err := databaseRepo.GetFlattenedResourceGraph(c, pkg.ResourceGraphType(graphType), graphOptions) + if len(graphOptions.ResourcesIds) == 0 { + logger.Errorln("No resource ids specified") + c.JSON(http.StatusBadRequest, gin.H{"success": false}) + return + } + + resourceListDictionary, err := databaseRepo.GetFlattenedResourceGraph(c, pkg.ResourceGraphType(graphType), graphOptions) if err != nil { logger.Errorln("An error occurred while retrieving list of resources", err) c.JSON(http.StatusInternalServerError, gin.H{"success": false}) @@ -148,7 +150,6 @@ func GetResourceFhirGraph(c *gin.Context) { } c.JSON(http.StatusOK, gin.H{"success": true, "data": map[string]interface{}{ - "results": resourceListDictionary, - "metadata": resourceListMetadata, + "results": resourceListDictionary, }}) } diff --git a/backend/pkg/web/server.go b/backend/pkg/web/server.go index 9a032cd5..a8171b24 100644 --- a/backend/pkg/web/server.go +++ b/backend/pkg/web/server.go @@ -72,7 +72,7 @@ func (ae *AppEngine) Setup() (*gin.RouterGroup, *gin.Engine) { secure.POST("/source/:sourceId/sync", handler.SourceSync) secure.GET("/source/:sourceId/summary", handler.GetSourceSummary) secure.GET("/resource/fhir", handler.ListResourceFhir) - secure.GET("/resource/graph/:graphType", handler.GetResourceFhirGraph) + secure.POST("/resource/graph/:graphType", handler.GetResourceFhirGraph) secure.GET("/resource/fhir/:sourceId/:resourceId", handler.GetResourceFhir) secure.POST("/resource/composition", handler.CreateResourceComposition) diff --git a/frontend/src/app/components/fhir/fhir-resource/fhir-resource.component.ts b/frontend/src/app/components/fhir/fhir-resource/fhir-resource.component.ts index d0951eaf..f87df80a 100644 --- a/frontend/src/app/components/fhir/fhir-resource/fhir-resource.component.ts +++ b/frontend/src/app/components/fhir/fhir-resource/fhir-resource.component.ts @@ -25,6 +25,10 @@ import {DiagnosticReportComponent} from '../resources/diagnostic-report/diagnost import {PractitionerComponent} from '../resources/practitioner/practitioner.component'; import {DocumentReferenceComponent} from '../resources/document-reference/document-reference.component'; import {MediaComponent} from '../resources/media/media.component'; +import {LocationComponent} from '../resources/location/location.component'; +import {OrganizationComponent} from '../resources/organization/organization.component'; +import {ObservationComponent} from '../resources/observation/observation.component'; + @Component({ selector: 'fhir-resource', @@ -119,6 +123,9 @@ export class FhirResourceComponent implements OnInit, OnChanges { case "Immunization": { return ImmunizationComponent; } + case "Location": { + return LocationComponent; + } case "Media": { return MediaComponent; } @@ -137,9 +144,12 @@ export class FhirResourceComponent implements OnInit, OnChanges { // case "NutritionOrder": { // return ListNutritionOrderComponent; // } - // case "Observation": { - // return ListObservationComponent; - // } + case "Observation": { + return ObservationComponent; + } + case "Organization": { + return OrganizationComponent; + } case "Procedure": { return ProcedureComponent; } diff --git a/frontend/src/app/components/fhir/resources/binary/binary.component.ts b/frontend/src/app/components/fhir/resources/binary/binary.component.ts index 191a2e55..259deec3 100644 --- a/frontend/src/app/components/fhir/resources/binary/binary.component.ts +++ b/frontend/src/app/components/fhir/resources/binary/binary.component.ts @@ -56,6 +56,7 @@ export class BinaryComponent implements OnInit, FhirResourceComponentInterface { this.markForCheck() }, (error) => { this.loading = false + console.error("Failed to lookup binary resource from attachment:", error) this.markForCheck() }) } diff --git a/frontend/src/app/components/fhir/resources/diagnostic-report/diagnostic-report.component.ts b/frontend/src/app/components/fhir/resources/diagnostic-report/diagnostic-report.component.ts index 0dba05b9..e0091d93 100644 --- a/frontend/src/app/components/fhir/resources/diagnostic-report/diagnostic-report.component.ts +++ b/frontend/src/app/components/fhir/resources/diagnostic-report/diagnostic-report.component.ts @@ -32,12 +32,12 @@ export class DiagnosticReportComponent implements OnInit, FhirResourceComponentI data: this.displayModel?.issued, enabled: !!this.displayModel?.issued, }, - { - label: 'Category', - data: this.displayModel?.category_coding, - data_type: TableRowItemDataType.CodingList, - enabled: this.displayModel?.has_category_coding, - }, + // { + // label: 'Category', + // data: this.displayModel?.category_coding, + // data_type: TableRowItemDataType.CodingList, + // enabled: this.displayModel?.has_category_coding, + // }, { label: 'Performer', data: this.displayModel?.performer, @@ -50,6 +50,15 @@ export class DiagnosticReportComponent implements OnInit, FhirResourceComponentI enabled: !!this.displayModel?.conclusion, }, ]; + + for(let categoryCodeable of (this.displayModel?.category_coding || [])){ + this.tableData.push({ + label: `Category`, + data_type: TableRowItemDataType.CodableConcept, + data: categoryCodeable, + enabled: true, + }) + } } markForCheck(){ this.changeRef.markForCheck() diff --git a/frontend/src/app/components/fhir/resources/location/location.component.html b/frontend/src/app/components/fhir/resources/location/location.component.html new file mode 100644 index 00000000..2668b35e --- /dev/null +++ b/frontend/src/app/components/fhir/resources/location/location.component.html @@ -0,0 +1,20 @@ +
+
+
+
{{displayModel?.name}}
+
+ {{displayModel?.status}} + + + + + +
+
+

Details and position information for a physical place where services are provided and resources and participants may be stored, found, contained, or accommodated.

+ +
+ +
diff --git a/frontend/src/app/components/fhir/resources/location/location.component.scss b/frontend/src/app/components/fhir/resources/location/location.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/frontend/src/app/components/fhir/resources/location/location.component.spec.ts b/frontend/src/app/components/fhir/resources/location/location.component.spec.ts new file mode 100644 index 00000000..ef7bda14 --- /dev/null +++ b/frontend/src/app/components/fhir/resources/location/location.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LocationComponent } from './location.component'; + +describe('LocationComponent', () => { + let component: LocationComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ LocationComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LocationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/fhir/resources/location/location.component.ts b/frontend/src/app/components/fhir/resources/location/location.component.ts new file mode 100644 index 00000000..6d484911 --- /dev/null +++ b/frontend/src/app/components/fhir/resources/location/location.component.ts @@ -0,0 +1,77 @@ +import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core'; +import {NgbCollapseModule} from '@ng-bootstrap/ng-bootstrap'; +import {CommonModule} from '@angular/common'; +import {BadgeComponent} from '../../common/badge/badge.component'; +import {TableComponent} from '../../common/table/table.component'; +import {Router, RouterModule} from '@angular/router'; +import {FhirResourceComponentInterface} from '../../fhir-resource/fhir-resource-component-interface'; +import {ImmunizationModel} from '../../../../../lib/models/resources/immunization-model'; +import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item'; +import * as _ from 'lodash'; +import {LocationModel} from '../../../../../lib/models/resources/location-model'; + +@Component({ + standalone: true, + imports: [NgbCollapseModule, CommonModule, BadgeComponent, TableComponent, RouterModule], + selector: 'fhir-location', + templateUrl: './location.component.html', + styleUrls: ['./location.component.scss'] +}) +export class LocationComponent implements OnInit, FhirResourceComponentInterface { + @Input() displayModel: LocationModel + @Input() showDetails: boolean = true + + isCollapsed: boolean = false + tableData: TableRowItem[] = [] + + constructor(public changeRef: ChangeDetectorRef, public router: Router) { } + + ngOnInit(): void { + + this.tableData.push( { + label: 'Type', + data: this.displayModel?.type?.[0], + data_type: TableRowItemDataType.CodableConcept, + enabled: !!this.displayModel?.type?.[0], + }, + { + label: 'Physical Type', + data: this.displayModel?.physical_type, + data_type: TableRowItemDataType.CodableConcept, + enabled: !!this.displayModel?.physical_type && this.displayModel?.physical_type?.coding?.length > 0, + }, + { + label: 'Location Mode', + data: this.displayModel?.mode, + enabled: !!this.displayModel?.mode, + }, + { + label: 'Description', + data: this.displayModel?.description, + enabled: !!this.displayModel?.description, + }, + // { + // label: 'Address', + // data: this.displayModel?.address, + // data_type: TableRowItemDataType.Reference, + // enabled: !!this.displayModel?.address, + // }, + { + label: 'Telecom', + data: this.displayModel?.telecom, + data_type: TableRowItemDataType.Reference, + enabled: !!this.displayModel?.telecom, + }, + { + label: 'Managing Organization', + data: this.displayModel?.managing_organization, + data_type: TableRowItemDataType.Reference, + enabled: !!this.displayModel?.managing_organization, + }) + + } + markForCheck(){ + this.changeRef.markForCheck() + } + +} diff --git a/frontend/src/app/components/fhir/resources/observation/observation.component.html b/frontend/src/app/components/fhir/resources/observation/observation.component.html new file mode 100644 index 00000000..636f4efd --- /dev/null +++ b/frontend/src/app/components/fhir/resources/observation/observation.component.html @@ -0,0 +1,28 @@ +
+
+
+
{{displayModel?.sort_title}}
+
+ {{displayModel?.status}} + + + + + +
+
+

Observations are a central element in healthcare, used to support diagnosis, monitor progress, determine baselines and patterns and even capture demographic characteristics.

+ + + +
+ +
diff --git a/frontend/src/app/components/fhir/resources/observation/observation.component.scss b/frontend/src/app/components/fhir/resources/observation/observation.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/frontend/src/app/components/fhir/resources/observation/observation.component.spec.ts b/frontend/src/app/components/fhir/resources/observation/observation.component.spec.ts new file mode 100644 index 00000000..9f8399ed --- /dev/null +++ b/frontend/src/app/components/fhir/resources/observation/observation.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ObservationComponent } from './observation.component'; + +describe('ObservationComponent', () => { + let component: ObservationComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ObservationComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ObservationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/fhir/resources/observation/observation.component.ts b/frontend/src/app/components/fhir/resources/observation/observation.component.ts new file mode 100644 index 00000000..174be02c --- /dev/null +++ b/frontend/src/app/components/fhir/resources/observation/observation.component.ts @@ -0,0 +1,154 @@ +import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core'; +import {NgbCollapseModule} from '@ng-bootstrap/ng-bootstrap'; +import {CommonModule, formatDate} from '@angular/common'; +import {BadgeComponent} from '../../common/badge/badge.component'; +import {TableComponent} from '../../common/table/table.component'; +import {Router, RouterModule} from '@angular/router'; +import {LocationModel} from '../../../../../lib/models/resources/location-model'; +import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item'; +import {ObservationModel} from '../../../../../lib/models/resources/observation-model'; +import {ChartConfiguration} from 'chart.js'; +import fhirpath from 'fhirpath'; +import {NgChartsModule} from 'ng2-charts'; + +@Component({ + standalone: true, + imports: [NgbCollapseModule, CommonModule, BadgeComponent, TableComponent, RouterModule, NgChartsModule], + selector: 'fhir-observation', + templateUrl: './observation.component.html', + styleUrls: ['./observation.component.scss'] +}) +export class ObservationComponent implements OnInit { + @Input() displayModel: ObservationModel + @Input() showDetails: boolean = true + + isCollapsed: boolean = false + tableData: TableRowItem[] = [] + + //observation chart data + chartHeight = 45 + + barChartData =[ + + { + label: "Reference", + data: [[55,102], [44,120]], + backgroundColor: "rgba(91, 71, 251,0.6)", + hoverBackgroundColor: "rgba(91, 71, 251,0.2)" + },{ + label: "Current", + data: [[80,81], [130,131]], + borderColor: "rgba(0,0,0,1)", + backgroundColor: "rgba(0,0,0,1)", + hoverBackgroundColor: "rgba(0,0,0,1)", + minBarLength: 3, + barPercentage: 1, + tooltip: { + + } + } + ] as ChartConfiguration<'bar'>['data']['datasets'] + + barChartLabels = [] // ["2020", "2018"] //["1","2","3","4","5","6","7","8"] + + barChartOptions = { + indexAxis: 'y', + legend:{ + display: false, + }, + autoPadding: true, + //add padding to fix tooltip cutoff + layout: { + padding: { + left: 0, + right: 4, + top: 0, + bottom: 10 + } + }, + scales: { + y: { + stacked: true, + ticks: { + beginAtZero: true, + fontSize: 10, + min: 0, + // max: 80 + }, + }, + x: { + scaleLabel:{ + display: false, + labelString: "xaxis", + padding: 4, + }, + // stacked: true, + ticks: { + beginAtZero: true, + fontSize: 10, + min: 0, + // max: 80 + }, + }, + } + } as ChartConfiguration<'bar'>['options'] + + barChartColors = [ + { + backgroundColor: 'white' + } + ]; + + + constructor(public changeRef: ChangeDetectorRef, public router: Router) { } + + ngOnInit(): void { + + this.tableData.push( { + label: 'Issued on', + data: this.displayModel?.effective_date, + enabled: !!this.displayModel?.effective_date, + }, + { + label: 'Subject', + data: this.displayModel?.subject, + data_type: TableRowItemDataType.Reference, + enabled: !!this.displayModel?.subject , + }, + { + label: 'Coding', + data: this.displayModel?.code, + data_type: TableRowItemDataType.Coding, + enabled: !!this.displayModel?.code, + }, + { + label: 'Value', + data: [this.displayModel?.value_quantity_value,this.displayModel?.value_quantity_unit].join(" "), + enabled: !!this.displayModel?.value_quantity_value, + }, + { + label: 'Reference', + data: [this.displayModel?.reference_range?.[0]?.low?.value,this.displayModel?.reference_range?.[0]?.high?.value].join(" "), + enabled: !!this.displayModel?.reference_range, + }) + + + //populate chart data + + this.barChartLabels.push( + formatDate(this.displayModel.effective_date, "mediumDate", "en-US", undefined) + ) + + this.barChartData[0].data = [[this.displayModel.reference_range?.[0]?.low?.value, this.displayModel.reference_range?.[0]?.high?.value]] + this.barChartData[1].data = [[this.displayModel.value_quantity_value as number, this.displayModel.value_quantity_value as number]] + + let suggestedMax = (this.displayModel.value_quantity_value as number) * 1.1; + this.barChartOptions.scales['x']['suggestedMax'] = suggestedMax + + console.log("Observation chart data: ", this.barChartData[0].data, this.barChartData[1].data) + } + markForCheck(){ + this.changeRef.markForCheck() + } + +} diff --git a/frontend/src/app/components/fhir/resources/organization/organization.component.html b/frontend/src/app/components/fhir/resources/organization/organization.component.html new file mode 100644 index 00000000..e48b81b3 --- /dev/null +++ b/frontend/src/app/components/fhir/resources/organization/organization.component.html @@ -0,0 +1,19 @@ +
+
+
+
{{displayModel?.name}}
+
+ + + + + +
+
+

A formally or informally recognized grouping of people or organizations formed for the purpose of achieving some form of collective action. Includes companies, institutions, corporations, departments, community groups, healthcare practice groups, payer/insurer, etc.

+ +
+ +
diff --git a/frontend/src/app/components/fhir/resources/organization/organization.component.scss b/frontend/src/app/components/fhir/resources/organization/organization.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/frontend/src/app/components/fhir/resources/organization/organization.component.spec.ts b/frontend/src/app/components/fhir/resources/organization/organization.component.spec.ts new file mode 100644 index 00000000..2f0e5fbe --- /dev/null +++ b/frontend/src/app/components/fhir/resources/organization/organization.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { OrganizationComponent } from './organization.component'; + +describe('OrganizationComponent', () => { + let component: OrganizationComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ OrganizationComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(OrganizationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/fhir/resources/organization/organization.component.ts b/frontend/src/app/components/fhir/resources/organization/organization.component.ts new file mode 100644 index 00000000..96a70241 --- /dev/null +++ b/frontend/src/app/components/fhir/resources/organization/organization.component.ts @@ -0,0 +1,77 @@ +import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core'; +import {NgbCollapseModule} from '@ng-bootstrap/ng-bootstrap'; +import {CommonModule} from '@angular/common'; +import {BadgeComponent} from '../../common/badge/badge.component'; +import {TableComponent} from '../../common/table/table.component'; +import {Router, RouterModule} from '@angular/router'; +import {LocationModel} from '../../../../../lib/models/resources/location-model'; +import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item'; +import {OrganizationModel} from '../../../../../lib/models/resources/organization-model'; + +@Component({ + standalone: true, + imports: [NgbCollapseModule, CommonModule, BadgeComponent, TableComponent, RouterModule], + selector: 'fhir-organization', + templateUrl: './organization.component.html', + styleUrls: ['./organization.component.scss'] +}) +export class OrganizationComponent implements OnInit { + @Input() displayModel: OrganizationModel + @Input() showDetails: boolean = true + + isCollapsed: boolean = false + tableData: TableRowItem[] = [] + + constructor(public changeRef: ChangeDetectorRef, public router: Router) { } + + ngOnInit(): void { + + for(let idCoding of (this.displayModel?.identifier || [])){ + this.tableData.push({ + label: `Identifier (${idCoding.system})`, + data: idCoding.display || idCoding.value, + enabled: true, + }) + } + + for(let address of (this.displayModel?.addresses || [])){ + let addressParts = [] + if(address.line){ + addressParts.push(address.line.join(' ')) + } + if(address.city){ + addressParts.push(address.city) + } + if(address.state){ + addressParts.push(address.state) + } + if(address.postalCode){ + addressParts.push(address.postalCode) + } + + this.tableData.push({ + label: 'Address', + data: addressParts.join(", "), + enabled: !!addressParts, + }) + } + + this.tableData.push( { + label: 'Contacts', + data: this.displayModel?.telecom, + data_type: TableRowItemDataType.CodingList, + enabled: !!this.displayModel?.telecom, + }, + { + label: 'Type', + data: this.displayModel?.type_codings, + data_type: TableRowItemDataType.CodableConcept, + enabled: !!this.displayModel?.type_codings && this.displayModel.type_codings.length > 0, + }) + + } + markForCheck(){ + this.changeRef.markForCheck() + } + +} diff --git a/frontend/src/app/components/footer/footer.component.html b/frontend/src/app/components/footer/footer.component.html index 7a0f6d04..7cdcf752 100644 --- a/frontend/src/app/components/footer/footer.component.html +++ b/frontend/src/app/components/footer/footer.component.html @@ -2,7 +2,7 @@ diff --git a/frontend/src/app/components/report-labs-observation/report-labs-observation.component.ts b/frontend/src/app/components/report-labs-observation/report-labs-observation.component.ts index 1dbc0b11..3852ba5c 100644 --- a/frontend/src/app/components/report-labs-observation/report-labs-observation.component.ts +++ b/frontend/src/app/components/report-labs-observation/report-labs-observation.component.ts @@ -23,7 +23,7 @@ export class ReportLabsObservationComponent implements OnInit { // https://stackoverflow.com/questions/62711919/chart-js-horizontal-lines-per-bar - chartHeight = 45 + chartHeight = 60 barChartData =[ // { @@ -127,7 +127,7 @@ export class ReportLabsObservationComponent implements OnInit { ngOnInit(): void { - let currentValues = [] + let currentValues: number[] = [] let referenceRanges = [] @@ -144,6 +144,12 @@ export class ReportLabsObservationComponent implements OnInit { ) //get current value + // let currentValue = fhirpath.evaluate(observation.resource_raw, "Observation.valueQuantity.value")[0] + // if(currentValue != null){ + // currentValues.push([currentValue, currentValue]) + // } else { + // currentValues.push([]) + // } currentValues.push(fhirpath.evaluate(observation.resource_raw, "Observation.valueQuantity.value")[0]) //set chart x-axis label @@ -158,6 +164,13 @@ export class ReportLabsObservationComponent implements OnInit { //add low/high ref value blocks + // let referenceLow = fhirpath.evaluate(observation.resource_raw, "Observation.referenceRange.low.value")[0] + // let referenceHigh = fhirpath.evaluate(observation.resource_raw, "Observation.referenceRange.high.value")[0] + // if (referenceLow != null && referenceHigh != null){ + // referenceRanges.push([referenceLow, referenceHigh]) + // } else { + // referenceRanges.push([0,0]) + // } referenceRanges.push([ fhirpath.evaluate(observation.resource_raw, "Observation.referenceRange.low.value")[0], fhirpath.evaluate(observation.resource_raw, "Observation.referenceRange.high.value")[0] @@ -169,6 +182,13 @@ export class ReportLabsObservationComponent implements OnInit { // @ts-ignore this.barChartData[0].data = referenceRanges this.barChartData[1].data = currentValues.map(v => [v, v]) + // this.barChartData[1].data = currentValues + + let suggestedMax = Math.max(...currentValues) * 1.1; + this.barChartOptions.scales['x']['suggestedMax'] = suggestedMax + + console.log(this.observationTitle, this.barChartData[0].data, this.barChartData[1].data) + if(currentValues.length > 1){ this.chartHeight = 30 * currentValues.length diff --git a/frontend/src/app/components/report-medical-history-condition/report-medical-history-condition.component.html b/frontend/src/app/components/report-medical-history-condition/report-medical-history-condition.component.html index 6c200b0a..2c0656be 100644 --- a/frontend/src/app/components/report-medical-history-condition/report-medical-history-condition.component.html +++ b/frontend/src/app/components/report-medical-history-condition/report-medical-history-condition.component.html @@ -91,9 +91,6 @@ - - - diff --git a/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.html b/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.html new file mode 100644 index 00000000..b7526173 --- /dev/null +++ b/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.html @@ -0,0 +1,157 @@ + +
+ {{displayModel.period_start | amDateFormat:'YYYY' }} + {{displayModel.period_start | amDateFormat:'MMM DD' }} +
+ + +
+   +
+ + +
+
+ + + {{displayModel.sort_title}} + + + + + | Primary + +
+ + + {{practitioner.sort_title}} + + + + + + + {{organization.sort_title}} + + + + + + + {{location.sort_title}} + + + + + +
+
+
+ Medications: +
    +
  • + {{medication.display }} + + + + +
  • +
  • + {{medication.title }} + + + + +
  • +
+
+ + +
+ Procedures: +
    +
  • + {{procedure.display}} + + + + +
  • +
+
+ +
+ Immunizations: +
    +
  • + {{immunization.sort_title}} + + + + +
  • +
+
+ +
+ Device: +
    +
  • + {{device.model}} +
  • +
+
+
+ +
+
+
+
+ + + + +
+
+
+
+
+ diff --git a/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.scss b/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.scss new file mode 100644 index 00000000..861b2327 --- /dev/null +++ b/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.scss @@ -0,0 +1,205 @@ +.timeline-time { + position: absolute; + left: 0; + width: 18%; + text-align: right; + top: 30px +} + +.timeline-time .date, +.timeline-time .time { + display: block; + font-weight: 600 +} + +.timeline-time .date { + line-height: 16px; + font-size: 12px +} + +.timeline-time .time { + line-height: 24px; + font-size: 20px; + color: #242a30 +} + +.timeline-icon { + left: 15%; + position: absolute; + width: 10%; + text-align: center; + top: 40px +} + +.timeline-icon a { + text-decoration: none; + width: 20px; + height: 20px; + display: inline-block; + border-radius: 20px; + background: #d9e0e7; + line-height: 10px; + color: #fff; + font-size: 14px; + border: 5px solid #2d353c; + transition: border-color .2s linear +} + +.timeline-body { + margin-left: 23%; + background: #fff; + position: relative; + padding: 20px 25px; + border-radius: 6px +} + +.timeline-body:before { + content: ''; + display: block; + position: absolute; + border: 10px solid transparent; + border-right-color: #fff; + left: -20px; + top: 20px +} + +.timeline-body>div+div { + margin-top: 15px +} + +.timeline-body>div+div:last-child { + margin-bottom: -20px; + padding-bottom: 20px; + border-radius: 0 0 6px 6px +} + +.timeline-header { + padding-bottom: 10px; + border-bottom: 1px solid #e2e7eb; + line-height: 30px +} + +.timeline-header .userimage { + float: left; + width: 34px; + height: 34px; + border-radius: 40px; + overflow: hidden; + margin: -2px 10px -2px 0 +} + +.timeline-header .username { + font-size: 16px; + font-weight: 600 +} + +.timeline-header .username, +.timeline-header .username a { + color: #2d353c +} + +.timeline-body img { + max-width: 100%; + display: block +} + +.timeline-content { + letter-spacing: .25px; + line-height: 18px; + font-size: 13px; + width: 100%; +} + +.timeline-content:after, +.timeline-content:before { + content: ''; + display: table; + clear: both +} + +.timeline-title { + margin-top: 0 +} + +.timeline-footer { + background: #fff; + border-top: 1px solid #e2e7ec; + padding-top: 15px +} + +.timeline-footer a:not(.btn) { + color: #575d63 +} + +.timeline-footer a:not(.btn):focus, +.timeline-footer a:not(.btn):hover { + color: #2d353c +} + +.timeline-likes { + color: #6d767f; + font-weight: 600; + font-size: 12px +} + +.timeline-likes .stats-right { + float: right +} + +.timeline-likes .stats-total { + display: inline-block; + line-height: 20px +} + +.timeline-likes .stats-icon { + float: left; + margin-right: 5px; + font-size: 9px +} + +.timeline-likes .stats-icon+.stats-icon { + margin-left: -2px +} + +.timeline-likes .stats-text { + line-height: 20px +} + +.timeline-likes .stats-text+.stats-text { + margin-left: 15px +} + +.timeline-comment-box { + background: #f2f3f4; + margin-left: -25px; + margin-right: -25px; + padding: 20px 25px +} + +.timeline-comment-box .user { + float: left; + width: 34px; + height: 34px; + overflow: hidden; + border-radius: 30px +} + +.timeline-comment-box .user img { + max-width: 100%; + max-height: 100% +} + +.timeline-comment-box .user+.input { + margin-left: 44px +} + +.lead { + margin-bottom: 20px; + font-size: 21px; + font-weight: 300; + line-height: 1.4; +} + +.text-danger, .text-red { + color: #ff5b57!important; +} diff --git a/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.spec.ts b/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.spec.ts new file mode 100644 index 00000000..26935c04 --- /dev/null +++ b/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ReportMedicalHistoryTimelinePanelComponent } from './report-medical-history-timeline-panel.component'; + +describe('ReportMedicalHistoryTimelinePanelComponent', () => { + let component: ReportMedicalHistoryTimelinePanelComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ReportMedicalHistoryTimelinePanelComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ReportMedicalHistoryTimelinePanelComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.ts b/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.ts new file mode 100644 index 00000000..83881c4a --- /dev/null +++ b/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.ts @@ -0,0 +1,38 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {ResourceFhir} from '../../models/fasten/resource_fhir'; +import {EncounterModel} from '../../../lib/models/resources/encounter-model'; +import {RecResourceRelatedDisplayModel} from '../../../lib/utils/resource_related_display_model'; +import {LocationModel} from '../../../lib/models/resources/location-model'; +import {PractitionerModel} from '../../../lib/models/resources/practitioner-model'; +import {OrganizationModel} from '../../../lib/models/resources/organization-model'; +import {fhirModelFactory} from '../../../lib/models/factory'; +import {ResourceType} from '../../../lib/models/constants'; +import {DiagnosticReportModel} from '../../../lib/models/resources/diagnostic-report-model'; +import {FastenDisplayModel} from '../../../lib/models/fasten/fasten-display-model'; + +@Component({ + selector: 'app-report-medical-history-timeline-panel', + templateUrl: './report-medical-history-timeline-panel.component.html', + styleUrls: ['./report-medical-history-timeline-panel.component.scss'] +}) +export class ReportMedicalHistoryTimelinePanelComponent implements OnInit { + @Input() resourceFhir: ResourceFhir + displayModel: EncounterModel + + constructor() { } + + ngOnInit(): void { + + let parsed = RecResourceRelatedDisplayModel(this.resourceFhir) + this.displayModel = parsed.displayModel as EncounterModel + } + + diagnosticReportLink(diagnosticReportRaw: FastenDisplayModel): string { + console.log('diagnosticReportRaw', diagnosticReportRaw) + let diagnosticReport = diagnosticReportRaw as DiagnosticReportModel + return diagnosticReport?.is_category_lab_report ? + '/labs/report/'+ diagnosticReport?.source_id + '/' + diagnosticReport?.source_resource_type + '/' + diagnosticReport?.source_resource_id : + '/explore/'+ diagnosticReport?.source_id + '/resource/' + diagnosticReport?.source_resource_id + '/' + } + +} diff --git a/frontend/src/app/components/shared.module.ts b/frontend/src/app/components/shared.module.ts index 1a7983ba..f3e05b9d 100644 --- a/frontend/src/app/components/shared.module.ts +++ b/frontend/src/app/components/shared.module.ts @@ -82,6 +82,10 @@ import {NgbCollapseModule, NgbModule, NgbDropdownModule, NgbAccordionModule} fro import {PipesModule} from '../pipes/pipes.module'; import {ResourceListOutletDirective} from './resource-list/resource-list-outlet.directive'; import {DirectivesModule} from '../directives/directives.module'; +import { ReportMedicalHistoryTimelinePanelComponent } from './report-medical-history-timeline-panel/report-medical-history-timeline-panel.component'; +import { OrganizationComponent } from './fhir/resources/organization/organization.component'; +import { LocationComponent } from './fhir/resources/location/location.component'; +import { ObservationComponent } from './fhir/resources/observation/observation.component'; @NgModule({ imports: [ @@ -102,27 +106,30 @@ import {DirectivesModule} from '../directives/directives.module'; DirectivesModule, //standalone components - LoadingSpinnerComponent, - GlossaryLookupComponent, - BadgeComponent, - TableComponent, - CodingComponent, AllergyIntoleranceComponent, - MedicationComponent, - MedicationRequestComponent, - PractitionerComponent, - ProcedureComponent, - ImmunizationComponent, - BinaryTextComponent, - HtmlComponent, - ImgComponent, - PdfComponent, - MarkdownComponent, - DicomComponent, + BadgeComponent, BinaryComponent, + BinaryTextComponent, + CodableConceptComponent, + CodingComponent, + DicomComponent, + GlossaryLookupComponent, GridstackComponent, GridstackItemComponent, - CodableConceptComponent + HtmlComponent, + ImgComponent, + ImmunizationComponent, + LoadingSpinnerComponent, + LocationComponent, + MarkdownComponent, + MedicationComponent, + MedicationRequestComponent, + OrganizationComponent, + ObservationComponent, + PdfComponent, + PractitionerComponent, + ProcedureComponent, + TableComponent, ], declarations: [ @@ -165,6 +172,7 @@ import {DirectivesModule} from '../directives/directives.module'; ReportMedicalHistoryEditorComponent, ReportMedicalHistoryConditionComponent, ReportLabsObservationComponent, + ReportMedicalHistoryTimelinePanelComponent, FhirResourceComponent, FhirResourceOutletDirective, @@ -235,23 +243,28 @@ import {DirectivesModule} from '../directives/directives.module'; UtilitiesSidebarComponent, MedicalSourcesCardComponent, MedicalSourcesConnectedComponent, - //standalone components - BadgeComponent, - TableComponent, - CodingComponent, - LoadingSpinnerComponent, - GlossaryLookupComponent, + ReportMedicalHistoryTimelinePanelComponent, + + //standalone components AllergyIntoleranceComponent, - MedicationComponent, - MedicationRequestComponent, - PractitionerComponent, - ProcedureComponent, - ImmunizationComponent, + BadgeComponent, BinaryComponent, + CodableConceptComponent, + CodingComponent, + GlossaryLookupComponent, GridstackComponent, GridstackItemComponent, + ImmunizationComponent, + LoadingSpinnerComponent, + LocationComponent, MedicalSourcesCategoryLookupPipe, - CodableConceptComponent, + MedicationComponent, + MedicationRequestComponent, + OrganizationComponent, + ObservationComponent, + PractitionerComponent, + ProcedureComponent, + TableComponent, ] }) diff --git a/frontend/src/app/models/fasten/resource-graph-response.ts b/frontend/src/app/models/fasten/resource-graph-response.ts index a48cad10..f08f0285 100644 --- a/frontend/src/app/models/fasten/resource-graph-response.ts +++ b/frontend/src/app/models/fasten/resource-graph-response.ts @@ -2,11 +2,4 @@ import {ResourceFhir} from './resource_fhir'; export class ResourceGraphResponse { results: {[resource_type: string]: ResourceFhir[]} - metadata: ResourceGraphMetadata -} - -export class ResourceGraphMetadata { - total_elements: number - page_size: number - page: number } diff --git a/frontend/src/app/pages/medical-history/medical-history.component.html b/frontend/src/app/pages/medical-history/medical-history.component.html index bb8d98b6..3e70171c 100644 --- a/frontend/src/app/pages/medical-history/medical-history.component.html +++ b/frontend/src/app/pages/medical-history/medical-history.component.html @@ -5,7 +5,7 @@ - + @@ -16,7 +16,7 @@ Warning! Fasten has detected medical Encounters that are not associated with a Condition. They are grouped under the "Unassigned" section below.
- You can re-organize your conditions & encounters by using the report editor + You can re-organize your conditions & encounters by using the report editor @@ -32,17 +32,23 @@ - - +
    +
  • + +
  • +
+ +
+ [collectionSize]="allEncounterGroups.length" + [(page)]="currentPage" + [pageSize]="pageSize" + (pageChange)="pageChange()" + > +
diff --git a/frontend/src/app/pages/medical-history/medical-history.component.scss b/frontend/src/app/pages/medical-history/medical-history.component.scss index e69de29b..3375cbdc 100644 --- a/frontend/src/app/pages/medical-history/medical-history.component.scss +++ b/frontend/src/app/pages/medical-history/medical-history.component.scss @@ -0,0 +1,23 @@ +.timeline { + list-style-type: none; + margin: 0; + padding: 0; + position: relative +} + +.timeline:before { + content: ''; + position: absolute; + top: 5px; + bottom: 5px; + width: 5px; + background: #2d353c; + left: 20%; + margin-left: -2.5px +} + +.timeline>li { + position: relative; + min-height: 50px; + padding: 20px 0 +} diff --git a/frontend/src/app/pages/medical-history/medical-history.component.ts b/frontend/src/app/pages/medical-history/medical-history.component.ts index f4999bd8..6ef6fa96 100644 --- a/frontend/src/app/pages/medical-history/medical-history.component.ts +++ b/frontend/src/app/pages/medical-history/medical-history.component.ts @@ -4,7 +4,7 @@ import {ResourceFhir} from '../../models/fasten/resource_fhir'; import { ModalDismissReasons, NgbModal } from '@ng-bootstrap/ng-bootstrap'; import {ReportMedicalHistoryEditorComponent} from '../../components/report-medical-history-editor/report-medical-history-editor.component'; import {forkJoin} from 'rxjs'; -import {ResourceGraphMetadata, ResourceGraphResponse} from '../../models/fasten/resource-graph-response'; +import {ResourceGraphResponse} from '../../models/fasten/resource-graph-response'; // import {ReportEditorRelatedComponent} from '../../components/report-editor-related/report-editor-related.component'; @Component({ @@ -15,18 +15,19 @@ import {ResourceGraphMetadata, ResourceGraphResponse} from '../../models/fasten/ export class MedicalHistoryComponent implements OnInit { loading: boolean = false + currentPage: number = 1 //1-based index due to the way the pagination component works + pageSize: number = 10 + allEncounterGroups: string[] = [] + closeResult = ''; - conditions: ResourceFhir[] = [] - explanationOfBenefits: ResourceFhir[] = [] + // conditions: ResourceFhir[] = [] + // explanationOfBenefits: ResourceFhir[] = [] + // + // unassigned_encounters: ResourceFhir[] = [] + // resourceLookup: {[name: string]: ResourceFhir} = {} - unassigned_encounters: ResourceFhir[] = [] - resourceLookup: {[name: string]: ResourceFhir} = {} + encounters: ResourceFhir[] = [] - resourceGraphMetadata: ResourceGraphMetadata = { - total_elements: 0, - page_size: 0, - page: 1 - } constructor( private fastenApi: FastenApiService, @@ -36,78 +37,110 @@ export class MedicalHistoryComponent implements OnInit { ngOnInit(): void { //load the first page + this.loading = true + this.pageChange(1) } pageChange(page: number){ this.loading = true - this.fastenApi.getResourceGraph(null, page).subscribe((response: ResourceGraphResponse) => { - this.loading = false - this.resourceGraphMetadata = response.metadata - //page counter is 1 indexed but the backend is 0 indexed - this.resourceGraphMetadata.page = this.resourceGraphMetadata.page + 1 - this.conditions = [].concat(response.results["Condition"] || [], response.results["Composition"] || []) - this.unassigned_encounters = response.results["Encounter"] || [] - this.explanationOfBenefits = response.results["ExplanationOfBenefit"] || [] + this.fastenApi.getResources('Encounter').subscribe( + (response: ResourceFhir[]) => { - //populate a lookup table with all resources - for(let condition of this.conditions){ - this.recPopulateResourceLookup(condition) + let selectedResourceIds = response.map((resource: ResourceFhir): Partial => { + return { + source_id: resource.source_id, + source_resource_type: resource.source_resource_type, + source_resource_id: resource.source_resource_id, + } + }) + + this.fastenApi.getResourceGraph(null, selectedResourceIds).subscribe((graphResponse: ResourceGraphResponse) => { + this.loading = false + + console.log("FLATTENED RESOURCES RESPONSE", graphResponse) + this.encounters = graphResponse.results["Encounter"] || [] + + }, + error => { + this.loading = false + }) + + + }, + error => { + this.loading = false } + ) - - if(this.unassigned_encounters.length > 0){ - console.log("Found mapping:", this.resourceLookup) - console.log("Found unassigned encounters:", this.unassigned_encounters.length, this.unassigned_encounters) - this.conditions.push({ - fhir_version: '', - resource_raw: { - resourceType: "Condition", - code:{ - text: "UNASSIGNED", - } - }, - source_id: 'UNASSIGNED', - source_resource_id: 'UNASSIGNED', - source_resource_type: 'Condition', - related_resources: this.unassigned_encounters - } as any) - } - - - }, error => { - this.loading = false - }) + // this.fastenApi.getResourceGraph(null, page).subscribe((response: ResourceGraphResponse) => { + // this.loading = false + // this.resourceGraphMetadata = response.metadata + // //page counter is 1 indexed but the backend is 0 indexed + // this.resourceGraphMetadata.page = this.resourceGraphMetadata.page + 1 + // + // this.conditions = [].concat(response.results["Condition"] || [], response.results["Composition"] || []) + // this.unassigned_encounters = response.results["Encounter"] || [] + // this.explanationOfBenefits = response.results["ExplanationOfBenefit"] || [] + // + // //populate a lookup table with all resources + // for(let condition of this.conditions){ + // this.recPopulateResourceLookup(condition) + // } + // + // + // if(this.unassigned_encounters.length > 0){ + // console.log("Found mapping:", this.resourceLookup) + // console.log("Found unassigned encounters:", this.unassigned_encounters.length, this.unassigned_encounters) + // this.conditions.push({ + // fhir_version: '', + // resource_raw: { + // resourceType: "Condition", + // code:{ + // text: "UNASSIGNED", + // } + // }, + // source_id: 'UNASSIGNED', + // source_resource_id: 'UNASSIGNED', + // source_resource_type: 'Condition', + // related_resources: this.unassigned_encounters + // } as any) + // } + // + // + // }, error => { + // this.loading = false + // }) } - - openEditorRelated(): void { - const modalRef = this.modalService.open(ReportMedicalHistoryEditorComponent, { - size: 'xl', - }); - modalRef.componentInstance.conditions = this.conditions; - } + // + // openEditorRelated(): void { + // const modalRef = this.modalService.open(ReportMedicalHistoryEditorComponent, { + // size: 'xl', + // }); + // modalRef.componentInstance.conditions = this.conditions; + // } - recPopulateResourceLookup(resourceFhir: ResourceFhir) { - if(!resourceFhir){ - return - } - this.resourceLookup[`${resourceFhir.source_id}/${resourceFhir.source_resource_type}/${resourceFhir.source_resource_id}`] = resourceFhir - - if(!resourceFhir.related_resources){ - return - } else { - - for(let relatedResourceFhir of resourceFhir.related_resources){ - this.recPopulateResourceLookup(relatedResourceFhir) - } - - return - } - } + // recPopulateResourceLookup(resourceFhir: ResourceFhir) { + // if(!resourceFhir){ + // return + // } + // this.resourceLookup[`${resourceFhir.source_id}/${resourceFhir.source_resource_type}/${resourceFhir.source_resource_id}`] = resourceFhir + // + // if(!resourceFhir.related_resources){ + // return + // } else { + // + // for(let relatedResourceFhir of resourceFhir.related_resources){ + // this.recPopulateResourceLookup(relatedResourceFhir) + // } + // + // return + // } + // } diff --git a/frontend/src/app/services/fasten-api.service.ts b/frontend/src/app/services/fasten-api.service.ts index 09e73212..2867358a 100644 --- a/frontend/src/app/services/fasten-api.service.ts +++ b/frontend/src/app/services/fasten-api.service.ts @@ -190,17 +190,12 @@ export class FastenApiService { return this._httpClient.post(`${GetEndpointAbsolutePath(globalThis.location, environment.fasten_api_endpoint_base)}/secure/query`, query) } - getResourceGraph(graphType?: string, page?:number): Observable { + getResourceGraph(graphType?: string, selectedResourceIds?: Partial[]): Observable { if(!graphType){ graphType = "MedicalHistory" } - let queryParams = {} - if(page){ - //the backend is 0 indexed, but the frontend is 1 indexed - queryParams["page"] = page - 1 - } - return this._httpClient.get(`${GetEndpointAbsolutePath(globalThis.location, environment.fasten_api_endpoint_base)}/secure/resource/graph/${graphType}`, {params: queryParams}) + return this._httpClient.post(`${GetEndpointAbsolutePath(globalThis.location, environment.fasten_api_endpoint_base)}/secure/resource/graph/${graphType}`, {resource_ids: selectedResourceIds}) .pipe( map((response: ResponseWrapper) => { console.log("RESPONSE", response) @@ -253,12 +248,11 @@ export class FastenApiService { let resourceType = "Binary" let resourceId = "" let binaryUrl = attachmentModel.url - //strip out the urn prefix (if this is an embedded id, eg. urn:uuid:2a35e080-c5f7-4dde-b0cf-8210505708f1) if (binaryUrl.startsWith(urnPrefix)) { // PREFIX is exactly at the beginning resourceId = binaryUrl.slice(urnPrefix.length); - } else if(binaryUrl.startsWith("http://") || binaryUrl.startsWith("https://")){ + } else if(binaryUrl.startsWith("http://") || binaryUrl.startsWith("https://") || binaryUrl.startsWith("Binary/")){ //this is an absolute URL (which could be a FHIR url with Binary/xxx-xxx-xxx-xxx or a direct link to a file) let urlParts = binaryUrl.split("Binary/"); if(urlParts.length > 1){ @@ -269,7 +263,6 @@ export class FastenApiService { resourceId = btoa(binaryUrl) } } - return this.getResourceBySourceId(sourceId, resourceId).pipe( map((resourceFhir: ResourceFhir) => { return new BinaryModel(resourceFhir.resource_raw) diff --git a/frontend/src/lib/models/resources/diagnostic-report-model.ts b/frontend/src/lib/models/resources/diagnostic-report-model.ts index 1dc51a2b..0c81e919 100644 --- a/frontend/src/lib/models/resources/diagnostic-report-model.ts +++ b/frontend/src/lib/models/resources/diagnostic-report-model.ts @@ -13,7 +13,7 @@ export class DiagnosticReportModel extends FastenDisplayModel { title: string | undefined status: string | undefined effective_datetime: string | undefined - category_coding: CodingModel[] | undefined + category_coding: CodableConceptModel[] | undefined code_coding: CodingModel[] | undefined has_category_coding: boolean | undefined has_performer: boolean | undefined @@ -21,6 +21,8 @@ export class DiagnosticReportModel extends FastenDisplayModel { performer: ReferenceModel | undefined issued: string | undefined presented_form: AttachmentModel[] | undefined + is_category_lab_report: boolean = false + constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) { super(fastenOptions) @@ -36,12 +38,16 @@ export class DiagnosticReportModel extends FastenDisplayModel { _.get(fhirResource, 'code.coding.0.display', null); this.status = _.get(fhirResource, 'status', ''); this.effective_datetime = _.get(fhirResource, 'effectiveDateTime'); - this.category_coding = _.get(fhirResource, 'category.coding'); + this.category_coding = _.get(fhirResource, 'category'); this.code_coding = _.get(fhirResource, 'code.coding'); this.has_category_coding = Array.isArray(this.category_coding); this.conclusion = _.get(fhirResource, 'conclusion'); this.issued = _.get(fhirResource, 'issued'); - + this.is_category_lab_report = _.some(fhirResource.category, function(codableConceptModel: CodableConceptModel){ + return _.some(codableConceptModel.coding, function(codingModel: CodingModel){ + return codingModel.code === 'LAB' && codingModel.system === 'http://terminology.hl7.org/CodeSystem/v2-0074'; + }) + }); }; dstu2DTO(fhirResource:any){ diff --git a/frontend/src/lib/models/resources/encounter-model.ts b/frontend/src/lib/models/resources/encounter-model.ts index 33bb0a15..9201ea9a 100644 --- a/frontend/src/lib/models/resources/encounter-model.ts +++ b/frontend/src/lib/models/resources/encounter-model.ts @@ -23,6 +23,8 @@ export class EncounterModel extends FastenDisplayModel { periodStart?:string }[] | undefined + reasonCode: CodableConceptModel[] | undefined + constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) { super(fastenOptions) this.source_resource_type = ResourceType.Encounter @@ -39,6 +41,7 @@ export class EncounterModel extends FastenDisplayModel { ); this.encounter_type = _.get(fhirResource, 'type'); this.has_participant = _.has(fhirResource, 'participant'); + this.reasonCode = _.get(fhirResource, 'reasonCode'); }; dstu2DTO(fhirResource:any){ diff --git a/frontend/src/lib/models/resources/observation-model.ts b/frontend/src/lib/models/resources/observation-model.ts index 105015d8..baa8d4ab 100644 --- a/frontend/src/lib/models/resources/observation-model.ts +++ b/frontend/src/lib/models/resources/observation-model.ts @@ -12,14 +12,22 @@ export class ObservationModel extends FastenDisplayModel { effective_date: string code_coding_display: string code_text: string - value_quantity_value: string + value_quantity_value: number | string value_quantity_unit: string status: string value_codeable_concept_text: string value_codeable_concept_coding_display: string value_codeable_concept_coding: string - value_quantity_value_number: string - subject: string + value_quantity_value_number: number + subject: ReferenceModel | undefined + reference_range: { + low: { + value: number + } + high: { + value: number + } + }[] | undefined constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) { super(fastenOptions) @@ -46,8 +54,7 @@ export class ObservationModel extends FastenDisplayModel { [], ); - this.value_quantity_value_number = this.value_quantity_value; - + this.reference_range = _.get(fhirResource, 'referenceRange', []); this.subject = _.get(fhirResource, 'subject'); } } diff --git a/frontend/src/lib/models/resources/organization-model.ts b/frontend/src/lib/models/resources/organization-model.ts index 2ba3233c..15edd369 100644 --- a/frontend/src/lib/models/resources/organization-model.ts +++ b/frontend/src/lib/models/resources/organization-model.ts @@ -5,13 +5,14 @@ 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'; +import {AddressModel} from '../datatypes/address-model'; export class OrganizationModel extends FastenDisplayModel { - identifier: string|undefined + identifier: CodingModel[]|undefined name: string|undefined - addresses: string|undefined - telecom: string|undefined + addresses: AddressModel[]|undefined + telecom: { system?: string, value?: string, use?: string }[]|undefined type_codings: any[]|undefined constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {