Parse reference ranges that use `<` and `>` (#450)
* Add vscode config * Comment out empty tests causing issues running tests in vscode * Add Fishery, update test dependencies, update chart.js * Add factory for building fhir r4 observation object * Fix deprecation warnings in _mixins.scss * Update observation model for better value and reference range parsing * Add observation-bar-chart.component to pull out bar chart logic into reusable component * Use new component in observation resource and report lab component
This commit is contained in:
parent
43579df659
commit
bcffbb4769
|
@ -36,6 +36,13 @@ cmake-build-release/
|
||||||
# IntelliJ
|
# IntelliJ
|
||||||
out/
|
out/
|
||||||
|
|
||||||
|
# VS Code files for those working on multiple tools
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
|
||||||
# mpeltonen/sbt-idea plugin
|
# mpeltonen/sbt-idea plugin
|
||||||
.idea_modules/
|
.idea_modules/
|
||||||
|
|
||||||
|
@ -70,3 +77,4 @@ fasten.db-shm
|
||||||
fasten.db-wal
|
fasten.db-wal
|
||||||
|
|
||||||
backend/resources/related_versions.json
|
backend/resources/related_versions.json
|
||||||
|
frontend/documentation.json
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Go Tests",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "test",
|
||||||
|
"program": "${workspaceFolder}/backend",
|
||||||
|
"args": [
|
||||||
|
"-test.run"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "attach",
|
||||||
|
"name": "Attach Karma Chrome",
|
||||||
|
"address": "localhost",
|
||||||
|
"port": 9333,
|
||||||
|
"pathMapping": {
|
||||||
|
"/": "${workspaceRoot}/frontend",
|
||||||
|
"/base/": "${workspaceRoot}/frontend"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"karmaTestExplorer.karmaConfFilePath": "frontend/karma.conf.js",
|
||||||
|
"karmaTestExplorer.projectWorkspaces": [
|
||||||
|
"frontend"
|
||||||
|
]
|
||||||
|
}
|
|
@ -52,8 +52,8 @@ module.exports = function(config) {
|
||||||
customLaunchers: {
|
customLaunchers: {
|
||||||
ChromeHeadlessCI: {
|
ChromeHeadlessCI: {
|
||||||
base: 'ChromeHeadless',
|
base: 'ChromeHeadless',
|
||||||
flags: ['--no-sandbox', '--disable-gpu']
|
flags: ['--no-sandbox', '--disable-gpu', '--remote-debugging-port=9333']
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||||
"asmcrypto.js": "^2.3.2",
|
"asmcrypto.js": "^2.3.2",
|
||||||
"bootstrap": "^4.4.1",
|
"bootstrap": "^4.4.1",
|
||||||
"chart.js": "^4.0.1",
|
"chart.js": "^4.4.2",
|
||||||
"dwv": "^0.31.0",
|
"dwv": "^0.31.0",
|
||||||
"fhirpath": "^3.3.0",
|
"fhirpath": "^3.3.0",
|
||||||
"gridstack": "8.1.1",
|
"gridstack": "8.1.1",
|
||||||
|
@ -77,14 +77,15 @@
|
||||||
"@types/jasminewd2": "~2.0.3",
|
"@types/jasminewd2": "~2.0.3",
|
||||||
"chromatic": "^6.19.8",
|
"chromatic": "^6.19.8",
|
||||||
"codelyzer": "^5.1.2",
|
"codelyzer": "^5.1.2",
|
||||||
"jasmine-core": "~3.5.0",
|
"fishery": "^2.2.2",
|
||||||
"jasmine-spec-reporter": "~5.0.0",
|
"jasmine-core": "~5.1.2",
|
||||||
"karma": "~6.4.0",
|
"jasmine-spec-reporter": "~7.0.0",
|
||||||
"karma-chrome-launcher": "~3.1.0",
|
"karma": "~6.4.3",
|
||||||
"karma-coverage": "^2.2.0",
|
"karma-chrome-launcher": "~3.2.0",
|
||||||
|
"karma-coverage": "^2.2.1",
|
||||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||||
"karma-jasmine": "~4.0.0",
|
"karma-jasmine": "~5.1.0",
|
||||||
"karma-jasmine-html-reporter": "^1.5.0",
|
"karma-jasmine-html-reporter": "^2.1.0",
|
||||||
"protractor": "~7.0.0",
|
"protractor": "~7.0.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<!-- surrounding div needed so chart can resize when window size changes -->
|
||||||
|
<div class="observation-bar-chart-container">
|
||||||
|
<canvas baseChart
|
||||||
|
[height]="chartHeight"
|
||||||
|
[type]="'bar'"
|
||||||
|
[datasets]="barChartData"
|
||||||
|
[labels]="barChartLabels"
|
||||||
|
[options]="barChartOptions"></canvas>
|
||||||
|
</div>
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { ObservationBarChartComponent } from './observation-bar-chart.component';
|
||||||
|
|
||||||
|
describe('ObservationBarChartComponent', () => {
|
||||||
|
let component: ObservationBarChartComponent;
|
||||||
|
let fixture: ComponentFixture<ObservationBarChartComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ ObservationBarChartComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ObservationBarChartComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,161 @@
|
||||||
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
import { formatDate } from '@angular/common';
|
||||||
|
import { ObservationModel } from '../../../../../lib/models/resources/observation-model';
|
||||||
|
import { ChartConfiguration } from 'chart.js';
|
||||||
|
import { NgChartsModule } from 'ng2-charts';
|
||||||
|
|
||||||
|
const defaultChartHeight = 65;
|
||||||
|
const defaultChartEntryHeight = 30;
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'observation-bar-chart',
|
||||||
|
imports: [ NgChartsModule ],
|
||||||
|
templateUrl: './observation-bar-chart.component.html',
|
||||||
|
styleUrls: ['./observation-bar-chart.component.scss']
|
||||||
|
})
|
||||||
|
export class ObservationBarChartComponent implements OnInit {
|
||||||
|
@Input() observations: [ObservationModel]
|
||||||
|
|
||||||
|
chartHeight = defaultChartEntryHeight;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
barChartData =[
|
||||||
|
{
|
||||||
|
label: "Reference",
|
||||||
|
data: [],
|
||||||
|
dataLabels: [],
|
||||||
|
backgroundColor: "rgba(91, 71, 251,0.6)",
|
||||||
|
hoverBackgroundColor: "rgba(91, 71, 251,0.2)",
|
||||||
|
parsing: {
|
||||||
|
xAxisKey: 'range'
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label: function(context) {
|
||||||
|
return `${context.dataset.label}: ${context.dataset.dataLabels[context.dataIndex]}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Result",
|
||||||
|
data: [],
|
||||||
|
// @ts-ignore
|
||||||
|
dataLabels: [],
|
||||||
|
borderColor: "rgba(0,0,0,1)",
|
||||||
|
backgroundColor: "rgba(0,0,0,1)",
|
||||||
|
hoverBackgroundColor: "rgba(0,0,0,1)",
|
||||||
|
minBarLength: 3,
|
||||||
|
barPercentage: 1,
|
||||||
|
parsing: {
|
||||||
|
xAxisKey: 'value'
|
||||||
|
},
|
||||||
|
// @ts-ignore
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label: function(context) {
|
||||||
|
let label = `${context.dataset.label}: ${context.parsed.x}`;
|
||||||
|
|
||||||
|
if (context.dataset.dataLabels[context.dataIndex]) {
|
||||||
|
return `${label} ${context.dataset.dataLabels[context.dataIndex]}`;
|
||||||
|
}
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
] as ChartConfiguration<'bar'>['data']['datasets']
|
||||||
|
|
||||||
|
barChartLabels = [] // ["2020", "2018"] //["1","2","3","4","5","6","7","8"]
|
||||||
|
|
||||||
|
barChartOptions = {
|
||||||
|
indexAxis: 'y',
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
scaleLabel:{
|
||||||
|
display: false,
|
||||||
|
padding: 4,
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true,
|
||||||
|
fontSize: 10,
|
||||||
|
min: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} as ChartConfiguration<'bar'>['options']
|
||||||
|
|
||||||
|
barChartColors = [
|
||||||
|
{
|
||||||
|
backgroundColor: 'white'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
if(!this.observations || !this.observations[0]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentValues: number[] = []
|
||||||
|
let referenceRanges = []
|
||||||
|
|
||||||
|
for(let observation of this.observations) {
|
||||||
|
let refRange = observation.reference_range;
|
||||||
|
|
||||||
|
referenceRanges.push([refRange.low || 0, refRange.high || 0]);
|
||||||
|
currentValues.push(observation.value_quantity_value);
|
||||||
|
|
||||||
|
if (observation.effective_date) {
|
||||||
|
this.barChartLabels.push(formatDate(observation.effective_date, "mediumDate", "en-US", undefined));
|
||||||
|
} else {
|
||||||
|
this.barChartLabels.push('Unknown date');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.barChartData[0]['dataLabels'].push(observation.referenceRangeDisplay());
|
||||||
|
this.barChartData[1]['dataLabels'].push(observation.value_quantity_unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
let xAxisMax = Math.max(...currentValues) * 1.3;
|
||||||
|
this.barChartOptions.scales['x']['max'] = xAxisMax
|
||||||
|
|
||||||
|
let updatedRefRanges = referenceRanges.map(range => {
|
||||||
|
if (range[0] && !range[1]) {
|
||||||
|
return [range[0], xAxisMax]
|
||||||
|
} else {
|
||||||
|
return [range[0], range[1]]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
this.barChartData[0].data = updatedRefRanges
|
||||||
|
this.barChartData[1].data = currentValues.map(v => [v, v])
|
||||||
|
|
||||||
|
this.chartHeight = defaultChartHeight + (defaultChartEntryHeight * currentValues.length)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
import type { Meta, StoryObj } from '@storybook/angular';
|
||||||
|
import { fhirVersions } from "../../../../../lib/models/constants";
|
||||||
|
import { ObservationBarChartComponent } from './observation-bar-chart.component';
|
||||||
|
import { ObservationModel } from 'src/lib/models/resources/observation-model';
|
||||||
|
import { observationR4Factory } from 'src/lib/fixtures/factories/r4/resources/observation-r4-factory';
|
||||||
|
|
||||||
|
// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction
|
||||||
|
const meta: Meta<ObservationBarChartComponent> = {
|
||||||
|
title: 'Fhir Card/Common/ObservationBarChart',
|
||||||
|
component: ObservationBarChartComponent,
|
||||||
|
decorators: [
|
||||||
|
],
|
||||||
|
tags: ['autodocs'],
|
||||||
|
render: (args: ObservationBarChartComponent) => ({
|
||||||
|
props: {
|
||||||
|
backgroundColor: null,
|
||||||
|
...args,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
argTypes: {
|
||||||
|
observations: {
|
||||||
|
control: 'object',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<ObservationBarChartComponent>;
|
||||||
|
|
||||||
|
export const NoRange: Story = {
|
||||||
|
args: {
|
||||||
|
observations: [new ObservationModel(observationR4Factory.build(), fhirVersions.R4)]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Range: Story = {
|
||||||
|
args: {
|
||||||
|
observations: [new ObservationModel(observationR4Factory.referenceRange().build(), fhirVersions.R4)]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RangeOnlyLow: Story = {
|
||||||
|
args: {
|
||||||
|
observations: [new ObservationModel(observationR4Factory.referenceRangeOnlyLow().build(), fhirVersions.R4)]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RangeOnlyLowText: Story = {
|
||||||
|
args: {
|
||||||
|
observations: [new ObservationModel(observationR4Factory.referenceRangeStringOnlyLow().build(), fhirVersions.R4)]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RangeOnlyHigh: Story = {
|
||||||
|
args: {
|
||||||
|
observations: [new ObservationModel(observationR4Factory.referenceRangeOnlyHigh().build(), fhirVersions.R4)]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RangeOnlyHighText: Story = {
|
||||||
|
args: {
|
||||||
|
observations: [new ObservationModel(observationR4Factory.referenceRangeStringOnlyHigh().build(), fhirVersions.R4)]
|
||||||
|
}
|
||||||
|
};
|
|
@ -28,6 +28,7 @@ import {FhirCardComponent} from './fhir-card/fhir-card.component';
|
||||||
import {FhirCardOutletDirective} from './fhir-card/fhir-card-outlet.directive';
|
import {FhirCardOutletDirective} from './fhir-card/fhir-card-outlet.directive';
|
||||||
import { EncounterComponent } from './resources/encounter/encounter.component';
|
import { EncounterComponent } from './resources/encounter/encounter.component';
|
||||||
import { RtfComponent } from './datatypes/rtf/rtf.component';
|
import { RtfComponent } from './datatypes/rtf/rtf.component';
|
||||||
|
import { ObservationBarChartComponent } from './common/observation-bar-chart/observation-bar-chart.component';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,6 +37,7 @@ import { RtfComponent } from './datatypes/rtf/rtf.component';
|
||||||
//common
|
//common
|
||||||
CommonModule,
|
CommonModule,
|
||||||
BadgeComponent,
|
BadgeComponent,
|
||||||
|
ObservationBarChartComponent,
|
||||||
//datatypes
|
//datatypes
|
||||||
TableComponent,
|
TableComponent,
|
||||||
BinaryTextComponent,
|
BinaryTextComponent,
|
||||||
|
@ -75,6 +77,7 @@ import { RtfComponent } from './datatypes/rtf/rtf.component';
|
||||||
//common
|
//common
|
||||||
BadgeComponent,
|
BadgeComponent,
|
||||||
TableComponent,
|
TableComponent,
|
||||||
|
ObservationBarChartComponent,
|
||||||
//datatypes
|
//datatypes
|
||||||
BinaryTextComponent,
|
BinaryTextComponent,
|
||||||
CodableConceptComponent,
|
CodableConceptComponent,
|
||||||
|
|
|
@ -14,13 +14,7 @@
|
||||||
<p class="az-content-text mg-b-20">Observations are a central element in healthcare, used to support diagnosis, monitor progress, determine baselines and patterns and even capture demographic characteristics.</p>
|
<p class="az-content-text mg-b-20">Observations are a central element in healthcare, used to support diagnosis, monitor progress, determine baselines and patterns and even capture demographic characteristics.</p>
|
||||||
<fhir-ui-table [displayModel]="displayModel" [tableData]="tableData"></fhir-ui-table>
|
<fhir-ui-table [displayModel]="displayModel" [tableData]="tableData"></fhir-ui-table>
|
||||||
|
|
||||||
<canvas baseChart
|
<observation-bar-chart [observations]="[displayModel]"></observation-bar-chart>
|
||||||
[height]="chartHeight"
|
|
||||||
[type]="'bar'"
|
|
||||||
[datasets]="barChartData"
|
|
||||||
[labels]="barChartLabels"
|
|
||||||
[options]="barChartOptions"
|
|
||||||
></canvas>
|
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="showDetails" class="card-footer">
|
<div *ngIf="showDetails" class="card-footer">
|
||||||
<a class="float-right" [routerLink]="['/explore', displayModel?.source_id, 'resource', displayModel?.source_resource_id]">details</a>
|
<a class="float-right" [routerLink]="['/explore', displayModel?.source_id, 'resource', displayModel?.source_resource_id]">details</a>
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core';
|
import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core';
|
||||||
import {NgbCollapseModule} from '@ng-bootstrap/ng-bootstrap';
|
import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import {CommonModule, formatDate} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {BadgeComponent} from '../../common/badge/badge.component';
|
import {BadgeComponent} from '../../common/badge/badge.component';
|
||||||
import {TableComponent} from '../../common/table/table.component';
|
import {TableComponent} from '../../common/table/table.component';
|
||||||
import {Router, RouterModule} from '@angular/router';
|
import {Router, RouterModule} from '@angular/router';
|
||||||
import {LocationModel} from '../../../../../lib/models/resources/location-model';
|
|
||||||
import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item';
|
import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item';
|
||||||
import {ObservationModel} from '../../../../../lib/models/resources/observation-model';
|
import {ObservationModel} from '../../../../../lib/models/resources/observation-model';
|
||||||
import {ChartConfiguration} from 'chart.js';
|
import { ObservationBarChartComponent } from 'src/app/components/fhir-card/common/observation-bar-chart/observation-bar-chart.component';
|
||||||
import fhirpath from 'fhirpath';
|
|
||||||
import {NgChartsModule} from 'ng2-charts';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [NgbCollapseModule, CommonModule, BadgeComponent, TableComponent, RouterModule, NgChartsModule],
|
imports: [CommonModule, BadgeComponent, TableComponent, RouterModule, NgbCollapseModule, ObservationBarChartComponent],
|
||||||
|
providers: [],
|
||||||
selector: 'fhir-observation',
|
selector: 'fhir-observation',
|
||||||
templateUrl: './observation.component.html',
|
templateUrl: './observation.component.html',
|
||||||
styleUrls: ['./observation.component.scss']
|
styleUrls: ['./observation.component.scss']
|
||||||
|
@ -25,81 +23,6 @@ export class ObservationComponent implements OnInit {
|
||||||
|
|
||||||
tableData: TableRowItem[] = []
|
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) { }
|
constructor(public changeRef: ChangeDetectorRef, public router: Router) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
@ -107,7 +30,8 @@ export class ObservationComponent implements OnInit {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tableData.push( {
|
this.tableData.push(
|
||||||
|
{
|
||||||
label: 'Issued on',
|
label: 'Issued on',
|
||||||
data: this.displayModel?.effective_date,
|
data: this.displayModel?.effective_date,
|
||||||
enabled: !!this.displayModel?.effective_date,
|
enabled: !!this.displayModel?.effective_date,
|
||||||
|
@ -131,30 +55,13 @@ export class ObservationComponent implements OnInit {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Reference',
|
label: 'Reference',
|
||||||
data: [this.displayModel?.reference_range?.[0]?.low?.value,this.displayModel?.reference_range?.[0]?.high?.value].join(" "),
|
data: this.displayModel.referenceRangeDisplay(),
|
||||||
enabled: !!this.displayModel?.reference_range,
|
enabled: !!this.displayModel?.reference_range,
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
//populate chart data
|
|
||||||
if(this.displayModel?.effective_date) {
|
|
||||||
this.barChartLabels.push(
|
|
||||||
formatDate(this.displayModel?.effective_date, "mediumDate", "en-US", undefined)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
this.barChartLabels.push("")
|
|
||||||
}
|
|
||||||
|
|
||||||
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(){
|
markForCheck(){
|
||||||
this.changeRef.markForCheck()
|
this.changeRef.markForCheck()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,18 @@
|
||||||
import type { Meta, StoryObj } from '@storybook/angular';
|
import type { Meta, StoryObj } from '@storybook/angular';
|
||||||
import {fhirVersions} from "../../../../../lib/models/constants";
|
import { moduleMetadata } from '@storybook/angular';
|
||||||
import R4Example1Json from "../../../../../lib/fixtures/r4/resources/observation/example1.json";
|
import { fhirVersions } from "../../../../../lib/models/constants";
|
||||||
import R4Example2Json from "../../../../../lib/fixtures/r4/resources/observation/example2.json";
|
import { ObservationComponent } from "./observation.component";
|
||||||
import R4Example3Json from "../../../../../lib/fixtures/r4/resources/observation/example3.json";
|
import { ObservationModel } from "../../../../../lib/models/resources/observation-model";
|
||||||
import {ObservationComponent} from "./observation.component";
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import {ObservationModel} from "../../../../../lib/models/resources/observation-model";
|
import { observationR4Factory } from 'src/lib/fixtures/factories/r4/resources/observation-r4-factory';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction
|
|
||||||
const meta: Meta<ObservationComponent> = {
|
const meta: Meta<ObservationComponent> = {
|
||||||
title: 'Fhir Card/Observation',
|
title: 'Fhir Card/Observation',
|
||||||
component: ObservationComponent,
|
component: ObservationComponent,
|
||||||
decorators: [
|
decorators: [
|
||||||
// moduleMetadata({
|
moduleMetadata({
|
||||||
// imports: [AppModule]
|
imports: [ RouterTestingModule ]
|
||||||
// })
|
})
|
||||||
// applicationConfig({
|
|
||||||
// providers: [importProvidersFrom(AppModule)],
|
|
||||||
// }),
|
|
||||||
],
|
],
|
||||||
tags: ['autodocs'],
|
tags: ['autodocs'],
|
||||||
render: (args: ObservationComponent) => ({
|
render: (args: ObservationComponent) => ({
|
||||||
|
@ -40,30 +34,11 @@ const meta: Meta<ObservationComponent> = {
|
||||||
export default meta;
|
export default meta;
|
||||||
type Story = StoryObj<ObservationComponent>;
|
type Story = StoryObj<ObservationComponent>;
|
||||||
|
|
||||||
// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args
|
let observation = new ObservationModel(observationR4Factory.referenceRange().build(), fhirVersions.R4);
|
||||||
let r4Example1DisplayModel = new ObservationModel(R4Example1Json, fhirVersions.R4)
|
observation.source_id = '123-456-789'
|
||||||
r4Example1DisplayModel.source_id = '123-456-789'
|
observation.source_resource_id = '123-456-789'
|
||||||
r4Example1DisplayModel.source_resource_id = '123-456-789'
|
export const Entry: Story = {
|
||||||
export const R4Example1: Story = {
|
|
||||||
args: {
|
args: {
|
||||||
displayModel: r4Example1DisplayModel
|
displayModel: observation
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let r4Example2DisplayModel = new ObservationModel(R4Example2Json, fhirVersions.R4)
|
|
||||||
r4Example2DisplayModel.source_id = '123-456-789'
|
|
||||||
r4Example2DisplayModel.source_resource_id = '123-456-789'
|
|
||||||
export const R4Example2: Story = {
|
|
||||||
args: {
|
|
||||||
displayModel: r4Example2DisplayModel
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let r4Example3DisplayModel = new ObservationModel(R4Example3Json, fhirVersions.R4)
|
|
||||||
r4Example3DisplayModel.source_id = '123-456-789'
|
|
||||||
r4Example3DisplayModel.source_resource_id = '123-456-789'
|
|
||||||
export const R4Example3: Story = {
|
|
||||||
args: {
|
|
||||||
displayModel: r4Example3DisplayModel
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,12 +36,13 @@
|
||||||
<div #collapse="ngbCollapse" [ngbCollapse]="true">
|
<div #collapse="ngbCollapse" [ngbCollapse]="true">
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li class="cursor-pointer tx-indigo" *ngFor="let observation of observations" [routerLink]="['/explore', observation?.source_id, 'resource', observation?.source_resource_id]">Observation: {{observation | fhirPath: "Observation.effectiveDateTime": "Observation.issued" | date}}</li>
|
<li class="cursor-pointer tx-indigo" *ngFor="let observation of observations">
|
||||||
|
<a [routerLink]="['/explore', observation?.source_id, 'resource', observation?.source_resource_id]">
|
||||||
|
Observation: {{observation | fhirPath: "Observation.effectiveDateTime": "Observation.issued" | date}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -53,15 +54,12 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<canvas baseChart [height]="chartHeight" [type]="'bar'" [datasets]="barChartData" [labels]="barChartLabels" [options]="barChartOptions"></canvas>
|
<observation-bar-chart [observations]="observationModels"></observation-bar-chart>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div><!-- card-body -->
|
</div><!-- card-body -->
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { ReportLabsObservationComponent } from './report-labs-observation.component';
|
import { ReportLabsObservationComponent } from './report-labs-observation.component';
|
||||||
import {PipesModule} from '../../pipes/pipes.module';
|
import { PipesModule } from '../../pipes/pipes.module';
|
||||||
import {NgbCollapseModule} from '@ng-bootstrap/ng-bootstrap';
|
import { NgbCollapse } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
|
||||||
describe('ReportLabsObservationComponent', () => {
|
describe('ReportLabsObservationComponent', () => {
|
||||||
let component: ReportLabsObservationComponent;
|
let component: ReportLabsObservationComponent;
|
||||||
|
@ -10,8 +9,8 @@ describe('ReportLabsObservationComponent', () => {
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [PipesModule, NgbCollapseModule],
|
imports: [PipesModule],
|
||||||
declarations: [ ReportLabsObservationComponent ]
|
declarations: [ ReportLabsObservationComponent, NgbCollapse ],
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
import {Component, Input, OnInit} from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import {ResourceFhir} from '../../models/fasten/resource_fhir';
|
import { ResourceFhir } from '../../models/fasten/resource_fhir';
|
||||||
import {ChartConfiguration} from 'chart.js';
|
import { ObservationModel } from 'src/lib/models/resources/observation-model';
|
||||||
// import { ChartConfiguration, ChartData, ChartEvent, ChartType } from 'chart.js';
|
|
||||||
// import { BaseChartDirective } from 'ng2-charts';
|
|
||||||
import * as fhirpath from 'fhirpath';
|
|
||||||
import {formatDate} from '@angular/common';
|
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-report-labs-observation',
|
selector: 'app-report-labs-observation',
|
||||||
|
@ -13,186 +8,23 @@ import {formatDate} from '@angular/common';
|
||||||
styleUrls: ['./report-labs-observation.component.scss']
|
styleUrls: ['./report-labs-observation.component.scss']
|
||||||
})
|
})
|
||||||
export class ReportLabsObservationComponent implements OnInit {
|
export class ReportLabsObservationComponent implements OnInit {
|
||||||
|
|
||||||
@Input() observations: ResourceFhir[] = []
|
@Input() observations: ResourceFhir[] = []
|
||||||
@Input() observationCode: string
|
@Input() observationCode: string
|
||||||
@Input() observationTitle: string
|
@Input() observationTitle: string
|
||||||
|
|
||||||
|
observationModels: ObservationModel[] = []
|
||||||
firstObservation: ResourceFhir = null
|
firstObservation: ResourceFhir = null
|
||||||
// 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 = 60
|
|
||||||
|
|
||||||
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",
|
|
||||||
}
|
|
||||||
] as ChartConfiguration<'bar'>['data']['datasets']
|
|
||||||
|
|
||||||
barChartLabels = [] // ["2020", "2018"] //["1","2","3","4","5","6","7","8"]
|
|
||||||
|
|
||||||
barChartOptions = {
|
|
||||||
indexAxis: 'y',
|
|
||||||
legend:{
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
//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() { }
|
constructor() { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
|
||||||
let currentValues: number[] = []
|
|
||||||
|
|
||||||
let referenceRanges = []
|
|
||||||
|
|
||||||
//sort observations
|
//sort observations
|
||||||
this.observations = this.observations?.sort((a, b) => a.sort_date > b.sort_date ? -1 : a.sort_date < b.sort_date ? 1 : 0)
|
this.observations = this.observations?.sort((a, b) => a.sort_date > b.sort_date ? -1 : a.sort_date < b.sort_date ? 1 : 0)
|
||||||
|
|
||||||
if(this.observations.length > 0){
|
if(this.observations.length > 0){
|
||||||
this.firstObservation = this.observations[0]
|
this.firstObservation = this.observations[0]
|
||||||
}
|
}
|
||||||
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
|
this.observationModels = this.observations.map(ob => new ObservationModel(ob.resource_raw))
|
||||||
// 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
|
|
||||||
let units = fhirpath.evaluate(observation.resource_raw, "Observation.valueQuantity.unit")[0]
|
|
||||||
|
|
||||||
//TODO: fix x-axis label
|
|
||||||
// if(units){
|
|
||||||
//
|
|
||||||
// (this.barChartOptions as ChartConfiguration<'bar'>['options']).scales['x']['scaleLabel'].display = true
|
|
||||||
// (this.barChartOptions as ChartConfiguration<'bar'>['options']).scales['y']['scaleLabel'].labelString = units
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
//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]
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// @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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { Observable, of } from 'rxjs';
|
||||||
|
|
||||||
import R4Example1Json from "../../../lib/fixtures/r4/resources/observation/example1.json";
|
import R4Example1Json from "../../../lib/fixtures/r4/resources/observation/example1.json";
|
||||||
import { Html as GlossaryLookupHtml } from '../glossary-lookup/glossary-lookup.stories';
|
import { Html as GlossaryLookupHtml } from '../glossary-lookup/glossary-lookup.stories';
|
||||||
|
import { ObservationBarChartComponent } from '../fhir-card/common/observation-bar-chart/observation-bar-chart.component';
|
||||||
|
|
||||||
|
|
||||||
const withHttpClientProvider: DecoratorFunction<any> = (storyFunc, context) => {
|
const withHttpClientProvider: DecoratorFunction<any> = (storyFunc, context) => {
|
||||||
|
@ -39,8 +40,8 @@ const meta: Meta<ReportLabsObservationComponent> = {
|
||||||
decorators: [
|
decorators: [
|
||||||
withHttpClientProvider,
|
withHttpClientProvider,
|
||||||
moduleMetadata({
|
moduleMetadata({
|
||||||
imports: [PipesModule, GlossaryLookupComponent, NgChartsModule, RouterTestingModule, HttpClientModule],
|
imports: [PipesModule, GlossaryLookupComponent, NgChartsModule, RouterTestingModule, HttpClientModule, ObservationBarChartComponent],
|
||||||
declarations: [NgbCollapse],
|
declarations: [ NgbCollapse ],
|
||||||
providers: [],
|
providers: [],
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
|
|
|
@ -248,8 +248,8 @@ describe('DashboardWidgetComponent', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
})
|
})
|
||||||
describe('Vitals - ListWidget', () => {})
|
// describe('Vitals - ListWidget', () => {})
|
||||||
describe('Resource Aggregation - DonutWidget', () => {})
|
// describe('Resource Aggregation - DonutWidget', () => {})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
|
|
||||||
$color2: $color;
|
$color2: $color;
|
||||||
$base2: $base;
|
$base2: $base;
|
||||||
$deg: ($perc/100*360)+deg;
|
$deg: calc($perc / 100 * 360) + deg;
|
||||||
$deg1: 90deg;
|
$deg1: 90deg;
|
||||||
$deg2: $deg;
|
$deg2: $deg;
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
$base: $color;
|
$base: $color;
|
||||||
$color: $base2;
|
$color: $base2;
|
||||||
$color2: $base2;
|
$color2: $base2;
|
||||||
$deg1: ($perc/100*360+90)+deg;
|
$deg1: calc($perc / 100 * 360 + 90) + deg;
|
||||||
$deg2: 0deg;
|
$deg2: 0deg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,14 +66,14 @@
|
||||||
|
|
||||||
.slice {
|
.slice {
|
||||||
&.one {
|
&.one {
|
||||||
clip: rect(0 $size $size/2 0);
|
clip: rect(0 $size calc($size / 2) 0);
|
||||||
-webkit-transform: rotate($deg1);
|
-webkit-transform: rotate($deg1);
|
||||||
transform: rotate($deg1);
|
transform: rotate($deg1);
|
||||||
background: $color;
|
background: $color;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.two {
|
&.two {
|
||||||
clip: rect(0 $size/2 $size 0);
|
clip: rect(0 calc($size / 2) $size 0);
|
||||||
-webkit-transform: rotate($deg2);
|
-webkit-transform: rotate($deg2);
|
||||||
transform: rotate($deg2);
|
transform: rotate($deg2);
|
||||||
background: $color2;
|
background: $color2;
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
import { Factory } from 'fishery';
|
||||||
|
|
||||||
|
class ObservationR4Factory extends Factory<{}> {
|
||||||
|
|
||||||
|
valueString(value?: string) {
|
||||||
|
return this.params({
|
||||||
|
valueQuantity: null,
|
||||||
|
valueString: value || '5.5mmol/l'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
referenceRange(high?: number, low?: number) {
|
||||||
|
return this.params({
|
||||||
|
referenceRange: [
|
||||||
|
{
|
||||||
|
low: {
|
||||||
|
value: low || 3.1,
|
||||||
|
unit: 'mmol/l',
|
||||||
|
system: 'http://unitsofmeasure.org',
|
||||||
|
code: 'mmol/L'
|
||||||
|
},
|
||||||
|
high: {
|
||||||
|
value: high || 6.5,
|
||||||
|
unit: 'mmol/l',
|
||||||
|
system: 'http://unitsofmeasure.org',
|
||||||
|
code: 'mmol/L'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
referenceRangeOnlyHigh(value?: number) {
|
||||||
|
return this.params({
|
||||||
|
referenceRange: [
|
||||||
|
{
|
||||||
|
high: {
|
||||||
|
value: value || 6.5,
|
||||||
|
unit: 'mmol/l',
|
||||||
|
system: 'http://unitsofmeasure.org',
|
||||||
|
code: 'mmol/L'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
referenceRangeOnlyLow(value?: number) {
|
||||||
|
return this.params({
|
||||||
|
referenceRange: [
|
||||||
|
{
|
||||||
|
low: {
|
||||||
|
value: value || 3.1,
|
||||||
|
unit: 'mmol/l',
|
||||||
|
system: 'http://unitsofmeasure.org',
|
||||||
|
code: 'mmol/L'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
referenceRangeString(range?: string) {
|
||||||
|
return this.params({
|
||||||
|
referenceRange: [
|
||||||
|
{
|
||||||
|
text: range || '3.1mmol/l-6.3mmol/l'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
referenceRangeStringOnlyHigh(high?: string) {
|
||||||
|
return this.params({
|
||||||
|
referenceRange: [
|
||||||
|
{
|
||||||
|
text: high || '<=5.5'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
referenceRangeStringOnlyLow(low?: | string) {
|
||||||
|
return this.params({
|
||||||
|
referenceRange: [
|
||||||
|
{
|
||||||
|
text: low || '>=4.5'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const observationR4Factory = ObservationR4Factory.define(() => (
|
||||||
|
{
|
||||||
|
resourceType: 'Observation',
|
||||||
|
id: 'f001',
|
||||||
|
text: {
|
||||||
|
status: 'generated',
|
||||||
|
div: "<div xmlns=\'http://www.w3.org/1999/xhtml\'><p><b>Generated Narrative with Details</b></p><p><b>id</b>: example</p><p><b>status</b>: final</p><p><b>category</b>: Vital Signs <span>(Details : {http://terminology.hl7.org/CodeSystem/observation-category code 'vital-signs' = 'Vital Signs', given as 'Vital Signs'})</span></p><p><b>code</b>: Body Weight <span>(Details : {LOINC code '29463-7' = 'Body weight', given as 'Body Weight'}; {LOINC code '3141-9' = 'Body weight Measured', given as 'Body weight Measured'}; {SNOMED CT code '27113001' = 'Body weight', given as 'Body weight'}; {http://acme.org/devices/clinical-codes code 'body-weight' = 'body-weight', given as 'Body Weight'})</span></p><p><b>subject</b>: <a>Patient/example</a></p><p><b>encounter</b>: <a>Encounter/example</a></p><p><b>effective</b>: 28/03/2016</p><p><b>value</b>: 185 lbs<span> (Details: UCUM code [lb_av] = 'lb_av')</span></p></div>"
|
||||||
|
},
|
||||||
|
identifier: [
|
||||||
|
{
|
||||||
|
use: 'official',
|
||||||
|
system: 'http://www.bmc.nl/zorgportal/identifiers/observations',
|
||||||
|
value: '6323'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
status: 'final',
|
||||||
|
code: {
|
||||||
|
coding: [
|
||||||
|
{
|
||||||
|
system: 'http://loinc.org',
|
||||||
|
code: '15074-8',
|
||||||
|
display: 'Glucose [Moles/volume] in Blood'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
subject: {
|
||||||
|
reference: 'Patient/f001',
|
||||||
|
display: 'P. van de Heuvel'
|
||||||
|
},
|
||||||
|
effectiveDateTime: '2016-03-28',
|
||||||
|
effectivePeriod: {
|
||||||
|
start: '2013-04-02T09:30:10+01:00'
|
||||||
|
},
|
||||||
|
issued: '2013-04-03T15:30:10+01:00',
|
||||||
|
performer: [
|
||||||
|
{
|
||||||
|
reference: 'Practitioner/f005',
|
||||||
|
display: 'A. Langeveld'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
valueQuantity: {
|
||||||
|
value: 6.3,
|
||||||
|
unit: 'mmol/l',
|
||||||
|
system: 'http://unitsofmeasure.org',
|
||||||
|
code: 'mmol/L'
|
||||||
|
},
|
||||||
|
interpretation: [
|
||||||
|
{
|
||||||
|
coding: [
|
||||||
|
{
|
||||||
|
system: 'http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation',
|
||||||
|
code: 'H',
|
||||||
|
display: 'High'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
));
|
|
@ -1,7 +1,95 @@
|
||||||
import { ObservationModel } from './observation-model';
|
import { ObservationModel } from './observation-model';
|
||||||
|
import { fhirVersions } from '../constants';
|
||||||
|
import { observationR4Factory } from 'src/lib/fixtures/factories/r4/resources/observation-r4-factory';
|
||||||
|
|
||||||
describe('ObservationModel', () => {
|
describe('ObservationModel', () => {
|
||||||
it('should create an instance', () => {
|
it('should create an instance', () => {
|
||||||
expect(new ObservationModel({})).toBeTruthy();
|
expect(new ObservationModel({})).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('parsing value', () => {
|
||||||
|
it('reads from valueQuantity.value if set', () => {
|
||||||
|
let observation = new ObservationModel(observationR4Factory.build(), fhirVersions.R4);
|
||||||
|
|
||||||
|
expect(observation.value_quantity_value).toEqual(6.3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses valueString correctly when value is a number if valueQuantity.value not set', () => {
|
||||||
|
let observation = new ObservationModel(observationR4Factory.valueString().build(), fhirVersions.R4);
|
||||||
|
|
||||||
|
expect(observation.value_quantity_value).toEqual(5.5);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('parsing unit', () => {
|
||||||
|
it('reads from valueQuantity.unit if set', () => {
|
||||||
|
let observation = new ObservationModel(observationR4Factory.build(), fhirVersions.R4);
|
||||||
|
|
||||||
|
expect(observation.value_quantity_unit).toEqual('mmol/l');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reads from valueString if valueQuantity.unit not set', () => {
|
||||||
|
let observation = new ObservationModel(observationR4Factory.valueString().build(), fhirVersions.R4);
|
||||||
|
|
||||||
|
expect(observation.value_quantity_unit).toEqual('mmol/l');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('parsing reference range', () => {
|
||||||
|
it('parses referenceRange correctly when high and low are not set', () => {
|
||||||
|
let observation = new ObservationModel(observationR4Factory.build(), fhirVersions.R4);
|
||||||
|
|
||||||
|
expect(observation.reference_range).toEqual({ low: null, high: null });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses referenceRange correctly when high and low are set', () => {
|
||||||
|
let observation = new ObservationModel(observationR4Factory.referenceRange().build(), fhirVersions.R4);
|
||||||
|
|
||||||
|
expect(observation.reference_range).toEqual({ low: 3.1, high: 6.5 });
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when referenceRange.text is set', () => {
|
||||||
|
it('parses values correctly when there is a high and a low', () => {
|
||||||
|
let tests = [
|
||||||
|
{ text: '50.3-109.2', result: { low: 50.3, high: 109.2 } },
|
||||||
|
{ text: '50.3mg/L-109.2mg/L', result: { low: 50.3, high: 109.2 } },
|
||||||
|
{ text: '50.3-109.2mg/L', result: { low: 50.3, high: 109.2 } },
|
||||||
|
{ text: '50.3mg/L-109.2', result: { low: 50.3, high: 109.2 } }
|
||||||
|
]
|
||||||
|
|
||||||
|
for(let test of tests) {
|
||||||
|
let observation = new ObservationModel(observationR4Factory.referenceRangeString(test.text).build(), fhirVersions.R4);
|
||||||
|
expect(observation.reference_range).toEqual(test.result)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses values correctly when there is only a low', () => {
|
||||||
|
let tests = [
|
||||||
|
{ text: '>50.3', result: { low: 50.3, high: null } },
|
||||||
|
{ text: '>50.3mg/L', result: { low: 50.3, high: null } },
|
||||||
|
{ text: '>=50.3', result: { low: 50.3, high: null } },
|
||||||
|
{ text: '>=50.3mg/L', result: { low: 50.3, high: null } }
|
||||||
|
]
|
||||||
|
|
||||||
|
for(let test of tests) {
|
||||||
|
let observation = new ObservationModel(observationR4Factory.referenceRangeStringOnlyLow(test.text).build(), fhirVersions.R4);
|
||||||
|
expect(observation.reference_range).toEqual(test.result)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses values correctly when there is only a high', () => {
|
||||||
|
let tests = [
|
||||||
|
{ text: '<109.2', result: { low: null, high: 109.2 } },
|
||||||
|
{ text: '<109.2mg/L', result: { low: null, high: 109.2 } },
|
||||||
|
{ text: '<=109.2', result: { low: null, high: 109.2 } },
|
||||||
|
{ text: '<=109.2mg/L', result: { low: null, high: 109.2 } }
|
||||||
|
]
|
||||||
|
|
||||||
|
for(let test of tests) {
|
||||||
|
let observation = new ObservationModel(observationR4Factory.referenceRangeStringOnlyHigh(test.text).build(), fhirVersions.R4);
|
||||||
|
expect(observation.reference_range).toEqual(test.result)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,17 +2,20 @@ import {fhirVersions, ResourceType} from '../constants';
|
||||||
import * as _ from "lodash";
|
import * as _ from "lodash";
|
||||||
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
|
||||||
import {ReferenceModel} from '../datatypes/reference-model';
|
import {ReferenceModel} from '../datatypes/reference-model';
|
||||||
import {CodingModel} from '../datatypes/coding-model';
|
|
||||||
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
import {FastenDisplayModel} from '../fasten/fasten-display-model';
|
||||||
import {FastenOptions} from '../fasten/fasten-options';
|
import {FastenOptions} from '../fasten/fasten-options';
|
||||||
|
|
||||||
|
interface referenceRangeHash {
|
||||||
|
low: number | null,
|
||||||
|
high: number | null
|
||||||
|
}
|
||||||
|
|
||||||
export class ObservationModel extends FastenDisplayModel {
|
export class ObservationModel extends FastenDisplayModel {
|
||||||
code: CodableConceptModel | undefined
|
code: CodableConceptModel | undefined
|
||||||
effective_date: string
|
effective_date: string
|
||||||
code_coding_display: string
|
code_coding_display: string
|
||||||
code_text: string
|
code_text: string
|
||||||
value_quantity_value: number | string
|
value_quantity_value: number
|
||||||
value_quantity_unit: string
|
value_quantity_unit: string
|
||||||
status: string
|
status: string
|
||||||
value_codeable_concept_text: string
|
value_codeable_concept_text: string
|
||||||
|
@ -20,25 +23,19 @@ export class ObservationModel extends FastenDisplayModel {
|
||||||
value_codeable_concept_coding: string
|
value_codeable_concept_coding: string
|
||||||
value_quantity_value_number: number
|
value_quantity_value_number: number
|
||||||
subject: ReferenceModel | undefined
|
subject: ReferenceModel | undefined
|
||||||
reference_range: {
|
fhirResource: any
|
||||||
low: {
|
reference_range: referenceRangeHash
|
||||||
value: number
|
|
||||||
}
|
|
||||||
high: {
|
|
||||||
value: number
|
|
||||||
}
|
|
||||||
}[] | undefined
|
|
||||||
|
|
||||||
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
|
||||||
super(fastenOptions)
|
super(fastenOptions)
|
||||||
|
this.fhirResource = fhirResource
|
||||||
this.source_resource_type = ResourceType.Observation
|
this.source_resource_type = ResourceType.Observation
|
||||||
this.effective_date = _.get(fhirResource, 'effectiveDateTime');
|
this.effective_date = _.get(fhirResource, 'effectiveDateTime');
|
||||||
this.code = _.get(fhirResource, 'code');
|
this.code = _.get(fhirResource, 'code');
|
||||||
this.code_coding_display = _.get(fhirResource, 'code.coding.0.display');
|
this.code_coding_display = _.get(fhirResource, 'code.coding.0.display');
|
||||||
this.code_text = _.get(fhirResource, 'code.text', '');
|
this.code_text = _.get(fhirResource, 'code.text', '');
|
||||||
this.value_quantity_value = _.get(fhirResource, 'valueQuantity.value', '');
|
this.value_quantity_value = this.parseValue();
|
||||||
// const issued = _.get(fhirResource, 'issued', '');
|
this.value_quantity_unit = this.parseUnit();
|
||||||
this.value_quantity_unit = _.get(fhirResource, 'valueQuantity.unit', '');
|
|
||||||
this.status = _.get(fhirResource, 'status', '');
|
this.status = _.get(fhirResource, 'status', '');
|
||||||
this.value_codeable_concept_text = _.get(
|
this.value_codeable_concept_text = _.get(
|
||||||
fhirResource,
|
fhirResource,
|
||||||
|
@ -54,7 +51,97 @@ export class ObservationModel extends FastenDisplayModel {
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
this.reference_range = _.get(fhirResource, 'referenceRange', []);
|
this.reference_range = this.parseReferenceRange();
|
||||||
this.subject = _.get(fhirResource, 'subject');
|
this.subject = _.get(fhirResource, 'subject');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private parseValue(): number {
|
||||||
|
// TODO: parseFloat would return NaN if it can't parse. Need to check and make sure that doesn't cause issues
|
||||||
|
return this.valueQuantity() || parseFloat(this.valueString())
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseUnit(): string {
|
||||||
|
return this.valueUnit() || this.valueStringUnit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for the observation's numeric value. Use this first before valueString which is a backup if this can't be found.
|
||||||
|
private valueQuantity(): number {
|
||||||
|
// debugger
|
||||||
|
return _.get(this.fhirResource, "valueQuantity.value");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for the observation's numeric value. Use this first before valueStringUnit which is a backup if this can't be found.
|
||||||
|
private valueUnit(): string {
|
||||||
|
return _.get(this.fhirResource, "valueQuantity.unit");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use if valueQuantity can't be found. This will check for valueString and attempt to parse the first number in the string
|
||||||
|
private valueString(): string {
|
||||||
|
return _.get(this.fhirResource, "valueString")?.match(/(?<value>[\d.]*)(?<text>.*)/).groups.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use if valueUnit can't be found.
|
||||||
|
private valueStringUnit(): string {
|
||||||
|
return _.get(this.fhirResource, "valueString")?.match(/(?<value>[\d.]*)(?<text>.*)/).groups.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
private referenceRangeFromString(str: string): referenceRangeHash {
|
||||||
|
let matches = str?.match(/(?<value1>[\d.]*)?(?<operator>[^\d]*)?(?<value2>[\d.]*)?/)
|
||||||
|
|
||||||
|
if(!matches) {
|
||||||
|
return { low: null, high: null }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!matches.groups['value1'] && !!matches.groups['value2']) {
|
||||||
|
return {
|
||||||
|
low: parseFloat(matches.groups['value1']),
|
||||||
|
high: parseFloat(matches.groups['value2'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['<', '<='].includes(matches.groups['operator'])) {
|
||||||
|
return {
|
||||||
|
low: null,
|
||||||
|
high: parseFloat(matches.groups['value2'])
|
||||||
|
}
|
||||||
|
} else { // > >=
|
||||||
|
return {
|
||||||
|
low: parseFloat(matches.groups['value2']),
|
||||||
|
high: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseReferenceRange(): referenceRangeHash {
|
||||||
|
let refRangeObject = _.get(this.fhirResource, "referenceRange.0")
|
||||||
|
|
||||||
|
if (refRangeObject?.low || refRangeObject?.high) {
|
||||||
|
return {
|
||||||
|
low: refRangeObject.low?.value,
|
||||||
|
high: refRangeObject.high?.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.referenceRangeFromString(refRangeObject?.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
public referenceRangeDisplay(): string {
|
||||||
|
// If text was sent just show it since we aren't storing difference between <= and <.
|
||||||
|
// Likely doesn't really matter, but might as well if we have that data.
|
||||||
|
if (_.get(this.fhirResource, 'referenceRange.0.text')) {
|
||||||
|
return _.get(this.fhirResource, 'referenceRange.0.text');
|
||||||
|
}
|
||||||
|
|
||||||
|
let refRange = this.parseReferenceRange()
|
||||||
|
|
||||||
|
if (refRange['low'] && refRange['high']) {
|
||||||
|
return `${refRange['low']}\u{2013}${refRange['high']}`;
|
||||||
|
} else if (refRange['low']) {
|
||||||
|
return `> ${refRange['low']}`;
|
||||||
|
} else if (refRange['high']) {
|
||||||
|
return `< ${refRange['high']}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4970,10 +4970,10 @@ chardet@^0.7.0:
|
||||||
resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
||||||
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
||||||
|
|
||||||
chart.js@^4.0.1:
|
chart.js@^4.4.2:
|
||||||
version "4.3.0"
|
version "4.4.2"
|
||||||
resolved "https://registry.npmjs.org/chart.js/-/chart.js-4.3.0.tgz#ac363030ab3fec572850d2d872956f32a46326a1"
|
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.4.2.tgz#95962fa6430828ed325a480cc2d5f2b4e385ac31"
|
||||||
integrity sha512-ynG0E79xGfMaV2xAHdbhwiPLczxnNNnasrmPEXriXsPJGjmhOBYzFVEsB65w2qMDz+CaBJJuJD0inE/ab/h36g==
|
integrity sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@kurkle/color" "^0.3.0"
|
"@kurkle/color" "^0.3.0"
|
||||||
|
|
||||||
|
@ -5592,7 +5592,7 @@ debug@2.6.9, debug@^2.6.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "2.0.0"
|
ms "2.0.0"
|
||||||
|
|
||||||
debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2:
|
debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2, debug@~4.3.4:
|
||||||
version "4.3.4"
|
version "4.3.4"
|
||||||
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||||
|
@ -6052,15 +6052,15 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
once "^1.4.0"
|
once "^1.4.0"
|
||||||
|
|
||||||
engine.io-parser@~5.0.3:
|
engine.io-parser@~5.2.1:
|
||||||
version "5.0.6"
|
version "5.2.2"
|
||||||
resolved "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz#7811244af173e157295dec9b2718dfe42a64ef45"
|
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.2.tgz#37b48e2d23116919a3453738c5720455e64e1c49"
|
||||||
integrity sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==
|
integrity sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==
|
||||||
|
|
||||||
engine.io@~6.2.1:
|
engine.io@~6.5.2:
|
||||||
version "6.2.1"
|
version "6.5.4"
|
||||||
resolved "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz#e3f7826ebc4140db9bbaa9021ad6b1efb175878f"
|
resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.5.4.tgz#6822debf324e781add2254e912f8568508850cdc"
|
||||||
integrity sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==
|
integrity sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/cookie" "^0.4.1"
|
"@types/cookie" "^0.4.1"
|
||||||
"@types/cors" "^2.8.12"
|
"@types/cors" "^2.8.12"
|
||||||
|
@ -6070,8 +6070,8 @@ engine.io@~6.2.1:
|
||||||
cookie "~0.4.1"
|
cookie "~0.4.1"
|
||||||
cors "~2.8.5"
|
cors "~2.8.5"
|
||||||
debug "~4.3.1"
|
debug "~4.3.1"
|
||||||
engine.io-parser "~5.0.3"
|
engine.io-parser "~5.2.1"
|
||||||
ws "~8.2.3"
|
ws "~8.11.0"
|
||||||
|
|
||||||
enhanced-resolve@^5.10.0, enhanced-resolve@^5.7.0:
|
enhanced-resolve@^5.10.0, enhanced-resolve@^5.7.0:
|
||||||
version "5.13.0"
|
version "5.13.0"
|
||||||
|
@ -6840,6 +6840,13 @@ findit2@^2.2.3:
|
||||||
resolved "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz#58a466697df8a6205cdfdbf395536b8bd777a5f6"
|
resolved "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz#58a466697df8a6205cdfdbf395536b8bd777a5f6"
|
||||||
integrity sha512-lg/Moejf4qXovVutL0Lz4IsaPoNYMuxt4PA0nGqFxnJ1CTTGGlEO2wKgoDpwknhvZ8k4Q2F+eesgkLbG2Mxfog==
|
integrity sha512-lg/Moejf4qXovVutL0Lz4IsaPoNYMuxt4PA0nGqFxnJ1CTTGGlEO2wKgoDpwknhvZ8k4Q2F+eesgkLbG2Mxfog==
|
||||||
|
|
||||||
|
fishery@^2.2.2:
|
||||||
|
version "2.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/fishery/-/fishery-2.2.2.tgz#94d3d9380295dd3ce555021e9353c5348b8beb77"
|
||||||
|
integrity sha512-jeU0nDhPHJkupmjX+r9niKgVMTBDB8X+U/pktoGHAiWOSyNlMd0HhmqnjrpjUOCDPJYaSSu4Ze16h6dZOKSp2w==
|
||||||
|
dependencies:
|
||||||
|
lodash.mergewith "^4.6.2"
|
||||||
|
|
||||||
flatted@^3.2.7:
|
flatted@^3.2.7:
|
||||||
version "3.2.7"
|
version "3.2.7"
|
||||||
resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
|
resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
|
||||||
|
@ -8138,25 +8145,25 @@ jake@^10.8.5:
|
||||||
filelist "^1.0.1"
|
filelist "^1.0.1"
|
||||||
minimatch "^3.0.4"
|
minimatch "^3.0.4"
|
||||||
|
|
||||||
jasmine-core@^3.6.0:
|
jasmine-core@^4.1.0:
|
||||||
version "3.99.1"
|
version "4.6.0"
|
||||||
resolved "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.99.1.tgz#5bfa4b2d76618868bfac4c8ff08bb26fffa4120d"
|
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-4.6.0.tgz#6884fc3d5b66bf293e422751eed6d6da217c38f5"
|
||||||
integrity sha512-Hu1dmuoGcZ7AfyynN3LsfruwMbxMALMka+YtZeGoLuDEySVmVAPaonkNoBRIw/ectu8b9tVQCJNgp4a4knp+tg==
|
integrity sha512-O236+gd0ZXS8YAjFx8xKaJ94/erqUliEkJTDedyE7iHvv4ZVqi+q+8acJxu05/WJDKm512EUNn809In37nWlAQ==
|
||||||
|
|
||||||
jasmine-core@~2.8.0:
|
jasmine-core@~2.8.0:
|
||||||
version "2.8.0"
|
version "2.8.0"
|
||||||
resolved "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz#bcc979ae1f9fd05701e45e52e65d3a5d63f1a24e"
|
resolved "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz#bcc979ae1f9fd05701e45e52e65d3a5d63f1a24e"
|
||||||
integrity sha512-SNkOkS+/jMZvLhuSx1fjhcNWUC/KG6oVyFUGkSBEr9n1axSNduWU8GlI7suaHXr4yxjet6KjrUZxUTE5WzzWwQ==
|
integrity sha512-SNkOkS+/jMZvLhuSx1fjhcNWUC/KG6oVyFUGkSBEr9n1axSNduWU8GlI7suaHXr4yxjet6KjrUZxUTE5WzzWwQ==
|
||||||
|
|
||||||
jasmine-core@~3.5.0:
|
jasmine-core@~5.1.2:
|
||||||
version "3.5.0"
|
version "5.1.2"
|
||||||
resolved "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz#132c23e645af96d85c8bca13c8758b18429fc1e4"
|
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-5.1.2.tgz#8f2789faa79ef1ffad7abab6bff8d4bd661094f7"
|
||||||
integrity sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==
|
integrity sha512-2oIUMGn00FdUiqz6epiiJr7xcFyNYj3rDcfmnzfkBnHyBQ3cBQUs4mmyGsOb7TTLb9kxk7dBcmEmqhDKkBoDyA==
|
||||||
|
|
||||||
jasmine-spec-reporter@~5.0.0:
|
jasmine-spec-reporter@~7.0.0:
|
||||||
version "5.0.2"
|
version "7.0.0"
|
||||||
resolved "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-5.0.2.tgz#b61288ab074ad440dc2477c4d42840b0e74a6b95"
|
resolved "https://registry.yarnpkg.com/jasmine-spec-reporter/-/jasmine-spec-reporter-7.0.0.tgz#94b939448e63d4e2bd01668142389f20f0a8ea49"
|
||||||
integrity sha512-6gP1LbVgJ+d7PKksQBc2H0oDGNRQI3gKUsWlswKaQ2fif9X5gzhQcgM5+kiJGCQVurOG09jqNhk7payggyp5+g==
|
integrity sha512-OtC7JRasiTcjsaCBPtMO0Tl8glCejM4J4/dNuOJdA8lBjz4PmWjYQ6pzb0uzpBNAWJMDudYuj9OdXJWqM2QTJg==
|
||||||
dependencies:
|
dependencies:
|
||||||
colors "1.4.0"
|
colors "1.4.0"
|
||||||
|
|
||||||
|
@ -8405,10 +8412,10 @@ jszip@~3.7.0:
|
||||||
readable-stream "~2.3.6"
|
readable-stream "~2.3.6"
|
||||||
set-immediate-shim "~1.0.1"
|
set-immediate-shim "~1.0.1"
|
||||||
|
|
||||||
karma-chrome-launcher@~3.1.0:
|
karma-chrome-launcher@~3.2.0:
|
||||||
version "3.1.1"
|
version "3.2.0"
|
||||||
resolved "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz#baca9cc071b1562a1db241827257bfe5cab597ea"
|
resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz#eb9c95024f2d6dfbb3748d3415ac9b381906b9a9"
|
||||||
integrity sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ==
|
integrity sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
which "^1.2.1"
|
which "^1.2.1"
|
||||||
|
|
||||||
|
@ -8423,10 +8430,10 @@ karma-coverage-istanbul-reporter@~3.0.2:
|
||||||
istanbul-reports "^3.0.2"
|
istanbul-reports "^3.0.2"
|
||||||
minimatch "^3.0.4"
|
minimatch "^3.0.4"
|
||||||
|
|
||||||
karma-coverage@^2.2.0:
|
karma-coverage@^2.2.1:
|
||||||
version "2.2.0"
|
version "2.2.1"
|
||||||
resolved "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.2.0.tgz#64f838b66b71327802e7f6f6c39d569b7024e40c"
|
resolved "https://registry.yarnpkg.com/karma-coverage/-/karma-coverage-2.2.1.tgz#e1cc074f93ace9dc4fb7e7aeca7135879c2e358c"
|
||||||
integrity sha512-gPVdoZBNDZ08UCzdMHHhEImKrw1+PAOQOIiffv1YsvxFhBjqvo/SVXNk4tqn1SYqX0BJZT6S/59zgxiBe+9OuA==
|
integrity sha512-yj7hbequkQP2qOSb20GuNSIyE//PgJWHwC2IydLE6XRtsnaflv+/OSGNssPjobYUlhVVagy99TQpqUt3vAUG7A==
|
||||||
dependencies:
|
dependencies:
|
||||||
istanbul-lib-coverage "^3.2.0"
|
istanbul-lib-coverage "^3.2.0"
|
||||||
istanbul-lib-instrument "^5.1.0"
|
istanbul-lib-instrument "^5.1.0"
|
||||||
|
@ -8435,17 +8442,17 @@ karma-coverage@^2.2.0:
|
||||||
istanbul-reports "^3.0.5"
|
istanbul-reports "^3.0.5"
|
||||||
minimatch "^3.0.4"
|
minimatch "^3.0.4"
|
||||||
|
|
||||||
karma-jasmine-html-reporter@^1.5.0:
|
karma-jasmine-html-reporter@^2.1.0:
|
||||||
version "1.7.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.7.0.tgz#52c489a74d760934a1089bfa5ea4a8fcb84cc28b"
|
resolved "https://registry.yarnpkg.com/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-2.1.0.tgz#f951ad00b08d61d03595402c914d1a589c4930e3"
|
||||||
integrity sha512-pzum1TL7j90DTE86eFt48/s12hqwQuiD+e5aXx2Dc9wDEn2LfGq6RoAxEZZjFiN0RDSCOnosEKRZWxbQ+iMpQQ==
|
integrity sha512-sPQE1+nlsn6Hwb5t+HHwyy0A1FNCVKuL1192b+XNauMYWThz2kweiBVW1DqloRpVvZIJkIoHVB7XRpK78n1xbQ==
|
||||||
|
|
||||||
karma-jasmine@~4.0.0:
|
karma-jasmine@~5.1.0:
|
||||||
version "4.0.2"
|
version "5.1.0"
|
||||||
resolved "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.2.tgz#386db2a3e1acc0af5265c711f673f78f1e4938de"
|
resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-5.1.0.tgz#3af4558a6502fa16856a0f346ec2193d4b884b2f"
|
||||||
integrity sha512-ggi84RMNQffSDmWSyyt4zxzh2CQGwsxvYYsprgyR1j8ikzIduEdOlcLvXjZGwXG/0j41KUXOWsUCBfbEHPWP9g==
|
integrity sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
jasmine-core "^3.6.0"
|
jasmine-core "^4.1.0"
|
||||||
|
|
||||||
karma-source-map-support@1.4.0:
|
karma-source-map-support@1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
|
@ -8454,10 +8461,10 @@ karma-source-map-support@1.4.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
source-map-support "^0.5.5"
|
source-map-support "^0.5.5"
|
||||||
|
|
||||||
karma@~6.4.0:
|
karma@~6.4.3:
|
||||||
version "6.4.1"
|
version "6.4.3"
|
||||||
resolved "https://registry.npmjs.org/karma/-/karma-6.4.1.tgz#f2253716dd3a41aaa813fa9f54b6ee047e1127d9"
|
resolved "https://registry.yarnpkg.com/karma/-/karma-6.4.3.tgz#763e500f99597218bbb536de1a14acc4ceea7ce8"
|
||||||
integrity sha512-Cj57NKOskK7wtFWSlMvZf459iX+kpYIPXmkNUzP2WAFcA7nhr/ALn5R7sw3w+1udFDcpMx/tuB8d5amgm3ijaA==
|
integrity sha512-LuucC/RE92tJ8mlCwqEoRWXP38UMAqpnq98vktmS9SznSoUPPUJQbc91dHcxcunROvfQjdORVA/YFviH+Xci9Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@colors/colors" "1.5.0"
|
"@colors/colors" "1.5.0"
|
||||||
body-parser "^1.19.0"
|
body-parser "^1.19.0"
|
||||||
|
@ -8478,7 +8485,7 @@ karma@~6.4.0:
|
||||||
qjobs "^1.2.0"
|
qjobs "^1.2.0"
|
||||||
range-parser "^1.2.1"
|
range-parser "^1.2.1"
|
||||||
rimraf "^3.0.2"
|
rimraf "^3.0.2"
|
||||||
socket.io "^4.4.1"
|
socket.io "^4.7.2"
|
||||||
source-map "^0.6.1"
|
source-map "^0.6.1"
|
||||||
tmp "^0.2.1"
|
tmp "^0.2.1"
|
||||||
ua-parser-js "^0.7.30"
|
ua-parser-js "^0.7.30"
|
||||||
|
@ -8652,6 +8659,11 @@ lodash.get@~4.4.2:
|
||||||
resolved "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
|
resolved "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
|
||||||
integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==
|
integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==
|
||||||
|
|
||||||
|
lodash.mergewith@^4.6.2:
|
||||||
|
version "4.6.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55"
|
||||||
|
integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==
|
||||||
|
|
||||||
lodash@^4.17.20, lodash@^4.17.21:
|
lodash@^4.17.20, lodash@^4.17.21:
|
||||||
version "4.17.21"
|
version "4.17.21"
|
||||||
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
|
@ -11349,30 +11361,34 @@ smart-buffer@^4.2.0:
|
||||||
resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
|
resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
|
||||||
integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
|
integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
|
||||||
|
|
||||||
socket.io-adapter@~2.4.0:
|
socket.io-adapter@~2.5.2:
|
||||||
version "2.4.0"
|
version "2.5.4"
|
||||||
resolved "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz#b50a4a9ecdd00c34d4c8c808224daa1a786152a6"
|
resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz#4fdb1358667f6d68f25343353bd99bd11ee41006"
|
||||||
integrity sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==
|
integrity sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==
|
||||||
|
dependencies:
|
||||||
|
debug "~4.3.4"
|
||||||
|
ws "~8.11.0"
|
||||||
|
|
||||||
socket.io-parser@~4.2.1:
|
socket.io-parser@~4.2.4:
|
||||||
version "4.2.2"
|
version "4.2.4"
|
||||||
resolved "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz#1dd384019e25b7a3d374877f492ab34f2ad0d206"
|
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83"
|
||||||
integrity sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==
|
integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@socket.io/component-emitter" "~3.1.0"
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
debug "~4.3.1"
|
debug "~4.3.1"
|
||||||
|
|
||||||
socket.io@^4.4.1:
|
socket.io@^4.7.2:
|
||||||
version "4.5.4"
|
version "4.7.4"
|
||||||
resolved "https://registry.npmjs.org/socket.io/-/socket.io-4.5.4.tgz#a4513f06e87451c17013b8d13fdfaf8da5a86a90"
|
resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.7.4.tgz#2401a2d7101e4bdc64da80b140d5d8b6a8c7738b"
|
||||||
integrity sha512-m3GC94iK9MfIEeIBfbhJs5BqFibMtkRk8ZpKwG2QwxV0m/eEhPIV4ara6XCF1LWNAus7z58RodiZlAH71U3EhQ==
|
integrity sha512-DcotgfP1Zg9iP/dH9zvAQcWrE0TtbMVwXmlV4T4mqsvY+gw+LqUGPfx2AoVyRk0FLME+GQhufDMyacFmw7ksqw==
|
||||||
dependencies:
|
dependencies:
|
||||||
accepts "~1.3.4"
|
accepts "~1.3.4"
|
||||||
base64id "~2.0.0"
|
base64id "~2.0.0"
|
||||||
|
cors "~2.8.5"
|
||||||
debug "~4.3.2"
|
debug "~4.3.2"
|
||||||
engine.io "~6.2.1"
|
engine.io "~6.5.2"
|
||||||
socket.io-adapter "~2.4.0"
|
socket.io-adapter "~2.5.2"
|
||||||
socket.io-parser "~4.2.1"
|
socket.io-parser "~4.2.4"
|
||||||
|
|
||||||
sockjs@^0.3.24:
|
sockjs@^0.3.24:
|
||||||
version "0.3.24"
|
version "0.3.24"
|
||||||
|
@ -12778,10 +12794,10 @@ ws@^8.4.2:
|
||||||
resolved "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz#485074cc392689da78e1828a9ff23585e06cddd8"
|
resolved "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz#485074cc392689da78e1828a9ff23585e06cddd8"
|
||||||
integrity sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==
|
integrity sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==
|
||||||
|
|
||||||
ws@~8.2.3:
|
ws@~8.11.0:
|
||||||
version "8.2.3"
|
version "8.11.0"
|
||||||
resolved "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba"
|
resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143"
|
||||||
integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==
|
integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==
|
||||||
|
|
||||||
xml2js@^0.4.17:
|
xml2js@^0.4.17:
|
||||||
version "0.4.23"
|
version "0.4.23"
|
||||||
|
|
Loading…
Reference in New Issue