added charts (#13)
This commit is contained in:
parent
918c856338
commit
c8e074ff4b
|
@ -17,6 +17,9 @@
|
|||
<li class="nav-item" ngbDropdown [ngClass]="{ 'active': medicalHistory.isActive }">
|
||||
<a routerLink="/medical-history" routerLinkActive="active" #medicalHistory="routerLinkActive" class="nav-link"><fa-icon [icon]="['fas', 'book-medical']"></fa-icon> Medical History</a>
|
||||
</li>
|
||||
<li class="nav-item" ngbDropdown [ngClass]="{ 'active': labs.isActive }">
|
||||
<a routerLink="/labs" routerLinkActive="active" #labs="routerLinkActive" class="nav-link"><fa-icon [icon]="['fas', 'heart-pulse']"></fa-icon> Labs</a>
|
||||
</li>
|
||||
<li class="nav-item" ngbDropdown [ngClass]="{ 'active': sources.isActive }">
|
||||
<a routerLink="/sources" routerLinkActive="active" #sources="routerLinkActive" class="nav-link"><fa-icon [icon]="['fas', 'hospital']"></fa-icon> Sources</a>
|
||||
</li>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div class="row" >
|
||||
<!-- Condition Header -->
|
||||
<div class="col-6">
|
||||
{{observationTitle}}
|
||||
<span routerLink="/source/{{observations[0]?.source_id}}/resource/{{observations[0]?.source_resource_id}}">{{observationTitle}}</span>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{{observations[0] | fhirPath: "Observation.effectiveDateTime": "Observation.issued" | date}}
|
||||
|
@ -20,12 +20,27 @@
|
|||
<div class="row pl-3">
|
||||
<div class="col-12 mt-3 mb-2">
|
||||
<p>
|
||||
<strong>Test Code (loinc):</strong> {{observationCode}} <br/>
|
||||
<strong>Short Name:</strong> {{observations[0] | fhirPath: "Observation.code.text"}} <br/>
|
||||
<strong>Result:</strong> {{observations[0] | fhirPath: "Observation.valueQuantity.value"}} {{observations[0] | fhirPath: "Observation.valueQuantity.unit"}} <br/>
|
||||
<br/>
|
||||
<strong>Latest Test Date:</strong> {{observations[0] | fhirPath: "Observation.effectiveDateTime": "Observation.issued" | date}} <br/>
|
||||
<strong>Ordered By:</strong> {{observations[0] | fhirPath: "Observation.encounter.display"}} <br/>
|
||||
<strong>Notes:</strong>
|
||||
<strong>LOINC Code:</strong> {{observationCode}} <br/>
|
||||
<strong>Notes:</strong> {{observations[0] | fhirPath: "Observation.note.text"}}
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
<a class="cursor-pointer tx-indigo" (click)="collapse.toggle()">show all</a>
|
||||
</p>
|
||||
|
||||
<div #collapse="ngbCollapse" [ngbCollapse]="true">
|
||||
|
||||
<ul>
|
||||
<li class="cursor-pointer tx-indigo" *ngFor="let observation of observations" routerLink="/source/{{observation?.source_id}}/resource/{{observation?.source_resource_id}}">Observation: {{observation | fhirPath: "Observation.effectiveDateTime": "Observation.issued" | date}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -33,20 +48,20 @@
|
|||
</div>
|
||||
<div class="col-6 bg-gray-100">
|
||||
<div class="row">
|
||||
<p>
|
||||
<small>
|
||||
Troponin I is a part of the troponin complex. It binds to actin in thin myofilaments to hold the actin-tropomyosin complex in place. Because of it myosin cannot bind actin in relaxed muscle. When calcium binds to the Troponin C it causes conformational changes which lead to dislocation of troponin I and finally tropomyosin leaves the binding site for myosin on actin leading to contraction of muscle. The letter I is given due to its inhibitory character.
|
||||
</small>
|
||||
</p>
|
||||
<div class="col-12 mt-3">
|
||||
<p>
|
||||
<small>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
</small>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<canvas baseChart [height]="chartHeight" [chartType]="'horizontalBar'" [datasets]="barChartData" [labels]="barChartLabels" [options]="barChartOptions"></canvas>
|
||||
</div>
|
||||
|
||||
|
||||
<ng-container *ngFor="let observation of observations">
|
||||
|
||||
<div routerLink="/source/{{observation?.source_id}}/resource/{{observation?.source_resource_id}}" class="col-6 mt-3 mb-2 tx-indigo">
|
||||
<strong>{{observation | fhirPath: "Observation.valueQuantity.value" }}</strong>
|
||||
</div>
|
||||
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {ResourceFhir} from '../../models/fasten/resource_fhir';
|
||||
import {ChartOptions} from 'chart.js';
|
||||
// import { ChartConfiguration, ChartData, ChartEvent, ChartType } from 'chart.js';
|
||||
// import { BaseChartDirective } from 'ng2-charts';
|
||||
import * as fhirpath from 'fhirpath';
|
||||
import {formatDate} from '@angular/common';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-report-labs-observation',
|
||||
|
@ -12,10 +18,179 @@ export class ReportLabsObservationComponent implements OnInit {
|
|||
@Input() observationCode: string
|
||||
@Input() observationTitle: string
|
||||
|
||||
// based on https://stackoverflow.com/questions/38889716/chartjs-2-stacked-bar-with-marker-on-top
|
||||
// https://stackoverflow.com/questions/62711919/chart-js-horizontal-lines-per-bar
|
||||
|
||||
|
||||
chartHeight = 45
|
||||
|
||||
barChartData =[
|
||||
// {
|
||||
// label: "Current",
|
||||
// backgroundColor: 'rgba(255, 0, 128, 1)',
|
||||
// data: [],
|
||||
// xAxisID: "x-axis-current"
|
||||
// },
|
||||
// {
|
||||
// label: "Reference",
|
||||
// backgroundColor: 'rgba(99,189,50,0.2)',
|
||||
// data: [],
|
||||
// xAxisID: "x-axis-ref"
|
||||
// },
|
||||
|
||||
|
||||
|
||||
{
|
||||
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: {
|
||||
|
||||
}
|
||||
// id: "x-axis-current",
|
||||
//important settings
|
||||
|
||||
//set the width of the line ( or point )
|
||||
// pointRadius: 50,
|
||||
// don´t show line betrween points
|
||||
// showLine: false,
|
||||
//change points of line chart to line style ( little bit confusin why it´s named point anyway )
|
||||
// pointStyle: 'line',
|
||||
|
||||
//chart type
|
||||
// type: "line",
|
||||
}
|
||||
]
|
||||
|
||||
barChartLabels = [] // ["2020", "2018"] //["1","2","3","4","5","6","7","8"]
|
||||
|
||||
barChartOptions = {
|
||||
legend:{
|
||||
display: false,
|
||||
},
|
||||
//add padding to fix tooltip cutoff
|
||||
layout: {
|
||||
padding: {
|
||||
left: 0,
|
||||
right: 4,
|
||||
top: 0,
|
||||
bottom: 10
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
yAxes: [{
|
||||
stacked: true,
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
fontSize: 10,
|
||||
min: 0,
|
||||
// max: 80
|
||||
},
|
||||
}],
|
||||
xAxes: [{
|
||||
scaleLabel:{
|
||||
display: false,
|
||||
labelString: "xaxis",
|
||||
padding: 4,
|
||||
},
|
||||
// stacked: true,
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
fontSize: 10,
|
||||
min: 0,
|
||||
// max: 80
|
||||
},
|
||||
|
||||
}],
|
||||
}
|
||||
// xAxes: [{
|
||||
// id: "x-axis-current",
|
||||
// stacked: true,
|
||||
// // barPercentage: 0.6,
|
||||
// ticks: {
|
||||
// beginAtZero:true,
|
||||
// fontSize: 11
|
||||
// }
|
||||
// },{
|
||||
// id: "x-axis-ref",
|
||||
// stacked: true,
|
||||
// ticks: {
|
||||
// beginAtZero:true,
|
||||
// fontSize: 11
|
||||
// }
|
||||
// // offset: true,
|
||||
//
|
||||
// // display: false,
|
||||
// // gridLines: {
|
||||
// // offsetGridLines: true
|
||||
// // }
|
||||
// }]
|
||||
// },
|
||||
// legend: {
|
||||
// display: false
|
||||
// },
|
||||
// elements: {
|
||||
// point: {
|
||||
// radius: 0
|
||||
// }
|
||||
// }
|
||||
} as ChartOptions
|
||||
|
||||
barChartColors = [
|
||||
{
|
||||
backgroundColor: 'white'
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
let currentValues = []
|
||||
|
||||
let referenceRanges = []
|
||||
|
||||
for(let observation of this.observations){
|
||||
//get label
|
||||
this.barChartLabels.push(
|
||||
formatDate(fhirpath.evaluate(observation.resource_raw, "Observation.effectiveDateTime")[0], "mediumDate", "en-US", undefined)
|
||||
)
|
||||
|
||||
//get current value
|
||||
currentValues.push(fhirpath.evaluate(observation.resource_raw, "Observation.valueQuantity.value")[0])
|
||||
|
||||
//set chart x-axis label
|
||||
let units = fhirpath.evaluate(observation.resource_raw, "Observation.valueQuantity.unit")[0]
|
||||
if(units){
|
||||
this.barChartOptions.scales.xAxes[0].scaleLabel.display = true
|
||||
this.barChartOptions.scales.xAxes[0].scaleLabel.labelString = units
|
||||
}
|
||||
|
||||
|
||||
//add low/high ref value blocks
|
||||
referenceRanges.push([
|
||||
fhirpath.evaluate(observation.resource_raw, "Observation.referenceRange.low.value")[0],
|
||||
fhirpath.evaluate(observation.resource_raw, "Observation.referenceRange.high.value")[0]
|
||||
])
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
this.barChartData[0].data = referenceRanges
|
||||
this.barChartData[1].data = currentValues.map(v => [v, v])
|
||||
|
||||
if(currentValues.length > 1){
|
||||
this.chartHeight = 30 * currentValues.length
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<div class="card card-dashboard-seven mb-3">
|
||||
<div class="card-header tx-medium">
|
||||
<div class="row" routerLink="/source/{{condition?.source_id}}/resource/{{condition?.source_resource_id}}">
|
||||
<div class="row cursor-pointer" routerLink="/source/{{condition?.source_id}}/resource/{{condition?.source_resource_id}}">
|
||||
<!-- Condition Header -->
|
||||
<div class="col-6">
|
||||
{{condition | fhirPath: "Condition.code.text.first()":"Condition.code.coding.display.first()"}}
|
||||
|
@ -48,6 +48,15 @@
|
|||
<!-- </div>-->
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="condition.related_resources.length > 0">
|
||||
<a class="cursor-pointer tx-indigo" (click)="collapse.toggle()">show all</a>
|
||||
<div #collapse="ngbCollapse" [ngbCollapse]="true">
|
||||
|
||||
<ul>
|
||||
<li class="cursor-pointer tx-indigo" *ngFor="let resource of condition.related_resources" routerLink="/source/{{resource?.source_id}}/resource/{{resource?.source_resource_id}}">Resource: {{resource.source_resource_type}}/{{resource.source_resource_id}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
|
||||
</div>
|
||||
|
|
|
@ -41,6 +41,7 @@ import { TreeModule } from '@circlon/angular-tree-component';
|
|||
import {FilterPipe} from '../pipes/filter.pipe';
|
||||
import { ReportMedicalHistoryConditionComponent } from './report-medical-history-condition/report-medical-history-condition.component';
|
||||
import { ReportLabsObservationComponent } from './report-labs-observation/report-labs-observation.component';
|
||||
import { ChartsModule } from 'ng2-charts';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -50,6 +51,7 @@ import { ReportLabsObservationComponent } from './report-labs-observation/report
|
|||
NgbModule,
|
||||
MomentModule,
|
||||
TreeModule,
|
||||
ChartsModule
|
||||
],
|
||||
declarations: [
|
||||
ComponentsSidebarComponent,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div class="az-content-body">
|
||||
|
||||
<!-- Header Row -->
|
||||
<report-header [reportHeaderTitle]="'Medical History'"></report-header>
|
||||
<report-header [reportHeaderTitle]="'Medical History'" [reportHeaderSubTitle]="'Organized by conditions, describes the scope and breadth of medical care'"></report-header>
|
||||
|
||||
|
||||
<!-- Editor Button -->
|
||||
|
|
|
@ -3,23 +3,42 @@
|
|||
<div class="az-content-body">
|
||||
|
||||
<!-- Header Row -->
|
||||
<report-header [reportHeaderTitle]="'Labs'"></report-header>
|
||||
<report-header [reportHeaderTitle]="'Labs'" [reportHeaderSubTitle]="'The Lab report turns data into meaningful information'" ></report-header>
|
||||
|
||||
|
||||
|
||||
<!-- Observations Title -->
|
||||
<div class="row mt-5 mb-3">
|
||||
<div class="col-6">
|
||||
<h1 class="az-dashboard-title">Observations</h1>
|
||||
<ng-container *ngIf="!isEmptyReport; else emptyReport">
|
||||
<!-- Observations Title -->
|
||||
<div class="row mt-5 mb-3">
|
||||
<div class="col-6">
|
||||
<h1 class="az-dashboard-title">Observations</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Observations List -->
|
||||
<app-report-labs-observation *ngFor="let observationGroup of observationGroups | keyvalue"
|
||||
[observations]="observationGroup.value"
|
||||
[observationCode]="observationGroup.key"
|
||||
[observationTitle]="observationGroupTitles[observationGroup.key]"
|
||||
></app-report-labs-observation>
|
||||
<!-- Observations List -->
|
||||
<app-report-labs-observation *ngFor="let observationGroup of observationGroups | keyvalue"
|
||||
[observations]="observationGroup.value"
|
||||
[observationCode]="observationGroup.key"
|
||||
[observationTitle]="observationGroupTitles[observationGroup.key]"
|
||||
></app-report-labs-observation>
|
||||
</ng-container>
|
||||
|
||||
|
||||
|
||||
|
||||
<ng-template #emptyReport>
|
||||
<div class="d-flex align-items-center" style="height:100%">
|
||||
<div class="modal-body tx-center pd-y-20 pd-x-20">
|
||||
<h4 class="tx-purple mg-b-20">No Lab Data Found!</h4>
|
||||
<p class="mg-b-20 mg-x-20">
|
||||
Fasten was unable to find any lab data from your connected sources. You may need to connect another source to import your Lab data.
|
||||
</p>
|
||||
<p class="mg-b-20 mg-x-20">
|
||||
Click below to add a new healthcare provider to Fasten.
|
||||
</p>
|
||||
<button [routerLink]="'/sources'" type="button" class="btn btn-purple pd-x-25">Add Source</button>
|
||||
</div><!-- modal-body -->
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -14,12 +14,17 @@ export class ReportLabsComponent implements OnInit {
|
|||
observationGroups: {[key: string]: ResourceFhir[]} = {}
|
||||
observationGroupTitles: {[key: string]: string} = {}
|
||||
|
||||
loading = true
|
||||
isEmptyReport = false
|
||||
|
||||
constructor(
|
||||
private fastenApi: FastenApiService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.fastenApi.getResources("Observation").subscribe(results => {
|
||||
this.loading = false
|
||||
results = results || []
|
||||
console.log("ALL OBSERVATIONS", results)
|
||||
|
||||
//loop though all observations, group by "code.system": "http://loinc.org"
|
||||
|
@ -31,18 +36,21 @@ export class ReportLabsComponent implements OnInit {
|
|||
if(!this.observationGroupTitles[observationGroup]){
|
||||
this.observationGroupTitles[observationGroup] = fhirpath.evaluate(observation.resource_raw, "Observation.code.coding.where(system='http://loinc.org').first().display")[0]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//TODO: sort observation groups
|
||||
|
||||
// this.observationGroups = results
|
||||
//
|
||||
// //populate a lookup table with all resources
|
||||
// for (let condition of this.conditions) {
|
||||
// this.recPopulateResourceLookup(condition)
|
||||
// }
|
||||
this.isEmptyReport = !!!results.length
|
||||
}, error => {
|
||||
this.loading = false
|
||||
this.isEmptyReport = true
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
isEmpty(obj: any) {
|
||||
return Object.keys(obj).length === 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue