Skip to content
This repository was archived by the owner on Jan 9, 2023. It is now read-only.

Commit 1c61301

Browse files
authored
Merge pull request #1880 from HospitalRun/model-updates
Model updates
2 parents ba9440e + 547142e commit 1c61301

18 files changed

+113
-77
lines changed

package.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
"react-scripts": "3.4.0",
3030
"redux": "~4.0.5",
3131
"redux-thunk": "~2.3.0",
32-
"typescript": "~3.8.2"
32+
"shortid": "^2.2.15",
33+
"typescript": "~3.8.2",
34+
"uuid": "^7.0.1"
3335
},
3436
"repository": {
3537
"type": "git",
@@ -56,6 +58,8 @@
5658
"@types/react-router": "~5.1.2",
5759
"@types/react-router-dom": "~5.1.0",
5860
"@types/redux-mock-store": "~1.0.1",
61+
"@types/shortid": "^0.0.29",
62+
"@types/uuid": "^7.0.0",
5963
"@typescript-eslint/eslint-plugin": "~2.22.0",
6064
"@typescript-eslint/parser": "~2.22.0",
6165
"commitizen": "~4.0.3",

src/__tests__/HospitalRun.test.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ describe('HospitalRun', () => {
8484
givenName: 'test',
8585
familyName: 'test',
8686
suffix: 'test',
87-
friendlyId: 'P00001',
87+
code: 'P00001',
8888
} as Patient
8989

9090
mockedPatientRepository.find.mockResolvedValue(patient)
@@ -163,7 +163,7 @@ describe('HospitalRun', () => {
163163
givenName: 'test',
164164
familyName: 'test',
165165
suffix: 'test',
166-
friendlyId: 'P00001',
166+
code: 'P00001',
167167
} as Patient
168168

169169
mockedPatientRepository.find.mockResolvedValue(patient)

src/__tests__/clients/db/AppointmentRepository.test.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import AppointmentRepository from 'clients/db/AppointmentRepository'
22
import { appointments } from 'config/pouchdb'
33
import Appointment from 'model/Appointment'
4-
import { fromUnixTime } from 'date-fns'
4+
5+
const uuidV4Regex = /^[A-F\d]{8}-[A-F\d]{4}-4[A-F\d]{3}-[89AB][A-F\d]{3}-[A-F\d]{12}$/i
56

67
describe('Appointment Repository', () => {
78
it('should create a repository with the database set to the appointments database', () => {
@@ -24,14 +25,23 @@ describe('Appointment Repository', () => {
2425
})
2526

2627
describe('save', () => {
27-
it('should create an id that is a timestamp', async () => {
28+
it('should create an id that is a uuid', async () => {
2829
const newAppointment = await AppointmentRepository.save({
2930
patientId: 'id',
3031
} as Appointment)
3132

32-
expect(fromUnixTime(parseInt(newAppointment.id, 10)).getTime() > 0).toBeTruthy()
33+
expect(uuidV4Regex.test(newAppointment.id)).toBeTruthy()
3334

3435
await appointments.remove(await appointments.get(newAppointment.id))
3536
})
37+
38+
it('should generate a timestamp for created date and last updated date', async () => {
39+
const newAppointment = await AppointmentRepository.save({
40+
patientId: 'id',
41+
} as Appointment)
42+
43+
expect(newAppointment.createdAt).toBeDefined()
44+
expect(newAppointment.updatedAt).toBeDefined()
45+
})
3646
})
3747
})

src/__tests__/clients/db/PatientRepository.test.ts

+57-21
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { patients } from 'config/pouchdb'
22
import PatientRepository from 'clients/db/PatientRepository'
33
import Patient from 'model/Patient'
4-
import { fromUnixTime } from 'date-fns'
4+
import shortid from 'shortid'
5+
import { getTime, isAfter } from 'date-fns'
6+
7+
const uuidV4Regex = /^[A-F\d]{8}-[A-F\d]{4}-4[A-F\d]{3}-[89AB][A-F\d]{3}-[A-F\d]{12}$/i
58

69
async function removeAllDocs() {
710
// eslint-disable-next-line
@@ -36,22 +39,22 @@ describe('patient repository', () => {
3639
await removeAllDocs()
3740
})
3841

39-
it('should return all records that friendly ids match search text', async () => {
40-
// same full name to prove that it is finding by friendly id
41-
const expectedFriendlyId = 'P00001'
42-
await patients.put({ _id: 'someId1', friendlyId: expectedFriendlyId, fullName: 'test test' })
43-
await patients.put({ _id: 'someId2', friendlyId: 'P00002', fullName: 'test test' })
42+
it('should return all records that patient code matches search text', async () => {
43+
// same full name to prove that it is finding by patient code
44+
const expectedPatientCode = 'P00001'
45+
await patients.put({ _id: 'someId1', code: expectedPatientCode, fullName: 'test test' })
46+
await patients.put({ _id: 'someId2', code: 'P00002', fullName: 'test test' })
4447

45-
const result = await PatientRepository.search(expectedFriendlyId)
48+
const result = await PatientRepository.search(expectedPatientCode)
4649

4750
expect(result).toHaveLength(1)
48-
expect(result[0].friendlyId).toEqual(expectedFriendlyId)
51+
expect(result[0].code).toEqual(expectedPatientCode)
4952
})
5053

5154
it('should return all records that fullName contains search text', async () => {
52-
await patients.put({ _id: 'id3333', friendlyId: 'P00002', fullName: 'blh test test blah' })
53-
await patients.put({ _id: 'id4444', friendlyId: 'P00001', fullName: 'test test' })
54-
await patients.put({ _id: 'id5555', friendlyId: 'P00003', fullName: 'not found' })
55+
await patients.put({ _id: 'id3333', code: 'P00002', fullName: 'blh test test blah' })
56+
await patients.put({ _id: 'id4444', code: 'P00001', fullName: 'test test' })
57+
await patients.put({ _id: 'id5555', code: 'P00003', fullName: 'not found' })
5558

5659
const result = await PatientRepository.search('test test')
5760

@@ -61,8 +64,8 @@ describe('patient repository', () => {
6164
})
6265

6366
it('should match search criteria with case insensitive match', async () => {
64-
await patients.put({ _id: 'id6666', friendlyId: 'P00001', fullName: 'test test' })
65-
await patients.put({ _id: 'id7777', friendlyId: 'P00002', fullName: 'not found' })
67+
await patients.put({ _id: 'id6666', code: 'P00001', fullName: 'test test' })
68+
await patients.put({ _id: 'id7777', code: 'P00002', fullName: 'not found' })
6669

6770
const result = await PatientRepository.search('TEST TEST')
6871

@@ -92,30 +95,41 @@ describe('patient repository', () => {
9295
await removeAllDocs()
9396
})
9497

95-
it('should generate an id that is a timestamp for the patient', async () => {
98+
it('should generate an id that is a uuid for the patient', async () => {
9699
const newPatient = await PatientRepository.save({
97100
fullName: 'test test',
98101
} as Patient)
99102

100-
expect(fromUnixTime(parseInt(newPatient.id, 10)).getTime() > 0).toBeTruthy()
103+
expect(uuidV4Regex.test(newPatient.id)).toBeTruthy()
101104
})
102105

103-
it('should generate a friendly id', async () => {
106+
it('should generate a patient code', async () => {
104107
const newPatient = await PatientRepository.save({
105108
fullName: 'test1 test1',
106109
} as Patient)
107110

108-
expect(newPatient.friendlyId).toEqual('P00001')
111+
expect(shortid.isValid(newPatient.code)).toBeTruthy()
109112
})
110113

111-
it('should sequentially generate a friendly id', async () => {
112-
await patients.put({ _id: 'id9999', friendlyId: 'P00001' })
114+
it('should generate a timestamp for created date and last updated date', async () => {
115+
const newPatient = await PatientRepository.save({
116+
fullName: 'test1 test1',
117+
} as Patient)
118+
119+
expect(newPatient.createdAt).toBeDefined()
120+
expect(newPatient.updatedAt).toBeDefined()
121+
})
113122

123+
it('should override the created date and last updated date even if one was passed in', async () => {
124+
const unexpectedTime = new Date(2020, 2, 1).toISOString()
114125
const newPatient = await PatientRepository.save({
115-
fullName: 'test3 test3',
126+
fullName: 'test1 test1',
127+
createdAt: unexpectedTime,
128+
updatedAt: unexpectedTime,
116129
} as Patient)
117130

118-
expect(newPatient.friendlyId).toEqual('P00002')
131+
expect(newPatient.createdAt).not.toEqual(unexpectedTime)
132+
expect(newPatient.updatedAt).not.toEqual(unexpectedTime)
119133
})
120134
})
121135

@@ -164,6 +178,28 @@ describe('patient repository', () => {
164178
expect(updatedPatient.fullName).toEqual(existingPatient.fullName)
165179
expect(updatedPatient.givenName).toEqual('givenName')
166180
})
181+
182+
it('should update the last updated date', async () => {
183+
const time = new Date(2020, 1, 1).toISOString()
184+
await patients.put({ _id: 'id2222222', createdAt: time, updatedAt: time })
185+
const existingPatient = await PatientRepository.find('id2222222')
186+
187+
const updatedPatient = await PatientRepository.saveOrUpdate(existingPatient)
188+
189+
expect(
190+
isAfter(new Date(updatedPatient.updatedAt), new Date(updatedPatient.createdAt)),
191+
).toBeTruthy()
192+
expect(updatedPatient.updatedAt).not.toEqual(existingPatient.updatedAt)
193+
})
194+
195+
it('should not update the created date', async () => {
196+
const time = getTime(new Date(2020, 1, 1))
197+
await patients.put({ _id: 'id111111', createdAt: time, updatedAt: time })
198+
const existingPatient = await PatientRepository.find('id111111')
199+
const updatedPatient = await PatientRepository.saveOrUpdate(existingPatient)
200+
201+
expect(updatedPatient.createdAt).toEqual(existingPatient.createdAt)
202+
})
167203
})
168204

169205
describe('delete', () => {

src/__tests__/patients/GeneralInformation.test.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ describe('General Information, without isEditable', () => {
4242
phoneNumber: 'phoneNumber',
4343
4444
address: 'address',
45-
friendlyId: 'P00001',
45+
code: 'P00001',
4646
dateOfBirth: startOfDay(subYears(new Date(), 30)).toISOString(),
4747
isApproximateDateOfBirth: false,
4848
} as Patient
@@ -197,7 +197,7 @@ describe('General Information, isEditable', () => {
197197
phoneNumber: 'phoneNumber',
198198
199199
address: 'address',
200-
friendlyId: 'P00001',
200+
code: 'P00001',
201201
dateOfBirth: startOfDay(subYears(new Date(), 30)).toISOString(),
202202
isApproximateDateOfBirth: false,
203203
} as Patient

src/__tests__/patients/edit/EditPatient.test.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ describe('Edit Patient', () => {
3333
phoneNumber: 'phoneNumber',
3434
3535
address: 'address',
36-
friendlyId: 'P00001',
36+
code: 'P00001',
3737
dateOfBirth: new Date().toISOString(),
3838
} as Patient
3939

src/__tests__/patients/list/Patients.test.tsx

+2-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const middlewares = [thunk]
1717
const mockStore = configureStore(middlewares)
1818

1919
describe('Patients', () => {
20-
const patients = [{ id: '123', fullName: 'test test', friendlyId: 'P12345' }]
20+
const patients = [{ id: '123', fullName: 'test test', code: 'P12345' }]
2121
const mockedPatientRepository = mocked(PatientRepository, true)
2222

2323
const setup = (isLoading?: boolean) => {
@@ -67,9 +67,7 @@ describe('Patients', () => {
6767

6868
const patientListItems = wrapper.find(ListItem)
6969
expect(patientListItems).toHaveLength(1)
70-
expect(patientListItems.at(0).text()).toEqual(
71-
`${patients[0].fullName} (${patients[0].friendlyId})`,
72-
)
70+
expect(patientListItems.at(0).text()).toEqual(`${patients[0].fullName} (${patients[0].code})`)
7371
})
7472

7573
it('should add a "New Patient" button to the button tool bar', () => {

src/__tests__/patients/view/ViewPatient.test.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ describe('ViewPatient', () => {
3737
phoneNumber: 'phoneNumber',
3838
3939
address: 'address',
40-
friendlyId: 'P00001',
40+
code: 'P00001',
4141
dateOfBirth: new Date().toISOString(),
4242
} as Patient
4343

@@ -90,7 +90,7 @@ describe('ViewPatient', () => {
9090
await setup()
9191
})
9292
expect(titleUtil.default).toHaveBeenCalledWith(
93-
`${patient.givenName} ${patient.familyName} ${patient.suffix} (${patient.friendlyId})`,
93+
`${patient.givenName} ${patient.familyName} ${patient.suffix} (${patient.code})`,
9494
)
9595
})
9696

src/__tests__/scheduling/appointments/edit/EditAppointment.test.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ describe('Edit Appointment', () => {
4646
phoneNumber: 'phoneNumber',
4747
4848
address: 'address',
49-
friendlyId: 'P00001',
49+
code: 'P00001',
5050
dateOfBirth: new Date().toISOString(),
5151
} as Patient
5252

src/clients/db/PatientRepository.ts

+6-26
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
1+
import shortid from 'shortid'
12
import Patient from '../../model/Patient'
23
import Repository from './Repository'
34
import { patients } from '../../config/pouchdb'
45

5-
const formatFriendlyId = (prefix: string, sequenceNumber: string) => `${prefix}${sequenceNumber}`
6+
const formatPatientCode = (prefix: string, sequenceNumber: string) => `${prefix}${sequenceNumber}`
67

7-
const generateSequenceNumber = (currentNumber: number): string => {
8-
const newNumber = currentNumber + 1
9-
if (newNumber < 10000) {
10-
return newNumber.toString().padStart(5, '0')
11-
}
12-
13-
return newNumber.toString()
14-
}
8+
const getPatientCode = (): string => formatPatientCode('P-', shortid.generate())
159

1610
export class PatientRepository extends Repository<Patient> {
1711
constructor() {
@@ -28,30 +22,16 @@ export class PatientRepository extends Repository<Patient> {
2822
},
2923
},
3024
{
31-
friendlyId: text,
25+
code: text,
3226
},
3327
],
3428
},
3529
})
3630
}
3731

38-
async getFriendlyId(): Promise<string> {
39-
const storedPatients = await this.findAll()
40-
41-
if (storedPatients.length === 0) {
42-
return formatFriendlyId('P', generateSequenceNumber(0))
43-
}
44-
45-
const maxPatient = storedPatients[storedPatients.length - 1]
46-
const { friendlyId } = maxPatient
47-
const currentSequenceNumber = friendlyId.slice(1, friendlyId.length)
48-
49-
return formatFriendlyId('P', generateSequenceNumber(parseInt(currentSequenceNumber, 10)))
50-
}
51-
5232
async save(entity: Patient): Promise<Patient> {
53-
const friendlyId = await this.getFriendlyId()
54-
entity.friendlyId = friendlyId
33+
const patientCode = getPatientCode()
34+
entity.code = patientCode
5535
return super.save(entity)
5636
}
5737
}

src/clients/db/Repository.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint "@typescript-eslint/camelcase": "off" */
2-
import { getTime } from 'date-fns'
2+
import { v4 as uuidv4 } from 'uuid'
33
import AbstractDBModel from '../../model/AbstractDBModel'
44

55
function mapRow(row: any): any {
@@ -47,8 +47,15 @@ export default class Repository<T extends AbstractDBModel> {
4747
}
4848

4949
async save(entity: T): Promise<T> {
50+
const currentTime = new Date().toISOString()
51+
5052
const { id, rev, ...valuesToSave } = entity
51-
const savedEntity = await this.db.put({ _id: getTime(new Date()).toString(), ...valuesToSave })
53+
const savedEntity = await this.db.put({
54+
_id: uuidv4(),
55+
...valuesToSave,
56+
createdAt: currentTime,
57+
updatedAt: currentTime,
58+
})
5259
return this.find(savedEntity.id)
5360
}
5461

@@ -65,6 +72,7 @@ export default class Repository<T extends AbstractDBModel> {
6572
_id: id,
6673
_rev: rev,
6774
...dataToSave,
75+
updatedAt: new Date().toISOString(),
6876
}
6977

7078
await this.db.put(entityToUpdate)

src/model/AbstractDBModel.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export default interface AbstractDBModel {
22
id: string
33
rev: string
4+
createdAt: string
5+
updatedAt: string
46
}

src/model/Patient.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export default interface Patient extends AbstractDBModel, Name, ContactInformati
1212
preferredLanguage?: string
1313
occupation?: string
1414
type?: string
15-
friendlyId: string
15+
code: string
1616
relatedPersons?: RelatedPerson[]
1717
allergies?: Allergy[]
1818
diagnoses?: Diagnosis[]

0 commit comments

Comments
 (0)