Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#8204): Add feature to support adding phone number when registering patients #8369

Merged
merged 61 commits into from
Aug 16, 2023

Conversation

PrjShrestha
Copy link
Contributor

@PrjShrestha PrjShrestha commented Jul 6, 2023

Feature (#8204): Support adding phone number when registering patients via SMS

Description

This code aims to complete the following:
Users should be able to add a patient's phone number while registering a patient.
CHT should automatically append the default country code to the phone number before saving it to the database.

Sample Message:
image


[x] Added unit tests
[x] Tested locally by setting up cht
[x] Fixed eslint issues
[x] Doc update for new field phone_number
[x] [Doc update validation] (medic/cht-docs#1144)

@PrjShrestha PrjShrestha changed the title 8204: Add feature to support adding phone number when registring patients 8204: Add feature to support adding phone number when registering patients Jul 12, 2023
Copy link
Member

@garethbowen garethbowen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice one. A couple of changes requested...

@PrjShrestha
Copy link
Contributor Author

PrjShrestha commented Aug 8, 2023

Update:
Moved logic to create patient or not based upon phone validation to addPatient trigger. 2 benefits:
a. add_phone_number trigger is no longer required.
b. The other option would have been to add a new validPhone validation in transition just like we have added uniquePhone. But validating whether a phone is valid or not should be part of the core rather than a choice that should be written in appsettings registration validations.

Also added logic where rather checking for phone_number field to have phone number value it checks for phone_number type filed in form to have phone_number value so people can do like :

Field
Name : NameTheyWant
Type: phone_number

@tatilepizs This (point b) is why the invalid phone validation was still creating a Patient because we weren't hard exiting addPatient when that validation failed, unlike uniquePhone validation which would hard exit patient creation. Thanks for catching that. 👍

@dianabarsan Thanks for you help and please kindly review.

Copy link
Member

@dianabarsan dianabarsan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really cool stuff. Thanks for implementing all the changes. I have some comments but mostly about readability and maintainability.
Awesome work!

const formattedAndValidatedPhone = phoneNumberParser.normalize(config.getAll(), raw);
if (formattedAndValidatedPhone) {
return formattedAndValidatedPhone;
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need an else here, because the previous if statement has a return.

@@ -461,6 +479,7 @@ const addPatient = (options) => {
const doc = options.doc;
const patientShortcode = options.doc.patient_id;
const patientNameField = getPatientNameField(options.params);
const patientPhoneField = getPatientPhoneField(config.getAll(), doc.form);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this function needs to get all the configs as the parameter.

shared-libs/transitions/src/transitions/registration.js Outdated Show resolved Hide resolved
contactTypeUtils.isPlace.args[0].should.deep.equal([{}, {_id: 'person', patient_id: '56987', type: 'person' }]);
contactTypeUtils.isPlace.args[0].should.deep.equal(
[{}, { _id: 'person', patient_id: '56987', type: 'person' }]
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add unit tests to cover this new functionality.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting there should be no change here. Think its formatting changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was failing the 120 line eslint length check on this test hence Formatted.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just picked this line to add the comment. I was referring to adding unit tests in general.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added tests for registration add_patient flow as well as function to get phone field.

@@ -248,6 +248,61 @@ describe('validations', () => {
});
});

it('unique phone validation should fail if db query for phone returns doc', () => {
clock = sinon.useFakeTimers();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are fake timers needed here? Same question for the test below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed. Thanks.

})
.then(() => utils.getDocs(docIds))
.then(updated => {
console.log(updated);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this debug log.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

chai.expect(updated[0].fields.phone_number).to.equal(patient_phone);
chai.expect(updated[0].patient_id).to.not.be.null;
})
.then(patients => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

patients will always be undefined here, because the previous block does not return anything. I think the block is missing this call:
return getContactsByReference([newPatientId, 'venus']);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added get patient call and asserted taht its undefined when phone is invalid.

.then(infos => {
infos.forEach(info => {
chai.expect(info).to.deep.nested.not.include({ 'transitions.registration.ok': false });
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also check that no patient_id field is added to the main doc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

@dianabarsan dianabarsan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool stuff! I spotted some stuff and I'm still missing unit tests updates for the registration transition.

return;
}

if (formDef && formDef.fields) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can further simplify this like:

Suggested change
if (formDef && formDef.fields) {
if (!formDef?.fields) {
return;
}
return Object
.keys(formDef.fields)
.find(key => formDef.fields[key].type === 'phone_number');

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.
P.S. eslintrc for shared-libs was in 2018 which coudln't recognize the optional chaining syntax (?.) so upgraded it to 2020. 3 or 4 folders are already in 2020.

@@ -505,6 +525,14 @@ const addPatient = (options) => {
patient.date_of_birth = doc.birth_date;
}

if (patientPhoneField && doc.fields[patientPhoneField]) {
if (!phoneNumberParser.validate(config.getAll(), (doc.fields[patientPhoneField]))) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To make this whole block more readable, please store doc.fields[patientPhoneField] in a variable.

Suggested change
if (!phoneNumberParser.validate(config.getAll(), (doc.fields[patientPhoneField]))) {
const patientPhone = doc.fields[patientPhoneField];
if (!phoneNumberParser.validate(config.getAll(), patientPhone)) {
....
return;
}
patient.phone = patientPhone;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

contactTypeUtils.isPlace.args[0].should.deep.equal([{}, {_id: 'person', patient_id: '56987', type: 'person' }]);
contactTypeUtils.isPlace.args[0].should.deep.equal(
[{}, { _id: 'person', patient_id: '56987', type: 'person' }]
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just picked this line to add the comment. I was referring to adding unit tests in general.


return utils
.updateSettings(testForm.forms.NP, 'sentinel')
.then(utils.saveDocs(docs))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to pass a function to the then

Suggested change
.then(utils.saveDocs(docs))
.then()) => utils.saveDocs(docs))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


return utils
.updateSettings(testForm.forms.NP, 'sentinel')
.then(utils.saveDocs(docs))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.then(utils.saveDocs(docs))
.then(() => utils.saveDocs(docs))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

let newPatientId;
return utils
.updateSettings(testForm.forms.NP, 'sentinel')
.then(utils.saveDocs(docs))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.then(utils.saveDocs(docs))
.then(() => utils.saveDocs(docs))

@@ -98,6 +98,150 @@ describe('registration', () => {
after(() => utils.revertDb([], true));
afterEach(() => utils.revertDb(getIds(contacts), true));

it('should add valid phone to patient doc', () => {
const patient_phone = '+9779841123123';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use camelCase for variables.

Suggested change
const patient_phone = '+9779841123123';
const patientPhone = '+9779841123123';

Comment on lines 161 to 162
const patient_phone = '+97796666';
const patient_id =uuid();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use camelCase for variables.

Suggested change
const patient_phone = '+97796666';
const patient_id =uuid();
const patientPhone = '+97796666';
const patientId = uuid();

return getContactsByReference([newPatientId, 'venus']);
})
.then(patients => {
chai.expect(patients[0]).to.be.undefined;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I look at the test above, it seems that this should be returning an object with a rows property instead of an array.

Suggested change
chai.expect(patients[0]).to.be.undefined;
chai.expect(patients.rows.length).to.equal(0);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done thanks.

})
.then(() => utils.getDocs(docIds))
.then(updated => {
chai.expect(updated.patient_id).to.be.undefined;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that updated will be an array here.

Suggested change
chai.expect(updated.patient_id).to.be.undefined;
chai.expect(updated[0].patient_id).to.be.undefined;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed thanks.

@PrjShrestha
Copy link
Contributor Author

PrjShrestha commented Aug 14, 2023

Doc update for the phone_number field here. Also closed PR for add_phone_number trigger as that functionality is done in add_patient. Updated description to reflect the same.

Copy link
Member

@dianabarsan dianabarsan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great stuff @PrjShrestha !

transitionUtils.addRejectionMessage(doc, options.registrationConfig, 'provided_phone_not_valid');
return;
}
patient.phone = doc.fields[patientPhoneField];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
patient.phone = doc.fields[patientPhoneField];
patient.phone = patientPhone;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

phone_number: (raw) => {
const formattedAndValidatedPhone = phoneNumberParser.normalize(config.getAll(), raw);
if (formattedAndValidatedPhone) {
return formattedAndValidatedPhone;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PrjShrestha , would you mind adding Nepali digit supporting code for this type as well? Here's the sample PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@garethbowen garethbowen merged commit bc2304a into medic:master Aug 16, 2023
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants