diff --git a/backend/pkg/database/interface.go b/backend/pkg/database/interface.go
index a7f8d830..53c56ee3 100644
--- a/backend/pkg/database/interface.go
+++ b/backend/pkg/database/interface.go
@@ -22,5 +22,6 @@ type DatabaseRepository interface {
CreateSource(context.Context, *models.Source) error
GetSource(context.Context, string) (*models.Source, error)
+ GetSourceSummary(context.Context, string) (*models.SourceSummary, error)
GetSources(context.Context) ([]models.Source, error)
}
diff --git a/backend/pkg/database/sqlite_repository.go b/backend/pkg/database/sqlite_repository.go
index f089ac12..85e4e8fd 100644
--- a/backend/pkg/database/sqlite_repository.go
+++ b/backend/pkg/database/sqlite_repository.go
@@ -231,6 +231,40 @@ func (sr *sqliteRepository) GetSource(ctx context.Context, sourceId string) (*mo
return &sourceCred, results.Error
}
+func (sr *sqliteRepository) GetSourceSummary(ctx context.Context, sourceId string) (*models.SourceSummary, error) {
+ sourceUUID, err := uuid.Parse(sourceId)
+ if err != nil {
+ return nil, err
+ }
+
+ sourceSummary := &models.SourceSummary{}
+
+ source, err := sr.GetSource(ctx, sourceId)
+ if err != nil {
+ return nil, err
+ }
+ sourceSummary.Source = source
+
+ //group by resource type and return counts
+ // SELECT source_resource_type as resource_type, COUNT(*) as count FROM resource_fhirs WHERE source_id = "53c1e930-63af-46c9-b760-8e83cbc1abd9" GROUP BY source_resource_type;
+
+ var results []map[string]interface{}
+
+ sr.gormClient.WithContext(ctx).
+ Model(models.ResourceFhir{}).
+ Select("source_id, source_resource_type as resource_type, count(*) as count").
+ Group("source_resource_type").
+ Where(models.OriginBase{
+ UserID: sr.GetCurrentUser(ctx).ID,
+ SourceID: sourceUUID,
+ }).
+ Scan(&results)
+
+ sourceSummary.ResourceTypeCounts = results
+
+ return sourceSummary, nil
+}
+
func (sr *sqliteRepository) GetSources(ctx context.Context) ([]models.Source, error) {
var sourceCreds []models.Source
diff --git a/backend/pkg/models/source_summary.go b/backend/pkg/models/source_summary.go
new file mode 100644
index 00000000..dd9976d0
--- /dev/null
+++ b/backend/pkg/models/source_summary.go
@@ -0,0 +1,6 @@
+package models
+
+type SourceSummary struct {
+ Source *Source `json:"source,omitempty"`
+ ResourceTypeCounts []map[string]interface{} `json:"resource_type_counts,omitempty"`
+}
diff --git a/backend/pkg/web/handler/source.go b/backend/pkg/web/handler/source.go
index 2c037dfb..35e00b35 100644
--- a/backend/pkg/web/handler/source.go
+++ b/backend/pkg/web/handler/source.go
@@ -118,6 +118,19 @@ func GetSource(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"success": true, "data": sourceCred})
}
+func GetSourceSummary(c *gin.Context) {
+ logger := c.MustGet("LOGGER").(*logrus.Entry)
+ databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
+
+ sourceSummary, err := databaseRepo.GetSourceSummary(c, c.Param("sourceId"))
+ if err != nil {
+ logger.Errorln("An error occurred while retrieving source summary", err)
+ c.JSON(http.StatusInternalServerError, gin.H{"success": false})
+ return
+ }
+ c.JSON(http.StatusOK, gin.H{"success": true, "data": sourceSummary})
+}
+
func ListSource(c *gin.Context) {
logger := c.MustGet("LOGGER").(*logrus.Entry)
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
diff --git a/backend/pkg/web/server.go b/backend/pkg/web/server.go
index 3cc12006..18f60667 100644
--- a/backend/pkg/web/server.go
+++ b/backend/pkg/web/server.go
@@ -49,6 +49,7 @@ func (ae *AppEngine) Setup(logger *logrus.Entry) *gin.Engine {
secure.POST("/source/manual", handler.CreateManualSource)
secure.GET("/source", handler.ListSource)
secure.GET("/source/:sourceId", handler.GetSource)
+ secure.GET("/source/:sourceId/summary", handler.GetSourceSummary)
//in debug mode, this endpoint lets us request data directly from the source api
secure.GET("/source/raw/:sourceType/*path", handler.RawRequestSource)
diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts
index 5e2896cb..575e3a6e 100644
--- a/frontend/src/app/app.module.ts
+++ b/frontend/src/app/app.module.ts
@@ -23,6 +23,8 @@ import { CanActivateAuthGuard } from './services/can-activate.auth-guard';
import {FastenApiService} from './services/fasten-api.service';
import {Router} from '@angular/router';
import { SourceDetailComponent } from './pages/source-detail/source-detail.component';
+import { ResourceListOutletDirective } from './directive/resource-list-outlet.directive';
+import {ResourceListComponent} from './components/resource-list/resource-list.component';
@NgModule({
declarations: [
AppComponent,
diff --git a/frontend/src/app/components/list-care-plan/list-care-plan.component.html b/frontend/src/app/components/list-care-plan/list-care-plan.component.html
deleted file mode 100644
index 62e422a9..00000000
--- a/frontend/src/app/components/list-care-plan/list-care-plan.component.html
+++ /dev/null
@@ -1,31 +0,0 @@
-
A clinical condition, problem, diagnosis, or other event, situation, issue, or clinical concept that has risen to a level of concern.
-
-
-
-
- Category |
- Reason |
- Period |
- Status |
-
-
-
-
-
- {{careplan.category}}
- |
-
-
- {{reason[0]}}
- -
-
- {{reason[1] || "no data"}}
-
-
- |
- {{careplan.period?.start}} - {{careplan.period?.end}} |
- {{careplan.status}} |
-
-
-
-
diff --git a/frontend/src/app/components/list-care-plan/list-care-plan.component.ts b/frontend/src/app/components/list-care-plan/list-care-plan.component.ts
deleted file mode 100644
index ed84b343..00000000
--- a/frontend/src/app/components/list-care-plan/list-care-plan.component.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import {Component, Input, OnInit} from '@angular/core';
-import {ResourceFhir} from '../../models/fasten/resource_fhir';
-import {CarePlan} from '../../models/display/care-plan';
-
-@Component({
- selector: 'app-list-care-plan',
- templateUrl: './list-care-plan.component.html',
- styleUrls: ['./list-care-plan.component.scss']
-})
-export class ListCarePlanComponent implements OnInit {
-
- @Input() resourceList: ResourceFhir[] = []
- careplanList: CarePlan[] = []
-
- constructor() { }
-
- ngOnInit(): void {
- let _careplanList = this.careplanList
- this.resourceList.forEach((resource) => {
- let careplan = new CarePlan(resource.payload)
- _careplanList.push(careplan)
- })
- }
-
-}
diff --git a/frontend/src/app/components/list-explanation-of-benefit/list-explanation-of-benefit.component.html b/frontend/src/app/components/list-explanation-of-benefit/list-explanation-of-benefit.component.html
deleted file mode 100644
index 5e229a6c..00000000
--- a/frontend/src/app/components/list-explanation-of-benefit/list-explanation-of-benefit.component.html
+++ /dev/null
@@ -1 +0,0 @@
-list-explanation-of-benefit works!
diff --git a/frontend/src/app/components/list-explanation-of-benefit/list-explanation-of-benefit.component.scss b/frontend/src/app/components/list-explanation-of-benefit/list-explanation-of-benefit.component.scss
deleted file mode 100644
index e69de29b..00000000
diff --git a/frontend/src/app/components/list-explanation-of-benefit/list-explanation-of-benefit.component.spec.ts b/frontend/src/app/components/list-explanation-of-benefit/list-explanation-of-benefit.component.spec.ts
deleted file mode 100644
index 673d3447..00000000
--- a/frontend/src/app/components/list-explanation-of-benefit/list-explanation-of-benefit.component.spec.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { ListExplanationOfBenefitComponent } from './list-explanation-of-benefit.component';
-
-describe('ListExplanationOfBenefitComponent', () => {
- let component: ListExplanationOfBenefitComponent;
- let fixture: ComponentFixture;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [ ListExplanationOfBenefitComponent ]
- })
- .compileComponents();
-
- fixture = TestBed.createComponent(ListExplanationOfBenefitComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/frontend/src/app/components/list-explanation-of-benefit/list-explanation-of-benefit.component.ts b/frontend/src/app/components/list-explanation-of-benefit/list-explanation-of-benefit.component.ts
deleted file mode 100644
index c7c83e1d..00000000
--- a/frontend/src/app/components/list-explanation-of-benefit/list-explanation-of-benefit.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
- selector: 'app-list-explanation-of-benefit',
- templateUrl: './list-explanation-of-benefit.component.html',
- styleUrls: ['./list-explanation-of-benefit.component.scss']
-})
-export class ListExplanationOfBenefitComponent implements OnInit {
-
- constructor() { }
-
- ngOnInit(): void {
- }
-
-}
diff --git a/frontend/src/app/components/list-generic-resource/list-adverse-event.component.ts b/frontend/src/app/components/list-generic-resource/list-adverse-event.component.ts
new file mode 100644
index 00000000..f5e8e4f0
--- /dev/null
+++ b/frontend/src/app/components/list-generic-resource/list-adverse-event.component.ts
@@ -0,0 +1,15 @@
+import {Component} from '@angular/core';
+import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component';
+import {attributeXTime} from './utils';
+
+@Component({
+ selector: 'app-list-adverse-event',
+ templateUrl: './list-generic-resource.component.html',
+ styleUrls: ['./list-generic-resource.component.scss']
+})
+export class ListAdverseEventComponent extends ListGenericResourceComponent {
+ columnDefinitions: GenericColumnDefn[] = [
+ { title: 'Event', versions: '*', format: 'code', getter: a => a.event.coding[0] },
+ { title: 'Date', versions: '*', format: 'date', getter: a => a.date }
+ ]
+}
diff --git a/frontend/src/app/components/list-generic-resource/list-allergy-intolerance.component.ts b/frontend/src/app/components/list-generic-resource/list-allergy-intolerance.component.ts
new file mode 100644
index 00000000..557812b2
--- /dev/null
+++ b/frontend/src/app/components/list-generic-resource/list-allergy-intolerance.component.ts
@@ -0,0 +1,17 @@
+import {Component} from '@angular/core';
+import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component';
+import {attributeXTime} from './utils';
+
+@Component({
+ selector: 'app-list-allergy-intolerance',
+ templateUrl: './list-generic-resource.component.html',
+ styleUrls: ['./list-generic-resource.component.scss']
+})
+export class ListAllergyIntoleranceComponent extends ListGenericResourceComponent {
+ columnDefinitions: GenericColumnDefn[] = [
+ { title: 'Allergy', versions: '*', format: 'code', getter: a => a.code.coding[0] },
+ { title: 'Date Recorded', versions: '*', format: 'date', getter: a => a.assertedDate || a.recordedDate },
+ { title: 'Onset', versions: '*', format: 'date', getter: a => a.onsetDateTime },
+ { title: 'Resolution Age', versions: '*', format: 'date', getter: a => a.extension.resolutionAge }
+ ]
+}
diff --git a/frontend/src/app/components/list-generic-resource/list-care-plan.component.ts b/frontend/src/app/components/list-generic-resource/list-care-plan.component.ts
new file mode 100644
index 00000000..c0723ae3
--- /dev/null
+++ b/frontend/src/app/components/list-generic-resource/list-care-plan.component.ts
@@ -0,0 +1,24 @@
+import {Component} from '@angular/core';
+import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component';
+import {attributeXTime} from './utils';
+import {getPath} from '../../fhir/utils';
+
+@Component({
+ selector: 'app-list-care-plan',
+ templateUrl: './list-generic-resource.component.html',
+ styleUrls: ['./list-generic-resource.component.scss']
+})
+export class ListCarePlanComponent extends ListGenericResourceComponent {
+ columnDefinitions: GenericColumnDefn[] = [
+ { title: 'Category', versions: '*', format: 'code', getter: c => c.category[0].coding[0] },
+ { title: 'Reason', versions: '*', getter: c => {
+ return (c.activity || []).map((a, i) => {
+ let reason = getPath(a, "detail.code.coding.0.display") || ""
+ return reason ? [reason, getPath(a, "detail.status") || "no data"] : []
+ })
+ } },
+ { title: 'Period', versions: '*', format: 'period', getter: c => c.period },
+ { title: 'Status', versions: '*', getter: a => a.status },
+
+ ]
+}
diff --git a/frontend/src/app/components/list-generic-resource/list-communication.component.ts b/frontend/src/app/components/list-generic-resource/list-communication.component.ts
new file mode 100644
index 00000000..214051c0
--- /dev/null
+++ b/frontend/src/app/components/list-generic-resource/list-communication.component.ts
@@ -0,0 +1,17 @@
+import {Component} from '@angular/core';
+import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component';
+import {attributeXTime} from './utils';
+
+@Component({
+ selector: 'app-list-communication',
+ templateUrl: './list-generic-resource.component.html',
+ styleUrls: ['./list-generic-resource.component.scss']
+})
+export class ListCommunicationComponent extends ListGenericResourceComponent {
+ columnDefinitions: GenericColumnDefn[] = [
+ { title: 'Reason', versions: '*', format: 'code', getter: c => c.reasonCode[0].coding[0] },
+ { title: 'Sent', versions: '*', format: 'date', getter: c => c.sent },
+ { title: 'Received', versions: '*', format: 'date', getter: c => c.received },
+ { title: 'Status', versions: '*', getter: c => c.status }
+ ]
+}
diff --git a/frontend/src/app/components/list-generic-resource/list-condition.component.ts b/frontend/src/app/components/list-generic-resource/list-condition.component.ts
index 186e32b7..c216e9da 100644
--- a/frontend/src/app/components/list-generic-resource/list-condition.component.ts
+++ b/frontend/src/app/components/list-generic-resource/list-condition.component.ts
@@ -1,5 +1,6 @@
-import {Component} from '@angular/core';
-import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component';
+import {Component, OnInit} from '@angular/core';
+import {GenericColumnDefn, ListGenericResourceComponent, ResourceListComponentInterface} from './list-generic-resource.component';
+import {FORMATTERS} from './utils';
@Component({
selector: 'app-list-condition',
diff --git a/frontend/src/app/components/list-generic-resource/list-coverage.component.ts b/frontend/src/app/components/list-generic-resource/list-coverage.component.ts
new file mode 100644
index 00000000..d05540c9
--- /dev/null
+++ b/frontend/src/app/components/list-generic-resource/list-coverage.component.ts
@@ -0,0 +1,15 @@
+import {Component} from '@angular/core';
+import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component';
+import {attributeXTime} from './utils';
+
+@Component({
+ selector: 'app-list-coverage',
+ templateUrl: './list-generic-resource.component.html',
+ styleUrls: ['./list-generic-resource.component.scss']
+})
+export class ListCoverageComponent extends ListGenericResourceComponent {
+ columnDefinitions: GenericColumnDefn[] = [
+ { title: 'Type', versions: '*', format: 'code', getter: c => c.type.coding[0] },
+ { title: 'Period', versions: '*', format: 'period', getter: c => c.period }
+ ]
+}
diff --git a/frontend/src/app/components/list-generic-resource/list-device-request.component.ts b/frontend/src/app/components/list-generic-resource/list-device-request.component.ts
new file mode 100644
index 00000000..905b12bb
--- /dev/null
+++ b/frontend/src/app/components/list-generic-resource/list-device-request.component.ts
@@ -0,0 +1,18 @@
+import {Component} from '@angular/core';
+import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component';
+import {attributeXTime} from './utils';
+
+@Component({
+ selector: 'app-list-device-request',
+ templateUrl: './list-generic-resource.component.html',
+ styleUrls: ['./list-generic-resource.component.scss']
+})
+export class ListDeviceRequestComponent extends ListGenericResourceComponent {
+ columnDefinitions: GenericColumnDefn[] = [
+ { title: 'Device', versions: '*', format: 'code', getter: d => d.codeCodeableConcept.coding[0] },
+ { title: 'Author Date', versions: '*', format: 'date', getter: d => d.authoredOn },
+ { title: 'Do Not Perform', versions: '*', getter: d => d.modifierExtension.doNotPerform },
+ { title: 'Do Not Perform Reason', versions: '*', format: 'code', getter: s => s.extension.doNotPerformReason.coding[0] }
+
+ ]
+}
diff --git a/frontend/src/app/components/list-generic-resource/list-document-reference.component.ts b/frontend/src/app/components/list-generic-resource/list-document-reference.component.ts
new file mode 100644
index 00000000..e55556a0
--- /dev/null
+++ b/frontend/src/app/components/list-generic-resource/list-document-reference.component.ts
@@ -0,0 +1,16 @@
+import {Component} from '@angular/core';
+import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component';
+import {attributeXTime} from './utils';
+
+@Component({
+ selector: 'app-list-document-reference',
+ templateUrl: './list-generic-resource.component.html',
+ styleUrls: ['./list-generic-resource.component.scss']
+})
+export class ListDocumentReferenceComponent extends ListGenericResourceComponent {
+ columnDefinitions: GenericColumnDefn[] = [
+ { title: 'Date', versions: '*', format: 'date', getter: d => d.date },
+ { title: 'Content', versions: '*', getter: d => atob(d.content[0].attachment.data) }
+
+ ]
+}
diff --git a/frontend/src/app/components/list-generic-resource/list-encounter.component.ts b/frontend/src/app/components/list-generic-resource/list-encounter.component.ts
index 78d1375b..8c9c1acc 100644
--- a/frontend/src/app/components/list-generic-resource/list-encounter.component.ts
+++ b/frontend/src/app/components/list-generic-resource/list-encounter.component.ts
@@ -1,12 +1,12 @@
-import {Component} from '@angular/core';
-import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component';
+import {Component, OnChanges, OnInit} from '@angular/core';
+import {GenericColumnDefn, ListGenericResourceComponent, ResourceListComponentInterface} from './list-generic-resource.component';
@Component({
selector: 'app-list-encounter',
templateUrl: './list-generic-resource.component.html',
styleUrls: ['./list-generic-resource.component.scss']
})
-export class ListEncounterComponent extends ListGenericResourceComponent {
+export class ListEncounterComponent extends ListGenericResourceComponent {
columnDefinitions: GenericColumnDefn[] = [
{ title: 'Encounter', versions: '*', format: 'code', getter: e => e.type[0].coding[0] },
{ title: 'Period', versions: '*', format: 'period', getter: e => e.period },
diff --git a/frontend/src/app/components/list-generic-resource/list-explanation-of-benefit.component.ts b/frontend/src/app/components/list-generic-resource/list-explanation-of-benefit.component.ts
new file mode 100644
index 00000000..26be1b77
--- /dev/null
+++ b/frontend/src/app/components/list-generic-resource/list-explanation-of-benefit.component.ts
@@ -0,0 +1,11 @@
+import {Component, OnChanges, OnInit} from '@angular/core';
+import {GenericColumnDefn, ListGenericResourceComponent, ResourceListComponentInterface} from './list-generic-resource.component';
+
+@Component({
+ selector: 'app-list-explanation-of-benefit',
+ templateUrl: './list-generic-resource.component.html',
+ styleUrls: ['./list-generic-resource.component.scss']
+})
+export class ListExplanationOfBenefitComponent extends ListGenericResourceComponent {
+ columnDefinitions: GenericColumnDefn[] = []
+}
diff --git a/frontend/src/app/components/list-generic-resource/list-generic-resource.component.html b/frontend/src/app/components/list-generic-resource/list-generic-resource.component.html
index 49fd66d6..b0cf9d4e 100644
--- a/frontend/src/app/components/list-generic-resource/list-generic-resource.component.html
+++ b/frontend/src/app/components/list-generic-resource/list-generic-resource.component.html
@@ -1,4 +1,6 @@
+
INSIDE THE GERNERIC COMPONENT
+
{
let column = {name: defn.title, prop: defn.title.replace(/[^A-Z0-9]/ig, "_")}
return column
@@ -44,10 +62,13 @@ export class ListGenericResourceComponent implements OnInit {
let row = {}
this.columnDefinitions.forEach((defn) => {
- let resourceProp = defn.getter(resource.payload)
- let resourceFormatted = defn.format ? FORMATTERS[defn.format](resourceProp) : resourceProp
- row[defn.title.replace(/[^A-Z0-9]/ig, "_")] = resourceFormatted
- //TODO: handle defaultValue
+ try{
+ let resourceProp = defn.getter(resource.payload)
+ let resourceFormatted = defn.format ? FORMATTERS[defn.format](resourceProp) : resourceProp
+ row[defn.title.replace(/[^A-Z0-9]/ig, "_")] = resourceFormatted
+ }catch (e){
+ //ignore
+ }
})
console.log("ROW:", row)
@@ -55,6 +76,7 @@ export class ListGenericResourceComponent implements OnInit {
return row
})
}
+
}
///////////////////////////////////////////////////////////////////////////////////////
diff --git a/frontend/src/app/components/list-generic-resource/list-medication-administration.component.ts b/frontend/src/app/components/list-generic-resource/list-medication-administration.component.ts
new file mode 100644
index 00000000..4d97f1ef
--- /dev/null
+++ b/frontend/src/app/components/list-generic-resource/list-medication-administration.component.ts
@@ -0,0 +1,19 @@
+import {Component} from '@angular/core';
+import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component';
+import {attributeXTime} from './utils';
+
+@Component({
+ selector: 'app-list-medication-administration',
+ templateUrl: './list-generic-resource.component.html',
+ styleUrls: ['./list-generic-resource.component.scss']
+})
+export class ListMedicationAdministrationComponent extends ListGenericResourceComponent {
+ columnDefinitions: GenericColumnDefn[] = [
+ { title: 'Medication', versions: '*', format: 'code', getter: m => m.medicationCodeableConcept.coding[0] },
+ { title: 'Route', versions: '*', format: 'code', getter: m => m.dosage.route.coding[0] },
+ { title: 'Effective', versions: '*', getter: m => attributeXTime(m,'effective')},
+ { title: 'Status', versions: '*', getter: m => m.status},
+ { title: 'Status Reason', versions: '*', format: 'code', getter: m => m.statusReason[0].coding[0] },
+ { title: 'Recorded', versions: '*', format: 'date', getter: m => m.extension.recorded }
+ ]
+}
diff --git a/frontend/src/app/components/list-generic-resource/list-medication-dispense.component.ts b/frontend/src/app/components/list-generic-resource/list-medication-dispense.component.ts
new file mode 100644
index 00000000..625b4991
--- /dev/null
+++ b/frontend/src/app/components/list-generic-resource/list-medication-dispense.component.ts
@@ -0,0 +1,15 @@
+import {Component} from '@angular/core';
+import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component';
+import {attributeXTime} from './utils';
+
+@Component({
+ selector: 'app-list-medication-dispense',
+ templateUrl: './list-generic-resource.component.html',
+ styleUrls: ['./list-generic-resource.component.scss']
+})
+export class ListMedicationDispenseComponent extends ListGenericResourceComponent {
+ columnDefinitions: GenericColumnDefn[] = [
+ { title: 'Medication', versions: '*', format: 'code', getter: m => m.medicationCodeableConcept.coding[0] },
+ { title: 'Handed Over Date', versions: '*', format: 'date', getter: m => m.whenHandedOver}
+ ]
+}
diff --git a/frontend/src/app/components/list-generic-resource/list-nutrition-order.component.ts b/frontend/src/app/components/list-generic-resource/list-nutrition-order.component.ts
new file mode 100644
index 00000000..8e132c61
--- /dev/null
+++ b/frontend/src/app/components/list-generic-resource/list-nutrition-order.component.ts
@@ -0,0 +1,17 @@
+import {Component} from '@angular/core';
+import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component';
+import {attributeXTime} from './utils';
+
+@Component({
+ selector: 'app-list-nutrition-order',
+ templateUrl: './list-generic-resource.component.html',
+ styleUrls: ['./list-generic-resource.component.scss']
+})
+export class ListNutritionOrderComponent extends ListGenericResourceComponent {
+ columnDefinitions: GenericColumnDefn[] = [
+ { title: 'Preference', versions: '*', format: 'code', getter: n => n.foodPreferenceModifier[0].coding[0] },
+ { title: 'Exclusion', versions: '*', format: 'code', getter: n => n.excludeFoodModifier[0].coding[0] },
+ { title: 'Date', versions: '*', format: 'date', getter: n => n.dateTime },
+ { title: 'Status', versions: '*', getter: n => n.status }
+ ]
+}
diff --git a/frontend/src/app/components/list-generic-resource/list-service-request.component.ts b/frontend/src/app/components/list-generic-resource/list-service-request.component.ts
new file mode 100644
index 00000000..bdf15248
--- /dev/null
+++ b/frontend/src/app/components/list-generic-resource/list-service-request.component.ts
@@ -0,0 +1,20 @@
+import {Component} from '@angular/core';
+import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component';
+import {attributeXTime} from './utils';
+
+@Component({
+ selector: 'app-list-service-request',
+ templateUrl: './list-generic-resource.component.html',
+ styleUrls: ['./list-generic-resource.component.scss']
+})
+export class ListServiceRequestComponent extends ListGenericResourceComponent {
+ columnDefinitions: GenericColumnDefn[] = [
+ { title: 'Service', versions: '*', format: 'code', getter: s => s.code.coding[0] },
+ { title: 'Author Date', versions: '*', format: 'date', getter: s => s.authoredOn },
+ { title: 'Status', versions: '*', getter: s => s.status },
+ { title: 'Reason', versions: '*', format: 'code', getter: s => s.reasonCode[0].coding[0] },
+ { title: 'ID', versions: '*', getter: s => s.id },
+ { title: 'Do Not Perform', versions: '*', getter: s => s.doNotPerform },
+ { title: 'Reason Refused', versions: '*', format: 'code', getter: s => s.extension.reasonRefused.coding[0] }
+ ]
+}
diff --git a/frontend/src/app/components/resource-list/resource-list.component.html b/frontend/src/app/components/resource-list/resource-list.component.html
new file mode 100644
index 00000000..8a3b0c18
--- /dev/null
+++ b/frontend/src/app/components/resource-list/resource-list.component.html
@@ -0,0 +1,3 @@
+{{resourceListType}}
+
+
diff --git a/frontend/src/app/components/list-care-plan/list-care-plan.component.scss b/frontend/src/app/components/resource-list/resource-list.component.scss
similarity index 100%
rename from frontend/src/app/components/list-care-plan/list-care-plan.component.scss
rename to frontend/src/app/components/resource-list/resource-list.component.scss
diff --git a/frontend/src/app/components/list-care-plan/list-care-plan.component.spec.ts b/frontend/src/app/components/resource-list/resource-list.component.spec.ts
similarity index 51%
rename from frontend/src/app/components/list-care-plan/list-care-plan.component.spec.ts
rename to frontend/src/app/components/resource-list/resource-list.component.spec.ts
index 4a897cae..a288d0e6 100644
--- a/frontend/src/app/components/list-care-plan/list-care-plan.component.spec.ts
+++ b/frontend/src/app/components/resource-list/resource-list.component.spec.ts
@@ -1,18 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { ListCarePlanComponent } from './list-care-plan.component';
+import { ResourceListComponent } from './resource-list.component';
-describe('ListCarePlanComponent', () => {
- let component: ListCarePlanComponent;
- let fixture: ComponentFixture;
+describe('ResourceListComponent', () => {
+ let component: ResourceListComponent;
+ let fixture: ComponentFixture;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ ListCarePlanComponent ]
+ declarations: [ ResourceListComponent ]
})
.compileComponents();
- fixture = TestBed.createComponent(ListCarePlanComponent);
+ fixture = TestBed.createComponent(ResourceListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/components/resource-list/resource-list.component.ts b/frontend/src/app/components/resource-list/resource-list.component.ts
new file mode 100644
index 00000000..0661336d
--- /dev/null
+++ b/frontend/src/app/components/resource-list/resource-list.component.ts
@@ -0,0 +1,152 @@
+import {ChangeDetectionStrategy, Component, Input, OnChanges, OnInit, SimpleChanges, Type, ViewChild} from '@angular/core';
+import {ResourceListOutletDirective} from '../../directive/resource-list-outlet.directive';
+import {FastenApiService} from '../../services/fasten-api.service';
+import {Source} from '../../models/fasten/source';
+import {Observable, of} from 'rxjs';
+import {ResourceFhir} from '../../models/fasten/resource_fhir';
+import {ListAdverseEventComponent} from '../list-generic-resource/list-adverse-event.component';
+import {ListCommunicationComponent} from '../list-generic-resource/list-communication.component';
+import {ListConditionComponent} from '../list-generic-resource/list-condition.component';
+import {ListCoverageComponent} from '../list-generic-resource/list-coverage.component';
+import {ListDeviceRequestComponent} from '../list-generic-resource/list-device-request.component';
+import {ListDocumentReferenceComponent} from '../list-generic-resource/list-document-reference.component';
+import {ListEncounterComponent} from '../list-generic-resource/list-encounter.component';
+import {ListImmunizationComponent} from '../list-generic-resource/list-immunization.component';
+import {ListMedicationComponent} from '../list-generic-resource/list-medication.component';
+import {ListMedicationAdministrationComponent} from '../list-generic-resource/list-medication-administration.component';
+import {ListMedicationDispenseComponent} from '../list-generic-resource/list-medication-dispense.component';
+import {ListMedicationRequestComponent} from '../list-generic-resource/list-medication-request.component';
+import {ListNutritionOrderComponent} from '../list-generic-resource/list-nutrition-order.component';
+import {ListObservationComponent} from '../list-generic-resource/list-observation.component';
+import {ListProcedureComponent} from '../list-generic-resource/list-procedure.component';
+import {ListServiceRequestComponent} from '../list-generic-resource/list-service-request.component';
+import {map} from 'rxjs/operators';
+import {ResponseWrapper} from '../../models/response-wrapper';
+import {ListGenericResourceComponent, ResourceListComponentInterface} from '../list-generic-resource/list-generic-resource.component';
+import {ListCarePlanComponent} from '../list-generic-resource/list-care-plan.component';
+import {ListAllergyIntoleranceComponent} from '../list-generic-resource/list-allergy-intolerance.component';
+
+@Component({
+ selector: 'source-resource-list',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ templateUrl: './resource-list.component.html',
+ styleUrls: ['./resource-list.component.scss']
+})
+export class ResourceListComponent implements OnInit, OnChanges {
+
+ @Input() source: Source;
+ @Input() resourceListType: string;
+ resourceListCache: { [name:string]: ResourceFhir[] } = {}
+
+
+
+ //location to dynamically load the resource list
+ @ViewChild(ResourceListOutletDirective, {static: true}) resourceListOutlet!: ResourceListOutletDirective;
+
+
+ constructor(private fastenApi: FastenApiService) { }
+
+ ngOnInit(): void {
+ this.loadComponent()
+ }
+ ngOnChanges(changes: SimpleChanges) {
+ this.loadComponent()
+ }
+
+ loadComponent() {
+ //clear the current outlet
+ const viewContainerRef = this.resourceListOutlet.viewContainerRef;
+ viewContainerRef.clear();
+
+ this.getResources().subscribe((resourceList) => {
+ let componentType = this.typeLookup(this.resourceListType)
+ if(componentType != null){
+ console.log("Attempting to create component", this.resourceListType, componentType)
+ const componentRef = viewContainerRef.createComponent(componentType);
+ componentRef.instance.resourceList = resourceList;
+ componentRef.instance.markForCheck()
+
+ }
+ })
+ }
+
+ getResources(): Observable{
+
+ if(!this.resourceListCache[this.resourceListType]){
+ // this resource type list has not been downloaded yet, do so now
+
+ return this.fastenApi.getResources(this.resourceListType, this.source.id)
+ .pipe(map((resourceList: ResourceFhir[]) => {
+ //cache this response so we can skip the request next time
+ this.resourceListCache[this.resourceListType] = resourceList
+ return resourceList
+ }))
+ } else {
+ return of(this.resourceListCache[this.resourceListType])
+ }
+ }
+
+ typeLookup(resourceType: string): Type {
+ switch(resourceType) {
+ case "AllergyIntolerance": {
+ return ListAllergyIntoleranceComponent;
+ }
+ case "AdverseEvent": {
+ return ListAdverseEventComponent;
+ }
+ case "CarePlan": {
+ return ListCarePlanComponent;
+ }
+ case "Communication": {
+ return ListCommunicationComponent;
+ }
+ case "Condition": {
+ return ListConditionComponent;
+ }
+ case "Coverage": {
+ return ListCoverageComponent;
+ }
+ case "DeviceRequest": {
+ return ListDeviceRequestComponent;
+ }
+ case "DocumentReference": {
+ return ListDocumentReferenceComponent;
+ }
+ case "Encounter": {
+ return ListEncounterComponent;
+ }
+ case "Immunization": {
+ return ListImmunizationComponent;
+ }
+ case "Medication": {
+ return ListMedicationComponent;
+ }
+ case "MedicationAdministration": {
+ return ListMedicationAdministrationComponent;
+ }
+ case "MedicationDispense": {
+ return ListMedicationDispenseComponent;
+ }
+ case "MedicationRequest": {
+ return ListMedicationRequestComponent;
+ }
+ case "NutritionOrder": {
+ return ListNutritionOrderComponent;
+ }
+ case "Observation": {
+ return ListObservationComponent;
+ }
+ case "Procedure": {
+ return ListProcedureComponent;
+ }
+ case "ServiceRequest": {
+ return ListServiceRequestComponent;
+ }
+ default: {
+ console.error("UNKNOWN COMPONENT TYPE", resourceType)
+ return null
+
+ }
+ }
+ }
+}
diff --git a/frontend/src/app/components/shared.module.ts b/frontend/src/app/components/shared.module.ts
index 3adb13dc..1d4f7f42 100644
--- a/frontend/src/app/components/shared.module.ts
+++ b/frontend/src/app/components/shared.module.ts
@@ -1,58 +1,93 @@
-import { NgModule } from '@angular/core';
import { ComponentsSidebarComponent } from './components-sidebar/components-sidebar.component';
+import { ListExplanationOfBenefitComponent } from './list-explanation-of-benefit/list-explanation-of-benefit.component';
+import { ListGenericResourceComponent,} from './list-generic-resource/list-generic-resource.component';
+import { ListPatientComponent } from './list-patient/list-patient.component';
+import { NgModule } from '@angular/core';
+import { NgxDatatableModule } from '@swimlane/ngx-datatable';
import { RouterModule } from '@angular/router';
import { UtilitiesSidebarComponent } from './utilities-sidebar/utilities-sidebar.component';
-import { ListPatientComponent } from './list-patient/list-patient.component';
-import { ListExplanationOfBenefitComponent } from './list-explanation-of-benefit/list-explanation-of-benefit.component';
-import { ListCarePlanComponent } from './list-care-plan/list-care-plan.component';
import {BrowserModule} from '@angular/platform-browser';
-import { ListGenericResourceComponent,} from './list-generic-resource/list-generic-resource.component';
+import {ListAdverseEventComponent} from './list-generic-resource/list-adverse-event.component';
import {ListConditionComponent} from './list-generic-resource/list-condition.component'
import {ListEncounterComponent} from './list-generic-resource/list-encounter.component'
+import {ListImmunizationComponent} from './list-generic-resource/list-immunization.component'
+import {ListMedicationAdministrationComponent} from './list-generic-resource/list-medication-administration.component';
import {ListMedicationComponent} from './list-generic-resource/list-medication.component'
+import {ListMedicationDispenseComponent} from './list-generic-resource/list-medication-dispense.component';
+import {ListMedicationRequestComponent} from './list-generic-resource/list-medication-request.component'
+import {ListNutritionOrderComponent} from './list-generic-resource/list-nutrition-order.component';
import {ListObservationComponent} from './list-generic-resource/list-observation.component'
import {ListProcedureComponent} from './list-generic-resource/list-procedure.component'
-import {ListImmunizationComponent} from './list-generic-resource/list-immunization.component'
-import {ListMedicationRequestComponent} from './list-generic-resource/list-medication-request.component'
-
-import { NgxDatatableModule } from '@swimlane/ngx-datatable';
+import {ListCommunicationComponent} from './list-generic-resource/list-communication.component';
+import {ListDeviceRequestComponent} from './list-generic-resource/list-device-request.component';
+import {ListCoverageComponent} from './list-generic-resource/list-coverage.component';
+import {ListServiceRequestComponent} from './list-generic-resource/list-service-request.component';
+import {ListDocumentReferenceComponent} from './list-generic-resource/list-document-reference.component';
+import { ResourceListComponent } from './resource-list/resource-list.component';
+import {ResourceListOutletDirective} from '../directive/resource-list-outlet.directive';
+import {ListCarePlanComponent} from './list-generic-resource/list-care-plan.component';
+import {ListAllergyIntoleranceComponent} from './list-generic-resource/list-allergy-intolerance.component';
@NgModule({
imports: [
RouterModule,
BrowserModule,
- NgxDatatableModule
+ NgxDatatableModule,
],
declarations: [
ComponentsSidebarComponent,
UtilitiesSidebarComponent,
- ListPatientComponent,
- ListObservationComponent,
- ListExplanationOfBenefitComponent,
- ListImmunizationComponent,
- ListEncounterComponent,
+ ListAllergyIntoleranceComponent,
+ ListAdverseEventComponent,
ListCarePlanComponent,
- ListGenericResourceComponent,
- ListMedicationComponent,
- ListProcedureComponent,
+ ListCommunicationComponent,
ListConditionComponent,
- ListMedicationRequestComponent
+ ListEncounterComponent,
+ ListExplanationOfBenefitComponent,
+ ListGenericResourceComponent,
+ ListImmunizationComponent,
+ ListMedicationAdministrationComponent,
+ ListMedicationComponent,
+ ListMedicationDispenseComponent,
+ ListMedicationRequestComponent,
+ ListNutritionOrderComponent,
+ ListObservationComponent,
+ ListPatientComponent,
+ ListDeviceRequestComponent,
+ ListProcedureComponent,
+ ListCoverageComponent,
+ ListServiceRequestComponent,
+ ListDocumentReferenceComponent,
+ ResourceListComponent,
+ ResourceListOutletDirective,
+
+
],
exports: [
- ComponentsSidebarComponent,
- UtilitiesSidebarComponent,
- ListPatientComponent,
- ListExplanationOfBenefitComponent,
- ListImmunizationComponent,
- ListEncounterComponent,
- ListCarePlanComponent,
- ListGenericResourceComponent,
- ListMedicationComponent,
- ListObservationComponent,
- ListProcedureComponent,
- ListConditionComponent,
- ListMedicationRequestComponent
-
+ ComponentsSidebarComponent,
+ ListAllergyIntoleranceComponent,
+ ListAdverseEventComponent,
+ ListCarePlanComponent,
+ ListCommunicationComponent,
+ ListConditionComponent,
+ ListEncounterComponent,
+ ListExplanationOfBenefitComponent,
+ ListGenericResourceComponent,
+ ListImmunizationComponent,
+ ListMedicationAdministrationComponent,
+ ListMedicationComponent,
+ ListMedicationDispenseComponent,
+ ListMedicationRequestComponent,
+ ListNutritionOrderComponent,
+ ListObservationComponent,
+ ListPatientComponent,
+ ListProcedureComponent,
+ ListDeviceRequestComponent,
+ UtilitiesSidebarComponent,
+ ListCoverageComponent,
+ ListServiceRequestComponent,
+ ListDocumentReferenceComponent,
+ ResourceListComponent
]
})
diff --git a/frontend/src/app/directive/resource-list-outlet.directive.spec.ts b/frontend/src/app/directive/resource-list-outlet.directive.spec.ts
new file mode 100644
index 00000000..2f16074f
--- /dev/null
+++ b/frontend/src/app/directive/resource-list-outlet.directive.spec.ts
@@ -0,0 +1,8 @@
+import { ResourceListOutletDirective } from './resource-list-outlet.directive';
+
+describe('ResourceListOutletDirective', () => {
+ it('should create an instance', () => {
+ const directive = new ResourceListOutletDirective();
+ expect(directive).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/directive/resource-list-outlet.directive.ts b/frontend/src/app/directive/resource-list-outlet.directive.ts
new file mode 100644
index 00000000..c8888430
--- /dev/null
+++ b/frontend/src/app/directive/resource-list-outlet.directive.ts
@@ -0,0 +1,10 @@
+import {Directive, ViewContainerRef} from '@angular/core';
+
+@Directive({
+ selector: '[resourceListOutlet]'
+})
+export class ResourceListOutletDirective {
+
+ constructor(public viewContainerRef: ViewContainerRef) { }
+
+}
diff --git a/frontend/src/app/models/fasten/source-summary.ts b/frontend/src/app/models/fasten/source-summary.ts
new file mode 100644
index 00000000..6510013e
--- /dev/null
+++ b/frontend/src/app/models/fasten/source-summary.ts
@@ -0,0 +1,12 @@
+import {Source} from './source';
+
+export class ResourceTypeCounts {
+ count: number
+ source_id: string
+ resource_type: string
+}
+
+export class SourceSummary {
+ source: Source
+ resource_type_counts: ResourceTypeCounts[]
+}
diff --git a/frontend/src/app/pages/source-detail/source-detail.component.html b/frontend/src/app/pages/source-detail/source-detail.component.html
index 0d46804b..b04bdf16 100644
--- a/frontend/src/app/pages/source-detail/source-detail.component.html
+++ b/frontend/src/app/pages/source-detail/source-detail.component.html
@@ -2,8 +2,8 @@
- -
- {{resourceGroup.key}} {{resourceGroup.value.length}}
+
-
+ {{resourceGroup.key}} {{resourceGroup.value}}
@@ -14,16 +14,7 @@
Details
- {{selectedResourceType}}
-
-
-
-
-
-
-
-
-
+
diff --git a/frontend/src/app/pages/source-detail/source-detail.component.ts b/frontend/src/app/pages/source-detail/source-detail.component.ts
index 2f50726a..e8d04369 100644
--- a/frontend/src/app/pages/source-detail/source-detail.component.ts
+++ b/frontend/src/app/pages/source-detail/source-detail.component.ts
@@ -1,7 +1,8 @@
-import { Component, OnInit } from '@angular/core';
+import {Component, OnInit, ViewChild} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {Source} from '../../models/fasten/source';
import {FastenApiService} from '../../services/fasten-api.service';
+import {ResourceListOutletDirective} from '../../directive/resource-list-outlet.directive';
@Component({
@@ -13,9 +14,8 @@ export class SourceDetailComponent implements OnInit {
selectedSource: Source = null
selectedResourceType: string = null
- selectedResources: any[] = []
- resourcesGroupedByType: { [name: string]: any[] } = {}
+ resourceTypeCounts: { [name: string]: number } = {}
constructor(private fastenApi: FastenApiService, private router: Router, private route: ActivatedRoute) {
//check if the current Source was sent over using the router state storage:
@@ -25,26 +25,16 @@ export class SourceDetailComponent implements OnInit {
}
ngOnInit(): void {
- if (this.selectedSource == null) {
- //lookup the source by ID.
- this.fastenApi.getSource(this.route.snapshot.paramMap.get('source_id')).subscribe((source) => {
- this.selectedSource = source;
- });
- }
-
- this.fastenApi.getResources(undefined, this.route.snapshot.paramMap.get('source_id')).subscribe((resourceList) => {
- console.log("RESOURCES", resourceList)
- let _resourcesGroupedByType = this.resourcesGroupedByType
- resourceList.forEach(resource => {
- let resourceTypeList = _resourcesGroupedByType[resource.source_resource_type] || [];
- resourceTypeList.push(resource)
- _resourcesGroupedByType[resource.source_resource_type] = resourceTypeList
- })
- })
+ //always request the source summary
+ this.fastenApi.getSourceSummary(this.route.snapshot.paramMap.get('source_id')).subscribe((sourceSummary) => {
+ this.selectedSource = sourceSummary.source;
+ for(let resourceTypeCount of sourceSummary.resource_type_counts){
+ this.resourceTypeCounts[resourceTypeCount.resource_type] = resourceTypeCount.count
+ }
+ });
}
selectResourceType(resourceType: string) {
this.selectedResourceType = resourceType
- this.selectedResources = this.resourcesGroupedByType[resourceType]
}
}
diff --git a/frontend/src/app/services/fasten-api.service.ts b/frontend/src/app/services/fasten-api.service.ts
index 46e11ad7..d837f61c 100644
--- a/frontend/src/app/services/fasten-api.service.ts
+++ b/frontend/src/app/services/fasten-api.service.ts
@@ -9,6 +9,7 @@ import {ResponseWrapper} from '../models/response-wrapper';
import {Source} from '../models/fasten/source';
import {User} from '../models/fasten/user';
import {ResourceFhir} from '../models/fasten/resource_fhir';
+import {SourceSummary} from '../models/fasten/source-summary';
@Injectable({
providedIn: 'root'
@@ -110,6 +111,16 @@ export class FastenApiService {
);
}
+ getSourceSummary(sourceId: string): Observable {
+ return this._httpClient.get(`${this.getBasePath()}/api/secure/source/${sourceId}/summary`)
+ .pipe(
+ map((response: ResponseWrapper) => {
+ console.log("SOURCE RESPONSE", response)
+ return response.data as SourceSummary
+ })
+ );
+ }
+
getResources(sourceResourceType?: string, sourceID?: string): Observable {
let queryParams = {}
if(sourceResourceType){