diff --git a/admin/src/js/services/get-summaries.js b/admin/src/js/services/get-summaries.js
index b5b22922300..12441f3ac35 100644
--- a/admin/src/js/services/get-summaries.js
+++ b/admin/src/js/services/get-summaries.js
@@ -93,6 +93,7 @@ angular.module('inboxServices').factory('GetSummaries',
contact_type: doc.contact_type,
contact: doc.contact && doc.contact._id,
lineage: getLineage(doc.parent),
+ simprints_id: doc.simprints_id,
date_of_death: doc.date_of_death,
muted: doc.muted
};
diff --git a/admin/tests/unit/services/get-summaries.js b/admin/tests/unit/services/get-summaries.js
index ef0eba24908..275c2aec3eb 100644
--- a/admin/tests/unit/services/get-summaries.js
+++ b/admin/tests/unit/services/get-summaries.js
@@ -188,6 +188,7 @@ describe('GetSummaries service', () => {
_id: 'g'
}
},
+ simprints_id: '987',
muted: true
} },
] });
@@ -206,6 +207,7 @@ describe('GetSummaries service', () => {
contact: 'c',
lineage: [],
date_of_death: 999,
+ simprints_id: undefined,
muted: undefined
},
{
@@ -218,6 +220,7 @@ describe('GetSummaries service', () => {
contact: undefined,
lineage: [ 'f', 'g' ],
date_of_death: undefined,
+ simprints_id: '987',
muted: true
}
]);
diff --git a/ddocs/medic-db/medic-client/views/contacts_by_reference/map.js b/ddocs/medic-db/medic-client/views/contacts_by_reference/map.js
index cb613361876..94c37aff24d 100644
--- a/ddocs/medic-db/medic-client/views/contacts_by_reference/map.js
+++ b/ddocs/medic-db/medic-client/views/contacts_by_reference/map.js
@@ -25,6 +25,9 @@ function(doc) {
if (doc.patient_id) {
emitReference('shortcode', doc.patient_id);
}
+ if (doc.simprints_id) {
+ emitReference('simprints', doc.simprints_id);
+ }
if (doc.rc_code) {
// need String because rewriter wraps everything in quotes
// keep refid case-insenstive since data is usually coming from SMS
diff --git a/ddocs/medic/_attachments/translations/messages-en.properties b/ddocs/medic/_attachments/translations/messages-en.properties
index f2899e78588..2cb1b1d72ef 100644
--- a/ddocs/medic/_attachments/translations/messages-en.properties
+++ b/ddocs/medic/_attachments/translations/messages-en.properties
@@ -1194,6 +1194,9 @@ setup.skip = Skip the setup wizard
setup.start = Finish
setup.statistics.description = Allow anonymous statistics to be submitted to Medic? Statistics will be sent monthly to help us improve the software and learn about the impact it's having. Strictly no confidential patient information will be shared with us. You can update these settings at any time from this wizard.
setup.statistics.title = Share impact statistics
+simprints.disabled = Simprints isn't available on this device. Use a recent version of the Medic Android app to enable Simprints integration.
+simprints.register = Register with Simprints
+simprints.search = Search with Simprints
sms_message.message = Incoming message
sms_received = SMS message received; it will be reviewed shortly. If you were trying to submit a text form, please enter a correct form code and try again.
state.cleared = cleared
diff --git a/ddocs/medic/views/doc_summaries_by_id/map.js b/ddocs/medic/views/doc_summaries_by_id/map.js
index 1187efb887e..a5d26d8e953 100644
--- a/ddocs/medic/views/doc_summaries_by_id/map.js
+++ b/ddocs/medic/views/doc_summaries_by_id/map.js
@@ -86,6 +86,7 @@ function(doc) {
contact_type: doc.contact_type,
contact: doc.contact && doc.contact._id,
lineage: getLineage(doc.parent),
+ simprints_id: doc.simprints_id,
date_of_death: doc.date_of_death,
muted: doc.muted
});
diff --git a/shared-libs/search/src/generate-search-requests.js b/shared-libs/search/src/generate-search-requests.js
index 8ace80eed78..a16655b326f 100644
--- a/shared-libs/search/src/generate-search-requests.js
+++ b/shared-libs/search/src/generate-search-requests.js
@@ -144,6 +144,19 @@ const contactTypeRequest = function(filters, sortByLastVisitedDate) {
return request;
};
+const simprintsRequest = function(filters) {
+ if (!filters.simprintsIdentities) {
+ return;
+ }
+ const keys = filters.simprintsIdentities.map(function(identity) {
+ return [ 'simprints', identity.id ];
+ });
+ return {
+ view: 'medic-client/contacts_by_reference',
+ params: { keys: keys }
+ };
+};
+
const defaultReportRequest = function() {
return {
view: 'medic-client/reports_by_date',
@@ -193,6 +206,11 @@ const requestBuilders = {
return requests;
},
contacts: function(filters, extensions) {
+ const simprints = simprintsRequest(filters);
+ if (simprints) {
+ return [ simprints ];
+ }
+
const shouldSortByLastVisitedDate = module.exports.shouldSortByLastVisitedDate(extensions);
const typeRequest = contactTypeRequest(filters, shouldSortByLastVisitedDate);
diff --git a/tests/e2e/forms/submit-photo-upload-form.spec.js b/tests/e2e/forms/submit-photo-upload-form.spec.js
index ffa316889e7..ae96c48e69e 100644
--- a/tests/e2e/forms/submit-photo-upload-form.spec.js
+++ b/tests/e2e/forms/submit-photo-upload-form.spec.js
@@ -34,7 +34,7 @@ describe('Submit Photo Upload form', () => {
await genericForm.selectFormNative('photo-upload');
await helper.waitElementToPresentNative(photoUpload.imagePathInput());
await photoUpload.imagePathInput().sendKeys(
- path.join(__dirname, '../../../webapp/src/img/setup-wizard-demo.png')
+ path.join(__dirname, '../../../webapp/src/img/simprints.png')
);
await helper.waitUntilReadyNative(photoUpload.imagePreview());
diff --git a/tests/e2e/service-worker.wdio-spec.js b/tests/e2e/service-worker.wdio-spec.js
index 4cf7cd18708..f3d4d1d7b5a 100644
--- a/tests/e2e/service-worker.wdio-spec.js
+++ b/tests/e2e/service-worker.wdio-spec.js
@@ -109,6 +109,7 @@ describe('Service worker cache', () => {
'/img/icon-pregnant.svg',
'/img/layers.png',
'/img/setup-wizard-demo.png',
+ '/img/simprints.png',
'/login/script.js',
'/login/style.css',
'/main.js',
diff --git a/webapp/src/css/enketo/medic.less b/webapp/src/css/enketo/medic.less
index a4567aa2a2b..629a6972f40 100644
--- a/webapp/src/css/enketo/medic.less
+++ b/webapp/src/css/enketo/medic.less
@@ -384,6 +384,9 @@
}
}
}
+ .or .simprints-register img {
+ display: inherit;
+ }
.select2-container .select2-selection--single {
height: @input-height;
}
diff --git a/webapp/src/css/inbox.less b/webapp/src/css/inbox.less
index 9b95cbf596e..1371857320e 100644
--- a/webapp/src/css/inbox.less
+++ b/webapp/src/css/inbox.less
@@ -418,7 +418,8 @@ body {
}
.reset-filter,
- .sort-results {
+ .sort-results,
+ .simprints-filter {
line-height: 38px;
> a {
color: @text-normal-color;
@@ -1216,6 +1217,21 @@ a.fa:hover {
}
}
+.simprints-stars {
+ &:after {
+ font-family: FontAwesome;
+ }
+ &.tier-1:after {
+ content: '\f005\f005\f005\f005\f005'; /* 5 stars full */
+ }
+ &.tier-2:after {
+ content: '\f005\f005\f005\f005\f006'; /* 4 stars full, 1 empty */
+ }
+ &.tier-3:after {
+ content: '\f005\f005\f005\f006\f006'; /* 3 star full, 2 empty */
+ }
+}
+
.upgrade-config .status {
text-align: center;
}
diff --git a/webapp/src/img/simprints.png b/webapp/src/img/simprints.png
new file mode 100644
index 00000000000..d3d43027d98
Binary files /dev/null and b/webapp/src/img/simprints.png differ
diff --git a/webapp/src/js/enketo/widgets.js b/webapp/src/js/enketo/widgets.js
index 4bee5f5500f..e838fa11bcb 100644
--- a/webapp/src/js/enketo/widgets.js
+++ b/webapp/src/js/enketo/widgets.js
@@ -21,6 +21,7 @@
require( './widgets/unselectable-radios' ),
require( './widgets/android-datepicker' ),
require( './widgets/bikram-sambat-datepicker' ),
+ require( './widgets/simprints' ),
require( './widgets/mrdt' ),
require( './widgets/android-app-launcher' ),
require( './widgets/display-base64-image' ),
diff --git a/webapp/src/js/enketo/widgets/simprints.js b/webapp/src/js/enketo/widgets/simprints.js
new file mode 100644
index 00000000000..3070d82672d
--- /dev/null
+++ b/webapp/src/js/enketo/widgets/simprints.js
@@ -0,0 +1,78 @@
+'use strict';
+const Widget = require('enketo-core/src/js/Widget');
+const $ = require( 'jquery' );
+require('enketo-core/src/js/plugins');
+
+const pluginName = 'simprintswidget';
+
+/**
+ * @constructor
+ * @param {Element} element [description]
+ * @param {(boolean|{touch: boolean, repeat: boolean})} options options
+ * @param {*=} e event
+ */
+function Simprintswidget( element, options ) {
+ this.namespace = pluginName;
+ Widget.call( this, element, options );
+ this._init();
+}
+
+//copy the prototype functions from the Widget super class
+Simprintswidget.prototype = Object.create( Widget.prototype );
+
+//ensure the constructor is the new one
+Simprintswidget.prototype.constructor = Simprintswidget;
+
+Simprintswidget.prototype._init = function() {
+ const $el = $( this.element );
+ const $input = $el.find( 'input' );
+ $input.attr( 'disabled', true );
+ const $translate = window.CHTCore.Translate;
+ // todo migrate when simprints are migrated
+ //const service = angularServices.get( 'Simprints' );
+ const service = {
+ enabled: () => {},
+ register: () => Promise.resolve(),
+ };
+
+ if ( !service.enabled() ) {
+ $translate.get( 'simprints.disabled' ).then(function( label ) {
+ $el.append( '
' + label + '
' );
+ });
+ return;
+ }
+
+ $el.on( 'click', '.btn.simprints-register', function() {
+ service.register().then( function(simprintsId) {
+ $input.val( simprintsId ).trigger( 'change' );
+ } );
+ } );
+
+ $translate.get( 'simprints.register' ).then( function( label ) {
+ $el.append( '' );
+ } );
+};
+
+Simprintswidget.prototype.destroy = function( element ) {}; // eslint-disable-line no-unused-vars
+
+$.fn[ pluginName ] = function( options, event ) {
+ return this.each( function() {
+ const $this = $( this );
+ let data = $this.data( pluginName );
+
+ options = options || {};
+
+ if ( !data && typeof options === 'object' ) {
+ $this.data( pluginName, ( data = new Simprintswidget( this, options, event ) ) );
+ } else if ( data && typeof options === 'string' ) {
+ data[ options ]( this );
+ }
+ } );
+};
+
+module.exports = {
+ 'name': pluginName,
+ 'selector': '.or-appearance-simprints-reg',
+};
diff --git a/webapp/src/ts/actions/global.ts b/webapp/src/ts/actions/global.ts
index ea18047061d..baa10e02d24 100644
--- a/webapp/src/ts/actions/global.ts
+++ b/webapp/src/ts/actions/global.ts
@@ -21,6 +21,7 @@ export const Actions = {
setFilter: createSingleValueAction('SET_FILTER', 'filter'),
setFilters: createSingleValueAction('SET_FILTERS', 'filters'),
setSelectMode: createSingleValueAction('SET_SELECT_MODE', 'selectMode'),
+ setIsAdmin: createSingleValueAction('SET_IS_ADMIN', 'isAdmin'),
setTitle: createSingleValueAction('SET_TITLE', 'title'),
setPrivacyPolicyAccepted: createSingleValueAction('SET_PRIVACY_POLICY_ACCEPTED', 'accepted'),
setShowPrivacyPolicy: createSingleValueAction('SET_SHOW_PRIVACY_POLICY', 'show'),
@@ -104,6 +105,10 @@ export class GlobalActions {
return this.store.dispatch(Actions.setSelectMode(selectMode));
}
+ setIsAdmin(isAdmin) {
+ return this.store.dispatch(Actions.setIsAdmin(isAdmin));
+ }
+
setLoadingShowContent(id) {
this.setLoadingContent(id);
this.setShowContent(true);
diff --git a/webapp/src/ts/app.component.ts b/webapp/src/ts/app.component.ts
index 3e75d899054..31f75fd4313 100644
--- a/webapp/src/ts/app.component.ts
+++ b/webapp/src/ts/app.component.ts
@@ -275,6 +275,7 @@ export class AppComponent implements OnInit {
.then(() => this.checkDateService.check(true))
.then(() => this.startRecurringProcesses());
+ this.globalActions.setIsAdmin(this.sessionService.isAdmin());
this.watchBrandingChanges();
this.watchDDocChanges();
this.watchUserContextChanges();
diff --git a/webapp/src/ts/components/components.module.ts b/webapp/src/ts/components/components.module.ts
index 06e1890ab11..d8630601cb6 100644
--- a/webapp/src/ts/components/components.module.ts
+++ b/webapp/src/ts/components/components.module.ts
@@ -23,6 +23,7 @@ import { FormTypeFilterComponent } from '@mm-components/filters/form-type-filter
import { StatusFilterComponent } from '@mm-components/filters/status-filter/status-filter.component';
import { FreetextFilterComponent } from '@mm-components/filters/freetext-filter/freetext-filter.component';
import { ResetFiltersComponent } from '@mm-components/filters/reset-filters/reset-filters.component';
+import { SimprintsFilterComponent} from '@mm-components/filters/simprints-filter/simprints-filter.component';
import { SortFilterComponent } from '@mm-components/filters/sort-filter/sort-filter.component';
import { SenderComponent } from '@mm-components/sender/sender.component';
import { ReportImageComponent } from '@mm-components/report-image/report-image.component';
@@ -52,6 +53,7 @@ import { MobileDetectionComponent } from '@mm-components/mobile-detection/mobile
StatusFilterComponent,
FreetextFilterComponent,
ResetFiltersComponent,
+ SimprintsFilterComponent,
SortFilterComponent,
SenderComponent,
ReportImageComponent,
@@ -84,6 +86,7 @@ import { MobileDetectionComponent } from '@mm-components/mobile-detection/mobile
StatusFilterComponent,
FreetextFilterComponent,
ResetFiltersComponent,
+ SimprintsFilterComponent,
SortFilterComponent,
SenderComponent,
ReportImageComponent,
diff --git a/webapp/src/ts/components/content-row-list-item/content-row-list-item.component.html b/webapp/src/ts/components/content-row-list-item/content-row-list-item.component.html
index 51cdc8d8721..119fab7830b 100644
--- a/webapp/src/ts/components/content-row-list-item/content-row-list-item.component.html
+++ b/webapp/src/ts/components/content-row-list-item/content-row-list-item.component.html
@@ -46,6 +46,7 @@
{{'contact.muted' | translate}}
+
{{warning}}
diff --git a/webapp/src/ts/components/content-row-list-item/content-row-list-item.component.ts b/webapp/src/ts/components/content-row-list-item/content-row-list-item.component.ts
index 98de433cda5..04e7d8f87f6 100644
--- a/webapp/src/ts/components/content-row-list-item/content-row-list-item.component.ts
+++ b/webapp/src/ts/components/content-row-list-item/content-row-list-item.component.ts
@@ -40,6 +40,8 @@ export class ContentRowListItemComponent {
@Input() verified;
// array: (optional) the hierarchy for the row
@Input() lineage;
+ // integer: (optional) the simprints tier of the contact match
+ @Input() simprintsTier;
// boolean: (optional) whether to mark this row as a primary contact
@Input() primaryContact;
// integer: (optional) how much tasks to show as pending for this contact
diff --git a/webapp/src/ts/components/filters/simprints-filter/simprints-filter.component.html b/webapp/src/ts/components/filters/simprints-filter/simprints-filter.component.html
new file mode 100644
index 00000000000..8c172acb4b1
--- /dev/null
+++ b/webapp/src/ts/components/filters/simprints-filter/simprints-filter.component.html
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/webapp/src/ts/components/filters/simprints-filter/simprints-filter.component.ts b/webapp/src/ts/components/filters/simprints-filter/simprints-filter.component.ts
new file mode 100644
index 00000000000..a1db7d10dc6
--- /dev/null
+++ b/webapp/src/ts/components/filters/simprints-filter/simprints-filter.component.ts
@@ -0,0 +1,14 @@
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+
+@Component({
+ selector: 'mm-simprints-filter',
+ templateUrl: './simprints-filter.component.html'
+})
+export class SimprintsFilterComponent {
+ @Input() simprintsEnabled;
+ @Output() identify: EventEmitter = new EventEmitter();
+
+ simprintsIdentify() {
+ this.identify.emit();
+ }
+}
diff --git a/webapp/src/ts/modules/contacts/contacts-filters.component.html b/webapp/src/ts/modules/contacts/contacts-filters.component.html
index 82769b6b701..18d8610f25b 100644
--- a/webapp/src/ts/modules/contacts/contacts-filters.component.html
+++ b/webapp/src/ts/modules/contacts/contacts-filters.component.html
@@ -1,5 +1,9 @@
+
= new EventEmitter();
+ @Output() simIdentify: EventEmitter = new EventEmitter();
@Output() sort: EventEmitter = new EventEmitter();
@Input() reset;
@Input() disabled;
+ @Input() simprintsEnabled;
@Input() sortDirection;
@Input() lastVisitedDateExtras;
@ViewChild(FreetextFilterComponent)
freetextFilter:FreetextFilterComponent;
+ @ViewChild(SimprintsFilterComponent)
+ simprintsFilter:SimprintsFilterComponent;
+
applyFilters(force?) {
this.search.emit(force);
}
+ simprintsIdentify() {
+ this.simIdentify.emit();
+ }
+
resetFilters() {
this.globalActions.clearFilters();
this.freetextFilter?.clear();
diff --git a/webapp/src/ts/modules/contacts/contacts.component.html b/webapp/src/ts/modules/contacts/contacts.component.html
index 7e278620381..8d28b5a46d0 100644
--- a/webapp/src/ts/modules/contacts/contacts.component.html
+++ b/webapp/src/ts/modules/contacts/contacts.component.html
@@ -2,7 +2,9 @@
@@ -47,6 +49,8 @@
{{contact.visits.summary}}
+
+
diff --git a/webapp/src/ts/modules/contacts/contacts.component.ts b/webapp/src/ts/modules/contacts/contacts.component.ts
index 10cf9e166e2..23f2bc59ba7 100644
--- a/webapp/src/ts/modules/contacts/contacts.component.ts
+++ b/webapp/src/ts/modules/contacts/contacts.component.ts
@@ -14,6 +14,7 @@ import { SessionService } from '@mm-services/session.service';
import { AuthService } from '@mm-services/auth.service';
import { SettingsService } from '@mm-services/settings.service';
import { UHCSettingsService } from '@mm-services/uhc-settings.service';
+import { SimprintsService } from '@mm-services/simprints.service';
import { Selectors } from '@mm-selectors/index';
import { SearchService } from '@mm-services/search.service';
import { ContactTypesService } from '@mm-services/contact-types.service';
@@ -54,6 +55,7 @@ export class ContactsComponent implements OnInit, OnDestroy{
defaultSortDirection = 'alpha';
sortDirection = this.defaultSortDirection;
additionalListItem = false;
+ simprintsEnabled;
enketoEdited;
selectedContact;
@@ -70,6 +72,7 @@ export class ContactsComponent implements OnInit, OnDestroy{
private authService: AuthService,
private settingsService: SettingsService,
private UHCSettings: UHCSettingsService,
+ private simprintsService: SimprintsService,
private scrollLoaderProvider: ScrollLoaderProvider,
private relativeDateService: RelativeDateService,
private tourService: TourService,
@@ -127,6 +130,7 @@ export class ContactsComponent implements OnInit, OnDestroy{
},
});
this.subscription.add(changesSubscription);
+ this.simprintsEnabled = this.simprintsService.enabled();
Promise
.all([
@@ -227,6 +231,7 @@ export class ContactsComponent implements OnInit, OnDestroy{
contact.valid = true;
contact.summary = null;
contact.primary = contact.home;
+ contact.simprintsTier = contact.simprints && contact.simprints.tierNumber;
contact.dod = contact.date_of_death;
if (type && type.count_visits && Number.isInteger(contact.lastVisitedDate)) {
if (contact.lastVisitedDate === 0) {
@@ -327,7 +332,7 @@ export class ContactsComponent implements OnInit, OnDestroy{
}
let searchFilters = this.defaultFilters;
- if (this.filters.search) {
+ if (this.filters.search || this.filters.simprintsIdentities) {
searchFilters = this.filters;
}
@@ -349,14 +354,15 @@ export class ContactsComponent implements OnInit, OnDestroy{
return this.searchService
.search('contacts', searchFilters, options, extensions, docIds)
- .then(updatedContacts => {
+ .then((updatedContacts) => {
// If you have a home place make sure its at the top
- if (this.usersHomePlace) {
+ if(this.usersHomePlace) {
const homeIndex = _findIndex(updatedContacts, (contact:any) => {
return contact._id === this.usersHomePlace._id;
});
this.additionalListItem =
!this.filters.search &&
+ !this.filters.simprintsIdentities &&
(this.additionalListItem || !this.appending) &&
homeIndex === -1;
@@ -365,9 +371,26 @@ export class ContactsComponent implements OnInit, OnDestroy{
// move it to the top
updatedContacts.splice(homeIndex, 1);
updatedContacts.unshift(this.usersHomePlace);
- } else if (!this.filters.search) {
+ } else if (
+ !this.filters.search &&
+ !this.filters.simprintsIdentities
+ ) {
+
updatedContacts.unshift(this.usersHomePlace);
}
+ if (this.filters.simprintsIdentities) {
+ updatedContacts.forEach((contact) => {
+ const identity = this.filters.simprintsIdentities.find(
+ function(identity) {
+ return identity.id === contact.simprints_id;
+ }
+ );
+ contact.simprints = identity || {
+ confidence: 0,
+ tierNumber: 5,
+ };
+ });
+ }
}
}
@@ -396,7 +419,11 @@ export class ContactsComponent implements OnInit, OnDestroy{
}
this.loading = true;
- return this.query();
+ if (this.filters.search || this.filters.simprintsIdentities) {
+ return this.query();
+ } else {
+ return this.query();
+ }
}
sort(sortDirection?) {
@@ -404,6 +431,16 @@ export class ContactsComponent implements OnInit, OnDestroy{
this.query();
}
+ simprintsIdentify() {
+ this.loading = true;
+ this.simprintsService
+ .identify()
+ .then((identities) => {
+ this.filters.simprintsIdentities = identities;
+ this.search();
+ });
+ }
+
listTrackBy(index, contact) {
return contact._id + contact._rev;
}
diff --git a/webapp/src/ts/reducers/contacts.ts b/webapp/src/ts/reducers/contacts.ts
index d726d23865c..4533077aa96 100644
--- a/webapp/src/ts/reducers/contacts.ts
+++ b/webapp/src/ts/reducers/contacts.ts
@@ -46,10 +46,11 @@ const orderBy = (c1, c2) => {
if (c1.sortByLastVisitedDate) {
return c1.lastVisitedDate - c2.lastVisitedDate;
}
-
+ if (c1.simprints && c2.simprints) {
+ return c2.simprints.confidence - c1.simprints.confidence;
+ }
const c1Type = getContactTypeOrder(c1) || '';
const c2Type = getContactTypeOrder(c2) || '';
-
if (c1Type !== c2Type) {
return c1Type < c2Type ? -1 : 1;
}
diff --git a/webapp/src/ts/reducers/global.ts b/webapp/src/ts/reducers/global.ts
index 4e25e7b6180..bdd871c4b94 100644
--- a/webapp/src/ts/reducers/global.ts
+++ b/webapp/src/ts/reducers/global.ts
@@ -24,6 +24,7 @@ const initialState = {
facilities: [],
filters: {},
forms: null,
+ isAdmin: false,
lastChangedDoc: false,
loadingContent: false,
loadingSubActionBar: false,
@@ -95,6 +96,9 @@ const _globalReducer = createReducer(
filters: { ...state.filters, ...filter }
};
}),
+ on(Actions.setIsAdmin, (state, { payload: { isAdmin } }) => {
+ return { ...state, isAdmin };
+ }),
on(Actions.setTitle, (state, { payload: { title} }) => {
return { ...state, title };
}),
diff --git a/webapp/src/ts/selectors/index.ts b/webapp/src/ts/selectors/index.ts
index 3ad1f07e459..22ee420c7c0 100644
--- a/webapp/src/ts/selectors/index.ts
+++ b/webapp/src/ts/selectors/index.ts
@@ -28,6 +28,7 @@ export const Selectors = {
getShowActionBar: createSelector(getGlobalState, (globalState) => globalState.showActionBar),
getForms: createSelector(getGlobalState, (globalState) => globalState.forms),
getFilters: createSelector(getGlobalState, (globalState) => globalState.filters),
+ getIsAdmin: createSelector(getGlobalState, (globalState) => globalState.isAdmin),
getTitle: createSelector(getGlobalState, (globalState) => globalState.title),
getPrivacyPolicyAccepted: createSelector(getGlobalState, (globalState) => globalState.privacyPolicyAccepted),
getShowPrivacyPolicy: createSelector(getGlobalState, (globalState) => globalState.showPrivacyPolicy),
diff --git a/webapp/src/ts/services/android-api.service.ts b/webapp/src/ts/services/android-api.service.ts
index dcdd84431d1..a6dd05575c7 100644
--- a/webapp/src/ts/services/android-api.service.ts
+++ b/webapp/src/ts/services/android-api.service.ts
@@ -4,6 +4,7 @@ import { AndroidAppLauncherService } from '@mm-services/android-app-launcher.ser
import { GeolocationService } from '@mm-services/geolocation.service';
import { MRDTService } from '@mm-services/mrdt.service';
import { SessionService } from '@mm-services/session.service';
+import { SimprintsService } from '@mm-services/simprints.service';
import { NavigationService } from '@mm-services/navigation.service';
/**
@@ -22,6 +23,7 @@ export class AndroidApiService {
private geolocationService:GeolocationService,
private mrdtService:MRDTService,
private sessionService:SessionService,
+ private simprintsService:SimprintsService,
private zone:NgZone,
private navigationService:NavigationService,
) { }
@@ -201,6 +203,32 @@ export class AndroidApiService {
}
}
+ /**
+ * Handle the response from the simprints device
+ *
+ * @param requestType Indicates the response handler to call. Either 'identify' or 'register'.
+ * @param requestIdString The unique ID of the request to the simprints device.
+ * @param response The stringified JSON response from the simprints device.
+ */
+ simprintsResponse(requestType, requestIdString, response) {
+ const requestId = parseInt(requestIdString, 10);
+ if (isNaN(requestId)) {
+ return console.error(new Error('Unable to parse requestId: "' + requestIdString + '"'));
+ }
+ try {
+ response = JSON.parse(response);
+ } catch(e) {
+ return console.error(new Error('Unable to parse JSON response from android app: "' + response + '"'));
+ }
+ if (requestType === 'identify') {
+ this.simprintsService.identifyResponse(requestId, response);
+ } else if (requestType === 'register') {
+ this.simprintsService.registerResponse(requestId, response);
+ } else {
+ return console.error(new Error('Unknown request type: "' + requestType + '"'));
+ }
+ }
+
smsStatusUpdate(id, destination, content, status, detail) {
console.debug('smsStatusUpdate() :: ' +
' id=' + id +
@@ -224,6 +252,7 @@ export class AndroidApiService {
logout: () => this.runInZone('logout'),
mrdtResponse: (...args) => this.runInZone('mrdtResponse', args),
mrdtTimeTakenResponse: (...args) => this.runInZone('mrdtTimeTakenResponse', args),
+ simprintsResponse: (...args) => this.runInZone('simprintsResponse', args),
smsStatusUpdate: (...args) => this.runInZone('smsStatusUpdate', args),
locationPermissionRequestResolved: () => this.runInZone('locationPermissionRequestResolve'),
resolveCHTExternalAppResponse: (...args) => this.runInZone('resolveCHTExternalAppResponse', args),
diff --git a/webapp/src/ts/services/get-summaries.service.ts b/webapp/src/ts/services/get-summaries.service.ts
index ecc42da2180..c09151a672d 100644
--- a/webapp/src/ts/services/get-summaries.service.ts
+++ b/webapp/src/ts/services/get-summaries.service.ts
@@ -98,6 +98,7 @@ export class GetSummariesService {
contact_type: doc.contact_type,
contact: doc.contact && doc.contact._id,
lineage: this.getLineage(doc.parent),
+ simprints_id: doc.simprints_id,
date_of_death: doc.date_of_death,
muted: doc.muted
};
diff --git a/webapp/src/ts/services/integration-api.service.ts b/webapp/src/ts/services/integration-api.service.ts
index e92ab2d7cf4..d0e41c3db30 100644
--- a/webapp/src/ts/services/integration-api.service.ts
+++ b/webapp/src/ts/services/integration-api.service.ts
@@ -36,6 +36,7 @@ export class IntegrationApiService {
private mrdtService:MRDTService,
private markdownService:MarkdownService,
private settingsService:SettingsService,
+ // todo simprints
private androidApiService:AndroidApiService,
) {
this.DB = dbService;
diff --git a/webapp/src/ts/services/simprints.service.ts b/webapp/src/ts/services/simprints.service.ts
new file mode 100644
index 00000000000..c5869c4d27d
--- /dev/null
+++ b/webapp/src/ts/services/simprints.service.ts
@@ -0,0 +1,73 @@
+import { Injectable } from '@angular/core';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class SimprintsService {
+ private currentRequest:any = {};
+ private SP_ID_MASK = 0xFFFFF8;
+ private MAX_TIER = 3;
+ private resolvePromise;
+
+ private request(endpoint) {
+ // eslint-disable-next-line no-bitwise
+ const requestId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) & this.SP_ID_MASK;
+ this.currentRequest = {
+ id: requestId,
+ deferred: new Promise((resolve) => {
+ this.resolvePromise = resolve;
+ }),
+ };
+ // `call` needed to specify context: #3511
+ endpoint.call(window.medicmobile_android, this.currentRequest.id);
+ return this.currentRequest.deferred;
+ }
+
+ private isCurrentRequest(requestId) {
+ return this.currentRequest.id === requestId;
+ }
+
+ private parseTierNumber(tier) {
+ return Number.parseInt(tier.split('_')[1]);
+ }
+
+ enabled() {
+ try {
+ return !!(
+ window.medicmobile_android &&
+ typeof window.medicmobile_android.simprints_available === 'function' &&
+ window.medicmobile_android.simprints_available()
+ );
+ } catch (err) {
+ console.error(err);
+ return false;
+ }
+ }
+
+ register() {
+ return this.request(window.medicmobile_android.simprints_reg);
+ }
+
+ registerResponse(requestId, response) {
+ if (this.isCurrentRequest(requestId)) {
+ this.resolvePromise(response.id);
+ }
+ }
+
+ identify() {
+ return this.request(window.medicmobile_android.simprints_ident);
+ }
+
+ identifyResponse(requestId, identities) {
+ if (this.isCurrentRequest(requestId)) {
+ identities.forEach((identity) => {
+ // Tier from TIER_1 (best) to TIER_5 (worst)
+ identity.tierNumber = this.parseTierNumber(identity.tier);
+ });
+ identities = identities.filter((identity) => {
+ return identity.tierNumber <= this.MAX_TIER;
+ });
+ this.resolvePromise(identities);
+ }
+ }
+}
diff --git a/webapp/tests/karma/ts/app.component.spec.ts b/webapp/tests/karma/ts/app.component.spec.ts
index 374d7642dec..03f1a6cb1b5 100644
--- a/webapp/tests/karma/ts/app.component.spec.ts
+++ b/webapp/tests/karma/ts/app.component.spec.ts
@@ -144,6 +144,7 @@ describe('AppComponent', () => {
setPrivacyPolicyAccepted: sinon.stub(GlobalActions.prototype, 'setPrivacyPolicyAccepted'),
setShowPrivacyPolicy: sinon.stub(GlobalActions.prototype, 'setShowPrivacyPolicy'),
setForms: sinon.stub(GlobalActions.prototype, 'setForms'),
+ setIsAdmin: sinon.stub(GlobalActions.prototype, 'setIsAdmin')
};
analyticsActions = {
setAnalyticsModules: sinon.stub(AnalyticsActions.prototype, 'setAnalyticsModules')
@@ -248,6 +249,9 @@ describe('AppComponent', () => {
// start recurring processes
expect(recurringProcessManagerService.startUpdateRelativeDate.callCount).to.equal(1);
expect(recurringProcessManagerService.startUpdateReadDocsCount.callCount).to.equal(0);
+
+ expect(globalActions.setIsAdmin.callCount).to.equal(1);
+ expect(globalActions.setIsAdmin.args[0][0]).to.equal(true);
});
it('should subscribe to xmlFormService when initing forms', async () => {
diff --git a/webapp/tests/karma/ts/modules/contacts/contacts-filters.component.spec.ts b/webapp/tests/karma/ts/modules/contacts/contacts-filters.component.spec.ts
index f821244cbd3..ccdfc53f7b5 100644
--- a/webapp/tests/karma/ts/modules/contacts/contacts-filters.component.spec.ts
+++ b/webapp/tests/karma/ts/modules/contacts/contacts-filters.component.spec.ts
@@ -10,6 +10,7 @@ import sinon from 'sinon';
import { ContactsFiltersComponent } from '@mm-modules/contacts/contacts-filters.component';
import { FreetextFilterComponent } from '@mm-components/filters/freetext-filter/freetext-filter.component';
import { ResetFiltersComponent } from '@mm-components/filters/reset-filters/reset-filters.component';
+import { SimprintsFilterComponent } from '@mm-components/filters/simprints-filter/simprints-filter.component';
import { SortFilterComponent } from '@mm-components/filters/sort-filter/sort-filter.component';
describe('Reports Filters Component', () => {
@@ -28,6 +29,7 @@ describe('Reports Filters Component', () => {
ContactsFiltersComponent,
FreetextFilterComponent,
ResetFiltersComponent,
+ SimprintsFilterComponent,
SortFilterComponent,
],
providers: [
diff --git a/webapp/tests/karma/ts/modules/contacts/contacts.component.spec.ts b/webapp/tests/karma/ts/modules/contacts/contacts.component.spec.ts
index e8b06712495..2ae4211dada 100644
--- a/webapp/tests/karma/ts/modules/contacts/contacts.component.spec.ts
+++ b/webapp/tests/karma/ts/modules/contacts/contacts.component.spec.ts
@@ -11,6 +11,7 @@ import { ContactsComponent } from '@mm-modules/contacts/contacts.component';
import { Selectors } from '@mm-selectors/index';
import { ChangesService } from '@mm-services/changes.service';
import { SearchService } from '@mm-services/search.service';
+import { SimprintsService } from '@mm-services/simprints.service';
import { SettingsService } from '@mm-services/settings.service';
import { UserSettingsService } from '@mm-services/user-settings.service';
import { GetDataRecordsService } from '@mm-services/get-data-records.service';
@@ -22,6 +23,7 @@ import { ScrollLoaderProvider } from '@mm-providers/scroll-loader.provider';
import { ContactsFiltersComponent } from '@mm-modules/contacts/contacts-filters.component';
import { FreetextFilterComponent } from '@mm-components/filters/freetext-filter/freetext-filter.component';
import { NavigationComponent } from '@mm-components/navigation/navigation.component';
+import { SimprintsFilterComponent } from '@mm-components/filters/simprints-filter/simprints-filter.component';
import { SortFilterComponent } from '@mm-components/filters/sort-filter/sort-filter.component';
import { ResetFiltersComponent } from '@mm-components/filters/reset-filters/reset-filters.component';
import { TourService } from '@mm-services/tour.service';
@@ -46,6 +48,7 @@ describe('Contacts component', () => {
let scrollLoaderCallback;
let scrollLoaderProvider;
let contactListContains;
+ let simprintsService;
let tourService;
let exportService;
let xmlFormsService;
@@ -92,6 +95,10 @@ describe('Contacts component', () => {
scrollLoaderCallback = callback;
}
};
+ simprintsService = {
+ enabled: sinon.stub().resolves([]),
+ identify: sinon.stub().resolves([])
+ };
exportService = { export: sinon.stub() };
xmlFormsService = { subscribe: sinon.stub() };
@@ -124,6 +131,7 @@ describe('Contacts component', () => {
ContactsFiltersComponent,
FreetextFilterComponent,
NavigationComponent,
+ SimprintsFilterComponent,
ResetFiltersComponent,
SortFilterComponent,
],
@@ -131,6 +139,7 @@ describe('Contacts component', () => {
provideMockStore({ selectors: mockedSelectors }),
{ provide: ChangesService, useValue: changesService },
{ provide: SearchService, useValue: searchService },
+ { provide: SimprintsService, useValue: simprintsService },
{ provide: SettingsService, useValue: settingsService },
{ provide: UserSettingsService, useValue: userSettingsService },
{ provide: GetDataRecordsService, useValue: getDataRecordsService },
@@ -338,6 +347,7 @@ describe('Contacts component', () => {
}));
it('when paginating, does not skip the extra place for admins #4085', fakeAsync(() => {
+ store.overrideSelector(Selectors.getIsAdmin, true);
userSettingsService.get.resolves({ facility_id: undefined });
const searchResult = { _id: 'search-result' };
searchResults = Array(50).fill(searchResult);
@@ -379,6 +389,7 @@ describe('Contacts component', () => {
}));
it('when refreshing list as admin, does not modify limit #4085', fakeAsync(() => {
+ store.overrideSelector(Selectors.getIsAdmin, true);
userSettingsService.get.resolves({ facility_id: undefined });
const searchResult = { _id: 'search-result' };
searchResults = Array(60).fill(searchResult);
@@ -564,6 +575,7 @@ describe('Contacts component', () => {
it('when handling deletes, does not shorten the list #4080', fakeAsync(() => {
const changesCallback = changesService.subscribe.args[0][0].callback;
+ store.overrideSelector(Selectors.getIsAdmin, true);
userSettingsService.get.resolves({ facility_id: undefined });
const searchResult = { _id: 'search-result' };
searchResults = Array(60).fill(searchResult);
diff --git a/webapp/tests/karma/ts/reducers/global.spec.ts b/webapp/tests/karma/ts/reducers/global.spec.ts
index 5aa927142d6..897508e0ee7 100644
--- a/webapp/tests/karma/ts/reducers/global.spec.ts
+++ b/webapp/tests/karma/ts/reducers/global.spec.ts
@@ -129,6 +129,11 @@ describe('Global Reducer', () => {
expect(state).to.deep.equal({ filters: { search: 'aaaaa', forms: [{ id: 'f2' }, { id: 'f3' }] } });
});
+ it('should set is Admin', () => {
+ expect(globalReducer(state, Actions.setIsAdmin(true))).to.deep.equal({ isAdmin: true });
+ expect(globalReducer(state, Actions.setIsAdmin(false))).to.deep.equal({ isAdmin: false });
+ });
+
it('should set left action bar', () => {
const left = { some: 'settings' };
diff --git a/webapp/tests/karma/ts/selectors/index.spec.ts b/webapp/tests/karma/ts/selectors/index.spec.ts
index f403ee23571..5e0e4d0239f 100644
--- a/webapp/tests/karma/ts/selectors/index.spec.ts
+++ b/webapp/tests/karma/ts/selectors/index.spec.ts
@@ -18,6 +18,7 @@ const state = {
showActionBar: 'is showing action bar',
forms: ['these', 'are', 'some', 'forms'],
filters: { some: 'filters' },
+ isAdmin: 'is it an admin',
navigation: {
cancelCallback: function() {},
preventNavigation: 'prevent',
@@ -171,6 +172,10 @@ describe('Selectors', () => {
expect(Selectors.getFilters(state)).to.deep.equal(clonedState.global.filters);
});
+ it('should getIsAdmin', () => {
+ expect(Selectors.getIsAdmin(state)).to.equal(clonedState.global.isAdmin);
+ });
+
it('should getCancelCallback', () => {
expect(Selectors.getCancelCallback(state)).to.deep.equal(clonedState.global.navigation.cancelCallback);
});
diff --git a/webapp/tests/karma/ts/services/android-api.service.spec.ts b/webapp/tests/karma/ts/services/android-api.service.spec.ts
index 80e01389e54..a885db8b008 100644
--- a/webapp/tests/karma/ts/services/android-api.service.spec.ts
+++ b/webapp/tests/karma/ts/services/android-api.service.spec.ts
@@ -7,6 +7,7 @@ import { AndroidApiService } from '@mm-services/android-api.service';
import { SessionService } from '@mm-services/session.service';
import { GeolocationService } from '@mm-services/geolocation.service';
import { MRDTService } from '@mm-services/mrdt.service';
+import { SimprintsService } from '@mm-services/simprints.service';
import { NavigationService } from '@mm-services/navigation.service';
import { AndroidAppLauncherService } from '@mm-services/android-app-launcher.service';
@@ -16,6 +17,7 @@ describe('AndroidApi service', () => {
let sessionService;
let mrdtService;
let geolocationService;
+ let simprintsService;
let consoleErrorMock;
let navigationService;
let androidAppLauncherService;
@@ -36,6 +38,11 @@ describe('AndroidApi service', () => {
permissionRequestResolved: sinon.stub()
};
+ simprintsService = {
+ identifyResponse: sinon.stub(),
+ registerResponse: sinon.stub()
+ };
+
navigationService = {
goBack: sinon.stub(),
goToPrimaryTab: sinon.stub(),
@@ -51,6 +58,7 @@ describe('AndroidApi service', () => {
providers: [
{ provide: SessionService, useValue: sessionService },
{ provide: GeolocationService, useValue: geolocationService },
+ { provide: SimprintsService, useValue: simprintsService },
{ provide: MRDTService, useValue: mrdtService },
{ provide: NavigationService, useValue: navigationService },
{ provide: AndroidAppLauncherService, useValue: androidAppLauncherService },
@@ -64,6 +72,59 @@ describe('AndroidApi service', () => {
sinon.restore();
});
+ describe('simprintsResponse', () => {
+ it('errors when given string id', () => {
+ service.v1.simprintsResponse(null, 'hello', null);
+ expect(consoleErrorMock.callCount).to.equal(1);
+ expect(consoleErrorMock.args[0][0].message).to.equal('Unable to parse requestId: "hello"');
+ expect(simprintsService.identifyResponse.callCount).to.equal(0);
+ expect(simprintsService.registerResponse.callCount).to.equal(0);
+ });
+
+ it('errors when given invalid response', () => {
+ service.v1.simprintsResponse(null, '1', 'not json');
+ expect(consoleErrorMock.callCount).to.equal(1);
+ expect(consoleErrorMock.args[0][0].message).to.equal(
+ 'Unable to parse JSON response from android app: "not json"'
+ );
+ expect(simprintsService.identifyResponse.callCount).to.equal(0);
+ expect(simprintsService.registerResponse.callCount).to.equal(0);
+ });
+
+ it('errors when given unknown request type', () => {
+ service.v1.simprintsResponse('query', '1', '{ "id": 153 }');
+ expect(consoleErrorMock.callCount).to.equal(1);
+ expect(consoleErrorMock.args[0][0].message).to.equal('Unknown request type: "query"');
+ expect(simprintsService.identifyResponse.callCount).to.equal(0);
+ expect(simprintsService.registerResponse.callCount).to.equal(0);
+ });
+
+ it('calls identify', () => {
+ const expectedRequestId = 55498890;
+ const expectedResponse = [
+ { id: 153, tier: 'TIER_1' },
+ { id: 486, tier: 'TIER_5' }
+ ];
+ service.v1.simprintsResponse('identify', expectedRequestId.toString(), JSON.stringify(expectedResponse));
+ expect(consoleErrorMock.callCount).to.equal(0);
+ expect(simprintsService.identifyResponse.callCount).to.equal(1);
+ expect(simprintsService.identifyResponse.args[0][0]).to.equal(expectedRequestId);
+ expect(simprintsService.identifyResponse.args[0][1]).to.deep.equal(expectedResponse);
+ expect(simprintsService.registerResponse.callCount).to.equal(0);
+ });
+
+ it('calls register', () => {
+ const expectedRequestId = 54895590;
+ const expectedResponse = { id: 849556216 };
+ service.v1.simprintsResponse('register', expectedRequestId.toString(), JSON.stringify(expectedResponse));
+ expect(consoleErrorMock.callCount).to.equal(0);
+ expect(simprintsService.identifyResponse.callCount).to.equal(0);
+ expect(simprintsService.registerResponse.callCount).to.equal(1);
+ expect(simprintsService.registerResponse.args[0][0]).to.equal(expectedRequestId);
+ expect(simprintsService.registerResponse.args[0][1]).to.deep.equal(expectedResponse);
+ });
+ });
+
describe('logout', () => {
it('should call sessionService logout', () => {
service.logout();
diff --git a/webapp/tests/karma/ts/services/get-summaries.service.spec.ts b/webapp/tests/karma/ts/services/get-summaries.service.spec.ts
index 6f96e98fcc3..365819ccd1f 100644
--- a/webapp/tests/karma/ts/services/get-summaries.service.spec.ts
+++ b/webapp/tests/karma/ts/services/get-summaries.service.spec.ts
@@ -250,6 +250,7 @@ describe('GetSummaries service', () => {
_id: 'g'
}
},
+ simprints_id: '987',
muted: true
} },
] });
@@ -268,6 +269,7 @@ describe('GetSummaries service', () => {
contact: 'c',
lineage: [],
date_of_death: 999,
+ simprints_id: undefined,
muted: undefined
},
{
@@ -280,6 +282,7 @@ describe('GetSummaries service', () => {
contact: undefined,
lineage: [ 'f', 'g' ],
date_of_death: undefined,
+ simprints_id: '987',
muted: true
}
]);
diff --git a/webapp/tests/karma/ts/services/simprints.service.spec.ts b/webapp/tests/karma/ts/services/simprints.service.spec.ts
new file mode 100644
index 00000000000..76496f5f6f1
--- /dev/null
+++ b/webapp/tests/karma/ts/services/simprints.service.spec.ts
@@ -0,0 +1,106 @@
+import { TestBed } from '@angular/core/testing';
+import sinon from 'sinon';
+import { expect } from 'chai';
+import { SimprintsService } from '@mm-services/simprints.service';
+
+describe('SimprintsService', () => {
+ let service: SimprintsService;
+ let medicmobile_android;
+ let simprints_reg;
+ let simprints_ident;
+
+ const assertCalledOnCorrectObject = function () {
+ // If the medicmobile_android functions are not called with `this` parameter
+ // correctly set, we receive the cryptic error:
+ // Java bridge method cannot be invoked on a non-injected object
+ // This function ensures that stubbed functions are being called on the
+ // correct object.
+ if(this !== medicmobile_android) {
+ throw new Error('Java bridge method cannot be invoked on a non-injected object');
+ }
+ };
+
+ beforeEach(() => {
+ simprints_reg = sinon.stub().callsFake(assertCalledOnCorrectObject);
+ simprints_ident = sinon.stub().callsFake(assertCalledOnCorrectObject);
+
+ medicmobile_android = {
+ simprints_reg,
+ simprints_ident
+ };
+
+ window.medicmobile_android = medicmobile_android;
+ service = TestBed.inject(SimprintsService);
+ });
+
+ describe('register', () => {
+ it('calls android endpoint and waits for response', () => {
+ const expected = 54685165;
+ const response = service.register();
+ response.then(actual => {
+ expect(simprints_reg.callCount).to.equal(1);
+ expect(simprints_ident.callCount).to.equal(0);
+ expect(actual).to.equal(expected);
+ }).catch((err) => {
+ throw err;
+ });
+ const requestId = simprints_reg.args[0][0];
+ service.registerResponse(requestId, { id: expected });
+ return response;
+ });
+ });
+
+ describe('identify', () => {
+
+ it('calls android endpoint and waits for response', () => {
+ const given = [
+ { id: 123, tier: 'TIER_1' },
+ { id: 456, tier: 'TIER_2' },
+ { id: 789, tier: 'TIER_3' }
+ ];
+ const expected = [
+ { id: 123, tier: 'TIER_1', tierNumber: 1 },
+ { id: 456, tier: 'TIER_2', tierNumber: 2 },
+ { id: 789, tier: 'TIER_3', tierNumber: 3 }
+ ];
+ const response = service.identify();
+ response.then(actual => {
+ expect(simprints_reg.callCount).to.equal(0);
+ expect(simprints_ident.callCount).to.equal(1);
+ expect(actual).to.deep.equal(expected);
+ }).catch(err => {
+ throw err;
+ });
+ const requestId = simprints_ident.args[0][0];
+ service.identifyResponse(requestId, given);
+ return response;
+ });
+
+ it('filters out tier 4 and 5 matches', () => {
+ const given = [
+ { id: 123, tier: 'TIER_1' },
+ { id: 456, tier: 'TIER_2' },
+ { id: 789, tier: 'TIER_3' },
+ { id: 147, tier: 'TIER_4' },
+ { id: 258, tier: 'TIER_5' }
+ ];
+ const expected = [
+ { id: 123, tier: 'TIER_1', tierNumber: 1 },
+ { id: 456, tier: 'TIER_2', tierNumber: 2 },
+ { id: 789, tier: 'TIER_3', tierNumber: 3 }
+ ];
+ const response = service.identify();
+ response.then(actual => {
+ expect(simprints_reg.callCount).to.equal(0);
+ expect(simprints_ident.callCount).to.equal(1);
+ expect(actual).to.deep.equal(expected);
+ }).catch(err => {
+ throw err;
+ });
+ const requestId = simprints_ident.args[0][0];
+ service.identifyResponse(requestId, given);
+ return response;
+ });
+
+ });
+});
diff --git a/webapp/tests/mocha/unit/views/doc_summaries_by_id.spec.js b/webapp/tests/mocha/unit/views/doc_summaries_by_id.spec.js
index a9bb6811704..88e520fa28e 100644
--- a/webapp/tests/mocha/unit/views/doc_summaries_by_id.spec.js
+++ b/webapp/tests/mocha/unit/views/doc_summaries_by_id.spec.js
@@ -21,6 +21,7 @@ const person = {
};
const personBis = Object.assign({}, person, {
_id: '2bba279f-8ad9-4823-be69-a8eb09879402-bis',
+ simprints_id: 22,
date_of_death: 10,
type: 'contact',
contact_type: 'patient',
@@ -214,7 +215,7 @@ const jsonHouseholdBis = Object.assign({}, jsonHousehold, {
});
describe('doc_summaries_by_id view', () => {
- it('indexes name, phone, type, contact, lineage, dod for non-data-records', () => {
+ it('indexes name, phone, type, contact, lineage, simprints, dod for non-data-records', () => {
const map = utils.loadView('', 'medic', 'doc_summaries_by_id');
const emitted = map(person, true) && map(personBis, true);
@@ -227,6 +228,7 @@ describe('doc_summaries_by_id view', () => {
type: 'person',
contact_type: undefined,
lineage: ['1a1aac55-04d6-40dc-aae2-e67a75a1496d'],
+ simprints_id: undefined,
date_of_death: undefined,
contact: undefined,
muted: undefined
@@ -241,6 +243,7 @@ describe('doc_summaries_by_id view', () => {
type: 'contact',
contact_type: 'patient',
lineage: ['1a1aac55-04d6-40dc-aae2-e67a75a1496d'],
+ simprints_id: 22,
date_of_death: 10,
contact: undefined,
muted: true