Skip to content

Commit

Permalink
MedicationAdministration blue-button-record piece, linked entries
Browse files Browse the repository at this point in the history
  • Loading branch information
Afsin Ustundag committed May 28, 2015
1 parent 6f32321 commit d7a5db0
Show file tree
Hide file tree
Showing 7 changed files with 285 additions and 10 deletions.
2 changes: 0 additions & 2 deletions index.js
Expand Up @@ -96,7 +96,6 @@ exports.saveMatches = function (secName, ptKey, inputSection, sourceId, callback
};

exports.getMatches = function (secName, ptKey, fields, callback) {
//console.log('matches: '+dbinfo.db.options.url);
match.getAll(dbinfo, secName, ptKey, fields, callback);
};

Expand All @@ -119,7 +118,6 @@ exports.acceptMatch = function (secName, ptKey, id, reason, callback) {
// section

exports.getSection = function (secName, ptKey, callback) {
//console.log(dbinfo.db.options.url);
section.get(dbinfo, secName, ptKey, callback);
};

Expand Down
36 changes: 35 additions & 1 deletion lib/entry.js
Expand Up @@ -36,6 +36,34 @@ exports.remove = function (dbinfo, secName, ptKey, id, callback) {
async.series([removeMerge, removeModel], callback);
};

var hide = function (dbinfo, secName, ptKey, id, callback) {
if (typeof id === 'string') {
id = mongoose.Types.ObjectId(id);
}

var hideModel = function (callback) {
var model = dbinfo.models[secName];
var query = model.update({
_id: id
}, {
hidden: true,
});
query.exec(callback);
};

var hideMerge = function (callback) {
var model = dbinfo.mergeModels[secName];
var query = model.update({
entry: id
}, {
hidden: true
});
query.exec(callback);
};

async.series([hideMerge, hideModel], callback);
};

exports.get = function (dbinfo, secName, ptKey, id, callback) {
var model = dbinfo.models[secName];
if (typeof id === 'string') {
Expand Down Expand Up @@ -171,7 +199,13 @@ exports.save = function (dbinfo, secName, input, sourceId, callback) {

var saveEntry = function (callback) {
entryModel.save(function (err, saveResult) {
callback(err, saveResult); // needed bacause model.save callback has 3 parameters
if (err || (!input._link)) {
callback(err, saveResult);
} else {
hide(dbinfo, secName, input.pat_key, input._link, function (err) {
callback(err, saveResult);
});
}
});
};

Expand Down
2 changes: 2 additions & 0 deletions lib/merge.js
Expand Up @@ -49,6 +49,7 @@ exports.getAll = function (dbinfo, secName, ptKey, typeFields, recordFields, cal
pat_key: ptKey
});
query.where('archived').in([null, false]);
query.where('hidden').in([null, false]);
query.where('entry_type', secName);
query.lean();
query.populate('record', allFields);
Expand Down Expand Up @@ -88,6 +89,7 @@ exports.count = function (dbinfo, secName, ptKey, conditions, callback) {
condsWPat.pat_key = ptKey;
var query = model.find({});
query.where(condsWPat);
query.where('hidden').in([null, false]);
query.exec(function (err, mergeResults) {
if (err) {
callback(err);
Expand Down
5 changes: 4 additions & 1 deletion lib/models.js
Expand Up @@ -77,7 +77,8 @@ exports.models = function (connection, supportedSections) {
},
merged: Date,
merge_reason: String,
archived: Boolean
archived: Boolean,
hidden: Boolean
});
result.merge[secName] = connection.model(mergeColName, mergeSchema);

Expand All @@ -102,9 +103,11 @@ exports.models = function (connection, supportedSections) {
}]
};
desc.reviewed = Boolean;
desc.hidden = Boolean;
desc.archived = Boolean;
desc.archived_on = Date;
desc._components = [ObjectId];
desc._link = ObjectId;

//extra metadata for demographics section for PIm purposes
if (secName === 'demographics') {
Expand Down
5 changes: 5 additions & 0 deletions lib/search.js
Expand Up @@ -49,6 +49,11 @@ var searchAllIdsForSection = function (dbinfo, sectionName, searchSpec, callback
var query = model.find(queryObject);
query.where('archived').in([null, false]);
query.where('reviewed', true);
if (searchSpec.mustLink) {
query.where('_link').exists();
} else {
query.where('_link', null);
}
query.sort({
_id: -1
});
Expand Down
11 changes: 5 additions & 6 deletions lib/section.js
Expand Up @@ -14,6 +14,7 @@ var localGet = function (dbinfo, secName, ptKey, reviewed, callback) {

var query = model.find({});
query.where('archived').in([null, false]);
query.where('hidden').in([null, false]);
query.where('reviewed', reviewed);
query.where('pat_key', ptKey);
query.lean();
Expand Down Expand Up @@ -52,7 +53,6 @@ exports.save = function (dbinfo, secName, ptKey, input, sourceId, callback) {
};

var prepForDb = function (entryObject) {
// var d = _.clone(entryObject);
var r = {
pat_key: ptKey,
reviewed: true
Expand All @@ -68,6 +68,10 @@ exports.save = function (dbinfo, secName, ptKey, input, sourceId, callback) {
r._components = d._components;
delete d._components;
}
if (d._link) {
r._link = d._link;
delete d._link;
}
}
return r;
};
Expand All @@ -86,13 +90,8 @@ exports.save = function (dbinfo, secName, ptKey, input, sourceId, callback) {
};

exports.savePartial = function (dbinfo, secName, ptKey, input, sourceId, callback) {

//console.log(input);

var savePartialEntry = function (entryObject, cb) {

//console.log(entryObject);

var localSaveNewEntry = function (cb2) {
entry.save(dbinfo, secName, entryObject.entry, sourceId, cb2);
};
Expand Down
234 changes: 234 additions & 0 deletions test/unit/fhir/test-linked-section.js
@@ -0,0 +1,234 @@
"use strict";

var util = require('util');
var chai = require('chai');
var async = require('async');
var _ = require('lodash');
var moment = require('moment');

var refmodel = require('../refmodel');
var section = require('../../../lib/section');
var entry = require('../../../lib/entry');
var search = require('../../../lib/search');
var common = require('./common');

var expect = chai.expect;

// Multiple FHIR medication related resources map to CCDA medications. This
// test simulates the case where multiple linked entries exist in the record
// for FHIR resources but only one must to be shown from blue-button model API.
describe('section with linked entries', function () {
var context = {}; // populated by refmodel common methods

var self = this;
var numPatients = 2;
var numAllergiesPerPt = 4;

refmodel.prepareConnection({
dbName: 'fhirlinkedentries',
fhir: true
}, context)();

var sourceIds;
it('add sources', function (done) {
refmodel.addSourcesPerPatient(context, _.times(numPatients, _.constant(1)), function (err, result) {
if (err) {
done(err);
} else {
sourceIds = result;
done();
}
});
});

var patientSamples = _.range(numPatients).map(function (ptIndex) {
var sourceKey = util.format('%s.%s', ptIndex, 0);
return refmodel.createTestSection('testdemographics', sourceKey, 1)[0];
});

var patientIds = [];
patientSamples.forEach(function (patientSample, ptIndex) {
var sampleClone = _.cloneDeep(patientSample);
var patKey = util.format('pat%s', ptIndex);
var title = util.format('create testdemographics for %s', patKey);
var momentBefore = moment();
it(title, function (done) {
var itself = this;
section.save(context.dbinfo, 'testdemographics', patKey, sampleClone, sourceIds[ptIndex], function (err, result) {
if (err) {
done(err);
} else {
expect(result).to.exist;
expect(result.versionId).to.equal('1');
common.verifyMoment.call(itself, momentBefore, result.lastUpdated);
patientIds.push(result.id);
done();
}
});
});
});

var numAllergies = 6;
var allergySamples = _.range(2).map(function (ptIndex) {
var sourceKey = util.format('%s.%s', ptIndex, 0);
return refmodel.createTestSection('testallergies', sourceKey, numAllergies);
});
allergySamples = _.flatten(allergySamples);
allergySamples.forEach(function (allergySample) {
allergySample.flag = 'not linked';
});

var allergiesIds = [];
allergySamples.forEach(function (allergySample, index) {
var sampleClone = _.cloneDeep(allergySample);
var ptIndex = Math.floor(index / numAllergies);
var patKey = util.format('pat%s', ptIndex);
var title = util.format('create testallergies %s for %s', index, patKey);
var momentBefore = moment();
it(title, function (done) {
var itself = this;
section.save(context.dbinfo, 'testallergies', patKey, sampleClone, sourceIds[ptIndex], function (err, result) {
if (err) {
done(err);
} else {
expect(result).to.exist;
expect(result.versionId).to.equal('1');
common.verifyMoment.call(itself, momentBefore, result.lastUpdated);
allergiesIds.push(result.id);
done();
}
});
});
});

var verifySection = function (section, ids, done) {
try {
expect(section.length).to.equal(numAllergies);
section.forEach(function (entry) {
var index = ids.indexOf(entry._id.toString());
expect(index).to.be.above(-1);
delete entry._id;
delete entry.metadata;
delete entry._link;
expect(entry).to.deep.equal(allergySamples[index]);
});
done();
} catch (err) {
done(err);
}
};

_.range(2).forEach(function (ptIndex) {
var patKey = util.format('pat%s', ptIndex);
var title = util.format('get testallergies section for %s', patKey);
it(title, function (done) {
section.get(context.dbinfo, 'testallergies', patKey, function (err, section) {
if (err) {
done(err);
} else {
verifySection(section, allergiesIds, done);
}
});
});
});

var linkedAllergyIds = [];
_.range(2).forEach(function (ptIndex) {
var patKey = util.format('pat%s', ptIndex);
_.range(2).forEach(function (allergyIndex) {
var title = util.format('create linked testallergies %s for %s', allergyIndex, patKey);
it(title, function (done) {
var actualIndex = allergyIndex + numAllergies * ptIndex;
var sampleClone = _.cloneDeep(allergySamples[allergyIndex + numAllergies * ptIndex]);
sampleClone._link = allergiesIds[actualIndex];
var momentBefore = moment();
section.save(context.dbinfo, 'testallergies', patKey, sampleClone, sourceIds[ptIndex], function (err, result) {
if (err) {
done(err);
} else {
expect(result).to.exist;
expect(result.versionId).to.equal('1');
common.verifyMoment(momentBefore, result.lastUpdated);
linkedAllergyIds.push(result.id);
done();
}
});
});
});
});

_.range(2).forEach(function (ptIndex) {
var patKey = util.format('pat%s', ptIndex);
var title = util.format('get testallergies section for %s', patKey);
it(title, function (done) {
var effectiveAllergyIds = _.clone(allergiesIds);
effectiveAllergyIds[0] = linkedAllergyIds[0];
effectiveAllergyIds[1] = linkedAllergyIds[1];
effectiveAllergyIds[numAllergies] = linkedAllergyIds[2];
effectiveAllergyIds[numAllergies + 1] = linkedAllergyIds[3];

section.get(context.dbinfo, 'testallergies', patKey, function (err, section) {
if (err) {
done(err);
} else {
verifySection(section, effectiveAllergyIds, done);
}
});
});
});

it('search one that has link', function (done) {
var searchSpec = {
section: 'testallergies',
query: {},
patientInfo: false,
mustLink: true
};
search.search(context.dbinfo, searchSpec, function (err, result, searchInfo) {
expect(result).to.have.length(4);
expect(searchInfo).to.exist;
expect(searchInfo.searchId).not.to.exist;
expect(searchInfo.page).to.equal(0);
expect(searchInfo.total).to.equal(4);
var resultData = result.map(function (r) {
result.forEach(function (e) {
expect(e._section).to.equal('testallergies');
});
});
done();
});
});

it('search one that does not have link', function (done) {
var searchSpec = {
section: 'testallergies',
query: {},
patientInfo: false
};
search.search(context.dbinfo, searchSpec, function (err, result, searchInfo) {
expect(result).to.have.length(12);
expect(searchInfo).to.exist;
expect(searchInfo.searchId).not.to.exist;
expect(searchInfo.page).to.equal(0);
expect(searchInfo.total).to.equal(12);
var resultData = result.map(function (r) {
result.forEach(function (e) {
expect(e._section).to.equal('testallergies');
});
});
done();
});
});

after(function (done) {
context.dbinfo.db.dropDatabase(function (err) {
if (err) {
done(err);
} else {
context.dbinfo.connection.close(function (err) {
done(err);
});
}
});
});
});

0 comments on commit d7a5db0

Please sign in to comment.