diff --git a/frontend/src/app/components/fhir-card/fhir-card.module.ts b/frontend/src/app/components/fhir-card/fhir-card.module.ts
index 257a2c64..aeb45bde 100644
--- a/frontend/src/app/components/fhir-card/fhir-card.module.ts
+++ b/frontend/src/app/components/fhir-card/fhir-card.module.ts
@@ -22,6 +22,7 @@ import {MedicationComponent} from './resources/medication/medication.component';
import {MedicationRequestComponent} from './resources/medication-request/medication-request.component';
import {ObservationComponent} from './resources/observation/observation.component';
import {OrganizationComponent} from './resources/organization/organization.component';
+import {PatientComponent} from './resources/patient/patient.component';
import {PractitionerComponent} from './resources/practitioner/practitioner.component';
import {ProcedureComponent} from './resources/procedure/procedure.component';
import {FhirCardComponent} from './fhir-card/fhir-card.component';
@@ -67,6 +68,7 @@ import { ObservationVisualizationComponent } from './common/observation-visualiz
MedicationRequestComponent,
ObservationComponent,
OrganizationComponent,
+ PatientComponent,
PractitionerComponent,
ProcedureComponent,
@@ -108,6 +110,7 @@ import { ObservationVisualizationComponent } from './common/observation-visualiz
MedicationRequestComponent,
ObservationComponent,
OrganizationComponent,
+ PatientComponent,
PractitionerComponent,
ProcedureComponent,
diff --git a/frontend/src/app/components/fhir-card/fhir-card/fhir-card.component.ts b/frontend/src/app/components/fhir-card/fhir-card/fhir-card.component.ts
index a753d602..bda48df1 100644
--- a/frontend/src/app/components/fhir-card/fhir-card/fhir-card.component.ts
+++ b/frontend/src/app/components/fhir-card/fhir-card/fhir-card.component.ts
@@ -29,6 +29,7 @@ import {LocationComponent} from '../resources/location/location.component';
import {OrganizationComponent} from '../resources/organization/organization.component';
import {ObservationComponent} from '../resources/observation/observation.component';
import {EncounterComponent} from '../resources/encounter/encounter.component';
+import {PatientComponent} from '../resources/patient/patient.component';
@Component({
@@ -152,6 +153,9 @@ export class FhirCardComponent implements OnInit, OnChanges {
case "Organization": {
return OrganizationComponent;
}
+ case "Patient": {
+ return PatientComponent;
+ }
case "Procedure": {
return ProcedureComponent;
}
diff --git a/frontend/src/app/components/fhir-card/resources/immunization/immunization.component.spec.ts b/frontend/src/app/components/fhir-card/resources/immunization/immunization.component.spec.ts
index b4a02742..a059f7a2 100644
--- a/frontend/src/app/components/fhir-card/resources/immunization/immunization.component.spec.ts
+++ b/frontend/src/app/components/fhir-card/resources/immunization/immunization.component.spec.ts
@@ -1,8 +1,8 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap';
+import { RouterTestingModule } from '@angular/router/testing';
import { ImmunizationComponent } from './immunization.component';
-import {NgbCollapseModule} from '@ng-bootstrap/ng-bootstrap';
-import {RouterTestingModule} from '@angular/router/testing';
describe('ImmunizationComponent', () => {
let component: ImmunizationComponent;
diff --git a/frontend/src/app/components/fhir-card/resources/immunization/immunization.component.ts b/frontend/src/app/components/fhir-card/resources/immunization/immunization.component.ts
index 916147a1..f372c154 100644
--- a/frontend/src/app/components/fhir-card/resources/immunization/immunization.component.ts
+++ b/frontend/src/app/components/fhir-card/resources/immunization/immunization.component.ts
@@ -1,13 +1,13 @@
-import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core';
-import {FhirCardComponentInterface} from '../../fhir-card/fhir-card-component-interface';
-import {Router, RouterModule} from '@angular/router';
-import {ImmunizationModel} from '../../../../../lib/models/resources/immunization-model';
-import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item';
+import { CommonModule } from "@angular/common";
+import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
+import { Router, RouterModule } from '@angular/router';
+import { NgbCollapseModule } from "@ng-bootstrap/ng-bootstrap";
import * as _ from "lodash";
-import {NgbCollapseModule} from "@ng-bootstrap/ng-bootstrap";
-import {CommonModule} from "@angular/common";
-import {BadgeComponent} from "../../common/badge/badge.component";
-import {TableComponent} from "../../common/table/table.component";
+import { ImmunizationModel } from '../../../../../lib/models/resources/immunization-model';
+import { BadgeComponent } from "../../common/badge/badge.component";
+import { TableRowItem, TableRowItemDataType } from '../../common/table/table-row-item';
+import { TableComponent } from "../../common/table/table.component";
+import { FhirCardComponentInterface } from '../../fhir-card/fhir-card-component-interface';
@Component({
standalone: true,
diff --git a/frontend/src/app/components/fhir-card/resources/patient/patient.component.html b/frontend/src/app/components/fhir-card/resources/patient/patient.component.html
new file mode 100644
index 00000000..6528e597
--- /dev/null
+++ b/frontend/src/app/components/fhir-card/resources/patient/patient.component.html
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
Basic Information
+
+ - Date of Birth: {{ displayModel.patient_birthdate | date:'mediumDate' }}
+ - Age: {{ displayModel.patient_age }}
+ - Gender: {{ displayModel.patient_gender | titlecase }}
+ - Birth Sex: {{ displayModel.birth_sex }}
+ - Marital Status: {{ displayModel.marital_status }}
+ - Race: {{ displayModel.race }}
+ - Ethnicity: {{ displayModel.ethnicity }}
+
+
+
+
+
+
Contact Information
+
+ - Address:
+
+
{{line}}
+
+
+ - Phone{{ displayModel.patient_phones.length > 0 ? '' : 's' }}:
+
+ - {{ phone.value }} ({{ phone.use }})
+
+
+ - Language{{ displayModel.communication_languages.length > 0 ? '' : 's' }}:
+
+ {{ language.display }}{{last ? '' : ', '}}
+
+
+
+
+
+
+
+
Additional Information
+
+ - Mother's Maiden Name: {{ displayModel.mothers_maiden_name }}
+ - Birth Place: {{ displayModel.birth_place }}
+ - Multiple Birth: {{ displayModel.multiple_birth ? 'Yes' : 'No' }}
+
+
+
+
+
+
Identifiers
+
+ - Medical Record Number:
{{ displayModel.mrn }}
+ - SSN:
{{ displayModel.ssn }}
+
+
0">
+ Other Identifiers:
+
+ -
+ {{ identifier.type }}:
{{ identifier.value }}
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/app/components/fhir-card/resources/patient/patient.component.scss b/frontend/src/app/components/fhir-card/resources/patient/patient.component.scss
new file mode 100644
index 00000000..f2b42c42
--- /dev/null
+++ b/frontend/src/app/components/fhir-card/resources/patient/patient.component.scss
@@ -0,0 +1,28 @@
+.bg-fasten-purple {
+ background-color: #5b47fb;
+}
+
+.patient-card {
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ padding: 20px;
+ margin-bottom: 20px;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+
+ h2 {
+ margin-top: 0;
+ margin-bottom: 15px;
+ font-size: 1.5rem;
+ }
+
+ .patient-info {
+ p {
+ margin: 5px 0;
+ }
+
+ strong {
+ font-weight: bold;
+ margin-right: 5px;
+ }
+ }
+}
diff --git a/frontend/src/app/components/fhir-card/resources/patient/patient.component.spec.ts b/frontend/src/app/components/fhir-card/resources/patient/patient.component.spec.ts
new file mode 100644
index 00000000..2aa9c306
--- /dev/null
+++ b/frontend/src/app/components/fhir-card/resources/patient/patient.component.spec.ts
@@ -0,0 +1,28 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap';
+
+import { PatientModel } from 'src/lib/models/resources/patient-model';
+import { PatientComponent } from './patient.component';
+
+describe('PatientComponent', () => {
+ let component: PatientComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [ PatientComponent, NgbCollapseModule, RouterTestingModule ]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(PatientComponent);
+ component = fixture.componentInstance;
+ component.displayModel = new PatientModel({});
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+});
diff --git a/frontend/src/app/components/fhir-card/resources/patient/patient.component.ts b/frontend/src/app/components/fhir-card/resources/patient/patient.component.ts
new file mode 100644
index 00000000..6f1eb3ff
--- /dev/null
+++ b/frontend/src/app/components/fhir-card/resources/patient/patient.component.ts
@@ -0,0 +1,31 @@
+import { CommonModule } from '@angular/common';
+import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
+import { Router, RouterModule } from '@angular/router';
+import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap';
+import { PatientModel } from '../../../../../lib/models/resources/patient-model';
+import { BadgeComponent } from '../../common/badge/badge.component';
+import { TableComponent } from '../../common/table/table.component';
+import { FhirCardComponentInterface } from '../../fhir-card/fhir-card-component-interface';
+
+@Component({
+ standalone: true,
+ imports: [NgbCollapseModule, CommonModule, BadgeComponent, TableComponent, RouterModule],
+ selector: 'fhir-patient',
+ templateUrl: './patient.component.html',
+ styleUrls: ['./patient.component.scss']
+})
+export class PatientComponent implements OnInit, FhirCardComponentInterface {
+ @Input() displayModel: PatientModel;
+ @Input() showDetails: boolean = true;
+ @Input() isCollapsed: boolean = false;
+
+ constructor(public changeRef: ChangeDetectorRef, public router: Router) { }
+
+ ngOnInit(): void {
+ }
+
+ markForCheck(){
+ this.changeRef.markForCheck()
+ }
+
+}
diff --git a/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-generic-resource.component.html b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-generic-resource.component.html
index b1b50d1e..628d8110 100644
--- a/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-generic-resource.component.html
+++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-generic-resource.component.html
@@ -1,7 +1,7 @@
p.name?.[0] },
+ { title: 'DOB', versions: '*', getter: p => p.birthDate, format: 'date' },
+ ]
+}
diff --git a/frontend/src/app/components/fhir-datatable/datatable-generic-resource/utils.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/utils.ts
index 23b486d0..26a5421e 100644
--- a/frontend/src/app/components/fhir-datatable/datatable-generic-resource/utils.ts
+++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/utils.ts
@@ -14,6 +14,19 @@ export function getPath(obj, path = ""): string {
}
export const FORMATTERS = {
+ age: (patientDOB: number|string): number => {
+ if (patientDOB == null) { return NaN; }
+ const dob = typeof patientDOB === 'string' ? new Date(patientDOB) : new Date(patientDOB);
+ const today = new Date();
+ let age = today.getFullYear() - dob.getFullYear();
+ const monthDiff = today.getMonth() - dob.getMonth();
+
+ if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < dob.getDate())) {
+ age--;
+ }
+
+ return age;
+ },
date: (str) => str ? moment(str).format('YYYY-MM-DD') : '',
time: (str) => str ? moment(str).format('HH:mm') : '',
dateTime: (str) => str ? moment(str).format('YYYY-MM-DD - h:mm a') : '',
@@ -24,15 +37,13 @@ export const FORMATTERS = {
if(codeableConcept.text) return codeableConcept.text
return codeableConcept.coding && codeableConcept.coding[0] ? `${codeableConcept.coding[0].code}: ${codeableConcept.coding[0].display ? codeableConcept.coding[0].display : ''}` : ''
},
- address: (address) => {
- if(!address) return ''
+ address: (address): Array => {
+ if(!address) return []
var addressParts = []
- if(address.line) addressParts.push(address.line.join(', '))
- if(address.city) addressParts.push(address.city)
- if(address.state) addressParts.push(address.state)
- if(address.postalCode) addressParts.push(address.postalCode)
+ if(address.line) addressParts.push(...address.line)
+ addressParts.push(`${address.city}, ${address.state} ${address.postalCode}`)
if(address.country) addressParts.push(address.country)
- return addressParts.join(', ')
+ return addressParts
},
humanName: (humanName) => {
if(!humanName) return ''
diff --git a/frontend/src/app/components/fhir-datatable/fhir-datatable.module.ts b/frontend/src/app/components/fhir-datatable/fhir-datatable.module.ts
index 7953a01a..42cdd144 100644
--- a/frontend/src/app/components/fhir-datatable/fhir-datatable.module.ts
+++ b/frontend/src/app/components/fhir-datatable/fhir-datatable.module.ts
@@ -25,7 +25,7 @@ import {DatatableMedicationRequestComponent} from './datatable-generic-resource/
import {DatatableNutritionOrderComponent} from './datatable-generic-resource/datatable-nutrition-order.component';
import {DatatableObservationComponent} from './datatable-generic-resource/datatable-observation.component';
import {DatatableOrganizationComponent} from './datatable-generic-resource/datatable-organization.component';
-import {ListPatientComponent} from './list-patient/list-patient.component';
+import {DatatablePatientComponent} from './datatable-generic-resource/datatable-patient.component';
import {DatatablePractitionerComponent} from './datatable-generic-resource/datatable-practitioner.component';
import {DatatableProcedureComponent} from './datatable-generic-resource/datatable-procedure.component';
import {DatatableServiceRequestComponent} from './datatable-generic-resource/datatable-service-request.component';
@@ -65,7 +65,7 @@ import {NgxDatatableModule} from '@swimlane/ngx-datatable';
DatatableNutritionOrderComponent,
DatatableObservationComponent,
DatatableOrganizationComponent,
- ListPatientComponent,
+ DatatablePatientComponent,
DatatablePractitionerComponent,
DatatableProcedureComponent,
DatatableServiceRequestComponent,
@@ -99,7 +99,7 @@ import {NgxDatatableModule} from '@swimlane/ngx-datatable';
DatatableNutritionOrderComponent,
DatatableObservationComponent,
DatatableOrganizationComponent,
- ListPatientComponent,
+ DatatablePatientComponent,
DatatablePractitionerComponent,
DatatableProcedureComponent,
DatatableServiceRequestComponent,
diff --git a/frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable.component.ts b/frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable.component.ts
index de931a5d..70ec5f49 100644
--- a/frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable.component.ts
+++ b/frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable.component.ts
@@ -27,6 +27,7 @@ import {DatatableMedicationRequestComponent} from '../datatable-generic-resource
import {DatatableNutritionOrderComponent} from '../datatable-generic-resource/datatable-nutrition-order.component';
import {DatatableObservationComponent} from '../datatable-generic-resource/datatable-observation.component';
import {DatatableOrganizationComponent} from '../datatable-generic-resource/datatable-organization.component';
+import {DatatablePatientComponent} from '../datatable-generic-resource/datatable-patient.component';
import {DatatablePractitionerComponent} from '../datatable-generic-resource/datatable-practitioner.component';
import {DatatableProcedureComponent} from '../datatable-generic-resource/datatable-procedure.component';
import {DatatableServiceRequestComponent} from '../datatable-generic-resource/datatable-service-request.component';
@@ -162,6 +163,9 @@ export class FhirDatatableComponent implements OnInit, OnChanges {
case "Organization": {
return DatatableOrganizationComponent;
}
+ case "Patient": {
+ return DatatablePatientComponent;
+ }
case "Practitioner": {
return DatatablePractitionerComponent;
}
diff --git a/frontend/src/app/components/form-request-health-system/form-request-health-system.component.spec.ts b/frontend/src/app/components/form-request-health-system/form-request-health-system.component.spec.ts
index 596942e5..ede05e7f 100644
--- a/frontend/src/app/components/form-request-health-system/form-request-health-system.component.spec.ts
+++ b/frontend/src/app/components/form-request-health-system/form-request-health-system.component.spec.ts
@@ -1,4 +1,9 @@
+import { HttpClient } from '@angular/common/http';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormsModule } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { HTTP_CLIENT_TOKEN } from '../../dependency-injection';
import { FormRequestHealthSystemComponent } from './form-request-health-system.component';
@@ -8,7 +13,15 @@ describe('FormRequestHealthSystemComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ FormRequestHealthSystemComponent ]
+ declarations: [ FormRequestHealthSystemComponent ],
+ imports: [HttpClientTestingModule, FormsModule],
+ providers: [
+ NgbActiveModal,
+ {
+ provide: HTTP_CLIENT_TOKEN,
+ useClass: HttpClient,
+ },
+ ]
})
.compileComponents();
diff --git a/frontend/src/app/components/form-request-health-system/form-request-health-system.component.ts b/frontend/src/app/components/form-request-health-system/form-request-health-system.component.ts
index 6dc49ee3..805d943f 100644
--- a/frontend/src/app/components/form-request-health-system/form-request-health-system.component.ts
+++ b/frontend/src/app/components/form-request-health-system/form-request-health-system.component.ts
@@ -1,10 +1,7 @@
import { Component, OnInit } from '@angular/core';
-import {NgbActiveModal, NgbModal} from '@ng-bootstrap/ng-bootstrap';
-import {SupportRequest} from '../../models/fasten/support-request';
-import {FormRequestHealthSystem} from '../../models/fasten/form-request-health-system';
-import {environment} from '../../../environments/environment';
-import {versionInfo} from '../../../environments/versions';
-import {FastenApiService} from '../../services/fasten-api.service';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { FormRequestHealthSystem } from '../../models/fasten/form-request-health-system';
+import { FastenApiService } from '../../services/fasten-api.service';
@Component({
selector: 'app-form-request-health-system',
diff --git a/frontend/src/custom.scss b/frontend/src/custom.scss
index f5f2e508..9f237790 100644
--- a/frontend/src/custom.scss
+++ b/frontend/src/custom.scss
@@ -264,6 +264,9 @@ app-medical-sources-filter > .az-content-left-components:hover{
background-color: $indigo !important;
color: #fff;
}
+.fhir-resource-datatable .datatable-body-row {
+ cursor: pointer;
+}
// Callouts
diff --git a/frontend/src/lib/models/resources/patient-model.spec.ts b/frontend/src/lib/models/resources/patient-model.spec.ts
index 033655c4..0a1baada 100644
--- a/frontend/src/lib/models/resources/patient-model.spec.ts
+++ b/frontend/src/lib/models/resources/patient-model.spec.ts
@@ -1,7 +1,148 @@
import { PatientModel } from './patient-model';
describe('PatientModel', () => {
+ let patientModel: PatientModel;
+ let mockFhirResource: any;
+
+ beforeEach(() => {
+ mockFhirResource = {
+ id: '123',
+ name: [{ given: ['John'], family: 'Doe' }],
+ birthDate: '1990-01-01',
+ gender: 'male',
+ telecom: [
+ { system: 'phone', value: '123-456-7890', use: 'home' },
+ { system: 'email', value: 'john@example.com', use: 'work' }
+ ],
+ communication: [
+ { language: { coding: [{ system: 'urn:ietf:bcp:47', code: 'en', display: 'English' }] } }
+ ],
+ maritalStatus: { text: 'Married' },
+ extension: [
+ {
+ url: 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-birthsex',
+ valueCode: 'M'
+ },
+ {
+ url: 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-race',
+ extension: [{ url: 'text', valueString: 'White' }]
+ },
+ {
+ url: 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity',
+ extension: [{ url: 'text', valueString: 'Not Hispanic or Latino' }]
+ },
+ {
+ url: 'http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName',
+ valueString: 'Smith'
+ },
+ {
+ url: 'http://hl7.org/fhir/StructureDefinition/patient-birthPlace',
+ valueAddress: { city: 'New York', state: 'NY', country: 'USA' }
+ }
+ ],
+ identifier: [
+ { system: 'http://hl7.org/fhir/sid/us-ssn', value: '123-45-6789' },
+ { type: { coding: [{ code: 'MR' }] }, value: 'MRN12345' },
+ { type: { text: 'Driver\'s License', coding: [{ display: 'DL' }] }, system: 'urn:oid:2.16.840.1.113883.4.3.25', value: 'S99969890' }
+ ]
+ };
+ patientModel = new PatientModel(mockFhirResource);
+ });
+
it('should create an instance', () => {
expect(new PatientModel({})).toBeTruthy();
});
+
+ it('should return the correct id', () => {
+ expect(patientModel.id).toBe('123');
+ });
+
+ it('should return the formatted name', () => {
+ expect(patientModel.patient_name).toBe('John Doe');
+ });
+
+ it('should return the correct birth date', () => {
+ expect(patientModel.patient_birthdate).toBe('1990-01-01');
+ });
+
+ // it('should return the correct age', () => {
+
+
+ // const currentDate = new Date('2022-02-01');
+ // jasmine.clock().install();
+ // jasmine.clock().mockDate(currentDate);
+
+ // expect(FORMATTERS.age('1990-01-01')).toBe(32);
+ // expect(patientModel.patient_birthdate).toBe('1990-01-01');
+ // expect(patientModel.patient_age).toBe(32);
+ // jasmine.clock().uninstall();
+ // });
+
+ it('should return the correct gender', () => {
+ expect(patientModel.patient_gender).toBe('male');
+ });
+
+ it('should return the correct telecom information', () => {
+ expect(patientModel.patient_phones).toEqual([
+ { system: 'phone', value: '123-456-7890', use: 'home' },
+ { system: 'email', value: 'john@example.com', use: 'work' }
+ ]);
+ });
+
+ it('should return the correct language information', () => {
+ expect(patientModel.communication_languages).toEqual([
+ { system: 'urn:ietf:bcp:47', code: 'en', display: 'English' }
+ ]);
+ });
+
+ it('should return the correct birth sex', () => {
+ expect(patientModel.birth_sex).toBe('M');
+ });
+
+ it('should return the correct marital status', () => {
+ expect(patientModel.marital_status).toBe('Married');
+ });
+
+ it('should return the correct race', () => {
+ expect(patientModel.race).toBe('White');
+ });
+
+ it('should return the correct ethnicity', () => {
+ expect(patientModel.ethnicity).toBe('Not Hispanic or Latino');
+ });
+
+ it('should return the correct mother\'s maiden name', () => {
+ expect(patientModel.mothers_maiden_name).toBe('Smith');
+ });
+
+ it('should return the correct birth place', () => {
+ expect(patientModel.birth_place).toBe('New York, NY, USA');
+ });
+
+ it('should correctly parse other identifiers', () => {
+ expect(patientModel.identifiers).toEqual([
+ { type: 'Driver\'s License', system: 'urn:oid:2.16.840.1.113883.4.3.25', value: 'S99969890' }
+ ]);
+ });
+
+ // it('should return the correct extensions', () => {
+ // const extensions = patientModel.getExtensions(mockFhirResource);
+ // expect(extensions).toContainEqual({ url: 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-birthsex', value: 'M' });
+ // expect(extensions).toContainEqual({ url: 'http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName', value: 'Smith' });
+ // });
+
+ it('should return the correct SSN', () => {
+ expect(patientModel.ssn).toBe('123-45-6789');
+ });
+
+ it('should return the correct MRN', () => {
+ const mockResourceWithMRN = {
+ ...mockFhirResource,
+ extension: [
+ ...mockFhirResource.extension,
+ { url: 'http://hl7.org/fhir/StructureDefinition/patient-mrn', valueString: 'MRN54321' }
+ ]
+ };
+ expect(patientModel.getMRN(mockResourceWithMRN)).toBe('MRN54321');
+ });
});
diff --git a/frontend/src/lib/models/resources/patient-model.ts b/frontend/src/lib/models/resources/patient-model.ts
index dc0b934e..43b92e8e 100644
--- a/frontend/src/lib/models/resources/patient-model.ts
+++ b/frontend/src/lib/models/resources/patient-model.ts
@@ -1,64 +1,161 @@
-import {fhirVersions, ResourceType} from '../constants';
import * as _ from "lodash";
-import {CodableConceptModel, hasValue} from '../datatypes/codable-concept-model';
-import {ReferenceModel} from '../datatypes/reference-model';
-import {CodingModel} from '../datatypes/coding-model';
-import {FastenDisplayModel} from '../fasten/fasten-display-model';
-import {FastenOptions} from '../fasten/fasten-options';
+import { FORMATTERS } from 'src/app/components/fhir-datatable/datatable-generic-resource/utils';
+import { fhirVersions, ResourceType } from '../constants';
+import { FastenDisplayModel } from '../fasten/fasten-display-model';
+import { FastenOptions } from '../fasten/fasten-options';
export class PatientModel extends FastenDisplayModel {
id: string|undefined
patient_name: string|undefined
patient_birthdate: string|undefined
+ patient_age: number|undefined
patient_gender: string|undefined
patient_contact: string|undefined
- patient_address: string|undefined
- patient_phones: string|undefined
- communication_language: string|undefined
+ patient_address: Array|undefined
+ patient_phones: Array|undefined
+ communication_languages: Array|undefined
has_communication_language: boolean|undefined
has_patient_phones: boolean|undefined
active: string|undefined
active_status: string|undefined
is_deceased: boolean|undefined
deceased_date: string|undefined
+ birth_sex: string|undefined
+ marital_status: string|undefined
+ race: string|undefined
+ ethnicity: string|undefined
+ mothers_maiden_name: string|undefined
+ birth_place: string|undefined
+ multiple_birth: boolean|undefined
+ identifiers: Array|undefined
+ extensions: Array|undefined
+ ssn: string|undefined
+ mrn: string|undefined
constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) {
super(fastenOptions)
this.source_resource_type = ResourceType.Patient
- this.id = this.getId(fhirResource);
- this.patient_name = this.getNames(fhirResource);
- this.patient_birthdate = this.getBirthDate(fhirResource);
- this.patient_gender = this.getGender(fhirResource);
+ this.id = _.get(fhirResource, 'id');
+ this.patient_name = FORMATTERS.humanName(_.get(fhirResource, 'name.0', null));
+ this.patient_birthdate = _.get(fhirResource, 'birthDate');
+ this.patient_age = FORMATTERS.age(this.patient_birthdate)
+ this.patient_gender = _.get(fhirResource, 'gender');
this.patient_contact = _.get(fhirResource, 'contact[0]');
- this.patient_address = _.get(fhirResource, 'address[0]');
- this.patient_phones = _.get(fhirResource, 'telecom', []).filter(
- (telecom: any) => telecom.system === 'phone',
- );
- let communicationLanguage = _.get(fhirResource, 'communication', [])
- .filter((item: any) => Boolean(_.get(item, 'language.coding', null)))
- .map((item: any) => item.language.coding);
- this.communication_language = _.get(communicationLanguage, '0', []);
- this.has_communication_language = !_.isEmpty(communicationLanguage);
+ this.patient_address = FORMATTERS.address(_.get(fhirResource, 'address[0]'));
+ this.patient_phones = this.getTelecom(fhirResource);
+ this.communication_languages = this.getCommunicationLanguages(fhirResource);
+ this.has_communication_language = !_.isEmpty(this.communication_languages);
this.has_patient_phones = !_.isEmpty(this.patient_phones);
this.active = _.get(fhirResource, 'active', false);
this.active_status = this.active ? 'active' : 'inactive';
let deceasedBoolean = _.get(fhirResource, 'deceasedBoolean', false);
this.deceased_date = _.get(fhirResource, 'deceasedDateTime');
this.is_deceased = deceasedBoolean || this.deceased_date;
+ this.birth_sex = this.getBirthSex(fhirResource);
+ this.marital_status = _.get(fhirResource, 'maritalStatus.text');
+ this.race = this.getRace(fhirResource);
+ this.ethnicity = this.getEthnicity(fhirResource);
+ this.mothers_maiden_name = this.getMothersMaidenName(fhirResource);
+ this.birth_place = this.getBirthPlace(fhirResource);
+ this.multiple_birth = _.get(fhirResource, 'multipleBirthBoolean', false);
+ this.identifiers = [];
+ this.parseIdentifiers(fhirResource);
+ this.extensions = this.getExtensions(fhirResource);
+ this.ssn = this.ssn || this.getSSN(fhirResource);
+ this.mrn = this.mrn || this.getMRN(fhirResource);
}
- getId(fhirResource: any) {
- return _.get(fhirResource, 'id');
+
+ getTelecom(fhirResource: any) {
+ return _.get(fhirResource, 'telecom', []).map((telecom: any) => ({
+ system: telecom.system,
+ value: telecom.value,
+ use: telecom.use
+ }));
}
- getNames(fhirResource: any) {
- return _.get(fhirResource, 'name.0', null);
+
+ getCommunicationLanguages(fhirResource: any) {
+ return _.get(fhirResource, 'communication', [])
+ .filter((item: any) => Boolean(_.get(item, 'language.coding', null)))
+ .map((item: any) => item.language.coding[0]);
}
- getBirthDate(fhirResource: any) {
- return _.get(fhirResource, 'birthDate');
+
+ getBirthSex(fhirResource: any) {
+ const extension = fhirResource.extension?.find((ext: any) =>
+ ext.url === "http://hl7.org/fhir/us/core/StructureDefinition/us-core-birthsex"
+ );
+ return extension?.valueCode;
}
- getGender(fhirResource: any) {
- return _.get(fhirResource, 'gender');
+
+ getRace(fhirResource: any) {
+ const extension = fhirResource.extension?.find((ext: any) =>
+ ext.url === "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race"
+ );
+ return extension?.extension?.find((ext: any) => ext.url === "text")?.valueString;
+ }
+
+ getEthnicity(fhirResource: any) {
+ const extension = fhirResource.extension?.find((ext: any) =>
+ ext.url === "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity"
+ );
+ return extension?.extension?.find((ext: any) => ext.url === "text")?.valueString;
+ }
+
+ getMothersMaidenName(fhirResource: any) {
+ const extension = fhirResource.extension?.find((ext: any) =>
+ ext.url === "http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName"
+ );
+ return extension?.valueString;
+ }
+
+ getBirthPlace(fhirResource: any) {
+ const extension = fhirResource.extension?.find((ext: any) =>
+ ext.url === "http://hl7.org/fhir/StructureDefinition/patient-birthPlace"
+ );
+ if (extension?.valueAddress) {
+ const address = extension.valueAddress;
+ return `${address.city}, ${address.state}, ${address.country}`;
+ }
+ return undefined;
+ }
+
+ parseIdentifiers(fhirResource: any) {
+ const identifiers = _.get(fhirResource, 'identifier', []);
+ identifiers.forEach((identifier: any) => {
+ if (identifier.system === "http://hl7.org/fhir/sid/us-ssn") {
+ this.ssn = identifier.value;
+ } else if (identifier.type?.coding?.some((coding: any) => coding.code === "MR")) {
+ this.mrn = identifier.value;
+ } else if (identifier.type) {
+ this.identifiers.push({
+ type: identifier.type.text || identifier.type.coding[0].display,
+ system: identifier.system,
+ value: identifier.value
+ });
+ }
+ });
+ }
+
+ getExtensions(fhirResource: any) {
+ return fhirResource.extension?.map((ext: any) => ({
+ url: ext.url,
+ value: ext.valueDecimal || ext.valueString || ext.valueCode || JSON.stringify(ext.extension)
+ }));
+ }
+
+ getSSN(fhirResource: any) {
+ const extension = fhirResource.extension?.find((ext: any) =>
+ ext.url === "http://hl7.org/fhir/StructureDefinition/us-core-ssn"
+ );
+ return extension?.valueString;
+ }
+
+ getMRN(fhirResource: any) {
+ const extension = fhirResource.extension?.find((ext: any) =>
+ ext.url === "http://hl7.org/fhir/StructureDefinition/patient-mrn"
+ );
+ return extension?.valueString;
}
}