Summary Table
Categories |
Total Count |
PII |
0 |
URL |
0 |
DNS |
0 |
EKL |
0 |
IP |
0 |
PORT |
0 |
VsID |
0 |
CF |
0 |
AI |
0 |
VPD |
0 |
PL |
0 |
Other |
0 |
File Content
import { Component, OnInit, Input } from '@angular/core';
import { PatientQueryParams, FilterOps, Observation, QueryParams } from '../../models/patientModels';
import { GridElement } from '../../models/uiModels';
import { AppState } from '../../models/state';
import { Store } from '@ngrx/store';
import * as patientActions from '../../actions/patient.action';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { VitalComponent } from '../vital/vital.component';
import * as _ from 'lodash';
@Component({
selector: 'app-vitals',
templateUrl: './vitals.component.html',
styleUrls: ['./vitals.component.css']
})
export class VitalsComponent implements OnInit {
_vitals = {
data: [],
err: '',
dataTable: [],
params: <QueryParams>{},
chartData: []
};
// displays the selected vital types
vitalType = '';
// date range & view selector controls
formData = {
queryStartDate: null,
queryEndDate: null,
viewMode: 'T'
};
@Input()
set vitals(incoming: { data: Observation[], err: string }) {
if (incoming) {
this._vitals.data = incoming.data;
this._vitals.err = incoming.err;
this._vitals.dataTable = [];
if (incoming.data) {
// generate data table & and apply the sort
this.generateDataTable();
this.generateChartData();
}
}
}
@Input()
set queryParams(params: PatientQueryParams) {
this._vitals.params = params.vitals;
if (params.vitals && params.vitals.filter && params.vitals.filter.timeTaken) {
this.formData.queryStartDate = (params.vitals.filter.timeTaken.value) ? new Date(params.vitals.filter.timeTaken.value) : null;
this.formData.queryEndDate = (params.vitals.filter.timeTaken.value2) ? new Date(params.vitals.filter.timeTaken.value2) : null;
}
try {
const keys = params.vitals.filter.name.value.split('+');
this.vitalType = '';
keys.forEach((key) => {
this.vitalType += _.find(params.vitalSummary, { 'key': key }).title + ', ';
});
this.vitalType = this.vitalType.replace(/,\s$/, '');
} catch (e) {
this.vitalType = 'Unknown Vital';
}
}
dataDef: Array<GridElement> = [
{
header: 'Type', key: 'type', tooltip: 'Vital Type'
},
{
header: 'Metric Value', key: 'metricQuantity',
tooltip: 'Value of Vital Type expressed in metric system measurement (i.e. Weight in kg)'
}, {
header: 'Imperial Value', key: 'imperialQuantity',
tooltip: 'Value of Vital Type expressed in US standard of measurement or non-metric (i.e. Weight in lb)'
}, {
header: 'Date/Time Taken', key: 'timeTaken', format: 'date',
tooltip: 'Date/Time vital measaurement taken by the care provider'
}, {
header: 'Facility', key: 'stationNumber',
tooltip: 'Station number of facility where vital measurement taken', dataTooltipKey: 'siteName'
}
];
constructor(private _store: Store<AppState>, private modalService: NgbModal) { }
ngOnInit() {
}
/**
* creates local dataTable array from the progress note data, applying local filters and
* sorting it.
*/
generateDataTable() {
this._vitals.dataTable = [];
// push the data into the data table
this._vitals.data.forEach((itm, idx) => {
const row = {
type: (itm.name) ? (itm.name) : '?',
metricQuantity: (itm.metric) ? itm.metric.value + ' ' + ((itm.metric.unit) ? itm.metric.unit : '') : '',
imperialQuantity: (itm.imperial) ? itm.imperial.value + ' ' + ((itm.imperial.unit) ? itm.imperial.unit : '') : '',
timeTaken: itm.timeTaken,
stationNumber: (itm.facility) ? itm.facility.stationNumber : '',
siteName: (itm.facility) ? itm.facility.siteName : '',
dataIndex: idx
};
if (itm.rate && itm.rate.match(/UNAVAILABLE|REFUSED/i)) {
row.metricQuantity = itm.rate;
row.imperialQuantity = itm.rate;
}
this._vitals.dataTable.push(row);
});
}
/**
* Format vitals data for the chart component.
*/
generateChartData() {
const newData = [];
// grab the vital types from the dataset & loop through them
const keys = _.uniq(this._vitals.data.map(itm => itm.name));
keys.forEach((key) => {
// if the key is 'blood pressure', that comes as a single slash-delimited value from healthshare. split the data into spb/dbp
// so the value becomes numeric & they can be charted. otherwise, just copy the data over..
if (key.match(/blood pressure/i)) {
_.filter(this._vitals.data, ['name', key]).forEach((itm) => {
const vals = (itm.metric.value.match(/\//)) ? itm.metric.value.split('/') : '';
if (vals && vals.length === 2) {
newData.push({ ...itm, name: 'systolic_bp', metric: { value: vals[0], unit: itm.metric.unit } });
newData.push({ ...itm, name: 'diastolic_bp', metric: { value: vals[1], unit: itm.metric.unit } });
}
});
} else {
newData.push(..._.filter(this._vitals.data, ['name', key]));
}
});
this._vitals.chartData = [...newData];
}
/**
* Triggered on form change of date range for vitals query. Dispatches valid change to redux store.
* @param evt string identifying which form field was changed - 'start' or 'end'
*/
changeDateRange(evt) {
const element = (evt === 'start') ? { field: 'queryStartDate', param: 'value' } : { field: 'queryEndDate', param: 'value2' };
const newParams = JSON.parse(JSON.stringify(this._vitals.params));
// if they've entered bad data, reset the value to what it was & quit...
if (this.formData[element.field] && !_.isDate(this.formData[element.field])) {
this.formData[element.field] = (newParams.filter.timeTaken[element.param]) ?
new Date(newParams.filter.timeTaken[element.param]) : null;
return;
}
// validate the range. if it's inverted, set the other date to null
if (this.formData.queryStartDate && this.formData.queryEndDate && this.formData.queryStartDate > this.formData.queryEndDate) {
if (evt === 'start') {
this.formData.queryEndDate = null;
} else {
this.formData.queryStartDate = null;
}
}
// set the hours inclusive for start/end
if (this.formData.queryStartDate) { this.formData.queryStartDate.setHours(0, 0, 0, 0); }
if (this.formData.queryEndDate) { this.formData.queryEndDate.setHours(23, 59, 59, 999); }
// check if new parameters are different from old, abort if same
if (this.formData.queryEndDate
&& newParams.filter.timeTaken
&& this.formData.queryEndDate.toISOString() === newParams.filter.timeTaken.value2
&& this.formData.queryStartDate
&& this.formData.queryStartDate.toISOString() === newParams.filter.timeTaken.value) {
return;
}
// set new parameters
newParams.filter['timeTaken'] = {
operator: FilterOps.in,
value: (this.formData.queryStartDate) ? this.formData.queryStartDate.toISOString() : null,
value2: (this.formData.queryEndDate) ? this.formData.queryEndDate.toISOString() : null
};
// set the new vitals parameters in the store...
this._store.dispatch(new patientActions.SetQueryParameters({ element: 'vitals', params: newParams }));
}
/**
* switch view between tabular/graphical. query parameters change when switching...
* @param mode string 'G' or 'T'
*/
switchDataView(mode) {
// if already in the clicked mode, do nothing.
if (mode === this.formData.viewMode) { return; }
this.formData.viewMode = mode;
const newParams = JSON.parse(JSON.stringify(this._vitals.params));
newParams.page = 1;
newParams.size = (mode === 'G') ? '' : 15;
this._store.dispatch(new patientActions.SetQueryParameters({ element: 'vitals', params: newParams }));
}
/**
* Method for displaying detailed Vitals record in a modal popup
* @param dataTableRecord the Vitals to display
*/
viewDetail(dataTableRecord: any) {
const vitalModal = this.modalService.open(VitalComponent, { size: 'lg', windowClass: 'record-popup' });
vitalModal.componentInstance.data = { ...this._vitals.data[dataTableRecord.dataIndex] };
}
/**
* Paging events bubble up from grid component. updating the store here triggers a requery
* @param action string in [prev, next, number]
*/
changePage(action: any) {
const newParams = JSON.parse(JSON.stringify(this._vitals.params));
switch (action) {
case 'next':
newParams.page++;
break;
case 'prev':
if (newParams.page > 1) { newParams.page--; }
break;
default:
if (Number.isInteger(action)) { newParams.page = action; }
}
this._store.dispatch(new patientActions.SetQueryParameters({ element: 'vitals', params: newParams }));
}
}