From 13e3133c44b3fba3c52171826e251b467cc29229 Mon Sep 17 00:00:00 2001 From: Prajwol Shrestha Date: Tue, 16 May 2023 10:37:36 +0545 Subject: [PATCH 01/56] Added sms form in app settings, trigger in registration and unit test skeleton --- config/default/app_settings.json | 7733 ++++++++++++++++- .../test/unit/transitions/registration.js | 3581 ++------ .../sentinel/transitions/registration.spec.js | 140 +- 3 files changed, 8150 insertions(+), 3304 deletions(-) diff --git a/config/default/app_settings.json b/config/default/app_settings.json index cc4f0f3b0fd..79724ad0ae8 100644 --- a/config/default/app_settings.json +++ b/config/default/app_settings.json @@ -1,67 +1,68 @@ { - "languages": [ + "locales": [ { - "locale": "bm", - "enabled": true + "code": "en", + "name": "English" }, { - "locale": "en", - "enabled": true + "code": "es", + "name": "Español (Spanish)" }, { - "locale": "es", - "enabled": true + "code": "fr", + "name": "Français (French)" }, { - "locale": "fr", - "enabled": true + "code": "ne", + "name": "नेपाली (Nepali)" }, { - "locale": "hi", - "enabled": true + "code": "sw", + "name": "Kiswahili (Swahili)" }, { - "locale": "id", - "enabled": true + "code": "hi", + "name": "हिन्दी (Hindi)" }, { - "locale": "ne", - "enabled": true - }, - { - "locale": "sw", - "enabled": true + "code": "id", + "name": "Bahasa Indonesia (Indonesian)" } ], "locale": "en", "locale_outgoing": "en", - "date_format": "D MMM, YYYY", - "reported_date_format": "DD-MMM-YYYY HH:mm:ss", + "muvuku_webapp_url": "/medic-reporter/_design/medic-reporter/_rewrite/?_embed_mode=2&_locale=en#/generic-anc.json", + "date_format": "ddd, MMM Do, YYYY", + "reported_date_format": "ddd, MMM Do, YYYY [at] h:mm A", "forms_only_mode": false, - "place_hierarchy_types": [ - "district_hospital", - "health_center" - ], "default_responses": { - "start_date": "" + "start_date": "1970-01-01" }, "district_admins_access_unallocated_messages": false, "public_access": false, - "default_country_code": 1, - "gateway_number": "+13125551212", - "dhis_data_sets": [ - { - "id": "VMuFODsyWaO", - "translation_key": "dhis.dataset.monthly.integration" - } - ], - "schedule_morning_hours": 0, + "default_country_code": "254", + "gateway_number": "+254741095736", + "anc_forms": { + "registration": "R", + "registrationLmp": "P", + "visit": "V", + "delivery": "D", + "flag": "F" + }, + "schedule_morning_hours": 6, "schedule_morning_minutes": 0, - "schedule_evening_hours": 23, + "schedule_evening_hours": 20, "schedule_evening_minutes": 0, "synthetic_date": "", "contact_display_short": "clinic.name", "roles": { + "national_admin": { + "name": "usertype.national-manager" + }, + "district_admin": { + "name": "usertype.district-manager", + "offline": true + }, "data_entry": { "name": "usertype.data-entry" }, @@ -70,385 +71,7427 @@ }, "gateway": { "name": "usertype.gateway" - }, - "program_officer": { - "name": "usertype.program-officer" - }, - "crfo": { - "name": "usertype.crfo" - }, - "chw_supervisor": { - "name": "usertype.chw-supervisor", - "offline": true - }, - "chw": { - "name": "usertype.chw", - "offline": true } }, "permissions": { - "can_access_gateway_api": [ - "gateway" - ], - "can_bulk_delete_reports": [ - "program_officer", - "chw_supervisor", - "chw" - ], - "can_configure": [ - "program_officer", - "crfo" - ], - "can_create_people": [ - "program_officer", - "chw_supervisor", - "chw" + "can_export_messages": [ + "national_admin", + "district_admin", + "analytics" ], - "can_create_places": [ - "program_officer", - "chw_supervisor", - "chw" + "can_export_forms": [ + "national_admin", + "district_admin", + "analytics" ], - "can_create_records": [ - "data_entry", - "gateway", - "program_officer", - "chw_supervisor", - "chw" + "can_export_contacts": [ + "national_admin", + "district_admin" ], - "can_create_users": [ - "program_officer" + "can_export_feedback": [ + "national_admin" ], - "can_delete_contacts": [ - "program_officer", - "chw_supervisor", - "chw" + "can_view_analytics": [ + "national_admin", + "district_admin", + "analytics" ], - "can_delete_messages": [ - "program_officer", - "chw_supervisor", - "chw" + "can_view_data_records": [ + "national_admin", + "district_admin", + "analytics", + "gateway" ], - "can_delete_reports": [ - "program_officer", - "chw_supervisor", - "chw" + "can_view_unallocated_data_records": [ + "national_admin", + "district_admin", + "gateway" ], - "can_delete_users": [ - "program_officer" + "can_view_users": [ + "national_admin" ], "can_edit": [ - "gateway", - "program_officer", - "chw_supervisor", - "chw" - ], - "can_edit_profile": [ - "program_officer", - "chw_supervisor", - "chw" + "national_admin", + "district_admin", + "gateway" ], - "can_edit_verification": [ - "program_officer", - "chw_supervisor", - "chw" + "can_log_out_on_android": [ + "district_admin", + "national_admin" ], - "can_export_all": [ - "program_officer", - "crfo" + "can_update_messages": [ + "national_admin", + "district_admin", + "gateway" ], - "can_export_contacts": [ - "program_officer", - "chw_supervisor", - "chw" + "can_update_users": [ + "national_admin" ], - "can_export_feedback": [ - "program_officer" + "can_export_dhis": [ + "national_admin" ], - "can_export_messages": [ - "analytics", - "program_officer", - "chw_supervisor", - "chw" + "can_update_people": [ + "national_admin", + "district_admin" ], - "can_log_out_on_android": [], "can_update_places": [ - "program_officer", - "chw_supervisor", - "chw" - ], - "can_update_reports": [ - "program_officer", - "chw_supervisor", - "chw" - ], - "can_update_users": [ - "program_officer" + "national_admin", + "district_admin" ], - "can_export_dhis": [ + "can_create_records": [ "national_admin", - "crfo" + "district_admin", + "data_entry", + "gateway" ], - "can_verify_reports": [ - "program_officer", - "chw_supervisor", - "chw" + "can_access_gateway_api": [ + "gateway" ], - "can_view_analytics": [ - "analytics", - "program_officer", - "chw_supervisor", - "chw" + "can_create_users": [ + "national_admin" ], - "can_view_analytics_tab": [ - "program_officer", - "chw_supervisor", - "chw" + "can_create_people": [ + "national_admin", + "district_admin" ], - "can_view_call_action": [ - "program_officer", - "chw_supervisor", - "chw" + "can_create_places": [ + "national_admin", + "district_admin" ], - "can_view_contacts": [ - "program_officer", - "chw_supervisor", - "chw" + "can_view_tasks": [ + "district_admin" ], - "can_view_contacts_tab": [ - "program_officer", - "chw_supervisor", - "chw" + "can_view_tasks_tab": [ + "district_admin" ], - "can_view_last_visited_date": [], - "can_view_uhc_stats": [], - "can_view_message_action": [ - "program_officer", - "chw_supervisor", - "chw" + "can_configure": [ + "national_admin" ], "can_view_messages": [ - "program_officer", - "chw_supervisor", - "chw" + "national_admin", + "district_admin" ], "can_view_messages_tab": [ - "program_officer", - "chw_supervisor", - "chw" - ], - "can_view_outgoing_messages": [ - "program_officer" + "national_admin", + "district_admin" ], "can_view_reports": [ - "program_officer", - "chw_supervisor", - "chw" + "national_admin", + "district_admin" ], "can_view_reports_tab": [ - "program_officer", - "chw_supervisor", - "chw" + "national_admin", + "district_admin" ], - "can_view_tasks": [ - "chw_supervisor", - "chw" + "can_view_analytics_tab": [ + "national_admin", + "district_admin" ], - "can_view_tasks_tab": [ - "chw_supervisor", - "chw" + "can_view_contacts": [ + "national_admin", + "district_admin" ], - "can_view_tasks_group": [ - "chw" + "can_view_contacts_tab": [ + "national_admin", + "district_admin" ], - "can_view_unallocated_data_records": [ - "gateway", - "program_officer", - "chw_supervisor", - "chw" + "can_edit_profile": [ + "national_admin", + "district_admin" ], - "can_view_users": [ - "program_officer" + "can_delete_reports": [ + "national_admin", + "district_admin" + ], + "can_bulk_delete_reports": [ + "national_admin", + "district_admin" + ], + "can_update_reports": [ + "national_admin", + "district_admin" ], - "can_write_wealth_quintiles": [], - "can_aggregate_targets": [ - "chw_supervisor" + "can_delete_contacts": [ + "national_admin", + "district_admin" + ], + "can_delete_messages": [ + "national_admin", + "district_admin" + ], + "can_delete_users": [ + "national_admin" + ], + "can_view_outgoing_messages": [ + "national_admin" ], - "can_upgrade": [ - "program_officer" + "can_export_all": [ + "national_admin" ], + "can_view_uhc_stats": [], "can_view_old_filter_and_search": [], "can_view_old_action_bar": [] }, - "uhc": { - "contacts_default_sort": "", - "visit_count": { - "month_start_date": 1, - "visit_count_goal": 0 - } - }, - "death_reporting": { - "mark_deceased_forms": [ - "death_report" - ], - "date_field": "fields.death_details.date_of_death", - "undo_deceased_forms": [ - "undo_death_report" - ] + "setup_complete": true, + "outgoing_phone_replace": { + "match": "", + "replace": "" }, - "contact_types": [ + "outgoing_phone_filters": [ { - "id": "district_hospital", - "name_key": "contact.type.district_hospital", - "group_key": "contact.type.district_hospital.plural", - "create_key": "contact.type.district_hospital.new", - "edit_key": "contact.type.place.edit", - "icon": "medic-district-hospital", - "create_form": "form:contact:district_hospital:create", - "edit_form": "form:contact:district_hospital:edit" - }, + "match": "", + "replace": "" + } + ], + "id_format": "1111", + "alerts": [ { - "id": "health_center", - "name_key": "contact.type.health_center", - "group_key": "contact.type.health_center.plural", - "create_key": "contact.type.health_center.new", - "edit_key": "contact.type.place.edit", - "parents": [ - "district_hospital" - ], - "icon": "medic-health-center", - "create_form": "form:contact:health_center:create", - "edit_form": "form:contact:health_center:edit" - }, + "form": "", + "condition": "", + "message": "", + "recipient": "" + } + ], + "reminders": [ { - "id": "clinic", - "name_key": "contact.type.clinic", - "group_key": "contact.type.clinic.plural", - "create_key": "contact.type.clinic.new", - "edit_key": "contact.type.place.edit", - "parents": [ - "health_center" - ], - "icon": "medic-clinic", - "create_form": "form:contact:clinic:create", - "edit_form": "form:contact:clinic:edit", - "count_visits": true - }, + "form": "", + "text_expression": "", + "cron": "", + "mute_after_form_for": "", + "message": "" + } + ], + "replications": [ { - "id": "person", - "name_key": "contact.type.person", - "group_key": "contact.type.person.plural", - "create_key": "contact.type.person.new", - "edit_key": "contact.type.person.edit", - "primary_contact_key": "clinic.field.contact", - "parents": [ - "district_hospital", - "health_center", - "clinic" - ], - "icon": "medic-person", - "create_form": "form:contact:person:create", - "edit_form": "form:contact:person:edit", - "person": true + "fromSuffix": "user-[^\\-]+-meta", + "toSuffix": "users-meta", + "text_expression": "", + "cron": "0 2 * * *" } ], - "contact_summary": "var ContactSummary = {}; !function(e,t){if('object'==typeof exports&&'object'==typeof module)module.exports=t();else if('function'==typeof define&&define.amd)define([],t);else{var n=t();for(var r in n)('object'==typeof exports?exports:e)[r]=n[r]}}(ContactSummary,(function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){'undefined'!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:'Module'}),Object.defineProperty(e,'__esModule',{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&'object'==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,'default',{enumerable:!0,value:e}),2&t&&'string'!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,'a',t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p='',n(n.s=1)}([function(e,t,n){(function(e){e.exports=function(){'use strict';var t,n;function r(){return t.apply(null,arguments)}function i(e){return e instanceof Array||'[object Array]'===Object.prototype.toString.call(e)}function s(e){return null!=e&&'[object Object]'===Object.prototype.toString.call(e)}function a(e){return void 0===e}function o(e){return'number'==typeof e||'[object Number]'===Object.prototype.toString.call(e)}function l(e){return e instanceof Date||'[object Date]'===Object.prototype.toString.call(e)}function u(e,t){var n,r=[];for(n=0;n>>0,r=0;r0)for(n=0;n=0?n?'+':'':'-')+Math.pow(10,Math.max(0,i)).toString().substr(1)+r}var L=/(\\[[^\\[]*\\])|(\\\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,A=/(\\[[^\\[]*\\])|(\\\\)?(LTS|LT|LL?L?L?|l{1,4})/g,V={},I={};function G(e,t,n,r){var i=r;'string'==typeof r&&(i=function(){return this[r]()}),e&&(I[e]=i),t&&(I[t[0]]=function(){return E(i.apply(this,arguments),t[1],t[2])}),n&&(I[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),e)})}function j(e,t){return e.isValid()?(t=Z(t,e.localeData()),V[t]=V[t]||function(e){var t,n,r,i=e.match(L);for(t=0,n=i.length;t=0&&A.test(e);)e=e.replace(A,r),A.lastIndex=0,n-=1;return e}var z=/\\d/,q=/\\d\\d/,$=/\\d{3}/,J=/\\d{4}/,B=/[+-]?\\d{6}/,Q=/\\d\\d?/,X=/\\d\\d\\d\\d?/,K=/\\d\\d\\d\\d\\d\\d?/,ee=/\\d{1,3}/,te=/\\d{1,4}/,ne=/[+-]?\\d{1,6}/,re=/\\d+/,ie=/[+-]?\\d+/,se=/Z|[+-]\\d\\d:?\\d\\d/gi,ae=/Z|[+-]\\d\\d(?::?\\d\\d)?/gi,oe=/[0-9]{0,256}['a-z\\u00A0-\\u05FF\\u0700-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFF07\\uFF10-\\uFFEF]{1,256}|[\\u0600-\\u06FF\\/]{1,256}(\\s*?[\\u0600-\\u06FF]{1,256}){1,2}/i,le={};function ue(e,t,n){le[e]=x(t)?t:function(e,r){return e&&n?n:t}}function de(e,t){return d(le,e)?le[e](t._strict,t._locale):new RegExp(ce(e.replace('\\\\','').replace(/\\\\(\\[)|\\\\(\\])|\\[([^\\]\\[]*)\\]|\\\\(.)/g,(function(e,t,n,r,i){return t||n||r||i}))))}function ce(e){return e.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g,'\\\\$&')}var he={};function fe(e,t){var n,r=t;for('string'==typeof e&&(e=[e]),o(t)&&(r=function(e,n){n[t]=D(e)}),n=0;n68?1900:2e3)};var ge,ve=we('FullYear',!0);function we(e,t){return function(n){return null!=n?(De(this,e,n),r.updateOffset(this,t),this):ke(this,e)}}function ke(e,t){return e.isValid()?e._d['get'+(e._isUTC?'UTC':'')+t]():NaN}function De(e,t,n){e.isValid()&&!isNaN(n)&&('FullYear'===t&&ye(e.year())&&1===e.month()&&29===e.date()?e._d['set'+(e._isUTC?'UTC':'')+t](n,e.month(),Me(n,e.month())):e._d['set'+(e._isUTC?'UTC':'')+t](n))}function Me(e,t){if(isNaN(e)||isNaN(t))return NaN;var n,r=(t%(n=12)+n)%n;return e+=(t-r)/12,1===r?ye(e)?29:28:31-r%7%2}ge=Array.prototype.indexOf?Array.prototype.indexOf:function(e){var t;for(t=0;t=0?(o=new Date(e+400,t,n,r,i,s,a),isFinite(o.getFullYear())&&o.setFullYear(e)):o=new Date(e,t,n,r,i,s,a),o}function Fe(e){var t;if(e<100&&e>=0){var n=Array.prototype.slice.call(arguments);n[0]=e+400,t=new Date(Date.UTC.apply(null,n)),isFinite(t.getUTCFullYear())&&t.setUTCFullYear(e)}else t=new Date(Date.UTC.apply(null,arguments));return t}function Ne(e,t,n){var r=7+t-n;return-(7+Fe(e,0,r).getUTCDay()-t)%7+r-1}function He(e,t,n,r,i){var s,a,o=1+7*(t-1)+(7+n-r)%7+Ne(e,r,i);return o<=0?a=pe(s=e-1)+o:o>pe(e)?(s=e+1,a=o-pe(e)):(s=e,a=o),{year:s,dayOfYear:a}}function Ue(e,t,n){var r,i,s=Ne(e.year(),t,n),a=Math.floor((e.dayOfYear()-s-1)/7)+1;return a<1?r=a+Ee(i=e.year()-1,t,n):a>Ee(e.year(),t,n)?(r=a-Ee(e.year(),t,n),i=e.year()+1):(i=e.year(),r=a),{week:r,year:i}}function Ee(e,t,n){var r=Ne(e,t,n),i=Ne(e+1,t,n);return(pe(e)-r+i)/7}function Le(e,t){return e.slice(t,7).concat(e.slice(0,t))}G('w',['ww',2],'wo','week'),G('W',['WW',2],'Wo','isoWeek'),C('week','w'),C('isoWeek','W'),U('week',5),U('isoWeek',5),ue('w',Q),ue('ww',Q,q),ue('W',Q),ue('WW',Q,q),_e(['w','ww','W','WW'],(function(e,t,n,r){t[r.substr(0,1)]=D(e)})),G('d',0,'do','day'),G('dd',0,0,(function(e){return this.localeData().weekdaysMin(this,e)})),G('ddd',0,0,(function(e){return this.localeData().weekdaysShort(this,e)})),G('dddd',0,0,(function(e){return this.localeData().weekdays(this,e)})),G('e',0,0,'weekday'),G('E',0,0,'isoWeekday'),C('day','d'),C('weekday','e'),C('isoWeekday','E'),U('day',11),U('weekday',11),U('isoWeekday',11),ue('d',Q),ue('e',Q),ue('E',Q),ue('dd',(function(e,t){return t.weekdaysMinRegex(e)})),ue('ddd',(function(e,t){return t.weekdaysShortRegex(e)})),ue('dddd',(function(e,t){return t.weekdaysRegex(e)})),_e(['dd','ddd','dddd'],(function(e,t,n,r){var i=n._locale.weekdaysParse(e,r,n._strict);null!=i?t.d=i:f(n).invalidWeekday=e})),_e(['d','e','E'],(function(e,t,n,r){t[r]=D(e)}));var Ae='Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),Ve='Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),Ie='Su_Mo_Tu_We_Th_Fr_Sa'.split('_');function Ge(e,t,n){var r,i,s,a=e.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],r=0;r<7;++r)s=h([2e3,1]).day(r),this._minWeekdaysParse[r]=this.weekdaysMin(s,'').toLocaleLowerCase(),this._shortWeekdaysParse[r]=this.weekdaysShort(s,'').toLocaleLowerCase(),this._weekdaysParse[r]=this.weekdays(s,'').toLocaleLowerCase();return n?'dddd'===t?-1!==(i=ge.call(this._weekdaysParse,a))?i:null:'ddd'===t?-1!==(i=ge.call(this._shortWeekdaysParse,a))?i:null:-1!==(i=ge.call(this._minWeekdaysParse,a))?i:null:'dddd'===t?-1!==(i=ge.call(this._weekdaysParse,a))||-1!==(i=ge.call(this._shortWeekdaysParse,a))||-1!==(i=ge.call(this._minWeekdaysParse,a))?i:null:'ddd'===t?-1!==(i=ge.call(this._shortWeekdaysParse,a))||-1!==(i=ge.call(this._weekdaysParse,a))||-1!==(i=ge.call(this._minWeekdaysParse,a))?i:null:-1!==(i=ge.call(this._minWeekdaysParse,a))||-1!==(i=ge.call(this._weekdaysParse,a))||-1!==(i=ge.call(this._shortWeekdaysParse,a))?i:null}var je=oe,Ze=oe,ze=oe;function qe(){function e(e,t){return t.length-e.length}var t,n,r,i,s,a=[],o=[],l=[],u=[];for(t=0;t<7;t++)n=h([2e3,1]).day(t),r=this.weekdaysMin(n,''),i=this.weekdaysShort(n,''),s=this.weekdays(n,''),a.push(r),o.push(i),l.push(s),u.push(r),u.push(i),u.push(s);for(a.sort(e),o.sort(e),l.sort(e),u.sort(e),t=0;t<7;t++)o[t]=ce(o[t]),l[t]=ce(l[t]),u[t]=ce(u[t]);this._weekdaysRegex=new RegExp('^('+u.join('|')+')','i'),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp('^('+l.join('|')+')','i'),this._weekdaysShortStrictRegex=new RegExp('^('+o.join('|')+')','i'),this._weekdaysMinStrictRegex=new RegExp('^('+a.join('|')+')','i')}function $e(){return this.hours()%12||12}function Je(e,t){G(e,0,0,(function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)}))}function Be(e,t){return t._meridiemParse}G('H',['HH',2],0,'hour'),G('h',['hh',2],0,$e),G('k',['kk',2],0,(function(){return this.hours()||24})),G('hmm',0,0,(function(){return''+$e.apply(this)+E(this.minutes(),2)})),G('hmmss',0,0,(function(){return''+$e.apply(this)+E(this.minutes(),2)+E(this.seconds(),2)})),G('Hmm',0,0,(function(){return''+this.hours()+E(this.minutes(),2)})),G('Hmmss',0,0,(function(){return''+this.hours()+E(this.minutes(),2)+E(this.seconds(),2)})),Je('a',!0),Je('A',!1),C('hour','h'),U('hour',13),ue('a',Be),ue('A',Be),ue('H',Q),ue('h',Q),ue('k',Q),ue('HH',Q,q),ue('hh',Q,q),ue('kk',Q,q),ue('hmm',X),ue('hmmss',K),ue('Hmm',X),ue('Hmmss',K),fe(['H','HH'],3),fe(['k','kk'],(function(e,t,n){var r=D(e);t[3]=24===r?0:r})),fe(['a','A'],(function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e})),fe(['h','hh'],(function(e,t,n){t[3]=D(e),f(n).bigHour=!0})),fe('hmm',(function(e,t,n){var r=e.length-2;t[3]=D(e.substr(0,r)),t[4]=D(e.substr(r)),f(n).bigHour=!0})),fe('hmmss',(function(e,t,n){var r=e.length-4,i=e.length-2;t[3]=D(e.substr(0,r)),t[4]=D(e.substr(r,2)),t[5]=D(e.substr(i)),f(n).bigHour=!0})),fe('Hmm',(function(e,t,n){var r=e.length-2;t[3]=D(e.substr(0,r)),t[4]=D(e.substr(r))})),fe('Hmmss',(function(e,t,n){var r=e.length-4,i=e.length-2;t[3]=D(e.substr(0,r)),t[4]=D(e.substr(r,2)),t[5]=D(e.substr(i))}));var Qe,Xe=we('Hours',!0),Ke={calendar:{sameDay:'[Today at] LT',nextDay:'[Tomorrow at] LT',nextWeek:'dddd [at] LT',lastDay:'[Yesterday at] LT',lastWeek:'[Last] dddd [at] LT',sameElse:'L'},longDateFormat:{LTS:'h:mm:ss A',LT:'h:mm A',L:'MM/DD/YYYY',LL:'MMMM D, YYYY',LLL:'MMMM D, YYYY h:mm A',LLLL:'dddd, MMMM D, YYYY h:mm A'},invalidDate:'Invalid date',ordinal:'%d',dayOfMonthOrdinalParse:/\\d{1,2}/,relativeTime:{future:'in %s',past:'%s ago',s:'a few seconds',ss:'%d seconds',m:'a minute',mm:'%d minutes',h:'an hour',hh:'%d hours',d:'a day',dd:'%d days',M:'a month',MM:'%d months',y:'a year',yy:'%d years'},months:be,monthsShort:Ye,week:{dow:0,doy:6},weekdays:Ae,weekdaysMin:Ie,weekdaysShort:Ve,meridiemParse:/[ap]\\.?m?\\.?/i},et={},tt={};function nt(e){return e?e.toLowerCase().replace('_','-'):e}function rt(t){var n=null;if(!et[t]&&void 0!==e&&e&&e.exports)try{n=Qe._abbr,!function(){var e=new Error('Cannot find module \\'undefined\\'');throw e.code='MODULE_NOT_FOUND',e}(),it(n)}catch(e){}return et[t]}function it(e,t){var n;return e&&((n=a(t)?at(e):st(e,t))?Qe=n:'undefined'!=typeof console&&console.warn&&console.warn('Locale '+e+' not found. Did you forget to load it?')),Qe._abbr}function st(e,t){if(null!==t){var n,r=Ke;if(t.abbr=e,null!=et[e])O('defineLocaleOverride','use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'),r=et[e]._config;else if(null!=t.parentLocale)if(null!=et[t.parentLocale])r=et[t.parentLocale]._config;else{if(null==(n=rt(t.parentLocale)))return tt[t.parentLocale]||(tt[t.parentLocale]=[]),tt[t.parentLocale].push({name:e,config:t}),null;r=n._config}return et[e]=new W(P(r,t)),tt[e]&&tt[e].forEach((function(e){st(e.name,e.config)})),it(e),et[e]}return delete et[e],null}function at(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return Qe;if(!i(e)){if(t=rt(e))return t;e=[e]}return function(e){for(var t,n,r,i,s=0;s0;){if(r=rt(i.slice(0,t).join('-')))return r;if(n&&n.length>=t&&M(i,n,!0)>=t-1)break;t--}s++}return Qe}(e)}function ot(e){var t,n=e._a;return n&&-2===f(e).overflow&&(t=n[1]<0||n[1]>11?1:n[2]<1||n[2]>Me(n[0],n[1])?2:n[3]<0||n[3]>24||24===n[3]&&(0!==n[4]||0!==n[5]||0!==n[6])?3:n[4]<0||n[4]>59?4:n[5]<0||n[5]>59?5:n[6]<0||n[6]>999?6:-1,f(e)._overflowDayOfYear&&(t<0||t>2)&&(t=2),f(e)._overflowWeeks&&-1===t&&(t=7),f(e)._overflowWeekday&&-1===t&&(t=8),f(e).overflow=t),e}function lt(e,t,n){return null!=e?e:null!=t?t:n}function ut(e){var t,n,i,s,a,o=[];if(!e._d){for(i=function(e){var t=new Date(r.now());return e._useUTC?[t.getUTCFullYear(),t.getUTCMonth(),t.getUTCDate()]:[t.getFullYear(),t.getMonth(),t.getDate()]}(e),e._w&&null==e._a[2]&&null==e._a[1]&&function(e){var t,n,r,i,s,a,o,l;if(null!=(t=e._w).GG||null!=t.W||null!=t.E)s=1,a=4,n=lt(t.GG,e._a[0],Ue(St(),1,4).year),r=lt(t.W,1),((i=lt(t.E,1))<1||i>7)&&(l=!0);else{s=e._locale._week.dow,a=e._locale._week.doy;var u=Ue(St(),s,a);n=lt(t.gg,e._a[0],u.year),r=lt(t.w,u.week),null!=t.d?((i=t.d)<0||i>6)&&(l=!0):null!=t.e?(i=t.e+s,(t.e<0||t.e>6)&&(l=!0)):i=s}r<1||r>Ee(n,s,a)?f(e)._overflowWeeks=!0:null!=l?f(e)._overflowWeekday=!0:(o=He(n,r,i,s,a),e._a[0]=o.year,e._dayOfYear=o.dayOfYear)}(e),null!=e._dayOfYear&&(a=lt(e._a[0],i[0]),(e._dayOfYear>pe(a)||0===e._dayOfYear)&&(f(e)._overflowDayOfYear=!0),n=Fe(a,0,e._dayOfYear),e._a[1]=n.getUTCMonth(),e._a[2]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=o[t]=i[t];for(;t<7;t++)e._a[t]=o[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[3]&&0===e._a[4]&&0===e._a[5]&&0===e._a[6]&&(e._nextDay=!0,e._a[3]=0),e._d=(e._useUTC?Fe:Ce).apply(null,o),s=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[3]=24),e._w&&void 0!==e._w.d&&e._w.d!==s&&(f(e).weekdayMismatch=!0)}}var dt=/^\\s*((?:[+-]\\d{6}|\\d{4})-(?:\\d\\d-\\d\\d|W\\d\\d-\\d|W\\d\\d|\\d\\d\\d|\\d\\d))(?:(T| )(\\d\\d(?::\\d\\d(?::\\d\\d(?:[.,]\\d+)?)?)?)([\\+\\-]\\d\\d(?::?\\d\\d)?|\\s*Z)?)?$/,ct=/^\\s*((?:[+-]\\d{6}|\\d{4})(?:\\d\\d\\d\\d|W\\d\\d\\d|W\\d\\d|\\d\\d\\d|\\d\\d))(?:(T| )(\\d\\d(?:\\d\\d(?:\\d\\d(?:[.,]\\d+)?)?)?)([\\+\\-]\\d\\d(?::?\\d\\d)?|\\s*Z)?)?$/,ht=/Z|[+-]\\d\\d(?::?\\d\\d)?/,ft=[['YYYYYY-MM-DD',/[+-]\\d{6}-\\d\\d-\\d\\d/],['YYYY-MM-DD',/\\d{4}-\\d\\d-\\d\\d/],['GGGG-[W]WW-E',/\\d{4}-W\\d\\d-\\d/],['GGGG-[W]WW',/\\d{4}-W\\d\\d/,!1],['YYYY-DDD',/\\d{4}-\\d{3}/],['YYYY-MM',/\\d{4}-\\d\\d/,!1],['YYYYYYMMDD',/[+-]\\d{10}/],['YYYYMMDD',/\\d{8}/],['GGGG[W]WWE',/\\d{4}W\\d{3}/],['GGGG[W]WW',/\\d{4}W\\d{2}/,!1],['YYYYDDD',/\\d{7}/]],_t=[['HH:mm:ss.SSSS',/\\d\\d:\\d\\d:\\d\\d\\.\\d+/],['HH:mm:ss,SSSS',/\\d\\d:\\d\\d:\\d\\d,\\d+/],['HH:mm:ss',/\\d\\d:\\d\\d:\\d\\d/],['HH:mm',/\\d\\d:\\d\\d/],['HHmmss.SSSS',/\\d\\d\\d\\d\\d\\d\\.\\d+/],['HHmmss,SSSS',/\\d\\d\\d\\d\\d\\d,\\d+/],['HHmmss',/\\d\\d\\d\\d\\d\\d/],['HHmm',/\\d\\d\\d\\d/],['HH',/\\d\\d/]],mt=/^\\/?Date\\((\\-?\\d+)/i;function pt(e){var t,n,r,i,s,a,o=e._i,l=dt.exec(o)||ct.exec(o);if(l){for(f(e).iso=!0,t=0,n=ft.length;t0&&f(e).unusedInput.push(a),o=o.slice(o.indexOf(n)+n.length),u+=n.length),I[s]?(n?f(e).empty=!1:f(e).unusedTokens.push(s),me(s,n,e)):e._strict&&!n&&f(e).unusedTokens.push(s);f(e).charsLeftOver=l-u,o.length>0&&f(e).unusedInput.push(o),e._a[3]<=12&&!0===f(e).bigHour&&e._a[3]>0&&(f(e).bigHour=void 0),f(e).parsedDateParts=e._a.slice(0),f(e).meridiem=e._meridiem,e._a[3]=function(e,t,n){var r;return null==n?t:null!=e.meridiemHour?e.meridiemHour(t,n):null!=e.isPM?((r=e.isPM(n))&&t<12&&(t+=12),r||12!==t||(t=0),t):t}(e._locale,e._a[3],e._meridiem),ut(e),ot(e)}else wt(e);else pt(e)}function Dt(e){var t=e._i,n=e._f;return e._locale=e._locale||at(e._l),null===t||void 0===n&&''===t?m({nullInput:!0}):('string'==typeof t&&(e._i=t=e._locale.preparse(t)),w(t)?new v(ot(t)):(l(t)?e._d=t:i(n)?function(e){var t,n,r,i,s;if(0===e._f.length)return f(e).invalidFormat=!0,void(e._d=new Date(NaN));for(i=0;ithis?this:e:m()}));function Tt(e,t){var n,r;if(1===t.length&&i(t[0])&&(t=t[0]),!t.length)return St();for(n=t[0],r=1;r=0?new Date(e+400,t,n)-126227808e5:new Date(e,t,n).valueOf()}function Kt(e,t,n){return e<100&&e>=0?Date.UTC(e+400,t,n)-126227808e5:Date.UTC(e,t,n)}function en(e,t){G(0,[e,e.length],0,t)}function tn(e,t,n,r,i){var s;return null==e?Ue(this,r,i).year:(t>(s=Ee(e,r,i))&&(t=s),nn.call(this,e,t,n,r,i))}function nn(e,t,n,r,i){var s=He(e,t,n,r,i),a=Fe(s.year,0,s.dayOfYear);return this.year(a.getUTCFullYear()),this.month(a.getUTCMonth()),this.date(a.getUTCDate()),this}G(0,['gg',2],0,(function(){return this.weekYear()%100})),G(0,['GG',2],0,(function(){return this.isoWeekYear()%100})),en('gggg','weekYear'),en('ggggg','weekYear'),en('GGGG','isoWeekYear'),en('GGGGG','isoWeekYear'),C('weekYear','gg'),C('isoWeekYear','GG'),U('weekYear',1),U('isoWeekYear',1),ue('G',ie),ue('g',ie),ue('GG',Q,q),ue('gg',Q,q),ue('GGGG',te,J),ue('gggg',te,J),ue('GGGGG',ne,B),ue('ggggg',ne,B),_e(['gggg','ggggg','GGGG','GGGGG'],(function(e,t,n,r){t[r.substr(0,2)]=D(e)})),_e(['gg','GG'],(function(e,t,n,i){t[i]=r.parseTwoDigitYear(e)})),G('Q',0,'Qo','quarter'),C('quarter','Q'),U('quarter',7),ue('Q',z),fe('Q',(function(e,t){t[1]=3*(D(e)-1)})),G('D',['DD',2],'Do','date'),C('date','D'),U('date',9),ue('D',Q),ue('DD',Q,q),ue('Do',(function(e,t){return e?t._dayOfMonthOrdinalParse||t._ordinalParse:t._dayOfMonthOrdinalParseLenient})),fe(['D','DD'],2),fe('Do',(function(e,t){t[2]=D(e.match(Q)[0])}));var rn=we('Date',!0);G('DDD',['DDDD',3],'DDDo','dayOfYear'),C('dayOfYear','DDD'),U('dayOfYear',4),ue('DDD',ee),ue('DDDD',$),fe(['DDD','DDDD'],(function(e,t,n){n._dayOfYear=D(e)})),G('m',['mm',2],0,'minute'),C('minute','m'),U('minute',14),ue('m',Q),ue('mm',Q,q),fe(['m','mm'],4);var sn=we('Minutes',!1);G('s',['ss',2],0,'second'),C('second','s'),U('second',15),ue('s',Q),ue('ss',Q,q),fe(['s','ss'],5);var an,on=we('Seconds',!1);for(G('S',0,0,(function(){return~~(this.millisecond()/100)})),G(0,['SS',2],0,(function(){return~~(this.millisecond()/10)})),G(0,['SSS',3],0,'millisecond'),G(0,['SSSS',4],0,(function(){return 10*this.millisecond()})),G(0,['SSSSS',5],0,(function(){return 100*this.millisecond()})),G(0,['SSSSSS',6],0,(function(){return 1e3*this.millisecond()})),G(0,['SSSSSSS',7],0,(function(){return 1e4*this.millisecond()})),G(0,['SSSSSSSS',8],0,(function(){return 1e5*this.millisecond()})),G(0,['SSSSSSSSS',9],0,(function(){return 1e6*this.millisecond()})),C('millisecond','ms'),U('millisecond',16),ue('S',ee,z),ue('SS',ee,q),ue('SSS',ee,$),an='SSSS';an.length<=9;an+='S')ue(an,re);function ln(e,t){t[6]=D(1e3*('0.'+e))}for(an='S';an.length<=9;an+='S')fe(an,ln);var un=we('Milliseconds',!1);G('z',0,0,'zoneAbbr'),G('zz',0,0,'zoneName');var dn=v.prototype;function cn(e){return e}dn.add=Zt,dn.calendar=function(e,t){var n=e||St(),i=Nt(n,this).startOf('day'),s=r.calendarFormat(this,i)||'sameElse',a=t&&(x(t[s])?t[s].call(this,n):t[s]);return this.format(a||this.localeData().calendar(s,this,St(n)))},dn.clone=function(){return new v(this)},dn.diff=function(e,t,n){var r,i,s;if(!this.isValid())return NaN;if(!(r=Nt(e,this)).isValid())return NaN;switch(i=6e4*(r.utcOffset()-this.utcOffset()),t=F(t)){case'year':s=qt(this,r)/12;break;case'month':s=qt(this,r);break;case'quarter':s=qt(this,r)/3;break;case'second':s=(this-r)/1e3;break;case'minute':s=(this-r)/6e4;break;case'hour':s=(this-r)/36e5;break;case'day':s=(this-r-i)/864e5;break;case'week':s=(this-r-i)/6048e5;break;default:s=this-r}return n?s:k(s)},dn.endOf=function(e){var t;if(void 0===(e=F(e))||'millisecond'===e||!this.isValid())return this;var n=this._isUTC?Kt:Xt;switch(e){case'year':t=n(this.year()+1,0,1)-1;break;case'quarter':t=n(this.year(),this.month()-this.month()%3+3,1)-1;break;case'month':t=n(this.year(),this.month()+1,1)-1;break;case'week':t=n(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case'isoWeek':t=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case'day':case'date':t=n(this.year(),this.month(),this.date()+1)-1;break;case'hour':t=this._d.valueOf(),t+=36e5-Qt(t+(this._isUTC?0:6e4*this.utcOffset()),36e5)-1;break;case'minute':t=this._d.valueOf(),t+=6e4-Qt(t,6e4)-1;break;case'second':t=this._d.valueOf(),t+=1e3-Qt(t,1e3)-1}return this._d.setTime(t),r.updateOffset(this,!0),this},dn.format=function(e){e||(e=this.isUtc()?r.defaultFormatUtc:r.defaultFormat);var t=j(this,e);return this.localeData().postformat(t)},dn.from=function(e,t){return this.isValid()&&(w(e)&&e.isValid()||St(e).isValid())?At({to:this,from:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},dn.fromNow=function(e){return this.from(St(),e)},dn.to=function(e,t){return this.isValid()&&(w(e)&&e.isValid()||St(e).isValid())?At({from:this,to:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},dn.toNow=function(e){return this.to(St(),e)},dn.get=function(e){return x(this[e=F(e)])?this[e]():this},dn.invalidAt=function(){return f(this).overflow},dn.isAfter=function(e,t){var n=w(e)?e:St(e);return!(!this.isValid()||!n.isValid())&&('millisecond'===(t=F(t)||'millisecond')?this.valueOf()>n.valueOf():n.valueOf()9999?j(n,t?'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]':'YYYYYY-MM-DD[T]HH:mm:ss.SSSZ'):x(Date.prototype.toISOString)?t?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace('Z',j(n,'Z')):j(n,t?'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]':'YYYY-MM-DD[T]HH:mm:ss.SSSZ')},dn.inspect=function(){if(!this.isValid())return'moment.invalid(/* '+this._i+' */)';var e='moment',t='';this.isLocal()||(e=0===this.utcOffset()?'moment.utc':'moment.parseZone',t='Z');var n='['+e+'(\"]',r=0<=this.year()&&this.year()<=9999?'YYYY':'YYYYYY',i=t+'[\")]';return this.format(n+r+'-MM-DD[T]HH:mm:ss.SSS'+i)},dn.toJSON=function(){return this.isValid()?this.toISOString():null},dn.toString=function(){return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ')},dn.unix=function(){return Math.floor(this.valueOf()/1e3)},dn.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},dn.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},dn.year=ve,dn.isLeapYear=function(){return ye(this.year())},dn.weekYear=function(e){return tn.call(this,e,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)},dn.isoWeekYear=function(e){return tn.call(this,e,this.isoWeek(),this.isoWeekday(),1,4)},dn.quarter=dn.quarters=function(e){return null==e?Math.ceil((this.month()+1)/3):this.month(3*(e-1)+this.month()%3)},dn.month=xe,dn.daysInMonth=function(){return Me(this.year(),this.month())},dn.week=dn.weeks=function(e){var t=this.localeData().week(this);return null==e?t:this.add(7*(e-t),'d')},dn.isoWeek=dn.isoWeeks=function(e){var t=Ue(this,1,4).week;return null==e?t:this.add(7*(e-t),'d')},dn.weeksInYear=function(){var e=this.localeData()._week;return Ee(this.year(),e.dow,e.doy)},dn.isoWeeksInYear=function(){return Ee(this.year(),1,4)},dn.date=rn,dn.day=dn.days=function(e){if(!this.isValid())return null!=e?this:NaN;var t=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=e?(e=function(e,t){return'string'!=typeof e?e:isNaN(e)?'number'==typeof(e=t.weekdaysParse(e))?e:null:parseInt(e,10)}(e,this.localeData()),this.add(e-t,'d')):t},dn.weekday=function(e){if(!this.isValid())return null!=e?this:NaN;var t=(this.day()+7-this.localeData()._week.dow)%7;return null==e?t:this.add(e-t,'d')},dn.isoWeekday=function(e){if(!this.isValid())return null!=e?this:NaN;if(null!=e){var t=function(e,t){return'string'==typeof e?t.weekdaysParse(e)%7||7:isNaN(e)?null:e}(e,this.localeData());return this.day(this.day()%7?t:t-7)}return this.day()||7},dn.dayOfYear=function(e){var t=Math.round((this.clone().startOf('day')-this.clone().startOf('year'))/864e5)+1;return null==e?t:this.add(e-t,'d')},dn.hour=dn.hours=Xe,dn.minute=dn.minutes=sn,dn.second=dn.seconds=on,dn.millisecond=dn.milliseconds=un,dn.utcOffset=function(e,t,n){var i,s=this._offset||0;if(!this.isValid())return null!=e?this:NaN;if(null!=e){if('string'==typeof e){if(null===(e=Ft(ae,e)))return this}else Math.abs(e)<16&&!n&&(e*=60);return!this._isUTC&&t&&(i=Ht(this)),this._offset=e,this._isUTC=!0,null!=i&&this.add(i,'m'),s!==e&&(!t||this._changeInProgress?jt(this,At(e-s,'m'),1,!1):this._changeInProgress||(this._changeInProgress=!0,r.updateOffset(this,!0),this._changeInProgress=null)),this}return this._isUTC?s:Ht(this)},dn.utc=function(e){return this.utcOffset(0,e)},dn.local=function(e){return this._isUTC&&(this.utcOffset(0,e),this._isUTC=!1,e&&this.subtract(Ht(this),'m')),this},dn.parseZone=function(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if('string'==typeof this._i){var e=Ft(se,this._i);null!=e?this.utcOffset(e):this.utcOffset(0,!0)}return this},dn.hasAlignedHourOffset=function(e){return!!this.isValid()&&(e=e?St(e).utcOffset():0,(this.utcOffset()-e)%60==0)},dn.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},dn.isLocal=function(){return!!this.isValid()&&!this._isUTC},dn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},dn.isUtc=Ut,dn.isUTC=Ut,dn.zoneAbbr=function(){return this._isUTC?'UTC':''},dn.zoneName=function(){return this._isUTC?'Coordinated Universal Time':''},dn.dates=b('dates accessor is deprecated. Use date instead.',rn),dn.months=b('months accessor is deprecated. Use month instead',xe),dn.years=b('years accessor is deprecated. Use year instead',ve),dn.zone=b('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/',(function(e,t){return null!=e?('string'!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()})),dn.isDSTShifted=b('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information',(function(){if(!a(this._isDSTShifted))return this._isDSTShifted;var e={};if(y(e,this),(e=Dt(e))._a){var t=e._isUTC?h(e._a):St(e._a);this._isDSTShifted=this.isValid()&&M(e._a,t.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}));var hn=W.prototype;function fn(e,t,n,r){var i=at(),s=h().set(r,t);return i[n](s,e)}function _n(e,t,n){if(o(e)&&(t=e,e=void 0),e=e||'',null!=t)return fn(e,t,n,'month');var r,i=[];for(r=0;r<12;r++)i[r]=fn(e,r,n,'month');return i}function mn(e,t,n,r){'boolean'==typeof e?(o(t)&&(n=t,t=void 0),t=t||''):(n=t=e,e=!1,o(t)&&(n=t,t=void 0),t=t||'');var i,s=at(),a=e?s._week.dow:0;if(null!=n)return fn(t,(n+a)%7,r,'day');var l=[];for(i=0;i<7;i++)l[i]=fn(t,(i+a)%7,r,'day');return l}hn.calendar=function(e,t,n){var r=this._calendar[e]||this._calendar.sameElse;return x(r)?r.call(t,n):r},hn.longDateFormat=function(e){var t=this._longDateFormat[e],n=this._longDateFormat[e.toUpperCase()];return t||!n?t:(this._longDateFormat[e]=n.replace(/MMMM|MM|DD|dddd/g,(function(e){return e.slice(1)})),this._longDateFormat[e])},hn.invalidDate=function(){return this._invalidDate},hn.ordinal=function(e){return this._ordinal.replace('%d',e)},hn.preparse=cn,hn.postformat=cn,hn.relativeTime=function(e,t,n,r){var i=this._relativeTime[n];return x(i)?i(e,t,n,r):i.replace(/%d/i,e)},hn.pastFuture=function(e,t){var n=this._relativeTime[e>0?'future':'past'];return x(n)?n(t):n.replace(/%s/i,t)},hn.set=function(e){var t,n;for(n in e)x(t=e[n])?this[n]=t:this['_'+n]=t;this._config=e,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+'|'+/\\d{1,2}/.source)},hn.months=function(e,t){return e?i(this._months)?this._months[e.month()]:this._months[(this._months.isFormat||Se).test(t)?'format':'standalone'][e.month()]:i(this._months)?this._months:this._months.standalone},hn.monthsShort=function(e,t){return e?i(this._monthsShort)?this._monthsShort[e.month()]:this._monthsShort[Se.test(t)?'format':'standalone'][e.month()]:i(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},hn.monthsParse=function(e,t,n){var r,i,s;if(this._monthsParseExact)return Te.call(this,e,t,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),r=0;r<12;r++){if(i=h([2e3,r]),n&&!this._longMonthsParse[r]&&(this._longMonthsParse[r]=new RegExp('^'+this.months(i,'').replace('.','')+'$','i'),this._shortMonthsParse[r]=new RegExp('^'+this.monthsShort(i,'').replace('.','')+'$','i')),n||this._monthsParse[r]||(s='^'+this.months(i,'')+'|^'+this.monthsShort(i,''),this._monthsParse[r]=new RegExp(s.replace('.',''),'i')),n&&'MMMM'===t&&this._longMonthsParse[r].test(e))return r;if(n&&'MMM'===t&&this._shortMonthsParse[r].test(e))return r;if(!n&&this._monthsParse[r].test(e))return r}},hn.monthsRegex=function(e){return this._monthsParseExact?(d(this,'_monthsRegex')||Re.call(this),e?this._monthsStrictRegex:this._monthsRegex):(d(this,'_monthsRegex')||(this._monthsRegex=We),this._monthsStrictRegex&&e?this._monthsStrictRegex:this._monthsRegex)},hn.monthsShortRegex=function(e){return this._monthsParseExact?(d(this,'_monthsRegex')||Re.call(this),e?this._monthsShortStrictRegex:this._monthsShortRegex):(d(this,'_monthsShortRegex')||(this._monthsShortRegex=Pe),this._monthsShortStrictRegex&&e?this._monthsShortStrictRegex:this._monthsShortRegex)},hn.week=function(e){return Ue(e,this._week.dow,this._week.doy).week},hn.firstDayOfYear=function(){return this._week.doy},hn.firstDayOfWeek=function(){return this._week.dow},hn.weekdays=function(e,t){var n=i(this._weekdays)?this._weekdays:this._weekdays[e&&!0!==e&&this._weekdays.isFormat.test(t)?'format':'standalone'];return!0===e?Le(n,this._week.dow):e?n[e.day()]:n},hn.weekdaysMin=function(e){return!0===e?Le(this._weekdaysMin,this._week.dow):e?this._weekdaysMin[e.day()]:this._weekdaysMin},hn.weekdaysShort=function(e){return!0===e?Le(this._weekdaysShort,this._week.dow):e?this._weekdaysShort[e.day()]:this._weekdaysShort},hn.weekdaysParse=function(e,t,n){var r,i,s;if(this._weekdaysParseExact)return Ge.call(this,e,t,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),r=0;r<7;r++){if(i=h([2e3,1]).day(r),n&&!this._fullWeekdaysParse[r]&&(this._fullWeekdaysParse[r]=new RegExp('^'+this.weekdays(i,'').replace('.','\\\\.?')+'$','i'),this._shortWeekdaysParse[r]=new RegExp('^'+this.weekdaysShort(i,'').replace('.','\\\\.?')+'$','i'),this._minWeekdaysParse[r]=new RegExp('^'+this.weekdaysMin(i,'').replace('.','\\\\.?')+'$','i')),this._weekdaysParse[r]||(s='^'+this.weekdays(i,'')+'|^'+this.weekdaysShort(i,'')+'|^'+this.weekdaysMin(i,''),this._weekdaysParse[r]=new RegExp(s.replace('.',''),'i')),n&&'dddd'===t&&this._fullWeekdaysParse[r].test(e))return r;if(n&&'ddd'===t&&this._shortWeekdaysParse[r].test(e))return r;if(n&&'dd'===t&&this._minWeekdaysParse[r].test(e))return r;if(!n&&this._weekdaysParse[r].test(e))return r}},hn.weekdaysRegex=function(e){return this._weekdaysParseExact?(d(this,'_weekdaysRegex')||qe.call(this),e?this._weekdaysStrictRegex:this._weekdaysRegex):(d(this,'_weekdaysRegex')||(this._weekdaysRegex=je),this._weekdaysStrictRegex&&e?this._weekdaysStrictRegex:this._weekdaysRegex)},hn.weekdaysShortRegex=function(e){return this._weekdaysParseExact?(d(this,'_weekdaysRegex')||qe.call(this),e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(d(this,'_weekdaysShortRegex')||(this._weekdaysShortRegex=Ze),this._weekdaysShortStrictRegex&&e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},hn.weekdaysMinRegex=function(e){return this._weekdaysParseExact?(d(this,'_weekdaysRegex')||qe.call(this),e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(d(this,'_weekdaysMinRegex')||(this._weekdaysMinRegex=ze),this._weekdaysMinStrictRegex&&e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},hn.isPM=function(e){return'p'===(e+'').toLowerCase().charAt(0)},hn.meridiem=function(e,t,n){return e>11?n?'pm':'PM':n?'am':'AM'},it('en',{dayOfMonthOrdinalParse:/\\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10;return e+(1===D(e%100/10)?'th':1===t?'st':2===t?'nd':3===t?'rd':'th')}}),r.lang=b('moment.lang is deprecated. Use moment.locale instead.',it),r.langData=b('moment.langData is deprecated. Use moment.localeData instead.',at);var pn=Math.abs;function yn(e,t,n,r){var i=At(t,n);return e._milliseconds+=r*i._milliseconds,e._days+=r*i._days,e._months+=r*i._months,e._bubble()}function gn(e){return e<0?Math.floor(e):Math.ceil(e)}function vn(e){return 4800*e/146097}function wn(e){return 146097*e/4800}function kn(e){return function(){return this.as(e)}}var Dn=kn('ms'),Mn=kn('s'),Sn=kn('m'),bn=kn('h'),Yn=kn('d'),Tn=kn('w'),On=kn('M'),xn=kn('Q'),Pn=kn('y');function Wn(e){return function(){return this.isValid()?this._data[e]:NaN}}var Rn=Wn('milliseconds'),Cn=Wn('seconds'),Fn=Wn('minutes'),Nn=Wn('hours'),Hn=Wn('days'),Un=Wn('months'),En=Wn('years'),Ln=Math.round,An={ss:44,s:45,m:45,h:22,d:26,M:11};function Vn(e,t,n,r,i){return i.relativeTime(t||1,!!n,e,r)}var In=Math.abs;function Gn(e){return(e>0)-(e<0)||+e}function jn(){if(!this.isValid())return this.localeData().invalidDate();var e,t,n=In(this._milliseconds)/1e3,r=In(this._days),i=In(this._months);e=k(n/60),t=k(e/60),n%=60,e%=60;var s=k(i/12),a=i%=12,o=r,l=t,u=e,d=n?n.toFixed(3).replace(/\\.?0+$/,''):'',c=this.asSeconds();if(!c)return'P0D';var h=c<0?'-':'',f=Gn(this._months)!==Gn(c)?'-':'',_=Gn(this._days)!==Gn(c)?'-':'',m=Gn(this._milliseconds)!==Gn(c)?'-':'';return h+'P'+(s?f+s+'Y':'')+(a?f+a+'M':'')+(o?_+o+'D':'')+(l||u||d?'T':'')+(l?m+l+'H':'')+(u?m+u+'M':'')+(d?m+d+'S':'')}var Zn=xt.prototype;return Zn.isValid=function(){return this._isValid},Zn.abs=function(){var e=this._data;return this._milliseconds=pn(this._milliseconds),this._days=pn(this._days),this._months=pn(this._months),e.milliseconds=pn(e.milliseconds),e.seconds=pn(e.seconds),e.minutes=pn(e.minutes),e.hours=pn(e.hours),e.months=pn(e.months),e.years=pn(e.years),this},Zn.add=function(e,t){return yn(this,e,t,1)},Zn.subtract=function(e,t){return yn(this,e,t,-1)},Zn.as=function(e){if(!this.isValid())return NaN;var t,n,r=this._milliseconds;if('month'===(e=F(e))||'quarter'===e||'year'===e)switch(t=this._days+r/864e5,n=this._months+vn(t),e){case'month':return n;case'quarter':return n/3;case'year':return n/12}else switch(t=this._days+Math.round(wn(this._months)),e){case'week':return t/7+r/6048e5;case'day':return t+r/864e5;case'hour':return 24*t+r/36e5;case'minute':return 1440*t+r/6e4;case'second':return 86400*t+r/1e3;case'millisecond':return Math.floor(864e5*t)+r;default:throw new Error('Unknown unit '+e)}},Zn.asMilliseconds=Dn,Zn.asSeconds=Mn,Zn.asMinutes=Sn,Zn.asHours=bn,Zn.asDays=Yn,Zn.asWeeks=Tn,Zn.asMonths=On,Zn.asQuarters=xn,Zn.asYears=Pn,Zn.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*D(this._months/12):NaN},Zn._bubble=function(){var e,t,n,r,i,s=this._milliseconds,a=this._days,o=this._months,l=this._data;return s>=0&&a>=0&&o>=0||s<=0&&a<=0&&o<=0||(s+=864e5*gn(wn(o)+a),a=0,o=0),l.milliseconds=s%1e3,e=k(s/1e3),l.seconds=e%60,t=k(e/60),l.minutes=t%60,n=k(t/60),l.hours=n%24,a+=k(n/24),i=k(vn(a)),o+=i,a-=gn(wn(i)),r=k(o/12),o%=12,l.days=a,l.months=o,l.years=r,this},Zn.clone=function(){return At(this)},Zn.get=function(e){return e=F(e),this.isValid()?this[e+'s']():NaN},Zn.milliseconds=Rn,Zn.seconds=Cn,Zn.minutes=Fn,Zn.hours=Nn,Zn.days=Hn,Zn.weeks=function(){return k(this.days()/7)},Zn.months=Un,Zn.years=En,Zn.humanize=function(e){if(!this.isValid())return this.localeData().invalidDate();var t=this.localeData(),n=function(e,t,n){var r=At(e).abs(),i=Ln(r.as('s')),s=Ln(r.as('m')),a=Ln(r.as('h')),o=Ln(r.as('d')),l=Ln(r.as('M')),u=Ln(r.as('y')),d=i<=An.ss&&['s',i]||i0,d[4]=n,Vn.apply(null,d)}(this,!e,t);return e&&(n=t.pastFuture(+this,n)),t.postformat(n)},Zn.toISOString=jn,Zn.toString=jn,Zn.toJSON=jn,Zn.locale=$t,Zn.localeData=Bt,Zn.toIsoString=b('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)',jn),Zn.lang=Jt,G('X',0,0,'unix'),G('x',0,0,'valueOf'),ue('x',ie),ue('X',/[+-]?\\d+(\\.\\d{1,3})?/),fe('X',(function(e,t,n){n._d=new Date(1e3*parseFloat(e,10))})),fe('x',(function(e,t,n){n._d=new Date(D(e))})),r.version='2.24.0',t=St,r.fn=dn,r.min=function(){var e=[].slice.call(arguments,0);return Tt('isBefore',e)},r.max=function(){var e=[].slice.call(arguments,0);return Tt('isAfter',e)},r.now=function(){return Date.now?Date.now():+new Date},r.utc=h,r.unix=function(e){return St(1e3*e)},r.months=function(e,t){return _n(e,t,'months')},r.isDate=l,r.locale=it,r.invalid=m,r.duration=At,r.isMoment=w,r.weekdays=function(e,t,n){return mn(e,t,n,'weekdays')},r.parseZone=function(){return St.apply(null,arguments).parseZone()},r.localeData=at,r.isDuration=Pt,r.monthsShort=function(e,t){return _n(e,t,'monthsShort')},r.weekdaysMin=function(e,t,n){return mn(e,t,n,'weekdaysMin')},r.defineLocale=st,r.updateLocale=function(e,t){if(null!=t){var n,r,i=Ke;null!=(r=rt(e))&&(i=r._config),t=P(i,t),(n=new W(t)).parentLocale=et[e],et[e]=n,it(e)}else null!=et[e]&&(null!=et[e].parentLocale?et[e]=et[e].parentLocale:null!=et[e]&&delete et[e]);return et[e]},r.locales=function(){return Y(et)},r.weekdaysShort=function(e,t,n){return mn(e,t,n,'weekdaysShort')},r.normalizeUnits=F,r.relativeTimeRounding=function(e){return void 0===e?Ln:'function'==typeof e&&(Ln=e,!0)},r.relativeTimeThreshold=function(e,t){return void 0!==An[e]&&(void 0===t?An[e]:(An[e]=t,'s'===e&&(An.ss=t-1),!0))},r.calendarFormat=function(e,t){var n=e.diff(t,'days',!0);return n<-6?'sameElse':n<-1?'lastWeek':n<0?'lastDay':n<1?'sameDay':n<2?'nextDay':n<7?'nextWeek':'sameElse'},r.prototype=dn,r.HTML5_FMT={DATETIME_LOCAL:'YYYY-MM-DDTHH:mm',DATETIME_LOCAL_SECONDS:'YYYY-MM-DDTHH:mm:ss',DATETIME_LOCAL_MS:'YYYY-MM-DDTHH:mm:ss.SSS',DATE:'YYYY-MM-DD',TIME:'HH:mm',TIME_SECONDS:'HH:mm:ss',TIME_MS:'HH:mm:ss.SSS',WEEK:'GGGG-[W]WW',MONTH:'YYYY-MM'},r}()}).call(this,n(3)(e))},function(e,t,n){var r=n(2),i=n(5);e.exports=i(r,contact,reports)},function(e,t,n){const r=n(0),i=n(4),{today:s,MAX_DAYS_IN_PREGNANCY:a,isHighRiskPregnancy:o,getNewestReport:l,getSubsequentPregnancyFollowUps:u,getSubsequentDeliveries:d,isAlive:c,isReadyForNewPregnancy:h,isReadyForDelivery:f,isActivePregnancy:_,countANCFacilityVisits:m,getAllRiskFactors:p,getLatestDangerSignsForPregnancy:y,getNextANCVisitDate:g,getMostRecentLMPDateForPregnancy:v,getMostRecentEDDForPregnancy:w,getDeliveryDate:k,getFormArraySubmittedInWindow:D,getRecentANCVisitWithEvent:M,getAllRiskFactorExtra:S,getField:b}=i,Y=contact,T=lineage,O=reports,x={alive:c(Y),muted:!1,show_pregnancy_form:h(Y,O),show_delivery_form:f(Y,O)},P=[{appliesToType:'person',label:'patient_id',value:Y.patient_id,width:4},{appliesToType:'person',label:'contact.age',value:Y.date_of_birth,width:4,filter:'age'},{appliesToType:'person',label:'contact.sex',value:'contact.sex.'+Y.sex,translate:!0,width:4},{appliesToType:'person',label:'person.field.phone',value:Y.phone,width:4},{appliesToType:'person',label:'person.field.alternate_phone',value:Y.phone_alternate,width:4},{appliesToType:'person',label:'External ID',value:Y.external_id,width:4},{appliesToType:'person',label:'contact.parent',value:T,filter:'lineage'},{appliesToType:'!person',label:'contact',value:Y.contact&&Y.contact.name,width:4},{appliesToType:'!person',label:'contact.phone',value:Y.contact&&Y.contact.phone,width:4},{appliesToType:'!person',label:'External ID',value:Y.external_id,width:4},{appliesToType:'!person',appliesIf:function(){return Y.parent&&T[0]},label:'contact.parent',value:T,filter:'lineage'},{appliesToType:'person',label:'contact.notes',value:Y.notes,width:12},{appliesToType:'!person',label:'contact.notes',value:Y.notes,width:12}];Y.short_name&&P.unshift({appliesToType:'person',label:'contact.short_name',value:Y.short_name,width:4});const W=[{label:'contact.profile.pregnancy.active',appliesToType:'report',appliesIf:function(e){return _(Y,O,e)},fields:function(e){const t=[],n=p(O,e),i=S(O,e),a=y(O,e),d=o(O,e),c=l(O,['pregnancy','pregnancy_home_visit']),h=r(c.reported_date),f=v(O,e),_=w(O,e),k=g(O,e),D=f?s.diff(f,'weeks'):null;let Y=b(e,'lmp_approx'),T=e.reported_date;u(O,e).forEach((function(e){e.reported_date>T&&'yes'===b(e,'lmp_updated')&&(T=e.reported_date,b(e,'lmp_method_approx')&&(Y=b(e,'lmp_method_approx')))}));const x=M(O,e,'migrated'),P=M(O,e,'refused'),W=x||P;if(W){const e='clear_all'===b(W,'pregnancy_ended.clear_option');t.push({label:'contact.profile.change_care',value:x?'Migrated out of area':'Refusing care',width:6},{label:'contact.profile.tasks_on_off',value:e?'Off':'On',width:6})}if(t.push({label:'Weeks Pregnant',value:D||0===D?{number:D,approximate:'yes'===Y}:'contact.profile.value.unknown',translate:!D&&0!==D,filter:D||0===D?'weeksPregnant':'',width:6},{label:'contact.profile.edd',value:_?_.valueOf():'contact.profile.value.unknown',translate:!_,filter:_?'simpleDate':'',width:6}),d){let e='';e=!n&&i?i.join(', '):n.length>1||n&&i?'contact.profile.risk.multiple':'contact.profile.danger_sign.'+n[0],t.push({label:'contact.profile.risk.high',value:e,translate:!0,icon:'icon-risk',width:6})}return a.length>0&&t.push({label:'contact.profile.danger_signs.current',value:a.length>1?'contact.profile.danger_sign.multiple':'contact.profile.danger_sign.'+a[0],translate:!0,width:6}),t.push({label:'contact.profile.visit',value:'contact.profile.visits.of',context:{count:m(O,e),total:8},translate:!0,width:6},{label:'contact.profile.last_visited',value:h.valueOf(),filter:'relativeDay',width:6}),k&&k.isSameOrAfter(s)&&t.push({label:'contact.profile.anc.next',value:k.valueOf(),filter:'simpleDate',width:6}),t},modifyContext:function(e,t){let n=b(t,'lmp_date_8601'),r=b(t,'lmp_method_approx'),i=b(t,'hiv_status_known'),s=b(t,'deworming_med_received'),a=b(t,'tt_received');const o=p(O,t),l=S(O,t);let d=b(t,'t_pregnancy_follow_up_date');u(O,t).forEach((function(e){'yes'===b(e,'lmp_updated')&&(n=b(e,'lmp_date_8601'),r=b(e,'lmp_method_approx')),i=b(e,'hiv_status_known'),s=b(e,'deworming_med_received'),a=b(e,'tt_received'),'yes'===b(e,'t_pregnancy_follow_up')&&(d=b(e,'t_pregnancy_follow_up_date'))})),e.lmp_date_8601=n,e.lmp_method_approx=r,e.is_active_pregnancy=!0,e.deworming_med_received=s,e.hiv_tested_past=i,e.tt_received_past=a,e.risk_factor_codes=o.join(' '),e.risk_factor_extra=l.join('; '),e.pregnancy_follow_up_date_recent=d,e.pregnancy_uuid=t._id}},{label:'contact.profile.death.title',appliesToType:'person',appliesIf:function(){return!c(Y)},fields:function(){const e=[];let t,n;const r=l(O,['death_report']);if(r){const e=b(r,'death_details');e&&(t=e.date_of_death,n=e.place_of_death)}else Y.date_of_death&&(t=Y.date_of_death);return e.push({label:'contact.profile.death.date',value:t||'contact.profile.value.unknown',filter:t?'simpleDate':'',translate:!t,width:6},{label:'contact.profile.death.place',value:n||'contact.profile.value.unknown',translate:!0,width:6}),e}},{label:'contact.profile.pregnancy.past',appliesToType:'report',appliesIf:function(e){if('person'!==Y.type)return!1;if('delivery'===e.form)return!0;if('pregnancy'===e.form){if(M(O,e,'abortion')||M(O,e,'miscarriage'))return!0;const t=v(O,e);return t&&s.isSameOrAfter(t.clone().add(42,'weeks'))&&0===d(O,e,a).length}return!1},fields:function(e){const t=[];let n,i,l='',u=0,c=0,h=0;if('delivery'===e.form){const s=r(e.reported_date);n=D(O,['pregnancy'],s.clone().subtract(a,'days').toDate(),s.toDate())[0],b(e,'delivery_outcome')&&(i=k(e),l=b(e,'delivery_outcome.delivery_place'),u=b(e,'delivery_outcome.babies_delivered_num'),c=b(e,'delivery_outcome.babies_deceased_num'),t.push({label:'contact.profile.delivery_date',value:i?i.valueOf():'',filter:'simpleDate',width:6},{label:'contact.profile.delivery_place',value:l,translate:!0,width:6},{label:'contact.profile.delivered_babies',value:u,width:6}))}else if('pregnancy'===e.form){n=e;const o=v(O,n),l=M(O,n,'abortion'),u=M(O,n,'miscarriage');if(l||u){let e='',n=r(0),i=0;l?(e='abortion',n=r(b(l,'pregnancy_ended.abortion_date'))):(e='miscarriage',n=r(b(u,'pregnancy_ended.miscarriage_date'))),i=n.diff(o,'weeks'),t.push({label:'contact.profile.pregnancy.end_early',value:e,translate:!0,width:6},{label:'contact.profile.pregnancy.end_date',value:n.valueOf(),filter:'simpleDate',width:6},{label:'contact.profile.pregnancy.end_weeks',value:i>0?i:'contact.profile.value.unknown',translate:i<=0,width:6})}else o&&s.isSameOrAfter(o.clone().add(42,'weeks'))&&0===d(O,e,a).length&&(i=w(O,e),t.push({label:'contact.profile.delivery_date',value:i?i.valueOf():'contact.profile.value.unknown',filter:'simpleDate',translate:!i,width:6}))}if(c>0&&b(e,'baby_death')){t.push({label:'contact.profile.deceased_babies',value:c,width:6});let n=b(e,'baby_death.baby_death_repeat');n||(n=[]);let r=0;n.forEach((function(e){r>0&&t.push({label:'',value:'',width:6}),t.push({label:'contact.profile.newborn.death_date',value:e.baby_death_date,filter:'simpleDate',width:6},{label:'contact.profile.newborn.death_place',value:e.baby_death_place,translate:!0,width:6},{label:'contact.profile.delivery.stillbirthQ',value:e.stillbirth,translate:!0,width:6}),r++,r===n.length&&t.push({label:'',value:'',width:6})}))}if(n){h=m(O,n),t.push({label:'contact.profile.anc_visit',value:h,width:3});if(o(O,n)){let e='';const r=p(O,n),i=S(O,n);e=!r&&i?i.join(', '):r.length>1||r&&i?'contact.profile.risk.multiple':'contact.profile.danger_sign.'+r[0],t.push({label:'contact.profile.risk.high',value:e,translate:!0,icon:'icon-risk',width:6})}}return t}}];e.exports={context:x,cards:W,fields:P}},function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,'loaded',{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,'id',{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},function(e,t,n){const r=n(0),i=r().startOf('day'),s=['pregnancy'],a=['pregnancy_home_visit'],o=['delivery'],l=['pregnancy','pregnancy_home_visit','pregnancy_danger_sign','pregannacy_danger_sign_follow_up'],u=(e,t)=>['fields',...(t||'').split('.')].reduce((e,t)=>{if(void 0!==e)return e[t]},e);function d(e,t,n,r){return e.filter((function(e){return t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r}))}function c(e,t){let n;return e.forEach((function(e){(function(e){return!!(e.form&&e.fields&&e.reported_date)})(e)&&t.includes(e.form)&&(!n||e.reported_date>n.reported_date)&&(n=e)})),n}function h(e){return k(e)&&u(e,'lmp_date_8601')&&r(u(e,'lmp_date_8601'))}function f(e,t){let n=h(t),i=t.reported_date;return T(e,t).forEach((function(e){const t=D(s=e)&&u(s,'lmp_date_8601')&&r(u(s,'lmp_date_8601'));var s;e.reported_date>i&&'yes'===u(e,'lmp_updated')&&(i=e.reported_date,n=t)})),n}function _(e,t){const n=f(e,t);if(n)return n.clone().add(280,'days')}function m(e){return M(e)&&u(e,'delivery_outcome.delivery_date')&&r(u(e,'delivery_outcome.delivery_date'))}function p(e){const t=[];if('yes'===u(e,'t_danger_signs_referral_follow_up')){const n=u(e,'danger_signs');if(n)for(const e in n)'yes'===n[e]&&'r_danger_sign_present'!==e&&t.push(e)}return t}function y(e,t){const n=function(e){const t=[];if(!k(e))return[];if('yes'===u(e,'risk_factors.r_risk_factor_present')){'yes'===u(e,'risk_factors.risk_factors_history.first_pregnancy')&&t.push('first_pregnancy'),'yes'===u(e,'risk_factors.risk_factors_history.previous_miscarriage')&&t.push('previous_miscarriage');const n=u(e,'risk_factors.risk_factors_present.primary_condition'),r=u(e,'risk_factors.risk_factors_present.secondary_condition');n?t.push(...n.split(' ')):r&&t.push(...r.split(' '))}return t}(t);return T(e,t).forEach((function(e){n.push(...function(e){const t=[];if(!D(e))return[];if('yes'===u(e,'anc_visits_hf.risk_factors.r_risk_factor_present')){const n=u(e,'anc_visits_hf.risk_factors.new_risks');n&&t.push(...n.split(' '))}return t}(e))})),n}function g(e){let t;return e&&k(e)?t=u(e,'risk_factors.risk_factors_present.additional_risk'):e&&D(e)&&(t=u(e,'anc_visits_hf.risk_factors.additional_risk')),t}function v(e,t){const n=[],r=g(t);r&&n.push(r);return T(e,t).forEach((function(e){const t=g(e);t&&n.push(t)})),n}function w(e){return e&&!e.date_of_death}function k(e){return e&&s.includes(e.form)}function D(e){return e&&a.includes(e.form)}function M(e){return e&&o.includes(e.form)}function S(e,t,n){if('person'!==e.type||!w(e)||!k(n))return!1;const r=(f(t,n)||n.reported_date)>i.clone().subtract(294,'day'),s=Y(t,n,42).length>0,a=function(e,t){return e.filter((function(e){return k(e)&&e.reported_date>t.reported_date}))}(t,n).length>0;return r&&!s&&!a&&!b(t,n,'abortion')&&!b(t,n,'miscarriage')}function b(e,t,n){const r=c(T(e,t),a);if(r&&u(r,'pregnancy_summary.visit_option')===n)return r}function Y(e,t,n){return e.filter((function(e){return M(e)&&e.reported_date>t.reported_date&&(!n||e.reported_date>=i.clone().subtract(n,'days'))}))}function T(e,t){let n=h(t);n||(n=r(t.reported_date));return e.filter((function(e){return D(e)&&e.reported_date>t.reported_date&&r(e.reported_date)S(e))},isActivePregnancy:S,countANCFacilityVisits:function(e,t){let n=0;const r=T(e,t);return u(t,'anc_visits_hf.anc_visits_hf_past')&&!isNaN(u(t,'anc_visits_hf.anc_visits_hf_past.visited_hf_count'))&&(n+=parseInt(u(t,'anc_visits_hf.anc_visits_hf_past.visited_hf_count'))),n+=r.reduce((function(e,t){const n=u(t,'anc_visits_hf.anc_visits_hf_past');return n?(e+='yes'===n.last_visit_attended&&1,isNaN(n.visited_hf_count)?e:e+('yes'===n.report_other_visits&&parseInt(n.visited_hf_count))):0}),0),n},knowsHIVStatusInPast3Months:function(e){let t=!1;return d(e,s,i.clone().subtract(3,'months'),i).forEach((function(e){'yes'===u(e,'pregnancy_new_or_current.hiv_status.hiv_status_know')&&(t=!0)})),t},getAllRiskFactors:y,getAllRiskFactorExtra:v,getDangerSignCodes:p,getLatestDangerSignsForPregnancy:function(e,t){if(!t)return[];let n=f(e,t);n||(n=r(t.reported_date));const i=d(e,l,n.toDate(),n.clone().add(294,'days').toDate()),s=[];i.forEach(e=>{D(e)?'yes'===u(e,'pregnancy_summary.visit_option')&&s.push(e):s.push(e)});const a=c(s,l);return a?p(a):[]},getNextANCVisitDate:function(e,t){let n=u(t,'t_pregnancy_follow_up_date'),i=t.reported_date;return T(e,t).forEach((function(e){e.reported_date>i&&u(e,'t_pregnancy_follow_up_date')&&(i=e.reported_date,n=u(e,'t_pregnancy_follow_up_date'))})),r(n)},isReadyForNewPregnancy:function(e,t){if('person'!==e.type)return!1;const n=c(t,s),a=c(t,o);if(!n&&!a)return!0;if(n){if(!a||a.reported_daten.reported_date))return m(a)0&&!r.includes('!'+l))&&(!e.appliesIf||e.appliesIf()))return delete e.appliesToType,delete e.appliesIf,!0}))};return o.forEach((function(e){var t,s,o,d,c=n(e.appliesToType);if(c.includes('report')&&c.length>1)throw new Error('You cannot set appliesToType to an array which includes the type \\'report\\' and another type.');if(c.includes('report'))for(t=0;t0)return;(o=i(e,a))&&u.cards.push(o)}})),u.context=a,u}}])})); return ContactSummary;", - "tasks": { - "rules": "define Target { _id: null, contact: null, deleted: null, type: null, pass: null, date: null, groupBy: null }\ndefine Contact { contact: null, reports: null, tasks: null }\ndefine Task { _id: null, deleted: null, doc: null, contact: null, icon: null, date: null, readyStart: null, readyEnd: null, title: null, fields: null, resolved: null, priority: null, priorityLabel: null, reports: null, actions: null }\nrule GenerateEvents {\n when { c: Contact } then { !function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){'undefined'!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:'Module'}),Object.defineProperty(e,'__esModule',{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&'object'==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,'default',{enumerable:!0,value:e}),2&t&&'string'!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,'a',t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p='',n(n.s=2)}([function(e,t){const n=d(Date.now()),r=['pregnancy'],i=['delivery'],o=['pregnancy_home_visit'],a=['pregnancy','pregnancy_home_visit','pregnancy_facility_visit_reminder','pregnancy_danger_sign','pregnancy_danger_sign_follow_up','delivery'];const c=(e,t)=>['fields',...(t||'').split('.')].reduce((e,t)=>{if(void 0!==e)return e[t]},e);function s(e,t){let n;return e.forEach((function(e){t.includes(e.form)&&!e.deleted&&(!n||e.reported_date>n.reported_date)&&(n=e)})),n}function p(e){if(!e)return new Date;const t=e.split(/\\D/),n=new Date(t[0],t[1]-1,t[2]);return function(e){return e instanceof Date&&!isNaN(e)}(n)?n:new Date}function l(e){const t=new Date(e);return t.setHours(0),t.setMinutes(0),t.setSeconds(0),t.setMilliseconds(0),t}function d(e){if('string'==typeof e){if(''===e)return null;e=p(e)}return l(e).getTime()}function u(e,t){const n=l(new Date(e));return n.setDate(n.getDate()+t),n}function _(e){return r.includes(e.form)}function f(e){return o.includes(e.form)}const y=function(e,t){let n;return e.forEach((function(e){t.includes(e.form)&&(!n||e.reported_date>n.reported_date)&&(n=e)})),n},g=function(e){return _(e)&&d(c(e,'lmp_date_8601'))};function m(e,t){return e.reports.filter((function(e){let n=g(t);return n||(n=t.reported_date),f(e)&&e.reported_date>t.reported_date&&e.reported_dater&&''!==t&&t!==n&&(r=e.reported_date,n=t)})),n}e.exports={today:n,MS_IN_DAY:864e5,MAX_DAYS_IN_PREGNANCY:294,addDays:u,isAlive:function(e){return e&&e.contact&&!e.contact.date_of_death},getTimeForMidnight:l,isFormArraySubmittedInWindow:function(e,t,n,r,i){let o=!1,a=0;return e.forEach((function(e){t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r&&(o=!0,i&&a++)})),i?a>=i:o},isFormArraySubmittedInWindowExcludingThisReport:function(e,t,n,r,i,o){let a=!1,c=0;return e.forEach((function(e){t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r&&e._id!==i._id&&(a=!0,o&&c++)})),o?c>=o:a},getDateMS:d,getDateISOLocal:p,isDeliveryForm:function(e){return i.includes(e.form)},getMostRecentReport:s,getNewestPregnancyTimestamp:function(e){if(!e.contact)return;const t=s(e.reports,'pregnancy');return t?t.reported_date:0},getNewestDeliveryTimestamp:function(e){if(!e.contact)return;const t=s(e.reports,'delivery');return t?t.reported_date:0},getReportsSubmittedInWindow:function(e,t,n,r,i){const o=[];return e.forEach((function(e){t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r&&(i&&!i(e)||o.push(e))})),o},countReportsSubmittedInWindow:function(e,t,n,r,i){let o=0;return e.forEach((function(e){t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r&&(i&&!i(e)||o++)})),o},countANCFacilityVisits:function(e,t){let n=0;const r=m(e,t);return c(t,'anc_visits_hf.anc_visits_hf_past')&&!isNaN(c(t,'anc_visits_hf.anc_visits_hf_past.visited_hf_count'))&&(n+=parseInt(c(t,'anc_visits_hf.anc_visits_hf_past.visited_hf_count'))),n+=r.reduce((function(e,t){const n=c(t,'anc_visits_hf.anc_visits_hf_past');return n?(e+='yes'===n.last_visit_attended&&1,isNaN(n.visited_hf_count)?e:e+('yes'===n.report_other_visits&&parseInt(n.visited_hf_count))):0}),0),n},isFacilityDelivery:function(e,t){return!!e&&(1===arguments.length&&(t=e),'yes'===c(t,'facility_delivery'))},getMostRecentLMPDateForPregnancy:v,getNewestReport:y,getSubsequentPregnancyFollowUps:m,isActivePregnancy:function(e,t){if(!_(t))return!1;const r=(v(e,t)||t.reported_date)>n-254016e5,i=function(e,t,r){return e.reports.filter((function(e){return'delivery'===e.form&&e.reported_date>t.reported_date&&(!r||t.reported_date>=n-864e5*r)}))}(e,t,42).length>0,a=function(e,t){return e.reports.filter((function(e){return _(e)&&e.reported_date>t.reported_date}))}(e,t).length>0;return r&&!i&&!a&&!function(e,t){const n=m(e,t),r=y(n,o);return r&&'abortion'===c(r,'pregnancy_summary.visit_option')}(e,t)&&!function(e,t){const n=m(e,t),r=y(n,o);return r&&'miscarriage'===c(r,'pregnancy_summary.visit_option')}(e,t)},getRecentANCVisitWithEvent:function(e,t,n){const r=m(e,t),i=y(r,o);if(i&&c(i,'pregnancy_summary.visit_option')===n)return i},isPregnancyTaskMuted:function(e){const t=y(e.reports,a);return t&&f(t)&&'clear_all'===c(t,'pregnancy_ended.clear_option')},getField:c}},function(e,t){e.exports=function(e){var t={};!function e(t,n){var r=Object.keys(t);for(var i in r){var o=r[i];switch(typeof t[o]){case'object':e(t[o],n);break;case'function':t[o]=t[o].bind(n)}}}(e,t),t.definition=function e(t){var n=Object.assign({},t),r=Object.keys(n);for(var i in r){var o=r[i];if(Array.isArray(n[o])){n[o]=n[o].slice(0);for(var a=0;a({id:'pregnancy-home-visit-week'+e,start:t,end:n,dueDate:function(t,n,r){const i=_(n,r);return f(i||r.reported_date,7*e)}});function h(e,t,n,r){if(t.reported_date=o},resolvedIf:h,actions:[{type:'report',form:'pregnancy_home_visit',label:'Pregnancy home visit'}],events:[...Array(21).keys()].map(e=>v(2*(e+1),6,7))},{name:'anc.facility_reminder',icon:'icon-pregnancy',title:'task.anc.facility_reminder.title',appliesTo:'reports',appliesToType:['pregnancy','pregnancy_home_visit'],appliesIf:function(e,t){return m(t,'t_pregnancy_follow_up_date')},resolvedIf:function(e,t,n,r){if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date),o=f(r,n.end+1).getTime();return p(e.reports,['pregnancy_facility_visit_reminder'],i,o)},actions:[{type:'report',form:'pregnancy_facility_visit_reminder',label:'Pregnancy facility visit reminder',modifyContent:function(e,t,n){e.source_visit_date=m(n,'t_pregnancy_follow_up_date')}}],events:[{id:'pregnancy-facility-visit-reminder',start:3,end:7,dueDate:function(e,t,n){return l(m(n,'t_pregnancy_follow_up_date'))}}]},{name:'anc.pregnancy_danger_sign_followup',icon:'icon-pregnancy-danger',title:'task.anc.pregnancy_danger_sign_followup.title',appliesTo:'reports',appliesToType:['pregnancy','pregnancy_home_visit','pregnancy_danger_sign','pregnancy_danger_sign_follow_up'],appliesIf:function(e,t){return'yes'===m(t,'t_danger_signs_referral_follow_up')&&s(e)},resolvedIf:function(e,t,n,r){if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date+1),o=f(r,n.end+1).getTime();return p(e.reports,['pregnancy_danger_sign_follow_up'],i,o)},actions:[{type:'report',form:'pregnancy_danger_sign_follow_up'}],events:[{id:'pregnancy-danger-sign-follow-up',start:3,end:7,dueDate:function(e,t,n){return l(m(n,'t_danger_signs_referral_follow_up_date'))}}]},{name:'anc.delivery',icon:'icon-mother-child',title:'task.anc.delivery.title',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){const n=_(e,t);return n&&f(n,336)>=o&&s(e)},resolvedIf:function(e,t,n,r){if(y(e,t,'abortion')||y(e,t,'miscarriage'))return!0;if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date),o=f(r,n.end+1).getTime();return p(e.reports,['delivery'],i,o)},actions:[{type:'report',form:'delivery'}],events:[{id:'delivery-reminder',start:28,end:42,dueDate:function(e,t,n){return f(_(t,n),i)}}]},{name:'pnc.danger_sign_followup_mother',icon:'icon-follow-up',title:'task.pnc.danger_sign_followup_mother.title',appliesTo:'reports',appliesToType:['delivery','pnc_danger_sign_follow_up_mother'],appliesIf:function(e,t){return'yes'===m(t,'t_danger_signs_referral_follow_up')&&s(e)},resolvedIf:function(e,t,n,r){if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date+1),o=f(r,n.end+1).getTime();return p(e.reports,['pnc_danger_sign_follow_up_mother'],i,o)},actions:[{type:'report',form:'pnc_danger_sign_follow_up_mother',modifyContent:function(e,t,n){u(n)?e.delivery_uuid=n._id:e.delivery_uuid=m(n,'inputs.delivery_uuid')}}],events:[{id:'pnc-danger-sign-follow-up-mother',start:3,end:7,dueDate:function(e,t,n){return l(m(n,'t_danger_signs_referral_follow_up_date'))}}]},{name:'pnc.danger_sign_followup_baby.from_contact',icon:'icon-follow-up',title:'task.pnc.danger_sign_followup_baby.title',appliesTo:'contacts',appliesToType:['person'],appliesIf:function(e){return e.contact&&'yes'===e.contact.t_danger_signs_referral_follow_up&&s(e)},resolvedIf:function(e,t,n,r){const i=Math.max(f(r,-n.start).getTime(),e.contact.reported_date),o=f(r,n.end).getTime();return p(e.reports,['pnc_danger_sign_follow_up_baby'],i,o)},actions:[{type:'report',form:'pnc_danger_sign_follow_up_baby',modifyContent:function(e,t){e.delivery_uuid=t.contact.created_by_doc}}],events:[{id:'pnc-danger-sign-follow-up-baby',start:3,end:7,dueDate:function(e,t){return l(t.contact.t_danger_signs_referral_follow_up_date)}}]},{name:'pnc.danger_sign_followup_baby.from_report',icon:'icon-follow-up',title:'task.pnc.danger_sign_followup_baby.title',appliesTo:'reports',appliesToType:['pnc_danger_sign_follow_up_baby'],appliesIf:function(e,t){return'yes'===m(t,'t_danger_signs_referral_follow_up')&&s(e)},resolvedIf:function(e,t,n,r){if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date+1),o=f(r,n.end+1).getTime();return p(e.reports,['pnc_danger_sign_follow_up_baby'],i,o)},actions:[{type:'report',form:'pnc_danger_sign_follow_up_baby',modifyContent:function(e,t,n){e.delivery_uuid=m(n,'inputs.delivery_uuid')}}],events:[{id:'pnc-danger-sign-follow-up-baby',start:3,end:7,dueDate:function(e,t,n){return l(m(n,'t_danger_signs_referral_follow_up_date'))}}]}]},function(e,t,n){const r=n(0),{isAlive:i,getSubsequentPregnancyFollowUps:o,getMostRecentLMPDateForPregnancy:a,isActivePregnancy:c,countANCFacilityVisits:s,getField:p}=r;e.exports=[{id:'deaths-this-month',type:'count',icon:'icon-death-general',goal:0,translation_key:'targets.death_reporting.deaths.title',subtitle_translation_key:'targets.this_month.subtitle',appliesTo:'contacts',appliesToType:['person'],appliesIf:function(e){return!i(e)},date:e=>e.contact.date_of_death},{id:'pregnancy-registrations-this-month',type:'count',icon:'icon-pregnancy',goal:20,translation_key:'targets.anc.new_pregnancy_registrations.title',subtitle_translation_key:'targets.this_month.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){return!!t&&a(e,t)},date:'reported',idType:'contact'},{id:'births-this-month',type:'count',icon:'icon-infant',goal:-1,translation_key:'targets.births.title',subtitle_translation_key:'targets.this_month.subtitle',appliesTo:'contacts',appliesToType:['person'],appliesIf:function(e){return e&&e.contact&&e.contact.date_of_birth},date:e=>e.contact.date_of_birth,dhis:{dataElement:'kB0ZBFisE0e'}},{id:'active-pregnancies',type:'count',icon:'icon-pregnancy',goal:-1,translation_key:'targets.anc.active_pregnancies.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){return c(e,t)},date:'now',idType:'contact'},{id:'active-pregnancies-1+-visits',type:'count',icon:'icon-clinic',goal:-1,translation_key:'targets.anc.active_pregnancies_1p_visits.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){if(!c(e,t))return!1;return s(e,t)>0},date:'now',idType:'contact'},{id:'facility-deliveries',type:'percent',icon:'icon-mother-child',goal:-1,translation_key:'targets.anc.facility_deliveries.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['delivery'],appliesIf:function(e,t){return p(t,'delivery_outcome.delivery_place')},passesIf:function(e,t){return'health_facility'===p(t,'delivery_outcome.delivery_place')},date:'now',idType:'contact',dhis:{dataElement:'e22tIwy1nKR',categoryOptionCombo:'HllvX50cXC0',attributeOptionCombo:'HllvX50cXC0'}},{id:'active-pregnancies-4+-visits',type:'count',icon:'icon-clinic',goal:-1,translation_key:'targets.anc.active_pregnancies_4p_visits.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){if(!c(e,t))return!1;return s(e,t)>3},date:'now',idType:'contact'},{id:'active-pregnancies-8+-contacts',type:'count',icon:'icon-follow-up',goal:-1,translation_key:'targets.anc.active_pregnancies_8p_contacts.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){if(!c(e,t))return!1;return 1+(o(e,t).length||0)+(s(e,t)||0)>7},date:'now',idType:'contact'}]},function(e,t,n){var r=n(1),i=n(6);function o(e,t,n,r,i,o){var a;if(e.appliesToType){var c;if('contacts'===e.appliesTo){if(!i.contact)return;c='contact'===i.contact.type?i.contact.contact_type:i.contact.type}else{if(!o)return;c=o.form}if(-1===e.appliesToType.indexOf(c))return}if('scheduled_tasks'===e.appliesTo||!e.appliesIf||e.appliesIf(i,o))if('scheduled_tasks'===e.appliesTo){if(o&&e.appliesIf){if(!o.scheduled_tasks)return;for(a=0;a", + "silence_type": "ANC Reminders, ANC Reminders LMP, ANC Reminders LMP from App", + "silence_for": "8 days", + "fields": [ + { + "field_name": "", + "title": "" + } + ], + "validations": { + "join_responses": true, + "list": [ + { + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "messages.generic.validation.patient_id" + } + ] + }, + "messages": [ + { + "translation_key": "messages.v.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "IMM", + "name": "Immunization Visit (SMS)", + "format": "IMM ", + "silence_type": "", + "silence_for": "", + "fields": [ + { + "field_name": "", + "title": "" + } + ], + "validations": { + "join_responses": true, + "list": [ + { + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "messages.generic.validation.patient_id" + } + ] + }, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "M", + "name": "Postnatal Visit (SMS)", + "format": "M ", + "silence_type": "Postnatal Care, Postnatal Care for Facility Birth, Postnatal Care from App", + "silence_for": "0 days", + "fields": [ + { + "field_name": "", + "title": "" + } + ], + "validations": { + "join_responses": true, + "list": [ + { + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "messages.generic.validation.patient_id" + } + ] + }, + "messages": [ + { + "translation_key": "messages.m.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "F", + "name": "Flag (SMS)", + "format": "F ", + "silence_type": "", + "silence_for": "", + "fields": [ + { + "field_name": "", + "title": "" + } + ], + "validations": { + "join_responses": true, + "list": [ + { + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "messages.generic.validation.patient_id" + } + ] + }, + "messages": [ + { + "translation_key": "messages.f.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.f.report_accepted_parent", + "event_type": "report_accepted", + "recipient": "parent" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "PENTA1", + "name": " PENTA1 Pentavalent", + "format": "PENTA1 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "PENTA2", + "name": " PENTA2 Pentavalent", + "format": "PENTA2 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "PENTA3", + "name": " PENTA3 Pentavalent", + "format": "PENTA3 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "BCG", + "name": " BCG Bacille Calmette-Guerin", + "format": "BCG ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "OPV0", + "name": " OPV0 Oral polio", + "format": "OPV0 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "OPV1", + "name": " OPV1 Oral polio", + "format": "OPV1 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "OPV2", + "name": " OPV2 Oral polio", + "format": "OPV2 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "OPV3", + "name": " OPV3 Oral polio", + "format": "OPV3 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "IPV1", + "name": " IPV1 Inactivated polio", + "format": "IPV1 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "IPV2", + "name": " IPV2 Inactivated polio", + "format": "IPV2 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "IPV3", + "name": " IPV3 Inactivated polio", + "format": "IPV3 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "PCV1", + "name": " PCV1 Pneumococcal pneumonia", + "format": "PCV1 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "PCV2", + "name": " PCV2 Pneumococcal pneumonia", + "format": "PCV2 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "PCV3", + "name": " PCV3 Pneumococcal pneumonia", + "format": "PCV3 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "PCV4", + "name": " PCV4 Pneumococcal pneumonia", + "format": "PCV4 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "ROTA1", + "name": " ROTA1 Rotavirus", + "format": "ROTA1 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "ROTA2", + "name": " ROTA2 Rotavirus", + "format": "ROTA2 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "ROTA3", + "name": " ROTA3 Rotavirus", + "format": "ROTA3 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "VITA", + "name": " VITA Vitamin A", + "format": "VITA ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "MMR1", + "name": " MMR1 Measles Mumps Rubella", + "format": "MMR1 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "MMR2", + "name": " MMR2 Measles Mumps Rubella", + "format": "MMR2 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "MMRV1", + "name": " MMRV1 Measles Mumps Rubella Varicella", + "format": "MMRV1 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "MMRV2", + "name": " MMRV2 Measles Mumps Rubella Varicella", + "format": "MMRV2 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "MN1", + "name": " MN1 Meningococcal", + "format": "MN1 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "MN2", + "name": " MN2 Meningococcal", + "format": "MN2 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "MN3", + "name": " MN3 Meningococcal", + "format": "MN3 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "MN4", + "name": " MN4 Meningococcal", + "format": "MN4 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "FLU", + "name": " FLU Influenza", + "format": "FLU ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "HA1", + "name": " HA1 Hepatitis A", + "format": "HA1 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "HA2", + "name": " HA2 Hepatitis A", + "format": "HA2 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "JE", + "name": " JE Japanese encephalitis", + "format": "JE ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "YF", + "name": " YF Yellow fever", + "format": "YF ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "TY1", + "name": " TY1 Typhoid", + "format": "TY1 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "TY2", + "name": " TY2 Typhoid", + "format": "TY2 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "HPV1", + "name": " HPV1 Human papillomavirus", + "format": "HPV1 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "HPV2", + "name": " HPV2 Human papillomavirus", + "format": "HPV2 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "HPV3", + "name": " HPV3 Human papillomavirus", + "format": "HPV3 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "CH1", + "name": " CH1 Cholera", + "format": "CH1 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "CH2", + "name": " CH2 Cholera", + "format": "CH2 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "CH3", + "name": " CH3 Cholera", + "format": "CH3 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "HB", + "name": "HB Hepatitis B", + "format": "HB ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "FIPV1", + "name": "FIPV1 Fractional IPV", + "format": "FIPV1 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "FIPV2", + "name": "FIPV2 Fractional IPV", + "format": "FIPV2 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "DPT4", + "name": "DPT4 DPT Booster", + "format": "DPT4 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + }, + { + "form": "DPT5", + "name": "DPT5 DPT Booster", + "format": "DPT5 ", + "silence_type": "", + "silence_for": "", + "fields": [], + "validations": {}, + "messages": [ + { + "translation_key": "messages.generic.report_accepted", + "event_type": "report_accepted", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.generic.registration_not_found", + "event_type": "registration_not_found", + "recipient": "reporting_unit" + } + ] + } + ], + "schedules": [ + { + "name": "Registration Followup ANC", + "translation_key": "schedule.registration_anc", + "summary": "", + "description": "", + "start_from": "reported_date", + "messages": [ + { + "translation_key": "messages.schedule.registration.followup_anc", + "group": 1, + "offset": "7 days", + "send_day": "", + "send_time": "", + "recipient": "clinic" + } + ] + }, + { + "name": "Registration Followup ANC PNC", + "translation_key": "schedule.registration_anc_pnc", + "summary": "", + "description": "", + "start_from": "reported_date", + "messages": [ + { + "translation_key": "messages.schedule.registration.followup_anc_pnc", + "group": 1, + "offset": "7 days", + "send_day": "", + "send_time": "", + "recipient": "clinic" + } + ] + }, + { + "name": "Postnatal Care", + "translation_key": "schedule.pnc", + "summary": "", + "description": "", + "start_from": "birth_date", + "messages": [ + { + "translation_key": "messages.schedule.postnatal.day_0", + "group": 1, + "offset": "0", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.postnatal.day_0_overdue", + "group": 1, + "offset": "1 day", + "send_day": "", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.postnatal.day_3", + "group": 2, + "offset": "2 days", + "send_day": "", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.postnatal.day_3_overdue", + "group": 2, + "offset": "4 days", + "send_day": "", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.postnatal.day_7", + "group": 3, + "offset": "6 days", + "send_day": "", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.postnatal.day_7_overdue", + "group": 3, + "offset": "8 days", + "send_day": "", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.postnatal.week_6", + "group": 4, + "offset": "41 days", + "send_day": "", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.postnatal.week_6_overdue", + "group": 4, + "offset": "43 days", + "send_day": "", + "send_time": "9:00", + "recipient": "clinic" + } + ] + }, + { + "name": "Postnatal Care for Facility Birth", + "translation_key": "schedule.pnc_facility_birth", + "summary": "", + "description": "", + "start_from": "birth_date", + "messages": [ + { + "translation_key": "messages.schedule.postnatal.day_3", + "group": 1, + "offset": "2 days", + "send_day": "", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.postnatal.day_3_overdue", + "group": 1, + "offset": "4 days", + "send_day": "", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.postnatal.day_7", + "group": 2, + "offset": "6 days", + "send_day": "", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.postnatal.day_7_overdue", + "group": 2, + "offset": "8 days", + "send_day": "", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.postnatal.week_6", + "group": 3, + "offset": "41 days", + "send_day": "", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.postnatal.week_6_overdue", + "group": 3, + "offset": "43 days", + "send_day": "", + "send_time": "9:00", + "recipient": "clinic" + } + ] + }, + { + "name": "Postnatal Care from App", + "translation_key": "schedule.pnc_from_app", + "summary": "", + "description": "", + "start_from": "fields.birth_date", + "messages": [ + { + "translation_key": "messages.schedule.postnatal.day_3", + "group": 1, + "offset": "2 days", + "send_day": "", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.postnatal.day_3_overdue", + "group": 1, + "offset": "4 days", + "send_day": "", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.postnatal.day_7", + "group": 2, + "offset": "6 days", + "send_day": "", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.postnatal.day_7_overdue", + "group": 2, + "offset": "8 days", + "send_day": "", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.postnatal.week_6", + "group": 3, + "offset": "41 days", + "send_day": "", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.postnatal.week_6_overdue", + "group": 3, + "offset": "43 days", + "send_day": "", + "send_time": "9:00", + "recipient": "clinic" + } + ] + }, + { + "name": "Child Health", + "translation_key": "schedule.child_health", + "summary": "", + "description": "", + "start_from": "birth_date", + "messages": [ + { + "translation_key": "messages.schedule.child.month_01", + "group": 1, + "offset": "1 month", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_02", + "group": 2, + "offset": "2 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_03", + "group": 3, + "offset": "3 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_04", + "group": 4, + "offset": "4 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_05", + "group": 5, + "offset": "5 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_06", + "group": 6, + "offset": "6 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_07", + "group": 7, + "offset": "7 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_08", + "group": 8, + "offset": "8 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_09", + "group": 9, + "offset": "9 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_10", + "group": 10, + "offset": "10 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_11", + "group": 11, + "offset": "11 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_12", + "group": 12, + "offset": "12 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_13", + "group": 13, + "offset": "13 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_14", + "group": 14, + "offset": "14 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_15", + "group": 15, + "offset": "15 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_16", + "group": 16, + "offset": "16 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_17", + "group": 17, + "offset": "17 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_18", + "group": 18, + "offset": "18 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_19", + "group": 19, + "offset": "19 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_20", + "group": 20, + "offset": "20 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_21", + "group": 21, + "offset": "21 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_22", + "group": 22, + "offset": "22 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_23", + "group": 23, + "offset": "23 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_24", + "group": 24, + "offset": "24 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_25", + "group": 25, + "offset": "25 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_26", + "group": 26, + "offset": "26 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_27", + "group": 27, + "offset": "27 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_28", + "group": 28, + "offset": "28 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_29", + "group": 29, + "offset": "29 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_30", + "group": 30, + "offset": "30 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_31", + "group": 31, + "offset": "31 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_32", + "group": 32, + "offset": "32 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_33", + "group": 33, + "offset": "33 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_34", + "group": 34, + "offset": "34 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_35", + "group": 35, + "offset": "35 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_36", + "group": 36, + "offset": "36 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_37", + "group": 37, + "offset": "37 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_38", + "group": 38, + "offset": "38 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_39", + "group": 39, + "offset": "39 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_40", + "group": 40, + "offset": "40 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_41", + "group": 41, + "offset": "41 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_42", + "group": 42, + "offset": "42 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_43", + "group": 43, + "offset": "43 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_44", + "group": 44, + "offset": "44 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_45", + "group": 45, + "offset": "45 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_46", + "group": 46, + "offset": "46 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_47", + "group": 47, + "offset": "47 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_48", + "group": 48, + "offset": "48 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_49", + "group": 49, + "offset": "49 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_50", + "group": 50, + "offset": "50 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_51", + "group": 51, + "offset": "51 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_52", + "group": 52, + "offset": "52 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_53", + "group": 53, + "offset": "53 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_54", + "group": 54, + "offset": "54 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_55", + "group": 55, + "offset": "55 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_56", + "group": 56, + "offset": "56 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_57", + "group": 57, + "offset": "57 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_58", + "group": 58, + "offset": "58 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_59", + "group": 59, + "offset": "59 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_60", + "group": 60, + "offset": "60 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + } + ] + }, + { + "name": "Child Health from App", + "translation_key": "schedule.child_health", + "summary": "", + "description": "", + "start_from": "fields.birth_date", + "messages": [ + { + "translation_key": "messages.schedule.child.month_01", + "group": 1, + "offset": "1 month", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_02", + "group": 2, + "offset": "2 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_03", + "group": 3, + "offset": "3 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_04", + "group": 4, + "offset": "4 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_05", + "group": 5, + "offset": "5 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_06", + "group": 6, + "offset": "6 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_07", + "group": 7, + "offset": "7 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_08", + "group": 8, + "offset": "8 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_09", + "group": 9, + "offset": "9 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_10", + "group": 10, + "offset": "10 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_11", + "group": 11, + "offset": "11 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_12", + "group": 12, + "offset": "12 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_13", + "group": 13, + "offset": "13 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_14", + "group": 14, + "offset": "14 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_15", + "group": 15, + "offset": "15 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_16", + "group": 16, + "offset": "16 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_17", + "group": 17, + "offset": "17 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_18", + "group": 18, + "offset": "18 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_19", + "group": 19, + "offset": "19 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_20", + "group": 20, + "offset": "20 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_21", + "group": 21, + "offset": "21 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_22", + "group": 22, + "offset": "22 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_23", + "group": 23, + "offset": "23 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_24", + "group": 24, + "offset": "24 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_25", + "group": 25, + "offset": "25 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_26", + "group": 26, + "offset": "26 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_27", + "group": 27, + "offset": "27 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_28", + "group": 28, + "offset": "28 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_29", + "group": 29, + "offset": "29 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_30", + "group": 30, + "offset": "30 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_31", + "group": 31, + "offset": "31 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_32", + "group": 32, + "offset": "32 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_33", + "group": 33, + "offset": "33 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_34", + "group": 34, + "offset": "34 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_35", + "group": 35, + "offset": "35 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_36", + "group": 36, + "offset": "36 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_37", + "group": 37, + "offset": "37 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_38", + "group": 38, + "offset": "38 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_39", + "group": 39, + "offset": "39 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_40", + "group": 40, + "offset": "40 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_41", + "group": 41, + "offset": "41 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_42", + "group": 42, + "offset": "42 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_43", + "group": 43, + "offset": "43 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_44", + "group": 44, + "offset": "44 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_45", + "group": 45, + "offset": "45 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_46", + "group": 46, + "offset": "46 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_47", + "group": 47, + "offset": "47 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_48", + "group": 48, + "offset": "48 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_49", + "group": 49, + "offset": "49 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_50", + "group": 50, + "offset": "50 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_51", + "group": 51, + "offset": "51 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_52", + "group": 52, + "offset": "52 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_53", + "group": 53, + "offset": "53 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_54", + "group": 54, + "offset": "54 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_55", + "group": 55, + "offset": "55 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_56", + "group": 56, + "offset": "56 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_57", + "group": 57, + "offset": "57 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_58", + "group": 58, + "offset": "58 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_59", + "group": 59, + "offset": "59 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.child.month_60", + "group": 60, + "offset": "60 months", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + } + ] + }, + { + "name": "ANC Reminders LMP", + "translation_key": "schedule.anc_lmp", + "summary": "", + "description": "", + "start_from": "lmp_date", + "messages": [ + { + "translation_key": "messages.schedule.anc.reminder", + "group": 1, + "offset": "12 weeks", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.followup", + "group": 1, + "offset": "13 weeks", + "send_day": "monday", + "send_time": "10:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder", + "group": 2, + "offset": "24 weeks", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.followup", + "group": 2, + "offset": "25 weeks", + "send_day": "monday", + "send_time": "10:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder", + "group": 3, + "offset": "32 weeks", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.followup", + "group": 3, + "offset": "33 weeks", + "send_day": "monday", + "send_time": "10:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder", + "group": 4, + "offset": "36 weeks", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.followup", + "group": 4, + "offset": "37 weeks", + "send_day": "monday", + "send_time": "10:00", + "recipient": "clinic" + } + ] + }, + { + "name": "ANC Reminders LMP from App", + "translation_key": "schedule.anc_lmp_from_app", + "summary": "", + "description": "", + "start_from": "fields.lmp_date_8601", + "messages": [ + { + "translation_key": "messages.schedule.anc.reminder", + "group": 1, + "offset": "12 weeks", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.followup", + "group": 1, + "offset": "13 weeks", + "send_day": "monday", + "send_time": "10:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder", + "group": 2, + "offset": "24 weeks", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.followup", + "group": 2, + "offset": "25 weeks", + "send_day": "monday", + "send_time": "10:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder", + "group": 3, + "offset": "32 weeks", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.followup", + "group": 3, + "offset": "33 weeks", + "send_day": "monday", + "send_time": "10:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder", + "group": 4, + "offset": "36 weeks", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.followup", + "group": 4, + "offset": "37 weeks", + "send_day": "monday", + "send_time": "10:00", + "recipient": "clinic" + } + ] + }, + { + "name": "ANC Reminders", + "translation_key": "schedule.anc_no_lmp", + "summary": "", + "description": "", + "start_from": "reported_date", + "messages": [ + { + "translation_key": "messages.schedule.anc.reminder", + "group": 1, + "offset": "1 weeks", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.followup", + "group": 1, + "offset": "2 weeks", + "send_day": "monday", + "send_time": "10:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder", + "group": 2, + "offset": "5 weeks", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.followup", + "group": 2, + "offset": "6 weeks", + "send_day": "monday", + "send_time": "10:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder", + "group": 3, + "offset": "9 weeks", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.followup", + "group": 3, + "offset": "10 weeks", + "send_day": "monday", + "send_time": "10:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder", + "group": 4, + "offset": "13 weeks", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.followup", + "group": 4, + "offset": "14 weeks", + "send_day": "monday", + "send_time": "10:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder", + "group": 5, + "offset": "17 weeks", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.followup", + "group": 5, + "offset": "18 weeks", + "send_day": "monday", + "send_time": "10:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder", + "group": 6, + "offset": "21 weeks", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.followup", + "group": 6, + "offset": "22 weeks", + "send_day": "monday", + "send_time": "10:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder", + "group": 7, + "offset": "25 weeks", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.followup", + "group": 7, + "offset": "26 weeks", + "send_day": "monday", + "send_time": "10:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_due", + "group": 8, + "offset": "29 weeks", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.followup", + "group": 8, + "offset": "30 weeks", + "send_day": "monday", + "send_time": "10:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_due", + "group": 9, + "offset": "33 weeks", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.followup", + "group": 9, + "offset": "34 weeks", + "send_day": "monday", + "send_time": "10:00", + "recipient": "clinic" + } + ] + }, + { + "name": "Delivery Reminders LMP", + "translation_key": "schedule.delivery_lmp", + "summary": "", + "description": "", + "start_from": "lmp_date", + "messages": [ + { + "translation_key": "messages.schedule.anc.week_38", + "group": 1, + "offset": "38 weeks", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.week_40", + "group": 1, + "offset": "40 weeks", + "send_day": "monday", + "send_time": "10:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.checkin", + "group": 1, + "offset": "42 weeks", + "send_day": "monday", + "send_time": "11:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.checkin", + "group": 1, + "offset": "44 weeks", + "send_day": "monday", + "send_time": "11:00", + "recipient": "clinic" + } + ] + }, + { + "name": "Delivery Reminders LMP from App", + "translation_key": "schedule.delivery_lmp_from_app", + "summary": "", + "description": "", + "start_from": "fields.lmp_date_8601", + "messages": [ + { + "translation_key": "messages.schedule.anc.week_38", + "group": 1, + "offset": "38 weeks", + "send_day": "monday", + "send_time": "09:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.week_40", + "group": 1, + "offset": "40 weeks", + "send_day": "monday", + "send_time": "10:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.checkin", + "group": 1, + "offset": "42 weeks", + "send_day": "monday", + "send_time": "11:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.checkin", + "group": 1, + "offset": "44 weeks", + "send_day": "monday", + "send_time": "11:00", + "recipient": "clinic" + } + ] + }, + { + "name": "Delivery Reminders", + "translation_key": "schedule.delivery_no_lmp", + "summary": "", + "description": "", + "start_from": "reported_date", + "messages": [ + { + "translation_key": "messages.schedule.anc.checkin", + "group": 1, + "offset": "37 weeks", + "send_day": "monday", + "send_time": "11:00", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.checkin", + "group": 1, + "offset": "39 weeks", + "send_day": "monday", + "send_time": "11:00", + "recipient": "clinic" + } + ] + }, + { + "name": "Nutrition Evaluation Moderate", + "translation_key": "schedule.nutrition_evaluation", + "summary": "", + "description": "", + "start_from": "reported_date", + "messages": [ + { + "translation_key": "messages.schedule.nutrition_evaluation", + "group": 1, + "offset": "1 week", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.nutrition_evaluation", + "group": 2, + "offset": "2 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + } + ] + }, + { + "name": "Nutrition Evaluation Severe", + "translation_key": "schedule.nutrition_evaluation", + "summary": "", + "description": "", + "start_from": "reported_date", + "messages": [ + { + "translation_key": "messages.schedule.nutrition_evaluation", + "group": 1, + "offset": "2 days", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.nutrition_evaluation", + "group": 2, + "offset": "4 days", + "send_day": "", + "send_time": "", + "recipient": "clinic" + } + ] + }, + { + "name": "OTP", + "translation_key": "schedule.nutrition_otp", + "summary": "", + "description": "", + "start_from": "reported_date", + "messages": [ + { + "translation_key": "messages.schedule.otp", + "group": 1, + "offset": "1 week", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.otp", + "group": 2, + "offset": "2 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.otp", + "group": 3, + "offset": "3 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.otp", + "group": 4, + "offset": "4 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.otp", + "group": 5, + "offset": "5 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.otp", + "group": 6, + "offset": "6 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.otp", + "group": 7, + "offset": "7 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.otp", + "group": 8, + "offset": "8 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.otp", + "group": 9, + "offset": "9 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.otp", + "group": 10, + "offset": "10 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.otp", + "group": 11, + "offset": "11 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.otp", + "group": 12, + "offset": "12 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + } + ] + }, + { + "name": "SFP", + "translation_key": "schedule.nutrition_sfp", + "summary": "", + "description": "", + "start_from": "reported_date", + "messages": [ + { + "translation_key": "messages.schedule.sfp", + "group": 1, + "offset": "2 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.sfp", + "group": 2, + "offset": "4 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.sfp", + "group": 3, + "offset": "6 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.sfp", + "group": 4, + "offset": "8 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.sfp", + "group": 5, + "offset": "10 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.sfp", + "group": 6, + "offset": "12 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.sfp", + "group": 7, + "offset": "16 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + } + ] + }, + { + "name": "SC", + "translation_key": "schedule.nutrition_sc", + "summary": "", + "description": "", + "start_from": "reported_date", + "messages": [ + { + "translation_key": "messages.schedule.sc", + "group": 1, + "offset": "1 day", + "send_day": "", + "send_time": "", + "recipient": "clinic" + } + ] + }, + { + "name": "SC Exit", + "translation_key": "schedule.nutrition_sc_exit", + "summary": "", + "description": "", + "start_from": "reported_date", + "messages": [ + { + "translation_key": "messages.schedule.sc_exit", + "group": 1, + "offset": "1 week", + "send_day": "", + "send_time": "", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.sc_exit", + "group": 2, + "offset": "2 weeks", + "send_day": "", + "send_time": "", + "recipient": "clinic" + } + ] + } + ], + "notifications": { + "off_form": "OFF", + "on_form": "ON", + "confirm_deactivation": false, + "validations": { + "join_responses": true, + "list": [ + { + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "messages.generic.validation.patient_id" + } + ] + }, + "messages": [ + { + "translation_key": "messages.off.report_accepted", + "event_type": "on_mute", + "recipient": "reporting_unit" + }, + { + "translation_key": "messages.on.report_accepted", + "event_type": "on_unmute", + "recipient": "reporting_unit" + }, + { + "translation_key": "generic.registration_not_found", + "event_type": "patient_not_found", + "recipient": "reporting_unit" + } + ] + }, + "_version": "2.9.0-alpha.4373", + "transitions": { + "default_responses": true, + "update_clinics": true, + "accept_patient_reports": true, + "conditional_alerts": true, + "update_sent_by": false, + "registration": true, + "update_scheduled_reports": false, + "multi_report_alerts": false, + "generate_patient_id_on_people": true, + "death_reporting": true, + "resolve_pending": false + }, + "death_reporting": { + "mark_deceased_forms": [ + "death_confirmation" + ], + "date_field": "fields.date_of_death" + }, + "forms": { + "NP":{ + "meta": { + "code": "NP", + "label": { + "en": "New Person with phone_number Registration" + } + }, + "fields": { + "phone": { + "labels": { + "tiny": { + "en": "phone number" + }, + "description": { + "en": "phone number" + }, + "short": { + "en": "phone number" + } + }, + "position": 0, + "type": "integer", + "length": [ + 9, + 10 + ], + "required": true + }, + "patient_name": { + "labels": { + "tiny": { + "en": "patient_name" + }, + "description": { + "en": "Patient name" + }, + "short": { + "en": "Patient name" + } + }, + "position": 1, + "type": "string", + "length": [ + 3, + 30 + ], + "required": true + } + } + }, + "DR": { + "meta": { + "code": "DR", + "icon": "icon-death-general", + "translation_key": "form.dr.title" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + }, + "tiny": { + "en": "ID" + } + }, + "position": 0, + "flags": { + "input_digits_only": true + }, + "length": [ + 5, + 13 + ], + "type": "string", + "required": true + } + }, + "use_sentinel": true + }, + "IMM": { + "meta": { + "code": "IMM", + "icon": "immunization", + "translation_key": "form.imm.title" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": true, + "use_sentinel": true + }, + "C_IMM": { + "meta": { + "code": "C_IMM", + "icon": "immunization", + "translation_key": "form.c_imm.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "ID" + }, + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 1, + 13 + ], + "required": false + }, + "visit_confirmed": { + "labels": { + "tiny": { + "en": "c" + }, + "short": { + "translation_key": "report.c_imm.visit_confirmed" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_bcg": { + "labels": { + "tiny": { + "en": "bcg" + }, + "short": { + "translation_key": "report.c_imm.received_bcg" + } + }, + "position": 3, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_cholera_1": { + "labels": { + "tiny": { + "en": "ch1" + }, + "short": { + "translation_key": "report.c_imm.received_cholera_1" + } + }, + "position": 4, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_cholera_2": { + "labels": { + "tiny": { + "en": "ch2" + }, + "short": { + "translation_key": "report.c_imm.received_cholera_2" + } + }, + "position": 5, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_cholera_3": { + "labels": { + "tiny": { + "en": "ch3" + }, + "short": { + "translation_key": "report.c_imm.received_cholera_3" + } + }, + "position": 6, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_hep_a_1": { + "labels": { + "tiny": { + "en": "ha1" + }, + "short": { + "translation_key": "report.c_imm.received_hep_a_1" + } + }, + "position": 7, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_hep_a_2": { + "labels": { + "tiny": { + "en": "ha2" + }, + "short": { + "translation_key": "report.c_imm.received_hep_a_2" + } + }, + "position": 8, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_hep_b": { + "labels": { + "tiny": { + "en": "hb" + }, + "short": { + "translation_key": "report.c_imm.received_hep_b" + } + }, + "position": 9, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_hpv_1": { + "labels": { + "tiny": { + "en": "hpv1" + }, + "short": { + "translation_key": "report.c_imm.received_hpv_1" + } + }, + "position": 10, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_hpv_2": { + "labels": { + "tiny": { + "en": "hpv2" + }, + "short": { + "translation_key": "report.c_imm.received_hpv_2" + } + }, + "position": 11, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_hpv_3": { + "labels": { + "tiny": { + "en": "hpv3" + }, + "short": { + "translation_key": "report.c_imm.received_hpv_3" + } + }, + "position": 12, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_flu": { + "labels": { + "tiny": { + "en": "flu" + }, + "short": { + "translation_key": "report.c_imm.received_flu" + } + }, + "position": 13, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_jap_enc": { + "labels": { + "tiny": { + "en": "je" + }, + "short": { + "translation_key": "report.c_imm.received_jap_enc" + } + }, + "position": 14, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_meningococcal_1": { + "labels": { + "tiny": { + "en": "mn1" + }, + "short": { + "translation_key": "report.c_imm.received_meningococcal_1" + } + }, + "position": 15, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_meningococcal_2": { + "labels": { + "tiny": { + "en": "mn2" + }, + "short": { + "translation_key": "report.c_imm.received_meningococcal_2" + } + }, + "position": 16, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_meningococcal_3": { + "labels": { + "tiny": { + "en": "mn3" + }, + "short": { + "translation_key": "report.c_imm.received_meningococcal_3" + } + }, + "position": 17, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_meningococcal_4": { + "labels": { + "tiny": { + "en": "mn4" + }, + "short": { + "translation_key": "report.c_imm.received_meningococcal_4" + } + }, + "position": 18, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_mmr_1": { + "labels": { + "tiny": { + "en": "mmr1" + }, + "short": { + "translation_key": "report.c_imm.received_mmr_1" + } + }, + "position": 19, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_mmr_2": { + "labels": { + "tiny": { + "en": "mmr2" + }, + "short": { + "translation_key": "report.c_imm.received_mmr_2" + } + }, + "position": 20, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_mmrv_1": { + "labels": { + "tiny": { + "en": "mmrv1" + }, + "short": { + "translation_key": "report.c_imm.received_mmrv_1" + } + }, + "position": 21, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_mmrv_2": { + "labels": { + "tiny": { + "en": "mmrv2" + }, + "short": { + "translation_key": "report.c_imm.received_mmrv_2" + } + }, + "position": 22, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_polio_0": { + "labels": { + "tiny": { + "en": "opv0" + }, + "short": { + "translation_key": "report.c_imm.received_polio_0" + } + }, + "position": 23, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_polio_1": { + "labels": { + "tiny": { + "en": "opv1" + }, + "short": { + "translation_key": "report.c_imm.received_polio_1" + } + }, + "position": 24, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_polio_2": { + "labels": { + "tiny": { + "en": "opv2" + }, + "short": { + "translation_key": "report.c_imm.received_polio_2" + } + }, + "position": 25, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_polio_3": { + "labels": { + "tiny": { + "en": "opv3" + }, + "short": { + "translation_key": "report.c_imm.received_polio_3" + } + }, + "position": 26, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_ipv_1": { + "labels": { + "tiny": { + "en": "ipv1" + }, + "short": { + "translation_key": "report.c_imm.received_ipv_1" + } + }, + "position": 27, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_ipv_2": { + "labels": { + "tiny": { + "en": "ipv2" + }, + "short": { + "translation_key": "report.c_imm.received_ipv_2" + } + }, + "position": 28, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_ipv_3": { + "labels": { + "tiny": { + "en": "ipv3" + }, + "short": { + "translation_key": "report.c_imm.received_ipv_3" + } + }, + "position": 29, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_fipv_1": { + "labels": { + "tiny": { + "en": "fipv1" + }, + "short": { + "translation_key": "report.c_imm.received_fipv_1" + } + }, + "position": 30, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_fipv_2": { + "labels": { + "tiny": { + "en": "fipv2" + }, + "short": { + "translation_key": "report.c_imm.received_fipv_2" + } + }, + "position": 31, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_penta_1": { + "labels": { + "tiny": { + "en": "penta1" + }, + "short": { + "translation_key": "report.c_imm.received_penta_1" + } + }, + "position": 32, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_penta_2": { + "labels": { + "tiny": { + "en": "penta2" + }, + "short": { + "translation_key": "report.c_imm.received_penta_2" + } + }, + "position": 33, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_penta_3": { + "labels": { + "tiny": { + "en": "penta3" + }, + "short": { + "translation_key": "report.c_imm.received_penta_3" + } + }, + "position": 34, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_dpt_4": { + "labels": { + "tiny": { + "en": "dpt4" + }, + "short": { + "translation_key": "report.c_imm.received_dpt_4" + } + }, + "position": 35, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_dpt_5": { + "labels": { + "tiny": { + "en": "dpt5" + }, + "short": { + "translation_key": "report.c_imm.received_dpt_5" + } + }, + "position": 36, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_pneumococcal_1": { + "labels": { + "tiny": { + "en": "pcv1" + }, + "short": { + "translation_key": "report.c_imm.received_pneumococcal_1" + } + }, + "position": 37, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_pneumococcal_2": { + "labels": { + "tiny": { + "en": "pcv2" + }, + "short": { + "translation_key": "report.c_imm.received_pneumococcal_2" + } + }, + "position": 38, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_pneumococcal_3": { + "labels": { + "tiny": { + "en": "pcv3" + }, + "short": { + "translation_key": "report.c_imm.received_pneumococcal_3" + } + }, + "position": 39, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_pneumococcal_4": { + "labels": { + "tiny": { + "en": "pcv4" + }, + "short": { + "translation_key": "report.c_imm.received_pneumococcal_4" + } + }, + "position": 40, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_rotavirus_1": { + "labels": { + "tiny": { + "en": "rv1" + }, + "short": { + "translation_key": "report.c_imm.received_rotavirus_1" + } + }, + "position": 41, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_rotavirus_2": { + "labels": { + "tiny": { + "en": "rv2" + }, + "short": { + "translation_key": "report.c_imm.received_rotavirus_2" + } + }, + "position": 42, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_rotavirus_3": { + "labels": { + "tiny": { + "en": "rv3" + }, + "short": { + "translation_key": "report.c_imm.received_rotavirus_3" + } + }, + "position": 43, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_typhoid_1": { + "labels": { + "tiny": { + "en": "ty1" + }, + "short": { + "translation_key": "report.c_imm.received_typhoid_1" + } + }, + "position": 44, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_typhoid_2": { + "labels": { + "tiny": { + "en": "ty2" + }, + "short": { + "translation_key": "report.c_imm.received_typhoid_2" + } + }, + "position": 45, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_vitamin_a": { + "labels": { + "tiny": { + "en": "vita" + }, + "short": { + "translation_key": "report.c_imm.received_vitamin_a" + } + }, + "position": 46, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + }, + "received_yellow_fever": { + "labels": { + "tiny": { + "en": "yf" + }, + "short": { + "translation_key": "report.c_imm.received_yellow_fever" + } + }, + "position": 47, + "type": "string", + "length": [ + 1, + 10 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "N": { + "meta": { + "code": "N", + "icon": "new-person", + "translation_key": "form.n.title" + }, + "fields": { + "patient_name": { + "labels": { + "tiny": { + "en": "N" + }, + "short": { + "translation_key": "report.n.patient_name" + } + }, + "position": 0, + "type": "string", + "length": [ + 1, + 30 + ], + "required": true + } + }, + "public_form": false, + "use_sentinel": true + }, + "CW": { + "meta": { + "code": "CW", + "icon": "child", + "translation_key": "form.cw.title" + }, + "fields": { + "weeks_since_birth": { + "labels": { + "short": { + "translation_key": "report.cw.weeks_since_birth" + } + }, + "position": 0, + "type": "integer", + "length": [ + 1, + 3 + ], + "range": [ + 0, + 260 + ], + "required": true, + "validations": {}, + "flags": {} + }, + "patient_name": { + "labels": { + "short": { + "translation_key": "report.cw.patient_name" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 30 + ], + "required": true + } + }, + "public_form": false, + "use_sentinel": true + }, + "C": { + "meta": { + "code": "C", + "icon": "child", + "translation_key": "form.cw.title" + }, + "fields": { + "months_since_birth": { + "labels": { + "short": { + "translation_key": "report.c.months_since_birth" + } + }, + "position": 0, + "type": "integer", + "length": [ + 1, + 3 + ], + "range": [ + 0, + 260 + ], + "required": true, + "validations": {}, + "flags": {} + }, + "patient_name": { + "labels": { + "short": { + "translation_key": "report.cw.patient_name" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 30 + ], + "required": true + } + }, + "public_form": false, + "use_sentinel": true + }, + "G": { + "meta": { + "code": "G", + "icon": "child", + "translation_key": "form.g.title" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + }, + "tiny": { + "en": "ID" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true, + "validations": {}, + "flags": {} + }, + "severity": { + "labels": { + "short": { + "translation_key": "report.g.severity" + }, + "tiny": { + "en": "severity" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 1 + ], + "required": true + } + }, + "public_form": false, + "use_sentinel": true + }, + "CF": { + "meta": { + "code": "CF", + "icon": "follow-up", + "translation_key": "form.cf.title" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + }, + "tiny": { + "en": "ID" + } + }, + "position": 0, + "flags": { + "input_digits_only": true + }, + "length": [ + 5, + 13 + ], + "type": "string", + "required": true + }, + "outcome": { + "labels": { + "short": { + "translation_key": "report.cf.outcome" + }, + "tiny": { + "en": "outcome" + } + }, + "position": 0, + "flags": { + "input_digits_only": true + }, + "length": [ + 0, + 1 + ], + "type": "string", + "required": false + } + }, + "use_sentinel": true + }, + "CHILD": { + "meta": { + "code": "CHILD", + "icon": "child", + "translation_key": "form.child.title" + }, + "fields": { + "days_since_birth": { + "labels": { + "tiny": { + "en": "D" + }, + "short": { + "translation_key": "report.child.days_since_birth" + } + }, + "position": 0, + "type": "integer", + "length": [ + 1, + 4 + ], + "range": [ + 0, + 1827 + ], + "required": true, + "validations": {}, + "flags": {} + }, + "patient_name": { + "labels": { + "tiny": { + "en": "N" + }, + "short": { + "translation_key": "report.child.patient_name" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 30 + ], + "required": true + } + }, + "public_form": false, + "use_sentinel": true + }, + "P": { + "meta": { + "code": "P", + "icon": "pregnancy-1", + "translation_key": "form.p.title" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + }, + "tiny": { + "en": "ID" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "last_menstrual_period": { + "labels": { + "tiny": { + "en": "LMP" + }, + "short": { + "translation_key": "report.p.last_menstrual_period" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 2 + ], + "range": [ + 2, + 42 + ], + "required": false, + "validations": {} + } + }, + "public_form": true, + "use_sentinel": true + }, + "V": { + "meta": { + "code": "V", + "icon": "clinic", + "translation_key": "form.v.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "id" + }, + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + }, + "tiny": { + "en": "N" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": true, + "use_sentinel": true + }, + "M": { + "meta": { + "code": "M", + "icon": "clinic", + "translation_key": "form.m.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "id" + }, + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + } + }, + "public_form": true, + "use_sentinel": true + }, + "D": { + "meta": { + "code": "D", + "icon": "mother-child", + "translation_key": "form.d.title" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + }, + "tiny": { + "en": "ID" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "delivery_code": { + "labels": { + "short": { + "translation_key": "report.d.delivery_code" + }, + "tiny": { + "en": "C" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 2 + ], + "required": true + }, + "days_since_birth": { + "labels": { + "short": { + "translation_key": "report.d.days_since_birth" + }, + "tiny": { + "en": "D" + } + }, + "position": 2, + "type": "string", + "length": [ + 1, + 3 + ], + "required": false + } + }, + "public_form": true, + "use_sentinel": true + }, + "F": { + "meta": { + "code": "F", + "icon": "risk", + "translation_key": "form.f.title" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + }, + "tiny": { + "en": "ID" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + }, + "tiny": { + "en": "N" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": true, + "use_sentinel": true + }, + "PENTA1": { + "meta": { + "code": "PENTA1", + "icon": "immunization", + "translation_key": "PENTA1 Pentavalent" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "PENTA2": { + "meta": { + "code": "PENTA2", + "icon": "immunization", + "translation_key": "PENTA2 Pentavalent" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "PENTA3": { + "meta": { + "code": "PENTA3", + "icon": "immunization", + "translation_key": "PENTA3 Pentavalent" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "BCG": { + "meta": { + "code": "BCG", + "icon": "immunization", + "translation_key": "BCG Bacille Calmette-Guerin" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "OPV0": { + "meta": { + "code": "OPV0", + "icon": "immunization", + "translation_key": "OPV0 Oral Polio" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "OPV1": { + "meta": { + "code": "OPV1", + "icon": "immunization", + "translation_key": "OPV1 Oral Polio" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "OPV2": { + "meta": { + "code": "OPV2", + "icon": "immunization", + "translation_key": "OPV2 Oral Polio" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "OPV3": { + "meta": { + "code": "OPV3", + "icon": "immunization", + "translation_key": "OPV3 Oral Polio" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "IPV1": { + "meta": { + "code": "IPV1", + "icon": "immunization", + "translation_key": "IPV1 Inactivated Polio" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "IPV2": { + "meta": { + "code": "IPV2", + "icon": "immunization", + "translation_key": "IPV2 Inactivated Polio" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "IPV3": { + "meta": { + "code": "IPV3", + "icon": "immunization", + "translation_key": "IPV3 Inactivated Polio" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "PCV1": { + "meta": { + "code": "PCV1", + "icon": "immunization", + "translation_key": "PCV1 Pneumococcal Pneumonia" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "PCV2": { + "meta": { + "code": "PCV2", + "icon": "immunization", + "translation_key": "PCV2 Pneumococcal Pneumonia" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "PCV3": { + "meta": { + "code": "PCV3", + "icon": "immunization", + "translation_key": "PCV3 Pneumococcal Pneumonia" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "PCV4": { + "meta": { + "code": "PCV4", + "icon": "immunization", + "translation_key": "PCV4 Pneumococcal Pneumonia" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "ROTA1": { + "meta": { + "code": "ROTA1", + "icon": "immunization", + "translation_key": "ROTA1 Rotavirus" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "ROTA2": { + "meta": { + "code": "ROTA2", + "icon": "immunization", + "translation_key": "ROTA2 Rotavirus" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "ROTA3": { + "meta": { + "code": "ROTA3", + "icon": "immunization", + "translation_key": "ROTA3 Rotavirus" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "VITA": { + "meta": { + "code": "VITA", + "icon": "immunization", + "translation_key": "VITA Vitamin A" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "MMR1": { + "meta": { + "code": "MMR1", + "icon": "immunization", + "translation_key": "MMR1 Measles Mumps Rubella" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "MMR2": { + "meta": { + "code": "MMR2", + "icon": "immunization", + "translation_key": "MMR2 Measles Mumps Rubella" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "MMRV1": { + "meta": { + "code": "MMRV1", + "icon": "immunization", + "translation_key": "MMRV1 Measles Mumps Rubella Varicella" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "MMRV2": { + "meta": { + "code": "MMRV2", + "icon": "immunization", + "translation_key": "MMRV2 Measles Mumps Rubella Varicella" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "MN1": { + "meta": { + "code": "MN1", + "icon": "immunization", + "translation_key": "MN1 Meningococcal" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "MN2": { + "meta": { + "code": "MN2", + "icon": "immunization", + "translation_key": "MN2 Meningococcal" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "MN3": { + "meta": { + "code": "MN3", + "icon": "immunization", + "translation_key": "MN3 Meningococcal" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "MN4": { + "meta": { + "code": "MN4", + "icon": "immunization", + "translation_key": "MN4 Meningococcal" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "FLU": { + "meta": { + "code": "FLU", + "icon": "immunization", + "translation_key": "FLU Influenza" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "HA1": { + "meta": { + "code": "HA1", + "icon": "immunization", + "translation_key": "HA1 Hepatitis A" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "HA2": { + "meta": { + "code": "HA2", + "icon": "immunization", + "translation_key": "HA2 Hepatitis A" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "JE": { + "meta": { + "code": "JE", + "icon": "immunization", + "translation_key": "JE Japanese Encephalitis" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "YF": { + "meta": { + "code": "YF", + "icon": "immunization", + "translation_key": "YF Yellow Fever" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "TY1": { + "meta": { + "code": "TY1", + "icon": "immunization", + "translation_key": "TY1 Typhoid" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "TY2": { + "meta": { + "code": "TY2", + "icon": "immunization", + "translation_key": "TY2 Typhoid" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "HPV1": { + "meta": { + "code": "HPV1", + "icon": "immunization", + "translation_key": "HPV1 Human Papillomavirus" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "HPV2": { + "meta": { + "code": "HPV2", + "icon": "immunization", + "translation_key": "HPV2 Human Papillomavirus" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "HPV3": { + "meta": { + "code": "HPV3", + "icon": "immunization", + "translation_key": "HPV3 Human Papillomavirus" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "CH1": { + "meta": { + "code": "CH1", + "icon": "immunization", + "translation_key": "CH1 Cholera" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "CH2": { + "meta": { + "code": "CH2", + "icon": "immunization", + "translation_key": "CH2 Cholera" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "CH3": { + "meta": { + "code": "CH3", + "icon": "immunization", + "translation_key": "CH3 Cholera" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "HB": { + "meta": { + "code": "HB", + "icon": "immunization", + "translation_key": "HB Hepatitis B" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "DPT4": { + "meta": { + "code": "DPT4", + "icon": "immunization", + "translation_key": "DPT4 DPT Booster 1" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "DPT5": { + "meta": { + "code": "DPT5", + "icon": "immunization", + "translation_key": "DPT5 DPT Booster 2" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "FIPV1": { + "meta": { + "code": "FIPV1", + "icon": "immunization", + "translation_key": "fIPV1 Fractional IPV 1" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "FIPV2": { + "meta": { + "code": "FIPV2", + "icon": "immunization", + "translation_key": "fIPV2 Fractional IPV 2" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "OFF": { + "meta": { + "code": "OFF", + "icon": "off", + "translation_key": "form.off.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "ID" + }, + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "tiny": { + "en": "r" + }, + "short": { + "translation_key": "report.off.reason" + } + }, + "position": 1, + "length": [ + 3, + 100 + ], + "type": "string" + } + }, + "public_form": true, + "use_sentinel": true + }, + "ON": { + "meta": { + "code": "ON", + "icon": "on", + "translation_key": "form.on.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "ID" + }, + "short": { + "translation_key": "patient_id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "tiny": { + "en": "N" + }, + "short": { + "translation_key": "notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "use_sentinel": true + } + }, + "statistics_submission": "none", + "statistics_submission_number": "", + "replication_depth": [ + { + "role": "non-existant-role", + "depth": 42 + } + ], + "tasks": { + "rules": "define Target { _id: null, contact: null, deleted: null, type: null, pass: null, date: null, groupBy: null }\ndefine Contact { contact: null, reports: null, tasks: null }\ndefine Task { _id: null, deleted: null, doc: null, contact: null, icon: null, date: null, readyStart: null, readyEnd: null, title: null, fields: null, resolved: null, priority: null, priorityLabel: null, reports: null, actions: null }\nrule GenerateEvents {\n when { c: Contact } then { !function(e){var t={};function i(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,i),r.l=!0,r.exports}i.m=e,i.c=t,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){'undefined'!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:'Module'}),Object.defineProperty(e,'__esModule',{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&'object'==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,'default',{enumerable:!0,value:e}),2&t&&'string'!=typeof e)for(var r in e)i.d(n,r,function(t){return e[t]}.bind(null,r));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,'a',t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p='',i(i.s=2)}([function(e,t){const i=new Date;var n=['V','pregnancy_visit'],r=['D','delivery'],s=['bcg','cholera_1','cholera_2','cholera_3','hep_a_1','hep_a_2','hpv_1','hpv_2','hpv_3','ipv_1','ipv_2','ipv_3','flu','jap_enc','meningococcal_1','meningococcal_2','meningococcal_3','meningococcal_4','mmr_1','mmr_2','mmrv_1','mmrv_2','polio_0','polio_1','polio_2','polio_3','penta_1','penta_2','penta_3','pneumococcal_1','pneumococcal_2','pneumococcal_3','pneumococcal_4','rotavirus_1','rotavirus_2','rotavirus_3','typhoid_1','typhoid_2','vitamin_a','yellow_fever'];function a(e,t){var i=null;return e.forEach((function(e){t.indexOf(e.form)>=0&&!e.deleted&&(!i||e.reported_date>i.reported_date)&&(i=e)})),i}function o(e){return Math.max(Utils.getMostRecentTimestamp(e.reports,'P'),Utils.getMostRecentTimestamp(e.reports,'pregnancy'))}function p(e){var t=a(e.reports,r);return t?t.reported_date:0}function l(e,t){return e&&e.trim().toLowerCase()===t}function c(e,t){var i='received_'+t;return l(e.form,t)||l(e.form,'immunization_visit')&&e.fields.vaccines_received&&'yes'===e.fields.vaccines_received[i]||l(e.form,'imm')&&e.fields.vaccines_received&&'yes'===e.fields.vaccines_received[i]}e.exports={MS_IN_DAY:864e5,MAX_DAYS_IN_PREGNANCY:308,DAYS_IN_PNC:42,antenatalForms:n,deliveryForms:r,postnatalForms:['M','postnatal_visit'],immunizationForms:['IMM','C_IMM','immunization_visit','BCG','OPV0','OPV1','OPV2','OPV3','IPV1','IPV2','IPV3','PCV1','PCV2','PCV3','PCV4','PENTA1','PENTA2','PENTA3','ROTA1','ROTA2','ROTA3','VITA','MMR1','MMR2','MMRV1','MMRV2','MN1','MN2','MN3','MN4','FLU','HA1','HA2','JE','YF','TY1','TY2','HPV1','HPV2','HPV3','CH1','CH2','CH3'],immunizationMonths:[1,2,3,6,8,12,16,24],now:i,getMostRecentReport:a,isFormFromArraySubmittedInWindow:function(e,t,i,n){return'string'==typeof t&&(t=[t]),t.some((function(t){return Utils.isFormSubmittedInWindow(e,t,i,n)}))},isCoveredByUseCase:function e(t,i){return!t||('string'==typeof t.use_cases?-1!==t.use_cases.split(' ').indexOf(i):e(t.parent,i))},isHighRiskPregnancy:function(e,t){return'pregnancy'===t.form&&t.fields&&(t.fields.risk_factors||t.fields.danger_signs)||Utils.isFormSubmittedInWindow(e.reports,'F',t.reported_date,Utils.addDate(new Date(t.reported_date),308).getTime())},isHomeBirth:function(e){return e.fields&&e.fields.delivery_code&&'F'!==e.fields.delivery_code.toUpperCase()},getNewestPregnancyTimestamp:o,getNewestDeliveryTimestamp:p,isNewestPregnancy:function(e,t){if('P'===t.form||'pregnancy'===t.form)return t.reported_date===o(e)},isHealthyDelivery:function(e,t){return'D'===t.form||'delivery'===t.form&&'healthy'===t.fields.pregnancy_outcome},isWomanInActivePncPeriod:function(e){var t=new Date(i.getFullYear(),i.getMonth(),i.getDate()-42);return p(e)>t.getTime()},isChildUnder5:function(e){var t=new Date(e.contact.date_of_birth),n=new Date(i-t.getTime());return 12*Math.abs(n.getFullYear()-1970)+n.getMonth()<60},isFormSubmittedInWindow:function(e,t,i,n,r){var s=!1,a=0;return e.forEach((function(e){!s&&t.indexOf(e.form)>=0&&e.reported_date>=i&&e.reported_date<=n&&(a++,(!r||e.fields&&e.fields.follow_up_count>r||a>=r)&&(s=!0))})),s},countReportsSubmittedInWindow:function(e,t,i,n){var r=0;return e.forEach((function(e){t.indexOf(e.form)>=0&&e.reported_date>=i&&e.reported_date<=n&&r++})),r},countANCVisits:function(e,t,i){var r=0;return e.forEach((function(e){n.indexOf(e.form)>=0&&e.reported_date>=t&&e.reported_date<=i&&('pregnancy_visit'===e.form&&'yes'===e.fields.visit_confirmed||'V'===e.form)&&r++})),r},isFormCodeSame:l,isFacilityDelivery:function(e,t){return 1===arguments.length&&(t=e),t.fields&&t.fields.delivery_code&&'F'===t.fields.delivery_code.toUpperCase()},receivedVaccine:c,isBcgReported:function(e){return e.reports.some((function(e){return c(e,'bcg')}))},countDoses:function(e){var t=0;return s.forEach((function(i){var n='received_'+i;'yes'===e.fields.vaccines_received[n]&&t++})),t}}},function(e,t){e.exports=function(e){var t={};!function e(t,i){var n=Object.keys(t);for(var r in n){var s=n[r];switch(typeof t[s]){case'object':e(t[s],i);break;case'function':t[s]=t[s].bind(i)}}}(e,t),t.definition=function e(t){var i=Object.assign({},t),n=Object.keys(i);for(var r in n){var s=n[r];if(Array.isArray(i[s])){i[s]=i[s].slice(0);for(var a=0;a=t.scheduled_tasks.length||t.scheduled_tasks[i].group!==t.scheduled_tasks[i+1].group)},priority:function(e,t){if(_(t))return{level:'high',label:'task.warning.home_birth'}},actions:[{form:'postnatal_visit'}],events:[{id:'postnatal-missing-visit',days:1,start:0,end:3}],resolvedIf:function(e,t,i,n,r){return'cleared'===t.scheduled_tasks[r].state||t.reported_date0},date:'now'},{id:'delivery-with-min-4-visits',type:'percent',icon:'nurse',goal:100,translation_key:'targets.delivery_4_visits.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',idType:'report',appliesIf:c,passesIf:function(e,t){return _(e.reports,t.reported_date-r*n,t.reported_date)>3},date:'now'},{id:'delivery-at-facility-total',type:'percent',icon:'clinic',goal:100,translation_key:'targets.facility_deliveries.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',idType:'report',appliesIf:c,passesIf:g,date:'now'},{id:'pnc-active',type:'count',icon:'mother-child',goal:-1,translation_key:'targets.active_pnc.title',subtitle_translation_key:'targets.all_time.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1)',appliesTo:'contacts',appliesToType:['person'],appliesIf:y},{id:'pnc-registered-this-month',type:'count',icon:'infant',goal:-1,translation_key:'targets.pnc_registrations.title',subtitle_translation_key:'targets.all_time.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1)',appliesTo:'reports',idType:'report',appliesIf:c,date:'reported'},{id:'pnc-visits-this-month',type:'count',icon:'infant',goal:-1,translation_key:'targets.pnc_visits.title',subtitle_translation_key:'targets.this_month.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1)',appliesTo:'reports',idType:'report',appliesIf:function(e,t){return-1!==o.indexOf(t.form)||-1!==h.indexOf(t.form)&&t.fields.delivery_code&&'F'===t.fields.delivery_code.toUpperCase()},date:'reported'},{id:'pnc-homebirth-0-visits',type:'count',icon:'household',goal:-1,translation_key:'targets.homebirth_no_visits.title',subtitle_translation_key:'targets.all_time.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1)',appliesTo:'contacts',appliesToType:['person'],appliesIf:e=>y(e)&&!p(e.reports,o,u(e),m.getTime())},{id:'pnc-homebirth-min-1-visit',type:'percent',icon:'nurse',goal:100,translation_key:'targets.homebirth_1_visit.title',subtitle_translation_key:'targets.all_time.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1)',appliesTo:'reports',appliesIf:function(e,t){return c(e,t)&&t.fields.delivery_code&&'F'!==t.fields.delivery_code.toUpperCase()},passesIf:function(e,t){var i=new Date(t&&(t.birth_date||t.fields.birth_date||t.reported_date)),n=new Date(i.getFullYear(),i.getMonth(),i.getDate()+s);return p(e.reports,o,i.getTime(),n.getTime())},date:'now'},{id:'pnc-3-visits',type:'percent',icon:'nurse',goal:50,translation_key:'targets.birth_3_visits.title',subtitle_translation_key:'targets.all_time.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1)',appliesTo:'reports',appliesIf:c,passesIf:function(e,t){var i=new Date(t&&(t.birth_date||t.fields.birth_date||t.reported_date)),n=new Date(i.getFullYear(),i.getMonth(),i.getDate()+s),r=v(e.reports,o,i.getTime(),n.getTime());return g(t)&&r++,r>2},date:'now'},{id:'imm-children-under-5-years',type:'count',icon:'children',goal:-1,translation_key:'targets.children_u5.title',subtitle_translation_key:'targets.all_time.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'imm\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'imm\\') !== -1)',appliesTo:'contacts',appliesToType:['person'],appliesIf:l},{id:'imm-children-registered-this-month',type:'count',icon:'child',goal:-1,translation_key:'targets.children_registered.title',subtitle_translation_key:'targets.this_month.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'imm\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'imm\\') !== -1)',appliesTo:'contacts',appliesToType:['person'],appliesIf:l,date:function(e){return e.contact.reported_date}},{id:'imm-vaccines-given-this-month',type:'count',icon:'immunization',goal:-1,translation_key:'targets.vaccines_given.title',subtitle_translation_key:'targets.this_month.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'imm\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'imm\\') !== -1)',appliesTo:'reports',appliesIf:function(e,t){return-1!==a.indexOf(t.form)},date:'reported',emitCustom:function(e,t,i,n){if('immunization_visit'===n.form||'imm'===n.form){var r=T(n);for(let i=0;i!!l(e)&&v(e.reports,a,m.getTime()-92*n,m.getTime())>=1},{id:'imm-no-vaccine-reported',type:'count',icon:'risk',goal:-1,translation_key:'targets.children_not_vaccinated.title',subtitle_translation_key:'targets.all_time.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'imm\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'imm\\') !== -1)',appliesTo:'contacts',appliesToType:['person'],appliesIf:e=>{if(!l(e))return!1;for(let t=0;t!!l(e)&&e.reports.some((function(e){return'nutrition_screening'===e.form&&e.fields.measurements.wfa<-2})),date:'reported'},{id:'children-stunted',translation_key:'targets.stunted.title',context:'ctx1',subtitle_translation_key:'targets.this_month.subtitle',type:'count',icon:'child',goal:-1,appliesTo:'contacts',appliesToType:['person'],appliesIf:e=>!!l(e)&&e.reports.some((function(e){return'nutrition_screening'===e.form&&e.fields.measurements.hfa<-2})),date:'reported'},{id:'children-mam',translation_key:'targets.active_mam.title',subtitle_translation_key:'targets.this_month.subtitle',type:'count',icon:'child',goal:-1,appliesTo:'contacts',appliesToType:['person'],appliesIf:e=>!!l(e)&&e.reports.some((function(e){return'nutrition_screening'===e.form&&(e.fields.measurements.wfh>=-3&&e.fields.measurements.wfh<-2||e.fields.measurements.muac>=11.5&&e.fields.measurements.muac<12.4)})),date:'reported'},{id:'children-sam',translation_key:'targets.active_sam.title',subtitle_translation_key:'targets.this_month.subtitle',type:'count',icon:'child',goal:-1,appliesTo:'contacts',appliesToType:['person'],appliesIf:e=>!!l(e)&&e.reports.some((function(e){return'nutrition_screening'===e.form&&(e.fields.measurements.wfh<-3||e.fields.measurements.muac<11.5)})),date:'reported'},{id:'children-otp',translation_key:'targets.active_otp.title',subtitle_translation_key:'targets.this_month.subtitle',type:'count',icon:'child',goal:-1,appliesTo:'contacts',appliesToType:['person'],appliesIf:e=>{if(!l(e))return!1;var t=!1,i=!1,n=!1;return e.reports.forEach((function(e){'nutrition_screening'===e.form?t=e.fields.treatment.program&&'OTP'===e.fields.treatment.program:'off'===e.form?n=e.fields.off&&'defaulter'===e.fields.off.reason:'death_confirmation'===e.form&&(i='yes'===e.fields.death_report.death)})),t&&!n&&!i},date:'reported'},{id:'children-sfp',translation_key:'targets.active_sfp.title',subtitle_translation_key:'targets.this_month.subtitle',type:'count',icon:'child',goal:-1,appliesTo:'contacts',appliesToType:['person'],appliesIf:e=>{if(!l(e))return!1;var t=!1,i=!1,n=!1;return e.reports.forEach((function(e){'nutrition_screening'===e.form?t=e.fields.treatment.program&&'SFP'===e.fields.treatment.program:'off'===e.form?n=e.fields.off&&'defaulter'===e.fields.off.reason:'death_confirmation'===e.form&&(i='yes'===e.fields.death_report.death)})),t&&!n&&!i},date:'reported'}]},function(e,t,i){var n=i(1),r=i(6);function s(e,t,i,n,r,s){var a;if(e.appliesToType){var o;if('contacts'===e.appliesTo){if(!r.contact)return;o='contact'===r.contact.type?r.contact.contact_type:r.contact.type}else{if(!s)return;o=s.form}if(-1===e.appliesToType.indexOf(o))return}if('scheduled_tasks'===e.appliesTo||!e.appliesIf||e.appliesIf(r,s))if('scheduled_tasks'===e.appliesTo){if(s&&e.appliesIf){if(!s.scheduled_tasks)return;for(a=0;a=e.start&&r<=e.end}},fields:[{label:function(){return'contact.profile.delivery_code.'+u(c())},value:function(){var e=c();return d(e).getTime()},filter:'relativeDay',width:12},{label:'contact.profile.pnc_visit',value:'contact.profile.visits.of',translate:!0,context:{count:function(){var e=c(),t=d(e),n=reports.filter((function(e){return('M'===e.form||'postnatal_visit'===e.form)&&e.reported_date>t.getTime()&&e.reported_date=0){if(0===(c=T(h)).length)return;if(e=P(c),t=d(e),f=I(h),(m=P(f))&&m.reported_date=0&&e.reported_date>h.reported_date&&e.reported_date=0&&e.reported_date>t.getTime()&&e.reported_datee.reported_date}))}function _(e){return reports.filter((function(t){return r.indexOf(t.form)>=0&&t.reported_date>e.reported_date}))}function h(){var e;if('person'===contact.type)return reports.forEach((function(t){a(t)&&('D'!==t.form&&'delivery'!==t.form||(!e||t.fields&&e.fields&&t.fields.date_of_birth>e.fields.date_of_birth)&&(e=t))})),e}function g(){var e={enrollment:null,exit:null},t=[];return reports.forEach((function(n){'nutrition_screening'!==n.form||n.deleted||e.enrollment&&!(n.reported_date>e.enrollment.reported_date&&'yes'===n.fields.treatment.enroll)||(e.enrollment=n),'nutrition_exit'===n.form&&t.push(n)})),e.enrollment&&(e.exit=t.find((function(t){return t.reported_date>e.enrollment.reported_date}))),e}e.exports={now:n,pregnancyForms:r,antenatalForms:['V','pregnancy_visit'],deliveryForms:i,postnatalForms:['M','postnatal_visit'],immunizationForms:['IMM','C_IMM','immunization_visit','PENTA1','PENTA2','PENTA3','BCG','OPV0','OPV1','OPV2','OPV3','IPV1','IPV2','IPV3','FIPV1','FIPV2','PCV1','PCV2','PCV3','PCV4','ROTA1','ROTA2','ROTA3','VITA','MMR1','MMR2','MMRV1','MMRV2','MN1','MN2','MN3','MN4','FLU','HA1','HA2','HB','JE','YF','TY1','TY2','HPV1','HPV2','HPV3','CH1','CH2','CH3','DPT4','DPT5'],MS_IN_DAY:864e5,MAX_DAYS_IN_PREGNANCY:308,DAYS_IN_PNC:45,IMMUNIZATION_DOSES:o,IMMUNIZATION_LIST:['bcg','cholera','hep_a','hep_b','hpv','ipv','fipv','flu','jap_enc','meningococcal','mmr','mmrv','polio','penta','pneumococcal','rotavirus','typhoid','vitamin_a','yellow_fever','dpt'],count:l,isVaccineInLineage:function(e,t){return e&&e.some((function(e){return e&&s(e.vaccines,t)}))},isCoveredByUseCaseInLineage:function(e,t){return e&&e.some((function(e){return e&&s(e.use_cases,t)}))},contains:s,isCoveredByUseCase:c,isFacilityDelivery:function(e){return e&&e.fields&&e.fields.delivery_code&&'f'===e.fields.delivery_code.toLowerCase()},isNonFacilityDelivery:f,getBirthDate:u,isReportValid:a,getPNCperiod:p,isHighRiskPregnancy:function(e){return'pregnancy'===e.form&&e.fields&&(e.fields.risk_factors||e.fields.danger_signs)||reports.some((function(t){return a(t)&&t.reported_date>e.reported_date&&t.reported_date<=d(e.reported_date,308).getTime()&&('F'===t.form.toUpperCase()||'pregnancy_visit'===t.form&&t.fields&&t.fields.danger_signs)}))},isHighRiskPostnatal:function(e){return e&&e.start&&e.end&&reports.some((function(t){return a(t)&&(f(t)||'F'===t.form.toUpperCase()&&t.reported_date>=e.end.getTime()&&t.reported_date<=e.end.getTime()||'postnatal_visit'===t.form&&t.fields&&(t.fields.danger_signs_mom||t.fields.danger_signs_baby))}))},getDeliveryCode:function(e){return(e&&e.fields&&(e.fields.group_note&&e.fields.group_note.default_chw_sms||e.fields.delivery_code)||'unknown').toLowerCase().replace('anc_only_','')},initImmunizations:function(){var e={};return o.forEach((function(t){e[t[0]]=!1})),e},addImmunizations:function(e,t){o.forEach((function(n){e[n[0]]||(e[n[0]]='string'==typeof t?t.toUpperCase()===n[1]:'yes'===t['received_'+n[0]])}))},countDosesReceived:function(e,t){return l(o,(function(n){return e[n[0]]&&0===n[0].indexOf(t+'_')}))},countDosesPossible:function(e){return l(o,(function(t){return 0===t[0].indexOf(e+'_')}))},isSingleDose:function(e){return o.some((function(t){return t[0]===e}))},countReportsSubmittedInWindow:function(e,t){return l(reports,(function(n){return n.reported_date<=t&&-1!==e.indexOf(n.form)}))},addDate:d,getOldestReport:function(e){var t;return e.forEach((function(e){a(e)&&(!t||e.reported_datee.reported_date}))},getTreatmentEnrollmentDate:function(){var e='',t=g().enrollment;if(t){var n=new Date(0);n.setUTCSeconds(t.reported_date/1e3),e=n.toISOString().slice(0,10)}return e},getTreatmentProgram:function(){var e='',t=g().enrollment,n=g().exit;return t&&!n&&(e=t.fields.treatment.program),e},countNutritionFollowups:function(){var e=0,t=g().enrollment;return reports.forEach((function(n){'nutrition_followup'===n.form&&n.reported_date>t.reported_date&&(e+=1)})),e},getMostRecentReport:function(e,t){var n=null;return e.forEach((function(e){t.indexOf(e.form)>=0&&!e.deleted&&(!n||e.reported_date>n.reported_date)&&(n=e)})),n},getMostRecentNutritionEnrollment:g}},function(e,t){function n(e){return e?Array.isArray(e)?e:[e]:[]}function r(e,t){switch(typeof e){case'undefined':return!0;case'function':return e(t);default:return e}}function i(e,t,n){if(r(e.appliesIf,n)){var i='function'==typeof e.fields?e.fields(n):e.fields.filter((function(e){return r(e.appliesIf,n)})).map((function(e){var t={};return o(e,t,'label'),o(e,t,'value'),o(e,t,'translate'),o(e,t,'filter'),o(e,t,'width'),o(e,t,'icon'),e.context&&(t.context={},o(e.context,t.context,'count'),o(e.context,t.context,'total')),t}));return e.modifyContext&&e.modifyContext(t,n),{label:e.label,fields:i}}function o(e,t,r){switch(typeof e[r]){case'undefined':return;case'function':t[r]=e[r](n);break;default:t[r]=e[r]}}}e.exports=function(e,t,r){var o=e.fields||[],a=e.context||{},l=e.cards||[],s=t&&('contact'===t.type?t.contact_type:t.type),c={cards:[],fields:o.filter((function(e){var t=n(e.appliesToType),r=t.filter((function(e){return e&&'!'===e.charAt(0)}));if((0===t.length||t.includes(s)||r.length>0&&!r.includes('!'+s))&&(!e.appliesIf||e.appliesIf()))return delete e.appliesToType,delete e.appliesIf,!0}))};return l.forEach((function(e){var t,o,l,f,u=n(e.appliesToType);if(u.includes('report')&&u.length>1)throw new Error('You cannot set appliesToType to an array which includes the type \\'report\\' and another type.');if(u.includes('report'))for(t=0;t0)return;(l=i(e,a))&&c.cards.push(l)}})),c.context=a,c}}])})); return ContactSummary;", + "multi_report_alerts": [], + "sms": { + "outgoing_service": "medic-gateway" + }, + "contact_types": [ + { + "id": "district_hospital", + "name_key": "contact.type.district_hospital", + "group_key": "contact.type.district_hospital.plural", + "create_key": "contact.type.district_hospital.new", + "edit_key": "contact.type.place.edit", + "icon": "medic-district-hospital", + "create_form": "form:contact:district_hospital:create", + "edit_form": "form:contact:district_hospital:edit" + }, + { + "id": "health_center", + "name_key": "contact.type.health_center", + "group_key": "contact.type.health_center.plural", + "create_key": "contact.type.health_center.new", + "edit_key": "contact.type.place.edit", + "parents": [ + "district_hospital" + ], + "icon": "medic-health-center", + "create_form": "form:contact:health_center:create", + "edit_form": "form:contact:health_center:edit" + }, + { + "id": "clinic", + "name_key": "contact.type.clinic", + "group_key": "contact.type.clinic.plural", + "create_key": "contact.type.clinic.new", + "edit_key": "contact.type.place.edit", + "parents": [ + "health_center" + ], + "icon": "medic-clinic", + "create_form": "form:contact:clinic:create", + "edit_form": "form:contact:clinic:edit", + "count_visits": true + }, + { + "id": "person", + "name_key": "contact.type.person", + "group_key": "contact.type.person.plural", + "create_key": "contact.type.person.new", + "edit_key": "contact.type.person.edit", + "primary_contact_key": "clinic.field.contact", + "parents": [ + "district_hospital", + "health_center", + "clinic" + ], + "icon": "medic-person", + "create_form": "form:contact:person:create", + "edit_form": "form:contact:person:edit", + "person": true + } + ] } diff --git a/shared-libs/transitions/test/unit/transitions/registration.js b/shared-libs/transitions/test/unit/transitions/registration.js index 8409842c2fa..5fa337b8aab 100644 --- a/shared-libs/transitions/test/unit/transitions/registration.js +++ b/shared-libs/transitions/test/unit/transitions/registration.js @@ -1,2939 +1,722 @@ -require('chai').should(); -const sinon = require('sinon'); -const rewire = require('rewire'); -const db = require('../../../src/db'); -const messages = require('../../../src/lib/messages'); -const utils = require('../../../src/lib/utils'); -const config = require('../../../src/config'); -const validation = require('@medic/validation'); -const contactTypeUtils = require('@medic/contact-types-utils'); - -let schedules; -let transitionUtils; -let acceptPatientReports; -let transition; -let settings; - -describe('registration', () => { - beforeEach(() => { - config.init({ - getAll: sinon - .stub() - .returns({}), - get: sinon.stub(), - getTranslations: sinon - .stub() - .returns({}) - }); - - schedules = require('../../../src/lib/schedules'); - transitionUtils = require('../../../src/transitions/utils'); - acceptPatientReports = require('../../../src/transitions/accept_patient_reports'); - transition = rewire('../../../src/transitions/registration'); - settings = { - contact_types: [ - { id: 'place' }, - { id: 'person', person: true, parents: ['place'] }, - ] +const _ = require('lodash'); +const utils = require('../lib/utils'); +const transitionUtils = require('./utils'); +const logger = require('../lib/logger'); +const db = require('../db'); +const lineage = require('@medic/lineage')(Promise, db.medic); +const messages = require('../lib/messages'); +const schedules = require('../lib/schedules'); +const acceptPatientReports = require('./accept_patient_reports'); +const moment = require('moment'); +const config = require('../config'); +const date = require('../date'); + +const contactTypesUtils = require('@medic/contact-types-utils'); + +const NAME = 'registration'; +const PARENT_NOT_FOUND = 'parent_not_found'; +const PARENT_FIELD_NOT_PROVIDED = 'parent_field_not_provided'; +const PARENT_INVALID = 'parent_invalid'; + +const findFirstDefinedValue = (doc, fields) => { + const definedField = fields.find(field => doc.fields[field] !== undefined && doc.fields[field] !== null); + return definedField && doc.fields[definedField]; +}; + +const getPatientNameField = (params) => getNameField(params, 'patient'); +const getPlaceNameField = (params) => getNameField(params, 'place'); + +const getNameField = (params, prefix) => { + if (Array.isArray(params) && params.length && params[0]) { + return params[0]; + } + + const nameField = `${prefix}_name_field`; + if (params && params[nameField]) { + return params[nameField]; + } + + const defaultNameField = `${prefix}_name`; + return defaultNameField; +}; + +const parseParams = params => { + if (!params) { + return {}; + } + if (params instanceof Object) { + // We support raw JSON even if we can't specify that + // correctly in app_settings + return params; + } + const firstCharacter = params.trim()[0]; + if (firstCharacter === '[' || firstCharacter === '{') { + // We support JSON in a string, eg: '{"foo": "bar"}' + return JSON.parse(params); + } + // And comma delimted strings, eg: "foo,bar", "foo" + return params.split(','); +}; + +const booleanExpressionFails = (doc, expr) => { + let result = false; + + if (utils.isNonEmptyString(expr)) { + try { + result = !utils.evalExpression(expr, { doc: doc }); + } catch (err) { + // TODO should this count as a fail or as a real error + logger.warn('Failed to eval boolean expression:'); + logger.info(err.toString()); + result = true; + } + } + + return result; +}; + +const getDOB = doc => { + if (!doc || !doc.fields) { + return ''; + } + const reportedDate = moment(doc.reported_date).startOf('day'); + const years = parseInt(getYearsSinceDOB(doc), 10); + if (!isNaN(years)) { + return reportedDate.subtract(years, 'years'); + } + const months = parseInt(getMonthsSinceDOB(doc), 10); + if (!isNaN(months)) { + return reportedDate.subtract(months, 'months'); + } + const weeks = parseInt(getWeeksSinceDOB(doc), 10); + if (!isNaN(weeks)) { + return reportedDate.subtract(weeks, 'weeks'); + } + const days = parseInt(getDaysSinceDOB(doc), 10); + if (!isNaN(days)) { + return reportedDate.subtract(days, 'days'); + } + // no given date of birth - return reportedDate as it's the best we can do + return reportedDate; +}; + +const getYearsSinceDOB = doc => { + const fields = ['years_since_dob', 'years_since_birth', 'age_in_years']; + return findFirstDefinedValue(doc, fields); +}; + +const getMonthsSinceDOB = doc => { + const fields = ['months_since_dob', 'months_since_birth', 'age_in_months']; + return findFirstDefinedValue(doc, fields); +}; + +const getWeeksSinceDOB = doc => { + const fields = [ + 'weeks_since_dob', + 'dob', + 'weeks_since_birth', + 'age_in_weeks', + ]; + return findFirstDefinedValue(doc, fields); +}; + +const getDaysSinceDOB = doc => { + const fields = ['days_since_dob', 'days_since_birth', 'age_in_days']; + return findFirstDefinedValue(doc, fields); +}; + +/* + * Given a doc get the LMP value as a number, including 0. Supports three + * property names atm. + */ +const getWeeksSinceLMP = doc => { + const props = ['weeks_since_lmp', 'last_menstrual_period', 'lmp']; + for (const prop of props) { + const lmp = Number(doc.fields && doc.fields[prop]); + if (!isNaN(lmp)) { + return lmp; + } + } +}; + +/* +* Given a doc, try to get the exact LMP date +*/ +const getLMPDate = doc => { + const props = ['lmp_date', 'date_lmp']; + for (const prop of props) { + const lmp = doc.fields && doc.fields[prop] && parseInt(doc.fields[prop]); + if (!isNaN(lmp)) {//milliseconds since epoch + return lmp; + } + } +}; + +const setExpectedBirthDate = doc => { + let start; + const lmpDate = getLMPDate(doc); + if (lmpDate) { + start = moment(lmpDate); + } else { + const lmp = getWeeksSinceLMP(doc); + if (lmp) { + start = moment(doc.reported_date).startOf('day'); + start.subtract(lmp, 'weeks'); + } + } + + if (!start) {// means baby was already born, chw just wants a registration. + doc.lmp_date = null; + doc.expected_date = null; + return; + } + + doc.lmp_date = start.toISOString(); + doc.expected_date = start + .clone() + .add(40, 'weeks') + .toISOString(); +}; + +const setBirthDate = doc => { + const dob = getDOB(doc); + if (dob) { + doc.birth_date = dob.toISOString(); + } +}; + +const getPhoneNumber = doc => { + if (!doc || !doc.fields) { + return ''; + } + return doc.fields.phone_number; +}; + +const setPhoneNumber = doc => { + const phoneNumber = getPhoneNumber(doc); + // get default country code + //countryCode = codeFromSettings() + doc.phone_number = countryCode.concat(phoneNumber.toString()); +}; + +const getConfig = () => config.get('registrations'); + +/* + * Given a form code and config array, return config for that form. + */ +const getRegistrationConfig = (config, formCode) => { + return config.find(conf => utils.isFormCodeSame(formCode, conf.form)); +}; + +const triggers = { + add_patient: (options) => { + // if we already have a patient id then return + if (options.doc.patient_id) { + return; + } + + return setPatientId(options).then(() => addPatient(options)); + }, + add_place: (options) => { + // if we already have a place id then return + if (options.doc.place_id) { + return; + } + + return setPlaceId(options).then(() => addPlace(options)); + }, + add_case: (options) => { + return setCaseId(options).then(() => addPlaceId(options)); + }, + add_patient_id: (options) => { + // Deprecated name for add_patient + logger.warn('Use of add_patient_id trigger. This is deprecated in favour of add_patient.'); + return triggers.add_patient(options); + }, + add_phone_number: (options) => { + return setPhoneNumber(options.doc); + }, + add_expected_date: (options) => { + return setExpectedBirthDate(options.doc); + }, + add_birth_date: (options) => { + return setBirthDate(options.doc); + }, + assign_schedule: (options) => { + return assignSchedule(options); + }, + clear_schedule: (options) => { + // Registration forms that clear schedules do so fully + // silence_type will be split again later, so join them back + const config = { + silence_type: options.params.join(','), + silence_for: null, }; - }); - - afterEach(done => { - sinon.reset(); - sinon.restore(); - done(); - }); - - describe('booleanExpressionFails', () => { - beforeEach(() => { - transition.booleanExpressionFails = transition.__get__('booleanExpressionFails'); - }); - - it('is false if there is no valid expression', () => { - transition.booleanExpressionFails({}, '').should.equal(false); - }); - - it('is true if the expresison fails', () => { - transition.booleanExpressionFails({ foo: 'bar' }, `doc.foo !== 'bar'`).should.equal(true); - }); - - it('is true if the expression is invalid', () => { - // TODO: should this error instead of just returning false? - // If there is a typo we're not going to know about it - transition.booleanExpressionFails({}, `doc.foo.bar === 'smang'`).should.equal(true); - }); - }); - - describe('addPatient', () => { - it('trigger creates a new patient', () => { - const patientName = 'jack'; - const submitterId = 'abc'; - const parentId = 'papa'; - const patientId = '05649'; - const reportId = 'def'; - const senderPhoneNumber = '+555123'; - const dob = '2017-03-31T01:15:09.000Z'; - const change = { - doc: { - _id: reportId, - type: 'data_record', - form: 'R', - reported_date: 53, - from: senderPhoneNumber, - fields: { patient_name: patientName }, - birth_date: dob, - }, - }; - const getContactUuid = sinon.stub(utils, 'getContactUuid').resolves(); - // return expected view results when searching for contacts_by_phone - const view = sinon.stub(db.medic, 'query').resolves({ - rows: [ - { - doc: { - _id: submitterId, - parent: { _id: parentId }, - }, - }, - ], - }); - sinon.stub(db.medic, 'get').withArgs('papa').resolves({ _id: parentId, type: 'contact', contact_type: 'place' }); - const saveDoc = sinon.stub(db.medic, 'post').resolves(); - const eventConfig = { - form: 'R', - events: [{ name: 'on_create', trigger: 'add_patient' }], - }; - config.get.returns([eventConfig]); - sinon.stub(validation, 'validate').resolves(null); - sinon.stub(utils, 'getRegistrations').resolves([]); - sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); - config.getAll.returns(settings); - - return transition.onMatch(change).then(() => { - getContactUuid.callCount.should.equal(1); - view.callCount.should.equal(1); - view.args[0][0].should.equal('medic-client/contacts_by_phone'); - view.args[0][1].key.should.equal(senderPhoneNumber); - view.args[0][1].include_docs.should.equal(true); - saveDoc.callCount.should.equal(1); - saveDoc.args[0][0].name.should.equal(patientName); - saveDoc.args[0][0].parent._id.should.equal(parentId); - saveDoc.args[0][0].reported_date.should.equal(53); - saveDoc.args[0][0].type.should.equal('person'); - saveDoc.args[0][0].patient_id.should.equal(patientId); - saveDoc.args[0][0].date_of_birth.should.equal(dob); - saveDoc.args[0][0].source_id.should.equal(reportId); - saveDoc.args[0][0].created_by.should.equal(submitterId); - }); - }); - - it('does nothing when patient already added', () => { - const patientId = '05649'; - const change = { - doc: { - type: 'data_record', - form: 'R', - patient_id: patientId, - reported_date: 53, - from: '+555123', - fields: { patient_name: 'jack' }, - }, - }; - sinon - .stub(db.medic, 'query') - .resolves({ - rows: [{ doc: { parent: { _id: 'papa' } } }], - }); - const saveDoc = sinon.stub(db.medic, 'post').resolves(); - const eventConfig = { - form: 'R', - events: [{ name: 'on_create', trigger: 'add_patient_id' }], - }; - config.get.returns([eventConfig]); - config.getAll.returns(settings); - sinon.stub(validation, 'validate').resolves(null); - return transition.onMatch(change).then(() => { - saveDoc.callCount.should.equal(0); - }); - }); - - it('uses a given id if configured to', () => { - const patientId = '05648'; - const doc = { - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+555123', - fields: { patient_name: 'jack', external_id: patientId }, - birth_date: '2017-03-31T01:15:09.000Z', - }; - const change = { doc: doc }; - sinon.stub(utils, 'getContactUuid').resolves(); - // return expected view results when searching for contacts_by_phone - sinon - .stub(db.medic, 'query') - .resolves({ - rows: [{ doc: { parent: { _id: 'papa' } } }], - }); - sinon.stub(db.medic, 'get').withArgs('papa').resolves({ _id: 'papa', type: 'place' }); - const saveDoc = sinon.stub(db.medic, 'post').resolves(1); - const eventConfig = { - form: 'R', - events: [ - { - name: 'on_create', - trigger: 'add_patient_id', - params: '{"patient_id_field": "external_id"}', - }, - ], - }; - config.get.returns([eventConfig]); - sinon.stub(validation, 'validate').resolves(); - sinon.stub(transitionUtils, 'isIdUnique').resolves(true); - config.getAll.returns(settings); - - return transition.onMatch(change).then(() => { - saveDoc.args[0][0].patient_id.should.equal(patientId); - doc.patient_id.should.equal(patientId); - (typeof doc.errors).should.equal('undefined'); - }); - }); - - it('trigger creates a new contact with the given type', () => { - const change = { - doc: { - _id: 'def', - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+555123', - fields: { patient_name: 'jack' }, - birth_date: '2017-03-31T01:15:09.000Z', - }, - }; - sinon.stub(utils, 'getContactUuid').resolves(); - // return expected view results when searching for contacts_by_phone - sinon.stub(db.medic, 'query').resolves({ - rows: [ - { - doc: { - _id: 'abc', - parent: { _id: 'papa' }, - }, - }, - ], - }); - sinon.stub(db.medic, 'get').withArgs('papa').resolves({ _id: 'papa', type: 'place' }); - const saveDoc = sinon.stub(db.medic, 'post').resolves(); - const eventConfig = { - form: 'R', - events: [{ - name: 'on_create', - trigger: 'add_patient', - params: '{ "contact_type": "patient" }' - }], - }; - config.get.returns([eventConfig]); - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations').resolves([]); - sinon.stub(transitionUtils, 'getUniqueId').resolves('05649'); - config.getAll.returns({ - contact_types: [ - { id: 'place' }, - { id: 'patient', person: true, parents: ['place'] }, - ] - }); - - return transition.onMatch(change).then(() => { - saveDoc.callCount.should.equal(1); - saveDoc.args[0][0].type.should.equal('contact'); - saveDoc.args[0][0].contact_type.should.equal('patient'); - }); - }); - - it('errors if the configuration does not point to an id', () => { - const patientId = '05648'; - const doc = { - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+555123', - fields: { patient_name: 'jack', external_id: patientId }, - birth_date: '2017-03-31T01:15:09.000Z', - }; - const change = { doc: doc }; - sinon.stub(utils, 'getContactUuid').resolves(); - // return expected view results when searching for contacts_by_phone - sinon - .stub(db.medic, 'query') - .resolves({ - rows: [{ doc: { parent: { _id: 'papa' } } }], - }); - sinon.stub(db.medic, 'get').withArgs('papa').resolves({ _id: 'papa', type: 'place' }); - sinon.stub(db.medic, 'post').resolves(); - const eventConfig = { - form: 'R', - events: [ - { - name: 'on_create', - trigger: 'add_patient', - params: '{"patient_id_field": "not_the_external_id"}', - }, - ], - }; - const configGet = config.get; - configGet.withArgs('outgoing_deny_list').returns(''); - configGet.returns([eventConfig]); - config.getAll.returns(settings); - - sinon.stub(validation, 'validate').resolves(); - - return transition.onMatch(change).then(() => { - (typeof doc.patient_id).should.equal('undefined'); - doc.errors.should.deep.equal([ - { - message: 'messages.generic.no_provided_patient_id', - code: 'no_provided_patient_id', - }, - ]); - }); - }); - - it('errors if the given id is not unique', () => { - const patientId = '05648'; - const doc = { - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+555123', - fields: { patient_name: 'jack', external_id: patientId }, - birth_date: '2017-03-31T01:15:09.000Z', - }; - const change = { doc: doc }; - sinon.stub(utils, 'getContactUuid').resolves(); - // return expected view results when searching for contacts_by_phone - sinon - .stub(db.medic, 'query') - .resolves({ - rows: [{ doc: { parent: { _id: 'papa' } } }], - }); - sinon.stub(db.medic, 'get').withArgs('papa').resolves({ _id: 'papa', type: 'place' }); - sinon.stub(db.medic, 'post').resolves(); - const eventConfig = { - form: 'R', - events: [ - { - name: 'on_create', - trigger: 'add_patient', - params: '{"patient_id_field": "external_id"}', - }, - ], - }; - const configGet = config.get; - configGet.withArgs('outgoing_deny_list').returns(''); - configGet.returns([eventConfig]); - - sinon.stub(transitionUtils, 'isIdUnique').resolves(false); - - sinon.stub(validation, 'validate').resolves(); - config.getAll.returns(settings); - - return transition.onMatch(change).then(() => { - (typeof doc.patient_id).should.be.equal('undefined'); - doc.errors.should.deep.equal([ - { - message: 'messages.generic.provided_patient_id_not_unique', - code: 'provided_patient_id_not_unique', - }, - ]); - }); - }); - - it('event parameter overwrites the default property for the name of the patient', () => { - const patientName = 'jack'; - const submitterId = 'papa'; - const patientId = '05649'; - const senderPhoneNumber = '+555123'; - const dob = '2017-03-31T01:15:09.000Z'; - const change = { - doc: { - type: 'data_record', - form: 'R', - reported_date: 53, - from: senderPhoneNumber, - fields: { name: patientName }, - birth_date: dob, - }, - }; - sinon.stub(utils, 'getContactUuid').resolves(); - // return expected view results when searching for contacts_by_phone - sinon - .stub(db.medic, 'query') - .resolves({ - rows: [{ doc: { parent: { _id: submitterId } } }], - }); - sinon.stub(db.medic, 'get').withArgs('papa').resolves({ _id: 'papa', type: 'place' }); - const saveDoc = sinon.stub(db.medic, 'post').resolves(); - const eventConfig = { - form: 'R', - events: [{ name: 'on_create', trigger: 'add_patient', params: 'name' }], - }; - config.get.returns([eventConfig]); - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations').resolves([]); - - sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); - config.getAll.returns(settings); - return transition.onMatch(change).then(() => { - saveDoc.callCount.should.equal(1); - saveDoc.args[0][0].name.should.equal(patientName); - }); - }); - - it('event parameter overwrites the default property for the name of the patient using JSON config', () => { - const patientName = 'jack'; - const submitterId = 'papa'; - const patientId = '05649'; - const senderPhoneNumber = '+555123'; - const dob = '2017-03-31T01:15:09.000Z'; - const change = { - doc: { - type: 'data_record', - form: 'R', - reported_date: 53, - from: senderPhoneNumber, - fields: { name: patientName }, - birth_date: dob, - }, - }; - sinon.stub(utils, 'getContactUuid').resolves(); - // return expected view results when searching for contacts_by_phone - sinon - .stub(db.medic, 'query') - .resolves({ - rows: [{ doc: { parent: { _id: submitterId } } }], - }); - sinon.stub(db.medic, 'get').withArgs('papa').resolves({ _id: 'papa', type: 'place' }); - const saveDoc = sinon.stub(db.medic, 'post').resolves(); - const eventConfig = { - form: 'R', - events: [ - { - name: 'on_create', - trigger: 'add_patient', - params: '{"patient_name_field": "name"}', - }, - ], - }; - config.get.returns([eventConfig]); - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations').resolves([]); - - sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); - config.getAll.returns(settings); - - return transition.onMatch(change).then(() => { - saveDoc.callCount.should.equal(1); - saveDoc.args[0][0].name.should.equal(patientName); - }); - }); - - it('add_patient and add_patient_id triggers are idempotent', () => { - const patientName = 'jack'; - const submitterId = 'papa'; - const patientId = '05649'; - const senderPhoneNumber = '+555123'; - const dob = '2017-03-31T01:15:09.000Z'; - const change = { - doc: { - type: 'data_record', - form: 'R', - reported_date: 53, - from: senderPhoneNumber, - fields: { name: patientName }, - birth_date: dob, + const subjectIds = [ + ...utils.getSubjectIds(options.doc.patient), + ...utils.getSubjectIds(options.doc.place), + ]; + const caseId = options.doc.case_id || + (options.doc.fields && options.doc.fields.case_id); + if (caseId) { + subjectIds.push(caseId); + } + + return utils + .getReportsBySubject({ ids: subjectIds, registrations: true }) + .then(registrations => acceptPatientReports.silenceRegistrations(config, options.doc, registrations)); + }, +}; + +const fireConfiguredTriggers = (registrationConfig, doc) => { + const promises = registrationConfig.events + .map(event => { + const trigger = triggers[event.trigger]; + if (!trigger || event.name !== 'on_create') { + return; + } + + const obj = _.defaults({}, doc, doc.fields); + + if (booleanExpressionFails(obj, event.bool_expr)) { + return; + } + + const options = { + doc, + registrationConfig, + params: parseParams(event.params), + }; + logger.debug( + `Parsed params for form "${registrationConfig.form}", trigger "${event.trigger}, params: ${options.params}"` + ); + return () => trigger(options); + }) + .filter(item => !!item); + + return promises + .reduce((promise, trigger) => promise.then(trigger), Promise.resolve()) + .then(() => addMessages(registrationConfig, doc)) + .then(() => true); +}; + +// NB: this is very similar to a function in accept_patient_reports, except +// we also allow for an empty event_type +const messageRelevant = (msg, doc) => { + if (!msg.event_type || msg.event_type === 'report_accepted') { + const expr = msg.bool_expr; + if (utils.isNonEmptyString(expr)) { + return utils.evalExpression(expr, { doc: doc }); + } else { + return true; + } + } +}; + +const addMessages = (config, doc) => { + const patientId = doc.fields && doc.fields.patient_id; + const placeId = doc.fields && doc.fields.place_id; + if (!config.messages || !config.messages.length) { + return; + } + + return Promise + .all([ + utils.getRegistrations({ id: patientId }), + utils.getRegistrations({ id: placeId }), + ]) + .then(([patientRegistrations, placeRegistrations]) => { + const context = { + patient: doc.patient, + place: doc.place, + registrations: patientRegistrations, + placeRegistrations: placeRegistrations, + templateContext: { + next_msg: schedules.getNextTimes(doc, moment(date.getDate())), }, }; - sinon.stub(utils, 'getContactUuid').resolves(); - // return expected view results when searching for contacts_by_phone - sinon - .stub(db.medic, 'query') - .resolves({ - rows: [{ doc: { parent: { _id: submitterId } } }], - }); - sinon.stub(db.medic, 'get').withArgs('papa').resolves({ _id: 'papa', type: 'place' }); - const saveDoc = sinon.stub(db.medic, 'post').resolves(); - const eventConfig = { - form: 'R', - events: [ - { name: 'on_create', trigger: 'add_patient', params: 'name' }, - { name: 'on_create', trigger: 'add_patient_id', params: 'name' }, - ], - }; - config.get.returns([eventConfig]); - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations').resolves([]); - - sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); - config.getAll.returns(settings); - - - return transition.onMatch(change).then(() => { - saveDoc.callCount.should.equal(1); - saveDoc.args[0][0].name.should.equal(patientName); - }); - }); - - it('fails when patient_id_field is set to patient_id', () => { - const eventConfig = [ - { - form: 'R', - events: [ - { - name: 'on_create', - trigger: 'add_patient', - params: '{ "patient_id_field": "patient_id" }', - }, - ], - }, - ]; - - config.get.returns(eventConfig); - transition.init.should.throw( - Error, - 'Configuration error in R.add_patient: patient_id_field cannot be set to patient_id' - ); - }); - it('should add configured type of person', () => { - const change = { - doc: { - _id: 'reportID', - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+111222', - fields: { patient_name: 'Bob' }, - contact: { - _id: 'pete', - name: 'Pete', - contact_type: 'chw', - type: 'contact', - parent: { - _id: 'petes', - name: 'Petes Place', - contact_type: 'clinic', - type: 'contact', - parent: { _id: 'west_hc', name: 'west hc', contact_type: 'health_center', type: 'contact' } - } - }, + config.messages.forEach(msg => { + if (messageRelevant(msg, doc)) { + messages.addMessage(doc, msg, msg.recipient, context); } - }; - const patientId = 'my_patient_id'; - sinon.stub(utils, 'getContactUuid').resolves(); - sinon.stub(db.medic, 'post').resolves(); - const eventConfig = { - form: 'R', - events: [{ name: 'on_create', trigger: 'add_patient', params: { contact_type: 'patient' } }], - messages: [ { - recipient: 'reporting_unit', - event_type: 'report_accepted', - bool_expr: 'doc.patient', - message: [ - { - locale: 'en', - content: 'Patient {{patient_name}} with type {{patient.contact_type}} was added to ' + - '{{patient.parent.name}}({{patient.parent.contact_type}})' - } - ] - }] - }; - const contactTypes = [ - { id: 'health_center' }, - { id: 'clinic', parents: ['health_center'] }, - { id: 'patient', parents: ['clinic'], person: true }, - ]; - config.get.returns([eventConfig]); - config.getAll.returns({ contact_types: contactTypes }); - - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations').resolves([]); - sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); - - return transition.onMatch(change).then(() => { - transitionUtils.getUniqueId.callCount.should.equal(1); - utils.getContactUuid.callCount.should.equal(1); - utils.getContactUuid.args[0].should.deep.equal([patientId]); - db.medic.post.callCount.should.equal(1); - db.medic.post.args[0].should.deep.equal([{ - name: 'Bob', - patient_id: patientId, - source_id: change.doc._id, - type: 'contact', - contact_type: 'patient', - parent: { _id: 'petes', parent: { _id: 'west_hc' } }, - created_by: 'pete', - reported_date: change.doc.reported_date, - }]); - (!!change.doc.errors).should.equal(false); - change.doc.tasks.length.should.equal(1); - change.doc.tasks[0].messages[0].should.include({ - to: change.doc.from, - message: 'Patient Bob with type patient was added to Petes Place(clinic)' - }); }); }); - - it('should add person with selected parent', () => { - const change = { - doc: { - _id: 'reportID', - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+111222', - fields: { buddy_name: 'Marcel', parent: 'georges_place' }, - contact: { - _id: 'pete', - name: 'Pete', - contact_type: 'chw', - type: 'contact', - parent: { - _id: 'petes', - name: 'Petes Place', - place_id: 'petes_place', - type: 'contact', - contact_type: 'area_type_1', - parent: { - _id: 'west_hc', name: 'west hc', contact_type: 'health_center', - type: 'contact', place_id: 'the_west_hc' - } - }, - }, +}; + +const assignSchedule = (options) => { + const patientId = options.doc.fields && options.doc.fields.patient_id; + const placeId = options.doc.fields && options.doc.fields.place_id; + + return Promise + .all([ + utils.getRegistrations({ id: patientId }), + utils.getRegistrations({ id: placeId }), + ]) + .then(([patientRegistrations, placeRegistrations]) => { + options.params.forEach(scheduleName => { + const schedule = schedules.getScheduleConfig(scheduleName); + const context = { + patientRegistrations, + patient: options.doc.patient, + placeRegistrations, + place: options.doc.place + }; + schedules.assignSchedule(options.doc, schedule, context); + }); + }); +}; + +const generateId = (doc, key) => { + return transitionUtils.getUniqueId().then(id => doc[key] = id); +}; + +const setPlaceId = ({ doc }) => generateId(doc, 'place_id'); +const setCaseId = ({ doc }) => { + if (doc.case_id) { + return Promise.resolve(); + } + return generateId(doc, 'case_id'); +}; + +const addPlaceId = ({ doc }) => { + const placeId = doc.contact && doc.contact.parent && doc.contact.parent._id; + if (!placeId) { + return; + } + if (!doc.fields) { + doc.fields = {}; + } + doc.fields.place_uuid = placeId; +}; + +const setPatientId = (options) => { + const doc = options.doc; + const patientIdField = options.params.patient_id_field; + + if (patientIdField) { + const providedId = doc.fields[options.params.patient_id_field]; + + if (!providedId) { + transitionUtils.addRejectionMessage(doc, options.registrationConfig, 'no_provided_patient_id'); + return Promise.resolve(); + } + + return transitionUtils + .isIdUnique(providedId) + .then(isUnique => { + if (isUnique) { + doc.patient_id = providedId; + return; } - }; - const parent = { - _id: 'georges', - name: 'Georges Place', - type: 'contact', - place_id: 'georges_place', - contact_type: 'area_type_2', - parent: { _id: 'west_hc' }, - }; - const patientId = 'my_patient_id'; - sinon.stub(utils, 'getContactUuid').resolves(); - sinon.stub(utils, 'getContact').resolves(parent); - sinon.stub(db.medic, 'post').resolves(); - - const eventConfig = { - form: 'R', - events: [{ - name: 'on_create', trigger: 'add_patient', - params: { contact_type: 'buddy', patient_name_field: 'buddy_name', parent_id: 'parent' } - }], - messages: [ { - recipient: 'reporting_unit', - event_type: 'report_accepted', - bool_expr: 'doc.patient', - message: [ - { - locale: 'en', - content: 'Friend {{buddy_name}} with type {{patient.contact_type}} was added to ' + - '{{patient.parent.name}}({{patient.parent.contact_type}})' - } - ] - }] - }; - const contactTypes = [ - { id: 'health_center' }, - { id: 'area_type_1', parents: ['health_center'] }, - { id: 'area_type_2', parents: ['health_center'] }, - { id: 'patient', parents: ['area_type_1'], person: true }, - { id: 'buddy', parents: ['area_type_2'], person: true }, - ]; - config.get.returns([eventConfig]); - config.getAll.returns({ contact_types: contactTypes }); - - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations').resolves([]); - sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); - - return transition.onMatch(change).then(() => { - transitionUtils.getUniqueId.callCount.should.equal(1); - utils.getContactUuid.callCount.should.equal(1); - utils.getContactUuid.args[0].should.deep.equal([patientId]); - utils.getContact.callCount.should.equal(1); - utils.getContact.args[0].should.deep.equal([change.doc.fields.parent]); - db.medic.post.callCount.should.equal(1); - db.medic.post.args[0].should.deep.equal([{ - name: 'Marcel', - patient_id: patientId, - source_id: change.doc._id, - type: 'contact', - contact_type: 'buddy', - parent: { _id: 'georges', parent: { _id: 'west_hc' } }, - created_by: 'pete', - reported_date: change.doc.reported_date, - }]); - (!!change.doc.errors).should.equal(false); - change.doc.tasks.length.should.equal(1); - change.doc.tasks[0].messages[0].should.include({ - to: change.doc.from, - message: 'Friend Marcel with type buddy was added to Georges Place(area_type_2)' - }); - }); - }); - it('should not create person when parent is required but not specified', () => { - const change = { - doc: { - _id: 'reportID', - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+111222', - fields: { patient_name: 'Patricia', parent_id: 'not_the_correct_parent_field' }, - contact: { - _id: 'pete', - name: 'Pete', - contact_type: 'chw', - type: 'contact', - parent: { - _id: 'petes', - name: 'Petes Place', - place_id: 'petes_place', - type: 'contact', - contact_type: 'area_type_1', - parent: { - _id: 'west_hc', name: 'west hc', contact_type: 'health_center', - type: 'contact', place_id: 'the_west_hc' - } - }, - }, + transitionUtils.addRejectionMessage(doc, options.registrationConfig, 'provided_patient_id_not_unique'); + }); + } else { + return generateId(doc, 'patient_id'); + } +}; + +const getParentByPhone = options => { + return db.medic + .query('medic-client/contacts_by_phone', { key: options.doc.from, include_docs: true }) + .then(result => result && result.rows && result.rows.length && result.rows[0].doc) + .then(contact => { + if (!contact) { + return; + } + + options.doc.contact = contact; + return contact.parent && db.medic.get(contact.parent._id); + }); +}; + +// returns the parent of the contact to be created (place or patient) +const getParent = options => { + // if the `parent_id` fields name is not specified in the trigger, default to the submitter's parent + if (!options.params.parent_id) { + // if `update_clinics` runs, the `contact` property already exists + if (options.doc.contact && options.doc.contact.parent) { + return Promise.resolve(JSON.parse(JSON.stringify(options.doc.contact.parent))); + } + + // when `update_clinics` is not enabled (for some reason), fallback to getting contact by phone + return getParentByPhone(options).then(parent => { + if (!parent) { + transitionUtils.addRejectionMessage(options.doc, options.registrationConfig, PARENT_NOT_FOUND); + return; + } + + return parent; + }); + } + + // if the `parent_id` field name is specified in the trigger, but is not defined in the doc, add an error + if (!options.doc.fields[options.params.parent_id]) { + transitionUtils.addRejectionMessage(options.doc, options.registrationConfig, PARENT_FIELD_NOT_PROVIDED); + return Promise.resolve(); + } + + return utils.getContact(options.doc.fields[options.params.parent_id]).then(parent => { + if (!parent) { + transitionUtils.addRejectionMessage(options.doc, options.registrationConfig, PARENT_NOT_FOUND); + return; + } + + return parent; + }); +}; + +const isValidParent = (parent, child) => { + const parentType = contactTypesUtils.getContactType(config.getAll(), parent); + const childType = contactTypesUtils.getContactType(config.getAll(), child); + return contactTypesUtils.isParentOf(parentType, childType); +}; + +const addPatient = (options) => { + const doc = options.doc; + const patientShortcode = options.doc.patient_id; + const patientNameField = getPatientNameField(options.params); + + // create a new patient with this patient_id + const patient = { + name: doc.fields[patientNameField], + reported_date: doc.reported_date, + patient_id: patientShortcode, + source_id: doc._id, + }; + + if (options.params.contact_type) { + patient.type = 'contact'; + patient.contact_type = options.params.contact_type; + } else { + patient.type = 'person'; + } + + return utils + .getContactUuid(patientShortcode) + .then(patientContactId => { + if (patientContactId) { + doc.patient_id = patientShortcode; + return; + } + + return getParent(options).then(parent => { + if (!parent) { + return; } - }; - - const patientId = 'my_patient_id'; - sinon.stub(utils, 'getContactUuid').resolves(); - sinon.stub(utils, 'getContact'); - sinon.stub(db.medic, 'post').resolves(); - - const eventConfig = { - form: 'R', - events: [{ - name: 'on_create', trigger: 'add_patient', params: { contact_type: 'patient', parent_id: 'parent' } - }], - messages: [ { - recipient: 'reporting_unit', - event_type: 'parent_field_not_provided', - message: [ - { - locale: 'en', - content: 'Cannot create patient without parent' - } - ] - }] - }; - const contactTypes = [ - { id: 'health_center' }, - { id: 'area_type_1', parents: ['health_center'] }, - { id: 'area_type_2', parents: ['health_center'] }, - { id: 'patient', parents: ['area_type_1'], person: true }, - ]; - config.get.returns([eventConfig]); - config.getAll.returns({ contact_types: contactTypes }); - - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations').resolves([]); - sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); - - return transition.onMatch(change).then(result => { - result.should.equal(true); - transitionUtils.getUniqueId.callCount.should.equal(1); - utils.getContactUuid.callCount.should.equal(1); - utils.getContactUuid.args[0].should.deep.equal([patientId]); - utils.getContact.callCount.should.equal(0); - db.medic.post.callCount.should.equal(0); - change.doc.errors.length.should.equal(1); - change.doc.errors[0].should.deep.equal({ - message: 'Cannot create patient without parent', - code: 'parent_field_not_provided', - }); - change.doc.tasks.length.should.equal(1); - change.doc.tasks[0].messages[0].should.include({ - to: change.doc.from, - message: 'Cannot create patient without parent' - }); - }); - }); - it('should not create person when parent is required and missing', () => { - const change = { - doc: { - _id: 'reportID', - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+111222', - fields: { patient_name: 'Patricia', parent_id: 'non-existent-contact' }, - contact: { - _id: 'pete', - name: 'Pete', - contact_type: 'chw', - type: 'contact', - parent: { - _id: 'petes', - name: 'Petes Place', - place_id: 'petes_place', - type: 'contact', - contact_type: 'area_type_1', - parent: { - _id: 'west_hc', name: 'west hc', contact_type: 'health_center', - type: 'contact', place_id: 'the_west_hc' - } - }, - }, + if (!isValidParent(parent, patient)) { + transitionUtils.addRejectionMessage( + options.doc, + options.registrationConfig, + PARENT_INVALID, + { templateContext: { parent } } + ); + return; } - }; - - const patientId = 'my_patient_id'; - sinon.stub(utils, 'getContactUuid').resolves(); - sinon.stub(utils, 'getContact').resolves(); - sinon.stub(db.medic, 'post').resolves(); - - const eventConfig = { - form: 'R', - events: [{ - name: 'on_create', trigger: 'add_patient', params: { contact_type: 'patient', parent_id: 'parent_id' } - }], - messages: [{ - recipient: 'reporting_unit', - event_type: 'parent_not_found', - message: [ - { - locale: 'en', - content: 'Selected parent {{fields.parent_id}} does not exists.' - } - ] - }] - }; - const contactTypes = [ - { id: 'health_center' }, - { id: 'area_type_1', parents: ['health_center'] }, - { id: 'area_type_2', parents: ['health_center'] }, - { id: 'patient', parents: ['area_type_1'], person: true }, - ]; - config.get.returns([eventConfig]); - config.getAll.returns({ contact_types: contactTypes }); - - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations').resolves([]); - sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); - - return transition.onMatch(change).then(result => { - result.should.equal(true); - transitionUtils.getUniqueId.callCount.should.equal(1); - utils.getContactUuid.callCount.should.equal(1); - utils.getContactUuid.args[0].should.deep.equal([patientId]); - utils.getContact.callCount.should.equal(1); - utils.getContact.args[0].should.deep.equal([change.doc.fields.parent_id]); - db.medic.post.callCount.should.equal(0); - change.doc.errors.length.should.equal(1); - change.doc.errors[0].should.deep.equal({ - message: 'Selected parent non-existent-contact does not exists.', - code: 'parent_not_found', - }); - change.doc.tasks.length.should.equal(1); - change.doc.tasks[0].messages[0].should.include({ - to: change.doc.from, - message: 'Selected parent non-existent-contact does not exists.' - }); - }); - }); - it('should not create person when parent is invalid', () => { - const change = { - doc: { - _id: 'reportID', - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+111222', - fields: { buddy_name: 'Marcel', the_parent_field: 'georges_place' }, - contact: { - _id: 'pete', - name: 'Pete', - contact_type: 'chw', - type: 'contact', - parent: { - _id: 'petes', - name: 'Petes Place', - place_id: 'petes_place', - type: 'contact', - contact_type: 'area_type_1', - parent: { - _id: 'west_hc', name: 'west hc', contact_type: 'health_center', - type: 'contact', place_id: 'the_west_hc' - } - }, - }, + // include the DOB if it was generated on report + if (doc.birth_date) { + patient.date_of_birth = doc.birth_date; } - }; - const parent = { - _id: 'georges', - name: 'Georges Place', - type: 'contact', - place_id: 'georges_place', - contact_type: 'area_type_1', - parent: { _id: 'west_hc' }, - }; - const patientId = 'my_patient_id'; - sinon.stub(utils, 'getContactUuid').resolves(); - sinon.stub(utils, 'getContact').resolves(parent); - sinon.stub(db.medic, 'post').resolves(); - - const eventConfig = { - form: 'R', - events: [{ - name: 'on_create', trigger: 'add_patient', params: { contact_type: 'patient', parent_id: 'the_parent_field' } - }], - messages: [{ - recipient: 'reporting_unit', - event_type: 'parent_invalid', - message: [ - { - locale: 'en', - content: 'Cannot create patient under parent {{parent.name}}({{parent.place_id}}) ' + - 'of type {{parent.contact_type}}.' - } - ] - }] - }; - const contactTypes = [ - { id: 'health_center' }, - { id: 'area_type_1', parents: ['health_center'] }, - { id: 'area_type_2', parents: ['health_center'] }, - { id: 'patient', parents: ['area_type_2'], person: true }, - ]; - config.get.returns([eventConfig]); - config.getAll.returns({ contact_types: contactTypes }); - - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations').resolves([]); - sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); - - return transition.onMatch(change).then(result => { - result.should.equal(true); - transitionUtils.getUniqueId.callCount.should.equal(1); - utils.getContactUuid.callCount.should.equal(1); - utils.getContactUuid.args[0].should.deep.equal([patientId]); - utils.getContact.callCount.should.equal(1); - utils.getContact.args[0].should.deep.equal([change.doc.fields.the_parent_field]); - db.medic.post.callCount.should.equal(0); - change.doc.errors.length.should.equal(1); - change.doc.errors[0].should.deep.equal({ - message: 'Cannot create patient under parent Georges Place(georges_place) of type area_type_1.', - code: 'parent_invalid', - }); - change.doc.tasks.length.should.equal(1); - change.doc.tasks[0].messages[0].should.include({ - to: change.doc.from, - message: 'Cannot create patient under parent Georges Place(georges_place) of type area_type_1.' - }); - }); - }); - }); - describe('addPlace', () => { - it('should add place with correct hardcoded type under correct parent', () => { - const change = { - doc: { - _id: 'reportID', - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+111222', - fields: { place_name: 'new clinic', parent_id: 'north_hc_place' }, - contact: { - _id: 'pete', - name: 'Pete', - contact_type: 'chw', - type: 'contact', - parent: { - _id: 'petes', - name: 'Petes Place', - contact_type: 'clinic', - type: 'contact', - parent: { - _id: 'west_hc', - name: 'west hc', - contact_type: 'health_center', - type: 'contact', - place_id: 'south_hc_place', - } - } - }, + // assign patient in doc with full parent doc - to be used in messages + doc.patient = Object.assign({ parent }, patient); + patient.parent = lineage.minifyLineage(parent); + patient.created_by = doc.contact && doc.contact._id; + + return db.medic.post(patient); + }); + }); +}; + +const addPlace = (options) => { + const doc = options.doc; + const placeShortcode = doc.place_id; + const placeNameField = getPlaceNameField(options.params); + + // create a new place with this place_id + const place = { + name: doc.fields[placeNameField], + reported_date: doc.reported_date, + place_id: placeShortcode, + source_id: doc._id, + }; + + if (contactTypesUtils.isHardcodedType(options.params.contact_type)) { + place.type = options.params.contact_type; + } else { + place.type = 'contact'; + place.contact_type = options.params.contact_type; + } + + return utils + .getContactUuid(placeShortcode) + .then(placeContactId => { + if (placeContactId) { + doc.place_id = placeShortcode; + return; + } + + return getParent(options).then(parent => { + if (!parent) { + return; } - }; - const placeId = 'my_place_id'; - const parent = { - _id: 'north_hc', - name: 'north hc', - type: 'contact', - contact_type: 'health_center', - place_id: 'north_hc_place', - }; - sinon.stub(utils, 'getContactUuid').resolves(); - sinon.stub(utils, 'getContact').resolves(parent); - sinon.stub(db.medic, 'post').resolves(); - const eventConfig = { - form: 'R', - events: [{ - name: 'on_create', trigger: 'add_place', params: { contact_type: 'clinic', parent_id: 'parent_id' } - }], - messages: [ { - recipient: 'reporting_unit', - event_type: 'report_accepted', - bool_expr: 'doc.place', - message: [ - { - locale: 'en', - content: 'Place {{place_name}} with type {{place.type}} was added to ' + - '{{place.parent.name}}({{place.parent.contact_type}})' - } - ] - }] - }; - const contactTypes = [ - { id: 'health_center' }, - { id: 'clinic', parents: ['health_center'] }, - { id: 'patient', parents: ['clinic'], person: true }, - ]; - config.get.returns([eventConfig]); - config.getAll.returns({ contact_types: contactTypes }); - - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations').resolves([]); - sinon.stub(transitionUtils, 'getUniqueId').resolves(placeId); - - return transition.onMatch(change).then(() => { - transitionUtils.getUniqueId.callCount.should.equal(1); - utils.getContactUuid.callCount.should.equal(1); - utils.getContactUuid.args[0].should.deep.equal([placeId]); - utils.getContact.callCount.should.equal(1); - utils.getContact.args[0].should.deep.equal([change.doc.fields.parent_id]); - db.medic.post.callCount.should.equal(1); - db.medic.post.args[0].should.deep.equal([{ - name: 'new clinic', - place_id: placeId, - source_id: change.doc._id, - type: 'clinic', - parent: { _id: 'north_hc' }, - created_by: 'pete', - reported_date: change.doc.reported_date, - }]); - (!!change.doc.errors).should.equal(false); - change.doc.tasks.length.should.equal(1); - change.doc.tasks[0].messages[0].should.include({ - to: change.doc.from, - message: 'Place new clinic with type clinic was added to north hc(health_center)' - }); - }); - }); - it('should add place with right configurable type and submitter parent when parent_id param not specified', () => { - const change = { - doc: { - _id: 'reportID', - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+111222', - fields: { place_name: 'new clinic' }, - contact: { - _id: 'supervisor', - name: 'Frank', - contact_type: 'supervisor', - type: 'contact', - parent: { - _id: 'west_hc', - name: 'west hc', - place_id: 'west_hc_place', - contact_type: 'health_center_1', - type: 'contact', - } - }, + if (!isValidParent(parent, place)) { + transitionUtils.addRejectionMessage( + options.doc, + options.registrationConfig, + PARENT_INVALID, + { templateContext: { parent } } + ); + return; } - }; - const placeId = 'my_place_id'; - sinon.stub(utils, 'getContactUuid').resolves(); - sinon.stub(utils, 'getContact'); - sinon.stub(db.medic, 'post').resolves(); - const eventConfig = { - form: 'R', - events: [{ name: 'on_create', trigger: 'add_place', params: { contact_type: 'clinic_1' }}], - messages: [ { - recipient: 'reporting_unit', - event_type: 'report_accepted', - bool_expr: 'doc.place', - message: [ - { - locale: 'en', - content: 'Place {{place_name}} with type {{place.contact_type}} was added to ' + - '{{place.parent.name}}({{place.parent.contact_type}})' - } - ] - }] - }; - const contactTypes = [ - { id: 'health_center_1' }, - { id: 'clinic_1', parents: ['health_center_1'] }, - { id: 'supervisor', parents: ['health_center_1'], person: true }, - { id: 'patient', parents: ['clinic_1'], person: true }, - ]; - config.get.returns([eventConfig]); - config.getAll.returns({ contact_types: contactTypes }); - - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations').resolves([]); - sinon.stub(transitionUtils, 'getUniqueId').resolves(placeId); - - return transition.onMatch(change).then(() => { - transitionUtils.getUniqueId.callCount.should.equal(1); - utils.getContactUuid.callCount.should.equal(1); - utils.getContactUuid.args[0].should.deep.equal([placeId]); - utils.getContact.callCount.should.equal(0); - db.medic.post.callCount.should.equal(1); - db.medic.post.args[0].should.deep.equal([{ - name: 'new clinic', - place_id: placeId, - source_id: change.doc._id, - type: 'contact', - contact_type: 'clinic_1', - parent: { _id: 'west_hc' }, - created_by: 'supervisor', - reported_date: change.doc.reported_date, - }]); - (!!change.doc.errors).should.equal(false); - change.doc.tasks.length.should.equal(1); - change.doc.tasks[0].messages[0].should.include({ - to: change.doc.from, - message: 'Place new clinic with type clinic_1 was added to west hc(health_center_1)' - }); - }); - }); - it('should default to submitter by phone parent when parent_id param not specified', () => { - const change = { - doc: { - _id: 'reportID', - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+111222', - fields: { place_name: 'new clinic' }, - } - }; - const placeId = 'my_place_id'; - sinon.stub(utils, 'getContactUuid').resolves(); - sinon.stub(utils, 'getContact'); - sinon.stub(db.medic, 'post').resolves(); - const eventConfig = { - form: 'R', - events: [{ name: 'on_create', trigger: 'add_place', params: { contact_type: 'clinic_1' }}], - messages: [ { - recipient: 'reporting_unit', - event_type: 'report_accepted', - bool_expr: 'doc.place', - message: [ - { - locale: 'en', - content: 'Place {{place_name}} with type {{place.contact_type}} was added to ' + - '{{place.parent.name}}({{place.parent.contact_type}})' - } - ] - }] - }; - sinon.stub(db.medic, 'query') - .withArgs('medic-client/contacts_by_phone') - .resolves({ rows: [ - { - doc: { - _id: 'supervisor', - name: 'Frank', - contact_type: 'supervisor', - type: 'contact', - phone: '+111222', - parent: { _id: 'west_hc' } - } + // assign place in doc with full parent doc - to be used in messages + doc.place = Object.assign({ parent }, place); + place.parent = lineage.minifyLineage(parent); + place.created_by = doc.contact && doc.contact._id; + + return db.medic.post(place); + }); + }); +}; + +const hasValidSubject = (doc, patientId, placeId) => { + // doc is already hydrated. + if (patientId && (!doc.patient || (doc.patient && !contactTypesUtils.isPerson(config.getAll(), doc.patient)))) { + return false; + } + + if (placeId && (!doc.place || (doc.place && !contactTypesUtils.isPlace(config.getAll(), doc.place)))) { + return false; + } + + return true; +}; + +module.exports = { + name: NAME, + init: () => { + const registrations = getConfig(); + registrations.forEach(registration => { + if (registration.events) { + registration.events.forEach(event => { + let params; + try { + params = parseParams(event.params); + } catch (e) { + throw new Error( + `Configuration error. Unable to parse params for ${registration.form}.${event.trigger}: ` + + `'${event.params}'. Error: ${e}` + ); } - ]}); - sinon.stub(db.medic, 'get').withArgs('west_hc').resolves({ - _id: 'west_hc', - name: 'west hc', - place_id: 'west_hc_place', - contact_type: 'health_center_1', - type: 'contact', - }); - const contactTypes = [ - { id: 'health_center_1' }, - { id: 'clinic_1', parents: ['health_center_1'] }, - { id: 'supervisor', parents: ['health_center_1'], person: true }, - { id: 'patient', parents: ['clinic_1'], person: true }, - ]; - config.get.returns([eventConfig]); - config.getAll.returns({ contact_types: contactTypes }); - - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations').resolves([]); - sinon.stub(transitionUtils, 'getUniqueId').resolves(placeId); - - return transition.onMatch(change).then(() => { - transitionUtils.getUniqueId.callCount.should.equal(1); - utils.getContactUuid.callCount.should.equal(1); - utils.getContactUuid.args[0].should.deep.equal([placeId]); - utils.getContact.callCount.should.equal(0); - db.medic.query.callCount.should.equal(1); - db.medic.query.args[0] - .should.deep.equal(['medic-client/contacts_by_phone', { key: '+111222', include_docs: true }]); - db.medic.get.callCount.should.equal(1); - db.medic.get.args[0].should.deep.equal(['west_hc']); - db.medic.post.callCount.should.equal(1); - db.medic.post.args[0].should.deep.equal([{ - name: 'new clinic', - place_id: placeId, - source_id: change.doc._id, - type: 'contact', - contact_type: 'clinic_1', - parent: { _id: 'west_hc' }, - created_by: 'supervisor', - reported_date: change.doc.reported_date, - }]); - (!!change.doc.errors).should.equal(false); - change.doc.tasks.length.should.equal(1); - change.doc.tasks[0].messages[0].should.include({ - to: change.doc.from, - message: 'Place new clinic with type clinic_1 was added to west hc(health_center_1)' - }); - }); - }); - it('should use the name property indicated within the event parameter ', () => { - const change = { - doc: { - _id: 'reportID', - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+111222', - fields: { doodle: 'newest place', parent_id: 'north_hc_place', place_name: 'this is a trick' }, - contact: { - _id: 'pete', - name: 'Pete', - contact_type: 'chw', - type: 'contact', - parent: { - _id: 'petes', - name: 'Petes Place', - contact_type: 'clinic', - type: 'contact', - parent: { - _id: 'west_hc', - name: 'west hc', - contact_type: 'health_center', - type: 'contact', - place_id: 'south_hc_place', - } + if (event.trigger === 'add_patient') { + if (params.patient_id_field === 'patient_id') { + throw new Error( + `Configuration error in ${registration.form}.${event.trigger}: ` + + `patient_id_field cannot be set to patient_id` + ); } - }, - } - }; - const placeId = 'my_place_id'; - const parent = { - _id: 'north_hc', - name: 'north hc', - type: 'contact', - contact_type: 'health_center', - place_id: 'north_hc_place', - }; - sinon.stub(utils, 'getContactUuid').resolves(); - sinon.stub(utils, 'getContact').resolves(parent); - sinon.stub(db.medic, 'post').resolves(); - const eventConfig = { - form: 'R', - events: [{ - name: 'on_create', trigger: 'add_place', - params: { contact_type: 'clinic_1', parent_id: 'parent_id', place_name_field: 'doodle' } - }], - messages: [ { - recipient: 'reporting_unit', - event_type: 'report_accepted', - bool_expr: 'doc.place', - message: [ - { - locale: 'en', - content: 'Place {{place.name}} with type {{place.contact_type}} was added to ' + - '{{place.parent.name}}({{place.parent.contact_type}})' - } - ] - }] - }; - const contactTypes = [ - { id: 'health_center' }, - { id: 'clinic_1', parents: ['health_center'] }, - { id: 'patient', parents: ['clinic_1'], person: true }, - ]; - config.get.returns([eventConfig]); - config.getAll.returns({ contact_types: contactTypes }); - - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations').resolves([]); - sinon.stub(transitionUtils, 'getUniqueId').resolves(placeId); - - return transition.onMatch(change).then(() => { - transitionUtils.getUniqueId.callCount.should.equal(1); - utils.getContactUuid.callCount.should.equal(1); - utils.getContactUuid.args[0].should.deep.equal([placeId]); - utils.getContact.callCount.should.equal(1); - utils.getContact.args[0].should.deep.equal([change.doc.fields.parent_id]); - db.medic.post.callCount.should.equal(1); - db.medic.post.args[0].should.deep.equal([{ - name: 'newest place', - place_id: placeId, - source_id: change.doc._id, - type: 'contact', - contact_type: 'clinic_1', - parent: { _id: 'north_hc' }, - created_by: 'pete', - reported_date: change.doc.reported_date, - }]); - (!!change.doc.errors).should.equal(false); - change.doc.tasks.length.should.equal(1); - change.doc.tasks[0].messages[0].should.include({ - to: change.doc.from, - message: 'Place newest place with type clinic_1 was added to north hc(health_center)' - }); - }); - }); - - it('should use the parent property indicated within the event parameter ', () => { - const change = { - doc: { - _id: 'reportID', - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+111222', - fields: { doodle: 'newest place', fiddle: 'north_hc_place', place_name: 'this is a trick' }, - contact: { - _id: 'pete', - name: 'Pete', - contact_type: 'chw', - type: 'contact', - parent: { - _id: 'petes', - name: 'Petes Place', - contact_type: 'clinic', - type: 'contact', - parent: { - _id: 'west_hc', - name: 'west hc', - contact_type: 'health_center', - type: 'contact', - place_id: 'south_hc_place', - } + const typeId = params.contact_type || 'person'; + const contactType = contactTypesUtils.getTypeById(config.getAll(), typeId); + if (!contactType) { + throw new Error( + `Configuration error in ${registration.form}.${event.trigger}: ` + + `trigger would create a doc with an unknown contact type "${typeId}"` + ); } - }, - } - }; - const placeId = 'my_place_id'; - const parent = { - _id: 'north_hc', - name: 'north hc', - type: 'contact', - contact_type: 'health_center', - place_id: 'north_hc_place', - }; - sinon.stub(utils, 'getContactUuid').resolves(); - sinon.stub(utils, 'getContact').resolves(parent); - sinon.stub(db.medic, 'post').resolves(); - const eventConfig = { - form: 'R', - events: [{ - name: 'on_create', trigger: 'add_place', - params: { contact_type: 'clinic', parent_id: 'fiddle', place_name_field: 'doodle' } - }], - messages: [ { - recipient: 'reporting_unit', - event_type: 'report_accepted', - bool_expr: 'doc.place', - message: [ - { - locale: 'en', - content: 'Place {{place.name}} with type {{place.type}} was added to ' + - '{{place.parent.name}}({{place.parent.contact_type}})' + if (!contactTypesUtils.isPersonType(contactType)) { + throw new Error( + `Configuration error in ${registration.form}.${event.trigger}: ` + + `trigger would create a person with a place contact type "${typeId}"` + ); } - ] - }] - }; - const contactTypes = [ - { id: 'health_center' }, - { id: 'clinic', parents: ['health_center'] }, - { id: 'patient', parents: ['clinic'], person: true }, - ]; - config.get.returns([eventConfig]); - config.getAll.returns({ contact_types: contactTypes }); - - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations').resolves([]); - sinon.stub(transitionUtils, 'getUniqueId').resolves(placeId); - - return transition.onMatch(change).then(() => { - transitionUtils.getUniqueId.callCount.should.equal(1); - utils.getContactUuid.callCount.should.equal(1); - utils.getContactUuid.args[0].should.deep.equal([placeId]); - utils.getContact.callCount.should.equal(1); - utils.getContact.args[0].should.deep.equal([change.doc.fields.fiddle]); - db.medic.post.callCount.should.equal(1); - db.medic.post.args[0].should.deep.equal([{ - name: 'newest place', - place_id: placeId, - source_id: change.doc._id, - type: 'clinic', - parent: { _id: 'north_hc' }, - created_by: 'pete', - reported_date: change.doc.reported_date, - }]); - (!!change.doc.errors).should.equal(false); - change.doc.tasks.length.should.equal(1); - change.doc.tasks[0].messages[0].should.include({ - to: change.doc.from, - message: 'Place newest place with type clinic was added to north hc(health_center)' - }); - }); - }); + } - it('should not create place when parent_id is not defined and no contact', () => { - const change = { - doc: { - _id: 'reportID', - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+111222', - fields: { place_name: 'New Orleans' }, - } - }; - const placeId = 'my_place_id'; - sinon.stub(utils, 'getContactUuid').resolves(); - sinon.stub(utils, 'getContact'); - sinon.stub(db.medic, 'post').resolves(); - const eventConfig = { - form: 'R', - events: [{ name: 'on_create', trigger: 'add_place', params: { contact_type: 'clinic' } }], - messages: [ { - recipient: 'reporting_unit', - event_type: 'parent_not_found', - message: [ - { - locale: 'en', - content: 'Cannot create clinic with name {{place_name}}: parent not found.' + if ( + event.trigger === 'assign_schedule' || + event.trigger === 'clear_schedule' + ) { + if (!event.params) { + throw new Error( + `Configuration error. Expecting params to be defined as the name of the schedule(s) ` + + `for ${registration.form}.${event.trigger}` + ); } - ] - }] - }; - config.get.withArgs('registrations').returns([eventConfig]); - sinon.stub(db.medic, 'query').withArgs('medic-client/contacts_by_phone').resolves({ rows: [] }); - sinon.stub(db.medic, 'get'); - - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations').resolves([]); - sinon.stub(transitionUtils, 'getUniqueId').resolves(placeId); - - return transition.onMatch(change).then(() => { - transitionUtils.getUniqueId.callCount.should.equal(1); - utils.getContactUuid.callCount.should.equal(1); - utils.getContactUuid.args[0].should.deep.equal([placeId]); - utils.getContact.callCount.should.equal(0); - db.medic.post.callCount.should.equal(0); - db.medic.query.callCount.should.equal(1); - db.medic.query.args[0] - .should.deep.equal(['medic-client/contacts_by_phone', { key: '+111222', include_docs: true }]); - db.medic.get.callCount.should.equal(0); - - change.doc.errors.length.should.equal(1); - change.doc.errors[0].should.deep.equal({ - code: 'parent_not_found', - message: 'Cannot create clinic with name New Orleans: parent not found.', - }); - change.doc.tasks.length.should.equal(1); - change.doc.tasks[0].messages[0].should.include({ - to: change.doc.from, - message: 'Cannot create clinic with name New Orleans: parent not found.' - }); - }); - }); - - it('should not create place when parent is not defined', () => { - const change = { - doc: { - _id: 'reportID', - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+111222', - fields: { place_name: 'New Orleans' }, - } - }; - const placeId = 'my_place_id'; - sinon.stub(utils, 'getContactUuid').resolves(); - sinon.stub(utils, 'getContact'); - sinon.stub(db.medic, 'post').resolves(); - const eventConfig = { - form: 'R', - events: [{ name: 'on_create', trigger: 'add_place', params: { contact_type: 'clinic', parent_id: 'some_id' } }], - messages: [ { - recipient: 'reporting_unit', - event_type: 'parent_field_not_provided', - message: [ - { - locale: 'en', - content: 'Cannot create clinic with name {{place_name}}: parent field not provided.' + if (!Array.isArray(params)) { + throw new Error( + `Configuration error. Expecting params to be a string, comma separated list, ` + + `or an array for ${registration.form}.${event.trigger}: '${event.params}' + `); } - ] - }] - }; - config.get.withArgs('registrations').returns([eventConfig]); - - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations').resolves([]); - sinon.stub(transitionUtils, 'getUniqueId').resolves(placeId); - - return transition.onMatch(change).then(() => { - transitionUtils.getUniqueId.callCount.should.equal(1); - utils.getContactUuid.callCount.should.equal(1); - utils.getContactUuid.args[0].should.deep.equal([placeId]); - utils.getContact.callCount.should.equal(0); - db.medic.post.callCount.should.equal(0); - - change.doc.errors.length.should.equal(1); - change.doc.errors[0].should.deep.equal({ - code: 'parent_field_not_provided', - message: 'Cannot create clinic with name New Orleans: parent field not provided.', - }); - change.doc.tasks.length.should.equal(1); - change.doc.tasks[0].messages[0].should.include({ - to: change.doc.from, - message: 'Cannot create clinic with name New Orleans: parent field not provided.' - }); - }); - }); + } - it('should not create place when parent is not found', () => { - const change = { - doc: { - _id: 'reportID', - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+111222', - fields: { place_name: 'New Orleans' }, - } - }; - const placeId = 'my_place_id'; - sinon.stub(utils, 'getContactUuid').resolves(); - sinon.stub(utils, 'getContact'); - sinon.stub(db.medic, 'post').resolves(); - const eventConfig = { - form: 'R', - events: [{ name: 'on_create', trigger: 'add_place', params: { contact_type: 'clinic', parent_id: 'some_id' } }], - messages: [ { - recipient: 'reporting_unit', - event_type: 'parent_field_not_provided', - message: [ - { - locale: 'en', - content: 'Cannot create clinic with name {{place_name}}: parent field not provided.' + if (event.trigger === 'add_place') { + const typeId = params.contact_type; + if (!typeId) { + throw new Error( + `Configuration error in ${registration.form}.${event.trigger}: ` + + `trigger would create a place with an undefined contact type` + ); } - ] - }] - }; - config.get.withArgs('registrations').returns([eventConfig]); - - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations').resolves([]); - sinon.stub(transitionUtils, 'getUniqueId').resolves(placeId); - - return transition.onMatch(change).then(() => { - transitionUtils.getUniqueId.callCount.should.equal(1); - utils.getContactUuid.callCount.should.equal(1); - utils.getContactUuid.args[0].should.deep.equal([placeId]); - utils.getContact.callCount.should.equal(0); - db.medic.post.callCount.should.equal(0); - - change.doc.errors.length.should.equal(1); - change.doc.errors[0].should.deep.equal({ - code: 'parent_field_not_provided', - message: 'Cannot create clinic with name New Orleans: parent field not provided.', - }); - change.doc.tasks.length.should.equal(1); - change.doc.tasks[0].messages[0].should.include({ - to: change.doc.from, - message: 'Cannot create clinic with name New Orleans: parent field not provided.' - }); - }); - }); - - it('should not create place when parent is not valid', () => { - const change = { - doc: { - _id: 'reportID', - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+111222', - fields: { place_name: 'New Orleans', parent_id: 'hc2' }, - contact: { - _id: 'bob', name: 'Bob', - parent: { - _id: 'a_health_center', name: 'HC1', type: 'contact', contact_type: 'health_center', place_id: 'hc1' + const contactType = contactTypesUtils.getTypeById(config.getAll(), typeId); + if (!contactType) { + throw new Error( + `Configuration error in ${registration.form}.${event.trigger}: ` + + `trigger would create a place with an unknown contact type "${typeId}"` + ); } - } - } - }; - const placeId = 'my_place_id'; - const parent = { - _id: 'other_health_center', - name: 'Other health center', - place_id: 'hc2', - type: 'contact', - contact_type: 'health_center', - parent: { _id: 'district1' }, - }; - sinon.stub(utils, 'getContactUuid').resolves(); - sinon.stub(utils, 'getContact').resolves(parent); - sinon.stub(db.medic, 'post').resolves(); - const eventConfig = { - form: 'R', - events: [{ name: 'on_create', trigger: 'add_place', params: { contact_type: 'area', parent_id: 'parent_id' } }], - messages: [ { - recipient: 'reporting_unit', - event_type: 'parent_invalid', - message: [ - { - locale: 'en', - content: 'Cannot create area with name {{place_name}} under parent {{parent.name}} of type ' + - '{{parent.contact_type}}' + if (!contactTypesUtils.isPlaceType(contactType)) { + throw new Error( + `Configuration error in ${registration.form}.${event.trigger}: ` + + `trigger would create a place with a person contact type "${typeId}"` + ); } - ] - }] - }; - const contactTypes = [ - { id: 'district' }, - { id: 'health_center', parents: ['district'] }, - { id: 'local_thing', parents: ['district'] }, - { id: 'clinic', parents: ['health_center'] }, - { id: 'area', parents: ['local_thing'] }, - ]; - config.get.returns([eventConfig]); - config.getAll.returns({ contact_types: contactTypes }); - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations').resolves([]); - sinon.stub(transitionUtils, 'getUniqueId').resolves(placeId); - - return transition.onMatch(change).then(() => { - transitionUtils.getUniqueId.callCount.should.equal(1); - utils.getContactUuid.callCount.should.equal(1); - utils.getContactUuid.args[0].should.deep.equal([placeId]); - utils.getContact.callCount.should.equal(1); - utils.getContact.args[0].should.deep.equal(['hc2']); - db.medic.post.callCount.should.equal(0); - - change.doc.errors.length.should.equal(1); - change.doc.errors[0].should.deep.equal({ - code: 'parent_invalid', - message: 'Cannot create area with name New Orleans under parent Other health center of type health_center', - }); - change.doc.tasks.length.should.equal(1); - change.doc.tasks[0].messages[0].should.include({ - to: change.doc.from, - message: 'Cannot create area with name New Orleans under parent Other health center of type health_center' - }); - }); - }); - }); - - describe('addCase', () => { - - it('trigger assigns a case id', () => { - const caseId = '99955'; - const change = { - doc: { - _id: 'def', - type: 'data_record', - form: 'S', - reported_date: 53, - from: '+555123', - fields: { level: 8 } - }, - }; - const eventConfig = { - form: 'S', - events: [{ name: 'on_create', trigger: 'add_case' }], - }; - config.get.returns([eventConfig]); - sinon.stub(validation, 'validate').resolves(); - sinon.stub(transitionUtils, 'getUniqueId').resolves(caseId); - config.getAll.returns(settings); - - return transition.onMatch(change).then(() => { - change.doc.case_id.should.equal(caseId); - }); - }); - - it('adds place_uuid if known', () => { - const caseId = '99955'; - const change = { - doc: { - _id: 'def', - type: 'data_record', - form: 'S', - reported_date: 53, - from: '+555123', - fields: { level: 8 }, - contact: { parent: { _id: 'abc' } } - }, - }; - const eventConfig = { - form: 'S', - events: [{ name: 'on_create', trigger: 'add_case' }], - }; - config.get.returns([eventConfig]); - sinon.stub(validation, 'validate').resolves(); - sinon.stub(transitionUtils, 'getUniqueId').resolves(caseId); - config.getAll.returns(settings); - - return transition.onMatch(change).then(() => { - change.doc.case_id.should.equal(caseId); - change.doc.fields.place_uuid.should.equal('abc'); - }); - }); - - }); - - describe('assign_schedule', () => { - it('event creates the named schedule', () => { - const change = { - doc: { - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+555123', - fields: { patient_id: '05649' }, - patient: { _id: 'patient', patient_id: '05649', type: 'person' } - }, - }; - sinon.stub(db.medic, 'post').resolves(); - const eventConfig = { - form: 'R', - events: [ - { - name: 'on_create', - trigger: 'assign_schedule', - params: 'myschedule', - }, - ], - }; - config.get.returns([eventConfig]); - sinon.stub(validation, 'validate').resolves(); - sinon - .stub(utils, 'getRegistrations') - .withArgs({ id: '05649' }).resolves([{ _id: 'xyz' }]) - .withArgs({ id: undefined }).resolves([]); - sinon.stub(schedules, 'getScheduleConfig').returns('someschedule'); - const assignSchedule = sinon - .stub(schedules, 'assignSchedule') - .returns(true); - - return transition.onMatch(change).then(() => { - assignSchedule.callCount.should.equal(1); - assignSchedule.args[0].should.deep.equal([ - change.doc, - 'someschedule', - { - patient: change.doc.patient, - place: undefined, - patientRegistrations: [{ _id: 'xyz' }], - placeRegistrations: [], - }, - ]); - utils.getRegistrations.callCount.should.equal(2); - utils.getRegistrations.args[0].should.deep.equal([{ id: '05649' }]); - }); - }); - - it('should create the named schedule for a place', () => { - const change = { - doc: { - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+555123', - fields: { place_id: '79999' }, - place: { _id: 'place_id', place_id: '79999', type: 'clinic' }, - }, - }; - sinon.stub(db.medic, 'post').resolves(); - - const eventConfig = { - form: 'R', - events: [ - { - name: 'on_create', - trigger: 'assign_schedule', - params: 'myschedule', - }, - ], - }; - config.get.returns([eventConfig]); - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations') - .withArgs({ id: '79999' }).resolves([{ _id: 'place_registration' }]) - .withArgs({ id: undefined }).resolves([]); - - sinon.stub(schedules, 'getScheduleConfig').returns('myschedule'); - sinon.stub(schedules, 'assignSchedule').returns(true); - sinon.stub(contactTypeUtils, 'isPlace').returns(true); - - return transition.onMatch(change).then(() => { - schedules.assignSchedule.callCount.should.equal(1); - schedules.assignSchedule.args[0].should.deep.equal([ - change.doc, - 'myschedule', - { - patient: undefined, - patientRegistrations: [], - place: change.doc.place, - placeRegistrations: [{ _id: 'place_registration' }], - }, - ]); - - utils.getRegistrations.callCount.should.equal(2); - utils.getRegistrations.args[0].should.deep.equal([{ id: undefined }]); - utils.getRegistrations.args[1].should.deep.equal([{ id: '79999' }]); - }); - }); - - it('should create the named schedule with place and patient registrations', () => { - const change = { - doc: { - type: 'data_record', - form: 'R', - reported_date: 53, - from: '+555123', - fields: { place_id: '89647', patient_id: '26954' }, - place: { _id: 'place_id', place_id: '89647' }, - patient: { _id: 'patient_id', place_id: '26954' }, - }, - }; - sinon.stub(db.medic, 'post').resolves(); - - const eventConfig = { - form: 'R', - events: [ - { - name: 'on_create', - trigger: 'assign_schedule', - params: 'myschedule', - }, - ], - }; - config.get.returns([eventConfig]); - sinon.stub(validation, 'validate').resolves(); - sinon.stub(utils, 'getRegistrations') - .onCall(0).resolves([{ _id: 'patient_registration' }]) - .onCall(1).resolves([{ _id: 'place_registration' }]); - - sinon.stub(schedules, 'getScheduleConfig').returns('myschedule'); - sinon.stub(schedules, 'assignSchedule').returns(true); - - sinon.stub(contactTypeUtils, 'isPerson').returns(true); - sinon.stub(contactTypeUtils, 'isPlace').returns(true); - - return transition.onMatch(change).then(() => { - schedules.assignSchedule.callCount.should.equal(1); - schedules.assignSchedule.args[0].should.deep.equal([ - change.doc, - 'myschedule', - { - patient: change.doc.patient, - patientRegistrations: [{ _id: 'patient_registration' }], - place: change.doc.place, - placeRegistrations: [{ _id: 'place_registration' }], - }, - ]); - - utils.getRegistrations.callCount.should.equal(2); - utils.getRegistrations.args[0].should.deep.equal([{ id: '26954' }]); - utils.getRegistrations.args[1].should.deep.equal([{ id: '89647' }]); - }); - }); - }); - - describe('filter', () => { - it('returns false for reports with no registration configured', () => { - const doc = { form: 'R', type: 'data_record' }; - const configGet = config.get.returns([{ form: 'XYZ' }]); - const actual = transition.filter({ doc }); - configGet.callCount.should.equal(1); - configGet.args[0][0].should.equal('registrations'); - actual.should.equal(false); - }); - - it('returns false for reports that are not valid submissions', () => { - const doc = { form: 'R', type: 'data_record' }; - sinon.stub(utils, 'isValidSubmission').returns(false); - config.get.returns([{ form: 'R' }]); - const actual = transition.filter({ doc, info: {} }); - config.get.callCount.should.equal(1); - config.get.args[0][0].should.equal('registrations'); - utils.isValidSubmission.callCount.should.equal(1); - utils.isValidSubmission.args[0].should.deep.equal([doc]); - actual.should.equal(false); - }); - - it('returns true for reports that are valid submissions', () => { - const doc = { form: 'R', type: 'data_record' }; - sinon.stub(utils, 'isValidSubmission').returns(true); - config.get.returns([{ form: 'R' }]); - const actual = transition.filter({ doc, info: {} }); - config.get.callCount.should.equal(1); - config.get.args[0][0].should.equal('registrations'); - utils.isValidSubmission.callCount.should.equal(1); - utils.isValidSubmission.args[0].should.deep.equal([doc]); - actual.should.equal(true); - }); - }); - - describe('trigger param configuration', () => { - beforeEach(() => { - transition.fireConfiguredTriggers = transition.__get__('fireConfiguredTriggers'); - transition.triggers = transition.__get__('triggers'); - }); - - it('supports strings', () => { - const eventConfig = { - form: 'R', - events: [ - { name: 'on_create', trigger: 'testparamparsing', params: 'foo' }, - ], - }; - - transition.triggers.testparamparsing = sinon.stub(); - - return transition.fireConfiguredTriggers(eventConfig, {}).then(() => { - transition.triggers.testparamparsing.callCount.should.equal(1); - transition.triggers.testparamparsing.args[0][0].params.should.deep.equal(['foo']); - }); - }); - - it('supports comma-delimited strings as array', () => { - const eventConfig = { - form: 'R', - events: [ - { name: 'on_create', trigger: 'testparamparsing', params: 'foo,bar' }, - ], - }; - - transition.triggers.testparamparsing = sinon.stub(); - - - return transition.fireConfiguredTriggers(eventConfig, {}).then(() => { - transition.triggers.testparamparsing.callCount.should.equal(1); - transition.triggers.testparamparsing.args[0][0].params.should.deep.equal(['foo', 'bar']); - }); - }); - - it('supports arrays as a string', () => { - const eventConfig = { - form: 'R', - events: [ - { - name: 'on_create', - trigger: 'testparamparsing', - params: '["foo","bar", 3]', - }, - ], - }; - - transition.triggers.testparamparsing = sinon.stub(); - - return transition.fireConfiguredTriggers(eventConfig, {}).then(() => { - transition.triggers.testparamparsing.callCount.should.equal(1); - transition.triggers.testparamparsing.args[0][0].params.should.deep.equal(['foo', 'bar', 3]); - }); - }); - - it('supports JSON as a string', () => { - const eventConfig = { - form: 'R', - events: [ - { - name: 'on_create', - trigger: 'testparamparsing', - params: '{"foo": "bar"}', - }, - ], - }; - - transition.triggers.testparamparsing = sinon.stub(); - - return transition.fireConfiguredTriggers(eventConfig, {}).then(() => { - transition.triggers.testparamparsing.callCount.should.equal(1); - transition.triggers.testparamparsing.args[0][0].params.should.deep.equal({ foo: 'bar' }); - }); - }); - - it('supports direct JSON', () => { - const eventConfig = { - form: 'R', - events: [ - { - name: 'on_create', - trigger: 'testparamparsing', - params: { foo: 'bar' }, - }, - ], - }; - - transition.triggers.testparamparsing = sinon.stub(); - - return transition.fireConfiguredTriggers(eventConfig, {}).then(() => { - transition.triggers.testparamparsing.callCount.should.equal(1); - transition.triggers.testparamparsing.args[0][0].params.should.deep.equal({ foo: 'bar' }); - }); - }); - - it('fails with no parameters', () => { - const eventConfig = [ - { - form: 'R', - events: [ - { - name: 'on_create', - trigger: 'assign_schedule', - params: '', - }, - ], - }, - ]; - - config.get.returns(eventConfig); - transition.init.should.throw(Error, 'Configuration error. Expecting params to be defined as the name of the ' + - 'schedule(s) for R.assign_schedule'); - }); - - it('fails with object parameters', () => { - const eventConfig = [ - { - form: 'R', - events: [ - { - name: 'on_create', - trigger: 'assign_schedule', - params: '{ "name": "hello" }', - }, - ], - }, - ]; - - config.get.returns(eventConfig); - transition.init.should.throw(Error, 'Configuration error. Expecting params to be a string, ' + - 'comma separated list, or an array for R.assign_schedule: \'{ "name": "hello" }\''); - }); - - it('succeeds with array parameters', () => { - const eventConfig = [ - { - form: 'R', - events: [ - { - name: 'on_create', - trigger: 'assign_schedule', - params: 'a,b', - }, - ], - }, - ]; - - config.get.returns(eventConfig); - transition.init(); - }); - - it('parse failure for invalid JSON propagates to the callbacks', () => { - const eventConfig = [ - { - form: 'R', - events: [ - { - name: 'on_create', - trigger: 'testparamparsing', - params: '{"foo": "bar"', // missing end "}" - }, - ], - }, - ]; - - config.get.returns(eventConfig); - transition.init.should.throw(Error, 'Configuration error. Unable to parse params for R.testparamparsing: ' + - '\'{"foo": "bar"\'. Error: SyntaxError: Unexpected end of JSON input'); - }); - - it('add_patient fails if the configured contact type is not known', () => { - const eventConfig = [{ - form: 'R', - events: [{ - name: 'on_create', - trigger: 'add_patient', - params: '{ "contact_type": "unknown" }' - }], - }]; - const contactTypes = [{ id: 'known' }]; - - config.get.returns(eventConfig); - config.getAll.returns({ contact_types: contactTypes }); - - transition.init.should.throw(Error, 'Configuration error in R.add_patient: trigger would create a doc with an ' + - 'unknown contact type "unknown"'); - }); - - it('add_patient fails if the configured contact type is not a person', () => { - const eventConfig = [{ - form: 'R', - events: [{ - name: 'on_create', - trigger: 'add_patient', - params: '{ "contact_type": "place" }' - }], - }]; - const contactTypes = [{ id: 'place' }]; - - config.get.returns(eventConfig); - config.getAll.returns({ contact_types: contactTypes }); - - transition.init.should.throw(Error, 'Configuration error in R.add_patient: trigger would create a person ' + - 'with a place contact type "place"'); - }); - - it('add_patient fails if the default "person" contact type is not known', () => { - const eventConfig = [{ - form: 'R', - events: [{ - name: 'on_create', - trigger: 'add_patient', - params: '' - }], - }]; - const contactTypes = [{ id: 'place' }]; - - config.get.returns(eventConfig); - config.getAll.returns({ contact_types: contactTypes }); - - transition.init.should.throw(Error, 'Configuration error in R.add_patient: trigger would create a doc ' + - 'with an unknown contact type "person"'); - }); - - it('succeeds for known person type', done => { - const eventConfig = [{ - form: 'R', - events: [{ - name: 'on_create', - trigger: 'add_patient', - params: '{ "contact_type": "patient" }' - }], - }]; - const contactTypes = [{ id: 'patient', person: true }]; - - config.get.returns(eventConfig); - config.getAll.returns({ contact_types: contactTypes }); - - transition.init(); - done(); - }); - - it('add_place should fail for no defined contact_type', () => { - const eventConfig = [{ - form: 'R', - events: [{ - name: 'on_create', - trigger: 'add_place', - params: '' - }], - }]; - const contactTypes = [{ id: 'place' }]; - - config.get.returns(eventConfig); - config.getAll.returns({ contact_types: contactTypes }); - - transition.init.should.throw(Error, 'Configuration error in R.add_place: trigger would create a place ' + - 'with an undefined contact type'); - }); - - it('add_place should fail for unknown contact_type', () => { - const eventConfig = [{ - form: 'R', - events: [{ - name: 'on_create', - trigger: 'add_place', - params: { contact_type: 'oh_noo' } - }], - }]; - const contactTypes = [{ id: 'place' }]; - - config.get.returns(eventConfig); - config.getAll.returns({ contact_types: contactTypes }); - - transition.init.should.throw(Error, 'Configuration error in R.add_place: trigger would create a place ' + - 'with an unknown contact type "oh_noo"'); - }); - - it('add_place should fail for person contact_type', () => { - const eventConfig = [{ - form: 'R', - events: [{ - name: 'on_create', - trigger: 'add_place', - params: { contact_type: 'person' } - }], - }]; - - config.get.returns(eventConfig); - config.getAll.returns(settings); - - transition.init.should.throw(Error, 'Configuration error in R.add_place: trigger would create a place with ' + - 'a person contact type "person"'); - }); - - it('should succeed for known place type', () => { - const eventConfig = [{ - form: 'R', - events: [{ - name: 'on_create', - trigger: 'add_place', - params: { contact_type: 'place' } - }], - }]; - - config.get.returns(eventConfig); - config.getAll.returns(settings); - - transition.init(); - }); - }); - - describe('addMessages', () => { - beforeEach(() => { - transition.addMessages = transition.__get__('addMessages'); - }); - - it('pops and passes the right information to messages.addMessage', () => { - const testPhone = '1234'; - const testMessage1 = { - message: 'A Test Message 1', - recipient: testPhone, - event_type: 'report_accepted', - }; - const testMessage2 = { - message: 'A Test Message 2', - recipient: testPhone, - event_type: 'report_accepted', - }; - const testRegistration = 'some registrations'; - const testPatient = 'a patient contact'; - - const addMessage = sinon.stub(messages, 'addMessage'); - - sinon - .stub(utils, 'getRegistrations') - .withArgs({ id: '12345' }).resolves(testRegistration) - .withArgs({ id: undefined }).resolves([]); - - const testConfig = { messages: [testMessage1, testMessage2] }; - const testDoc = { - fields: { - patient_id: '12345', - }, - patient: testPatient, - }; - - return transition.addMessages(testConfig, testDoc).then(() => { - // Registration will send messages with no event_type - addMessage.callCount.should.equal(2); - - const expectedContext = { - patient: testPatient, - place: undefined, - registrations: testRegistration, - placeRegistrations: [], - templateContext: { - next_msg: { - minutes: 0, - hours: 0, - days: 0, - weeks: 0, - months: 0, - years: 0, - }, - }, - }; - addMessage.args[0].should.deep.equal([ - testDoc, - testMessage1, - testPhone, - expectedContext, - ]); - addMessage.args[1].should.deep.equal([ - testDoc, - testMessage2, - testPhone, - expectedContext, - ]); - - utils.getRegistrations.callCount.should.equal(2); - utils.getRegistrations.args[0].should.deep.equal([{ id: '12345' }]); - utils.getRegistrations.args[1].should.deep.equal([{ id: undefined }]); - }); - }); - - it('pops and passes the right information to messages.addMessage with place_id', () => { - const testPhone = '1234'; - const testMessage1 = { - message: 'A Test Message 1', - recipient: testPhone, - event_type: 'report_accepted', - }; - const testMessage2 = { - message: 'A Test Message 2', - recipient: testPhone, - event_type: 'report_accepted', - }; - const placeRegistrations = [{ _id: 'some registration' }]; - - sinon.stub(messages, 'addMessage'); - sinon.stub(utils, 'getRegistrations') - .withArgs({ id: undefined }).resolves([]) - .withArgs({ id: '65498' }).resolves(placeRegistrations); - - const testConfig = { messages: [testMessage1, testMessage2] }; - const testDoc = { - fields: { - place_id: '65498', - }, - place: { _id: 'test_place', place_id: '65498' }, - }; - - return transition.addMessages(testConfig, testDoc).then(() => { - // Registration will send messages with no event_type - messages.addMessage.callCount.should.equal(2); - - const expectedContext = { - patient: undefined, - place: testDoc.place, - registrations: [], - placeRegistrations: placeRegistrations, - templateContext: { - next_msg: { - minutes: 0, - hours: 0, - days: 0, - weeks: 0, - months: 0, - years: 0, - }, - }, - }; - messages.addMessage.args[0].should.deep.equal([ - testDoc, - testMessage1, - testPhone, - expectedContext, - ]); - messages.addMessage.args[1].should.deep.equal([ - testDoc, - testMessage2, - testPhone, - expectedContext, - ]); - utils.getRegistrations.callCount.should.equal(2); - utils.getRegistrations.args[0].should.deep.equal([{ id: undefined }]); - utils.getRegistrations.args[1].should.deep.equal([{ id: '65498' }]); - }); - }); - - it('pops and passes the right information to messages.addMessage with place_id and patient_id', () => { - const testPhone = '1234'; - const testMessage1 = { - message: 'A Test Message 1', - recipient: testPhone, - event_type: 'report_accepted', - }; - const testMessage2 = { - message: 'A Test Message 2', - recipient: testPhone, - event_type: 'report_accepted', - }; - const patientRegistrations = [{ _id: 'some registration' }]; - const placeRegistrations = [{ _id: 'other registration' }]; - - sinon.stub(messages, 'addMessage'); - sinon.stub(utils, 'getRegistrations') - .onCall(0).resolves(patientRegistrations) - .onCall(1).resolves(placeRegistrations); - - const testConfig = { messages: [testMessage1, testMessage2] }; - const testDoc = { - fields: { - place_id: '999999', - patient_id: '111111' - }, - place: { _id: 'test_place', place_id: '999999' }, - patient: { _id: 'test_patient', place_id: '111111' }, - }; - - return transition.addMessages(testConfig, testDoc).then(() => { - // Registration will send messages with no event_type - messages.addMessage.callCount.should.equal(2); - - const expectedContext = { - patient: testDoc.patient, - place: testDoc.place, - registrations: patientRegistrations, - placeRegistrations: placeRegistrations, - templateContext: { - next_msg: { - minutes: 0, - hours: 0, - days: 0, - weeks: 0, - months: 0, - years: 0, - }, - }, - }; - messages.addMessage.args[0].should.deep.equal([ - testDoc, - testMessage1, - testPhone, - expectedContext, - ]); - messages.addMessage.args[1].should.deep.equal([ - testDoc, - testMessage2, - testPhone, - expectedContext, - ]); - - utils.getRegistrations.callCount.should.equal(2); - utils.getRegistrations.args[0].should.deep.equal([{ id: '111111' }]); - utils.getRegistrations.args[1].should.deep.equal([{ id: '999999' }]); - }); - }); - - it('supports ignoring messages based on bool_expr', () => { - const testPhone = '1234'; - const testMessage1 = { - message: 'A Test Message 1', - recipient: testPhone, - event_type: 'report_accepted', - }; - const testMessage2 = { - message: 'A Test Message 2', - recipient: testPhone, - event_type: 'report_accepted', - bool_expr: '1 === 2', - }; - const testRegistration = 'some registrations'; - const testPatient = 'a patient contact'; - - const addMessage = sinon.stub(messages, 'addMessage'); - - sinon.stub(utils, 'getRegistrations') - .withArgs({ id: '12345' }).resolves(testRegistration) - .withArgs({ id: undefined }).resolves([]); - - const testConfig = { messages: [testMessage1, testMessage2] }; - const testDoc = { - fields: { - patient_id: '12345', - }, - patient: testPatient, - }; - - return transition.addMessages(testConfig, testDoc).then( () => { - addMessage.callCount.should.equal(1); - const expectedContext = { - patient: testPatient, - place: undefined, - registrations: testRegistration, - placeRegistrations: [], - templateContext: { - next_msg: { - minutes: 0, - hours: 0, - days: 0, - weeks: 0, - months: 0, - years: 0, - }, - }, - }; - addMessage.args[0][0].should.equal(testDoc); - addMessage.args[0][1].should.equal(testMessage1); - addMessage.args[0][2].should.equal(testPhone); - addMessage.args[0][3].should.deep.equal(expectedContext); - }); - }); - }); - - describe('clear_schedule', () => { - beforeEach(() => { - transition.triggers = transition.__get__('triggers'); - }); - - it('should work when doc has no patient', () => { - sinon.stub(utils, 'getReportsBySubject').resolves([]); - return transition.triggers.clear_schedule({ doc: {}, params: [] }); - }); - - it('should query utils.getReportsBySubject with correct params with patient', () => { - sinon.stub(utils, 'getReportsBySubject').resolves([]); - sinon.stub(utils, 'getSubjectIds') - .returns(['uuid', 'patient_id']) - .withArgs(undefined).returns([]); - const doc = { patient: { _id: 'uuid', patient_id: 'patient_id' } }; - sinon.stub(acceptPatientReports, 'silenceRegistrations').resolves(); - - return transition.triggers.clear_schedule({ doc, params: [] }).then(() => { - utils.getSubjectIds.callCount.should.equal(2); - utils.getSubjectIds.args[0].should.deep.equal([doc.patient]); - utils.getSubjectIds.args[1].should.deep.equal([doc.place]); - utils.getReportsBySubject.callCount.should.equal(1); - utils.getReportsBySubject.args[0].should.deep.equal([ { - ids: ['uuid', 'patient_id'], - registrations: true - } ]); - }); - }); - - it('should query utils.getReportsBySubject with correct params with place', () => { - sinon.stub(utils, 'getReportsBySubject').resolves([]); - sinon.stub(utils, 'getSubjectIds') - .returns(['uuid', 'place_id']) - .withArgs(undefined).returns([]); - const doc = { place: { _id: 'uuid', place_id: 'place_id' } }; - sinon.stub(acceptPatientReports, 'silenceRegistrations').resolves(); - - return transition.triggers.clear_schedule({ doc, params: [] }).then(() => { - utils.getSubjectIds.callCount.should.equal(2); - utils.getSubjectIds.args[0].should.deep.equal([doc.patient]); - utils.getSubjectIds.args[1].should.deep.equal([doc.place]); - utils.getReportsBySubject.callCount.should.equal(1); - utils.getReportsBySubject.args[0].should.deep.equal([ { - ids: ['uuid', 'place_id'], - registrations: true - } ]); - }); - }); - - it('should query utils.getReportsBySubject with correct params with patient and place', () => { - const doc = { - patient: { _id: 'uuid', patient_id: 'patient_id' }, - place: { _id: 'place_uuid', place_id: 'place_id' }, - }; - sinon.stub(utils, 'getReportsBySubject').resolves([]); - sinon.stub(utils, 'getSubjectIds') - .withArgs(doc.patient).returns(['uuid', 'patient_id']) - .withArgs(doc.place).returns(['place_uuid', 'place_id']); - sinon.stub(acceptPatientReports, 'silenceRegistrations').resolves(); - - return transition.triggers.clear_schedule({ doc, params: [] }).then(() => { - utils.getSubjectIds.callCount.should.equal(2); - utils.getSubjectIds.args[0].should.deep.equal([doc.patient]); - utils.getSubjectIds.args[1].should.deep.equal([doc.place]); - utils.getReportsBySubject.callCount.should.equal(1); - utils.getReportsBySubject.args[0].should.deep.equal([ { - ids: ['uuid', 'patient_id', 'place_uuid', 'place_id'], - registrations: true - } ]); - }); - }); - - it('should call silenceRegistrations with correct params', () => { - const doc = { _id: 'uuid', patient_id: 'patient_id' }; - const params = ['1', '2', '3', '4']; - const registrations = ['a', 'b', 'c']; - sinon.stub(utils, 'getReportsBySubject').resolves(registrations); - sinon.stub(utils, 'getSubjectIds').returns(['uuid', 'patient_id']); - sinon.stub(acceptPatientReports, 'silenceRegistrations').resolves(); - - return transition.triggers.clear_schedule({ doc, params }).then(() => { - acceptPatientReports.silenceRegistrations.callCount.should.equal(1); - acceptPatientReports.silenceRegistrations.args[0][0].should.deep.equal({ - silence_type: '1,2,3,4', - silence_for: null - }); - acceptPatientReports.silenceRegistrations.args[0][1].should.deep.equal(doc); - acceptPatientReports.silenceRegistrations.args[0][2].should.deep.equal(registrations); - }); - }); - - it('should catch getReportsBySubject errors', () => { - sinon.stub(utils, 'getReportsBySubject').rejects({ some: 'error' }); - sinon.stub(utils, 'getSubjectIds').returns([]); - sinon.stub(acceptPatientReports, 'silenceRegistrations'); - - return transition.triggers - .clear_schedule({ doc: {}, params: [] }) - .then(r => r.should.deep.equal('Should have thrown')) - .catch((err) => { - err.should.deep.equal({ some: 'error' }); - utils.getReportsBySubject.callCount.should.equal(1); - utils.getSubjectIds.callCount.should.equal(2); - acceptPatientReports.silenceRegistrations.callCount.should.equal(0); - }); - }); - - it('should catch silenceRegistrations errors', () => { - sinon.stub(utils, 'getReportsBySubject').resolves([]); - sinon.stub(utils, 'getSubjectIds').returns([]); - sinon.stub(acceptPatientReports, 'silenceRegistrations').rejects({ some: 'err' }); - return transition.triggers - .clear_schedule({ doc: {}, params: [] }) - .then(r => r.should.deep.equal('Should have thrown')) - .catch(err => { - err.should.deep.equal({ some: 'err' }); - utils.getReportsBySubject.callCount.should.equal(1); - utils.getSubjectIds.callCount.should.equal(2); - acceptPatientReports.silenceRegistrations.callCount.should.equal(1); + } }); - }); - }); - - describe('onMatch', () => { - let fireConfiguredTriggers; - const registrationConfig = { - form: 'R', - events: [ - { - name: 'on_create', - trigger: 'assign_schedule', - params: 'myschedule', - }, - ], - validations: { list: ['validation!!'] }, - }; - let doc; - - beforeEach(() => { - transition.__set__('fireConfiguredTriggers', sinon.stub().resolves(true)); - fireConfiguredTriggers = transition.__get__('fireConfiguredTriggers'); - config.get.withArgs('registrations').returns([registrationConfig]); - }); - - it('should adds error if validation fails, with patient subject', () => { - sinon.stub(validation, 'validate').resolves(['error']); - sinon.stub(messages, 'addErrors'); - - doc = { - form: 'R', - patient: { _id: 'patient' }, - }; - - return transition.onMatch({ doc }).then((result) => { - result.should.equal(true); - fireConfiguredTriggers.callCount.should.equal(0); - validation.validate.callCount.should.equal(1); - validation.validate.args[0].slice(0, 2).should.deep.equal([ doc, ['validation!!'] ]); - messages.addErrors.callCount.should.equal(1); - messages.addErrors.args[0].should.deep.equal([ - registrationConfig, - doc, - ['error'], - { patient: doc.patient, place: undefined }, - ]); - }); - }); - - it('should adds error if validation fails, with place subject', () => { - sinon.stub(validation, 'validate').resolves(['an error']); - sinon.stub(messages, 'addErrors'); - - doc = { - form: 'R', - place: { _id: 'place' }, - }; - - return transition.onMatch({ doc }).then((result) => { - result.should.equal(true); - fireConfiguredTriggers.callCount.should.equal(0); - validation.validate.callCount.should.equal(1); - messages.addErrors.callCount.should.equal(1); - messages.addErrors.args[0].should.deep.equal([ - registrationConfig, - doc, - ['an error'], - { patient: undefined, place: doc.place }, - ]); - }); - }); - - it('should add error if validation fails, with both patient and place subjects', () => { - sinon.stub(validation, 'validate').resolves(['some error']); - sinon.stub(messages, 'addErrors'); - - doc = { - form: 'R', - patient: { _id: 'patient', patient_id: 'patient_id' }, - place: { _id: 'place', place_id: 'place_id' }, - fields: { patient_id: 'patient_id', place_id: 'place_id' } - }; - - return transition.onMatch({ doc }).then((result) => { - result.should.equal(true); - fireConfiguredTriggers.callCount.should.equal(0); - validation.validate.callCount.should.equal(1); - messages.addErrors.callCount.should.equal(1); - messages.addErrors.args[0].should.deep.equal([ - registrationConfig, - doc, - ['some error'], - { patient: doc.patient, place: doc.place }, - ]); - }); - }); - - it('should run triggers when there is no subject', () => { - sinon.stub(validation, 'validate').resolves(); - sinon.stub(messages, 'addErrors'); - - doc = { form: 'R' }; - - return transition.onMatch({ doc }).then((result) => { - result.should.equal(true); - fireConfiguredTriggers.callCount.should.equal(1); - fireConfiguredTriggers.args[0].should.deep.equal([registrationConfig, doc]); - validation.validate.callCount.should.equal(1); - messages.addErrors.callCount.should.equal(0); - }); - }); - - it('should fail if subject does not exist, with patient_id', () => { - sinon.stub(validation, 'validate').resolves(); - doc = { form: 'R', fields: { patient_id: '56987' } }; - - sinon.stub(transitionUtils, 'addRegistrationNotFoundError'); - - return transition.onMatch({ doc }).then(result => { - result.should.equal(true); - fireConfiguredTriggers.callCount.should.equal(0); - transitionUtils.addRegistrationNotFoundError.callCount.should.equal(1); - transitionUtils.addRegistrationNotFoundError.args[0].should.deep.equal([doc, registrationConfig]); - }); - }); - - it('should fail if patient exists but its not a person', () => { - sinon.stub(validation, 'validate').resolves(); - doc = { - form: 'R', - fields: { patient_id: '56987' }, - patient: { - _id: 'clinic', - place_id: '56987', - type: 'clinic', + } + }); + }, + filter: ({ doc, info }) => { + return Boolean( + doc.type === 'data_record' && + getRegistrationConfig(getConfig(), doc.form) && + !transitionUtils.hasRun(info, NAME) && + utils.isValidSubmission(doc) // requires either an xform, a known submitter or public form for SMS + ); + }, + onMatch: change => { + const doc = change.doc; + const registrationConfig = getRegistrationConfig(getConfig(), doc.form); + + return transitionUtils + .validate(registrationConfig, doc) + .then(errors => { + if (errors && errors.length > 0) { + messages.addErrors(registrationConfig, doc, errors, { patient: doc.patient, place: doc.place }); + return true; } - }; - sinon.stub(contactTypeUtils, 'isPerson').returns(false); - sinon.stub(transitionUtils, 'addRegistrationNotFoundError'); - - return transition.onMatch({ doc }).then(result => { - result.should.equal(true); - fireConfiguredTriggers.callCount.should.equal(0); - transitionUtils.addRegistrationNotFoundError.callCount.should.equal(1); - transitionUtils.addRegistrationNotFoundError.args[0].should.deep.equal([doc, registrationConfig]); - contactTypeUtils.isPerson.callCount.should.equal(1); - contactTypeUtils.isPerson.args[0].should.deep.equal([{}, { _id: 'clinic', place_id: '56987', type: 'clinic' }]); - }); - }); - - it('should fail if subject does not exist, with place_id', () => { - sinon.stub(validation, 'validate').resolves(); - doc = { form: 'R', fields: { place_id: '98754' } }; - sinon.stub(transitionUtils, 'addRegistrationNotFoundError'); - return transition.onMatch({ doc }).then(result => { - result.should.equal(true); - fireConfiguredTriggers.callCount.should.equal(0); - transitionUtils.addRegistrationNotFoundError.callCount.should.equal(1); - transitionUtils.addRegistrationNotFoundError.args[0].should.deep.equal([doc, registrationConfig]); - }); - }); + const patientId = doc.fields && doc.fields.patient_id; + const placeId = doc.fields && doc.fields.place_id; - it('should fail if place exists but its not a place', () => { - sinon.stub(validation, 'validate').resolves(); - doc = { - form: 'R', - fields: { place_id: '56987' }, - place: { - _id: 'person', - patient_id: '56987', - type: 'person', + if (!patientId && !placeId) { + return fireConfiguredTriggers(registrationConfig, doc); } - }; - sinon.stub(contactTypeUtils, 'isPlace').returns(false); - sinon.stub(transitionUtils, 'addRegistrationNotFoundError'); - - return transition.onMatch({ doc }).then(result => { - result.should.equal(true); - fireConfiguredTriggers.callCount.should.equal(0); - transitionUtils.addRegistrationNotFoundError.callCount.should.equal(1); - transitionUtils.addRegistrationNotFoundError.args[0].should.deep.equal([doc, registrationConfig]); - contactTypeUtils.isPlace.callCount.should.equal(1); - contactTypeUtils.isPlace.args[0].should.deep.equal([{}, {_id: 'person', patient_id: '56987', type: 'person' }]); - }); - }); - - it('should fail if subject does not exist, with place_id and patient_id', () => { - sinon.stub(validation, 'validate').resolves(); - doc = { form: 'R', fields: { place_id: 'the_place_id', patient_id: 'the_patient_id' } }; - sinon.stub(transitionUtils, 'addRegistrationNotFoundError'); - - return transition.onMatch({ doc }).then(result => { - result.should.equal(true); - fireConfiguredTriggers.callCount.should.equal(0); - transitionUtils.addRegistrationNotFoundError.callCount.should.equal(1); - transitionUtils.addRegistrationNotFoundError.args[0].should.deep.equal([doc, registrationConfig]); - }); - }); - - it('should fail if place and patient exist, but are switched', () => { - sinon.stub(validation, 'validate').resolves(); - doc = { - form: 'R', - fields: { place_id: '56987', patient_id: '69874' }, - place: { - _id: 'person', - patient_id: '56987', - type: 'person', - }, - patient: { - _id: 'place', - patient_id: '69874', - type: 'clinic', - }, - }; - - sinon.stub(contactTypeUtils, 'isPerson').returns(false); - sinon.stub(transitionUtils, 'addRegistrationNotFoundError'); - - return transition.onMatch({ doc }).then(result => { - result.should.equal(true); - fireConfiguredTriggers.callCount.should.equal(0); - transitionUtils.addRegistrationNotFoundError.callCount.should.equal(1); - transitionUtils.addRegistrationNotFoundError.args[0].should.deep.equal([doc, registrationConfig]); - contactTypeUtils.isPerson.callCount.should.equal(1); - contactTypeUtils.isPerson.args[0].should.deep.equal([ - {}, - { _id: 'place', patient_id: '69874', type: 'clinic' } - ]); - }); - }); - - it('should fail if place and patient exist, place is a place but patient is not a person', () => { - sinon.stub(validation, 'validate').resolves(); - doc = { - form: 'R', - fields: { place_id: '56987', patient_id: '69874' }, - place: { - _id: 'place', - place_id: '56987', - type: 'some_place', - }, - patient: { - _id: 'place', - place_id: '69874', - type: 'clinic', - }, - }; - - sinon.stub(contactTypeUtils, 'isPerson').returns(false); - sinon.stub(contactTypeUtils, 'isPlace').returns(true); - sinon.stub(transitionUtils, 'addRegistrationNotFoundError'); - - return transition.onMatch({ doc }).then(result => { - result.should.equal(true); - fireConfiguredTriggers.callCount.should.equal(0); - transitionUtils.addRegistrationNotFoundError.callCount.should.equal(1); - transitionUtils.addRegistrationNotFoundError.args[0].should.deep.equal([doc, registrationConfig]); - contactTypeUtils.isPerson.callCount.should.equal(1); - contactTypeUtils.isPerson.args[0].should.deep.equal([{}, { _id: 'place', place_id: '69874', type: 'clinic' }]); - }); - }); - - it('should fail if place and patient exist, patient is a person but place is not a place', () => { - sinon.stub(validation, 'validate').resolves(); - doc = { - form: 'R', - fields: { place_id: '56987', patient_id: '69874' }, - place: { - _id: 'person', - patient_id: '56987', - type: 'person', - }, - patient: { - _id: 'patient', - patient_id: '69874', - type: 'person', - }, - }; - - sinon.stub(contactTypeUtils, 'isPerson').returns(true); - sinon.stub(contactTypeUtils, 'isPlace').returns(false); - sinon.stub(transitionUtils, 'addRegistrationNotFoundError'); - - return transition.onMatch({ doc }).then(result => { - result.should.equal(true); - fireConfiguredTriggers.callCount.should.equal(0); - transitionUtils.addRegistrationNotFoundError.callCount.should.equal(1); - transitionUtils.addRegistrationNotFoundError.args[0].should.deep.equal([doc, registrationConfig]); - contactTypeUtils.isPerson.callCount.should.equal(1); - contactTypeUtils.isPerson.args[0].should.deep.equal([ - {}, - { _id: 'patient', patient_id: '69874', type: 'person' } - ]); - contactTypeUtils.isPlace.callCount.should.equal(1); - contactTypeUtils.isPlace.args[0].should.deep.equal([ - {}, - { _id: 'person', patient_id: '56987', type: 'person' } - ]); - }); - }); - - it('should run triggers when subject exists', () => { - sinon.stub(validation, 'validate').resolves(); - doc = { - form: 'R', - fields: { patient_id: 'the_patient_id' }, - patient: { patient_id: 'the_patient_id', type: 'person', _id: 'some uuid' } - }; - sinon.stub(transitionUtils, 'addRegistrationNotFoundError'); + // We're attaching this registration to an existing contact, let's + // make sure it's valid + if (!hasValidSubject(doc, patientId, placeId)) { + transitionUtils.addRegistrationNotFoundError(doc, registrationConfig); + return true; + } - return transition.onMatch({ doc }).then(result => { - result.should.equal(true); - fireConfiguredTriggers.callCount.should.equal(1); - fireConfiguredTriggers.args[0].should.deep.equal([registrationConfig, doc]); - transitionUtils.addRegistrationNotFoundError.callCount.should.equal(0); + return fireConfiguredTriggers(registrationConfig, doc); }); - }); - }); -}); + }, +}; diff --git a/tests/integration/sentinel/transitions/registration.spec.js b/tests/integration/sentinel/transitions/registration.spec.js index 66f8ae0e09e..280e99843a2 100644 --- a/tests/integration/sentinel/transitions/registration.spec.js +++ b/tests/integration/sentinel/transitions/registration.spec.js @@ -32,7 +32,7 @@ const contacts = [ place_id: 'the_clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } }, contact: { - _id: 'person', parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + _id: 'person', parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } }, reported_date: new Date().getTime() }, @@ -113,7 +113,7 @@ describe('registration', () => { }], }] }], - forms: { FORM: { }} + forms: { FORM: {} } }; const doc = { @@ -123,7 +123,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -152,7 +152,7 @@ describe('registration', () => { }], }] }], - forms: { FORM: { }} + forms: { FORM: {} } }; const doc = { @@ -162,7 +162,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -211,7 +211,7 @@ describe('registration', () => { }] } }], - forms: { FORM: { }} + forms: { FORM: {} } }; const noSubjects = { // doesn't patient_id or place_id fields @@ -225,7 +225,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -241,7 +241,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -257,11 +257,11 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; - const docs = [ noSubjects, noPatient, noPlace ]; + const docs = [noSubjects, noPatient, noPlace]; const docIds = getIds(docs); return utils @@ -332,7 +332,7 @@ describe('registration', () => { }], }], }], - forms: { 'FORM-A': { }} + forms: { 'FORM-A': {} } }; const placeInsteadOfPatient = { // has a patient_id field containing shortcode corresponding to a place @@ -346,7 +346,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -361,7 +361,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -377,7 +377,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -393,7 +393,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -409,7 +409,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -429,14 +429,14 @@ describe('registration', () => { .then(() => sentinelUtils.getInfoDocs(allIds)) .then(infos => { infos.forEach((info, idx) => { - const errorMsg = `failed for doc${idx+1}`; + const errorMsg = `failed for doc${idx + 1}`; chai.expect(info).to.deep.nested.include({ 'transitions.registration.ok': true }, errorMsg); }); }) .then(() => utils.getDocs(allIds)) .then(updated => { updated.forEach((doc, idx) => { - const errorMsg = `failed for doc${idx+1}`; + const errorMsg = `failed for doc${idx + 1}`; chai.expect(doc.tasks).to.have.lengthOf(1, errorMsg); chai.expect(doc.tasks[0].messages[0]).to.include( { message: 'Subject not found or invalid', to: '+444999' }, @@ -486,7 +486,7 @@ describe('registration', () => { }], }], }], - forms: { 'FORM-A': { }, 'FORM-B': { }} + forms: { 'FORM-A': {}, 'FORM-B': {} } }; const justPatientName = { // has just the `patient_name` field, and should create this person @@ -500,7 +500,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -516,7 +516,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -532,7 +532,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -548,7 +548,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -711,7 +711,7 @@ describe('registration', () => { }], }], }], - forms: { 'FORM-A': { }, 'FORM-B': { }, 'FORM-CHW': {}, 'FORM-NURSE': {}}, + forms: { 'FORM-A': {}, 'FORM-B': {}, 'FORM-CHW': {}, 'FORM-NURSE': {} }, contact_types: [...defaultSettings.contact_types, chwContactType, nurseContactType] }; @@ -857,6 +857,26 @@ describe('registration', () => { }); }); + it('should create new person with when correct phone number is supplied', () => { + + }); + + it('should not create new person when invalid phone number is supplied', () => { + + }); + + it('should append country code to to provided phone number during patient registration', () => { + + }); + + it('should append nothing if country code is not available to phone number', () => { + + }); + + it('should only accept numbers for the phone number field', () => { + + }); + it('should create people with selected parent', () => { const settings = { transitions: { registration: true }, @@ -912,7 +932,7 @@ describe('registration', () => { }] }], }], - forms: { 'FORM-PERSON': { }, 'FORM-CHW': {}, 'FORM-NURSE': {}}, + forms: { 'FORM-PERSON': {}, 'FORM-CHW': {}, 'FORM-NURSE': {} }, contact_types: [...defaultSettings.contact_types, chwContactType, nurseContactType] }; @@ -954,7 +974,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -1045,7 +1065,7 @@ describe('registration', () => { }], messages: [], }], - forms: { FORM: { }} + forms: { FORM: {} } }; const withWeeksSinceLMP = { @@ -1060,7 +1080,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -1076,7 +1096,7 @@ describe('registration', () => { reported_date: moment().subtract(2, 'weeks').valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -1092,7 +1112,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -1144,7 +1164,7 @@ describe('registration', () => { }], messages: [], }], - forms: { FORM: { }} + forms: { FORM: {} } }; const withMonthsSinceBirth = { @@ -1159,7 +1179,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -1175,7 +1195,7 @@ describe('registration', () => { reported_date: moment().subtract(2, 'weeks').valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -1191,11 +1211,11 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; - const docs = [ withMonthsSinceBirth, withWeeksSinceBirth, withAgeInYears ]; + const docs = [withMonthsSinceBirth, withWeeksSinceBirth, withAgeInYears]; const docIds = getIds(docs); return utils @@ -1231,7 +1251,7 @@ describe('registration', () => { }], messages: [], }], - forms: { 'FORM-PLACE': { } }, + forms: { 'FORM-PLACE': {} }, }; const createPlace = { @@ -1345,7 +1365,7 @@ describe('registration', () => { }], }], }], - forms: { 'FORM-CLINIC_NO_PARENT': { }, 'FORM-CLINIC': { }, 'FORM-HEALTH_CENTER': {} }, + forms: { 'FORM-CLINIC_NO_PARENT': {}, 'FORM-CLINIC': {}, 'FORM-HEALTH_CENTER': {} }, }; const clinicNoParentUnderClinic = { @@ -1359,7 +1379,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -1486,34 +1506,34 @@ describe('registration', () => { chai.expect(updated[1].errors[0]).to.deep.equal({ code: 'parent_invalid', message: 'Cannot create a place type "clinic" under parent ' + - 'the_district_hospital(contact type district_hospital)', + 'the_district_hospital(contact type district_hospital)', }); chai.expect(updated[1].tasks[0].messages[0]).to.include({ to: '+00000000', message: 'Cannot create a place type "clinic" under parent ' + - 'the_district_hospital(contact type district_hospital)', + 'the_district_hospital(contact type district_hospital)', }); chai.expect(updated[2].errors[0]).to.deep.equal({ code: 'parent_invalid', message: 'Cannot create a place type "clinic" under parent ' + - 'the_clinic(contact type clinic)', + 'the_clinic(contact type clinic)', }); chai.expect(updated[2].tasks[0].messages[0]).to.include({ to: '+11111111', message: 'Cannot create a place type "clinic" under parent ' + - 'the_clinic(contact type clinic)', + 'the_clinic(contact type clinic)', }); chai.expect(updated[3].errors[0]).to.deep.equal({ code: 'parent_invalid', message: 'Cannot create a place type "clinic" under parent ' + - 'the_district_hospital(contact type district_hospital)', + 'the_district_hospital(contact type district_hospital)', }); chai.expect(updated[3].tasks[0].messages[0]).to.include({ to: '+11111111', message: 'Cannot create a place type "clinic" under parent ' + - 'the_district_hospital(contact type district_hospital)', + 'the_district_hospital(contact type district_hospital)', }); chai.expect(updated[4].errors[0]).to.deep.equal({ @@ -1537,12 +1557,12 @@ describe('registration', () => { chai.expect(updated[6].errors[0]).to.deep.equal({ code: 'parent_invalid', message: 'Cannot create a place type "health_center" under parent ' + - 'the_clinic(contact type clinic)', + 'the_clinic(contact type clinic)', }); chai.expect(updated[6].tasks[0].messages[0]).to.include({ to: '+11111111', message: 'Cannot create a place type "health_center" under parent ' + - 'the_clinic(contact type clinic)', + 'the_clinic(contact type clinic)', }); return getContactsByReference(updated.map(doc => doc.place_id)); @@ -1651,7 +1671,7 @@ describe('registration', () => { }], }], }], - forms: { 'FORM-CLINIC_NO_PARENT': { }, 'FORM-NURSING_HOME': { }, 'FORM-HEALTH_CENTER': {} }, + forms: { 'FORM-CLINIC_NO_PARENT': {}, 'FORM-NURSING_HOME': {}, 'FORM-HEALTH_CENTER': {} }, contact_types: [ ...defaultSettings.contact_types, nursingHomeType, @@ -1696,7 +1716,7 @@ describe('registration', () => { contact: { _id: 'supervisor', parent: { _id: 'district_hospital' } } }; - const docs = [ clinicNoParent, nursingHome, healthCenter ]; + const docs = [clinicNoParent, nursingHome, healthCenter]; const ids = getIds(docs); let updatedDocs; @@ -1807,7 +1827,7 @@ describe('registration', () => { message: [{ locale: 'en', content: 'Cannot create a place type "clinic" under parent ' + - '{{parent.place_id}}(contact type {{parent.contact_type}})' + '{{parent.place_id}}(contact type {{parent.contact_type}})' }], }], }, @@ -1833,12 +1853,12 @@ describe('registration', () => { message: [{ locale: 'en', content: 'Cannot create a person type "person" under parent ' + - '{{parent.place_id}}(contact type {{parent.contact_type}})' + '{{parent.place_id}}(contact type {{parent.contact_type}})' }], }], }, ], - forms: { 'FORM-CLINIC': { public_form: true }, 'FORM-PERSON': { public_form: true }}, + forms: { 'FORM-CLINIC': { public_form: true }, 'FORM-PERSON': { public_form: true } }, }; const createClinic = { @@ -1944,7 +1964,7 @@ describe('registration', () => { }], messages: [], }], - forms: { FORM: { }}, + forms: { FORM: {} }, schedules: [{ name: 'sch1', start_from: 'some_date_field', @@ -2010,7 +2030,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; @@ -2104,8 +2124,8 @@ describe('registration', () => { chai.expect(infoWithPatient).to.deep.nested.include({ 'transitions.registration.ok': true }); chai.expect(infoWithClinic).to.deep.nested.include({ 'transitions.registration.ok': true }); }) - .then(() => utils.getDocs([ withPatient1._id, withPatient2._id, withClinic1._id, withClinic2._id ])) - .then(([ updWithPatient1, updWithPatient2, updWithClinic1, updWithClinic2 ]) => { + .then(() => utils.getDocs([withPatient1._id, withPatient2._id, withClinic1._id, withClinic2._id])) + .then(([updWithPatient1, updWithPatient2, updWithClinic1, updWithClinic2]) => { //1st doc has cleared schedules chai.expect(updWithPatient1.scheduled_tasks).to.be.ok; chai.expect(updWithPatient1.scheduled_tasks).to.have.lengthOf(3); @@ -2140,8 +2160,8 @@ describe('registration', () => { .then((infodoc) => { chai.expect(infodoc).to.deep.nested.include({ 'transitions.registration.ok': true }); }) - .then(() => utils.getDocs([ withPatient2._id, withClinic2._id, withClinicAndPatient1._id])) - .then(([ updWithPatient2, updWithClinic2, withClinicAndPatient ]) => { + .then(() => utils.getDocs([withPatient2._id, withClinic2._id, withClinicAndPatient1._id])) + .then(([updWithPatient2, updWithClinic2, withClinicAndPatient]) => { // cleared schedules for the withPatient doc chai.expect(updWithPatient2.scheduled_tasks).to.be.ok; chai.expect(updWithPatient2.scheduled_tasks).to.have.lengthOf(3); @@ -2175,7 +2195,7 @@ describe('registration', () => { withClinic1._id, withClinic2._id, withClinicAndPatient1._id, ])) - .then(([updWithClinicAndPatient2, ...docsWithClearedTasks ]) => { + .then(([updWithClinicAndPatient2, ...docsWithClearedTasks]) => { // withPatientAndClinic has schedules chai.expect(updWithClinicAndPatient2.scheduled_tasks).to.be.ok; chai.expect(updWithClinicAndPatient2.scheduled_tasks).to.have.lengthOf(3); @@ -2229,7 +2249,7 @@ describe('registration', () => { }], }], }], - forms: { FORM: { }}, + forms: { FORM: {} }, }; const doc = { @@ -2245,7 +2265,7 @@ describe('registration', () => { reported_date: moment().valueOf(), contact: { _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; From 0db854d47a4d4edae0c56a6dc29bb32fe3074a56 Mon Sep 17 00:00:00 2001 From: Prajwol Shrestha Date: Tue, 16 May 2023 10:48:40 +0545 Subject: [PATCH 02/56] Reverting registration.js tests --- .../test/unit/transitions/registration.js | 3585 +++++++++++++---- 1 file changed, 2902 insertions(+), 683 deletions(-) diff --git a/shared-libs/transitions/test/unit/transitions/registration.js b/shared-libs/transitions/test/unit/transitions/registration.js index 5fa337b8aab..4725b86b80b 100644 --- a/shared-libs/transitions/test/unit/transitions/registration.js +++ b/shared-libs/transitions/test/unit/transitions/registration.js @@ -1,722 +1,2941 @@ -const _ = require('lodash'); -const utils = require('../lib/utils'); -const transitionUtils = require('./utils'); -const logger = require('../lib/logger'); -const db = require('../db'); -const lineage = require('@medic/lineage')(Promise, db.medic); -const messages = require('../lib/messages'); -const schedules = require('../lib/schedules'); -const acceptPatientReports = require('./accept_patient_reports'); -const moment = require('moment'); -const config = require('../config'); -const date = require('../date'); - -const contactTypesUtils = require('@medic/contact-types-utils'); - -const NAME = 'registration'; -const PARENT_NOT_FOUND = 'parent_not_found'; -const PARENT_FIELD_NOT_PROVIDED = 'parent_field_not_provided'; -const PARENT_INVALID = 'parent_invalid'; - -const findFirstDefinedValue = (doc, fields) => { - const definedField = fields.find(field => doc.fields[field] !== undefined && doc.fields[field] !== null); - return definedField && doc.fields[definedField]; -}; - -const getPatientNameField = (params) => getNameField(params, 'patient'); -const getPlaceNameField = (params) => getNameField(params, 'place'); - -const getNameField = (params, prefix) => { - if (Array.isArray(params) && params.length && params[0]) { - return params[0]; - } - - const nameField = `${prefix}_name_field`; - if (params && params[nameField]) { - return params[nameField]; - } - - const defaultNameField = `${prefix}_name`; - return defaultNameField; -}; - -const parseParams = params => { - if (!params) { - return {}; - } - if (params instanceof Object) { - // We support raw JSON even if we can't specify that - // correctly in app_settings - return params; - } - const firstCharacter = params.trim()[0]; - if (firstCharacter === '[' || firstCharacter === '{') { - // We support JSON in a string, eg: '{"foo": "bar"}' - return JSON.parse(params); - } - // And comma delimted strings, eg: "foo,bar", "foo" - return params.split(','); -}; - -const booleanExpressionFails = (doc, expr) => { - let result = false; - - if (utils.isNonEmptyString(expr)) { - try { - result = !utils.evalExpression(expr, { doc: doc }); - } catch (err) { - // TODO should this count as a fail or as a real error - logger.warn('Failed to eval boolean expression:'); - logger.info(err.toString()); - result = true; - } - } - - return result; -}; - -const getDOB = doc => { - if (!doc || !doc.fields) { - return ''; - } - const reportedDate = moment(doc.reported_date).startOf('day'); - const years = parseInt(getYearsSinceDOB(doc), 10); - if (!isNaN(years)) { - return reportedDate.subtract(years, 'years'); - } - const months = parseInt(getMonthsSinceDOB(doc), 10); - if (!isNaN(months)) { - return reportedDate.subtract(months, 'months'); - } - const weeks = parseInt(getWeeksSinceDOB(doc), 10); - if (!isNaN(weeks)) { - return reportedDate.subtract(weeks, 'weeks'); - } - const days = parseInt(getDaysSinceDOB(doc), 10); - if (!isNaN(days)) { - return reportedDate.subtract(days, 'days'); - } - // no given date of birth - return reportedDate as it's the best we can do - return reportedDate; -}; - -const getYearsSinceDOB = doc => { - const fields = ['years_since_dob', 'years_since_birth', 'age_in_years']; - return findFirstDefinedValue(doc, fields); -}; - -const getMonthsSinceDOB = doc => { - const fields = ['months_since_dob', 'months_since_birth', 'age_in_months']; - return findFirstDefinedValue(doc, fields); -}; - -const getWeeksSinceDOB = doc => { - const fields = [ - 'weeks_since_dob', - 'dob', - 'weeks_since_birth', - 'age_in_weeks', - ]; - return findFirstDefinedValue(doc, fields); -}; - -const getDaysSinceDOB = doc => { - const fields = ['days_since_dob', 'days_since_birth', 'age_in_days']; - return findFirstDefinedValue(doc, fields); -}; - -/* - * Given a doc get the LMP value as a number, including 0. Supports three - * property names atm. - */ -const getWeeksSinceLMP = doc => { - const props = ['weeks_since_lmp', 'last_menstrual_period', 'lmp']; - for (const prop of props) { - const lmp = Number(doc.fields && doc.fields[prop]); - if (!isNaN(lmp)) { - return lmp; - } - } -}; - -/* -* Given a doc, try to get the exact LMP date -*/ -const getLMPDate = doc => { - const props = ['lmp_date', 'date_lmp']; - for (const prop of props) { - const lmp = doc.fields && doc.fields[prop] && parseInt(doc.fields[prop]); - if (!isNaN(lmp)) {//milliseconds since epoch - return lmp; - } - } -}; - -const setExpectedBirthDate = doc => { - let start; - const lmpDate = getLMPDate(doc); - if (lmpDate) { - start = moment(lmpDate); - } else { - const lmp = getWeeksSinceLMP(doc); - if (lmp) { - start = moment(doc.reported_date).startOf('day'); - start.subtract(lmp, 'weeks'); - } - } - - if (!start) {// means baby was already born, chw just wants a registration. - doc.lmp_date = null; - doc.expected_date = null; - return; - } - - doc.lmp_date = start.toISOString(); - doc.expected_date = start - .clone() - .add(40, 'weeks') - .toISOString(); -}; - -const setBirthDate = doc => { - const dob = getDOB(doc); - if (dob) { - doc.birth_date = dob.toISOString(); - } -}; - -const getPhoneNumber = doc => { - if (!doc || !doc.fields) { - return ''; - } - return doc.fields.phone_number; -}; - -const setPhoneNumber = doc => { - const phoneNumber = getPhoneNumber(doc); - // get default country code - //countryCode = codeFromSettings() - doc.phone_number = countryCode.concat(phoneNumber.toString()); -}; - -const getConfig = () => config.get('registrations'); - -/* - * Given a form code and config array, return config for that form. - */ -const getRegistrationConfig = (config, formCode) => { - return config.find(conf => utils.isFormCodeSame(formCode, conf.form)); -}; - -const triggers = { - add_patient: (options) => { - // if we already have a patient id then return - if (options.doc.patient_id) { - return; - } - - return setPatientId(options).then(() => addPatient(options)); - }, - add_place: (options) => { - // if we already have a place id then return - if (options.doc.place_id) { - return; - } - - return setPlaceId(options).then(() => addPlace(options)); - }, - add_case: (options) => { - return setCaseId(options).then(() => addPlaceId(options)); - }, - add_patient_id: (options) => { - // Deprecated name for add_patient - logger.warn('Use of add_patient_id trigger. This is deprecated in favour of add_patient.'); - return triggers.add_patient(options); - }, - add_phone_number: (options) => { - return setPhoneNumber(options.doc); - }, - add_expected_date: (options) => { - return setExpectedBirthDate(options.doc); - }, - add_birth_date: (options) => { - return setBirthDate(options.doc); - }, - assign_schedule: (options) => { - return assignSchedule(options); - }, - clear_schedule: (options) => { - // Registration forms that clear schedules do so fully - // silence_type will be split again later, so join them back - const config = { - silence_type: options.params.join(','), - silence_for: null, +require('chai').should(); +const sinon = require('sinon'); +const rewire = require('rewire'); +const db = require('../../../src/db'); +const messages = require('../../../src/lib/messages'); +const utils = require('../../../src/lib/utils'); +const config = require('../../../src/config'); +const validation = require('@medic/validation'); +const contactTypeUtils = require('@medic/contact-types-utils'); + +let schedules; +let transitionUtils; +let acceptPatientReports; +let transition; +let settings; + +describe('registration', () => { + beforeEach(() => { + config.init({ + getAll: sinon + .stub() + .returns({}), + get: sinon.stub(), + getTranslations: sinon + .stub() + .returns({}) + }); + + schedules = require('../../../src/lib/schedules'); + transitionUtils = require('../../../src/transitions/utils'); + acceptPatientReports = require('../../../src/transitions/accept_patient_reports'); + transition = rewire('../../../src/transitions/registration'); + settings = { + contact_types: [ + { id: 'place' }, + { id: 'person', person: true, parents: ['place'] }, + ] }; + }); - const subjectIds = [ - ...utils.getSubjectIds(options.doc.patient), - ...utils.getSubjectIds(options.doc.place), - ]; - const caseId = options.doc.case_id || - (options.doc.fields && options.doc.fields.case_id); - if (caseId) { - subjectIds.push(caseId); - } - - return utils - .getReportsBySubject({ ids: subjectIds, registrations: true }) - .then(registrations => acceptPatientReports.silenceRegistrations(config, options.doc, registrations)); - }, -}; - -const fireConfiguredTriggers = (registrationConfig, doc) => { - const promises = registrationConfig.events - .map(event => { - const trigger = triggers[event.trigger]; - if (!trigger || event.name !== 'on_create') { - return; - } - - const obj = _.defaults({}, doc, doc.fields); - - if (booleanExpressionFails(obj, event.bool_expr)) { - return; - } - - const options = { - doc, - registrationConfig, - params: parseParams(event.params), - }; - logger.debug( - `Parsed params for form "${registrationConfig.form}", trigger "${event.trigger}, params: ${options.params}"` - ); - return () => trigger(options); - }) - .filter(item => !!item); - - return promises - .reduce((promise, trigger) => promise.then(trigger), Promise.resolve()) - .then(() => addMessages(registrationConfig, doc)) - .then(() => true); -}; - -// NB: this is very similar to a function in accept_patient_reports, except -// we also allow for an empty event_type -const messageRelevant = (msg, doc) => { - if (!msg.event_type || msg.event_type === 'report_accepted') { - const expr = msg.bool_expr; - if (utils.isNonEmptyString(expr)) { - return utils.evalExpression(expr, { doc: doc }); - } else { - return true; - } - } -}; - -const addMessages = (config, doc) => { - const patientId = doc.fields && doc.fields.patient_id; - const placeId = doc.fields && doc.fields.place_id; - if (!config.messages || !config.messages.length) { - return; - } - - return Promise - .all([ - utils.getRegistrations({ id: patientId }), - utils.getRegistrations({ id: placeId }), - ]) - .then(([patientRegistrations, placeRegistrations]) => { - const context = { - patient: doc.patient, - place: doc.place, - registrations: patientRegistrations, - placeRegistrations: placeRegistrations, - templateContext: { - next_msg: schedules.getNextTimes(doc, moment(date.getDate())), + afterEach(done => { + sinon.reset(); + sinon.restore(); + done(); + }); + + describe('booleanExpressionFails', () => { + beforeEach(() => { + transition.booleanExpressionFails = transition.__get__('booleanExpressionFails'); + }); + + it('is false if there is no valid expression', () => { + transition.booleanExpressionFails({}, '').should.equal(false); + }); + + it('is true if the expresison fails', () => { + transition.booleanExpressionFails({ foo: 'bar' }, `doc.foo !== 'bar'`).should.equal(true); + }); + + it('is true if the expression is invalid', () => { + // TODO: should this error instead of just returning false? + // If there is a typo we're not going to know about it + transition.booleanExpressionFails({}, `doc.foo.bar === 'smang'`).should.equal(true); + }); + }); + + describe('addPatient', () => { + it('trigger creates a new patient', () => { + const patientName = 'jack'; + const submitterId = 'abc'; + const parentId = 'papa'; + const patientId = '05649'; + const reportId = 'def'; + const senderPhoneNumber = '+555123'; + const dob = '2017-03-31T01:15:09.000Z'; + const change = { + doc: { + _id: reportId, + type: 'data_record', + form: 'R', + reported_date: 53, + from: senderPhoneNumber, + fields: { patient_name: patientName }, + birth_date: dob, }, }; + const getContactUuid = sinon.stub(utils, 'getContactUuid').resolves(); + // return expected view results when searching for contacts_by_phone + const view = sinon.stub(db.medic, 'query').resolves({ + rows: [ + { + doc: { + _id: submitterId, + parent: { _id: parentId }, + }, + }, + ], + }); + sinon.stub(db.medic, 'get').withArgs('papa').resolves({ _id: parentId, type: 'contact', contact_type: 'place' }); + const saveDoc = sinon.stub(db.medic, 'post').resolves(); + const eventConfig = { + form: 'R', + events: [{ name: 'on_create', trigger: 'add_patient' }], + }; + config.get.returns([eventConfig]); + sinon.stub(validation, 'validate').resolves(null); + sinon.stub(utils, 'getRegistrations').resolves([]); + sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); + config.getAll.returns(settings); + + return transition.onMatch(change).then(() => { + getContactUuid.callCount.should.equal(1); + view.callCount.should.equal(1); + view.args[0][0].should.equal('medic-client/contacts_by_phone'); + view.args[0][1].key.should.equal(senderPhoneNumber); + view.args[0][1].include_docs.should.equal(true); + saveDoc.callCount.should.equal(1); + saveDoc.args[0][0].name.should.equal(patientName); + saveDoc.args[0][0].parent._id.should.equal(parentId); + saveDoc.args[0][0].reported_date.should.equal(53); + saveDoc.args[0][0].type.should.equal('person'); + saveDoc.args[0][0].patient_id.should.equal(patientId); + saveDoc.args[0][0].date_of_birth.should.equal(dob); + saveDoc.args[0][0].source_id.should.equal(reportId); + saveDoc.args[0][0].created_by.should.equal(submitterId); + }); + }); - config.messages.forEach(msg => { - if (messageRelevant(msg, doc)) { - messages.addMessage(doc, msg, msg.recipient, context); - } + it('does nothing when patient already added', () => { + const patientId = '05649'; + const change = { + doc: { + type: 'data_record', + form: 'R', + patient_id: patientId, + reported_date: 53, + from: '+555123', + fields: { patient_name: 'jack' }, + }, + }; + sinon + .stub(db.medic, 'query') + .resolves({ + rows: [{ doc: { parent: { _id: 'papa' } } }], + }); + const saveDoc = sinon.stub(db.medic, 'post').resolves(); + const eventConfig = { + form: 'R', + events: [{ name: 'on_create', trigger: 'add_patient_id' }], + }; + config.get.returns([eventConfig]); + config.getAll.returns(settings); + sinon.stub(validation, 'validate').resolves(null); + return transition.onMatch(change).then(() => { + saveDoc.callCount.should.equal(0); }); }); -}; - -const assignSchedule = (options) => { - const patientId = options.doc.fields && options.doc.fields.patient_id; - const placeId = options.doc.fields && options.doc.fields.place_id; - - return Promise - .all([ - utils.getRegistrations({ id: patientId }), - utils.getRegistrations({ id: placeId }), - ]) - .then(([patientRegistrations, placeRegistrations]) => { - options.params.forEach(scheduleName => { - const schedule = schedules.getScheduleConfig(scheduleName); - const context = { - patientRegistrations, - patient: options.doc.patient, - placeRegistrations, - place: options.doc.place - }; - schedules.assignSchedule(options.doc, schedule, context); - }); - }); -}; - -const generateId = (doc, key) => { - return transitionUtils.getUniqueId().then(id => doc[key] = id); -}; - -const setPlaceId = ({ doc }) => generateId(doc, 'place_id'); -const setCaseId = ({ doc }) => { - if (doc.case_id) { - return Promise.resolve(); - } - return generateId(doc, 'case_id'); -}; - -const addPlaceId = ({ doc }) => { - const placeId = doc.contact && doc.contact.parent && doc.contact.parent._id; - if (!placeId) { - return; - } - if (!doc.fields) { - doc.fields = {}; - } - doc.fields.place_uuid = placeId; -}; - -const setPatientId = (options) => { - const doc = options.doc; - const patientIdField = options.params.patient_id_field; - - if (patientIdField) { - const providedId = doc.fields[options.params.patient_id_field]; - - if (!providedId) { - transitionUtils.addRejectionMessage(doc, options.registrationConfig, 'no_provided_patient_id'); - return Promise.resolve(); - } - - return transitionUtils - .isIdUnique(providedId) - .then(isUnique => { - if (isUnique) { - doc.patient_id = providedId; - return; + + it('uses a given id if configured to', () => { + const patientId = '05648'; + const doc = { + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+555123', + fields: { patient_name: 'jack', external_id: patientId }, + birth_date: '2017-03-31T01:15:09.000Z', + }; + const change = { doc: doc }; + sinon.stub(utils, 'getContactUuid').resolves(); + // return expected view results when searching for contacts_by_phone + sinon + .stub(db.medic, 'query') + .resolves({ + rows: [{ doc: { parent: { _id: 'papa' } } }], + }); + sinon.stub(db.medic, 'get').withArgs('papa').resolves({ _id: 'papa', type: 'place' }); + const saveDoc = sinon.stub(db.medic, 'post').resolves(1); + const eventConfig = { + form: 'R', + events: [ + { + name: 'on_create', + trigger: 'add_patient_id', + params: '{"patient_id_field": "external_id"}', + }, + ], + }; + config.get.returns([eventConfig]); + sinon.stub(validation, 'validate').resolves(); + sinon.stub(transitionUtils, 'isIdUnique').resolves(true); + config.getAll.returns(settings); + + return transition.onMatch(change).then(() => { + saveDoc.args[0][0].patient_id.should.equal(patientId); + doc.patient_id.should.equal(patientId); + (typeof doc.errors).should.equal('undefined'); + }); + }); + + it('trigger creates a new contact with the given type', () => { + const change = { + doc: { + _id: 'def', + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+555123', + fields: { patient_name: 'jack' }, + birth_date: '2017-03-31T01:15:09.000Z', + }, + }; + sinon.stub(utils, 'getContactUuid').resolves(); + // return expected view results when searching for contacts_by_phone + sinon.stub(db.medic, 'query').resolves({ + rows: [ + { + doc: { + _id: 'abc', + parent: { _id: 'papa' }, + }, + }, + ], + }); + sinon.stub(db.medic, 'get').withArgs('papa').resolves({ _id: 'papa', type: 'place' }); + const saveDoc = sinon.stub(db.medic, 'post').resolves(); + const eventConfig = { + form: 'R', + events: [{ + name: 'on_create', + trigger: 'add_patient', + params: '{ "contact_type": "patient" }' + }], + }; + config.get.returns([eventConfig]); + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations').resolves([]); + sinon.stub(transitionUtils, 'getUniqueId').resolves('05649'); + config.getAll.returns({ + contact_types: [ + { id: 'place' }, + { id: 'patient', person: true, parents: ['place'] }, + ] + }); + + return transition.onMatch(change).then(() => { + saveDoc.callCount.should.equal(1); + saveDoc.args[0][0].type.should.equal('contact'); + saveDoc.args[0][0].contact_type.should.equal('patient'); + }); + }); + + it('errors if the configuration does not point to an id', () => { + const patientId = '05648'; + const doc = { + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+555123', + fields: { patient_name: 'jack', external_id: patientId }, + birth_date: '2017-03-31T01:15:09.000Z', + }; + const change = { doc: doc }; + sinon.stub(utils, 'getContactUuid').resolves(); + // return expected view results when searching for contacts_by_phone + sinon + .stub(db.medic, 'query') + .resolves({ + rows: [{ doc: { parent: { _id: 'papa' } } }], + }); + sinon.stub(db.medic, 'get').withArgs('papa').resolves({ _id: 'papa', type: 'place' }); + sinon.stub(db.medic, 'post').resolves(); + const eventConfig = { + form: 'R', + events: [ + { + name: 'on_create', + trigger: 'add_patient', + params: '{"patient_id_field": "not_the_external_id"}', + }, + ], + }; + const configGet = config.get; + configGet.withArgs('outgoing_deny_list').returns(''); + configGet.returns([eventConfig]); + config.getAll.returns(settings); + + sinon.stub(validation, 'validate').resolves(); + + return transition.onMatch(change).then(() => { + (typeof doc.patient_id).should.equal('undefined'); + doc.errors.should.deep.equal([ + { + message: 'messages.generic.no_provided_patient_id', + code: 'no_provided_patient_id', + }, + ]); + }); + }); + + it('errors if the given id is not unique', () => { + const patientId = '05648'; + const doc = { + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+555123', + fields: { patient_name: 'jack', external_id: patientId }, + birth_date: '2017-03-31T01:15:09.000Z', + }; + const change = { doc: doc }; + sinon.stub(utils, 'getContactUuid').resolves(); + // return expected view results when searching for contacts_by_phone + sinon + .stub(db.medic, 'query') + .resolves({ + rows: [{ doc: { parent: { _id: 'papa' } } }], + }); + sinon.stub(db.medic, 'get').withArgs('papa').resolves({ _id: 'papa', type: 'place' }); + sinon.stub(db.medic, 'post').resolves(); + const eventConfig = { + form: 'R', + events: [ + { + name: 'on_create', + trigger: 'add_patient', + params: '{"patient_id_field": "external_id"}', + }, + ], + }; + const configGet = config.get; + configGet.withArgs('outgoing_deny_list').returns(''); + configGet.returns([eventConfig]); + + sinon.stub(transitionUtils, 'isIdUnique').resolves(false); + + sinon.stub(validation, 'validate').resolves(); + config.getAll.returns(settings); + + return transition.onMatch(change).then(() => { + (typeof doc.patient_id).should.be.equal('undefined'); + doc.errors.should.deep.equal([ + { + message: 'messages.generic.provided_patient_id_not_unique', + code: 'provided_patient_id_not_unique', + }, + ]); + }); + }); + + it('event parameter overwrites the default property for the name of the patient', () => { + const patientName = 'jack'; + const submitterId = 'papa'; + const patientId = '05649'; + const senderPhoneNumber = '+555123'; + const dob = '2017-03-31T01:15:09.000Z'; + const change = { + doc: { + type: 'data_record', + form: 'R', + reported_date: 53, + from: senderPhoneNumber, + fields: { name: patientName }, + birth_date: dob, + }, + }; + sinon.stub(utils, 'getContactUuid').resolves(); + // return expected view results when searching for contacts_by_phone + sinon + .stub(db.medic, 'query') + .resolves({ + rows: [{ doc: { parent: { _id: submitterId } } }], + }); + sinon.stub(db.medic, 'get').withArgs('papa').resolves({ _id: 'papa', type: 'place' }); + const saveDoc = sinon.stub(db.medic, 'post').resolves(); + const eventConfig = { + form: 'R', + events: [{ name: 'on_create', trigger: 'add_patient', params: 'name' }], + }; + config.get.returns([eventConfig]); + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations').resolves([]); + + sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); + config.getAll.returns(settings); + + return transition.onMatch(change).then(() => { + saveDoc.callCount.should.equal(1); + saveDoc.args[0][0].name.should.equal(patientName); + }); + }); + + it('event parameter overwrites the default property for the name of the patient using JSON config', () => { + const patientName = 'jack'; + const submitterId = 'papa'; + const patientId = '05649'; + const senderPhoneNumber = '+555123'; + const dob = '2017-03-31T01:15:09.000Z'; + const change = { + doc: { + type: 'data_record', + form: 'R', + reported_date: 53, + from: senderPhoneNumber, + fields: { name: patientName }, + birth_date: dob, + }, + }; + sinon.stub(utils, 'getContactUuid').resolves(); + // return expected view results when searching for contacts_by_phone + sinon + .stub(db.medic, 'query') + .resolves({ + rows: [{ doc: { parent: { _id: submitterId } } }], + }); + sinon.stub(db.medic, 'get').withArgs('papa').resolves({ _id: 'papa', type: 'place' }); + const saveDoc = sinon.stub(db.medic, 'post').resolves(); + const eventConfig = { + form: 'R', + events: [ + { + name: 'on_create', + trigger: 'add_patient', + params: '{"patient_name_field": "name"}', + }, + ], + }; + config.get.returns([eventConfig]); + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations').resolves([]); + + sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); + config.getAll.returns(settings); + + return transition.onMatch(change).then(() => { + saveDoc.callCount.should.equal(1); + saveDoc.args[0][0].name.should.equal(patientName); + }); + }); + + it('add_patient and add_patient_id triggers are idempotent', () => { + const patientName = 'jack'; + const submitterId = 'papa'; + const patientId = '05649'; + const senderPhoneNumber = '+555123'; + const dob = '2017-03-31T01:15:09.000Z'; + const change = { + doc: { + type: 'data_record', + form: 'R', + reported_date: 53, + from: senderPhoneNumber, + fields: { name: patientName }, + birth_date: dob, + }, + }; + sinon.stub(utils, 'getContactUuid').resolves(); + // return expected view results when searching for contacts_by_phone + sinon + .stub(db.medic, 'query') + .resolves({ + rows: [{ doc: { parent: { _id: submitterId } } }], + }); + sinon.stub(db.medic, 'get').withArgs('papa').resolves({ _id: 'papa', type: 'place' }); + const saveDoc = sinon.stub(db.medic, 'post').resolves(); + const eventConfig = { + form: 'R', + events: [ + { name: 'on_create', trigger: 'add_patient', params: 'name' }, + { name: 'on_create', trigger: 'add_patient_id', params: 'name' }, + ], + }; + config.get.returns([eventConfig]); + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations').resolves([]); + + sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); + config.getAll.returns(settings); + + + return transition.onMatch(change).then(() => { + saveDoc.callCount.should.equal(1); + saveDoc.args[0][0].name.should.equal(patientName); + }); + }); + + it('fails when patient_id_field is set to patient_id', () => { + const eventConfig = [ + { + form: 'R', + events: [ + { + name: 'on_create', + trigger: 'add_patient', + params: '{ "patient_id_field": "patient_id" }', + }, + ], + }, + ]; + + config.get.returns(eventConfig); + transition.init.should.throw( + Error, + 'Configuration error in R.add_patient: patient_id_field cannot be set to patient_id' + ); + }); + + it('should add configured type of person', () => { + const change = { + doc: { + _id: 'reportID', + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+111222', + fields: { patient_name: 'Bob' }, + contact: { + _id: 'pete', + name: 'Pete', + contact_type: 'chw', + type: 'contact', + parent: { + _id: 'petes', + name: 'Petes Place', + contact_type: 'clinic', + type: 'contact', + parent: { _id: 'west_hc', name: 'west hc', contact_type: 'health_center', type: 'contact' } + } + }, } + }; + const patientId = 'my_patient_id'; + sinon.stub(utils, 'getContactUuid').resolves(); + sinon.stub(db.medic, 'post').resolves(); + const eventConfig = { + form: 'R', + events: [{ name: 'on_create', trigger: 'add_patient', params: { contact_type: 'patient' } }], + messages: [{ + recipient: 'reporting_unit', + event_type: 'report_accepted', + bool_expr: 'doc.patient', + message: [ + { + locale: 'en', + content: 'Patient {{patient_name}} with type {{patient.contact_type}} was added to ' + + '{{patient.parent.name}}({{patient.parent.contact_type}})' + } + ] + }] + }; + const contactTypes = [ + { id: 'health_center' }, + { id: 'clinic', parents: ['health_center'] }, + { id: 'patient', parents: ['clinic'], person: true }, + ]; + config.get.returns([eventConfig]); + config.getAll.returns({ contact_types: contactTypes }); + + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations').resolves([]); + sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); + + return transition.onMatch(change).then(() => { + transitionUtils.getUniqueId.callCount.should.equal(1); + utils.getContactUuid.callCount.should.equal(1); + utils.getContactUuid.args[0].should.deep.equal([patientId]); + db.medic.post.callCount.should.equal(1); + db.medic.post.args[0].should.deep.equal([{ + name: 'Bob', + patient_id: patientId, + source_id: change.doc._id, + type: 'contact', + contact_type: 'patient', + parent: { _id: 'petes', parent: { _id: 'west_hc' } }, + created_by: 'pete', + reported_date: change.doc.reported_date, + }]); + (!!change.doc.errors).should.equal(false); + change.doc.tasks.length.should.equal(1); + change.doc.tasks[0].messages[0].should.include({ + to: change.doc.from, + message: 'Patient Bob with type patient was added to Petes Place(clinic)' + }); + }); + }); - transitionUtils.addRejectionMessage(doc, options.registrationConfig, 'provided_patient_id_not_unique'); - }); - } else { - return generateId(doc, 'patient_id'); - } -}; - -const getParentByPhone = options => { - return db.medic - .query('medic-client/contacts_by_phone', { key: options.doc.from, include_docs: true }) - .then(result => result && result.rows && result.rows.length && result.rows[0].doc) - .then(contact => { - if (!contact) { - return; - } - - options.doc.contact = contact; - return contact.parent && db.medic.get(contact.parent._id); - }); -}; - -// returns the parent of the contact to be created (place or patient) -const getParent = options => { - // if the `parent_id` fields name is not specified in the trigger, default to the submitter's parent - if (!options.params.parent_id) { - // if `update_clinics` runs, the `contact` property already exists - if (options.doc.contact && options.doc.contact.parent) { - return Promise.resolve(JSON.parse(JSON.stringify(options.doc.contact.parent))); - } - - // when `update_clinics` is not enabled (for some reason), fallback to getting contact by phone - return getParentByPhone(options).then(parent => { - if (!parent) { - transitionUtils.addRejectionMessage(options.doc, options.registrationConfig, PARENT_NOT_FOUND); - return; - } - - return parent; - }); - } - - // if the `parent_id` field name is specified in the trigger, but is not defined in the doc, add an error - if (!options.doc.fields[options.params.parent_id]) { - transitionUtils.addRejectionMessage(options.doc, options.registrationConfig, PARENT_FIELD_NOT_PROVIDED); - return Promise.resolve(); - } - - return utils.getContact(options.doc.fields[options.params.parent_id]).then(parent => { - if (!parent) { - transitionUtils.addRejectionMessage(options.doc, options.registrationConfig, PARENT_NOT_FOUND); - return; - } - - return parent; - }); -}; - -const isValidParent = (parent, child) => { - const parentType = contactTypesUtils.getContactType(config.getAll(), parent); - const childType = contactTypesUtils.getContactType(config.getAll(), child); - return contactTypesUtils.isParentOf(parentType, childType); -}; - -const addPatient = (options) => { - const doc = options.doc; - const patientShortcode = options.doc.patient_id; - const patientNameField = getPatientNameField(options.params); - - // create a new patient with this patient_id - const patient = { - name: doc.fields[patientNameField], - reported_date: doc.reported_date, - patient_id: patientShortcode, - source_id: doc._id, - }; - - if (options.params.contact_type) { - patient.type = 'contact'; - patient.contact_type = options.params.contact_type; - } else { - patient.type = 'person'; - } - - return utils - .getContactUuid(patientShortcode) - .then(patientContactId => { - if (patientContactId) { - doc.patient_id = patientShortcode; - return; - } - - return getParent(options).then(parent => { - if (!parent) { - return; + it('should add person with selected parent', () => { + const change = { + doc: { + _id: 'reportID', + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+111222', + fields: { buddy_name: 'Marcel', parent: 'georges_place' }, + contact: { + _id: 'pete', + name: 'Pete', + contact_type: 'chw', + type: 'contact', + parent: { + _id: 'petes', + name: 'Petes Place', + place_id: 'petes_place', + type: 'contact', + contact_type: 'area_type_1', + parent: { + _id: 'west_hc', name: 'west hc', contact_type: 'health_center', + type: 'contact', place_id: 'the_west_hc' + } + }, + }, } + }; + const parent = { + _id: 'georges', + name: 'Georges Place', + type: 'contact', + place_id: 'georges_place', + contact_type: 'area_type_2', + parent: { _id: 'west_hc' }, + }; + const patientId = 'my_patient_id'; + sinon.stub(utils, 'getContactUuid').resolves(); + sinon.stub(utils, 'getContact').resolves(parent); + sinon.stub(db.medic, 'post').resolves(); + + const eventConfig = { + form: 'R', + events: [{ + name: 'on_create', trigger: 'add_patient', + params: { contact_type: 'buddy', patient_name_field: 'buddy_name', parent_id: 'parent' } + }], + messages: [{ + recipient: 'reporting_unit', + event_type: 'report_accepted', + bool_expr: 'doc.patient', + message: [ + { + locale: 'en', + content: 'Friend {{buddy_name}} with type {{patient.contact_type}} was added to ' + + '{{patient.parent.name}}({{patient.parent.contact_type}})' + } + ] + }] + }; + const contactTypes = [ + { id: 'health_center' }, + { id: 'area_type_1', parents: ['health_center'] }, + { id: 'area_type_2', parents: ['health_center'] }, + { id: 'patient', parents: ['area_type_1'], person: true }, + { id: 'buddy', parents: ['area_type_2'], person: true }, + ]; + config.get.returns([eventConfig]); + config.getAll.returns({ contact_types: contactTypes }); + + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations').resolves([]); + sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); + + return transition.onMatch(change).then(() => { + transitionUtils.getUniqueId.callCount.should.equal(1); + utils.getContactUuid.callCount.should.equal(1); + utils.getContactUuid.args[0].should.deep.equal([patientId]); + utils.getContact.callCount.should.equal(1); + utils.getContact.args[0].should.deep.equal([change.doc.fields.parent]); + db.medic.post.callCount.should.equal(1); + db.medic.post.args[0].should.deep.equal([{ + name: 'Marcel', + patient_id: patientId, + source_id: change.doc._id, + type: 'contact', + contact_type: 'buddy', + parent: { _id: 'georges', parent: { _id: 'west_hc' } }, + created_by: 'pete', + reported_date: change.doc.reported_date, + }]); + (!!change.doc.errors).should.equal(false); + change.doc.tasks.length.should.equal(1); + change.doc.tasks[0].messages[0].should.include({ + to: change.doc.from, + message: 'Friend Marcel with type buddy was added to Georges Place(area_type_2)' + }); + }); + }); - if (!isValidParent(parent, patient)) { - transitionUtils.addRejectionMessage( - options.doc, - options.registrationConfig, - PARENT_INVALID, - { templateContext: { parent } } - ); - return; + it('should not create person when parent is required but not specified', () => { + const change = { + doc: { + _id: 'reportID', + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+111222', + fields: { patient_name: 'Patricia', parent_id: 'not_the_correct_parent_field' }, + contact: { + _id: 'pete', + name: 'Pete', + contact_type: 'chw', + type: 'contact', + parent: { + _id: 'petes', + name: 'Petes Place', + place_id: 'petes_place', + type: 'contact', + contact_type: 'area_type_1', + parent: { + _id: 'west_hc', name: 'west hc', contact_type: 'health_center', + type: 'contact', place_id: 'the_west_hc' + } + }, + }, } + }; + + const patientId = 'my_patient_id'; + sinon.stub(utils, 'getContactUuid').resolves(); + sinon.stub(utils, 'getContact'); + sinon.stub(db.medic, 'post').resolves(); + + const eventConfig = { + form: 'R', + events: [{ + name: 'on_create', trigger: 'add_patient', params: { contact_type: 'patient', parent_id: 'parent' } + }], + messages: [{ + recipient: 'reporting_unit', + event_type: 'parent_field_not_provided', + message: [ + { + locale: 'en', + content: 'Cannot create patient without parent' + } + ] + }] + }; + const contactTypes = [ + { id: 'health_center' }, + { id: 'area_type_1', parents: ['health_center'] }, + { id: 'area_type_2', parents: ['health_center'] }, + { id: 'patient', parents: ['area_type_1'], person: true }, + ]; + config.get.returns([eventConfig]); + config.getAll.returns({ contact_types: contactTypes }); + + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations').resolves([]); + sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); + + return transition.onMatch(change).then(result => { + result.should.equal(true); + transitionUtils.getUniqueId.callCount.should.equal(1); + utils.getContactUuid.callCount.should.equal(1); + utils.getContactUuid.args[0].should.deep.equal([patientId]); + utils.getContact.callCount.should.equal(0); + db.medic.post.callCount.should.equal(0); + change.doc.errors.length.should.equal(1); + change.doc.errors[0].should.deep.equal({ + message: 'Cannot create patient without parent', + code: 'parent_field_not_provided', + }); + change.doc.tasks.length.should.equal(1); + change.doc.tasks[0].messages[0].should.include({ + to: change.doc.from, + message: 'Cannot create patient without parent' + }); + }); + }); - // include the DOB if it was generated on report - if (doc.birth_date) { - patient.date_of_birth = doc.birth_date; + it('should not create person when parent is required and missing', () => { + const change = { + doc: { + _id: 'reportID', + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+111222', + fields: { patient_name: 'Patricia', parent_id: 'non-existent-contact' }, + contact: { + _id: 'pete', + name: 'Pete', + contact_type: 'chw', + type: 'contact', + parent: { + _id: 'petes', + name: 'Petes Place', + place_id: 'petes_place', + type: 'contact', + contact_type: 'area_type_1', + parent: { + _id: 'west_hc', name: 'west hc', contact_type: 'health_center', + type: 'contact', place_id: 'the_west_hc' + } + }, + }, } + }; - // assign patient in doc with full parent doc - to be used in messages - doc.patient = Object.assign({ parent }, patient); - patient.parent = lineage.minifyLineage(parent); - patient.created_by = doc.contact && doc.contact._id; - - return db.medic.post(patient); - }); - }); -}; - -const addPlace = (options) => { - const doc = options.doc; - const placeShortcode = doc.place_id; - const placeNameField = getPlaceNameField(options.params); - - // create a new place with this place_id - const place = { - name: doc.fields[placeNameField], - reported_date: doc.reported_date, - place_id: placeShortcode, - source_id: doc._id, - }; - - if (contactTypesUtils.isHardcodedType(options.params.contact_type)) { - place.type = options.params.contact_type; - } else { - place.type = 'contact'; - place.contact_type = options.params.contact_type; - } - - return utils - .getContactUuid(placeShortcode) - .then(placeContactId => { - if (placeContactId) { - doc.place_id = placeShortcode; - return; - } - - return getParent(options).then(parent => { - if (!parent) { - return; + const patientId = 'my_patient_id'; + sinon.stub(utils, 'getContactUuid').resolves(); + sinon.stub(utils, 'getContact').resolves(); + sinon.stub(db.medic, 'post').resolves(); + + const eventConfig = { + form: 'R', + events: [{ + name: 'on_create', trigger: 'add_patient', params: { contact_type: 'patient', parent_id: 'parent_id' } + }], + messages: [{ + recipient: 'reporting_unit', + event_type: 'parent_not_found', + message: [ + { + locale: 'en', + content: 'Selected parent {{fields.parent_id}} does not exists.' + } + ] + }] + }; + const contactTypes = [ + { id: 'health_center' }, + { id: 'area_type_1', parents: ['health_center'] }, + { id: 'area_type_2', parents: ['health_center'] }, + { id: 'patient', parents: ['area_type_1'], person: true }, + ]; + config.get.returns([eventConfig]); + config.getAll.returns({ contact_types: contactTypes }); + + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations').resolves([]); + sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); + + return transition.onMatch(change).then(result => { + result.should.equal(true); + transitionUtils.getUniqueId.callCount.should.equal(1); + utils.getContactUuid.callCount.should.equal(1); + utils.getContactUuid.args[0].should.deep.equal([patientId]); + utils.getContact.callCount.should.equal(1); + utils.getContact.args[0].should.deep.equal([change.doc.fields.parent_id]); + db.medic.post.callCount.should.equal(0); + change.doc.errors.length.should.equal(1); + change.doc.errors[0].should.deep.equal({ + message: 'Selected parent non-existent-contact does not exists.', + code: 'parent_not_found', + }); + change.doc.tasks.length.should.equal(1); + change.doc.tasks[0].messages[0].should.include({ + to: change.doc.from, + message: 'Selected parent non-existent-contact does not exists.' + }); + }); + }); + + it('should not create person when parent is invalid', () => { + const change = { + doc: { + _id: 'reportID', + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+111222', + fields: { buddy_name: 'Marcel', the_parent_field: 'georges_place' }, + contact: { + _id: 'pete', + name: 'Pete', + contact_type: 'chw', + type: 'contact', + parent: { + _id: 'petes', + name: 'Petes Place', + place_id: 'petes_place', + type: 'contact', + contact_type: 'area_type_1', + parent: { + _id: 'west_hc', name: 'west hc', contact_type: 'health_center', + type: 'contact', place_id: 'the_west_hc' + } + }, + }, } + }; + const parent = { + _id: 'georges', + name: 'Georges Place', + type: 'contact', + place_id: 'georges_place', + contact_type: 'area_type_1', + parent: { _id: 'west_hc' }, + }; + const patientId = 'my_patient_id'; + sinon.stub(utils, 'getContactUuid').resolves(); + sinon.stub(utils, 'getContact').resolves(parent); + sinon.stub(db.medic, 'post').resolves(); + + const eventConfig = { + form: 'R', + events: [{ + name: 'on_create', trigger: 'add_patient', params: { contact_type: 'patient', parent_id: 'the_parent_field' } + }], + messages: [{ + recipient: 'reporting_unit', + event_type: 'parent_invalid', + message: [ + { + locale: 'en', + content: 'Cannot create patient under parent {{parent.name}}({{parent.place_id}}) ' + + 'of type {{parent.contact_type}}.' + } + ] + }] + }; + const contactTypes = [ + { id: 'health_center' }, + { id: 'area_type_1', parents: ['health_center'] }, + { id: 'area_type_2', parents: ['health_center'] }, + { id: 'patient', parents: ['area_type_2'], person: true }, + ]; + config.get.returns([eventConfig]); + config.getAll.returns({ contact_types: contactTypes }); + + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations').resolves([]); + sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); + + return transition.onMatch(change).then(result => { + result.should.equal(true); + transitionUtils.getUniqueId.callCount.should.equal(1); + utils.getContactUuid.callCount.should.equal(1); + utils.getContactUuid.args[0].should.deep.equal([patientId]); + utils.getContact.callCount.should.equal(1); + utils.getContact.args[0].should.deep.equal([change.doc.fields.the_parent_field]); + db.medic.post.callCount.should.equal(0); + change.doc.errors.length.should.equal(1); + change.doc.errors[0].should.deep.equal({ + message: 'Cannot create patient under parent Georges Place(georges_place) of type area_type_1.', + code: 'parent_invalid', + }); + change.doc.tasks.length.should.equal(1); + change.doc.tasks[0].messages[0].should.include({ + to: change.doc.from, + message: 'Cannot create patient under parent Georges Place(georges_place) of type area_type_1.' + }); + }); + }); + }); - if (!isValidParent(parent, place)) { - transitionUtils.addRejectionMessage( - options.doc, - options.registrationConfig, - PARENT_INVALID, - { templateContext: { parent } } - ); - return; + describe('addPlace', () => { + it('should add place with correct hardcoded type under correct parent', () => { + const change = { + doc: { + _id: 'reportID', + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+111222', + fields: { place_name: 'new clinic', parent_id: 'north_hc_place' }, + contact: { + _id: 'pete', + name: 'Pete', + contact_type: 'chw', + type: 'contact', + parent: { + _id: 'petes', + name: 'Petes Place', + contact_type: 'clinic', + type: 'contact', + parent: { + _id: 'west_hc', + name: 'west hc', + contact_type: 'health_center', + type: 'contact', + place_id: 'south_hc_place', + } + } + }, } + }; + const placeId = 'my_place_id'; + const parent = { + _id: 'north_hc', + name: 'north hc', + type: 'contact', + contact_type: 'health_center', + place_id: 'north_hc_place', + }; + sinon.stub(utils, 'getContactUuid').resolves(); + sinon.stub(utils, 'getContact').resolves(parent); + sinon.stub(db.medic, 'post').resolves(); + const eventConfig = { + form: 'R', + events: [{ + name: 'on_create', trigger: 'add_place', params: { contact_type: 'clinic', parent_id: 'parent_id' } + }], + messages: [{ + recipient: 'reporting_unit', + event_type: 'report_accepted', + bool_expr: 'doc.place', + message: [ + { + locale: 'en', + content: 'Place {{place_name}} with type {{place.type}} was added to ' + + '{{place.parent.name}}({{place.parent.contact_type}})' + } + ] + }] + }; + const contactTypes = [ + { id: 'health_center' }, + { id: 'clinic', parents: ['health_center'] }, + { id: 'patient', parents: ['clinic'], person: true }, + ]; + config.get.returns([eventConfig]); + config.getAll.returns({ contact_types: contactTypes }); + + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations').resolves([]); + sinon.stub(transitionUtils, 'getUniqueId').resolves(placeId); + + return transition.onMatch(change).then(() => { + transitionUtils.getUniqueId.callCount.should.equal(1); + utils.getContactUuid.callCount.should.equal(1); + utils.getContactUuid.args[0].should.deep.equal([placeId]); + utils.getContact.callCount.should.equal(1); + utils.getContact.args[0].should.deep.equal([change.doc.fields.parent_id]); + db.medic.post.callCount.should.equal(1); + db.medic.post.args[0].should.deep.equal([{ + name: 'new clinic', + place_id: placeId, + source_id: change.doc._id, + type: 'clinic', + parent: { _id: 'north_hc' }, + created_by: 'pete', + reported_date: change.doc.reported_date, + }]); + (!!change.doc.errors).should.equal(false); + change.doc.tasks.length.should.equal(1); + change.doc.tasks[0].messages[0].should.include({ + to: change.doc.from, + message: 'Place new clinic with type clinic was added to north hc(health_center)' + }); + }); + }); - // assign place in doc with full parent doc - to be used in messages - doc.place = Object.assign({ parent }, place); - place.parent = lineage.minifyLineage(parent); - place.created_by = doc.contact && doc.contact._id; - - return db.medic.post(place); - }); - }); -}; - -const hasValidSubject = (doc, patientId, placeId) => { - // doc is already hydrated. - if (patientId && (!doc.patient || (doc.patient && !contactTypesUtils.isPerson(config.getAll(), doc.patient)))) { - return false; - } - - if (placeId && (!doc.place || (doc.place && !contactTypesUtils.isPlace(config.getAll(), doc.place)))) { - return false; - } - - return true; -}; - -module.exports = { - name: NAME, - init: () => { - const registrations = getConfig(); - registrations.forEach(registration => { - if (registration.events) { - registration.events.forEach(event => { - let params; - try { - params = parseParams(event.params); - } catch (e) { - throw new Error( - `Configuration error. Unable to parse params for ${registration.form}.${event.trigger}: ` + - `'${event.params}'. Error: ${e}` - ); - } + it('should add place with right configurable type and submitter parent when parent_id param not specified', () => { + const change = { + doc: { + _id: 'reportID', + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+111222', + fields: { place_name: 'new clinic' }, + contact: { + _id: 'supervisor', + name: 'Frank', + contact_type: 'supervisor', + type: 'contact', + parent: { + _id: 'west_hc', + name: 'west hc', + place_id: 'west_hc_place', + contact_type: 'health_center_1', + type: 'contact', + } + }, + } + }; + const placeId = 'my_place_id'; + sinon.stub(utils, 'getContactUuid').resolves(); + sinon.stub(utils, 'getContact'); + sinon.stub(db.medic, 'post').resolves(); + const eventConfig = { + form: 'R', + events: [{ name: 'on_create', trigger: 'add_place', params: { contact_type: 'clinic_1' } }], + messages: [{ + recipient: 'reporting_unit', + event_type: 'report_accepted', + bool_expr: 'doc.place', + message: [ + { + locale: 'en', + content: 'Place {{place_name}} with type {{place.contact_type}} was added to ' + + '{{place.parent.name}}({{place.parent.contact_type}})' + } + ] + }] + }; + const contactTypes = [ + { id: 'health_center_1' }, + { id: 'clinic_1', parents: ['health_center_1'] }, + { id: 'supervisor', parents: ['health_center_1'], person: true }, + { id: 'patient', parents: ['clinic_1'], person: true }, + ]; + config.get.returns([eventConfig]); + config.getAll.returns({ contact_types: contactTypes }); + + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations').resolves([]); + sinon.stub(transitionUtils, 'getUniqueId').resolves(placeId); + + return transition.onMatch(change).then(() => { + transitionUtils.getUniqueId.callCount.should.equal(1); + utils.getContactUuid.callCount.should.equal(1); + utils.getContactUuid.args[0].should.deep.equal([placeId]); + utils.getContact.callCount.should.equal(0); + db.medic.post.callCount.should.equal(1); + db.medic.post.args[0].should.deep.equal([{ + name: 'new clinic', + place_id: placeId, + source_id: change.doc._id, + type: 'contact', + contact_type: 'clinic_1', + parent: { _id: 'west_hc' }, + created_by: 'supervisor', + reported_date: change.doc.reported_date, + }]); + (!!change.doc.errors).should.equal(false); + change.doc.tasks.length.should.equal(1); + change.doc.tasks[0].messages[0].should.include({ + to: change.doc.from, + message: 'Place new clinic with type clinic_1 was added to west hc(health_center_1)' + }); + }); + }); - if (event.trigger === 'add_patient') { - if (params.patient_id_field === 'patient_id') { - throw new Error( - `Configuration error in ${registration.form}.${event.trigger}: ` + - `patient_id_field cannot be set to patient_id` - ); + it('should default to submitter by phone parent when parent_id param not specified', () => { + const change = { + doc: { + _id: 'reportID', + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+111222', + fields: { place_name: 'new clinic' }, + } + }; + const placeId = 'my_place_id'; + sinon.stub(utils, 'getContactUuid').resolves(); + sinon.stub(utils, 'getContact'); + sinon.stub(db.medic, 'post').resolves(); + const eventConfig = { + form: 'R', + events: [{ name: 'on_create', trigger: 'add_place', params: { contact_type: 'clinic_1' } }], + messages: [{ + recipient: 'reporting_unit', + event_type: 'report_accepted', + bool_expr: 'doc.place', + message: [ + { + locale: 'en', + content: 'Place {{place_name}} with type {{place.contact_type}} was added to ' + + '{{place.parent.name}}({{place.parent.contact_type}})' + } + ] + }] + }; + sinon.stub(db.medic, 'query') + .withArgs('medic-client/contacts_by_phone') + .resolves({ + rows: [ + { + doc: { + _id: 'supervisor', + name: 'Frank', + contact_type: 'supervisor', + type: 'contact', + phone: '+111222', + parent: { _id: 'west_hc' } + } } - const typeId = params.contact_type || 'person'; - const contactType = contactTypesUtils.getTypeById(config.getAll(), typeId); - if (!contactType) { - throw new Error( - `Configuration error in ${registration.form}.${event.trigger}: ` + - `trigger would create a doc with an unknown contact type "${typeId}"` - ); + ] + }); + sinon.stub(db.medic, 'get').withArgs('west_hc').resolves({ + _id: 'west_hc', + name: 'west hc', + place_id: 'west_hc_place', + contact_type: 'health_center_1', + type: 'contact', + }); + const contactTypes = [ + { id: 'health_center_1' }, + { id: 'clinic_1', parents: ['health_center_1'] }, + { id: 'supervisor', parents: ['health_center_1'], person: true }, + { id: 'patient', parents: ['clinic_1'], person: true }, + ]; + config.get.returns([eventConfig]); + config.getAll.returns({ contact_types: contactTypes }); + + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations').resolves([]); + sinon.stub(transitionUtils, 'getUniqueId').resolves(placeId); + + return transition.onMatch(change).then(() => { + transitionUtils.getUniqueId.callCount.should.equal(1); + utils.getContactUuid.callCount.should.equal(1); + utils.getContactUuid.args[0].should.deep.equal([placeId]); + utils.getContact.callCount.should.equal(0); + db.medic.query.callCount.should.equal(1); + db.medic.query.args[0] + .should.deep.equal(['medic-client/contacts_by_phone', { key: '+111222', include_docs: true }]); + db.medic.get.callCount.should.equal(1); + db.medic.get.args[0].should.deep.equal(['west_hc']); + db.medic.post.callCount.should.equal(1); + db.medic.post.args[0].should.deep.equal([{ + name: 'new clinic', + place_id: placeId, + source_id: change.doc._id, + type: 'contact', + contact_type: 'clinic_1', + parent: { _id: 'west_hc' }, + created_by: 'supervisor', + reported_date: change.doc.reported_date, + }]); + (!!change.doc.errors).should.equal(false); + change.doc.tasks.length.should.equal(1); + change.doc.tasks[0].messages[0].should.include({ + to: change.doc.from, + message: 'Place new clinic with type clinic_1 was added to west hc(health_center_1)' + }); + }); + }); + + it('should use the name property indicated within the event parameter ', () => { + const change = { + doc: { + _id: 'reportID', + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+111222', + fields: { doodle: 'newest place', parent_id: 'north_hc_place', place_name: 'this is a trick' }, + contact: { + _id: 'pete', + name: 'Pete', + contact_type: 'chw', + type: 'contact', + parent: { + _id: 'petes', + name: 'Petes Place', + contact_type: 'clinic', + type: 'contact', + parent: { + _id: 'west_hc', + name: 'west hc', + contact_type: 'health_center', + type: 'contact', + place_id: 'south_hc_place', + } } - if (!contactTypesUtils.isPersonType(contactType)) { - throw new Error( - `Configuration error in ${registration.form}.${event.trigger}: ` + - `trigger would create a person with a place contact type "${typeId}"` - ); + }, + } + }; + const placeId = 'my_place_id'; + const parent = { + _id: 'north_hc', + name: 'north hc', + type: 'contact', + contact_type: 'health_center', + place_id: 'north_hc_place', + }; + sinon.stub(utils, 'getContactUuid').resolves(); + sinon.stub(utils, 'getContact').resolves(parent); + sinon.stub(db.medic, 'post').resolves(); + const eventConfig = { + form: 'R', + events: [{ + name: 'on_create', trigger: 'add_place', + params: { contact_type: 'clinic_1', parent_id: 'parent_id', place_name_field: 'doodle' } + }], + messages: [{ + recipient: 'reporting_unit', + event_type: 'report_accepted', + bool_expr: 'doc.place', + message: [ + { + locale: 'en', + content: 'Place {{place.name}} with type {{place.contact_type}} was added to ' + + '{{place.parent.name}}({{place.parent.contact_type}})' } - } + ] + }] + }; + const contactTypes = [ + { id: 'health_center' }, + { id: 'clinic_1', parents: ['health_center'] }, + { id: 'patient', parents: ['clinic_1'], person: true }, + ]; + config.get.returns([eventConfig]); + config.getAll.returns({ contact_types: contactTypes }); + + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations').resolves([]); + sinon.stub(transitionUtils, 'getUniqueId').resolves(placeId); + + return transition.onMatch(change).then(() => { + transitionUtils.getUniqueId.callCount.should.equal(1); + utils.getContactUuid.callCount.should.equal(1); + utils.getContactUuid.args[0].should.deep.equal([placeId]); + utils.getContact.callCount.should.equal(1); + utils.getContact.args[0].should.deep.equal([change.doc.fields.parent_id]); + db.medic.post.callCount.should.equal(1); + db.medic.post.args[0].should.deep.equal([{ + name: 'newest place', + place_id: placeId, + source_id: change.doc._id, + type: 'contact', + contact_type: 'clinic_1', + parent: { _id: 'north_hc' }, + created_by: 'pete', + reported_date: change.doc.reported_date, + }]); + (!!change.doc.errors).should.equal(false); + change.doc.tasks.length.should.equal(1); + change.doc.tasks[0].messages[0].should.include({ + to: change.doc.from, + message: 'Place newest place with type clinic_1 was added to north hc(health_center)' + }); + }); + }); - if ( - event.trigger === 'assign_schedule' || - event.trigger === 'clear_schedule' - ) { - if (!event.params) { - throw new Error( - `Configuration error. Expecting params to be defined as the name of the schedule(s) ` + - `for ${registration.form}.${event.trigger}` - ); + it('should use the parent property indicated within the event parameter ', () => { + const change = { + doc: { + _id: 'reportID', + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+111222', + fields: { doodle: 'newest place', fiddle: 'north_hc_place', place_name: 'this is a trick' }, + contact: { + _id: 'pete', + name: 'Pete', + contact_type: 'chw', + type: 'contact', + parent: { + _id: 'petes', + name: 'Petes Place', + contact_type: 'clinic', + type: 'contact', + parent: { + _id: 'west_hc', + name: 'west hc', + contact_type: 'health_center', + type: 'contact', + place_id: 'south_hc_place', + } } - if (!Array.isArray(params)) { - throw new Error( - `Configuration error. Expecting params to be a string, comma separated list, ` + - `or an array for ${registration.form}.${event.trigger}: '${event.params}' - `); + }, + } + }; + const placeId = 'my_place_id'; + const parent = { + _id: 'north_hc', + name: 'north hc', + type: 'contact', + contact_type: 'health_center', + place_id: 'north_hc_place', + }; + sinon.stub(utils, 'getContactUuid').resolves(); + sinon.stub(utils, 'getContact').resolves(parent); + sinon.stub(db.medic, 'post').resolves(); + const eventConfig = { + form: 'R', + events: [{ + name: 'on_create', trigger: 'add_place', + params: { contact_type: 'clinic', parent_id: 'fiddle', place_name_field: 'doodle' } + }], + messages: [{ + recipient: 'reporting_unit', + event_type: 'report_accepted', + bool_expr: 'doc.place', + message: [ + { + locale: 'en', + content: 'Place {{place.name}} with type {{place.type}} was added to ' + + '{{place.parent.name}}({{place.parent.contact_type}})' } - } + ] + }] + }; + const contactTypes = [ + { id: 'health_center' }, + { id: 'clinic', parents: ['health_center'] }, + { id: 'patient', parents: ['clinic'], person: true }, + ]; + config.get.returns([eventConfig]); + config.getAll.returns({ contact_types: contactTypes }); + + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations').resolves([]); + sinon.stub(transitionUtils, 'getUniqueId').resolves(placeId); + + return transition.onMatch(change).then(() => { + transitionUtils.getUniqueId.callCount.should.equal(1); + utils.getContactUuid.callCount.should.equal(1); + utils.getContactUuid.args[0].should.deep.equal([placeId]); + utils.getContact.callCount.should.equal(1); + utils.getContact.args[0].should.deep.equal([change.doc.fields.fiddle]); + db.medic.post.callCount.should.equal(1); + db.medic.post.args[0].should.deep.equal([{ + name: 'newest place', + place_id: placeId, + source_id: change.doc._id, + type: 'clinic', + parent: { _id: 'north_hc' }, + created_by: 'pete', + reported_date: change.doc.reported_date, + }]); + (!!change.doc.errors).should.equal(false); + change.doc.tasks.length.should.equal(1); + change.doc.tasks[0].messages[0].should.include({ + to: change.doc.from, + message: 'Place newest place with type clinic was added to north hc(health_center)' + }); + }); + }); + + it('should not create place when parent_id is not defined and no contact', () => { + const change = { + doc: { + _id: 'reportID', + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+111222', + fields: { place_name: 'New Orleans' }, + } + }; + const placeId = 'my_place_id'; + sinon.stub(utils, 'getContactUuid').resolves(); + sinon.stub(utils, 'getContact'); + sinon.stub(db.medic, 'post').resolves(); + const eventConfig = { + form: 'R', + events: [{ name: 'on_create', trigger: 'add_place', params: { contact_type: 'clinic' } }], + messages: [{ + recipient: 'reporting_unit', + event_type: 'parent_not_found', + message: [ + { + locale: 'en', + content: 'Cannot create clinic with name {{place_name}}: parent not found.' + } + ] + }] + }; + config.get.withArgs('registrations').returns([eventConfig]); + sinon.stub(db.medic, 'query').withArgs('medic-client/contacts_by_phone').resolves({ rows: [] }); + sinon.stub(db.medic, 'get'); + + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations').resolves([]); + sinon.stub(transitionUtils, 'getUniqueId').resolves(placeId); + + return transition.onMatch(change).then(() => { + transitionUtils.getUniqueId.callCount.should.equal(1); + utils.getContactUuid.callCount.should.equal(1); + utils.getContactUuid.args[0].should.deep.equal([placeId]); + utils.getContact.callCount.should.equal(0); + db.medic.post.callCount.should.equal(0); + db.medic.query.callCount.should.equal(1); + db.medic.query.args[0] + .should.deep.equal(['medic-client/contacts_by_phone', { key: '+111222', include_docs: true }]); + db.medic.get.callCount.should.equal(0); + + change.doc.errors.length.should.equal(1); + change.doc.errors[0].should.deep.equal({ + code: 'parent_not_found', + message: 'Cannot create clinic with name New Orleans: parent not found.', + }); + change.doc.tasks.length.should.equal(1); + change.doc.tasks[0].messages[0].should.include({ + to: change.doc.from, + message: 'Cannot create clinic with name New Orleans: parent not found.' + }); + }); + }); - if (event.trigger === 'add_place') { - const typeId = params.contact_type; - if (!typeId) { - throw new Error( - `Configuration error in ${registration.form}.${event.trigger}: ` + - `trigger would create a place with an undefined contact type` - ); + it('should not create place when parent is not defined', () => { + const change = { + doc: { + _id: 'reportID', + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+111222', + fields: { place_name: 'New Orleans' }, + } + }; + const placeId = 'my_place_id'; + sinon.stub(utils, 'getContactUuid').resolves(); + sinon.stub(utils, 'getContact'); + sinon.stub(db.medic, 'post').resolves(); + const eventConfig = { + form: 'R', + events: [{ name: 'on_create', trigger: 'add_place', params: { contact_type: 'clinic', parent_id: 'some_id' } }], + messages: [{ + recipient: 'reporting_unit', + event_type: 'parent_field_not_provided', + message: [ + { + locale: 'en', + content: 'Cannot create clinic with name {{place_name}}: parent field not provided.' } - const contactType = contactTypesUtils.getTypeById(config.getAll(), typeId); - if (!contactType) { - throw new Error( - `Configuration error in ${registration.form}.${event.trigger}: ` + - `trigger would create a place with an unknown contact type "${typeId}"` - ); + ] + }] + }; + config.get.withArgs('registrations').returns([eventConfig]); + + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations').resolves([]); + sinon.stub(transitionUtils, 'getUniqueId').resolves(placeId); + + return transition.onMatch(change).then(() => { + transitionUtils.getUniqueId.callCount.should.equal(1); + utils.getContactUuid.callCount.should.equal(1); + utils.getContactUuid.args[0].should.deep.equal([placeId]); + utils.getContact.callCount.should.equal(0); + db.medic.post.callCount.should.equal(0); + + change.doc.errors.length.should.equal(1); + change.doc.errors[0].should.deep.equal({ + code: 'parent_field_not_provided', + message: 'Cannot create clinic with name New Orleans: parent field not provided.', + }); + change.doc.tasks.length.should.equal(1); + change.doc.tasks[0].messages[0].should.include({ + to: change.doc.from, + message: 'Cannot create clinic with name New Orleans: parent field not provided.' + }); + }); + }); + + it('should not create place when parent is not found', () => { + const change = { + doc: { + _id: 'reportID', + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+111222', + fields: { place_name: 'New Orleans' }, + } + }; + const placeId = 'my_place_id'; + sinon.stub(utils, 'getContactUuid').resolves(); + sinon.stub(utils, 'getContact'); + sinon.stub(db.medic, 'post').resolves(); + const eventConfig = { + form: 'R', + events: [{ name: 'on_create', trigger: 'add_place', params: { contact_type: 'clinic', parent_id: 'some_id' } }], + messages: [{ + recipient: 'reporting_unit', + event_type: 'parent_field_not_provided', + message: [ + { + locale: 'en', + content: 'Cannot create clinic with name {{place_name}}: parent field not provided.' } - if (!contactTypesUtils.isPlaceType(contactType)) { - throw new Error( - `Configuration error in ${registration.form}.${event.trigger}: ` + - `trigger would create a place with a person contact type "${typeId}"` - ); + ] + }] + }; + config.get.withArgs('registrations').returns([eventConfig]); + + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations').resolves([]); + sinon.stub(transitionUtils, 'getUniqueId').resolves(placeId); + + return transition.onMatch(change).then(() => { + transitionUtils.getUniqueId.callCount.should.equal(1); + utils.getContactUuid.callCount.should.equal(1); + utils.getContactUuid.args[0].should.deep.equal([placeId]); + utils.getContact.callCount.should.equal(0); + db.medic.post.callCount.should.equal(0); + + change.doc.errors.length.should.equal(1); + change.doc.errors[0].should.deep.equal({ + code: 'parent_field_not_provided', + message: 'Cannot create clinic with name New Orleans: parent field not provided.', + }); + change.doc.tasks.length.should.equal(1); + change.doc.tasks[0].messages[0].should.include({ + to: change.doc.from, + message: 'Cannot create clinic with name New Orleans: parent field not provided.' + }); + }); + }); + + it('should not create place when parent is not valid', () => { + const change = { + doc: { + _id: 'reportID', + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+111222', + fields: { place_name: 'New Orleans', parent_id: 'hc2' }, + contact: { + _id: 'bob', name: 'Bob', + parent: { + _id: 'a_health_center', name: 'HC1', type: 'contact', contact_type: 'health_center', place_id: 'hc1' } } - }); - } - }); - }, - filter: ({ doc, info }) => { - return Boolean( - doc.type === 'data_record' && - getRegistrationConfig(getConfig(), doc.form) && - !transitionUtils.hasRun(info, NAME) && - utils.isValidSubmission(doc) // requires either an xform, a known submitter or public form for SMS - ); - }, - onMatch: change => { - const doc = change.doc; - const registrationConfig = getRegistrationConfig(getConfig(), doc.form); - - return transitionUtils - .validate(registrationConfig, doc) - .then(errors => { - if (errors && errors.length > 0) { - messages.addErrors(registrationConfig, doc, errors, { patient: doc.patient, place: doc.place }); - return true; } + }; + const placeId = 'my_place_id'; + const parent = { + _id: 'other_health_center', + name: 'Other health center', + place_id: 'hc2', + type: 'contact', + contact_type: 'health_center', + parent: { _id: 'district1' }, + }; + sinon.stub(utils, 'getContactUuid').resolves(); + sinon.stub(utils, 'getContact').resolves(parent); + sinon.stub(db.medic, 'post').resolves(); + const eventConfig = { + form: 'R', + events: [{ name: 'on_create', trigger: 'add_place', params: { contact_type: 'area', parent_id: 'parent_id' } }], + messages: [{ + recipient: 'reporting_unit', + event_type: 'parent_invalid', + message: [ + { + locale: 'en', + content: 'Cannot create area with name {{place_name}} under parent {{parent.name}} of type ' + + '{{parent.contact_type}}' + } + ] + }] + }; + const contactTypes = [ + { id: 'district' }, + { id: 'health_center', parents: ['district'] }, + { id: 'local_thing', parents: ['district'] }, + { id: 'clinic', parents: ['health_center'] }, + { id: 'area', parents: ['local_thing'] }, + ]; + config.get.returns([eventConfig]); + config.getAll.returns({ contact_types: contactTypes }); + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations').resolves([]); + sinon.stub(transitionUtils, 'getUniqueId').resolves(placeId); + + return transition.onMatch(change).then(() => { + transitionUtils.getUniqueId.callCount.should.equal(1); + utils.getContactUuid.callCount.should.equal(1); + utils.getContactUuid.args[0].should.deep.equal([placeId]); + utils.getContact.callCount.should.equal(1); + utils.getContact.args[0].should.deep.equal(['hc2']); + db.medic.post.callCount.should.equal(0); + + change.doc.errors.length.should.equal(1); + change.doc.errors[0].should.deep.equal({ + code: 'parent_invalid', + message: 'Cannot create area with name New Orleans under parent Other health center of type health_center', + }); + change.doc.tasks.length.should.equal(1); + change.doc.tasks[0].messages[0].should.include({ + to: change.doc.from, + message: 'Cannot create area with name New Orleans under parent Other health center of type health_center' + }); + }); + }); + }); + + describe('addCase', () => { + + it('trigger assigns a case id', () => { + const caseId = '99955'; + const change = { + doc: { + _id: 'def', + type: 'data_record', + form: 'S', + reported_date: 53, + from: '+555123', + fields: { level: 8 } + }, + }; + const eventConfig = { + form: 'S', + events: [{ name: 'on_create', trigger: 'add_case' }], + }; + config.get.returns([eventConfig]); + sinon.stub(validation, 'validate').resolves(); + sinon.stub(transitionUtils, 'getUniqueId').resolves(caseId); + config.getAll.returns(settings); + + return transition.onMatch(change).then(() => { + change.doc.case_id.should.equal(caseId); + }); + }); + + it('adds place_uuid if known', () => { + const caseId = '99955'; + const change = { + doc: { + _id: 'def', + type: 'data_record', + form: 'S', + reported_date: 53, + from: '+555123', + fields: { level: 8 }, + contact: { parent: { _id: 'abc' } } + }, + }; + const eventConfig = { + form: 'S', + events: [{ name: 'on_create', trigger: 'add_case' }], + }; + config.get.returns([eventConfig]); + sinon.stub(validation, 'validate').resolves(); + sinon.stub(transitionUtils, 'getUniqueId').resolves(caseId); + config.getAll.returns(settings); + + return transition.onMatch(change).then(() => { + change.doc.case_id.should.equal(caseId); + change.doc.fields.place_uuid.should.equal('abc'); + }); + }); + + }); - const patientId = doc.fields && doc.fields.patient_id; - const placeId = doc.fields && doc.fields.place_id; + describe('assign_schedule', () => { + it('event creates the named schedule', () => { + const change = { + doc: { + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+555123', + fields: { patient_id: '05649' }, + patient: { _id: 'patient', patient_id: '05649', type: 'person' } + }, + }; + sinon.stub(db.medic, 'post').resolves(); + const eventConfig = { + form: 'R', + events: [ + { + name: 'on_create', + trigger: 'assign_schedule', + params: 'myschedule', + }, + ], + }; + config.get.returns([eventConfig]); + sinon.stub(validation, 'validate').resolves(); + sinon + .stub(utils, 'getRegistrations') + .withArgs({ id: '05649' }).resolves([{ _id: 'xyz' }]) + .withArgs({ id: undefined }).resolves([]); + sinon.stub(schedules, 'getScheduleConfig').returns('someschedule'); + const assignSchedule = sinon + .stub(schedules, 'assignSchedule') + .returns(true); + + return transition.onMatch(change).then(() => { + assignSchedule.callCount.should.equal(1); + assignSchedule.args[0].should.deep.equal([ + change.doc, + 'someschedule', + { + patient: change.doc.patient, + place: undefined, + patientRegistrations: [{ _id: 'xyz' }], + placeRegistrations: [], + }, + ]); + utils.getRegistrations.callCount.should.equal(2); + utils.getRegistrations.args[0].should.deep.equal([{ id: '05649' }]); + }); + }); - if (!patientId && !placeId) { - return fireConfiguredTriggers(registrationConfig, doc); + it('should create the named schedule for a place', () => { + const change = { + doc: { + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+555123', + fields: { place_id: '79999' }, + place: { _id: 'place_id', place_id: '79999', type: 'clinic' }, + }, + }; + sinon.stub(db.medic, 'post').resolves(); + + const eventConfig = { + form: 'R', + events: [ + { + name: 'on_create', + trigger: 'assign_schedule', + params: 'myschedule', + }, + ], + }; + config.get.returns([eventConfig]); + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations') + .withArgs({ id: '79999' }).resolves([{ _id: 'place_registration' }]) + .withArgs({ id: undefined }).resolves([]); + + sinon.stub(schedules, 'getScheduleConfig').returns('myschedule'); + sinon.stub(schedules, 'assignSchedule').returns(true); + sinon.stub(contactTypeUtils, 'isPlace').returns(true); + + return transition.onMatch(change).then(() => { + schedules.assignSchedule.callCount.should.equal(1); + schedules.assignSchedule.args[0].should.deep.equal([ + change.doc, + 'myschedule', + { + patient: undefined, + patientRegistrations: [], + place: change.doc.place, + placeRegistrations: [{ _id: 'place_registration' }], + }, + ]); + + utils.getRegistrations.callCount.should.equal(2); + utils.getRegistrations.args[0].should.deep.equal([{ id: undefined }]); + utils.getRegistrations.args[1].should.deep.equal([{ id: '79999' }]); + }); + }); + + it('should create the named schedule with place and patient registrations', () => { + const change = { + doc: { + type: 'data_record', + form: 'R', + reported_date: 53, + from: '+555123', + fields: { place_id: '89647', patient_id: '26954' }, + place: { _id: 'place_id', place_id: '89647' }, + patient: { _id: 'patient_id', place_id: '26954' }, + }, + }; + sinon.stub(db.medic, 'post').resolves(); + + const eventConfig = { + form: 'R', + events: [ + { + name: 'on_create', + trigger: 'assign_schedule', + params: 'myschedule', + }, + ], + }; + config.get.returns([eventConfig]); + sinon.stub(validation, 'validate').resolves(); + sinon.stub(utils, 'getRegistrations') + .onCall(0).resolves([{ _id: 'patient_registration' }]) + .onCall(1).resolves([{ _id: 'place_registration' }]); + + sinon.stub(schedules, 'getScheduleConfig').returns('myschedule'); + sinon.stub(schedules, 'assignSchedule').returns(true); + + sinon.stub(contactTypeUtils, 'isPerson').returns(true); + sinon.stub(contactTypeUtils, 'isPlace').returns(true); + + return transition.onMatch(change).then(() => { + schedules.assignSchedule.callCount.should.equal(1); + schedules.assignSchedule.args[0].should.deep.equal([ + change.doc, + 'myschedule', + { + patient: change.doc.patient, + patientRegistrations: [{ _id: 'patient_registration' }], + place: change.doc.place, + placeRegistrations: [{ _id: 'place_registration' }], + }, + ]); + + utils.getRegistrations.callCount.should.equal(2); + utils.getRegistrations.args[0].should.deep.equal([{ id: '26954' }]); + utils.getRegistrations.args[1].should.deep.equal([{ id: '89647' }]); + }); + }); + }); + + describe('filter', () => { + it('returns false for reports with no registration configured', () => { + const doc = { form: 'R', type: 'data_record' }; + const configGet = config.get.returns([{ form: 'XYZ' }]); + const actual = transition.filter({ doc }); + configGet.callCount.should.equal(1); + configGet.args[0][0].should.equal('registrations'); + actual.should.equal(false); + }); + + it('returns false for reports that are not valid submissions', () => { + const doc = { form: 'R', type: 'data_record' }; + sinon.stub(utils, 'isValidSubmission').returns(false); + config.get.returns([{ form: 'R' }]); + const actual = transition.filter({ doc, info: {} }); + config.get.callCount.should.equal(1); + config.get.args[0][0].should.equal('registrations'); + utils.isValidSubmission.callCount.should.equal(1); + utils.isValidSubmission.args[0].should.deep.equal([doc]); + actual.should.equal(false); + }); + + it('returns true for reports that are valid submissions', () => { + const doc = { form: 'R', type: 'data_record' }; + sinon.stub(utils, 'isValidSubmission').returns(true); + config.get.returns([{ form: 'R' }]); + const actual = transition.filter({ doc, info: {} }); + config.get.callCount.should.equal(1); + config.get.args[0][0].should.equal('registrations'); + utils.isValidSubmission.callCount.should.equal(1); + utils.isValidSubmission.args[0].should.deep.equal([doc]); + actual.should.equal(true); + }); + }); + + describe('trigger param configuration', () => { + beforeEach(() => { + transition.fireConfiguredTriggers = transition.__get__('fireConfiguredTriggers'); + transition.triggers = transition.__get__('triggers'); + }); + + it('supports strings', () => { + const eventConfig = { + form: 'R', + events: [ + { name: 'on_create', trigger: 'testparamparsing', params: 'foo' }, + ], + }; + + transition.triggers.testparamparsing = sinon.stub(); + + return transition.fireConfiguredTriggers(eventConfig, {}).then(() => { + transition.triggers.testparamparsing.callCount.should.equal(1); + transition.triggers.testparamparsing.args[0][0].params.should.deep.equal(['foo']); + }); + }); + + it('supports comma-delimited strings as array', () => { + const eventConfig = { + form: 'R', + events: [ + { name: 'on_create', trigger: 'testparamparsing', params: 'foo,bar' }, + ], + }; + + transition.triggers.testparamparsing = sinon.stub(); + + + return transition.fireConfiguredTriggers(eventConfig, {}).then(() => { + transition.triggers.testparamparsing.callCount.should.equal(1); + transition.triggers.testparamparsing.args[0][0].params.should.deep.equal(['foo', 'bar']); + }); + }); + + it('supports arrays as a string', () => { + const eventConfig = { + form: 'R', + events: [ + { + name: 'on_create', + trigger: 'testparamparsing', + params: '["foo","bar", 3]', + }, + ], + }; + + transition.triggers.testparamparsing = sinon.stub(); + + return transition.fireConfiguredTriggers(eventConfig, {}).then(() => { + transition.triggers.testparamparsing.callCount.should.equal(1); + transition.triggers.testparamparsing.args[0][0].params.should.deep.equal(['foo', 'bar', 3]); + }); + }); + + it('supports JSON as a string', () => { + const eventConfig = { + form: 'R', + events: [ + { + name: 'on_create', + trigger: 'testparamparsing', + params: '{"foo": "bar"}', + }, + ], + }; + + transition.triggers.testparamparsing = sinon.stub(); + + return transition.fireConfiguredTriggers(eventConfig, {}).then(() => { + transition.triggers.testparamparsing.callCount.should.equal(1); + transition.triggers.testparamparsing.args[0][0].params.should.deep.equal({ foo: 'bar' }); + }); + }); + + it('supports direct JSON', () => { + const eventConfig = { + form: 'R', + events: [ + { + name: 'on_create', + trigger: 'testparamparsing', + params: { foo: 'bar' }, + }, + ], + }; + + transition.triggers.testparamparsing = sinon.stub(); + + return transition.fireConfiguredTriggers(eventConfig, {}).then(() => { + transition.triggers.testparamparsing.callCount.should.equal(1); + transition.triggers.testparamparsing.args[0][0].params.should.deep.equal({ foo: 'bar' }); + }); + }); + + it('fails with no parameters', () => { + const eventConfig = [ + { + form: 'R', + events: [ + { + name: 'on_create', + trigger: 'assign_schedule', + params: '', + }, + ], + }, + ]; + + config.get.returns(eventConfig); + transition.init.should.throw(Error, 'Configuration error. Expecting params to be defined as the name of the ' + + 'schedule(s) for R.assign_schedule'); + }); + + it('fails with object parameters', () => { + const eventConfig = [ + { + form: 'R', + events: [ + { + name: 'on_create', + trigger: 'assign_schedule', + params: '{ "name": "hello" }', + }, + ], + }, + ]; + + config.get.returns(eventConfig); + transition.init.should.throw(Error, 'Configuration error. Expecting params to be a string, ' + + 'comma separated list, or an array for R.assign_schedule: \'{ "name": "hello" }\''); + }); + + it('succeeds with array parameters', () => { + const eventConfig = [ + { + form: 'R', + events: [ + { + name: 'on_create', + trigger: 'assign_schedule', + params: 'a,b', + }, + ], + }, + ]; + + config.get.returns(eventConfig); + transition.init(); + }); + + it('parse failure for invalid JSON propagates to the callbacks', () => { + const eventConfig = [ + { + form: 'R', + events: [ + { + name: 'on_create', + trigger: 'testparamparsing', + params: '{"foo": "bar"', // missing end "}" + }, + ], + }, + ]; + + config.get.returns(eventConfig); + transition.init.should.throw(Error, 'Configuration error. Unable to parse params for R.testparamparsing: ' + + '\'{"foo": "bar"\'. Error: SyntaxError: Unexpected end of JSON input'); + }); + + it('add_patient fails if the configured contact type is not known', () => { + const eventConfig = [{ + form: 'R', + events: [{ + name: 'on_create', + trigger: 'add_patient', + params: '{ "contact_type": "unknown" }' + }], + }]; + const contactTypes = [{ id: 'known' }]; + + config.get.returns(eventConfig); + config.getAll.returns({ contact_types: contactTypes }); + + transition.init.should.throw(Error, 'Configuration error in R.add_patient: trigger would create a doc with an ' + + 'unknown contact type "unknown"'); + }); + + it('add_patient fails if the configured contact type is not a person', () => { + const eventConfig = [{ + form: 'R', + events: [{ + name: 'on_create', + trigger: 'add_patient', + params: '{ "contact_type": "place" }' + }], + }]; + const contactTypes = [{ id: 'place' }]; + + config.get.returns(eventConfig); + config.getAll.returns({ contact_types: contactTypes }); + + transition.init.should.throw(Error, 'Configuration error in R.add_patient: trigger would create a person ' + + 'with a place contact type "place"'); + }); + + it('add_patient fails if the default "person" contact type is not known', () => { + const eventConfig = [{ + form: 'R', + events: [{ + name: 'on_create', + trigger: 'add_patient', + params: '' + }], + }]; + const contactTypes = [{ id: 'place' }]; + + config.get.returns(eventConfig); + config.getAll.returns({ contact_types: contactTypes }); + + transition.init.should.throw(Error, 'Configuration error in R.add_patient: trigger would create a doc ' + + 'with an unknown contact type "person"'); + }); + + it('succeeds for known person type', done => { + const eventConfig = [{ + form: 'R', + events: [{ + name: 'on_create', + trigger: 'add_patient', + params: '{ "contact_type": "patient" }' + }], + }]; + const contactTypes = [{ id: 'patient', person: true }]; + + config.get.returns(eventConfig); + config.getAll.returns({ contact_types: contactTypes }); + + transition.init(); + done(); + }); + + it('add_place should fail for no defined contact_type', () => { + const eventConfig = [{ + form: 'R', + events: [{ + name: 'on_create', + trigger: 'add_place', + params: '' + }], + }]; + const contactTypes = [{ id: 'place' }]; + + config.get.returns(eventConfig); + config.getAll.returns({ contact_types: contactTypes }); + + transition.init.should.throw(Error, 'Configuration error in R.add_place: trigger would create a place ' + + 'with an undefined contact type'); + }); + + it('add_place should fail for unknown contact_type', () => { + const eventConfig = [{ + form: 'R', + events: [{ + name: 'on_create', + trigger: 'add_place', + params: { contact_type: 'oh_noo' } + }], + }]; + const contactTypes = [{ id: 'place' }]; + + config.get.returns(eventConfig); + config.getAll.returns({ contact_types: contactTypes }); + + transition.init.should.throw(Error, 'Configuration error in R.add_place: trigger would create a place ' + + 'with an unknown contact type "oh_noo"'); + }); + + it('add_place should fail for person contact_type', () => { + const eventConfig = [{ + form: 'R', + events: [{ + name: 'on_create', + trigger: 'add_place', + params: { contact_type: 'person' } + }], + }]; + + config.get.returns(eventConfig); + config.getAll.returns(settings); + + transition.init.should.throw(Error, 'Configuration error in R.add_place: trigger would create a place with ' + + 'a person contact type "person"'); + }); + + it('should succeed for known place type', () => { + const eventConfig = [{ + form: 'R', + events: [{ + name: 'on_create', + trigger: 'add_place', + params: { contact_type: 'place' } + }], + }]; + + config.get.returns(eventConfig); + config.getAll.returns(settings); + + transition.init(); + }); + }); + + describe('addMessages', () => { + beforeEach(() => { + transition.addMessages = transition.__get__('addMessages'); + }); + + it('pops and passes the right information to messages.addMessage', () => { + const testPhone = '1234'; + const testMessage1 = { + message: 'A Test Message 1', + recipient: testPhone, + event_type: 'report_accepted', + }; + const testMessage2 = { + message: 'A Test Message 2', + recipient: testPhone, + event_type: 'report_accepted', + }; + const testRegistration = 'some registrations'; + const testPatient = 'a patient contact'; + + const addMessage = sinon.stub(messages, 'addMessage'); + + sinon + .stub(utils, 'getRegistrations') + .withArgs({ id: '12345' }).resolves(testRegistration) + .withArgs({ id: undefined }).resolves([]); + + const testConfig = { messages: [testMessage1, testMessage2] }; + const testDoc = { + fields: { + patient_id: '12345', + }, + patient: testPatient, + }; + + return transition.addMessages(testConfig, testDoc).then(() => { + // Registration will send messages with no event_type + addMessage.callCount.should.equal(2); + + const expectedContext = { + patient: testPatient, + place: undefined, + registrations: testRegistration, + placeRegistrations: [], + templateContext: { + next_msg: { + minutes: 0, + hours: 0, + days: 0, + weeks: 0, + months: 0, + years: 0, + }, + }, + }; + addMessage.args[0].should.deep.equal([ + testDoc, + testMessage1, + testPhone, + expectedContext, + ]); + addMessage.args[1].should.deep.equal([ + testDoc, + testMessage2, + testPhone, + expectedContext, + ]); + + utils.getRegistrations.callCount.should.equal(2); + utils.getRegistrations.args[0].should.deep.equal([{ id: '12345' }]); + utils.getRegistrations.args[1].should.deep.equal([{ id: undefined }]); + }); + }); + + it('pops and passes the right information to messages.addMessage with place_id', () => { + const testPhone = '1234'; + const testMessage1 = { + message: 'A Test Message 1', + recipient: testPhone, + event_type: 'report_accepted', + }; + const testMessage2 = { + message: 'A Test Message 2', + recipient: testPhone, + event_type: 'report_accepted', + }; + const placeRegistrations = [{ _id: 'some registration' }]; + + sinon.stub(messages, 'addMessage'); + sinon.stub(utils, 'getRegistrations') + .withArgs({ id: undefined }).resolves([]) + .withArgs({ id: '65498' }).resolves(placeRegistrations); + + const testConfig = { messages: [testMessage1, testMessage2] }; + const testDoc = { + fields: { + place_id: '65498', + }, + place: { _id: 'test_place', place_id: '65498' }, + }; + + return transition.addMessages(testConfig, testDoc).then(() => { + // Registration will send messages with no event_type + messages.addMessage.callCount.should.equal(2); + + const expectedContext = { + patient: undefined, + place: testDoc.place, + registrations: [], + placeRegistrations: placeRegistrations, + templateContext: { + next_msg: { + minutes: 0, + hours: 0, + days: 0, + weeks: 0, + months: 0, + years: 0, + }, + }, + }; + messages.addMessage.args[0].should.deep.equal([ + testDoc, + testMessage1, + testPhone, + expectedContext, + ]); + messages.addMessage.args[1].should.deep.equal([ + testDoc, + testMessage2, + testPhone, + expectedContext, + ]); + utils.getRegistrations.callCount.should.equal(2); + utils.getRegistrations.args[0].should.deep.equal([{ id: undefined }]); + utils.getRegistrations.args[1].should.deep.equal([{ id: '65498' }]); + }); + }); + + it('pops and passes the right information to messages.addMessage with place_id and patient_id', () => { + const testPhone = '1234'; + const testMessage1 = { + message: 'A Test Message 1', + recipient: testPhone, + event_type: 'report_accepted', + }; + const testMessage2 = { + message: 'A Test Message 2', + recipient: testPhone, + event_type: 'report_accepted', + }; + const patientRegistrations = [{ _id: 'some registration' }]; + const placeRegistrations = [{ _id: 'other registration' }]; + + sinon.stub(messages, 'addMessage'); + sinon.stub(utils, 'getRegistrations') + .onCall(0).resolves(patientRegistrations) + .onCall(1).resolves(placeRegistrations); + + const testConfig = { messages: [testMessage1, testMessage2] }; + const testDoc = { + fields: { + place_id: '999999', + patient_id: '111111' + }, + place: { _id: 'test_place', place_id: '999999' }, + patient: { _id: 'test_patient', place_id: '111111' }, + }; + + return transition.addMessages(testConfig, testDoc).then(() => { + // Registration will send messages with no event_type + messages.addMessage.callCount.should.equal(2); + + const expectedContext = { + patient: testDoc.patient, + place: testDoc.place, + registrations: patientRegistrations, + placeRegistrations: placeRegistrations, + templateContext: { + next_msg: { + minutes: 0, + hours: 0, + days: 0, + weeks: 0, + months: 0, + years: 0, + }, + }, + }; + messages.addMessage.args[0].should.deep.equal([ + testDoc, + testMessage1, + testPhone, + expectedContext, + ]); + messages.addMessage.args[1].should.deep.equal([ + testDoc, + testMessage2, + testPhone, + expectedContext, + ]); + + utils.getRegistrations.callCount.should.equal(2); + utils.getRegistrations.args[0].should.deep.equal([{ id: '111111' }]); + utils.getRegistrations.args[1].should.deep.equal([{ id: '999999' }]); + }); + }); + + it('supports ignoring messages based on bool_expr', () => { + const testPhone = '1234'; + const testMessage1 = { + message: 'A Test Message 1', + recipient: testPhone, + event_type: 'report_accepted', + }; + const testMessage2 = { + message: 'A Test Message 2', + recipient: testPhone, + event_type: 'report_accepted', + bool_expr: '1 === 2', + }; + const testRegistration = 'some registrations'; + const testPatient = 'a patient contact'; + + const addMessage = sinon.stub(messages, 'addMessage'); + + sinon.stub(utils, 'getRegistrations') + .withArgs({ id: '12345' }).resolves(testRegistration) + .withArgs({ id: undefined }).resolves([]); + + const testConfig = { messages: [testMessage1, testMessage2] }; + const testDoc = { + fields: { + patient_id: '12345', + }, + patient: testPatient, + }; + + return transition.addMessages(testConfig, testDoc).then(() => { + addMessage.callCount.should.equal(1); + const expectedContext = { + patient: testPatient, + place: undefined, + registrations: testRegistration, + placeRegistrations: [], + templateContext: { + next_msg: { + minutes: 0, + hours: 0, + days: 0, + weeks: 0, + months: 0, + years: 0, + }, + }, + }; + addMessage.args[0][0].should.equal(testDoc); + addMessage.args[0][1].should.equal(testMessage1); + addMessage.args[0][2].should.equal(testPhone); + addMessage.args[0][3].should.deep.equal(expectedContext); + }); + }); + }); + + describe('clear_schedule', () => { + beforeEach(() => { + transition.triggers = transition.__get__('triggers'); + }); + + it('should work when doc has no patient', () => { + sinon.stub(utils, 'getReportsBySubject').resolves([]); + return transition.triggers.clear_schedule({ doc: {}, params: [] }); + }); + + it('should query utils.getReportsBySubject with correct params with patient', () => { + sinon.stub(utils, 'getReportsBySubject').resolves([]); + sinon.stub(utils, 'getSubjectIds') + .returns(['uuid', 'patient_id']) + .withArgs(undefined).returns([]); + const doc = { patient: { _id: 'uuid', patient_id: 'patient_id' } }; + sinon.stub(acceptPatientReports, 'silenceRegistrations').resolves(); + + return transition.triggers.clear_schedule({ doc, params: [] }).then(() => { + utils.getSubjectIds.callCount.should.equal(2); + utils.getSubjectIds.args[0].should.deep.equal([doc.patient]); + utils.getSubjectIds.args[1].should.deep.equal([doc.place]); + utils.getReportsBySubject.callCount.should.equal(1); + utils.getReportsBySubject.args[0].should.deep.equal([{ + ids: ['uuid', 'patient_id'], + registrations: true + }]); + }); + }); + + it('should query utils.getReportsBySubject with correct params with place', () => { + sinon.stub(utils, 'getReportsBySubject').resolves([]); + sinon.stub(utils, 'getSubjectIds') + .returns(['uuid', 'place_id']) + .withArgs(undefined).returns([]); + const doc = { place: { _id: 'uuid', place_id: 'place_id' } }; + sinon.stub(acceptPatientReports, 'silenceRegistrations').resolves(); + + return transition.triggers.clear_schedule({ doc, params: [] }).then(() => { + utils.getSubjectIds.callCount.should.equal(2); + utils.getSubjectIds.args[0].should.deep.equal([doc.patient]); + utils.getSubjectIds.args[1].should.deep.equal([doc.place]); + utils.getReportsBySubject.callCount.should.equal(1); + utils.getReportsBySubject.args[0].should.deep.equal([{ + ids: ['uuid', 'place_id'], + registrations: true + }]); + }); + }); + + it('should query utils.getReportsBySubject with correct params with patient and place', () => { + const doc = { + patient: { _id: 'uuid', patient_id: 'patient_id' }, + place: { _id: 'place_uuid', place_id: 'place_id' }, + }; + sinon.stub(utils, 'getReportsBySubject').resolves([]); + sinon.stub(utils, 'getSubjectIds') + .withArgs(doc.patient).returns(['uuid', 'patient_id']) + .withArgs(doc.place).returns(['place_uuid', 'place_id']); + sinon.stub(acceptPatientReports, 'silenceRegistrations').resolves(); + + return transition.triggers.clear_schedule({ doc, params: [] }).then(() => { + utils.getSubjectIds.callCount.should.equal(2); + utils.getSubjectIds.args[0].should.deep.equal([doc.patient]); + utils.getSubjectIds.args[1].should.deep.equal([doc.place]); + utils.getReportsBySubject.callCount.should.equal(1); + utils.getReportsBySubject.args[0].should.deep.equal([{ + ids: ['uuid', 'patient_id', 'place_uuid', 'place_id'], + registrations: true + }]); + }); + }); + + it('should call silenceRegistrations with correct params', () => { + const doc = { _id: 'uuid', patient_id: 'patient_id' }; + const params = ['1', '2', '3', '4']; + const registrations = ['a', 'b', 'c']; + sinon.stub(utils, 'getReportsBySubject').resolves(registrations); + sinon.stub(utils, 'getSubjectIds').returns(['uuid', 'patient_id']); + sinon.stub(acceptPatientReports, 'silenceRegistrations').resolves(); + + return transition.triggers.clear_schedule({ doc, params }).then(() => { + acceptPatientReports.silenceRegistrations.callCount.should.equal(1); + acceptPatientReports.silenceRegistrations.args[0][0].should.deep.equal({ + silence_type: '1,2,3,4', + silence_for: null + }); + acceptPatientReports.silenceRegistrations.args[0][1].should.deep.equal(doc); + acceptPatientReports.silenceRegistrations.args[0][2].should.deep.equal(registrations); + }); + }); + + it('should catch getReportsBySubject errors', () => { + sinon.stub(utils, 'getReportsBySubject').rejects({ some: 'error' }); + sinon.stub(utils, 'getSubjectIds').returns([]); + sinon.stub(acceptPatientReports, 'silenceRegistrations'); + + return transition.triggers + .clear_schedule({ doc: {}, params: [] }) + .then(r => r.should.deep.equal('Should have thrown')) + .catch((err) => { + err.should.deep.equal({ some: 'error' }); + utils.getReportsBySubject.callCount.should.equal(1); + utils.getSubjectIds.callCount.should.equal(2); + acceptPatientReports.silenceRegistrations.callCount.should.equal(0); + }); + }); + + it('should catch silenceRegistrations errors', () => { + sinon.stub(utils, 'getReportsBySubject').resolves([]); + sinon.stub(utils, 'getSubjectIds').returns([]); + sinon.stub(acceptPatientReports, 'silenceRegistrations').rejects({ some: 'err' }); + return transition.triggers + .clear_schedule({ doc: {}, params: [] }) + .then(r => r.should.deep.equal('Should have thrown')) + .catch(err => { + err.should.deep.equal({ some: 'err' }); + utils.getReportsBySubject.callCount.should.equal(1); + utils.getSubjectIds.callCount.should.equal(2); + acceptPatientReports.silenceRegistrations.callCount.should.equal(1); + }); + }); + }); + + describe('onMatch', () => { + let fireConfiguredTriggers; + const registrationConfig = { + form: 'R', + events: [ + { + name: 'on_create', + trigger: 'assign_schedule', + params: 'myschedule', + }, + ], + validations: { list: ['validation!!'] }, + }; + let doc; + + beforeEach(() => { + transition.__set__('fireConfiguredTriggers', sinon.stub().resolves(true)); + fireConfiguredTriggers = transition.__get__('fireConfiguredTriggers'); + config.get.withArgs('registrations').returns([registrationConfig]); + }); + + it('should adds error if validation fails, with patient subject', () => { + sinon.stub(validation, 'validate').resolves(['error']); + sinon.stub(messages, 'addErrors'); + + doc = { + form: 'R', + patient: { _id: 'patient' }, + }; + + return transition.onMatch({ doc }).then((result) => { + result.should.equal(true); + fireConfiguredTriggers.callCount.should.equal(0); + validation.validate.callCount.should.equal(1); + validation.validate.args[0].slice(0, 2).should.deep.equal([doc, ['validation!!']]); + messages.addErrors.callCount.should.equal(1); + messages.addErrors.args[0].should.deep.equal([ + registrationConfig, + doc, + ['error'], + { patient: doc.patient, place: undefined }, + ]); + }); + }); + + it('should adds error if validation fails, with place subject', () => { + sinon.stub(validation, 'validate').resolves(['an error']); + sinon.stub(messages, 'addErrors'); + + doc = { + form: 'R', + place: { _id: 'place' }, + }; + + return transition.onMatch({ doc }).then((result) => { + result.should.equal(true); + fireConfiguredTriggers.callCount.should.equal(0); + validation.validate.callCount.should.equal(1); + messages.addErrors.callCount.should.equal(1); + messages.addErrors.args[0].should.deep.equal([ + registrationConfig, + doc, + ['an error'], + { patient: undefined, place: doc.place }, + ]); + }); + }); + + it('should add error if validation fails, with both patient and place subjects', () => { + sinon.stub(validation, 'validate').resolves(['some error']); + sinon.stub(messages, 'addErrors'); + + doc = { + form: 'R', + patient: { _id: 'patient', patient_id: 'patient_id' }, + place: { _id: 'place', place_id: 'place_id' }, + fields: { patient_id: 'patient_id', place_id: 'place_id' } + }; + + return transition.onMatch({ doc }).then((result) => { + result.should.equal(true); + fireConfiguredTriggers.callCount.should.equal(0); + validation.validate.callCount.should.equal(1); + messages.addErrors.callCount.should.equal(1); + messages.addErrors.args[0].should.deep.equal([ + registrationConfig, + doc, + ['some error'], + { patient: doc.patient, place: doc.place }, + ]); + }); + }); + + it('should run triggers when there is no subject', () => { + sinon.stub(validation, 'validate').resolves(); + sinon.stub(messages, 'addErrors'); + + doc = { form: 'R' }; + + return transition.onMatch({ doc }).then((result) => { + result.should.equal(true); + fireConfiguredTriggers.callCount.should.equal(1); + fireConfiguredTriggers.args[0].should.deep.equal([registrationConfig, doc]); + validation.validate.callCount.should.equal(1); + messages.addErrors.callCount.should.equal(0); + }); + }); + + it('should fail if subject does not exist, with patient_id', () => { + sinon.stub(validation, 'validate').resolves(); + doc = { form: 'R', fields: { patient_id: '56987' } }; + + sinon.stub(transitionUtils, 'addRegistrationNotFoundError'); + + return transition.onMatch({ doc }).then(result => { + result.should.equal(true); + fireConfiguredTriggers.callCount.should.equal(0); + transitionUtils.addRegistrationNotFoundError.callCount.should.equal(1); + transitionUtils.addRegistrationNotFoundError.args[0].should.deep.equal([doc, registrationConfig]); + }); + }); + + it('should fail if patient exists but its not a person', () => { + sinon.stub(validation, 'validate').resolves(); + doc = { + form: 'R', + fields: { patient_id: '56987' }, + patient: { + _id: 'clinic', + place_id: '56987', + type: 'clinic', } + }; + sinon.stub(contactTypeUtils, 'isPerson').returns(false); + sinon.stub(transitionUtils, 'addRegistrationNotFoundError'); + + return transition.onMatch({ doc }).then(result => { + result.should.equal(true); + fireConfiguredTriggers.callCount.should.equal(0); + transitionUtils.addRegistrationNotFoundError.callCount.should.equal(1); + transitionUtils.addRegistrationNotFoundError.args[0].should.deep.equal([doc, registrationConfig]); + contactTypeUtils.isPerson.callCount.should.equal(1); + contactTypeUtils.isPerson.args[0].should.deep.equal([{}, { _id: 'clinic', place_id: '56987', type: 'clinic' }]); + }); + }); + + it('should fail if subject does not exist, with place_id', () => { + sinon.stub(validation, 'validate').resolves(); + doc = { form: 'R', fields: { place_id: '98754' } }; + sinon.stub(transitionUtils, 'addRegistrationNotFoundError'); + + return transition.onMatch({ doc }).then(result => { + result.should.equal(true); + fireConfiguredTriggers.callCount.should.equal(0); + transitionUtils.addRegistrationNotFoundError.callCount.should.equal(1); + transitionUtils.addRegistrationNotFoundError.args[0].should.deep.equal([doc, registrationConfig]); + }); + }); - // We're attaching this registration to an existing contact, let's - // make sure it's valid - if (!hasValidSubject(doc, patientId, placeId)) { - transitionUtils.addRegistrationNotFoundError(doc, registrationConfig); - return true; + it('should fail if place exists but its not a place', () => { + sinon.stub(validation, 'validate').resolves(); + doc = { + form: 'R', + fields: { place_id: '56987' }, + place: { + _id: 'person', + patient_id: '56987', + type: 'person', } + }; + sinon.stub(contactTypeUtils, 'isPlace').returns(false); + sinon.stub(transitionUtils, 'addRegistrationNotFoundError'); + + return transition.onMatch({ doc }).then(result => { + result.should.equal(true); + fireConfiguredTriggers.callCount.should.equal(0); + transitionUtils.addRegistrationNotFoundError.callCount.should.equal(1); + transitionUtils.addRegistrationNotFoundError.args[0].should.deep.equal([doc, registrationConfig]); + contactTypeUtils.isPlace.callCount.should.equal(1); + contactTypeUtils.isPlace.args[0].should.deep.equal([{}, { _id: 'person', patient_id: '56987', type: 'person' }]); + }); + }); + + it('should fail if subject does not exist, with place_id and patient_id', () => { + sinon.stub(validation, 'validate').resolves(); + doc = { form: 'R', fields: { place_id: 'the_place_id', patient_id: 'the_patient_id' } }; + sinon.stub(transitionUtils, 'addRegistrationNotFoundError'); - return fireConfiguredTriggers(registrationConfig, doc); + return transition.onMatch({ doc }).then(result => { + result.should.equal(true); + fireConfiguredTriggers.callCount.should.equal(0); + transitionUtils.addRegistrationNotFoundError.callCount.should.equal(1); + transitionUtils.addRegistrationNotFoundError.args[0].should.deep.equal([doc, registrationConfig]); }); - }, -}; + }); + + it('should fail if place and patient exist, but are switched', () => { + sinon.stub(validation, 'validate').resolves(); + doc = { + form: 'R', + fields: { place_id: '56987', patient_id: '69874' }, + place: { + _id: 'person', + patient_id: '56987', + type: 'person', + }, + patient: { + _id: 'place', + patient_id: '69874', + type: 'clinic', + }, + }; + + sinon.stub(contactTypeUtils, 'isPerson').returns(false); + sinon.stub(transitionUtils, 'addRegistrationNotFoundError'); + + return transition.onMatch({ doc }).then(result => { + result.should.equal(true); + fireConfiguredTriggers.callCount.should.equal(0); + transitionUtils.addRegistrationNotFoundError.callCount.should.equal(1); + transitionUtils.addRegistrationNotFoundError.args[0].should.deep.equal([doc, registrationConfig]); + contactTypeUtils.isPerson.callCount.should.equal(1); + contactTypeUtils.isPerson.args[0].should.deep.equal([ + {}, + { _id: 'place', patient_id: '69874', type: 'clinic' } + ]); + }); + }); + + it('should fail if place and patient exist, place is a place but patient is not a person', () => { + sinon.stub(validation, 'validate').resolves(); + doc = { + form: 'R', + fields: { place_id: '56987', patient_id: '69874' }, + place: { + _id: 'place', + place_id: '56987', + type: 'some_place', + }, + patient: { + _id: 'place', + place_id: '69874', + type: 'clinic', + }, + }; + + sinon.stub(contactTypeUtils, 'isPerson').returns(false); + sinon.stub(contactTypeUtils, 'isPlace').returns(true); + sinon.stub(transitionUtils, 'addRegistrationNotFoundError'); + + return transition.onMatch({ doc }).then(result => { + result.should.equal(true); + fireConfiguredTriggers.callCount.should.equal(0); + transitionUtils.addRegistrationNotFoundError.callCount.should.equal(1); + transitionUtils.addRegistrationNotFoundError.args[0].should.deep.equal([doc, registrationConfig]); + contactTypeUtils.isPerson.callCount.should.equal(1); + contactTypeUtils.isPerson.args[0].should.deep.equal([{}, { _id: 'place', place_id: '69874', type: 'clinic' }]); + }); + }); + + it('should fail if place and patient exist, patient is a person but place is not a place', () => { + sinon.stub(validation, 'validate').resolves(); + doc = { + form: 'R', + fields: { place_id: '56987', patient_id: '69874' }, + place: { + _id: 'person', + patient_id: '56987', + type: 'person', + }, + patient: { + _id: 'patient', + patient_id: '69874', + type: 'person', + }, + }; + + sinon.stub(contactTypeUtils, 'isPerson').returns(true); + sinon.stub(contactTypeUtils, 'isPlace').returns(false); + sinon.stub(transitionUtils, 'addRegistrationNotFoundError'); + + return transition.onMatch({ doc }).then(result => { + result.should.equal(true); + fireConfiguredTriggers.callCount.should.equal(0); + transitionUtils.addRegistrationNotFoundError.callCount.should.equal(1); + transitionUtils.addRegistrationNotFoundError.args[0].should.deep.equal([doc, registrationConfig]); + contactTypeUtils.isPerson.callCount.should.equal(1); + contactTypeUtils.isPerson.args[0].should.deep.equal([ + {}, + { _id: 'patient', patient_id: '69874', type: 'person' } + ]); + contactTypeUtils.isPlace.callCount.should.equal(1); + contactTypeUtils.isPlace.args[0].should.deep.equal([ + {}, + { _id: 'person', patient_id: '56987', type: 'person' } + ]); + }); + }); + + it('should run triggers when subject exists', () => { + sinon.stub(validation, 'validate').resolves(); + doc = { + form: 'R', + fields: { patient_id: 'the_patient_id' }, + patient: { patient_id: 'the_patient_id', type: 'person', _id: 'some uuid' } + }; + + sinon.stub(transitionUtils, 'addRegistrationNotFoundError'); + + return transition.onMatch({ doc }).then(result => { + result.should.equal(true); + fireConfiguredTriggers.callCount.should.equal(1); + fireConfiguredTriggers.args[0].should.deep.equal([registrationConfig, doc]); + transitionUtils.addRegistrationNotFoundError.callCount.should.equal(0); + }); + }); + }); +}); From 6ab02533d37314a214e946a71e4335b06e5c977f Mon Sep 17 00:00:00 2001 From: Prajwol Shrestha Date: Tue, 16 May 2023 10:52:41 +0545 Subject: [PATCH 03/56] registration.js add phone number function added --- .../src/transitions/registration.js | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index c0525a3aaa2..5fa337b8aab 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -185,6 +185,20 @@ const setBirthDate = doc => { } }; +const getPhoneNumber = doc => { + if (!doc || !doc.fields) { + return ''; + } + return doc.fields.phone_number; +}; + +const setPhoneNumber = doc => { + const phoneNumber = getPhoneNumber(doc); + // get default country code + //countryCode = codeFromSettings() + doc.phone_number = countryCode.concat(phoneNumber.toString()); +}; + const getConfig = () => config.get('registrations'); /* @@ -219,6 +233,9 @@ const triggers = { logger.warn('Use of add_patient_id trigger. This is deprecated in favour of add_patient.'); return triggers.add_patient(options); }, + add_phone_number: (options) => { + return setPhoneNumber(options.doc); + }, add_expected_date: (options) => { return setExpectedBirthDate(options.doc); }, @@ -309,7 +326,7 @@ const addMessages = (config, doc) => { utils.getRegistrations({ id: patientId }), utils.getRegistrations({ id: placeId }), ]) - .then(([ patientRegistrations, placeRegistrations ]) => { + .then(([patientRegistrations, placeRegistrations]) => { const context = { patient: doc.patient, place: doc.place, @@ -337,7 +354,7 @@ const assignSchedule = (options) => { utils.getRegistrations({ id: patientId }), utils.getRegistrations({ id: placeId }), ]) - .then(([ patientRegistrations, placeRegistrations ]) => { + .then(([patientRegistrations, placeRegistrations]) => { options.params.forEach(scheduleName => { const schedule = schedules.getScheduleConfig(scheduleName); const context = { From 439cb8b002c1ec9c341002652fb40d392508f5ad Mon Sep 17 00:00:00 2001 From: root Date: Fri, 19 May 2023 01:10:08 +0545 Subject: [PATCH 04/56] Added phone_number parser using google lib and changed app settings to include new person with phone number --- api/src/services/report/smsparser.js | 39 +++--- config/default/app_settings.json | 195 ++++++++++++++++++--------- 2 files changed, 152 insertions(+), 82 deletions(-) diff --git a/api/src/services/report/smsparser.js b/api/src/services/report/smsparser.js index dc4082b5277..821a1d92337 100644 --- a/api/src/services/report/smsparser.js +++ b/api/src/services/report/smsparser.js @@ -8,6 +8,7 @@ const textformsParser = require('./textforms-parser'); const logger = require('../../logger'); const moment = require('moment'); const bs = require('bikram-sambat'); +const phoneNumberParser = require('@medic/phone-number'); const MUVUKU_REGEX = /^\s*([A-Za-z]?\d)!.+!.+/; // matches invisible characters that can mess up our parsing @@ -78,12 +79,12 @@ const getParser = (exports.getParser = (def, doc) => { const match = msg.match(MUVUKU_REGEX); if (match && match[1]) { switch (match[1].toUpperCase()) { - case '1': - return mpParser.parse; - case 'J1': - return javarosaParser.parse; - default: - return; + case '1': + return mpParser.parse; + case 'J1': + return javarosaParser.parse; + default: + return; } } msg = stripFormCode(code, msg); @@ -209,9 +210,15 @@ const fieldParsers = { month: (raw) => { // keep months integers, not their list value. return parseNum(stripInvisibleCharacters(raw)); + }, + phone_number: (raw) => { + if (phoneNumberParser.validate(config.getAll(), raw)) { + return raw; + } } }; +//selects parser by field type and parses and validates the given data. exports.parseField = (field, raw, key) => { const parser = fieldParsers[field.type]; if (!parser) { @@ -281,21 +288,21 @@ exports.parse = (def, doc) => { } } - if(aggregateBSDateField) { + if (aggregateBSDateField) { let bsYear; let bsMonth = 1; let bsDay = 1; for (const k of Object.keys(def.fields)) { switch (def.fields[k].type) { - case 'bsYear': - bsYear = msgData[k]; - break; - case 'bsMonth': - bsMonth = msgData[k]; - break; - case 'bsDay': - bsDay = msgData[k]; - break; + case 'bsYear': + bsYear = msgData[k]; + break; + case 'bsMonth': + bsMonth = msgData[k]; + break; + case 'bsDay': + bsDay = msgData[k]; + break; } } diff --git a/config/default/app_settings.json b/config/default/app_settings.json index 79724ad0ae8..b9f907d30b2 100644 --- a/config/default/app_settings.json +++ b/config/default/app_settings.json @@ -271,22 +271,61 @@ ], "registrations": [ { - "form":"NP", - "events": [ - { - "name": "on_create", - "trigger": "add_phone_number", - "params": "phone_number", - "bool_expr": "doc.fields.phone_number || doc.fields.phone_number === 0" - }, - { - "name": "on_create", - "trigger": "add_patient", - "params": "", - "bool_expr": "" - } - ] - } + "form": "NP", + "events": [{ + "name": "on_create", + "trigger": "add_phone_number", + "params": "phone_number", + "bool_expr": "doc.fields.phone_number || doc.fields.phone_number === 0" + }, + { + "name": "on_create", + "trigger": "add_patient", + "params": "", + "bool_expr": "" + } + ], + "validations": { + "join_responses": false, + "list": [ + { + "property": "patient_age", + "rule": "(integer && min(0) && max(125))", + "translation_key": "", + "message": [{ + "content": "The registration format for {{patient_name}} is incorrect, please ensure that age is a number between 0 and 125.", + "locale": "en" + }] + }, + { + "property": "patient_name", + "rule": "(string && lenMin(1) && lenMax(30))", + "translation_key": "", + "message": [{ + "content": "The registration format for {{patient_name}} is incorrect, please ensure that name length is between 2 and 42.", + "locale": "en" + }] + }, + { + "property": "phone_nume", + "rule": "(lenMin(10) && lenMax(13))", + "translation_key": "", + "message": [{ + "content": "The registration format for {{patient_name}} is incorrect, please ensure that phone number length is between 9 and 13.", + "locale": "en" + }] + } + ] + }, + "messages": [{ + "translation_key": "", + "message": [{ + "content": "Thankyou{{contact.name}}, for registering {{patient_name}} with ID: ({{patient_id}})", + "locale": "" + }], + "recipient": "" + }] + }, { "form": "CW", "events": [ @@ -3931,56 +3970,80 @@ "date_field": "fields.date_of_death" }, "forms": { - "NP":{ - "meta": { - "code": "NP", - "label": { - "en": "New Person with phone_number Registration" - } - }, - "fields": { - "phone": { - "labels": { - "tiny": { - "en": "phone number" - }, - "description": { - "en": "phone number" - }, - "short": { - "en": "phone number" - } - }, - "position": 0, - "type": "integer", - "length": [ - 9, - 10 - ], - "required": true - }, - "patient_name": { - "labels": { - "tiny": { - "en": "patient_name" - }, - "description": { - "en": "Patient name" - }, - "short": { - "en": "Patient name" - } - }, - "position": 1, - "type": "string", - "length": [ - 3, - 30 - ], - "required": true - } - } - }, + "NP": { + "meta": { + "code": "NP", + "translation_key": "forms.np.title", + "label": { + "en": "New Person with phone_number Registration" + } + }, + "fields": { + "patient_age": { + "labels": { + "tiny": { + "en": "Person Age" + }, + "short": { + "en": "Person Age" + } + }, + "position": 0, + "type": "integer", + "length": [ + 1, + 3 + ], + "range": [ + 0, + 125 + ], + "required": true + }, + "patient_name": { + "labels": { + "tiny": { + "en": "patient_name" + }, + "description": { + "en": "Patient name" + }, + "short": { + "en": "Patient name" + } + }, + "position": 1, + "type": "string", + "length": [ + 3, + 30 + ], + "required": true + }, + "phone": { + "labels": { + "tiny": { + "en": "phone number" + }, + "description": { + "en": "phone number" + }, + "short": { + "en": "phone number" + } + }, + "position": 2, + "type": "phone_number", + "length": [ + 9, + 10 + ], + "required": true + } + }, + "public_form": false, + "use_sentinel": true + }, "DR": { "meta": { "code": "DR", From 1184519cbf20d7561d509d15240f0a7d30544b00 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 19 May 2023 14:12:19 +0545 Subject: [PATCH 05/56] phone number length validation --- config/default/app_settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/default/app_settings.json b/config/default/app_settings.json index b9f907d30b2..bff2982083d 100644 --- a/config/default/app_settings.json +++ b/config/default/app_settings.json @@ -307,8 +307,8 @@ }] }, { - "property": "phone_nume", - "rule": "(lenMin(10) && lenMax(13))", + "property": "phone_number", + "rule": "(lenMin(9) && lenMax(13))", "translation_key": "", "message": [{ "content": "The registration format for {{patient_name}} is incorrect, please ensure that phone number length is between 9 and 13.", From 61de363260f415274d0952f0b2315aba74cb6ea3 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 31 May 2023 09:35:09 +0545 Subject: [PATCH 06/56] Added support for phone number and concrete unit tests --- api/src/services/report/smsparser.js | 10 +- api/tests/form-definitions.js | 67 +- .../mocha/services/report/smsparser.spec.js | 156 +- config/default/app_settings.json | 10420 +++++----------- shared-libs/phone-number/src/phone-number.js | 22 +- .../src/transitions/registration.js | 3 +- .../transitions/src/transitions/utils.js | 7 +- 7 files changed, 3121 insertions(+), 7564 deletions(-) diff --git a/api/src/services/report/smsparser.js b/api/src/services/report/smsparser.js index 821a1d92337..2c9318f05ef 100644 --- a/api/src/services/report/smsparser.js +++ b/api/src/services/report/smsparser.js @@ -212,9 +212,15 @@ const fieldParsers = { return parseNum(stripInvisibleCharacters(raw)); }, phone_number: (raw) => { - if (phoneNumberParser.validate(config.getAll(), raw)) { - return raw; + let formattedAndValidatedPhone = phoneNumberParser.format(config.getAll(), raw); + if (formattedAndValidatedPhone) { + return formattedAndValidatedPhone; } + else { + logger.error(`The provided phone number ${raw} is invalid`); + return null; + } + } }; diff --git a/api/tests/form-definitions.js b/api/tests/form-definitions.js index 955c49bb83b..3efa90228ea 100644 --- a/api/tests/form-definitions.js +++ b/api/tests/form-definitions.js @@ -791,5 +791,70 @@ exports.forms = { required: true } } - } + }, + YYYR: { + meta: { + code: 'YYYR', + label: 'Test date parts in BS calendar without bsMonth, bsDay' + }, + fields: { + patient_id: { + labels: { + long: 'Patient ID', + tiny: 'ID' + }, + type: 'string', + required: true + }, + lmp_year: { + labels: { + short: 'Year', + tiny: 'Y' + }, + type: 'bsYear', + required: true + }, + lmp_date: { + labels: { + short: 'LMP Date', + tiny: 'LMP' + }, + type: 'bsAggreDate', + required: true + } + } + }, + "NP": { + "meta": { + "code": "NP", + "translation_key": "forms.np.title", + "label": { + "en": "New Person with phone_number Registration" + } + }, + "fields": { + "phone_number": { + "labels": { + "tiny": { + "en": "phone number" + }, + "description": { + "en": "phone number" + }, + "short": { + "en": "phone number" + } + }, + "position": 2, + "type": "phone_number", + "length": [ + 9, + 16 + ], + "required": true + } + }, + "public_form": false, + "use_sentinel": true + }, }; diff --git a/api/tests/mocha/services/report/smsparser.spec.js b/api/tests/mocha/services/report/smsparser.spec.js index 3153df126d3..205dd61dfc9 100644 --- a/api/tests/mocha/services/report/smsparser.spec.js +++ b/api/tests/mocha/services/report/smsparser.spec.js @@ -97,10 +97,60 @@ describe('sms parser', () => { chai.expect(smsparser.getFormCode('द CDT33')).to.equal('द'); }); + it('accepts form with phone_number type', () => { + const doc = { message: '1234' }; + const def = definitions.forms.NP; + sinon.stub(config, 'getAll').returns({ + nepal_country_code: 977 + }); + const data = smsparser.parse(def, doc); + chai.expect(data.phone_number).to.equal(null); + }); + + it('accepts phone number with extension', () => { + const doc = { message: '+9779841161718' }; + const def = definitions.forms.NP; + sinon.stub(config, 'getAll').returns({ + default_country_code: 977 + }); + const data = smsparser.parse(def, doc); + chai.expect(data.phone_number).to.equal("+977 984-1161718"); + }); + + it('accepts correct phone number without extension', () => { + const doc = { message: '9841161718' }; + const def = definitions.forms.NP; + sinon.stub(config, 'getAll').returns({ + default_country_code: 977 + }); + const data = smsparser.parse(def, doc); + chai.expect(data.phone_number).to.equal("+977 984-1161718"); + }); + + it('returns null if phone number is invalid for the region', () => { + const doc = { message: '+97712312' }; + const def = definitions.forms.NP; + sinon.stub(config, 'getAll').returns({ + default_country_code: 977 + }); + const data = smsparser.parse(def, doc); + chai.expect(data.phone_number).to.equal(null); + }); + + it('returns null if phone number is invalid for the region', () => { + const doc = { message: '8750660880' }; + const def = definitions.forms.NP; + sinon.stub(config, 'getAll').returns({ + default_country_code: 977 + }); + const data = smsparser.parse(def, doc); + chai.expect(data.phone_number).to.equal(null); + }); + it('parse month type', () => { const doc = { message: '1!FOO!10' }; const def = { - meta: {code: 'FOO', label: 'Test Monthly Report'}, + meta: { code: 'FOO', label: 'Test Monthly Report' }, fields: { month: { labels: { @@ -118,9 +168,9 @@ describe('sms parser', () => { it('form not found', () => { const doc = { - message:'1!X0X0!facility#2011#11#1#2#3#4#5#6#9#8#7#6#5#4', - type:'sms_message', - form:'X0X0' + message: '1!X0X0!facility#2011#11#1#2#3#4#5#6#9#8#7#6#5#4', + type: 'sms_message', + form: 'X0X0' }; const def = {}; const data = smsparser.parse(def, doc); @@ -129,9 +179,9 @@ describe('sms parser', () => { it('wrong field type', () => { const doc = { - message:'1!YYYY!facility#2011#11#yyyyy#zzzz#2#3#4#5#6#9#8#7#6#5#4', - type:'sms_message', - form:'YYYY' + message: '1!YYYY!facility#2011#11#yyyyy#zzzz#2#3#4#5#6#9#8#7#6#5#4', + type: 'sms_message', + form: 'YYYY' }; const def = definitions.forms.YYYY; const data = smsparser.parse(def, doc); @@ -161,9 +211,9 @@ describe('sms parser', () => { it('missing fields', () => { const doc = { - message:'1!YYYY!facility#2011#11#1#1#2#3', - type:'sms_message', - form:'YYYY' + message: '1!YYYY!facility#2011#11#1#1#2#3', + type: 'sms_message', + form: 'YYYY' }; const def = definitions.forms.YYYY; const data = smsparser.parse(def, doc); @@ -193,9 +243,9 @@ describe('sms parser', () => { it('extra fields', () => { const doc = { - message:'1!YYYY!facility#2011#11#0#1#2#3#1#1#1#1#1#1#1#1#1#1#####77#', - type:'sms_message', - form:'YYYY' + message: '1!YYYY!facility#2011#11#0#1#2#3#1#1#1#1#1#1#1#1#1#1#####77#', + type: 'sms_message', + form: 'YYYY' }; const def = definitions.forms.YYYY; const data = smsparser.parse(def, doc); @@ -383,7 +433,7 @@ describe('sms parser', () => { }; const data = smsparser.parse(def, sms); // q2 should be null. empty string attempted to be parsed as number. - chai.expect(data).to.deep.equal({q1: 'No', q2: null}); + chai.expect(data).to.deep.equal({ q1: 'No', q2: null }); }); it('parse zero value list field', () => { @@ -400,7 +450,7 @@ describe('sms parser', () => { } }; const data = smsparser.parse(def, sms); - chai.expect(data).to.deep.equal({q1: 'Yes'}); + chai.expect(data).to.deep.equal({ q1: 'Yes' }); }); it('ignore whitespace in list field textforms', () => { @@ -525,7 +575,7 @@ describe('sms parser', () => { } }; const data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({testdate: 1331510400000}); + chai.expect(data).to.deep.equal({ testdate: 1331510400000 }); }); it('parse date field yyyz: muvuku', () => { @@ -543,7 +593,7 @@ describe('sms parser', () => { const doc = { message: 'YYYZ BIR2012-03-12' }; const def = definitions.forms.YYYZ; const data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({birthdate: 1331510400000}); + chai.expect(data).to.deep.equal({ birthdate: 1331510400000 }); }); it('parse bsDate field: muvuku', () => { @@ -563,7 +613,7 @@ describe('sms parser', () => { } }; const data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({testdate: moment('2012-03-12').valueOf()}); + chai.expect(data).to.deep.equal({ testdate: moment('2012-03-12').valueOf() }); }); it('parse bsDate field: compact textforms', () => { @@ -583,7 +633,7 @@ describe('sms parser', () => { } }; const data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({testdate: moment('2012-03-12').valueOf()}); + chai.expect(data).to.deep.equal({ testdate: moment('2012-03-12').valueOf() }); }); it('parse bsDate field yyyt: muvuku', () => { @@ -610,7 +660,7 @@ describe('sms parser', () => { const doc = { message: '12345 2068-11-32' }; const def = definitions.forms.YYYT; const data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({patient_id: '12345', lmp_date: null}); + chai.expect(data).to.deep.equal({ patient_id: '12345', lmp_date: null }); }); it('parse BS date parts yyys 2: textforms', () => { @@ -679,7 +729,7 @@ describe('sms parser', () => { } }; const data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({testbool: true}); + chai.expect(data).to.deep.equal({ testbool: true }); }); it('parse boolean field: false', () => { @@ -693,7 +743,7 @@ describe('sms parser', () => { } }; const data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({testbool: false}); + chai.expect(data).to.deep.equal({ testbool: false }); }); it('parse string field mixed: muvuku', () => { @@ -713,7 +763,7 @@ describe('sms parser', () => { } }; const data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({foo: '16A'}); + chai.expect(data).to.deep.equal({ foo: '16A' }); }); it('parse string field mixed: textforms', () => { @@ -733,7 +783,7 @@ describe('sms parser', () => { } }; const data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({foo: '16A'}); + chai.expect(data).to.deep.equal({ foo: '16A' }); }); it('parse string field with exlamation mark: textforms', () => { @@ -753,7 +803,7 @@ describe('sms parser', () => { } }; const data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({foo: '16A!'}); + chai.expect(data).to.deep.equal({ foo: '16A!' }); }); it('parse string field with exlamation mark: muvuku', () => { @@ -773,7 +823,7 @@ describe('sms parser', () => { } }; const data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({foo: '16A!'}); + chai.expect(data).to.deep.equal({ foo: '16A!' }); }); it('parse string field leading zero: textforms', () => { @@ -793,7 +843,7 @@ describe('sms parser', () => { } }; const data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({foo: '012345'}); + chai.expect(data).to.deep.equal({ foo: '012345' }); }); it('parse string field leading zero: muvuku', () => { @@ -813,14 +863,14 @@ describe('sms parser', () => { } }; const data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({foo: '012345'}); + chai.expect(data).to.deep.equal({ foo: '012345' }); }); it('smsformats unstructured', () => { const docs = [ { message: 'testing one two three.' }, { message: 'HELP ME' }, - { message: ''} // blank message + { message: '' } // blank message ]; docs.forEach(doc => { chai.expect(smsparser.parse(null, doc)).to.deep.equal({}); @@ -829,8 +879,8 @@ describe('sms parser', () => { it('smsformats structured but no form', () => { const docs = [ - {message: '1!0000!1'}, - {message: '0000 ABC 123-123-123'} + { message: '1!0000!1' }, + { message: '0000 ABC 123-123-123' } ]; docs.forEach(doc => { chai.expect(smsparser.parse(null, doc)).to.deep.equal({}); @@ -930,7 +980,7 @@ describe('sms parser', () => { sent_timestamp: '12-11-11 15:00', from: '+15551212', message: 'J1!YYYY!EDO#4#ODT#5#RPM#11#L2T#2#HFI#facility#CDT#3#CDO#7#MSP#0#ZDO#6#L1O#9#RPY#2011#EOT#6#ODO#' + - '5#L1T#1#ZDT#4#L2O#8' + '5#L1T#1#ZDT#4#L2O#8' }; const actual = smsparser.parse(def, doc); chai.expect(actual).to.deep.equal({ @@ -1213,7 +1263,7 @@ describe('sms parser', () => { } }, position: 1, - length: [ 3, 100 ], + length: [3, 100], type: 'string' } } @@ -1243,17 +1293,17 @@ describe('sms parser', () => { // textforms let doc = { message: 'ग foo 16A' }; let data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({foo: '16A'}); + chai.expect(data).to.deep.equal({ foo: '16A' }); // compact doc = { message: 'ग 16A' }; data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({foo: '16A'}); + chai.expect(data).to.deep.equal({ foo: '16A' }); // muvuku doc = { message: '1!ग!16A' }; data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({foo: '16A'}); + chai.expect(data).to.deep.equal({ foo: '16A' }); }); it('support textforms locale on tiny labels', () => { @@ -1282,7 +1332,7 @@ describe('sms parser', () => { locale: 'sw' }; let data = smsparser.parse(def, doc, doc.locale); - chai.expect(data).to.deep.equal({name: 'n jane'}); + chai.expect(data).to.deep.equal({ name: 'n jane' }); // textforms with locale match parses correctly doc = { @@ -1290,7 +1340,7 @@ describe('sms parser', () => { locale: 'sw' }; data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({name: 'jane'}); + chai.expect(data).to.deep.equal({ name: 'jane' }); // same thing but case insensitive check doc = { @@ -1298,21 +1348,21 @@ describe('sms parser', () => { locale: 'sw' }; data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({name: 'jane'}); + chai.expect(data).to.deep.equal({ name: 'jane' }); // compact parses correctly doc = { message: 'R jane' }; data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({name: 'jane'}); + chai.expect(data).to.deep.equal({ name: 'jane' }); // muvuku parses correctly doc = { message: '1!R!jane' }; data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({name: 'jane'}); + chai.expect(data).to.deep.equal({ name: 'jane' }); }); it('support translation keys on tiny labels', () => { @@ -1343,7 +1393,7 @@ describe('sms parser', () => { locale: 'sw' }; let data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({name: 'jane'}); + chai.expect(data).to.deep.equal({ name: 'jane' }); // same thing but case insensitive check doc = { @@ -1351,21 +1401,21 @@ describe('sms parser', () => { locale: 'sw' }; data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({name: 'jane'}); + chai.expect(data).to.deep.equal({ name: 'jane' }); // compact parses correctly doc = { message: 'R jane' }; data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({name: 'jane'}); + chai.expect(data).to.deep.equal({ name: 'jane' }); // muvuku parses correctly doc = { message: '1!R!jane' }; data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({name: 'jane'}); + chai.expect(data).to.deep.equal({ name: 'jane' }); }); it('support mixed case field keys', () => { @@ -1389,21 +1439,21 @@ describe('sms parser', () => { message: 'R n jane', }; let data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({ooOoo: 'jane'}); + chai.expect(data).to.deep.equal({ ooOoo: 'jane' }); // compact textforms doc = { message: 'R jane' }; data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({ooOoo: 'jane'}); + chai.expect(data).to.deep.equal({ ooOoo: 'jane' }); // muvuku doc = { message: '1!R!jane' }; data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({ooOoo: 'jane'}); + chai.expect(data).to.deep.equal({ ooOoo: 'jane' }); }); it('support uppercase field keys', () => { @@ -1427,21 +1477,21 @@ describe('sms parser', () => { message: 'R n jane', }; let data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({OOOOO: 'jane'}); + chai.expect(data).to.deep.equal({ OOOOO: 'jane' }); // compact textforms doc = { message: 'R jane' }; data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({OOOOO: 'jane'}); + chai.expect(data).to.deep.equal({ OOOOO: 'jane' }); // muvuku doc = { message: '1!R!jane' }; data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({OOOOO: 'jane'}); + chai.expect(data).to.deep.equal({ OOOOO: 'jane' }); }); it('support regex chars in form code, parser escapes them', () => { @@ -1465,7 +1515,7 @@ describe('sms parser', () => { message: '.*.* n jane' }; const data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({name: 'jane'}); + chai.expect(data).to.deep.equal({ name: 'jane' }); }); it('stop input values from getting translated', () => { @@ -1497,7 +1547,7 @@ describe('sms parser', () => { message: 'J1!c_imm!bcg#yes#ch#no' }; const data = smsparser.parse(def, doc); - chai.expect(data).to.deep.equal({bcg: 'yes', ch:'no'}); + chai.expect(data).to.deep.equal({ bcg: 'yes', ch: 'no' }); chai.expect(config.translate.callCount).to.equal(2); }); diff --git a/config/default/app_settings.json b/config/default/app_settings.json index bff2982083d..bf5e99a9063 100644 --- a/config/default/app_settings.json +++ b/config/default/app_settings.json @@ -1,276 +1,424 @@ { - "locales": [ - { - "code": "en", - "name": "English" - }, - { - "code": "es", - "name": "Español (Spanish)" - }, - { - "code": "fr", - "name": "Français (French)" - }, - { - "code": "ne", - "name": "नेपाली (Nepali)" - }, - { - "code": "sw", - "name": "Kiswahili (Swahili)" - }, - { - "code": "hi", - "name": "हिन्दी (Hindi)" - }, - { - "code": "id", - "name": "Bahasa Indonesia (Indonesian)" - } - ], - "locale": "en", - "locale_outgoing": "en", - "muvuku_webapp_url": "/medic-reporter/_design/medic-reporter/_rewrite/?_embed_mode=2&_locale=en#/generic-anc.json", - "date_format": "ddd, MMM Do, YYYY", - "reported_date_format": "ddd, MMM Do, YYYY [at] h:mm A", - "forms_only_mode": false, - "default_responses": { - "start_date": "1970-01-01" - }, - "district_admins_access_unallocated_messages": false, - "public_access": false, - "default_country_code": "254", - "gateway_number": "+254741095736", - "anc_forms": { - "registration": "R", - "registrationLmp": "P", - "visit": "V", - "delivery": "D", - "flag": "F" - }, - "schedule_morning_hours": 6, - "schedule_morning_minutes": 0, - "schedule_evening_hours": 20, - "schedule_evening_minutes": 0, - "synthetic_date": "", - "contact_display_short": "clinic.name", - "roles": { - "national_admin": { - "name": "usertype.national-manager" - }, - "district_admin": { - "name": "usertype.district-manager", - "offline": true - }, - "data_entry": { - "name": "usertype.data-entry" - }, - "analytics": { - "name": "usertype.analytics" - }, - "gateway": { - "name": "usertype.gateway" - } - }, - "permissions": { - "can_export_messages": [ - "national_admin", - "district_admin", - "analytics" - ], - "can_export_forms": [ - "national_admin", - "district_admin", - "analytics" - ], - "can_export_contacts": [ - "national_admin", - "district_admin" - ], - "can_export_feedback": [ - "national_admin" - ], - "can_view_analytics": [ - "national_admin", - "district_admin", - "analytics" - ], - "can_view_data_records": [ - "national_admin", - "district_admin", - "analytics", - "gateway" - ], - "can_view_unallocated_data_records": [ - "national_admin", - "district_admin", - "gateway" - ], - "can_view_users": [ - "national_admin" - ], - "can_edit": [ - "national_admin", - "district_admin", - "gateway" - ], - "can_log_out_on_android": [ - "district_admin", - "national_admin" - ], - "can_update_messages": [ - "national_admin", - "district_admin", - "gateway" - ], - "can_update_users": [ - "national_admin" - ], - "can_export_dhis": [ - "national_admin" - ], - "can_update_people": [ - "national_admin", - "district_admin" - ], - "can_update_places": [ - "national_admin", - "district_admin" - ], - "can_create_records": [ - "national_admin", - "district_admin", - "data_entry", - "gateway" - ], - "can_access_gateway_api": [ - "gateway" - ], - "can_create_users": [ - "national_admin" - ], - "can_create_people": [ - "national_admin", - "district_admin" - ], - "can_create_places": [ - "national_admin", - "district_admin" - ], - "can_view_tasks": [ - "district_admin" - ], - "can_view_tasks_tab": [ - "district_admin" - ], - "can_configure": [ - "national_admin" - ], - "can_view_messages": [ - "national_admin", - "district_admin" - ], - "can_view_messages_tab": [ - "national_admin", - "district_admin" - ], - "can_view_reports": [ - "national_admin", - "district_admin" - ], - "can_view_reports_tab": [ - "national_admin", - "district_admin" - ], - "can_view_analytics_tab": [ - "national_admin", - "district_admin" - ], - "can_view_contacts": [ - "national_admin", - "district_admin" - ], - "can_view_contacts_tab": [ - "national_admin", - "district_admin" - ], - "can_edit_profile": [ - "national_admin", - "district_admin" - ], - "can_delete_reports": [ - "national_admin", - "district_admin" - ], - "can_bulk_delete_reports": [ - "national_admin", - "district_admin" - ], - "can_update_reports": [ - "national_admin", - "district_admin" - ], - "can_delete_contacts": [ - "national_admin", - "district_admin" - ], - "can_delete_messages": [ - "national_admin", - "district_admin" - ], - "can_delete_users": [ - "national_admin" - ], - "can_view_outgoing_messages": [ - "national_admin" - ], - "can_export_all": [ - "national_admin" - ], - "can_view_uhc_stats": [], - "can_view_old_filter_and_search": [], - "can_view_old_action_bar": [] - }, - "setup_complete": true, - "outgoing_phone_replace": { - "match": "", - "replace": "" - }, - "outgoing_phone_filters": [ - { - "match": "", - "replace": "" - } - ], - "id_format": "1111", - "alerts": [ - { - "form": "", - "condition": "", - "message": "", - "recipient": "" - } - ], - "reminders": [ - { - "form": "", - "text_expression": "", - "cron": "", - "mute_after_form_for": "", - "message": "" - } - ], - "replications": [ - { - "fromSuffix": "user-[^\\-]+-meta", - "toSuffix": "users-meta", - "text_expression": "", - "cron": "0 2 * * *" - } - ], - "registrations": [ - { + "locales": null, + "locale": "en", + "locale_outgoing": "ne", + "date_format": "ddd, MMM Do, YYYY", + "reported_date_format": "ddd, MMM Do, YYYY [at] h:mm A", + "forms_only_mode": false, + "place_hierarchy_types": [ + "district_hospital", + "health_center" + ], + "default_responses": { + "start_date": "1970-01-01" + }, + "district_admins_access_unallocated_messages": false, + "public_access": false, + "default_country_code": 977, + "gateway_number": "+9779812345678", + "dhis_data_sets": [{ + "id": "VMuFODsyWaO", + "translation_key": "dhis.dataset.monthly.integration" + }], + "schedule_morning_hours": 6, + "schedule_morning_minutes": 0, + "schedule_evening_hours": 20, + "schedule_evening_minutes": 0, + "synthetic_date": "", + "contact_display_short": "clinic.name", + "roles": { + "national_admin": { + "name": "usertype.national-manager" + }, + "district_admin": { + "name": "usertype.district-manager", + "offline": true + }, + "data_entry": { + "name": "usertype.data-entry" + }, + "analytics": { + "name": "usertype.analytics" + }, + "gateway": { + "name": "usertype.gateway" + }, + "district_level": { + "name": "usertype.district_level" + }, + "munic_level": { + "name": "usertype.munic_level", + "offline": true + }, + "hf_level": { + "name": "usertype.hf_level", + "offline": true + } + }, + "permissions": { + "can_access_gateway_api": [ + "gateway" + ], + "can_bulk_delete_reports": [ + "national_admin", + "district_admin" + ], + "can_configure": [ + "national_admin" + ], + "can_create_people": [ + "national_admin" + ], + "can_create_places": [ + "national_admin" + ], + "can_create_records": [ + "national_admin", + "district_admin", + "data_entry", + "gateway" + ], + "can_create_users": [ + "national_admin" + ], + "can_delete_contacts": [ + "national_admin", + "district_admin" + ], + "can_delete_messages": [ + "national_admin", + "district_admin" + ], + "can_delete_reports": [ + "national_admin", + "district_admin" + ], + "can_delete_users": [ + "national_admin" + ], + "can_edit": [ + "national_admin", + "district_admin", + "gateway" + ], + "can_edit_profile": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_edit_verification": [ + "national_admin", + "district_admin" + ], + "can_export_all": [ + "national_admin" + ], + "can_export_audit": [ + "national_admin" + ], + "can_export_contacts": [ + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_export_feedback": [], + "can_export_forms": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level", + "analytics" + ], + "can_export_messages": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level", + "analytics" + ], + "can_log_out_on_android": [], + "can_update_messages": [ + "national_admin", + "district_admin", + "gateway" + ], + "can_update_people": [ + "national_admin" + ], + "can_update_places": [ + "national_admin" + ], + "can_update_reports": [ + "national_admin" + ], + "can_update_users": [ + "national_admin" + ], + "can_verify_reports": [ + "national_admin", + "district_admin" + ], + "can_view_analytics": [ + "national_admin", + "analytics" + ], + "can_view_analytics_tab": [ + "national_admin" + ], + "can_view_call_action": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_view_contacts": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_view_contacts_tab": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_view_data_records": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level", + "analytics", + "gateway" + ], + "can_view_last_visited_date": [], + "can_view_message_action": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_view_messages": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_view_messages_tab": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_view_outgoing_messages": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_view_reports": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_view_reports_tab": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_view_tasks": [ + "district_admin" + ], + "can_view_tasks_tab": [ + "district_admin" + ], + "can_view_unallocated_data_records": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level", + "gateway" + ], + "can_view_users": [ + "national_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_write_wealth_quintiles": [], + "can_export_dhis": [ + "national_admin", + "crfo" + ], + "can_view_uhc_stats": [], + "can_view_tasks_group": [ + "chw" + ], + "can_aggregate_targets": [ + "chw_supervisor" + ], + "can_upgrade": [ + "program_officer" + ], + "can_view_old_filter_and_search": [], + "can_view_old_action_bar": [] + }, + "uhc": { + "contacts_default_sort": "", + "visit_count": { + "month_start_date": 1, + "visit_count_goal": 0 + } + }, + "death_reporting": { + "mark_deceased_forms": [ + "death_report" + ], + "date_field": "fields.death_details.date_of_death", + "undo_deceased_forms": [ + "undo_death_report" + ] + }, + "contact_types": [{ + "id": "district_hospital", + "name_key": "contact.type.district_hospital", + "group_key": "contact.type.district_hospital.plural", + "create_key": "contact.type.district_hospital.new", + "edit_key": "contact.type.place.edit", + "icon": "medic-district-hospital", + "create_form": "form:contact:district_hospital:create", + "edit_form": "form:contact:district_hospital:edit" + }, + { + "id": "health_center", + "name_key": "contact.type.health_center", + "group_key": "contact.type.health_center.plural", + "create_key": "contact.type.health_center.new", + "edit_key": "contact.type.place.edit", + "parents": [ + "district_hospital" + ], + "icon": "medic-health-center", + "create_form": "form:contact:health_center:create", + "edit_form": "form:contact:health_center:edit" + }, + { + "id": "clinic", + "name_key": "contact.type.clinic", + "group_key": "contact.type.clinic.plural", + "create_key": "contact.type.clinic.new", + "edit_key": "contact.type.place.edit", + "parents": [ + "health_center" + ], + "icon": "medic-clinic", + "create_form": "form:contact:clinic:create", + "edit_form": "form:contact:clinic:edit", + "count_visits": true + }, + { + "id": "person", + "name_key": "contact.type.person", + "group_key": "contact.type.person.plural", + "create_key": "contact.type.person.new", + "edit_key": "contact.type.person.edit", + "primary_contact_key": "clinic.field.contact", + "parents": [ + "district_hospital", + "health_center", + "clinic" + ], + "icon": "medic-person", + "create_form": "form:contact:person:create", + "edit_form": "form:contact:person:edit", + "person": true + } + ], + "setup_complete": true, + "contact_summary": "var ContactSummary = {}; !function(t,e){if('object'==typeof exports&&'object'==typeof module)module.exports=e();else if('function'==typeof define&&define.amd)define([],e);else{var n=e();for(var a in n)('object'==typeof exports?exports:t)[a]=n[a]}}(ContactSummary,(function(){return function(t){var e={};function n(a){if(e[a])return e[a].exports;var o=e[a]={i:a,l:!1,exports:{}};return t[a].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,a){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:a})},n.r=function(t){'undefined'!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:'Module'}),Object.defineProperty(t,'__esModule',{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&'object'==typeof t&&t&&t.__esModule)return t;var a=Object.create(null);if(n.r(a),Object.defineProperty(a,'default',{enumerable:!0,value:t}),2&e&&'string'!=typeof t)for(var o in t)n.d(a,o,function(e){return t[e]}.bind(null,o));return a},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,'a',e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p='',n(n.s=0)}([function(t,e,n){var a=n(1),o=n(2);t.exports=o(a,contact,reports)},function(t,e){const n={},a=[{appliesToType:'person',appliesIf:function(){return'chw'===contact.role},label:'female.community.health.volunteer',value:'',width:12},{appliesToType:'person',appliesIf:function(){return'chw'!==contact.role},label:'patient_id',value:contact.patient_id,width:6},{appliesToType:'person',appliesIf:function(){return'chw'!==contact.role},label:'contact.phone.number',value:contact.phone,width:6,filter:'phone'},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role},label:'contact.mhealth.phone.number',value:contact.phone,width:6,filter:'phone'},{appliesToType:'person',appliesIf:function(){return contact.date_of_birth},label:'contact.age',value:contact.date_of_birth,width:6,filter:'age'},{appliesToType:'person',appliesIf:function(){return!contact.date_of_birth&&contact.year_of_birth_approx},label:'contact.age',value:'duration.as.fchv',context:{year:(new Date).getFullYear()-contact.year_of_birth_approx},translate:!0,width:6},{appliesToType:'person',appliesIf:function(){return!contact.date_of_birth&&!contact.year_of_birth_approx},label:'contact.age',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role},label:'contact.education.level',value:'level.'+contact.level_of_education,width:6,translate:!0},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&''!==contact.start_date_as_fchv&&void 0!==contact.start_date_as_fchv},label:'contact.fchv.duration',value:'duration.as.fchv',context:{year:(new Date).getFullYear()-(contact.start_date_as_fchv-57)},translate:!0,width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&(''===contact.start_date_as_fchv||void 0===contact.start_date_as_fchv)},label:'contact.fchv.duration',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&''!==contact.training_date&&void 0!==contact.training_date},label:'contact.fchv.training',value:'duration.as.fchv',context:{year:(new Date).getFullYear()-(contact.training_date-57)},translate:!0,width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&(''===contact.training_date||void 0===contact.training_date)},label:'contact.fchv.training',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&''!==contact.fchv_id_number&&void 0!==contact.fchv_id_number},label:'contact.fchv.id',value:contact.fchv_id_number,width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&(''===contact.fchv_id_number||void 0===contact.fchv_id_number)},label:'contact.fchv.id',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&''!==contact.most_recent_fchv_training&&void 0!==contact.most_recent_fchv_training},label:'contact.fchv.refresher.training',value:contact.most_recent_fchv_training,filter:'relativeDay',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&(''===contact.most_recent_fchv_training||void 0===contact.most_recent_fchv_training)},label:'contact.fchv.refresher.training',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return''!==contact.phone_alternate&&void 0!==contact.phone_alternate},label:'contact.alternate.phone',value:contact.phone_alternate,width:6},{appliesToType:'person',appliesIf:function(){return''===contact.phone_alternate||void 0===contact.phone_alternate},label:'contact.alternate.phone',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return''!==contact.notes&&void 0!==contact.notes},label:'contact.notes',value:contact.notes,width:6},{appliesToType:'person',appliesIf:function(){return''===contact.notes||void 0===contact.notes},label:'contact.notes',value:'-',width:6},{appliesToType:'person',label:'contact.parent',value:lineage,filter:'lineage'},{appliesToType:'!person',label:'contact.notes',value:contact.notes,width:12},{appliesToType:'!person',appliesIf:function(){return contact.parent&&lineage[0]},label:'contact.parent',value:lineage,filter:'lineage'}];lineage[0]&&lineage[0].contact&&(n.chw_name=lineage[0].contact.name,n.chw_phone=lineage[0].contact.phone),t.exports={context:n,cards:[],fields:a}},function(t,e){function n(t){return t?Array.isArray(t)?t:[t]:[]}function a(t,e){switch(typeof t){case'undefined':return!0;case'function':return t(e);default:return t}}function o(t,e,n){if(a(t.appliesIf,n)){var o='function'==typeof t.fields?t.fields(n):t.fields.filter((function(t){return a(t.appliesIf,n)})).map((function(t){var e={};return c(t,e,'label'),c(t,e,'value'),c(t,e,'translate'),c(t,e,'filter'),c(t,e,'width'),c(t,e,'icon'),t.context&&(e.context={},c(t.context,e.context,'count'),c(t.context,e.context,'total')),e}));return t.modifyContext&&t.modifyContext(e,n),{label:t.label,fields:o}}function c(t,e,a){switch(typeof t[a]){case'undefined':return;case'function':e[a]=t[a](n);break;default:e[a]=t[a]}}}t.exports=function(t,e,a){var c=t.fields||[],r=t.context||{},i=t.cards||[],l=e&&('contact'===e.type?e.contact_type:e.type),p={cards:[],fields:c.filter((function(t){var e=n(t.appliesToType),a=e.filter((function(t){return t&&'!'===t.charAt(0)}));if((0===e.length||e.includes(l)||a.length>0&&!a.includes('!'+l))&&(!t.appliesIf||t.appliesIf()))return delete t.appliesToType,delete t.appliesIf,!0}))};return i.forEach((function(t){var e,c,i,f,u=n(t.appliesToType);if(u.includes('report')&&u.length>1)throw new Error('You cannot set appliesToType to an array which includes the type \\'report\\' and another type.');if(u.includes('report'))for(e=0;e0)return;(i=o(t,r))&&p.cards.push(i)}})),p.context=r,p}}])})); return ContactSummary;", + "tasks": { + "rules": "define Target { _id: null, contact: null, deleted: null, type: null, pass: null, date: null, groupBy: null }\ndefine Contact { contact: null, reports: null, tasks: null }\ndefine Task { _id: null, deleted: null, doc: null, contact: null, icon: null, date: null, readyStart: null, readyEnd: null, title: null, fields: null, resolved: null, priority: null, priorityLabel: null, reports: null, actions: null }\nrule GenerateEvents {\n when { c: Contact } then { !function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){'undefined'!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:'Module'}),Object.defineProperty(e,'__esModule',{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&'object'==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,'default',{enumerable:!0,value:e}),2&t&&'string'!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,'a',t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p='',n(n.s=1)}([function(e,t){e.exports=function(e){var t={};!function e(t,n){var r=Object.keys(t);for(var o in r){var a=r[o];switch(typeof t[a]){case'object':e(t[a],n);break;case'function':t[a]=t[a].bind(n)}}}(e,t),t.definition=function e(t){var n=Object.assign({},t),r=Object.keys(n);for(var o in r){var a=r[o];if(Array.isArray(n[a])){n[a]=n[a].slice(0);for(var i=0;i", - "silence_type": "ANC Reminders, ANC Reminders LMP, ANC Reminders LMP from App", - "silence_for": "8 days", - "fields": [ - { - "field_name": "", - "title": "" - } - ], - "validations": { - "join_responses": true, - "list": [ - { - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "messages.generic.validation.patient_id" - } - ] - }, - "messages": [ - { - "translation_key": "messages.v.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "IMM", - "name": "Immunization Visit (SMS)", - "format": "IMM ", - "silence_type": "", - "silence_for": "", - "fields": [ - { - "field_name": "", - "title": "" - } - ], - "validations": { - "join_responses": true, - "list": [ - { - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "messages.generic.validation.patient_id" - } - ] - }, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "M", - "name": "Postnatal Visit (SMS)", - "format": "M ", - "silence_type": "Postnatal Care, Postnatal Care for Facility Birth, Postnatal Care from App", - "silence_for": "0 days", - "fields": [ - { - "field_name": "", - "title": "" - } - ], - "validations": { - "join_responses": true, - "list": [ - { - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "messages.generic.validation.patient_id" - } - ] - }, - "messages": [ - { - "translation_key": "messages.m.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "F", - "name": "Flag (SMS)", - "format": "F ", - "silence_type": "", - "silence_for": "", - "fields": [ - { - "field_name": "", - "title": "" - } - ], - "validations": { - "join_responses": true, - "list": [ - { - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "messages.generic.validation.patient_id" - } - ] - }, - "messages": [ - { - "translation_key": "messages.f.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.f.report_accepted_parent", - "event_type": "report_accepted", - "recipient": "parent" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "PENTA1", - "name": " PENTA1 Pentavalent", - "format": "PENTA1 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "PENTA2", - "name": " PENTA2 Pentavalent", - "format": "PENTA2 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "PENTA3", - "name": " PENTA3 Pentavalent", - "format": "PENTA3 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "BCG", - "name": " BCG Bacille Calmette-Guerin", - "format": "BCG ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "OPV0", - "name": " OPV0 Oral polio", - "format": "OPV0 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "OPV1", - "name": " OPV1 Oral polio", - "format": "OPV1 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "OPV2", - "name": " OPV2 Oral polio", - "format": "OPV2 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "OPV3", - "name": " OPV3 Oral polio", - "format": "OPV3 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "IPV1", - "name": " IPV1 Inactivated polio", - "format": "IPV1 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "IPV2", - "name": " IPV2 Inactivated polio", - "format": "IPV2 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "IPV3", - "name": " IPV3 Inactivated polio", - "format": "IPV3 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "PCV1", - "name": " PCV1 Pneumococcal pneumonia", - "format": "PCV1 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "PCV2", - "name": " PCV2 Pneumococcal pneumonia", - "format": "PCV2 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "PCV3", - "name": " PCV3 Pneumococcal pneumonia", - "format": "PCV3 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "PCV4", - "name": " PCV4 Pneumococcal pneumonia", - "format": "PCV4 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "ROTA1", - "name": " ROTA1 Rotavirus", - "format": "ROTA1 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "ROTA2", - "name": " ROTA2 Rotavirus", - "format": "ROTA2 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "ROTA3", - "name": " ROTA3 Rotavirus", - "format": "ROTA3 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "VITA", - "name": " VITA Vitamin A", - "format": "VITA ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "MMR1", - "name": " MMR1 Measles Mumps Rubella", - "format": "MMR1 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "MMR2", - "name": " MMR2 Measles Mumps Rubella", - "format": "MMR2 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "MMRV1", - "name": " MMRV1 Measles Mumps Rubella Varicella", - "format": "MMRV1 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "MMRV2", - "name": " MMRV2 Measles Mumps Rubella Varicella", - "format": "MMRV2 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "MN1", - "name": " MN1 Meningococcal", - "format": "MN1 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "MN2", - "name": " MN2 Meningococcal", - "format": "MN2 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "MN3", - "name": " MN3 Meningococcal", - "format": "MN3 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "MN4", - "name": " MN4 Meningococcal", - "format": "MN4 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "FLU", - "name": " FLU Influenza", - "format": "FLU ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "HA1", - "name": " HA1 Hepatitis A", - "format": "HA1 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "HA2", - "name": " HA2 Hepatitis A", - "format": "HA2 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "JE", - "name": " JE Japanese encephalitis", - "format": "JE ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "YF", - "name": " YF Yellow fever", - "format": "YF ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "TY1", - "name": " TY1 Typhoid", - "format": "TY1 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "TY2", - "name": " TY2 Typhoid", - "format": "TY2 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "HPV1", - "name": " HPV1 Human papillomavirus", - "format": "HPV1 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "HPV2", - "name": " HPV2 Human papillomavirus", - "format": "HPV2 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "HPV3", - "name": " HPV3 Human papillomavirus", - "format": "HPV3 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "CH1", - "name": " CH1 Cholera", - "format": "CH1 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "CH2", - "name": " CH2 Cholera", - "format": "CH2 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "CH3", - "name": " CH3 Cholera", - "format": "CH3 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "HB", - "name": "HB Hepatitis B", - "format": "HB ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "FIPV1", - "name": "FIPV1 Fractional IPV", - "format": "FIPV1 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "FIPV2", - "name": "FIPV2 Fractional IPV", - "format": "FIPV2 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "DPT4", - "name": "DPT4 DPT Booster", - "format": "DPT4 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - }, - { - "form": "DPT5", - "name": "DPT5 DPT Booster", - "format": "DPT5 ", - "silence_type": "", - "silence_for": "", - "fields": [], - "validations": {}, - "messages": [ - { - "translation_key": "messages.generic.report_accepted", - "event_type": "report_accepted", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.generic.registration_not_found", - "event_type": "registration_not_found", - "recipient": "reporting_unit" - } - ] - } - ], - "schedules": [ - { - "name": "Registration Followup ANC", - "translation_key": "schedule.registration_anc", - "summary": "", - "description": "", - "start_from": "reported_date", - "messages": [ - { - "translation_key": "messages.schedule.registration.followup_anc", - "group": 1, - "offset": "7 days", - "send_day": "", - "send_time": "", - "recipient": "clinic" - } - ] - }, - { - "name": "Registration Followup ANC PNC", - "translation_key": "schedule.registration_anc_pnc", - "summary": "", - "description": "", - "start_from": "reported_date", - "messages": [ - { - "translation_key": "messages.schedule.registration.followup_anc_pnc", - "group": 1, - "offset": "7 days", - "send_day": "", - "send_time": "", - "recipient": "clinic" - } - ] - }, - { - "name": "Postnatal Care", - "translation_key": "schedule.pnc", - "summary": "", - "description": "", - "start_from": "birth_date", - "messages": [ - { - "translation_key": "messages.schedule.postnatal.day_0", - "group": 1, - "offset": "0", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.postnatal.day_0_overdue", - "group": 1, - "offset": "1 day", - "send_day": "", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.postnatal.day_3", - "group": 2, - "offset": "2 days", - "send_day": "", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.postnatal.day_3_overdue", - "group": 2, - "offset": "4 days", - "send_day": "", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.postnatal.day_7", - "group": 3, - "offset": "6 days", - "send_day": "", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.postnatal.day_7_overdue", - "group": 3, - "offset": "8 days", - "send_day": "", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.postnatal.week_6", - "group": 4, - "offset": "41 days", - "send_day": "", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.postnatal.week_6_overdue", - "group": 4, - "offset": "43 days", - "send_day": "", - "send_time": "9:00", - "recipient": "clinic" - } - ] - }, - { - "name": "Postnatal Care for Facility Birth", - "translation_key": "schedule.pnc_facility_birth", - "summary": "", - "description": "", - "start_from": "birth_date", - "messages": [ - { - "translation_key": "messages.schedule.postnatal.day_3", - "group": 1, - "offset": "2 days", - "send_day": "", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.postnatal.day_3_overdue", - "group": 1, - "offset": "4 days", - "send_day": "", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.postnatal.day_7", - "group": 2, - "offset": "6 days", - "send_day": "", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.postnatal.day_7_overdue", - "group": 2, - "offset": "8 days", - "send_day": "", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.postnatal.week_6", - "group": 3, - "offset": "41 days", - "send_day": "", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.postnatal.week_6_overdue", - "group": 3, - "offset": "43 days", - "send_day": "", - "send_time": "9:00", - "recipient": "clinic" - } - ] - }, - { - "name": "Postnatal Care from App", - "translation_key": "schedule.pnc_from_app", - "summary": "", - "description": "", - "start_from": "fields.birth_date", - "messages": [ - { - "translation_key": "messages.schedule.postnatal.day_3", - "group": 1, - "offset": "2 days", - "send_day": "", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.postnatal.day_3_overdue", - "group": 1, - "offset": "4 days", - "send_day": "", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.postnatal.day_7", - "group": 2, - "offset": "6 days", - "send_day": "", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.postnatal.day_7_overdue", - "group": 2, - "offset": "8 days", - "send_day": "", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.postnatal.week_6", - "group": 3, - "offset": "41 days", - "send_day": "", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.postnatal.week_6_overdue", - "group": 3, - "offset": "43 days", - "send_day": "", - "send_time": "9:00", - "recipient": "clinic" - } - ] - }, - { - "name": "Child Health", - "translation_key": "schedule.child_health", - "summary": "", - "description": "", - "start_from": "birth_date", - "messages": [ - { - "translation_key": "messages.schedule.child.month_01", - "group": 1, - "offset": "1 month", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_02", - "group": 2, - "offset": "2 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_03", - "group": 3, - "offset": "3 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_04", - "group": 4, - "offset": "4 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_05", - "group": 5, - "offset": "5 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_06", - "group": 6, - "offset": "6 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_07", - "group": 7, - "offset": "7 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_08", - "group": 8, - "offset": "8 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_09", - "group": 9, - "offset": "9 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_10", - "group": 10, - "offset": "10 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_11", - "group": 11, - "offset": "11 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_12", - "group": 12, - "offset": "12 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_13", - "group": 13, - "offset": "13 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_14", - "group": 14, - "offset": "14 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_15", - "group": 15, - "offset": "15 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_16", - "group": 16, - "offset": "16 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_17", - "group": 17, - "offset": "17 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_18", - "group": 18, - "offset": "18 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_19", - "group": 19, - "offset": "19 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_20", - "group": 20, - "offset": "20 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_21", - "group": 21, - "offset": "21 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_22", - "group": 22, - "offset": "22 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_23", - "group": 23, - "offset": "23 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_24", - "group": 24, - "offset": "24 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_25", - "group": 25, - "offset": "25 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_26", - "group": 26, - "offset": "26 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_27", - "group": 27, - "offset": "27 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_28", - "group": 28, - "offset": "28 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_29", - "group": 29, - "offset": "29 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_30", - "group": 30, - "offset": "30 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_31", - "group": 31, - "offset": "31 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_32", - "group": 32, - "offset": "32 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_33", - "group": 33, - "offset": "33 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_34", - "group": 34, - "offset": "34 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_35", - "group": 35, - "offset": "35 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_36", - "group": 36, - "offset": "36 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_37", - "group": 37, - "offset": "37 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_38", - "group": 38, - "offset": "38 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_39", - "group": 39, - "offset": "39 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_40", - "group": 40, - "offset": "40 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_41", - "group": 41, - "offset": "41 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_42", - "group": 42, - "offset": "42 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_43", - "group": 43, - "offset": "43 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_44", - "group": 44, - "offset": "44 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_45", - "group": 45, - "offset": "45 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_46", - "group": 46, - "offset": "46 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_47", - "group": 47, - "offset": "47 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_48", - "group": 48, - "offset": "48 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_49", - "group": 49, - "offset": "49 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_50", - "group": 50, - "offset": "50 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_51", - "group": 51, - "offset": "51 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_52", - "group": 52, - "offset": "52 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_53", - "group": 53, - "offset": "53 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_54", - "group": 54, - "offset": "54 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_55", - "group": 55, - "offset": "55 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_56", - "group": 56, - "offset": "56 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_57", - "group": 57, - "offset": "57 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_58", - "group": 58, - "offset": "58 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_59", - "group": 59, - "offset": "59 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_60", - "group": 60, - "offset": "60 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - } - ] - }, - { - "name": "Child Health from App", - "translation_key": "schedule.child_health", - "summary": "", - "description": "", - "start_from": "fields.birth_date", - "messages": [ - { - "translation_key": "messages.schedule.child.month_01", - "group": 1, - "offset": "1 month", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_02", - "group": 2, - "offset": "2 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_03", - "group": 3, - "offset": "3 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_04", - "group": 4, - "offset": "4 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_05", - "group": 5, - "offset": "5 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_06", - "group": 6, - "offset": "6 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_07", - "group": 7, - "offset": "7 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_08", - "group": 8, - "offset": "8 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_09", - "group": 9, - "offset": "9 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_10", - "group": 10, - "offset": "10 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_11", - "group": 11, - "offset": "11 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_12", - "group": 12, - "offset": "12 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_13", - "group": 13, - "offset": "13 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_14", - "group": 14, - "offset": "14 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_15", - "group": 15, - "offset": "15 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_16", - "group": 16, - "offset": "16 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_17", - "group": 17, - "offset": "17 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_18", - "group": 18, - "offset": "18 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_19", - "group": 19, - "offset": "19 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_20", - "group": 20, - "offset": "20 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_21", - "group": 21, - "offset": "21 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_22", - "group": 22, - "offset": "22 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_23", - "group": 23, - "offset": "23 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_24", - "group": 24, - "offset": "24 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_25", - "group": 25, - "offset": "25 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_26", - "group": 26, - "offset": "26 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_27", - "group": 27, - "offset": "27 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_28", - "group": 28, - "offset": "28 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_29", - "group": 29, - "offset": "29 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_30", - "group": 30, - "offset": "30 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_31", - "group": 31, - "offset": "31 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_32", - "group": 32, - "offset": "32 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_33", - "group": 33, - "offset": "33 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_34", - "group": 34, - "offset": "34 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_35", - "group": 35, - "offset": "35 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_36", - "group": 36, - "offset": "36 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_37", - "group": 37, - "offset": "37 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_38", - "group": 38, - "offset": "38 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_39", - "group": 39, - "offset": "39 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_40", - "group": 40, - "offset": "40 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_41", - "group": 41, - "offset": "41 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_42", - "group": 42, - "offset": "42 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_43", - "group": 43, - "offset": "43 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_44", - "group": 44, - "offset": "44 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_45", - "group": 45, - "offset": "45 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_46", - "group": 46, - "offset": "46 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_47", - "group": 47, - "offset": "47 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_48", - "group": 48, - "offset": "48 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_49", - "group": 49, - "offset": "49 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_50", - "group": 50, - "offset": "50 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_51", - "group": 51, - "offset": "51 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_52", - "group": 52, - "offset": "52 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_53", - "group": 53, - "offset": "53 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_54", - "group": 54, - "offset": "54 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_55", - "group": 55, - "offset": "55 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_56", - "group": 56, - "offset": "56 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_57", - "group": 57, - "offset": "57 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_58", - "group": 58, - "offset": "58 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_59", - "group": 59, - "offset": "59 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.child.month_60", - "group": 60, - "offset": "60 months", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - } - ] - }, - { - "name": "ANC Reminders LMP", - "translation_key": "schedule.anc_lmp", - "summary": "", - "description": "", - "start_from": "lmp_date", - "messages": [ - { - "translation_key": "messages.schedule.anc.reminder", - "group": 1, - "offset": "12 weeks", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.followup", - "group": 1, - "offset": "13 weeks", - "send_day": "monday", - "send_time": "10:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder", - "group": 2, - "offset": "24 weeks", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.followup", - "group": 2, - "offset": "25 weeks", - "send_day": "monday", - "send_time": "10:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder", - "group": 3, - "offset": "32 weeks", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.followup", - "group": 3, - "offset": "33 weeks", - "send_day": "monday", - "send_time": "10:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder", - "group": 4, - "offset": "36 weeks", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.followup", - "group": 4, - "offset": "37 weeks", - "send_day": "monday", - "send_time": "10:00", - "recipient": "clinic" - } - ] - }, - { - "name": "ANC Reminders LMP from App", - "translation_key": "schedule.anc_lmp_from_app", - "summary": "", - "description": "", - "start_from": "fields.lmp_date_8601", - "messages": [ - { - "translation_key": "messages.schedule.anc.reminder", - "group": 1, - "offset": "12 weeks", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.followup", - "group": 1, - "offset": "13 weeks", - "send_day": "monday", - "send_time": "10:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder", - "group": 2, - "offset": "24 weeks", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.followup", - "group": 2, - "offset": "25 weeks", - "send_day": "monday", - "send_time": "10:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder", - "group": 3, - "offset": "32 weeks", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.followup", - "group": 3, - "offset": "33 weeks", - "send_day": "monday", - "send_time": "10:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder", - "group": 4, - "offset": "36 weeks", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.followup", - "group": 4, - "offset": "37 weeks", - "send_day": "monday", - "send_time": "10:00", - "recipient": "clinic" - } - ] - }, - { - "name": "ANC Reminders", - "translation_key": "schedule.anc_no_lmp", - "summary": "", - "description": "", - "start_from": "reported_date", - "messages": [ - { - "translation_key": "messages.schedule.anc.reminder", - "group": 1, - "offset": "1 weeks", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.followup", - "group": 1, - "offset": "2 weeks", - "send_day": "monday", - "send_time": "10:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder", - "group": 2, - "offset": "5 weeks", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.followup", - "group": 2, - "offset": "6 weeks", - "send_day": "monday", - "send_time": "10:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder", - "group": 3, - "offset": "9 weeks", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.followup", - "group": 3, - "offset": "10 weeks", - "send_day": "monday", - "send_time": "10:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder", - "group": 4, - "offset": "13 weeks", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.followup", - "group": 4, - "offset": "14 weeks", - "send_day": "monday", - "send_time": "10:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder", - "group": 5, - "offset": "17 weeks", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.followup", - "group": 5, - "offset": "18 weeks", - "send_day": "monday", - "send_time": "10:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder", - "group": 6, - "offset": "21 weeks", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.followup", - "group": 6, - "offset": "22 weeks", - "send_day": "monday", - "send_time": "10:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder", - "group": 7, - "offset": "25 weeks", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.followup", - "group": 7, - "offset": "26 weeks", - "send_day": "monday", - "send_time": "10:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_due", - "group": 8, - "offset": "29 weeks", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.followup", - "group": 8, - "offset": "30 weeks", - "send_day": "monday", - "send_time": "10:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_due", - "group": 9, - "offset": "33 weeks", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.followup", - "group": 9, - "offset": "34 weeks", - "send_day": "monday", - "send_time": "10:00", - "recipient": "clinic" - } - ] - }, - { - "name": "Delivery Reminders LMP", - "translation_key": "schedule.delivery_lmp", - "summary": "", - "description": "", - "start_from": "lmp_date", - "messages": [ - { - "translation_key": "messages.schedule.anc.week_38", - "group": 1, - "offset": "38 weeks", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.week_40", - "group": 1, - "offset": "40 weeks", - "send_day": "monday", - "send_time": "10:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.checkin", - "group": 1, - "offset": "42 weeks", - "send_day": "monday", - "send_time": "11:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.checkin", - "group": 1, - "offset": "44 weeks", - "send_day": "monday", - "send_time": "11:00", - "recipient": "clinic" - } - ] - }, - { - "name": "Delivery Reminders LMP from App", - "translation_key": "schedule.delivery_lmp_from_app", - "summary": "", - "description": "", - "start_from": "fields.lmp_date_8601", - "messages": [ - { - "translation_key": "messages.schedule.anc.week_38", - "group": 1, - "offset": "38 weeks", - "send_day": "monday", - "send_time": "09:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.week_40", - "group": 1, - "offset": "40 weeks", - "send_day": "monday", - "send_time": "10:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.checkin", - "group": 1, - "offset": "42 weeks", - "send_day": "monday", - "send_time": "11:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.checkin", - "group": 1, - "offset": "44 weeks", - "send_day": "monday", - "send_time": "11:00", - "recipient": "clinic" - } - ] - }, - { - "name": "Delivery Reminders", - "translation_key": "schedule.delivery_no_lmp", - "summary": "", - "description": "", - "start_from": "reported_date", - "messages": [ - { - "translation_key": "messages.schedule.anc.checkin", - "group": 1, - "offset": "37 weeks", - "send_day": "monday", - "send_time": "11:00", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.checkin", - "group": 1, - "offset": "39 weeks", - "send_day": "monday", - "send_time": "11:00", - "recipient": "clinic" - } - ] - }, - { - "name": "Nutrition Evaluation Moderate", - "translation_key": "schedule.nutrition_evaluation", - "summary": "", - "description": "", - "start_from": "reported_date", - "messages": [ - { - "translation_key": "messages.schedule.nutrition_evaluation", - "group": 1, - "offset": "1 week", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.nutrition_evaluation", - "group": 2, - "offset": "2 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - } - ] - }, - { - "name": "Nutrition Evaluation Severe", - "translation_key": "schedule.nutrition_evaluation", - "summary": "", - "description": "", - "start_from": "reported_date", - "messages": [ - { - "translation_key": "messages.schedule.nutrition_evaluation", - "group": 1, - "offset": "2 days", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.nutrition_evaluation", - "group": 2, - "offset": "4 days", - "send_day": "", - "send_time": "", - "recipient": "clinic" - } - ] - }, - { - "name": "OTP", - "translation_key": "schedule.nutrition_otp", - "summary": "", - "description": "", - "start_from": "reported_date", - "messages": [ - { - "translation_key": "messages.schedule.otp", - "group": 1, - "offset": "1 week", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.otp", - "group": 2, - "offset": "2 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.otp", - "group": 3, - "offset": "3 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.otp", - "group": 4, - "offset": "4 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.otp", - "group": 5, - "offset": "5 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.otp", - "group": 6, - "offset": "6 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.otp", - "group": 7, - "offset": "7 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.otp", - "group": 8, - "offset": "8 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.otp", - "group": 9, - "offset": "9 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.otp", - "group": 10, - "offset": "10 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.otp", - "group": 11, - "offset": "11 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.otp", - "group": 12, - "offset": "12 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - } - ] - }, - { - "name": "SFP", - "translation_key": "schedule.nutrition_sfp", - "summary": "", - "description": "", - "start_from": "reported_date", - "messages": [ - { - "translation_key": "messages.schedule.sfp", - "group": 1, - "offset": "2 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.sfp", - "group": 2, - "offset": "4 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.sfp", - "group": 3, - "offset": "6 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.sfp", - "group": 4, - "offset": "8 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.sfp", - "group": 5, - "offset": "10 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.sfp", - "group": 6, - "offset": "12 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.sfp", - "group": 7, - "offset": "16 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - } - ] - }, - { - "name": "SC", - "translation_key": "schedule.nutrition_sc", - "summary": "", - "description": "", - "start_from": "reported_date", - "messages": [ - { - "translation_key": "messages.schedule.sc", - "group": 1, - "offset": "1 day", - "send_day": "", - "send_time": "", - "recipient": "clinic" - } - ] - }, - { - "name": "SC Exit", - "translation_key": "schedule.nutrition_sc_exit", - "summary": "", - "description": "", - "start_from": "reported_date", - "messages": [ - { - "translation_key": "messages.schedule.sc_exit", - "group": 1, - "offset": "1 week", - "send_day": "", - "send_time": "", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.sc_exit", - "group": 2, - "offset": "2 weeks", - "send_day": "", - "send_time": "", - "recipient": "clinic" - } - ] - } - ], - "notifications": { - "off_form": "OFF", - "on_form": "ON", - "confirm_deactivation": false, - "validations": { - "join_responses": true, - "list": [ - { - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "messages.generic.validation.patient_id" - } - ] - }, - "messages": [ - { - "translation_key": "messages.off.report_accepted", - "event_type": "on_mute", - "recipient": "reporting_unit" - }, - { - "translation_key": "messages.on.report_accepted", - "event_type": "on_unmute", - "recipient": "reporting_unit" - }, - { - "translation_key": "generic.registration_not_found", - "event_type": "patient_not_found", - "recipient": "reporting_unit" - } - ] - }, - "_version": "2.9.0-alpha.4373", - "transitions": { - "default_responses": true, - "update_clinics": true, - "accept_patient_reports": true, - "conditional_alerts": true, - "update_sent_by": false, - "registration": true, - "update_scheduled_reports": false, - "multi_report_alerts": false, - "generate_patient_id_on_people": true, - "death_reporting": true, - "resolve_pending": false - }, - "death_reporting": { - "mark_deceased_forms": [ - "death_confirmation" - ], - "date_field": "fields.date_of_death" - }, - "forms": { - "NP": { - "meta": { - "code": "NP", - "translation_key": "forms.np.title", - "label": { - "en": "New Person with phone_number Registration" + { + "form": "अ", + "events": [{ + "name": "on_create", + "trigger": "add_expected_date", + "params": "lmp_date", + "bool_expr": "doc.last_menstrual_period" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "ANC Reminders LMP", + "bool_expr": "doc.last_menstrual_period" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "Delivery Reminders", + "bool_expr": "doc.last_menstrual_period" + }, + { + "name": "on_create", + "trigger": "clear_schedule", + "params": "Registration Reminders", + "bool_expr": "doc.last_menstrual_period" } + ], + "validations": { + "join_responses": false, + "list": [{ + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! अ <दर्ता नं (५ देखि १३ अंकको)> <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)>", + "locale": "ne" + }] + }, + { + "property": "last_menstrual_period", + "rule": "(integer && min(6) && max(42))", + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_name}} {{patient_id}} को दर्ता मिलेन। अन्तिम महिनावारी भएको समय ६ देखि ४२ हप्ता भित्र भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! अ {{patient_id}} <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)>", + "locale": "ne" + }] + } + ] }, - "fields": { - "patient_age": { - "labels": { - "tiny": { - "en": "Person Age" + "messages": [{ + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}। {{patient_name}} {{patient_id}} को गर्भवस्था दर्ता गरेकोमा धन्यबाद! उहाँको प्रसुति अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}}", + "locale": "ne" }, - "short": { - "en": "Person Age" + { + "content": "नमस्ते {{contact.name}}! {{patient_name}} {{patient_id}} को गर्भवस्था दर्ता गरेकोमा धन्यबाद! उहाँको प्रसुति अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}}", + "locale": "ne" } - }, - "position": 0, - "type": "integer", - "length": [ - 1, - 3 ], - "range": [ - 0, - 125 - ], - "required": true + "event_type": "report_accepted", + "recipient": "clinic" }, - "patient_name": { - "labels": { - "tiny": { - "en": "patient_name" - }, - "description": { - "en": "Patient name" - }, - "short": { - "en": "Patient name" - } + { + "translation_key": "", + "message": [{ + "content": "नमस्ते! {{contact.name}} ले {{patient_name}} को गर्भवस्था दर्ता गर्नु भएको छ। उहाँको दर्ता नं {{patient_id}} र प्रसुती अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}} हो। धन्यबाद!", + "locale": "ne" + }], + "event_type": "report_accepted", + "recipient": "parent" + }, + { + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} कसैलाई पनि दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! अ <दर्ता नं (५ देखि १३ अंकको)> <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)> ", + "locale": "ne" + }], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "ल", + "events": [{ + "name": "on_create", + "trigger": "add_expected_date", + "params": "lmp_date", + "bool_expr": "doc.lmp_date" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "ANC Reminders LMP", + "bool_expr": "doc.lmp_date" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "Delivery Reminders", + "bool_expr": "doc.lmp_date" + }, + { + "name": "on_create", + "trigger": "clear_schedule", + "params": "Registration Reminders", + "bool_expr": "doc.lmp_date" + } + ], + "validations": { + "join_responses": false, + "list": [{ + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "registration.lmp_date.patient_id.incorrect" }, - "position": 1, - "type": "string", - "length": [ - 3, - 30 - ], - "required": true + { + "property": "lmp_year", + "rule": "(integer && min(2078) && max(2090))", + "translation_key": "registration.lmp_date.year.incorrect" + }, + { + "property": "lmp_month", + "rule": "(integer && min(1) && max(12))", + "translation_key": "registration.lmp_date.month.incorrect" + }, + { + "property": "lmp_day", + "rule": "(integer && min(1) && max(32))", + "translation_key": "registration.lmp_date.day.incorrect" + }, + { + "property": "lmp_date", + "rule": "isBefore('1 month')", + "translation_key": "messages.registration.lmp_date.incorrect.before" + }, + { + "property": "lmp_date", + "rule": "isAfter('-9 months')", + "translation_key": "messages.registration.lmp_date.incorrect.after" + } + ] + }, + "messages": [{ + "translation_key": "registration.lmp_date.ok", + "event_type": "report_accepted", + "recipient": "clinic" }, - "phone": { - "labels": { - "tiny": { - "en": "phone number" - }, - "description": { - "en": "phone number" - }, - "short": { - "en": "phone number" - } + { + "translation_key": "registration.lmp_date.ok.health_center", + "event_type": "report_accepted", + "recipient": "health_center" + }, + { + "translation_key": "registration.lmp_date.patient_id.not_found", + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "ज", + "events": [{ + "name": "on_create", + "trigger": "add_birth_date", + "params": "birth_date", + "bool_expr": "doc.days_since_birth" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "PNC Reminders", + "bool_expr": "doc.days_since_birth" + }, + { + "name": "on_create", + "trigger": "clear_schedule", + "params": "Delivery Reminders, ANC Reminders LMP", + "bool_expr": "doc.patient_id" + } + ], + "validations": { + "join_responses": false, + "list": [{ + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! ज <डेलिभरी कोड><दर्ता नं (५ देखि १३ अंकको)> <डेलिभरी भएको दिन>", + "locale": "ne" + }] }, - "position": 2, - "type": "phone_number", - "length": [ - 9, - 10 - ], - "required": true + { + "property": "delivery_code", + "rule": "regex('^(स|श|ष|सद|शद|षद|घद|घ)$')", + "translation_key": "", + "message": [{ + "content": "{{#delivery_code}} नमस्ते {{contact.name}}! {{delivery_code}} डेलिभरी कोड मिलेन। स्वास्थ्य सँस्थामा भएको जन्मको लागि ‘स’ र घरमा भएको जन्मको लागि ‘घ’ प्रयोग गर्नुहोला। धन्यवाद। {{/delivery_code}}", + "locale": "ne" + }] + }, + { + "property": "days_since_birth", + "rule": "(integer && min(0) && max(50))", + "translation_key": "", + "message": [{ + "content": "{{#days_since_birth}}नमस्ते {{contact.name}}! प्रसुति पश्चातको अवधि मिलेन, सो अवधि ० देखि ५० दिन भित्र हुनुपर्दछ। तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! ज <डेलिभरी कोड> {{patient_id}} <डेलिभरी भएको दिन>{{/days_since_birth}}{{^days_since_birth}}नमस्ते {{contact.name}}! प्रसुति पश्चातको अवधि ० देखि ५० दिन भित्र हुनुपर्दछ। तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! ज <डेलिभरी कोड> {{patient_id}} <डेलिभरी भएको दिन>{{/days_since_birth}}", + "locale": "ne" + }] + } + ] + }, + "messages": [{ + "translation_key": "", + "message": [{ + "content": "नमस्ते! {{patient_name}} ({{patient_id}}) का लागि प्रसुति प्रतिवेदन प्राप्त भयो। १,३,७-१४ र ४२ दिनमा हुनु पर्ने सुत्केरी जाँचभेट एकिन गरी SMS बाट प {{patient_id}} <जाँचको पटक> पठाउनुहोला धन्यबाद!", + "locale": "ne" + }], + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [{ + "content": "नमस्ते! {{contact.name}} बाट {{patient_name}} ({{patient_id}}) को प्रसुति प्रतिवेदन प्राप्त भयो। १,३,७-१४ र ४२ दिनमा हुनु पर्ने सुत्केरी जाँचभेट गर्न घर भेट गर्नु होला, धन्यबाद!", + "locale": "ne" + }], + "recipient": "parent" + }, + { + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! ज <डेलिभरी कोड><दर्ता नं (५ देखि १३ अंकको)> <डेलिभरी भएको दिन>", + "locale": "ne" + }], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "क", + "events": [{ + "name": "on_create", + "trigger": "add_patient_id", + "params": "", + "bool_expr": "doc.patient_name" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "Registration Reminders", + "bool_expr": "doc.patient_name" + } + ], + "validations": { + "join_responses": false, + "list": [{ + "property": "patient_age", + "rule": "(integer && min(0) && max(125))", + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! दर्ताको ढाँचा मिलेन। कृपया तलको ढाँचाअनुसार पुनः पठाउनुहोस्: क <उमेर (बर्षमा)> <नाम (बढीमा ३० अक्षर सम्मको)>", + "locale": "ne" + }] + }, + { + "property": "patient_name", + "rule": "(string && lenMin(1) && lenMax(30))", + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! दर्ताको ढाँचा मिलेन। कृपया तलको ढाँचाअनुसार पुनः पठाउनुहोस्: क <उमेर (बर्षमा)> <नाम (बढीमा ३० अक्षर सम्मको)>", + "locale": "ne" + }] + } + ] + }, + "messages": [{ + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}, {{patient_name}} ({{patient_id}}) लाई दर्ता गर्नुभएकोमा धन्यवाद! ", + "locale": "" + }], + "recipient": "" + }] + }, + { + "form": "म", + "events": [{ + "name": "on_create", + "trigger": "clear_schedule", + "params": "Registration Reminders, ANC Reminders LMP, PNC Reminders, Delivery Reminders", + "bool_expr": "doc.patient_id" + }], + "validations": { + "join_responses": false, + "list": [{ + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [{ + "content": "{{#patient_id}}नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! म <दर्ता नं (५ देखि १३ अंकको)>{{/patient_id}}{{^patient_id}}नमस्ते {{contact.name}}! तपाईले पठाउनु भएको रिपोर्टको ढाँचा मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! म <दर्ता नं (५ देखि १३ अंकको)>{{/patient_id}}", + "locale": "ne" + }] + }] + }, + "messages": [{ + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}, {{patient_name}} {{patient_id}} को मृत्युको दु:खद घटनाको सूचना दिनुभएकोमा धन्यवाद। ", + "locale": "ne" + }], + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [{ + "content": "{{contact.name}} ले {{patient_age}} बर्षिया {{patient_name}} ({{patient_id}}) को मृत्यु भएको सूचना दिनुभएको छ। ", + "locale": "ne" + }], + "recipient": "parent" + }, + { + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} कसैलाई पनि दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! म <दर्ता नं (५ देखि १३ अंकको)", + "locale": "ne" + }], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "A", + "events": [{ + "name": "on_create", + "trigger": "add_expected_date", + "params": "lmp_date", + "bool_expr": "doc.last_menstrual_period" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "ANC Reminders LMP", + "bool_expr": "doc.last_menstrual_period" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "Delivery Reminders", + "bool_expr": "doc.last_menstrual_period" + }, + { + "name": "on_create", + "trigger": "clear_schedule", + "params": "Registration Reminders", + "bool_expr": "doc.last_menstrual_period" } + ], + "validations": { + "join_responses": false, + "list": [{ + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! A <दर्ता नं (५ देखि १३ अंकको)> <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)>", + "locale": "ne" + }] + }, + { + "property": "last_menstrual_period", + "rule": "(integer && min(6) && max(42))", + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_name}} {{patient_id}} को दर्ता मिलेन। अन्तिम महिनावारी भएको समय ६ देखि ४२ हप्ता भित्र भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! A {{patient_id}} <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)>", + "locale": "ne" + }] + } + ] }, - "public_form": false, - "use_sentinel": true + "messages": [{ + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}। {{patient_name}} {{patient_id}} को गर्भवस्था दर्ता गरेकोमा धन्यबाद! उहाँको प्रसुति अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}}", + "locale": "ne" + }], + "event_type": "report_accepted", + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [{ + "content": "नमस्ते! {{contact.name}} ले {{patient_name}} को गर्भवस्था दर्ता गर्नु भएको छ। उहाँको दर्ता नं {{patient_id}} र प्रसुती अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}} हो। धन्यबाद!", + "locale": "ne" + }], + "event_type": "report_accepted", + "recipient": "parent" + }, + { + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} कसैलाई पनि दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! A <दर्ता नं (५ देखि १३ अंकको)> <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)> ", + "locale": "ne" + }], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] }, - "DR": { - "meta": { - "code": "DR", - "icon": "icon-death-general", - "translation_key": "form.dr.title" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - }, - "tiny": { - "en": "ID" - } - }, - "position": 0, - "flags": { - "input_digits_only": true - }, - "length": [ - 5, - 13 - ], - "type": "string", - "required": true - } - }, - "use_sentinel": true - }, - "IMM": { - "meta": { - "code": "IMM", - "icon": "immunization", - "translation_key": "form.imm.title" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": true, - "use_sentinel": true - }, - "C_IMM": { - "meta": { - "code": "C_IMM", - "icon": "immunization", - "translation_key": "form.c_imm.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "ID" - }, - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 1, - 13 - ], - "required": false - }, - "visit_confirmed": { - "labels": { - "tiny": { - "en": "c" - }, - "short": { - "translation_key": "report.c_imm.visit_confirmed" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_bcg": { - "labels": { - "tiny": { - "en": "bcg" - }, - "short": { - "translation_key": "report.c_imm.received_bcg" - } - }, - "position": 3, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_cholera_1": { - "labels": { - "tiny": { - "en": "ch1" - }, - "short": { - "translation_key": "report.c_imm.received_cholera_1" - } - }, - "position": 4, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_cholera_2": { - "labels": { - "tiny": { - "en": "ch2" - }, - "short": { - "translation_key": "report.c_imm.received_cholera_2" - } - }, - "position": 5, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_cholera_3": { - "labels": { - "tiny": { - "en": "ch3" - }, - "short": { - "translation_key": "report.c_imm.received_cholera_3" - } - }, - "position": 6, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_hep_a_1": { - "labels": { - "tiny": { - "en": "ha1" - }, - "short": { - "translation_key": "report.c_imm.received_hep_a_1" - } - }, - "position": 7, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_hep_a_2": { - "labels": { - "tiny": { - "en": "ha2" - }, - "short": { - "translation_key": "report.c_imm.received_hep_a_2" - } - }, - "position": 8, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_hep_b": { - "labels": { - "tiny": { - "en": "hb" - }, - "short": { - "translation_key": "report.c_imm.received_hep_b" - } - }, - "position": 9, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_hpv_1": { - "labels": { - "tiny": { - "en": "hpv1" - }, - "short": { - "translation_key": "report.c_imm.received_hpv_1" - } - }, - "position": 10, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_hpv_2": { - "labels": { - "tiny": { - "en": "hpv2" - }, - "short": { - "translation_key": "report.c_imm.received_hpv_2" - } - }, - "position": 11, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_hpv_3": { - "labels": { - "tiny": { - "en": "hpv3" - }, - "short": { - "translation_key": "report.c_imm.received_hpv_3" - } - }, - "position": 12, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_flu": { - "labels": { - "tiny": { - "en": "flu" - }, - "short": { - "translation_key": "report.c_imm.received_flu" - } - }, - "position": 13, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_jap_enc": { - "labels": { - "tiny": { - "en": "je" - }, - "short": { - "translation_key": "report.c_imm.received_jap_enc" - } - }, - "position": 14, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_meningococcal_1": { - "labels": { - "tiny": { - "en": "mn1" - }, - "short": { - "translation_key": "report.c_imm.received_meningococcal_1" - } - }, - "position": 15, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_meningococcal_2": { - "labels": { - "tiny": { - "en": "mn2" - }, - "short": { - "translation_key": "report.c_imm.received_meningococcal_2" - } - }, - "position": 16, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_meningococcal_3": { - "labels": { - "tiny": { - "en": "mn3" - }, - "short": { - "translation_key": "report.c_imm.received_meningococcal_3" - } - }, - "position": 17, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_meningococcal_4": { - "labels": { - "tiny": { - "en": "mn4" - }, - "short": { - "translation_key": "report.c_imm.received_meningococcal_4" - } - }, - "position": 18, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_mmr_1": { - "labels": { - "tiny": { - "en": "mmr1" - }, - "short": { - "translation_key": "report.c_imm.received_mmr_1" - } - }, - "position": 19, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_mmr_2": { - "labels": { - "tiny": { - "en": "mmr2" - }, - "short": { - "translation_key": "report.c_imm.received_mmr_2" - } - }, - "position": 20, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_mmrv_1": { - "labels": { - "tiny": { - "en": "mmrv1" - }, - "short": { - "translation_key": "report.c_imm.received_mmrv_1" - } - }, - "position": 21, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_mmrv_2": { - "labels": { - "tiny": { - "en": "mmrv2" - }, - "short": { - "translation_key": "report.c_imm.received_mmrv_2" - } - }, - "position": 22, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_polio_0": { - "labels": { - "tiny": { - "en": "opv0" - }, - "short": { - "translation_key": "report.c_imm.received_polio_0" - } - }, - "position": 23, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_polio_1": { - "labels": { - "tiny": { - "en": "opv1" - }, - "short": { - "translation_key": "report.c_imm.received_polio_1" - } - }, - "position": 24, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_polio_2": { - "labels": { - "tiny": { - "en": "opv2" - }, - "short": { - "translation_key": "report.c_imm.received_polio_2" - } - }, - "position": 25, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_polio_3": { - "labels": { - "tiny": { - "en": "opv3" - }, - "short": { - "translation_key": "report.c_imm.received_polio_3" - } - }, - "position": 26, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_ipv_1": { - "labels": { - "tiny": { - "en": "ipv1" - }, - "short": { - "translation_key": "report.c_imm.received_ipv_1" - } - }, - "position": 27, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_ipv_2": { - "labels": { - "tiny": { - "en": "ipv2" - }, - "short": { - "translation_key": "report.c_imm.received_ipv_2" - } - }, - "position": 28, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_ipv_3": { - "labels": { - "tiny": { - "en": "ipv3" - }, - "short": { - "translation_key": "report.c_imm.received_ipv_3" - } - }, - "position": 29, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_fipv_1": { - "labels": { - "tiny": { - "en": "fipv1" - }, - "short": { - "translation_key": "report.c_imm.received_fipv_1" - } - }, - "position": 30, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_fipv_2": { - "labels": { - "tiny": { - "en": "fipv2" - }, - "short": { - "translation_key": "report.c_imm.received_fipv_2" - } - }, - "position": 31, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_penta_1": { - "labels": { - "tiny": { - "en": "penta1" - }, - "short": { - "translation_key": "report.c_imm.received_penta_1" - } - }, - "position": 32, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_penta_2": { - "labels": { - "tiny": { - "en": "penta2" - }, - "short": { - "translation_key": "report.c_imm.received_penta_2" - } - }, - "position": 33, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_penta_3": { - "labels": { - "tiny": { - "en": "penta3" - }, - "short": { - "translation_key": "report.c_imm.received_penta_3" - } - }, - "position": 34, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_dpt_4": { - "labels": { - "tiny": { - "en": "dpt4" - }, - "short": { - "translation_key": "report.c_imm.received_dpt_4" - } - }, - "position": 35, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_dpt_5": { - "labels": { - "tiny": { - "en": "dpt5" - }, - "short": { - "translation_key": "report.c_imm.received_dpt_5" - } - }, - "position": 36, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_pneumococcal_1": { - "labels": { - "tiny": { - "en": "pcv1" - }, - "short": { - "translation_key": "report.c_imm.received_pneumococcal_1" - } - }, - "position": 37, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_pneumococcal_2": { - "labels": { - "tiny": { - "en": "pcv2" - }, - "short": { - "translation_key": "report.c_imm.received_pneumococcal_2" - } - }, - "position": 38, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_pneumococcal_3": { - "labels": { - "tiny": { - "en": "pcv3" - }, - "short": { - "translation_key": "report.c_imm.received_pneumococcal_3" - } - }, - "position": 39, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_pneumococcal_4": { - "labels": { - "tiny": { - "en": "pcv4" - }, - "short": { - "translation_key": "report.c_imm.received_pneumococcal_4" - } - }, - "position": 40, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_rotavirus_1": { - "labels": { - "tiny": { - "en": "rv1" - }, - "short": { - "translation_key": "report.c_imm.received_rotavirus_1" - } - }, - "position": 41, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_rotavirus_2": { - "labels": { - "tiny": { - "en": "rv2" - }, - "short": { - "translation_key": "report.c_imm.received_rotavirus_2" - } - }, - "position": 42, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_rotavirus_3": { - "labels": { - "tiny": { - "en": "rv3" - }, - "short": { - "translation_key": "report.c_imm.received_rotavirus_3" - } - }, - "position": 43, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_typhoid_1": { - "labels": { - "tiny": { - "en": "ty1" - }, - "short": { - "translation_key": "report.c_imm.received_typhoid_1" - } - }, - "position": 44, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_typhoid_2": { - "labels": { - "tiny": { - "en": "ty2" - }, - "short": { - "translation_key": "report.c_imm.received_typhoid_2" - } - }, - "position": 45, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_vitamin_a": { - "labels": { - "tiny": { - "en": "vita" - }, - "short": { - "translation_key": "report.c_imm.received_vitamin_a" - } - }, - "position": 46, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - }, - "received_yellow_fever": { - "labels": { - "tiny": { - "en": "yf" - }, - "short": { - "translation_key": "report.c_imm.received_yellow_fever" - } - }, - "position": 47, - "type": "string", - "length": [ - 1, - 10 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "N": { - "meta": { - "code": "N", - "icon": "new-person", - "translation_key": "form.n.title" - }, - "fields": { - "patient_name": { - "labels": { - "tiny": { - "en": "N" - }, - "short": { - "translation_key": "report.n.patient_name" - } - }, - "position": 0, - "type": "string", - "length": [ - 1, - 30 - ], - "required": true - } - }, - "public_form": false, - "use_sentinel": true - }, - "CW": { - "meta": { - "code": "CW", - "icon": "child", - "translation_key": "form.cw.title" - }, - "fields": { - "weeks_since_birth": { - "labels": { - "short": { - "translation_key": "report.cw.weeks_since_birth" - } - }, - "position": 0, - "type": "integer", - "length": [ - 1, - 3 - ], - "range": [ - 0, - 260 - ], - "required": true, - "validations": {}, - "flags": {} - }, - "patient_name": { - "labels": { - "short": { - "translation_key": "report.cw.patient_name" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 30 - ], - "required": true - } - }, - "public_form": false, - "use_sentinel": true - }, - "C": { - "meta": { - "code": "C", - "icon": "child", - "translation_key": "form.cw.title" - }, - "fields": { - "months_since_birth": { - "labels": { - "short": { - "translation_key": "report.c.months_since_birth" - } - }, - "position": 0, - "type": "integer", - "length": [ - 1, - 3 - ], - "range": [ - 0, - 260 - ], - "required": true, - "validations": {}, - "flags": {} - }, - "patient_name": { - "labels": { - "short": { - "translation_key": "report.cw.patient_name" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 30 - ], - "required": true - } - }, - "public_form": false, - "use_sentinel": true - }, - "G": { - "meta": { - "code": "G", - "icon": "child", - "translation_key": "form.g.title" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - }, - "tiny": { - "en": "ID" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true, - "validations": {}, - "flags": {} - }, - "severity": { - "labels": { - "short": { - "translation_key": "report.g.severity" - }, - "tiny": { - "en": "severity" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 1 - ], - "required": true - } - }, - "public_form": false, - "use_sentinel": true - }, - "CF": { - "meta": { - "code": "CF", - "icon": "follow-up", - "translation_key": "form.cf.title" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - }, - "tiny": { - "en": "ID" - } - }, - "position": 0, - "flags": { - "input_digits_only": true - }, - "length": [ - 5, - 13 - ], - "type": "string", - "required": true - }, - "outcome": { - "labels": { - "short": { - "translation_key": "report.cf.outcome" - }, - "tiny": { - "en": "outcome" - } - }, - "position": 0, - "flags": { - "input_digits_only": true - }, - "length": [ - 0, - 1 - ], - "type": "string", - "required": false - } - }, - "use_sentinel": true - }, - "CHILD": { - "meta": { - "code": "CHILD", - "icon": "child", - "translation_key": "form.child.title" - }, - "fields": { - "days_since_birth": { - "labels": { - "tiny": { - "en": "D" - }, - "short": { - "translation_key": "report.child.days_since_birth" - } - }, - "position": 0, - "type": "integer", - "length": [ - 1, - 4 - ], - "range": [ - 0, - 1827 - ], - "required": true, - "validations": {}, - "flags": {} - }, - "patient_name": { - "labels": { - "tiny": { - "en": "N" - }, - "short": { - "translation_key": "report.child.patient_name" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 30 - ], - "required": true - } - }, - "public_form": false, - "use_sentinel": true - }, - "P": { - "meta": { - "code": "P", - "icon": "pregnancy-1", - "translation_key": "form.p.title" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - }, - "tiny": { - "en": "ID" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "last_menstrual_period": { - "labels": { - "tiny": { - "en": "LMP" - }, - "short": { - "translation_key": "report.p.last_menstrual_period" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 2 - ], - "range": [ - 2, - 42 - ], - "required": false, - "validations": {} - } - }, - "public_form": true, - "use_sentinel": true - }, - "V": { - "meta": { - "code": "V", - "icon": "clinic", - "translation_key": "form.v.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "id" - }, - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - }, - "tiny": { - "en": "N" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": true, - "use_sentinel": true - }, - "M": { - "meta": { - "code": "M", - "icon": "clinic", - "translation_key": "form.m.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "id" - }, - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - } - }, - "public_form": true, - "use_sentinel": true - }, - "D": { - "meta": { - "code": "D", - "icon": "mother-child", - "translation_key": "form.d.title" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - }, - "tiny": { - "en": "ID" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "delivery_code": { - "labels": { - "short": { - "translation_key": "report.d.delivery_code" - }, - "tiny": { - "en": "C" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 2 - ], - "required": true - }, - "days_since_birth": { - "labels": { - "short": { - "translation_key": "report.d.days_since_birth" - }, - "tiny": { - "en": "D" - } - }, - "position": 2, - "type": "string", - "length": [ - 1, - 3 - ], - "required": false - } - }, - "public_form": true, - "use_sentinel": true - }, - "F": { - "meta": { - "code": "F", - "icon": "risk", - "translation_key": "form.f.title" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - }, - "tiny": { - "en": "ID" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - }, - "tiny": { - "en": "N" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": true, - "use_sentinel": true - }, - "PENTA1": { - "meta": { - "code": "PENTA1", - "icon": "immunization", - "translation_key": "PENTA1 Pentavalent" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "PENTA2": { - "meta": { - "code": "PENTA2", - "icon": "immunization", - "translation_key": "PENTA2 Pentavalent" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "PENTA3": { - "meta": { - "code": "PENTA3", - "icon": "immunization", - "translation_key": "PENTA3 Pentavalent" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "BCG": { - "meta": { - "code": "BCG", - "icon": "immunization", - "translation_key": "BCG Bacille Calmette-Guerin" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "OPV0": { - "meta": { - "code": "OPV0", - "icon": "immunization", - "translation_key": "OPV0 Oral Polio" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "OPV1": { - "meta": { - "code": "OPV1", - "icon": "immunization", - "translation_key": "OPV1 Oral Polio" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "OPV2": { - "meta": { - "code": "OPV2", - "icon": "immunization", - "translation_key": "OPV2 Oral Polio" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "OPV3": { - "meta": { - "code": "OPV3", - "icon": "immunization", - "translation_key": "OPV3 Oral Polio" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "IPV1": { - "meta": { - "code": "IPV1", - "icon": "immunization", - "translation_key": "IPV1 Inactivated Polio" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "IPV2": { - "meta": { - "code": "IPV2", - "icon": "immunization", - "translation_key": "IPV2 Inactivated Polio" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "IPV3": { - "meta": { - "code": "IPV3", - "icon": "immunization", - "translation_key": "IPV3 Inactivated Polio" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "PCV1": { - "meta": { - "code": "PCV1", - "icon": "immunization", - "translation_key": "PCV1 Pneumococcal Pneumonia" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "PCV2": { - "meta": { - "code": "PCV2", - "icon": "immunization", - "translation_key": "PCV2 Pneumococcal Pneumonia" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "PCV3": { - "meta": { - "code": "PCV3", - "icon": "immunization", - "translation_key": "PCV3 Pneumococcal Pneumonia" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "PCV4": { - "meta": { - "code": "PCV4", - "icon": "immunization", - "translation_key": "PCV4 Pneumococcal Pneumonia" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "ROTA1": { - "meta": { - "code": "ROTA1", - "icon": "immunization", - "translation_key": "ROTA1 Rotavirus" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "ROTA2": { - "meta": { - "code": "ROTA2", - "icon": "immunization", - "translation_key": "ROTA2 Rotavirus" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "ROTA3": { - "meta": { - "code": "ROTA3", - "icon": "immunization", - "translation_key": "ROTA3 Rotavirus" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "VITA": { - "meta": { - "code": "VITA", - "icon": "immunization", - "translation_key": "VITA Vitamin A" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "MMR1": { - "meta": { - "code": "MMR1", - "icon": "immunization", - "translation_key": "MMR1 Measles Mumps Rubella" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "MMR2": { - "meta": { - "code": "MMR2", - "icon": "immunization", - "translation_key": "MMR2 Measles Mumps Rubella" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "MMRV1": { - "meta": { - "code": "MMRV1", - "icon": "immunization", - "translation_key": "MMRV1 Measles Mumps Rubella Varicella" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "MMRV2": { - "meta": { - "code": "MMRV2", - "icon": "immunization", - "translation_key": "MMRV2 Measles Mumps Rubella Varicella" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "MN1": { - "meta": { - "code": "MN1", - "icon": "immunization", - "translation_key": "MN1 Meningococcal" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "MN2": { - "meta": { - "code": "MN2", - "icon": "immunization", - "translation_key": "MN2 Meningococcal" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "MN3": { - "meta": { - "code": "MN3", - "icon": "immunization", - "translation_key": "MN3 Meningococcal" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "MN4": { - "meta": { - "code": "MN4", - "icon": "immunization", - "translation_key": "MN4 Meningococcal" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "FLU": { - "meta": { - "code": "FLU", - "icon": "immunization", - "translation_key": "FLU Influenza" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "HA1": { - "meta": { - "code": "HA1", - "icon": "immunization", - "translation_key": "HA1 Hepatitis A" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "HA2": { - "meta": { - "code": "HA2", - "icon": "immunization", - "translation_key": "HA2 Hepatitis A" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "JE": { - "meta": { - "code": "JE", - "icon": "immunization", - "translation_key": "JE Japanese Encephalitis" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "YF": { - "meta": { - "code": "YF", - "icon": "immunization", - "translation_key": "YF Yellow Fever" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "TY1": { - "meta": { - "code": "TY1", - "icon": "immunization", - "translation_key": "TY1 Typhoid" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "TY2": { - "meta": { - "code": "TY2", - "icon": "immunization", - "translation_key": "TY2 Typhoid" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "HPV1": { - "meta": { - "code": "HPV1", - "icon": "immunization", - "translation_key": "HPV1 Human Papillomavirus" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "HPV2": { - "meta": { - "code": "HPV2", - "icon": "immunization", - "translation_key": "HPV2 Human Papillomavirus" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "HPV3": { - "meta": { - "code": "HPV3", - "icon": "immunization", - "translation_key": "HPV3 Human Papillomavirus" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "CH1": { - "meta": { - "code": "CH1", - "icon": "immunization", - "translation_key": "CH1 Cholera" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "CH2": { - "meta": { - "code": "CH2", - "icon": "immunization", - "translation_key": "CH2 Cholera" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "CH3": { - "meta": { - "code": "CH3", - "icon": "immunization", - "translation_key": "CH3 Cholera" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "HB": { - "meta": { - "code": "HB", - "icon": "immunization", - "translation_key": "HB Hepatitis B" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "DPT4": { - "meta": { - "code": "DPT4", - "icon": "immunization", - "translation_key": "DPT4 DPT Booster 1" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "DPT5": { - "meta": { - "code": "DPT5", - "icon": "immunization", - "translation_key": "DPT5 DPT Booster 2" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "FIPV1": { - "meta": { - "code": "FIPV1", - "icon": "immunization", - "translation_key": "fIPV1 Fractional IPV 1" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "FIPV2": { - "meta": { - "code": "FIPV2", - "icon": "immunization", - "translation_key": "fIPV2 Fractional IPV 2" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "OFF": { - "meta": { - "code": "OFF", - "icon": "off", - "translation_key": "form.off.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "ID" - }, - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "tiny": { - "en": "r" - }, - "short": { - "translation_key": "report.off.reason" - } - }, - "position": 1, - "length": [ - 3, - 100 - ], - "type": "string" - } - }, - "public_form": true, - "use_sentinel": true - }, - "ON": { - "meta": { - "code": "ON", - "icon": "on", - "translation_key": "form.on.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "ID" - }, - "short": { - "translation_key": "patient_id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "tiny": { - "en": "N" - }, - "short": { - "translation_key": "notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "use_sentinel": true - } - }, - "statistics_submission": "none", - "statistics_submission_number": "", - "replication_depth": [ - { - "role": "non-existant-role", - "depth": 42 - } - ], - "tasks": { - "rules": "define Target { _id: null, contact: null, deleted: null, type: null, pass: null, date: null, groupBy: null }\ndefine Contact { contact: null, reports: null, tasks: null }\ndefine Task { _id: null, deleted: null, doc: null, contact: null, icon: null, date: null, readyStart: null, readyEnd: null, title: null, fields: null, resolved: null, priority: null, priorityLabel: null, reports: null, actions: null }\nrule GenerateEvents {\n when { c: Contact } then { !function(e){var t={};function i(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,i),r.l=!0,r.exports}i.m=e,i.c=t,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){'undefined'!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:'Module'}),Object.defineProperty(e,'__esModule',{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&'object'==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,'default',{enumerable:!0,value:e}),2&t&&'string'!=typeof e)for(var r in e)i.d(n,r,function(t){return e[t]}.bind(null,r));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,'a',t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p='',i(i.s=2)}([function(e,t){const i=new Date;var n=['V','pregnancy_visit'],r=['D','delivery'],s=['bcg','cholera_1','cholera_2','cholera_3','hep_a_1','hep_a_2','hpv_1','hpv_2','hpv_3','ipv_1','ipv_2','ipv_3','flu','jap_enc','meningococcal_1','meningococcal_2','meningococcal_3','meningococcal_4','mmr_1','mmr_2','mmrv_1','mmrv_2','polio_0','polio_1','polio_2','polio_3','penta_1','penta_2','penta_3','pneumococcal_1','pneumococcal_2','pneumococcal_3','pneumococcal_4','rotavirus_1','rotavirus_2','rotavirus_3','typhoid_1','typhoid_2','vitamin_a','yellow_fever'];function a(e,t){var i=null;return e.forEach((function(e){t.indexOf(e.form)>=0&&!e.deleted&&(!i||e.reported_date>i.reported_date)&&(i=e)})),i}function o(e){return Math.max(Utils.getMostRecentTimestamp(e.reports,'P'),Utils.getMostRecentTimestamp(e.reports,'pregnancy'))}function p(e){var t=a(e.reports,r);return t?t.reported_date:0}function l(e,t){return e&&e.trim().toLowerCase()===t}function c(e,t){var i='received_'+t;return l(e.form,t)||l(e.form,'immunization_visit')&&e.fields.vaccines_received&&'yes'===e.fields.vaccines_received[i]||l(e.form,'imm')&&e.fields.vaccines_received&&'yes'===e.fields.vaccines_received[i]}e.exports={MS_IN_DAY:864e5,MAX_DAYS_IN_PREGNANCY:308,DAYS_IN_PNC:42,antenatalForms:n,deliveryForms:r,postnatalForms:['M','postnatal_visit'],immunizationForms:['IMM','C_IMM','immunization_visit','BCG','OPV0','OPV1','OPV2','OPV3','IPV1','IPV2','IPV3','PCV1','PCV2','PCV3','PCV4','PENTA1','PENTA2','PENTA3','ROTA1','ROTA2','ROTA3','VITA','MMR1','MMR2','MMRV1','MMRV2','MN1','MN2','MN3','MN4','FLU','HA1','HA2','JE','YF','TY1','TY2','HPV1','HPV2','HPV3','CH1','CH2','CH3'],immunizationMonths:[1,2,3,6,8,12,16,24],now:i,getMostRecentReport:a,isFormFromArraySubmittedInWindow:function(e,t,i,n){return'string'==typeof t&&(t=[t]),t.some((function(t){return Utils.isFormSubmittedInWindow(e,t,i,n)}))},isCoveredByUseCase:function e(t,i){return!t||('string'==typeof t.use_cases?-1!==t.use_cases.split(' ').indexOf(i):e(t.parent,i))},isHighRiskPregnancy:function(e,t){return'pregnancy'===t.form&&t.fields&&(t.fields.risk_factors||t.fields.danger_signs)||Utils.isFormSubmittedInWindow(e.reports,'F',t.reported_date,Utils.addDate(new Date(t.reported_date),308).getTime())},isHomeBirth:function(e){return e.fields&&e.fields.delivery_code&&'F'!==e.fields.delivery_code.toUpperCase()},getNewestPregnancyTimestamp:o,getNewestDeliveryTimestamp:p,isNewestPregnancy:function(e,t){if('P'===t.form||'pregnancy'===t.form)return t.reported_date===o(e)},isHealthyDelivery:function(e,t){return'D'===t.form||'delivery'===t.form&&'healthy'===t.fields.pregnancy_outcome},isWomanInActivePncPeriod:function(e){var t=new Date(i.getFullYear(),i.getMonth(),i.getDate()-42);return p(e)>t.getTime()},isChildUnder5:function(e){var t=new Date(e.contact.date_of_birth),n=new Date(i-t.getTime());return 12*Math.abs(n.getFullYear()-1970)+n.getMonth()<60},isFormSubmittedInWindow:function(e,t,i,n,r){var s=!1,a=0;return e.forEach((function(e){!s&&t.indexOf(e.form)>=0&&e.reported_date>=i&&e.reported_date<=n&&(a++,(!r||e.fields&&e.fields.follow_up_count>r||a>=r)&&(s=!0))})),s},countReportsSubmittedInWindow:function(e,t,i,n){var r=0;return e.forEach((function(e){t.indexOf(e.form)>=0&&e.reported_date>=i&&e.reported_date<=n&&r++})),r},countANCVisits:function(e,t,i){var r=0;return e.forEach((function(e){n.indexOf(e.form)>=0&&e.reported_date>=t&&e.reported_date<=i&&('pregnancy_visit'===e.form&&'yes'===e.fields.visit_confirmed||'V'===e.form)&&r++})),r},isFormCodeSame:l,isFacilityDelivery:function(e,t){return 1===arguments.length&&(t=e),t.fields&&t.fields.delivery_code&&'F'===t.fields.delivery_code.toUpperCase()},receivedVaccine:c,isBcgReported:function(e){return e.reports.some((function(e){return c(e,'bcg')}))},countDoses:function(e){var t=0;return s.forEach((function(i){var n='received_'+i;'yes'===e.fields.vaccines_received[n]&&t++})),t}}},function(e,t){e.exports=function(e){var t={};!function e(t,i){var n=Object.keys(t);for(var r in n){var s=n[r];switch(typeof t[s]){case'object':e(t[s],i);break;case'function':t[s]=t[s].bind(i)}}}(e,t),t.definition=function e(t){var i=Object.assign({},t),n=Object.keys(i);for(var r in n){var s=n[r];if(Array.isArray(i[s])){i[s]=i[s].slice(0);for(var a=0;a=t.scheduled_tasks.length||t.scheduled_tasks[i].group!==t.scheduled_tasks[i+1].group)},priority:function(e,t){if(_(t))return{level:'high',label:'task.warning.home_birth'}},actions:[{form:'postnatal_visit'}],events:[{id:'postnatal-missing-visit',days:1,start:0,end:3}],resolvedIf:function(e,t,i,n,r){return'cleared'===t.scheduled_tasks[r].state||t.reported_date0},date:'now'},{id:'delivery-with-min-4-visits',type:'percent',icon:'nurse',goal:100,translation_key:'targets.delivery_4_visits.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',idType:'report',appliesIf:c,passesIf:function(e,t){return _(e.reports,t.reported_date-r*n,t.reported_date)>3},date:'now'},{id:'delivery-at-facility-total',type:'percent',icon:'clinic',goal:100,translation_key:'targets.facility_deliveries.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',idType:'report',appliesIf:c,passesIf:g,date:'now'},{id:'pnc-active',type:'count',icon:'mother-child',goal:-1,translation_key:'targets.active_pnc.title',subtitle_translation_key:'targets.all_time.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1)',appliesTo:'contacts',appliesToType:['person'],appliesIf:y},{id:'pnc-registered-this-month',type:'count',icon:'infant',goal:-1,translation_key:'targets.pnc_registrations.title',subtitle_translation_key:'targets.all_time.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1)',appliesTo:'reports',idType:'report',appliesIf:c,date:'reported'},{id:'pnc-visits-this-month',type:'count',icon:'infant',goal:-1,translation_key:'targets.pnc_visits.title',subtitle_translation_key:'targets.this_month.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1)',appliesTo:'reports',idType:'report',appliesIf:function(e,t){return-1!==o.indexOf(t.form)||-1!==h.indexOf(t.form)&&t.fields.delivery_code&&'F'===t.fields.delivery_code.toUpperCase()},date:'reported'},{id:'pnc-homebirth-0-visits',type:'count',icon:'household',goal:-1,translation_key:'targets.homebirth_no_visits.title',subtitle_translation_key:'targets.all_time.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1)',appliesTo:'contacts',appliesToType:['person'],appliesIf:e=>y(e)&&!p(e.reports,o,u(e),m.getTime())},{id:'pnc-homebirth-min-1-visit',type:'percent',icon:'nurse',goal:100,translation_key:'targets.homebirth_1_visit.title',subtitle_translation_key:'targets.all_time.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1)',appliesTo:'reports',appliesIf:function(e,t){return c(e,t)&&t.fields.delivery_code&&'F'!==t.fields.delivery_code.toUpperCase()},passesIf:function(e,t){var i=new Date(t&&(t.birth_date||t.fields.birth_date||t.reported_date)),n=new Date(i.getFullYear(),i.getMonth(),i.getDate()+s);return p(e.reports,o,i.getTime(),n.getTime())},date:'now'},{id:'pnc-3-visits',type:'percent',icon:'nurse',goal:50,translation_key:'targets.birth_3_visits.title',subtitle_translation_key:'targets.all_time.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'pnc\\') !== -1)',appliesTo:'reports',appliesIf:c,passesIf:function(e,t){var i=new Date(t&&(t.birth_date||t.fields.birth_date||t.reported_date)),n=new Date(i.getFullYear(),i.getMonth(),i.getDate()+s),r=v(e.reports,o,i.getTime(),n.getTime());return g(t)&&r++,r>2},date:'now'},{id:'imm-children-under-5-years',type:'count',icon:'children',goal:-1,translation_key:'targets.children_u5.title',subtitle_translation_key:'targets.all_time.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'imm\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'imm\\') !== -1)',appliesTo:'contacts',appliesToType:['person'],appliesIf:l},{id:'imm-children-registered-this-month',type:'count',icon:'child',goal:-1,translation_key:'targets.children_registered.title',subtitle_translation_key:'targets.this_month.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'imm\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'imm\\') !== -1)',appliesTo:'contacts',appliesToType:['person'],appliesIf:l,date:function(e){return e.contact.reported_date}},{id:'imm-vaccines-given-this-month',type:'count',icon:'immunization',goal:-1,translation_key:'targets.vaccines_given.title',subtitle_translation_key:'targets.this_month.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'imm\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'imm\\') !== -1)',appliesTo:'reports',appliesIf:function(e,t){return-1!==a.indexOf(t.form)},date:'reported',emitCustom:function(e,t,i,n){if('immunization_visit'===n.form||'imm'===n.form){var r=T(n);for(let i=0;i!!l(e)&&v(e.reports,a,m.getTime()-92*n,m.getTime())>=1},{id:'imm-no-vaccine-reported',type:'count',icon:'risk',goal:-1,translation_key:'targets.children_not_vaccinated.title',subtitle_translation_key:'targets.all_time.subtitle',context:'(user.parent.use_cases && user.parent.use_cases.split(\\' \\').indexOf(\\'imm\\') !== -1) || (user.parent.parent.use_cases && user.parent.parent.use_cases.split(\\' \\').indexOf(\\'imm\\') !== -1)',appliesTo:'contacts',appliesToType:['person'],appliesIf:e=>{if(!l(e))return!1;for(let t=0;t!!l(e)&&e.reports.some((function(e){return'nutrition_screening'===e.form&&e.fields.measurements.wfa<-2})),date:'reported'},{id:'children-stunted',translation_key:'targets.stunted.title',context:'ctx1',subtitle_translation_key:'targets.this_month.subtitle',type:'count',icon:'child',goal:-1,appliesTo:'contacts',appliesToType:['person'],appliesIf:e=>!!l(e)&&e.reports.some((function(e){return'nutrition_screening'===e.form&&e.fields.measurements.hfa<-2})),date:'reported'},{id:'children-mam',translation_key:'targets.active_mam.title',subtitle_translation_key:'targets.this_month.subtitle',type:'count',icon:'child',goal:-1,appliesTo:'contacts',appliesToType:['person'],appliesIf:e=>!!l(e)&&e.reports.some((function(e){return'nutrition_screening'===e.form&&(e.fields.measurements.wfh>=-3&&e.fields.measurements.wfh<-2||e.fields.measurements.muac>=11.5&&e.fields.measurements.muac<12.4)})),date:'reported'},{id:'children-sam',translation_key:'targets.active_sam.title',subtitle_translation_key:'targets.this_month.subtitle',type:'count',icon:'child',goal:-1,appliesTo:'contacts',appliesToType:['person'],appliesIf:e=>!!l(e)&&e.reports.some((function(e){return'nutrition_screening'===e.form&&(e.fields.measurements.wfh<-3||e.fields.measurements.muac<11.5)})),date:'reported'},{id:'children-otp',translation_key:'targets.active_otp.title',subtitle_translation_key:'targets.this_month.subtitle',type:'count',icon:'child',goal:-1,appliesTo:'contacts',appliesToType:['person'],appliesIf:e=>{if(!l(e))return!1;var t=!1,i=!1,n=!1;return e.reports.forEach((function(e){'nutrition_screening'===e.form?t=e.fields.treatment.program&&'OTP'===e.fields.treatment.program:'off'===e.form?n=e.fields.off&&'defaulter'===e.fields.off.reason:'death_confirmation'===e.form&&(i='yes'===e.fields.death_report.death)})),t&&!n&&!i},date:'reported'},{id:'children-sfp',translation_key:'targets.active_sfp.title',subtitle_translation_key:'targets.this_month.subtitle',type:'count',icon:'child',goal:-1,appliesTo:'contacts',appliesToType:['person'],appliesIf:e=>{if(!l(e))return!1;var t=!1,i=!1,n=!1;return e.reports.forEach((function(e){'nutrition_screening'===e.form?t=e.fields.treatment.program&&'SFP'===e.fields.treatment.program:'off'===e.form?n=e.fields.off&&'defaulter'===e.fields.off.reason:'death_confirmation'===e.form&&(i='yes'===e.fields.death_report.death)})),t&&!n&&!i},date:'reported'}]},function(e,t,i){var n=i(1),r=i(6);function s(e,t,i,n,r,s){var a;if(e.appliesToType){var o;if('contacts'===e.appliesTo){if(!r.contact)return;o='contact'===r.contact.type?r.contact.contact_type:r.contact.type}else{if(!s)return;o=s.form}if(-1===e.appliesToType.indexOf(o))return}if('scheduled_tasks'===e.appliesTo||!e.appliesIf||e.appliesIf(r,s))if('scheduled_tasks'===e.appliesTo){if(s&&e.appliesIf){if(!s.scheduled_tasks)return;for(a=0;a=e.start&&r<=e.end}},fields:[{label:function(){return'contact.profile.delivery_code.'+u(c())},value:function(){var e=c();return d(e).getTime()},filter:'relativeDay',width:12},{label:'contact.profile.pnc_visit',value:'contact.profile.visits.of',translate:!0,context:{count:function(){var e=c(),t=d(e),n=reports.filter((function(e){return('M'===e.form||'postnatal_visit'===e.form)&&e.reported_date>t.getTime()&&e.reported_date=0){if(0===(c=T(h)).length)return;if(e=P(c),t=d(e),f=I(h),(m=P(f))&&m.reported_date=0&&e.reported_date>h.reported_date&&e.reported_date=0&&e.reported_date>t.getTime()&&e.reported_datee.reported_date}))}function _(e){return reports.filter((function(t){return r.indexOf(t.form)>=0&&t.reported_date>e.reported_date}))}function h(){var e;if('person'===contact.type)return reports.forEach((function(t){a(t)&&('D'!==t.form&&'delivery'!==t.form||(!e||t.fields&&e.fields&&t.fields.date_of_birth>e.fields.date_of_birth)&&(e=t))})),e}function g(){var e={enrollment:null,exit:null},t=[];return reports.forEach((function(n){'nutrition_screening'!==n.form||n.deleted||e.enrollment&&!(n.reported_date>e.enrollment.reported_date&&'yes'===n.fields.treatment.enroll)||(e.enrollment=n),'nutrition_exit'===n.form&&t.push(n)})),e.enrollment&&(e.exit=t.find((function(t){return t.reported_date>e.enrollment.reported_date}))),e}e.exports={now:n,pregnancyForms:r,antenatalForms:['V','pregnancy_visit'],deliveryForms:i,postnatalForms:['M','postnatal_visit'],immunizationForms:['IMM','C_IMM','immunization_visit','PENTA1','PENTA2','PENTA3','BCG','OPV0','OPV1','OPV2','OPV3','IPV1','IPV2','IPV3','FIPV1','FIPV2','PCV1','PCV2','PCV3','PCV4','ROTA1','ROTA2','ROTA3','VITA','MMR1','MMR2','MMRV1','MMRV2','MN1','MN2','MN3','MN4','FLU','HA1','HA2','HB','JE','YF','TY1','TY2','HPV1','HPV2','HPV3','CH1','CH2','CH3','DPT4','DPT5'],MS_IN_DAY:864e5,MAX_DAYS_IN_PREGNANCY:308,DAYS_IN_PNC:45,IMMUNIZATION_DOSES:o,IMMUNIZATION_LIST:['bcg','cholera','hep_a','hep_b','hpv','ipv','fipv','flu','jap_enc','meningococcal','mmr','mmrv','polio','penta','pneumococcal','rotavirus','typhoid','vitamin_a','yellow_fever','dpt'],count:l,isVaccineInLineage:function(e,t){return e&&e.some((function(e){return e&&s(e.vaccines,t)}))},isCoveredByUseCaseInLineage:function(e,t){return e&&e.some((function(e){return e&&s(e.use_cases,t)}))},contains:s,isCoveredByUseCase:c,isFacilityDelivery:function(e){return e&&e.fields&&e.fields.delivery_code&&'f'===e.fields.delivery_code.toLowerCase()},isNonFacilityDelivery:f,getBirthDate:u,isReportValid:a,getPNCperiod:p,isHighRiskPregnancy:function(e){return'pregnancy'===e.form&&e.fields&&(e.fields.risk_factors||e.fields.danger_signs)||reports.some((function(t){return a(t)&&t.reported_date>e.reported_date&&t.reported_date<=d(e.reported_date,308).getTime()&&('F'===t.form.toUpperCase()||'pregnancy_visit'===t.form&&t.fields&&t.fields.danger_signs)}))},isHighRiskPostnatal:function(e){return e&&e.start&&e.end&&reports.some((function(t){return a(t)&&(f(t)||'F'===t.form.toUpperCase()&&t.reported_date>=e.end.getTime()&&t.reported_date<=e.end.getTime()||'postnatal_visit'===t.form&&t.fields&&(t.fields.danger_signs_mom||t.fields.danger_signs_baby))}))},getDeliveryCode:function(e){return(e&&e.fields&&(e.fields.group_note&&e.fields.group_note.default_chw_sms||e.fields.delivery_code)||'unknown').toLowerCase().replace('anc_only_','')},initImmunizations:function(){var e={};return o.forEach((function(t){e[t[0]]=!1})),e},addImmunizations:function(e,t){o.forEach((function(n){e[n[0]]||(e[n[0]]='string'==typeof t?t.toUpperCase()===n[1]:'yes'===t['received_'+n[0]])}))},countDosesReceived:function(e,t){return l(o,(function(n){return e[n[0]]&&0===n[0].indexOf(t+'_')}))},countDosesPossible:function(e){return l(o,(function(t){return 0===t[0].indexOf(e+'_')}))},isSingleDose:function(e){return o.some((function(t){return t[0]===e}))},countReportsSubmittedInWindow:function(e,t){return l(reports,(function(n){return n.reported_date<=t&&-1!==e.indexOf(n.form)}))},addDate:d,getOldestReport:function(e){var t;return e.forEach((function(e){a(e)&&(!t||e.reported_datee.reported_date}))},getTreatmentEnrollmentDate:function(){var e='',t=g().enrollment;if(t){var n=new Date(0);n.setUTCSeconds(t.reported_date/1e3),e=n.toISOString().slice(0,10)}return e},getTreatmentProgram:function(){var e='',t=g().enrollment,n=g().exit;return t&&!n&&(e=t.fields.treatment.program),e},countNutritionFollowups:function(){var e=0,t=g().enrollment;return reports.forEach((function(n){'nutrition_followup'===n.form&&n.reported_date>t.reported_date&&(e+=1)})),e},getMostRecentReport:function(e,t){var n=null;return e.forEach((function(e){t.indexOf(e.form)>=0&&!e.deleted&&(!n||e.reported_date>n.reported_date)&&(n=e)})),n},getMostRecentNutritionEnrollment:g}},function(e,t){function n(e){return e?Array.isArray(e)?e:[e]:[]}function r(e,t){switch(typeof e){case'undefined':return!0;case'function':return e(t);default:return e}}function i(e,t,n){if(r(e.appliesIf,n)){var i='function'==typeof e.fields?e.fields(n):e.fields.filter((function(e){return r(e.appliesIf,n)})).map((function(e){var t={};return o(e,t,'label'),o(e,t,'value'),o(e,t,'translate'),o(e,t,'filter'),o(e,t,'width'),o(e,t,'icon'),e.context&&(t.context={},o(e.context,t.context,'count'),o(e.context,t.context,'total')),t}));return e.modifyContext&&e.modifyContext(t,n),{label:e.label,fields:i}}function o(e,t,r){switch(typeof e[r]){case'undefined':return;case'function':t[r]=e[r](n);break;default:t[r]=e[r]}}}e.exports=function(e,t,r){var o=e.fields||[],a=e.context||{},l=e.cards||[],s=t&&('contact'===t.type?t.contact_type:t.type),c={cards:[],fields:o.filter((function(e){var t=n(e.appliesToType),r=t.filter((function(e){return e&&'!'===e.charAt(0)}));if((0===t.length||t.includes(s)||r.length>0&&!r.includes('!'+s))&&(!e.appliesIf||e.appliesIf()))return delete e.appliesToType,delete e.appliesIf,!0}))};return l.forEach((function(e){var t,o,l,f,u=n(e.appliesToType);if(u.includes('report')&&u.length>1)throw new Error('You cannot set appliesToType to an array which includes the type \\'report\\' and another type.');if(u.includes('report'))for(t=0;t0)return;(l=i(e,a))&&c.cards.push(l)}})),c.context=a,c}}])})); return ContactSummary;", - "multi_report_alerts": [], - "sms": { - "outgoing_service": "medic-gateway" - }, - "contact_types": [ - { - "id": "district_hospital", - "name_key": "contact.type.district_hospital", - "group_key": "contact.type.district_hospital.plural", - "create_key": "contact.type.district_hospital.new", - "edit_key": "contact.type.place.edit", - "icon": "medic-district-hospital", - "create_form": "form:contact:district_hospital:create", - "edit_form": "form:contact:district_hospital:edit" - }, - { - "id": "health_center", - "name_key": "contact.type.health_center", - "group_key": "contact.type.health_center.plural", - "create_key": "contact.type.health_center.new", - "edit_key": "contact.type.place.edit", - "parents": [ - "district_hospital" - ], - "icon": "medic-health-center", - "create_form": "form:contact:health_center:create", - "edit_form": "form:contact:health_center:edit" - }, - { - "id": "clinic", - "name_key": "contact.type.clinic", - "group_key": "contact.type.clinic.plural", - "create_key": "contact.type.clinic.new", - "edit_key": "contact.type.place.edit", - "parents": [ - "health_center" - ], - "icon": "medic-clinic", - "create_form": "form:contact:clinic:create", - "edit_form": "form:contact:clinic:edit", - "count_visits": true - }, - { - "id": "person", - "name_key": "contact.type.person", - "group_key": "contact.type.person.plural", - "create_key": "contact.type.person.new", - "edit_key": "contact.type.person.edit", - "primary_contact_key": "clinic.field.contact", - "parents": [ - "district_hospital", - "health_center", - "clinic" - ], - "icon": "medic-person", - "create_form": "form:contact:person:create", - "edit_form": "form:contact:person:edit", - "person": true - } - ] -} + { + "form": "L", + "events": [{ + "name": "on_create", + "trigger": "add_expected_date", + "params": "lmp_date", + "bool_expr": "doc.lmp_date" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "ANC Reminders LMP", + "bool_expr": "doc.lmp_date" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "Delivery Reminders", + "bool_expr": "doc.lmp_date" + }, + { + "name": "on_create", + "trigger": "clear_schedule", + "params": "Registration Reminders", + "bool_expr": "doc.lmp_date" + } + ], + "validations": { + "join_responses": false, + "list": [{ + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "registration.lmp_date.patient_id.incorrect" + }, + { + "property": "lmp_year", + "rule": "(integer && min(2078) && max(2090))", + "translation_key": "registration.lmp_date.year.incorrect" + }, + { + "property": "lmp_month", + "rule": "(integer && min(1) && max(12))", + "translation_key": "registration.lmp_date.month.incorrect" + }, + { + "property": "lmp_day", + "rule": "(integer && min(1) && max(32))", + "translation_key": "registration.lmp_date.day.incorrect" + }, + { + "property": "lmp_date", + "rule": "isBefore('1 month')", + "translation_key": "messages.registration.lmp_date.incorrect.before" + }, + { + "property": "lmp_date", + "rule": "isAfter('-9 months')", + "translation_key": "messages.registration.lmp_date.incorrect.after" + } + ] + }, + "messages": [{ + "translation_key": "registration.lmp_date.ok", + "event_type": "report_accepted", + "recipient": "clinic" + }, + { + "translation_key": "registration.lmp_date.ok.health_center", + "event_type": "report_accepted", + "recipient": "health_center" + }, + { + "translation_key": "registration.lmp_date.patient_id.not_found", + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "J", + "events": [{ + "name": "on_create", + "trigger": "add_birth_date", + "params": "birth_date", + "bool_expr": "doc.days_since_birth" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "PNC Reminders", + "bool_expr": "doc.days_since_birth" + }, + { + "name": "on_create", + "trigger": "clear_schedule", + "params": "Delivery Reminders, ANC Reminders LMP", + "bool_expr": "doc.patient_id" + } + ], + "validations": { + "join_responses": false, + "list": [{ + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! J <डेलिभरी कोड><दर्ता नं (५ देखि १३ अंकको)> <डेलिभरी भएको दिन>", + "locale": "ne" + }] + }, + { + "property": "delivery_code", + "rule": "regex('^(S|A|s|a)$')", + "translation_key": "", + "message": [{ + "content": "{{#delivery_code}} नमस्ते {{contact.name}}! {{delivery_code}} डेलिभरी कोड मिलेन। स्वास्थ्य सँस्थामा भएको जन्मको लागि ‘S’ र घरमा भएको जन्मको लागि ‘A’ प्रयोग गर्नुहोला। धन्यवाद। {{/delivery_code}}", + "locale": "ne" + }] + }, + { + "property": "days_since_birth", + "rule": "(integer && min(0) && max(50))", + "translation_key": "", + "message": [{ + "content": "{{#days_since_birth}}नमस्ते {{contact.name}}! प्रसुति पश्चातको अवधि मिलेन, सो अवधि ० देखि ५० दिन भित्र हुनुपर्दछ। तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! J <डेलिभरी कोड> {{patient_id}} <डेलिभरी भएको दिन>{{/days_since_birth}}{{^days_since_birth}}नमस्ते {{contact.name}}! प्रसुति पश्चातको अवधि ० देखि ५० दिन भित्र हुनुपर्दछ। तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! J <डेलिभरी कोड> {{patient_id}} <डेलिभरी भएको दिन>{{/days_since_birth}}", + "locale": "ne" + }] + } + ] + }, + "messages": [{ + "translation_key": "", + "message": [{ + "content": "नमस्ते! {{patient_name}} ({{patient_id}}) का लागि प्रसुति प्रतिवेदन प्राप्त भयो। १,३,७-१४ र ४२ दिनमा हुनु पर्ने सुत्केरी जाँचभेट एकिन गरी SMS बाट P {{patient_id}} <जाँचको पटक> पठाउनुहोला धन्यबाद!", + "locale": "ne" + }], + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [{ + "content": "नमस्ते! {{contact.name}} बाट {{patient_name}} ({{patient_id}}) को प्रसुति प्रतिवेदन प्राप्त भयो। १,३,७-१४ र ४२ दिनमा हुनु पर्ने सुत्केरी जाँचभेट गर्न घर भेट गर्नु होला, धन्यबाद!", + "locale": "ne" + }], + "recipient": "parent" + }, + { + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! J <डेलिभरी कोड><दर्ता नं (५ देखि १३ अंकको)> <डेलिभरी भएको दिन>", + "locale": "ne" + }], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "K", + "events": [{ + "name": "on_create", + "trigger": "add_patient_id", + "params": "", + "bool_expr": "doc.patient_name" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "Registration Reminders", + "bool_expr": "doc.patient_name" + } + ], + "validations": { + "join_responses": false, + "list": [{ + "property": "patient_age", + "rule": "(integer && min(0) && max(125))", + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! दर्ताको ढाँचा मिलेन। कृपया तलको ढाँचाअनुसार पुनः पठाउनुहोस्: K <उमेर (बर्षमा)> <नाम (बढीमा ३० अक्षर सम्मको)>", + "locale": "ne" + }] + }, + { + "property": "patient_name", + "rule": "(string && lenMin(1) && lenMax(30))", + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! दर्ताको ढाँचा मिलेन। कृपया तलको ढाँचाअनुसार पुनः पठाउनुहोस्: K <उमेर (बर्षमा)> <नाम (बढीमा ३० अक्षर सम्मको)>", + "locale": "ne" + }] + } + ] + }, + "messages": [{ + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}, {{patient_name}} ({{patient_id}}) लाई दर्ता गर्नुभएकोमा धन्यवाद! ", + "locale": "" + }], + "recipient": "" + }] + }, + { + "form": "M", + "events": [{ + "name": "on_create", + "trigger": "clear_schedule", + "params": "Registration Reminders, ANC Reminders LMP, PNC Reminders, Delivery Reminders", + "bool_expr": "doc.patient_id" + }], + "validations": { + "join_responses": false, + "list": [{ + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [{ + "content": "{{#patient_id}}नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! M <दर्ता नं (५ देखि १३ अंकको)>{{/patient_id}}{{^patient_id}}नमस्ते {{contact.name}}! तपाईले पठाउनु भएको रिपोर्टको ढाँचा मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! M <दर्ता नं (५ देखि १३ अंकको)>{{/patient_id}}", + "locale": "ne" + }] + }] + }, + "messages": [{ + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}, {{patient_name}} {{patient_id}} को मृत्युको दु:खद घटनाको सूचना दिनुभएकोमा धन्यवाद। ", + "locale": "ne" + }], + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [{ + "content": "{{contact.name}} ले {{patient_age}} बर्षिया {{patient_name}} ({{patient_id}}) को मृत्यु भएको सूचना दिनुभएको छ। ", + "locale": "ne" + }], + "recipient": "parent" + }, + { + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} कसैलाई पनि दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! म <दर्ता नं (५ देखि १३ अंकको)", + "locale": "ne" + }], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + } + ], + "patient_reports": [{ + "form": "ग", + "name": "Visits", + "format": "ग ", + "silence_type": "ANC Reminders LMP", + "silence_for": "0 days", + "fields": [{ + "field_name": "", + "title": "" + }], + "validations": { + "join_responses": false, + "list": [{ + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! ग <दर्ता नं (५ देखि १३ अंकको)>", + "locale": "ne" + }] + }] + }, + "messages": [{ + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_name}} ({{patient_id}}) लाई भेटेकोमा धन्यबाद!", + "locale": "ne" + }], + "event_type": "report_accepted", + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन।दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! ग <दर्ता नं (५ देखि १३ अंकको)>", + "locale": "ne" + }], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "ख", + "name": "Flag", + "format": "ख ", + "silence_type": "", + "silence_for": "", + "fields": [{ + "field_name": "patient_id", + "title": "Patient ID" + }], + "validations": { + "join_responses": false, + "list": [{ + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [{ + "content": "{{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! ख <दर्ता नं (५ देखि १३ अंकको)>", + "locale": "ne" + }] + }] + }, + "messages": [{ + "translation_key": "", + "message": [{ + "content": "नमस्ते! {{patient_name}} {{patient_id}}का लागि खतराको चिन्ह पहिचान भएको रिपोर्ट प्राप्त भयो। उहाँलाई बिशेष निगरानीमा राखी आवश्यकता अनुसार स्वास्थ्य संस्था पठाउनुहोला।", + "locale": "ne" + }], + "event_type": "report_accepted", + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [{ + "content": "नमस्ते! {{contact.name}}ले {{patient_name}} {{patient_id}} का लागि खतराको चिन्ह पहिचान गर्नुभएको छ । म.स्वा.स्व.से.संग सोधखोज गरि आवश्यक सल्लाह दिनुहोला |", + "locale": "ne" + }], + "event_type": "report_accepted", + "recipient": "parent" + }, + { + "translation_key": "", + "message": [{ + "content": "{{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी पुन: फारम पठाउनुहोला, धन्यबाद!", + "locale": "ne" + }], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "प", + "name": "PNC Visit", + "format": "प ", + "silence_type": "PNC Reminders", + "silence_for": "0 days", + "fields": [{ + "field_name": "patient_id", + "title": "Patient ID" + }, + { + "field_name": "pnc_visits", + "title": "No. of PNC visits" + } + ], + "validations": { + "join_responses": false, + "list": [{ + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} सन्देशको ढांचा मिलेन! ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! प <दर्ता नं (५ देखि १३ अंकको)> <हालसम्मको जम्मा सुत्केरी जाँच भेट संख्या>", + "locale": "ne" + }] + }, + { + "property": "pnc_visits", + "rule": "(integer && min(0) && max(4))", + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} को सुत्केरी जाँच भेट संख्या मिलेन। ० देखि ४ बीचको संख्या हुनु पर्दछ। मिलाएर पठाउनु होला, धन्यवाद।", + "locale": "ne" + }] + } + ] + }, + "messages": [{ + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} को सुत्केरी जाँच प्रतिवेदन पठाउनु भएकोमा धन्यवाद।", + "locale": "ne" + }], + "event_type": "report_accepted", + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! सन्देशको ढांचा मिलेन! ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! प <दर्ता नं (५ देखि १३ अंकको)> <हालसम्मको जम्मा सुत्केरी जाँच भेट संख्या>", + "locale": "ne" + }], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "G", + "name": "Visits", + "format": "G ", + "silence_type": "ANC Reminders LMP", + "silence_for": "0 days", + "fields": [{ + "field_name": "", + "title": "" + }], + "validations": { + "join_responses": false, + "list": [{ + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! G <दर्ता नं (५ देखि १३ अंकको)>", + "locale": "ne" + }] + }] + }, + "messages": [{ + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_name}} ({{patient_id}}) लाई भेटेकोमा धन्यबाद!", + "locale": "ne" + }], + "event_type": "report_accepted", + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन।दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! G <दर्ता नं (५ देखि १३ अंकको)>", + "locale": "ne" + }], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "D", + "name": "Flag", + "format": "D ", + "silence_type": "", + "silence_for": "", + "fields": [{ + "field_name": "patient_id", + "title": "Patient ID" + }], + "validations": { + "join_responses": false, + "list": [{ + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [{ + "content": "{{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! D <दर्ता नं (५ देखि १३ अंकको)>", + "locale": "ne" + }] + }] + }, + "messages": [{ + "translation_key": "", + "message": [{ + "content": "नमस्ते! {{patient_name}} {{patient_id}}का लागि खतराको चिन्ह पहिचान भएको रिपोर्ट प्राप्त भयो। उहाँलाई बिशेष निगरानीमा राखी आवश्यकता अनुसार स्वास्थ्य संस्था पठाउनुहोला।", + "locale": "ne" + }], + "event_type": "report_accepted", + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [{ + "content": "नमस्ते! {{contact.name}}ले {{patient_name}} {{patient_id}} का लागि खतराको चिन्ह पहिचान गर्नुभएको छ । म.स्वा.स्व.से.संग सोधखोज गरि आवश्यक सल्लाह दिनुहोला |", + "locale": "ne" + }], + "event_type": "report_accepted", + "recipient": "parent" + }, + { + "translation_key": "", + "message": [{ + "content": "{{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी पुन: फारम पठाउनुहोला, धन्यबाद!", + "locale": "ne" + }], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "P", + "name": "PNC Visit", + "format": "P ", + "silence_type": "PNC Reminders", + "silence_for": "0 days", + "fields": [{ + "field_name": "patient_id", + "title": "Patient ID" + }, + { + "field_name": "pnc_visits", + "title": "No. of PNC visits" + } + ], + "validations": { + "join_responses": false, + "list": [{ + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} सन्देशको ढांचा मिलेन! ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! P <दर्ता नं (५ देखि १३ अंकको)> <हालसम्मको जम्मा सुत्केरी जाँच भेट संख्या>", + "locale": "ne" + }] + }, + { + "property": "pnc_visits", + "rule": "(integer && min(0) && max(4))", + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} को सुत्केरी जाँच भेट संख्या मिलेन। ० देखि ४ बीचको संख्या हुनु पर्दछ। मिलाएर पठाउनु होला, धन्यवाद।", + "locale": "ne" + }] + } + ] + }, + "messages": [{ + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} को सुत्केरी जाँच प्रतिवेदन पठाउनु भएकोमा धन्यवाद।", + "locale": "ne" + }], + "event_type": "report_accepted", + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! सन्देशको ढांचा मिलेन! ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! P <दर्ता नं (५ देखि १३ अंकको)> <हालसम्मको जम्मा सुत्केरी जाँच भेट संख्या>", + "locale": "ne" + }], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + } + ], + "muting": { + "mute_forms": [ + "ब", + "B" + ], + "unmute_forms": [ + "स", + "F" + ], + "validations": { + "join_responses": false, + "list": [{ + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! सूचना बन्द गरेको भए : ब वा B <दर्ता नं (५ देखि १३ अंकको)>, सूचना सुरु गरेको भए: स वा F <दर्ता नं (५ देखि १३ अंकको)>", + "locale": "ne" + }] + }] + }, + "messages": [{ + "translation_key": "", + "message": [{ + "content": "धन्यवाद, {{contact.name}}! {{patient_name}} {{patient_id}} का लागि सबै सुचना बन्द गरिएको छ। सुचारु गर्नका लागि स वा F {{patient_id}} लेखि SMS पठाउनुहोला।", + "locale": "ne" + }], + "event_type": "mute", + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [{ + "content": "{{contact.name}} ले {{patient_name}} ({{patient_id}}) का लागि सबै सुचना बन्द गराउनु भएको छ।", + "locale": "ne" + }], + "event_type": "mute", + "recipient": "parent" + }, + { + "translation_key": "", + "message": [{ + "content": "धन्यवाद, {{contact.name}}! {{patient_name}} ({{patient_id}}) का लागि सुचना सुचारु गरिएको छ।", + "locale": "ne" + }], + "event_type": "unmute", + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [{ + "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी पुन: फारम पठाउनुहोला, धन्यबाद!", + "locale": "ne" + }], + "event_type": "contact_not_found", + "recipient": "clinic" + } + ] + }, + "_version": "3.17.1", + "outgoing_deny_list": "1414, 9771414, +9771414, 1408, 9771408, +9771408, 1415, 9771415, +9771415, 1455, 9771455, +9771455, 9851028801, 9779851028801, +9779851028801, 1477, +9771477, NT, 34455, +97734455, 1422, +9771422", + "outgoing_deny_shorter_than": 13, + "outgoing_deny_with_alphas": true, + "multipart_sms_limit": 10, + "sms": { + "outgoing_service": "medic-gateway" + }, + "forms": { + "NP": { + "meta": { + "code": "NP", + "translation_key": "forms.np.title", + "label": { + "en": "New Person with phone_number Registration" + } + }, + "fields": { + "patient_age": { + "labels": { + "tiny": { + "en": "Person Age" + }, + "short": { + "en": "Person Age" + } + }, + "position": 0, + "type": "integer", + "length": [ + 1, + 3 + ], + "range": [ + 0, + 125 + ], + "required": true + }, + "patient_name": { + "labels": { + "tiny": { + "en": "patient_name" + }, + "description": { + "en": "Patient name" + }, + "short": { + "en": "Patient name" + } + }, + "position": 1, + "type": "string", + "length": [ + 3, + 30 + ], + "required": true + }, + "phone_number": { + "labels": { + "tiny": { + "en": "phone number" + }, + "description": { + "en": "phone number" + }, + "short": { + "en": "phone number" + } + }, + "position": 2, + "type": "phone_number", + "length": [ + 9, + 10 + ], + "required": true + } + }, + "public_form": false, + "use_sentinel": true + }, + "क": { + "meta": { + "code": "क", + "translation_key": "forms.ka.title" + }, + "fields": { + "patient_age": { + "labels": { + "tiny": { + "en": "Person Age" + }, + "short": { + "en": "Person Age" + } + }, + "position": 0, + "type": "integer", + "length": [ + 1, + 3 + ], + "range": [ + 0, + 125 + ], + "required": true + }, + "patient_name": { + "labels": { + "tiny": { + "en": "patient_name" + }, + "short": { + "en": "Patient Name" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 40 + ], + "required": true + } + }, + "public_form": false, + "use_sentinel": true + }, + "अ": { + "meta": { + "code": "अ", + "translation_key": "forms.a.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "patient_id" + }, + "short": { + "en": "Patient Id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "last_menstrual_period": { + "labels": { + "tiny": { + "en": "LMP" + }, + "short": { + "en": "Weeks since LMP" + } + }, + "position": 1, + "type": "integer", + "length": [ + 1, + 2 + ], + "range": [ + 6, + 42 + ], + "required": true + } + }, + "public_form": false, + "use_sentinel": true + }, + "ग": { + "meta": { + "code": "ग", + "translation_key": "forms.ga.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "id" + }, + "short": { + "en": "ID" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 20 + ], + "required": false + } + }, + "use_sentinel": true + }, + "ज": { + "meta": { + "code": "ज", + "translation_key": "forms.ja.title" + }, + "fields": { + "delivery_code": { + "labels": { + "short": { + "en": "Delivery Code" + } + }, + "position": 0, + "type": "string", + "length": [ + 1, + 2 + ], + "required": true + }, + "patient_id": { + "labels": { + "short": { + "en": "ID" + } + }, + "position": 1, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "days_since_birth": { + "labels": { + "short": { + "en": "Days Since Birth" + }, + "tiny": { + "en": "DSB" + } + }, + "position": 2, + "type": "integer", + "length": [ + 1, + 2 + ], + "range": [ + 1, + 50 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 3, + "type": "string", + "length": [ + 1, + 20 + ], + "required": false + } + }, + "use_sentinel": true + }, + "ख": { + "meta": { + "code": "ख", + "translation_key": "forms.kha.title" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "en": "ID" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "use_sentinel": true + }, + "ब": { + "meta": { + "code": "ब", + "translation_key": "forms.ba.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "ID" + }, + "short": { + "en": "ID" + } + }, + "position": 0, + "length": [ + 5, + 13 + ], + "type": "string", + "required": true + }, + "notes": { + "labels": { + "tiny": { + "en": "r", + "sw": "r" + }, + "short": { + "en": "Reason" + } + }, + "position": 1, + "length": [ + 3, + 100 + ], + "type": "string" + } + }, + "use_sentinel": true + }, + "स": { + "meta": { + "code": "स", + "translation_key": "forms.sa.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "ID" + }, + "short": { + "en": "ID" + } + }, + "position": 0, + "length": [ + 5, + 13 + ], + "type": "string" + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "use_sentinel": true + }, + "प": { + "meta": { + "code": "प", + "translation_key": "forms.pa.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "ID" + }, + "short": { + "en": "ID" + } + }, + "position": 0, + "length": [ + 5, + 13 + ], + "type": "string", + "required": true + }, + "pnc_visits": { + "labels": { + "tiny": { + "en": "PNCV" + }, + "short": { + "en": "PNC Visits" + } + }, + "position": 1, + "length": [ + 1, + 1 + ], + "range": [ + 0, + 4 + ], + "type": "string", + "required": true + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 2, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "use_sentinel": true + }, + "म": { + "meta": { + "code": "म", + "translation_key": "forms.ma.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "id" + }, + "short": { + "en": "Patient Id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "death_notes": { + "labels": { + "tiny": { + "en": "note" + }, + "short": { + "en": "Notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 20 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "ल": { + "meta": { + "code": "ल", + "translation_key": "forms.la.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "patient_id" + }, + "short": { + "en": "Patient Id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "lmp_year": { + "labels": { + "tiny": { + "en": "LMP_Y" + }, + "short": { + "en": "LMP Year" + } + }, + "position": 1, + "type": "bsYear", + "required": true + }, + "lmp_month": { + "labels": { + "tiny": { + "en": "LMP_M" + }, + "short": { + "en": "LMP Month" + } + }, + "position": 2, + "type": "bsMonth", + "required": true + }, + "lmp_day": { + "labels": { + "tiny": { + "en": "LMP_D" + }, + "short": { + "en": "LMP Day" + } + }, + "position": 3, + "type": "bsDay", + "required": true + }, + "lmp_date": { + "labels": { + "tiny": { + "en": "LMP_DATE" + }, + "short": { + "en": "LMP Date" + } + }, + "position": 4, + "type": "bsAggreDate", + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "K": { + "meta": { + "code": "K", + "translation_key": "forms.k.title" + }, + "fields": { + "patient_age": { + "labels": { + "tiny": { + "en": "Person Age" + }, + "short": { + "en": "Person Age" + } + }, + "position": 0, + "type": "integer", + "length": [ + 1, + 3 + ], + "range": [ + 0, + 125 + ], + "required": true + }, + "patient_name": { + "labels": { + "tiny": { + "en": "patient_name" + }, + "short": { + "en": "Patient Name" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 40 + ], + "required": true + } + }, + "public_form": false, + "use_sentinel": true + }, + "A": { + "meta": { + "code": "A", + "translation_key": "forms.a_en.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "patient_id" + }, + "short": { + "en": "Patient Id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "last_menstrual_period": { + "labels": { + "tiny": { + "en": "LMP" + }, + "short": { + "en": "Weeks since LMP" + } + }, + "position": 1, + "type": "integer", + "length": [ + 1, + 2 + ], + "range": [ + 6, + 42 + ], + "required": true + } + }, + "public_form": false, + "use_sentinel": true + }, + "L": { + "meta": { + "code": "L", + "translation_key": "forms.l.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "patient_id" + }, + "short": { + "en": "Patient Id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "lmp_year": { + "labels": { + "tiny": { + "en": "LMP_Y" + }, + "short": { + "en": "LMP Year" + } + }, + "position": 1, + "type": "bsYear", + "required": true + }, + "lmp_month": { + "labels": { + "tiny": { + "en": "LMP_M" + }, + "short": { + "en": "LMP Month" + } + }, + "position": 2, + "type": "bsMonth", + "required": true + }, + "lmp_day": { + "labels": { + "tiny": { + "en": "LMP_D" + }, + "short": { + "en": "LMP Day" + } + }, + "position": 3, + "type": "bsDay", + "required": true + }, + "lmp_date": { + "labels": { + "tiny": { + "en": "LMP_DATE" + }, + "short": { + "en": "LMP Date" + } + }, + "position": 4, + "type": "bsAggreDate", + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "G": { + "meta": { + "code": "G", + "translation_key": "forms.g.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "id" + }, + "short": { + "en": "ID" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 20 + ], + "required": false + } + }, + "use_sentinel": true + }, + "J": { + "meta": { + "code": "J", + "translation_key": "forms.j.title" + }, + "fields": { + "delivery_code": { + "labels": { + "short": { + "en": "Delivery Code" + } + }, + "position": 0, + "type": "string", + "length": [ + 1, + 2 + ], + "required": true + }, + "patient_id": { + "labels": { + "short": { + "en": "ID" + } + }, + "position": 1, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "days_since_birth": { + "labels": { + "short": { + "en": "Days Since Birth" + }, + "tiny": { + "en": "DSB" + } + }, + "position": 2, + "type": "integer", + "length": [ + 1, + 2 + ], + "range": [ + 1, + 50 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 3, + "type": "string", + "length": [ + 1, + 20 + ], + "required": false + } + }, + "use_sentinel": true + }, + "D": { + "meta": { + "code": "D", + "translation_key": "forms.d.title" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "en": "ID" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "use_sentinel": true + }, + "B": { + "meta": { + "code": "B", + "translation_key": "forms.b.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "ID" + }, + "short": { + "en": "ID" + } + }, + "position": 0, + "length": [ + 5, + 13 + ], + "type": "string", + "required": true + }, + "notes": { + "labels": { + "tiny": { + "en": "r", + "sw": "r" + }, + "short": { + "en": "Reason" + } + }, + "position": 1, + "length": [ + 3, + 100 + ], + "type": "string" + } + }, + "use_sentinel": true + }, + "F": { + "meta": { + "code": "F", + "translation_key": "forms.f.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "ID" + }, + "short": { + "en": "ID" + } + }, + "position": 0, + "length": [ + 5, + 13 + ], + "type": "string" + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "use_sentinel": true + }, + "P": { + "meta": { + "code": "P", + "translation_key": "forms.p.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "ID" + }, + "short": { + "en": "ID" + } + }, + "position": 0, + "length": [ + 5, + 13 + ], + "type": "string", + "required": true + }, + "pnc_visits": { + "labels": { + "tiny": { + "en": "PNCV" + }, + "short": { + "en": "PNC Visits" + } + }, + "position": 1, + "length": [ + 1, + 1 + ], + "range": [ + 0, + 4 + ], + "type": "string", + "required": true + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 2, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "use_sentinel": true + }, + "M": { + "meta": { + "code": "M", + "translation_key": "forms.m.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "id" + }, + "short": { + "en": "Patient Id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "death_notes": { + "labels": { + "tiny": { + "en": "note" + }, + "short": { + "en": "Notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 20 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + } + }, + "schedules": [{ + "name": "ANC Reminders LMP", + "translation_key": "schedule.anc_lmp", + "summary": "ANC Reminders", + "description": "", + "start_from": "lmp_date", + "messages": [{ + "translation_key": "messages.schedule.anc.reminder_1_1", + "group": 1, + "offset": "57 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_1_2", + "group": 1, + "offset": "70 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_1_not_done", + "group": 1, + "offset": "77 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_1_parent", + "group": 1, + "offset": "77 days", + "send_day": "", + "send_time": "4:30", + "recipient": "parent" + }, + { + "translation_key": "messages.schedule.anc.reminder_2_1", + "group": 2, + "offset": "85 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_2_2", + "group": 2, + "offset": "98 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_2_not_done", + "group": 2, + "offset": "105 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_2_parent", + "group": 2, + "offset": "105 days", + "send_day": "", + "send_time": "4:30", + "recipient": "parent" + }, + { + "translation_key": "messages.schedule.anc.reminder_3_1", + "group": 3, + "offset": "134 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_3_2", + "group": 3, + "offset": "148 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_3_not_done", + "group": 3, + "offset": "161 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_3_parent", + "group": 3, + "offset": "161 days", + "send_day": "", + "send_time": "4:30", + "recipient": "parent" + }, + { + "translation_key": "messages.schedule.anc.reminder_4_1", + "group": 4, + "offset": "188 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_4_not_done", + "group": 4, + "offset": "194 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_4_parent", + "group": 4, + "offset": "194 days", + "send_day": "", + "send_time": "4:30", + "recipient": "parent" + }, + { + "translation_key": "messages.schedule.anc.reminder_5_1", + "group": 5, + "offset": "216 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_5_not_done", + "group": 5, + "offset": "222 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_5_parent", + "group": 5, + "offset": "222 days", + "send_day": "", + "send_time": "4:30", + "recipient": "parent" + }, + { + "translation_key": "messages.schedule.anc.reminder_6_1", + "group": 6, + "offset": "230 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_6_not_done", + "group": 6, + "offset": "236 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_6_parent", + "group": 6, + "offset": "236 days", + "send_day": "", + "send_time": "4:30", + "recipient": "parent" + }, + { + "translation_key": "messages.schedule.anc.reminder_7_1", + "group": 7, + "offset": "244 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_7_not_done", + "group": 7, + "offset": "250 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_7_parent", + "group": 7, + "offset": "250 days", + "send_day": "", + "send_time": "4:30", + "recipient": "parent" + }, + { + "translation_key": "messages.schedule.anc.reminder_8_1", + "group": 8, + "offset": "260 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_8_not_done", + "group": 8, + "offset": "273 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_8_parent", + "group": 8, + "offset": "273 days", + "send_day": "", + "send_time": "4:30", + "recipient": "parent" + } + ] + }, + { + "name": "PNC Reminders", + "translation_key": "schedule.pnc_rem", + "summary": "", + "description": "Scheduled reminders for PNC", + "start_from": "birth_date", + "messages": [{ + "translation_key": "messages.schedule.pnc.day_7", + "group": 1, + "offset": "7 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.pnc.day_15", + "group": 1, + "offset": "15 days", + "send_day": "", + "send_time": "4:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.pnc.day_15_HF", + "group": 1, + "offset": "15 days", + "send_day": "", + "send_time": "5:00", + "recipient": "parent" + }, + { + "translation_key": "messages.schedule.pnc.day_40", + "group": 2, + "offset": "40 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.pnc.day_45", + "group": 2, + "offset": "45 days", + "send_day": "", + "send_time": "4:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.pnc.day_45_HF", + "group": 2, + "offset": "45 days", + "send_day": "", + "send_time": "5:00", + "recipient": "parent" + } + ] + }, + { + "name": "Registration Reminders", + "translation_key": "schedule.registration.new_person", + "summary": "", + "description": "Scheduled reminders for Person Registration", + "start_from": "reported_date", + "messages": [{ + "translation_key": "messages.schedule.registration.day_1", + "group": 1, + "offset": "1 days", + "send_day": "", + "send_time": "5:30 +05:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.registration.day_3", + "group": 2, + "offset": "3 days", + "send_day": "", + "send_time": "5:30 +05:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.registration.day_14", + "group": 3, + "offset": "14 days", + "send_day": "", + "send_time": "5:30 +05:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.registration.day_14.hf", + "group": 3, + "offset": "14 days", + "send_day": "", + "send_time": "10:30 +05:45", + "recipient": "health_center" + } + ] + }, + { + "name": "Delivery Reminders", + "translation_key": "schedule.delivery_lmp", + "summary": "", + "description": "Scheduled reminders for Delivery", + "start_from": "lmp_date", + "messages": [{ + "translation_key": "messages.schedule.anc.delivery_1", + "group": 1, + "offset": "266 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.delivery_2", + "group": 1, + "offset": "273 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.delivery_3", + "group": 2, + "offset": "287 days", + "send_day": "", + "send_time": "4:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.delivery_parent_1", + "group": 2, + "offset": "288 days", + "send_day": "", + "send_time": "4:45", + "recipient": "parent" + }, + { + "translation_key": "messages.schedule.anc.delivery_4", + "group": 3, + "offset": "315 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.delivery_parent_2", + "group": 3, + "offset": "322 days", + "send_day": "", + "send_time": "4:45", + "recipient": "parent" + } + ] + } + ] +} \ No newline at end of file diff --git a/shared-libs/phone-number/src/phone-number.js b/shared-libs/phone-number/src/phone-number.js index 8d1a43c1329..7d7561a7e8d 100644 --- a/shared-libs/phone-number/src/phone-number.js +++ b/shared-libs/phone-number/src/phone-number.js @@ -5,7 +5,7 @@ const phonenumber = require('google-libphonenumber'); const CHARACTER_REGEX = /[a-z]/i; -const _init = function(settings, phone) { +const _init = function (settings, phone) { const instance = phonenumber.PhoneNumberUtil.getInstance(); const shortInfo = phonenumber.ShortNumberInfo.getInstance(); const countryCode = settings && settings.default_country_code; @@ -15,11 +15,13 @@ const _init = function(settings, phone) { const validPhone = () => { if (validationType === 'partial') { + //quickly guesses whether a number is a possible phone number by using only the length information, return instance.isPossibleNumber(parsed); } - if(validationType === 'none') { + if (validationType === 'none') { return true; } + //Does full validation of a phone number for a region using length and prefix information. return instance.isValidNumber(parsed); }; @@ -27,7 +29,7 @@ const _init = function(settings, phone) { if (shortInfo.isValidShortNumber(parsed)) { return phonenumber.PhoneNumberFormat.NATIONAL; } - if (typeof(given) !== 'undefined') { + if (typeof (given) !== 'undefined') { return given; } if (parsed.getCountryCode() + '' === countryCode) { @@ -37,13 +39,13 @@ const _init = function(settings, phone) { }; return { - format: function(scheme) { + format: function (scheme) { if (!this.validate()) { return false; } return instance.format(parsed, getScheme(scheme)).toString(); }, - validate: function() { + validate: function () { return validPhone() && // Disallow alpha numbers which libphonenumber considers valid, // e.g. 1-800-MICROSOFT. @@ -58,7 +60,7 @@ const _init = function(settings, phone) { * @param {String} phone The phone number to normalize. * @returns {(String|Boolean)} The normalized number or false if invalid. */ -exports.normalize = function(settings, phone) { +exports.normalize = function (settings, phone) { try { return _init(settings, phone).format(phonenumber.PhoneNumberFormat.E164); } catch (e) { @@ -73,7 +75,7 @@ exports.normalize = function(settings, phone) { * @param {String} phone The phone number to normalize. * @returns {(String|Boolean)} The formatted number or false if invalid. */ -exports.format = function(settings, phone) { +exports.format = function (settings, phone) { try { return _init(settings, phone).format(); } catch (e) { @@ -89,7 +91,7 @@ exports.format = function(settings, phone) { * @param {String} phone The phone number to normalize. * @returns {Boolean} Whether or not the number is valid. */ -exports.validate = function(settings, phone) { +exports.validate = function (settings, phone) { try { return _init(settings, phone).validate(); } catch (e) { @@ -104,11 +106,11 @@ exports.validate = function(settings, phone) { * @param {String} b The second phone number * @returns {Boolean} Whether or not the numbers match */ -exports.same = function(a, b) { +exports.same = function (a, b) { try { const match = phonenumber.PhoneNumberUtil.getInstance().isNumberMatch(a, b); return match === phonenumber.PhoneNumberUtil.MatchType.NSN_MATCH || - match === phonenumber.PhoneNumberUtil.MatchType.EXACT_MATCH; + match === phonenumber.PhoneNumberUtil.MatchType.EXACT_MATCH; } catch (e) { // exception thrown trying to match given numbers } diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 5fa337b8aab..5d1e28faa93 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -194,8 +194,7 @@ const getPhoneNumber = doc => { const setPhoneNumber = doc => { const phoneNumber = getPhoneNumber(doc); - // get default country code - //countryCode = codeFromSettings() + const countryCode = config.getAll().default_country_code; doc.phone_number = countryCode.concat(phoneNumber.toString()); }; diff --git a/shared-libs/transitions/src/transitions/utils.js b/shared-libs/transitions/src/transitions/utils.js index 1536e4a6ac4..2d6a55167a1 100644 --- a/shared-libs/transitions/src/transitions/utils.js +++ b/shared-libs/transitions/src/transitions/utils.js @@ -58,7 +58,7 @@ module.exports = { }, isIdUnique: (id) => { return db.medic - .query('medic-client/contacts_by_reference', { key: [ 'shortcode', id ]}) + .query('medic-client/contacts_by_reference', { key: ['shortcode', id] }) .then(results => !(results && results.rows && results.rows.length)); }, addUniqueId: (doc) => { @@ -66,6 +66,11 @@ module.exports = { doc.patient_id = patientId; }); }, + isPhoneUnique: (phone) => { + return db.medic + .query('medic-client/contacts_by_reference', { key: ['shortcode', id] }) + .then(results => !(results && results.rows && results.rows.length)); + }, getUniqueId: () => idGenerator.next().value, hasRun: (doc, transition) => { From 0ffa8c86cf3af2292cbbbb94248c9d0b588dedc0 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 31 May 2023 10:52:42 +0545 Subject: [PATCH 07/56] Added add_age trigger as well as country code validadtion at registration --- config/default/app_settings.json | 6191 +++++++++-------- .../src/transitions/registration.js | 26 +- 2 files changed, 3228 insertions(+), 2989 deletions(-) diff --git a/config/default/app_settings.json b/config/default/app_settings.json index bf5e99a9063..a4344989fa5 100644 --- a/config/default/app_settings.json +++ b/config/default/app_settings.json @@ -1,2990 +1,3205 @@ { - "locales": null, - "locale": "en", - "locale_outgoing": "ne", - "date_format": "ddd, MMM Do, YYYY", - "reported_date_format": "ddd, MMM Do, YYYY [at] h:mm A", - "forms_only_mode": false, - "place_hierarchy_types": [ - "district_hospital", - "health_center" - ], - "default_responses": { - "start_date": "1970-01-01" - }, - "district_admins_access_unallocated_messages": false, - "public_access": false, - "default_country_code": 977, - "gateway_number": "+9779812345678", - "dhis_data_sets": [{ - "id": "VMuFODsyWaO", - "translation_key": "dhis.dataset.monthly.integration" - }], - "schedule_morning_hours": 6, - "schedule_morning_minutes": 0, - "schedule_evening_hours": 20, - "schedule_evening_minutes": 0, - "synthetic_date": "", - "contact_display_short": "clinic.name", - "roles": { - "national_admin": { - "name": "usertype.national-manager" - }, - "district_admin": { - "name": "usertype.district-manager", - "offline": true - }, - "data_entry": { - "name": "usertype.data-entry" - }, - "analytics": { - "name": "usertype.analytics" - }, - "gateway": { - "name": "usertype.gateway" - }, - "district_level": { - "name": "usertype.district_level" - }, - "munic_level": { - "name": "usertype.munic_level", - "offline": true - }, - "hf_level": { - "name": "usertype.hf_level", - "offline": true - } - }, - "permissions": { - "can_access_gateway_api": [ - "gateway" - ], - "can_bulk_delete_reports": [ - "national_admin", - "district_admin" - ], - "can_configure": [ - "national_admin" - ], - "can_create_people": [ - "national_admin" - ], - "can_create_places": [ - "national_admin" - ], - "can_create_records": [ - "national_admin", - "district_admin", - "data_entry", - "gateway" - ], - "can_create_users": [ - "national_admin" - ], - "can_delete_contacts": [ - "national_admin", - "district_admin" - ], - "can_delete_messages": [ - "national_admin", - "district_admin" - ], - "can_delete_reports": [ - "national_admin", - "district_admin" - ], - "can_delete_users": [ - "national_admin" - ], - "can_edit": [ - "national_admin", - "district_admin", - "gateway" - ], - "can_edit_profile": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_edit_verification": [ - "national_admin", - "district_admin" - ], - "can_export_all": [ - "national_admin" - ], - "can_export_audit": [ - "national_admin" - ], - "can_export_contacts": [ - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_export_feedback": [], - "can_export_forms": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level", - "analytics" - ], - "can_export_messages": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level", - "analytics" - ], - "can_log_out_on_android": [], - "can_update_messages": [ - "national_admin", - "district_admin", - "gateway" - ], - "can_update_people": [ - "national_admin" - ], - "can_update_places": [ - "national_admin" - ], - "can_update_reports": [ - "national_admin" - ], - "can_update_users": [ - "national_admin" - ], - "can_verify_reports": [ - "national_admin", - "district_admin" - ], - "can_view_analytics": [ - "national_admin", - "analytics" - ], - "can_view_analytics_tab": [ - "national_admin" - ], - "can_view_call_action": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_view_contacts": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_view_contacts_tab": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_view_data_records": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level", - "analytics", - "gateway" - ], - "can_view_last_visited_date": [], - "can_view_message_action": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_view_messages": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_view_messages_tab": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_view_outgoing_messages": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_view_reports": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_view_reports_tab": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_view_tasks": [ - "district_admin" - ], - "can_view_tasks_tab": [ - "district_admin" - ], - "can_view_unallocated_data_records": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level", - "gateway" - ], - "can_view_users": [ - "national_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_write_wealth_quintiles": [], - "can_export_dhis": [ - "national_admin", - "crfo" - ], - "can_view_uhc_stats": [], - "can_view_tasks_group": [ - "chw" - ], - "can_aggregate_targets": [ - "chw_supervisor" - ], - "can_upgrade": [ - "program_officer" - ], - "can_view_old_filter_and_search": [], - "can_view_old_action_bar": [] - }, - "uhc": { - "contacts_default_sort": "", - "visit_count": { - "month_start_date": 1, - "visit_count_goal": 0 - } - }, - "death_reporting": { - "mark_deceased_forms": [ - "death_report" - ], - "date_field": "fields.death_details.date_of_death", - "undo_deceased_forms": [ - "undo_death_report" - ] - }, - "contact_types": [{ - "id": "district_hospital", - "name_key": "contact.type.district_hospital", - "group_key": "contact.type.district_hospital.plural", - "create_key": "contact.type.district_hospital.new", - "edit_key": "contact.type.place.edit", - "icon": "medic-district-hospital", - "create_form": "form:contact:district_hospital:create", - "edit_form": "form:contact:district_hospital:edit" - }, - { - "id": "health_center", - "name_key": "contact.type.health_center", - "group_key": "contact.type.health_center.plural", - "create_key": "contact.type.health_center.new", - "edit_key": "contact.type.place.edit", - "parents": [ - "district_hospital" - ], - "icon": "medic-health-center", - "create_form": "form:contact:health_center:create", - "edit_form": "form:contact:health_center:edit" - }, - { - "id": "clinic", - "name_key": "contact.type.clinic", - "group_key": "contact.type.clinic.plural", - "create_key": "contact.type.clinic.new", - "edit_key": "contact.type.place.edit", - "parents": [ - "health_center" - ], - "icon": "medic-clinic", - "create_form": "form:contact:clinic:create", - "edit_form": "form:contact:clinic:edit", - "count_visits": true - }, - { - "id": "person", - "name_key": "contact.type.person", - "group_key": "contact.type.person.plural", - "create_key": "contact.type.person.new", - "edit_key": "contact.type.person.edit", - "primary_contact_key": "clinic.field.contact", - "parents": [ - "district_hospital", - "health_center", - "clinic" - ], - "icon": "medic-person", - "create_form": "form:contact:person:create", - "edit_form": "form:contact:person:edit", - "person": true - } - ], - "setup_complete": true, - "contact_summary": "var ContactSummary = {}; !function(t,e){if('object'==typeof exports&&'object'==typeof module)module.exports=e();else if('function'==typeof define&&define.amd)define([],e);else{var n=e();for(var a in n)('object'==typeof exports?exports:t)[a]=n[a]}}(ContactSummary,(function(){return function(t){var e={};function n(a){if(e[a])return e[a].exports;var o=e[a]={i:a,l:!1,exports:{}};return t[a].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,a){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:a})},n.r=function(t){'undefined'!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:'Module'}),Object.defineProperty(t,'__esModule',{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&'object'==typeof t&&t&&t.__esModule)return t;var a=Object.create(null);if(n.r(a),Object.defineProperty(a,'default',{enumerable:!0,value:t}),2&e&&'string'!=typeof t)for(var o in t)n.d(a,o,function(e){return t[e]}.bind(null,o));return a},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,'a',e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p='',n(n.s=0)}([function(t,e,n){var a=n(1),o=n(2);t.exports=o(a,contact,reports)},function(t,e){const n={},a=[{appliesToType:'person',appliesIf:function(){return'chw'===contact.role},label:'female.community.health.volunteer',value:'',width:12},{appliesToType:'person',appliesIf:function(){return'chw'!==contact.role},label:'patient_id',value:contact.patient_id,width:6},{appliesToType:'person',appliesIf:function(){return'chw'!==contact.role},label:'contact.phone.number',value:contact.phone,width:6,filter:'phone'},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role},label:'contact.mhealth.phone.number',value:contact.phone,width:6,filter:'phone'},{appliesToType:'person',appliesIf:function(){return contact.date_of_birth},label:'contact.age',value:contact.date_of_birth,width:6,filter:'age'},{appliesToType:'person',appliesIf:function(){return!contact.date_of_birth&&contact.year_of_birth_approx},label:'contact.age',value:'duration.as.fchv',context:{year:(new Date).getFullYear()-contact.year_of_birth_approx},translate:!0,width:6},{appliesToType:'person',appliesIf:function(){return!contact.date_of_birth&&!contact.year_of_birth_approx},label:'contact.age',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role},label:'contact.education.level',value:'level.'+contact.level_of_education,width:6,translate:!0},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&''!==contact.start_date_as_fchv&&void 0!==contact.start_date_as_fchv},label:'contact.fchv.duration',value:'duration.as.fchv',context:{year:(new Date).getFullYear()-(contact.start_date_as_fchv-57)},translate:!0,width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&(''===contact.start_date_as_fchv||void 0===contact.start_date_as_fchv)},label:'contact.fchv.duration',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&''!==contact.training_date&&void 0!==contact.training_date},label:'contact.fchv.training',value:'duration.as.fchv',context:{year:(new Date).getFullYear()-(contact.training_date-57)},translate:!0,width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&(''===contact.training_date||void 0===contact.training_date)},label:'contact.fchv.training',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&''!==contact.fchv_id_number&&void 0!==contact.fchv_id_number},label:'contact.fchv.id',value:contact.fchv_id_number,width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&(''===contact.fchv_id_number||void 0===contact.fchv_id_number)},label:'contact.fchv.id',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&''!==contact.most_recent_fchv_training&&void 0!==contact.most_recent_fchv_training},label:'contact.fchv.refresher.training',value:contact.most_recent_fchv_training,filter:'relativeDay',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&(''===contact.most_recent_fchv_training||void 0===contact.most_recent_fchv_training)},label:'contact.fchv.refresher.training',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return''!==contact.phone_alternate&&void 0!==contact.phone_alternate},label:'contact.alternate.phone',value:contact.phone_alternate,width:6},{appliesToType:'person',appliesIf:function(){return''===contact.phone_alternate||void 0===contact.phone_alternate},label:'contact.alternate.phone',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return''!==contact.notes&&void 0!==contact.notes},label:'contact.notes',value:contact.notes,width:6},{appliesToType:'person',appliesIf:function(){return''===contact.notes||void 0===contact.notes},label:'contact.notes',value:'-',width:6},{appliesToType:'person',label:'contact.parent',value:lineage,filter:'lineage'},{appliesToType:'!person',label:'contact.notes',value:contact.notes,width:12},{appliesToType:'!person',appliesIf:function(){return contact.parent&&lineage[0]},label:'contact.parent',value:lineage,filter:'lineage'}];lineage[0]&&lineage[0].contact&&(n.chw_name=lineage[0].contact.name,n.chw_phone=lineage[0].contact.phone),t.exports={context:n,cards:[],fields:a}},function(t,e){function n(t){return t?Array.isArray(t)?t:[t]:[]}function a(t,e){switch(typeof t){case'undefined':return!0;case'function':return t(e);default:return t}}function o(t,e,n){if(a(t.appliesIf,n)){var o='function'==typeof t.fields?t.fields(n):t.fields.filter((function(t){return a(t.appliesIf,n)})).map((function(t){var e={};return c(t,e,'label'),c(t,e,'value'),c(t,e,'translate'),c(t,e,'filter'),c(t,e,'width'),c(t,e,'icon'),t.context&&(e.context={},c(t.context,e.context,'count'),c(t.context,e.context,'total')),e}));return t.modifyContext&&t.modifyContext(e,n),{label:t.label,fields:o}}function c(t,e,a){switch(typeof t[a]){case'undefined':return;case'function':e[a]=t[a](n);break;default:e[a]=t[a]}}}t.exports=function(t,e,a){var c=t.fields||[],r=t.context||{},i=t.cards||[],l=e&&('contact'===e.type?e.contact_type:e.type),p={cards:[],fields:c.filter((function(t){var e=n(t.appliesToType),a=e.filter((function(t){return t&&'!'===t.charAt(0)}));if((0===e.length||e.includes(l)||a.length>0&&!a.includes('!'+l))&&(!t.appliesIf||t.appliesIf()))return delete t.appliesToType,delete t.appliesIf,!0}))};return i.forEach((function(t){var e,c,i,f,u=n(t.appliesToType);if(u.includes('report')&&u.length>1)throw new Error('You cannot set appliesToType to an array which includes the type \\'report\\' and another type.');if(u.includes('report'))for(e=0;e0)return;(i=o(t,r))&&p.cards.push(i)}})),p.context=r,p}}])})); return ContactSummary;", - "tasks": { - "rules": "define Target { _id: null, contact: null, deleted: null, type: null, pass: null, date: null, groupBy: null }\ndefine Contact { contact: null, reports: null, tasks: null }\ndefine Task { _id: null, deleted: null, doc: null, contact: null, icon: null, date: null, readyStart: null, readyEnd: null, title: null, fields: null, resolved: null, priority: null, priorityLabel: null, reports: null, actions: null }\nrule GenerateEvents {\n when { c: Contact } then { !function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){'undefined'!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:'Module'}),Object.defineProperty(e,'__esModule',{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&'object'==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,'default',{enumerable:!0,value:e}),2&t&&'string'!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,'a',t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p='',n(n.s=1)}([function(e,t){e.exports=function(e){var t={};!function e(t,n){var r=Object.keys(t);for(var o in r){var a=r[o];switch(typeof t[a]){case'object':e(t[a],n);break;case'function':t[a]=t[a].bind(n)}}}(e,t),t.definition=function e(t){var n=Object.assign({},t),r=Object.keys(n);for(var o in r){var a=r[o];if(Array.isArray(n[a])){n[a]=n[a].slice(0);for(var i=0;i <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)>", - "locale": "ne" - }] - }, - { - "property": "last_menstrual_period", - "rule": "(integer && min(6) && max(42))", - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_name}} {{patient_id}} को दर्ता मिलेन। अन्तिम महिनावारी भएको समय ६ देखि ४२ हप्ता भित्र भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! अ {{patient_id}} <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)>", - "locale": "ne" - }] - } - ] - }, - "messages": [{ - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}। {{patient_name}} {{patient_id}} को गर्भवस्था दर्ता गरेकोमा धन्यबाद! उहाँको प्रसुति अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}}", - "locale": "ne" - }, - { - "content": "नमस्ते {{contact.name}}! {{patient_name}} {{patient_id}} को गर्भवस्था दर्ता गरेकोमा धन्यबाद! उहाँको प्रसुति अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}}", - "locale": "ne" - } - ], - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [{ - "content": "नमस्ते! {{contact.name}} ले {{patient_name}} को गर्भवस्था दर्ता गर्नु भएको छ। उहाँको दर्ता नं {{patient_id}} र प्रसुती अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}} हो। धन्यबाद!", - "locale": "ne" - }], - "event_type": "report_accepted", - "recipient": "parent" - }, - { - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} कसैलाई पनि दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! अ <दर्ता नं (५ देखि १३ अंकको)> <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)> ", - "locale": "ne" - }], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "ल", - "events": [{ - "name": "on_create", - "trigger": "add_expected_date", - "params": "lmp_date", - "bool_expr": "doc.lmp_date" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "ANC Reminders LMP", - "bool_expr": "doc.lmp_date" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "Delivery Reminders", - "bool_expr": "doc.lmp_date" - }, - { - "name": "on_create", - "trigger": "clear_schedule", - "params": "Registration Reminders", - "bool_expr": "doc.lmp_date" - } - ], - "validations": { - "join_responses": false, - "list": [{ - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "registration.lmp_date.patient_id.incorrect" - }, - { - "property": "lmp_year", - "rule": "(integer && min(2078) && max(2090))", - "translation_key": "registration.lmp_date.year.incorrect" - }, - { - "property": "lmp_month", - "rule": "(integer && min(1) && max(12))", - "translation_key": "registration.lmp_date.month.incorrect" - }, - { - "property": "lmp_day", - "rule": "(integer && min(1) && max(32))", - "translation_key": "registration.lmp_date.day.incorrect" - }, - { - "property": "lmp_date", - "rule": "isBefore('1 month')", - "translation_key": "messages.registration.lmp_date.incorrect.before" - }, - { - "property": "lmp_date", - "rule": "isAfter('-9 months')", - "translation_key": "messages.registration.lmp_date.incorrect.after" - } - ] - }, - "messages": [{ - "translation_key": "registration.lmp_date.ok", - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "registration.lmp_date.ok.health_center", - "event_type": "report_accepted", - "recipient": "health_center" - }, - { - "translation_key": "registration.lmp_date.patient_id.not_found", - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "ज", - "events": [{ - "name": "on_create", - "trigger": "add_birth_date", - "params": "birth_date", - "bool_expr": "doc.days_since_birth" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "PNC Reminders", - "bool_expr": "doc.days_since_birth" - }, - { - "name": "on_create", - "trigger": "clear_schedule", - "params": "Delivery Reminders, ANC Reminders LMP", - "bool_expr": "doc.patient_id" - } - ], - "validations": { - "join_responses": false, - "list": [{ - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! ज <डेलिभरी कोड><दर्ता नं (५ देखि १३ अंकको)> <डेलिभरी भएको दिन>", - "locale": "ne" - }] - }, - { - "property": "delivery_code", - "rule": "regex('^(स|श|ष|सद|शद|षद|घद|घ)$')", - "translation_key": "", - "message": [{ - "content": "{{#delivery_code}} नमस्ते {{contact.name}}! {{delivery_code}} डेलिभरी कोड मिलेन। स्वास्थ्य सँस्थामा भएको जन्मको लागि ‘स’ र घरमा भएको जन्मको लागि ‘घ’ प्रयोग गर्नुहोला। धन्यवाद। {{/delivery_code}}", - "locale": "ne" - }] - }, - { - "property": "days_since_birth", - "rule": "(integer && min(0) && max(50))", - "translation_key": "", - "message": [{ - "content": "{{#days_since_birth}}नमस्ते {{contact.name}}! प्रसुति पश्चातको अवधि मिलेन, सो अवधि ० देखि ५० दिन भित्र हुनुपर्दछ। तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! ज <डेलिभरी कोड> {{patient_id}} <डेलिभरी भएको दिन>{{/days_since_birth}}{{^days_since_birth}}नमस्ते {{contact.name}}! प्रसुति पश्चातको अवधि ० देखि ५० दिन भित्र हुनुपर्दछ। तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! ज <डेलिभरी कोड> {{patient_id}} <डेलिभरी भएको दिन>{{/days_since_birth}}", - "locale": "ne" - }] - } - ] - }, - "messages": [{ - "translation_key": "", - "message": [{ - "content": "नमस्ते! {{patient_name}} ({{patient_id}}) का लागि प्रसुति प्रतिवेदन प्राप्त भयो। १,३,७-१४ र ४२ दिनमा हुनु पर्ने सुत्केरी जाँचभेट एकिन गरी SMS बाट प {{patient_id}} <जाँचको पटक> पठाउनुहोला धन्यबाद!", - "locale": "ne" - }], - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [{ - "content": "नमस्ते! {{contact.name}} बाट {{patient_name}} ({{patient_id}}) को प्रसुति प्रतिवेदन प्राप्त भयो। १,३,७-१४ र ४२ दिनमा हुनु पर्ने सुत्केरी जाँचभेट गर्न घर भेट गर्नु होला, धन्यबाद!", - "locale": "ne" - }], - "recipient": "parent" - }, - { - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! ज <डेलिभरी कोड><दर्ता नं (५ देखि १३ अंकको)> <डेलिभरी भएको दिन>", - "locale": "ne" - }], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "क", - "events": [{ - "name": "on_create", - "trigger": "add_patient_id", - "params": "", - "bool_expr": "doc.patient_name" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "Registration Reminders", - "bool_expr": "doc.patient_name" - } - ], - "validations": { - "join_responses": false, - "list": [{ - "property": "patient_age", - "rule": "(integer && min(0) && max(125))", - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! दर्ताको ढाँचा मिलेन। कृपया तलको ढाँचाअनुसार पुनः पठाउनुहोस्: क <उमेर (बर्षमा)> <नाम (बढीमा ३० अक्षर सम्मको)>", - "locale": "ne" - }] - }, - { - "property": "patient_name", - "rule": "(string && lenMin(1) && lenMax(30))", - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! दर्ताको ढाँचा मिलेन। कृपया तलको ढाँचाअनुसार पुनः पठाउनुहोस्: क <उमेर (बर्षमा)> <नाम (बढीमा ३० अक्षर सम्मको)>", - "locale": "ne" - }] - } - ] - }, - "messages": [{ - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}, {{patient_name}} ({{patient_id}}) लाई दर्ता गर्नुभएकोमा धन्यवाद! ", - "locale": "" - }], - "recipient": "" - }] - }, - { - "form": "म", - "events": [{ - "name": "on_create", - "trigger": "clear_schedule", - "params": "Registration Reminders, ANC Reminders LMP, PNC Reminders, Delivery Reminders", - "bool_expr": "doc.patient_id" - }], - "validations": { - "join_responses": false, - "list": [{ - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [{ - "content": "{{#patient_id}}नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! म <दर्ता नं (५ देखि १३ अंकको)>{{/patient_id}}{{^patient_id}}नमस्ते {{contact.name}}! तपाईले पठाउनु भएको रिपोर्टको ढाँचा मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! म <दर्ता नं (५ देखि १३ अंकको)>{{/patient_id}}", - "locale": "ne" - }] - }] - }, - "messages": [{ - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}, {{patient_name}} {{patient_id}} को मृत्युको दु:खद घटनाको सूचना दिनुभएकोमा धन्यवाद। ", - "locale": "ne" - }], - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [{ - "content": "{{contact.name}} ले {{patient_age}} बर्षिया {{patient_name}} ({{patient_id}}) को मृत्यु भएको सूचना दिनुभएको छ। ", - "locale": "ne" - }], - "recipient": "parent" - }, - { - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} कसैलाई पनि दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! म <दर्ता नं (५ देखि १३ अंकको)", - "locale": "ne" - }], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "A", - "events": [{ - "name": "on_create", - "trigger": "add_expected_date", - "params": "lmp_date", - "bool_expr": "doc.last_menstrual_period" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "ANC Reminders LMP", - "bool_expr": "doc.last_menstrual_period" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "Delivery Reminders", - "bool_expr": "doc.last_menstrual_period" - }, - { - "name": "on_create", - "trigger": "clear_schedule", - "params": "Registration Reminders", - "bool_expr": "doc.last_menstrual_period" - } - ], - "validations": { - "join_responses": false, - "list": [{ - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! A <दर्ता नं (५ देखि १३ अंकको)> <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)>", - "locale": "ne" - }] - }, - { - "property": "last_menstrual_period", - "rule": "(integer && min(6) && max(42))", - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_name}} {{patient_id}} को दर्ता मिलेन। अन्तिम महिनावारी भएको समय ६ देखि ४२ हप्ता भित्र भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! A {{patient_id}} <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)>", - "locale": "ne" - }] - } - ] - }, - "messages": [{ - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}। {{patient_name}} {{patient_id}} को गर्भवस्था दर्ता गरेकोमा धन्यबाद! उहाँको प्रसुति अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}}", - "locale": "ne" - }], - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [{ - "content": "नमस्ते! {{contact.name}} ले {{patient_name}} को गर्भवस्था दर्ता गर्नु भएको छ। उहाँको दर्ता नं {{patient_id}} र प्रसुती अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}} हो। धन्यबाद!", - "locale": "ne" - }], - "event_type": "report_accepted", - "recipient": "parent" - }, - { - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} कसैलाई पनि दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! A <दर्ता नं (५ देखि १३ अंकको)> <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)> ", - "locale": "ne" - }], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "L", - "events": [{ - "name": "on_create", - "trigger": "add_expected_date", - "params": "lmp_date", - "bool_expr": "doc.lmp_date" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "ANC Reminders LMP", - "bool_expr": "doc.lmp_date" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "Delivery Reminders", - "bool_expr": "doc.lmp_date" - }, - { - "name": "on_create", - "trigger": "clear_schedule", - "params": "Registration Reminders", - "bool_expr": "doc.lmp_date" - } - ], - "validations": { - "join_responses": false, - "list": [{ - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "registration.lmp_date.patient_id.incorrect" - }, - { - "property": "lmp_year", - "rule": "(integer && min(2078) && max(2090))", - "translation_key": "registration.lmp_date.year.incorrect" - }, - { - "property": "lmp_month", - "rule": "(integer && min(1) && max(12))", - "translation_key": "registration.lmp_date.month.incorrect" - }, - { - "property": "lmp_day", - "rule": "(integer && min(1) && max(32))", - "translation_key": "registration.lmp_date.day.incorrect" - }, - { - "property": "lmp_date", - "rule": "isBefore('1 month')", - "translation_key": "messages.registration.lmp_date.incorrect.before" - }, - { - "property": "lmp_date", - "rule": "isAfter('-9 months')", - "translation_key": "messages.registration.lmp_date.incorrect.after" - } - ] - }, - "messages": [{ - "translation_key": "registration.lmp_date.ok", - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "registration.lmp_date.ok.health_center", - "event_type": "report_accepted", - "recipient": "health_center" - }, - { - "translation_key": "registration.lmp_date.patient_id.not_found", - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "J", - "events": [{ - "name": "on_create", - "trigger": "add_birth_date", - "params": "birth_date", - "bool_expr": "doc.days_since_birth" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "PNC Reminders", - "bool_expr": "doc.days_since_birth" - }, - { - "name": "on_create", - "trigger": "clear_schedule", - "params": "Delivery Reminders, ANC Reminders LMP", - "bool_expr": "doc.patient_id" - } - ], - "validations": { - "join_responses": false, - "list": [{ - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! J <डेलिभरी कोड><दर्ता नं (५ देखि १३ अंकको)> <डेलिभरी भएको दिन>", - "locale": "ne" - }] - }, - { - "property": "delivery_code", - "rule": "regex('^(S|A|s|a)$')", - "translation_key": "", - "message": [{ - "content": "{{#delivery_code}} नमस्ते {{contact.name}}! {{delivery_code}} डेलिभरी कोड मिलेन। स्वास्थ्य सँस्थामा भएको जन्मको लागि ‘S’ र घरमा भएको जन्मको लागि ‘A’ प्रयोग गर्नुहोला। धन्यवाद। {{/delivery_code}}", - "locale": "ne" - }] - }, - { - "property": "days_since_birth", - "rule": "(integer && min(0) && max(50))", - "translation_key": "", - "message": [{ - "content": "{{#days_since_birth}}नमस्ते {{contact.name}}! प्रसुति पश्चातको अवधि मिलेन, सो अवधि ० देखि ५० दिन भित्र हुनुपर्दछ। तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! J <डेलिभरी कोड> {{patient_id}} <डेलिभरी भएको दिन>{{/days_since_birth}}{{^days_since_birth}}नमस्ते {{contact.name}}! प्रसुति पश्चातको अवधि ० देखि ५० दिन भित्र हुनुपर्दछ। तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! J <डेलिभरी कोड> {{patient_id}} <डेलिभरी भएको दिन>{{/days_since_birth}}", - "locale": "ne" - }] - } - ] - }, - "messages": [{ - "translation_key": "", - "message": [{ - "content": "नमस्ते! {{patient_name}} ({{patient_id}}) का लागि प्रसुति प्रतिवेदन प्राप्त भयो। १,३,७-१४ र ४२ दिनमा हुनु पर्ने सुत्केरी जाँचभेट एकिन गरी SMS बाट P {{patient_id}} <जाँचको पटक> पठाउनुहोला धन्यबाद!", - "locale": "ne" - }], - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [{ - "content": "नमस्ते! {{contact.name}} बाट {{patient_name}} ({{patient_id}}) को प्रसुति प्रतिवेदन प्राप्त भयो। १,३,७-१४ र ४२ दिनमा हुनु पर्ने सुत्केरी जाँचभेट गर्न घर भेट गर्नु होला, धन्यबाद!", - "locale": "ne" - }], - "recipient": "parent" - }, - { - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! J <डेलिभरी कोड><दर्ता नं (५ देखि १३ अंकको)> <डेलिभरी भएको दिन>", - "locale": "ne" - }], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "K", - "events": [{ - "name": "on_create", - "trigger": "add_patient_id", - "params": "", - "bool_expr": "doc.patient_name" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "Registration Reminders", - "bool_expr": "doc.patient_name" - } - ], - "validations": { - "join_responses": false, - "list": [{ - "property": "patient_age", - "rule": "(integer && min(0) && max(125))", - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! दर्ताको ढाँचा मिलेन। कृपया तलको ढाँचाअनुसार पुनः पठाउनुहोस्: K <उमेर (बर्षमा)> <नाम (बढीमा ३० अक्षर सम्मको)>", - "locale": "ne" - }] - }, - { - "property": "patient_name", - "rule": "(string && lenMin(1) && lenMax(30))", - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! दर्ताको ढाँचा मिलेन। कृपया तलको ढाँचाअनुसार पुनः पठाउनुहोस्: K <उमेर (बर्षमा)> <नाम (बढीमा ३० अक्षर सम्मको)>", - "locale": "ne" - }] - } - ] - }, - "messages": [{ - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}, {{patient_name}} ({{patient_id}}) लाई दर्ता गर्नुभएकोमा धन्यवाद! ", - "locale": "" - }], - "recipient": "" - }] - }, - { - "form": "M", - "events": [{ - "name": "on_create", - "trigger": "clear_schedule", - "params": "Registration Reminders, ANC Reminders LMP, PNC Reminders, Delivery Reminders", - "bool_expr": "doc.patient_id" - }], - "validations": { - "join_responses": false, - "list": [{ - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [{ - "content": "{{#patient_id}}नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! M <दर्ता नं (५ देखि १३ अंकको)>{{/patient_id}}{{^patient_id}}नमस्ते {{contact.name}}! तपाईले पठाउनु भएको रिपोर्टको ढाँचा मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! M <दर्ता नं (५ देखि १३ अंकको)>{{/patient_id}}", - "locale": "ne" - }] - }] - }, - "messages": [{ - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}, {{patient_name}} {{patient_id}} को मृत्युको दु:खद घटनाको सूचना दिनुभएकोमा धन्यवाद। ", - "locale": "ne" - }], - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [{ - "content": "{{contact.name}} ले {{patient_age}} बर्षिया {{patient_name}} ({{patient_id}}) को मृत्यु भएको सूचना दिनुभएको छ। ", - "locale": "ne" - }], - "recipient": "parent" - }, - { - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} कसैलाई पनि दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! म <दर्ता नं (५ देखि १३ अंकको)", - "locale": "ne" - }], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - } - ], - "patient_reports": [{ - "form": "ग", - "name": "Visits", - "format": "ग ", - "silence_type": "ANC Reminders LMP", - "silence_for": "0 days", - "fields": [{ - "field_name": "", - "title": "" - }], - "validations": { - "join_responses": false, - "list": [{ - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! ग <दर्ता नं (५ देखि १३ अंकको)>", - "locale": "ne" - }] - }] - }, - "messages": [{ - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_name}} ({{patient_id}}) लाई भेटेकोमा धन्यबाद!", - "locale": "ne" - }], - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन।दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! ग <दर्ता नं (५ देखि १३ अंकको)>", - "locale": "ne" - }], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "ख", - "name": "Flag", - "format": "ख ", - "silence_type": "", - "silence_for": "", - "fields": [{ - "field_name": "patient_id", - "title": "Patient ID" - }], - "validations": { - "join_responses": false, - "list": [{ - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [{ - "content": "{{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! ख <दर्ता नं (५ देखि १३ अंकको)>", - "locale": "ne" - }] - }] - }, - "messages": [{ - "translation_key": "", - "message": [{ - "content": "नमस्ते! {{patient_name}} {{patient_id}}का लागि खतराको चिन्ह पहिचान भएको रिपोर्ट प्राप्त भयो। उहाँलाई बिशेष निगरानीमा राखी आवश्यकता अनुसार स्वास्थ्य संस्था पठाउनुहोला।", - "locale": "ne" - }], - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [{ - "content": "नमस्ते! {{contact.name}}ले {{patient_name}} {{patient_id}} का लागि खतराको चिन्ह पहिचान गर्नुभएको छ । म.स्वा.स्व.से.संग सोधखोज गरि आवश्यक सल्लाह दिनुहोला |", - "locale": "ne" - }], - "event_type": "report_accepted", - "recipient": "parent" - }, - { - "translation_key": "", - "message": [{ - "content": "{{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी पुन: फारम पठाउनुहोला, धन्यबाद!", - "locale": "ne" - }], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "प", - "name": "PNC Visit", - "format": "प ", - "silence_type": "PNC Reminders", - "silence_for": "0 days", - "fields": [{ - "field_name": "patient_id", - "title": "Patient ID" - }, - { - "field_name": "pnc_visits", - "title": "No. of PNC visits" - } - ], - "validations": { - "join_responses": false, - "list": [{ - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} सन्देशको ढांचा मिलेन! ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! प <दर्ता नं (५ देखि १३ अंकको)> <हालसम्मको जम्मा सुत्केरी जाँच भेट संख्या>", - "locale": "ne" - }] - }, - { - "property": "pnc_visits", - "rule": "(integer && min(0) && max(4))", - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} को सुत्केरी जाँच भेट संख्या मिलेन। ० देखि ४ बीचको संख्या हुनु पर्दछ। मिलाएर पठाउनु होला, धन्यवाद।", - "locale": "ne" - }] - } - ] - }, - "messages": [{ - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} को सुत्केरी जाँच प्रतिवेदन पठाउनु भएकोमा धन्यवाद।", - "locale": "ne" - }], - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! सन्देशको ढांचा मिलेन! ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! प <दर्ता नं (५ देखि १३ अंकको)> <हालसम्मको जम्मा सुत्केरी जाँच भेट संख्या>", - "locale": "ne" - }], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "G", - "name": "Visits", - "format": "G ", - "silence_type": "ANC Reminders LMP", - "silence_for": "0 days", - "fields": [{ - "field_name": "", - "title": "" - }], - "validations": { - "join_responses": false, - "list": [{ - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! G <दर्ता नं (५ देखि १३ अंकको)>", - "locale": "ne" - }] - }] - }, - "messages": [{ - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_name}} ({{patient_id}}) लाई भेटेकोमा धन्यबाद!", - "locale": "ne" - }], - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन।दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! G <दर्ता नं (५ देखि १३ अंकको)>", - "locale": "ne" - }], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "D", - "name": "Flag", - "format": "D ", - "silence_type": "", - "silence_for": "", - "fields": [{ - "field_name": "patient_id", - "title": "Patient ID" - }], - "validations": { - "join_responses": false, - "list": [{ - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [{ - "content": "{{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! D <दर्ता नं (५ देखि १३ अंकको)>", - "locale": "ne" - }] - }] - }, - "messages": [{ - "translation_key": "", - "message": [{ - "content": "नमस्ते! {{patient_name}} {{patient_id}}का लागि खतराको चिन्ह पहिचान भएको रिपोर्ट प्राप्त भयो। उहाँलाई बिशेष निगरानीमा राखी आवश्यकता अनुसार स्वास्थ्य संस्था पठाउनुहोला।", - "locale": "ne" - }], - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [{ - "content": "नमस्ते! {{contact.name}}ले {{patient_name}} {{patient_id}} का लागि खतराको चिन्ह पहिचान गर्नुभएको छ । म.स्वा.स्व.से.संग सोधखोज गरि आवश्यक सल्लाह दिनुहोला |", - "locale": "ne" - }], - "event_type": "report_accepted", - "recipient": "parent" - }, - { - "translation_key": "", - "message": [{ - "content": "{{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी पुन: फारम पठाउनुहोला, धन्यबाद!", - "locale": "ne" - }], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "P", - "name": "PNC Visit", - "format": "P ", - "silence_type": "PNC Reminders", - "silence_for": "0 days", - "fields": [{ - "field_name": "patient_id", - "title": "Patient ID" - }, - { - "field_name": "pnc_visits", - "title": "No. of PNC visits" - } - ], - "validations": { - "join_responses": false, - "list": [{ - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} सन्देशको ढांचा मिलेन! ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! P <दर्ता नं (५ देखि १३ अंकको)> <हालसम्मको जम्मा सुत्केरी जाँच भेट संख्या>", - "locale": "ne" - }] - }, - { - "property": "pnc_visits", - "rule": "(integer && min(0) && max(4))", - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} को सुत्केरी जाँच भेट संख्या मिलेन। ० देखि ४ बीचको संख्या हुनु पर्दछ। मिलाएर पठाउनु होला, धन्यवाद।", - "locale": "ne" - }] - } - ] - }, - "messages": [{ - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} को सुत्केरी जाँच प्रतिवेदन पठाउनु भएकोमा धन्यवाद।", - "locale": "ne" - }], - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! सन्देशको ढांचा मिलेन! ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! P <दर्ता नं (५ देखि १३ अंकको)> <हालसम्मको जम्मा सुत्केरी जाँच भेट संख्या>", - "locale": "ne" - }], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - } - ], - "muting": { - "mute_forms": [ - "ब", - "B" - ], - "unmute_forms": [ - "स", - "F" - ], - "validations": { - "join_responses": false, - "list": [{ - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! सूचना बन्द गरेको भए : ब वा B <दर्ता नं (५ देखि १३ अंकको)>, सूचना सुरु गरेको भए: स वा F <दर्ता नं (५ देखि १३ अंकको)>", - "locale": "ne" - }] - }] - }, - "messages": [{ - "translation_key": "", - "message": [{ - "content": "धन्यवाद, {{contact.name}}! {{patient_name}} {{patient_id}} का लागि सबै सुचना बन्द गरिएको छ। सुचारु गर्नका लागि स वा F {{patient_id}} लेखि SMS पठाउनुहोला।", - "locale": "ne" - }], - "event_type": "mute", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [{ - "content": "{{contact.name}} ले {{patient_name}} ({{patient_id}}) का लागि सबै सुचना बन्द गराउनु भएको छ।", - "locale": "ne" - }], - "event_type": "mute", - "recipient": "parent" - }, - { - "translation_key": "", - "message": [{ - "content": "धन्यवाद, {{contact.name}}! {{patient_name}} ({{patient_id}}) का लागि सुचना सुचारु गरिएको छ।", - "locale": "ne" - }], - "event_type": "unmute", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [{ - "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी पुन: फारम पठाउनुहोला, धन्यबाद!", - "locale": "ne" - }], - "event_type": "contact_not_found", - "recipient": "clinic" - } - ] - }, - "_version": "3.17.1", - "outgoing_deny_list": "1414, 9771414, +9771414, 1408, 9771408, +9771408, 1415, 9771415, +9771415, 1455, 9771455, +9771455, 9851028801, 9779851028801, +9779851028801, 1477, +9771477, NT, 34455, +97734455, 1422, +9771422", - "outgoing_deny_shorter_than": 13, - "outgoing_deny_with_alphas": true, - "multipart_sms_limit": 10, - "sms": { - "outgoing_service": "medic-gateway" - }, - "forms": { - "NP": { - "meta": { - "code": "NP", - "translation_key": "forms.np.title", - "label": { - "en": "New Person with phone_number Registration" - } - }, - "fields": { - "patient_age": { - "labels": { - "tiny": { - "en": "Person Age" - }, - "short": { - "en": "Person Age" - } - }, - "position": 0, - "type": "integer", - "length": [ - 1, - 3 - ], - "range": [ - 0, - 125 - ], - "required": true - }, - "patient_name": { - "labels": { - "tiny": { - "en": "patient_name" - }, - "description": { - "en": "Patient name" - }, - "short": { - "en": "Patient name" - } - }, - "position": 1, - "type": "string", - "length": [ - 3, - 30 - ], - "required": true - }, - "phone_number": { - "labels": { - "tiny": { - "en": "phone number" - }, - "description": { - "en": "phone number" - }, - "short": { - "en": "phone number" - } - }, - "position": 2, - "type": "phone_number", - "length": [ - 9, - 10 - ], - "required": true - } - }, - "public_form": false, - "use_sentinel": true - }, - "क": { - "meta": { - "code": "क", - "translation_key": "forms.ka.title" - }, - "fields": { - "patient_age": { - "labels": { - "tiny": { - "en": "Person Age" - }, - "short": { - "en": "Person Age" - } - }, - "position": 0, - "type": "integer", - "length": [ - 1, - 3 - ], - "range": [ - 0, - 125 - ], - "required": true - }, - "patient_name": { - "labels": { - "tiny": { - "en": "patient_name" - }, - "short": { - "en": "Patient Name" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 40 - ], - "required": true - } - }, - "public_form": false, - "use_sentinel": true - }, - "अ": { - "meta": { - "code": "अ", - "translation_key": "forms.a.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "patient_id" - }, - "short": { - "en": "Patient Id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "last_menstrual_period": { - "labels": { - "tiny": { - "en": "LMP" - }, - "short": { - "en": "Weeks since LMP" - } - }, - "position": 1, - "type": "integer", - "length": [ - 1, - 2 - ], - "range": [ - 6, - 42 - ], - "required": true - } - }, - "public_form": false, - "use_sentinel": true - }, - "ग": { - "meta": { - "code": "ग", - "translation_key": "forms.ga.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "id" - }, - "short": { - "en": "ID" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 20 - ], - "required": false - } - }, - "use_sentinel": true - }, - "ज": { - "meta": { - "code": "ज", - "translation_key": "forms.ja.title" - }, - "fields": { - "delivery_code": { - "labels": { - "short": { - "en": "Delivery Code" - } - }, - "position": 0, - "type": "string", - "length": [ - 1, - 2 - ], - "required": true - }, - "patient_id": { - "labels": { - "short": { - "en": "ID" - } - }, - "position": 1, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "days_since_birth": { - "labels": { - "short": { - "en": "Days Since Birth" - }, - "tiny": { - "en": "DSB" - } - }, - "position": 2, - "type": "integer", - "length": [ - 1, - 2 - ], - "range": [ - 1, - 50 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 3, - "type": "string", - "length": [ - 1, - 20 - ], - "required": false - } - }, - "use_sentinel": true - }, - "ख": { - "meta": { - "code": "ख", - "translation_key": "forms.kha.title" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "en": "ID" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "use_sentinel": true - }, - "ब": { - "meta": { - "code": "ब", - "translation_key": "forms.ba.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "ID" - }, - "short": { - "en": "ID" - } - }, - "position": 0, - "length": [ - 5, - 13 - ], - "type": "string", - "required": true - }, - "notes": { - "labels": { - "tiny": { - "en": "r", - "sw": "r" - }, - "short": { - "en": "Reason" - } - }, - "position": 1, - "length": [ - 3, - 100 - ], - "type": "string" - } - }, - "use_sentinel": true - }, - "स": { - "meta": { - "code": "स", - "translation_key": "forms.sa.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "ID" - }, - "short": { - "en": "ID" - } - }, - "position": 0, - "length": [ - 5, - 13 - ], - "type": "string" - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "use_sentinel": true - }, - "प": { - "meta": { - "code": "प", - "translation_key": "forms.pa.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "ID" - }, - "short": { - "en": "ID" - } - }, - "position": 0, - "length": [ - 5, - 13 - ], - "type": "string", - "required": true - }, - "pnc_visits": { - "labels": { - "tiny": { - "en": "PNCV" - }, - "short": { - "en": "PNC Visits" - } - }, - "position": 1, - "length": [ - 1, - 1 - ], - "range": [ - 0, - 4 - ], - "type": "string", - "required": true - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 2, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "use_sentinel": true - }, - "म": { - "meta": { - "code": "म", - "translation_key": "forms.ma.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "id" - }, - "short": { - "en": "Patient Id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "death_notes": { - "labels": { - "tiny": { - "en": "note" - }, - "short": { - "en": "Notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 20 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "ल": { - "meta": { - "code": "ल", - "translation_key": "forms.la.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "patient_id" - }, - "short": { - "en": "Patient Id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "lmp_year": { - "labels": { - "tiny": { - "en": "LMP_Y" - }, - "short": { - "en": "LMP Year" - } - }, - "position": 1, - "type": "bsYear", - "required": true - }, - "lmp_month": { - "labels": { - "tiny": { - "en": "LMP_M" - }, - "short": { - "en": "LMP Month" - } - }, - "position": 2, - "type": "bsMonth", - "required": true - }, - "lmp_day": { - "labels": { - "tiny": { - "en": "LMP_D" - }, - "short": { - "en": "LMP Day" - } - }, - "position": 3, - "type": "bsDay", - "required": true - }, - "lmp_date": { - "labels": { - "tiny": { - "en": "LMP_DATE" - }, - "short": { - "en": "LMP Date" - } - }, - "position": 4, - "type": "bsAggreDate", - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "K": { - "meta": { - "code": "K", - "translation_key": "forms.k.title" - }, - "fields": { - "patient_age": { - "labels": { - "tiny": { - "en": "Person Age" - }, - "short": { - "en": "Person Age" - } - }, - "position": 0, - "type": "integer", - "length": [ - 1, - 3 - ], - "range": [ - 0, - 125 - ], - "required": true - }, - "patient_name": { - "labels": { - "tiny": { - "en": "patient_name" - }, - "short": { - "en": "Patient Name" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 40 - ], - "required": true - } - }, - "public_form": false, - "use_sentinel": true - }, - "A": { - "meta": { - "code": "A", - "translation_key": "forms.a_en.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "patient_id" - }, - "short": { - "en": "Patient Id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "last_menstrual_period": { - "labels": { - "tiny": { - "en": "LMP" - }, - "short": { - "en": "Weeks since LMP" - } - }, - "position": 1, - "type": "integer", - "length": [ - 1, - 2 - ], - "range": [ - 6, - 42 - ], - "required": true - } - }, - "public_form": false, - "use_sentinel": true - }, - "L": { - "meta": { - "code": "L", - "translation_key": "forms.l.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "patient_id" - }, - "short": { - "en": "Patient Id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "lmp_year": { - "labels": { - "tiny": { - "en": "LMP_Y" - }, - "short": { - "en": "LMP Year" - } - }, - "position": 1, - "type": "bsYear", - "required": true - }, - "lmp_month": { - "labels": { - "tiny": { - "en": "LMP_M" - }, - "short": { - "en": "LMP Month" - } - }, - "position": 2, - "type": "bsMonth", - "required": true - }, - "lmp_day": { - "labels": { - "tiny": { - "en": "LMP_D" - }, - "short": { - "en": "LMP Day" - } - }, - "position": 3, - "type": "bsDay", - "required": true - }, - "lmp_date": { - "labels": { - "tiny": { - "en": "LMP_DATE" - }, - "short": { - "en": "LMP Date" - } - }, - "position": 4, - "type": "bsAggreDate", - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "G": { - "meta": { - "code": "G", - "translation_key": "forms.g.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "id" - }, - "short": { - "en": "ID" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 20 - ], - "required": false - } - }, - "use_sentinel": true - }, - "J": { - "meta": { - "code": "J", - "translation_key": "forms.j.title" - }, - "fields": { - "delivery_code": { - "labels": { - "short": { - "en": "Delivery Code" - } - }, - "position": 0, - "type": "string", - "length": [ - 1, - 2 - ], - "required": true - }, - "patient_id": { - "labels": { - "short": { - "en": "ID" - } - }, - "position": 1, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "days_since_birth": { - "labels": { - "short": { - "en": "Days Since Birth" - }, - "tiny": { - "en": "DSB" - } - }, - "position": 2, - "type": "integer", - "length": [ - 1, - 2 - ], - "range": [ - 1, - 50 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 3, - "type": "string", - "length": [ - 1, - 20 - ], - "required": false - } - }, - "use_sentinel": true - }, - "D": { - "meta": { - "code": "D", - "translation_key": "forms.d.title" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "en": "ID" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "use_sentinel": true - }, - "B": { - "meta": { - "code": "B", - "translation_key": "forms.b.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "ID" - }, - "short": { - "en": "ID" - } - }, - "position": 0, - "length": [ - 5, - 13 - ], - "type": "string", - "required": true - }, - "notes": { - "labels": { - "tiny": { - "en": "r", - "sw": "r" - }, - "short": { - "en": "Reason" - } - }, - "position": 1, - "length": [ - 3, - 100 - ], - "type": "string" - } - }, - "use_sentinel": true - }, - "F": { - "meta": { - "code": "F", - "translation_key": "forms.f.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "ID" - }, - "short": { - "en": "ID" - } - }, - "position": 0, - "length": [ - 5, - 13 - ], - "type": "string" - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "use_sentinel": true - }, - "P": { - "meta": { - "code": "P", - "translation_key": "forms.p.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "ID" - }, - "short": { - "en": "ID" - } - }, - "position": 0, - "length": [ - 5, - 13 - ], - "type": "string", - "required": true - }, - "pnc_visits": { - "labels": { - "tiny": { - "en": "PNCV" - }, - "short": { - "en": "PNC Visits" - } - }, - "position": 1, - "length": [ - 1, - 1 - ], - "range": [ - 0, - 4 - ], - "type": "string", - "required": true - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 2, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "use_sentinel": true - }, - "M": { - "meta": { - "code": "M", - "translation_key": "forms.m.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "id" - }, - "short": { - "en": "Patient Id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "death_notes": { - "labels": { - "tiny": { - "en": "note" - }, - "short": { - "en": "Notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 20 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - } - }, - "schedules": [{ - "name": "ANC Reminders LMP", - "translation_key": "schedule.anc_lmp", - "summary": "ANC Reminders", - "description": "", - "start_from": "lmp_date", - "messages": [{ - "translation_key": "messages.schedule.anc.reminder_1_1", - "group": 1, - "offset": "57 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_1_2", - "group": 1, - "offset": "70 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_1_not_done", - "group": 1, - "offset": "77 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_1_parent", - "group": 1, - "offset": "77 days", - "send_day": "", - "send_time": "4:30", - "recipient": "parent" - }, - { - "translation_key": "messages.schedule.anc.reminder_2_1", - "group": 2, - "offset": "85 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_2_2", - "group": 2, - "offset": "98 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_2_not_done", - "group": 2, - "offset": "105 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_2_parent", - "group": 2, - "offset": "105 days", - "send_day": "", - "send_time": "4:30", - "recipient": "parent" - }, - { - "translation_key": "messages.schedule.anc.reminder_3_1", - "group": 3, - "offset": "134 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_3_2", - "group": 3, - "offset": "148 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_3_not_done", - "group": 3, - "offset": "161 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_3_parent", - "group": 3, - "offset": "161 days", - "send_day": "", - "send_time": "4:30", - "recipient": "parent" - }, - { - "translation_key": "messages.schedule.anc.reminder_4_1", - "group": 4, - "offset": "188 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_4_not_done", - "group": 4, - "offset": "194 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_4_parent", - "group": 4, - "offset": "194 days", - "send_day": "", - "send_time": "4:30", - "recipient": "parent" - }, - { - "translation_key": "messages.schedule.anc.reminder_5_1", - "group": 5, - "offset": "216 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_5_not_done", - "group": 5, - "offset": "222 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_5_parent", - "group": 5, - "offset": "222 days", - "send_day": "", - "send_time": "4:30", - "recipient": "parent" - }, - { - "translation_key": "messages.schedule.anc.reminder_6_1", - "group": 6, - "offset": "230 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_6_not_done", - "group": 6, - "offset": "236 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_6_parent", - "group": 6, - "offset": "236 days", - "send_day": "", - "send_time": "4:30", - "recipient": "parent" - }, - { - "translation_key": "messages.schedule.anc.reminder_7_1", - "group": 7, - "offset": "244 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_7_not_done", - "group": 7, - "offset": "250 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_7_parent", - "group": 7, - "offset": "250 days", - "send_day": "", - "send_time": "4:30", - "recipient": "parent" - }, - { - "translation_key": "messages.schedule.anc.reminder_8_1", - "group": 8, - "offset": "260 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_8_not_done", - "group": 8, - "offset": "273 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_8_parent", - "group": 8, - "offset": "273 days", - "send_day": "", - "send_time": "4:30", - "recipient": "parent" - } - ] - }, - { - "name": "PNC Reminders", - "translation_key": "schedule.pnc_rem", - "summary": "", - "description": "Scheduled reminders for PNC", - "start_from": "birth_date", - "messages": [{ - "translation_key": "messages.schedule.pnc.day_7", - "group": 1, - "offset": "7 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.pnc.day_15", - "group": 1, - "offset": "15 days", - "send_day": "", - "send_time": "4:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.pnc.day_15_HF", - "group": 1, - "offset": "15 days", - "send_day": "", - "send_time": "5:00", - "recipient": "parent" - }, - { - "translation_key": "messages.schedule.pnc.day_40", - "group": 2, - "offset": "40 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.pnc.day_45", - "group": 2, - "offset": "45 days", - "send_day": "", - "send_time": "4:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.pnc.day_45_HF", - "group": 2, - "offset": "45 days", - "send_day": "", - "send_time": "5:00", - "recipient": "parent" - } - ] - }, - { - "name": "Registration Reminders", - "translation_key": "schedule.registration.new_person", - "summary": "", - "description": "Scheduled reminders for Person Registration", - "start_from": "reported_date", - "messages": [{ - "translation_key": "messages.schedule.registration.day_1", - "group": 1, - "offset": "1 days", - "send_day": "", - "send_time": "5:30 +05:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.registration.day_3", - "group": 2, - "offset": "3 days", - "send_day": "", - "send_time": "5:30 +05:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.registration.day_14", - "group": 3, - "offset": "14 days", - "send_day": "", - "send_time": "5:30 +05:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.registration.day_14.hf", - "group": 3, - "offset": "14 days", - "send_day": "", - "send_time": "10:30 +05:45", - "recipient": "health_center" - } - ] - }, - { - "name": "Delivery Reminders", - "translation_key": "schedule.delivery_lmp", - "summary": "", - "description": "Scheduled reminders for Delivery", - "start_from": "lmp_date", - "messages": [{ - "translation_key": "messages.schedule.anc.delivery_1", - "group": 1, - "offset": "266 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.delivery_2", - "group": 1, - "offset": "273 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.delivery_3", - "group": 2, - "offset": "287 days", - "send_day": "", - "send_time": "4:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.delivery_parent_1", - "group": 2, - "offset": "288 days", - "send_day": "", - "send_time": "4:45", - "recipient": "parent" - }, - { - "translation_key": "messages.schedule.anc.delivery_4", - "group": 3, - "offset": "315 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.delivery_parent_2", - "group": 3, - "offset": "322 days", - "send_day": "", - "send_time": "4:45", - "recipient": "parent" - } - ] - } - ] + "locales": null, + "locale": "en", + "locale_outgoing": "ne", + "date_format": "ddd, MMM Do, YYYY", + "reported_date_format": "ddd, MMM Do, YYYY [at] h:mm A", + "forms_only_mode": false, + "place_hierarchy_types": [ + "district_hospital", + "health_center" + ], + "default_responses": { + "start_date": "1970-01-01" + }, + "district_admins_access_unallocated_messages": false, + "public_access": false, + "default_country_code": 977, + "gateway_number": "+9779812345678", + "dhis_data_sets": [ + { + "id": "VMuFODsyWaO", + "translation_key": "dhis.dataset.monthly.integration" + } + ], + "schedule_morning_hours": 6, + "schedule_morning_minutes": 0, + "schedule_evening_hours": 20, + "schedule_evening_minutes": 0, + "synthetic_date": "", + "contact_display_short": "clinic.name", + "roles": { + "national_admin": { + "name": "usertype.national-manager" + }, + "district_admin": { + "name": "usertype.district-manager", + "offline": true + }, + "data_entry": { + "name": "usertype.data-entry" + }, + "analytics": { + "name": "usertype.analytics" + }, + "gateway": { + "name": "usertype.gateway" + }, + "district_level": { + "name": "usertype.district_level" + }, + "munic_level": { + "name": "usertype.munic_level", + "offline": true + }, + "hf_level": { + "name": "usertype.hf_level", + "offline": true + } + }, + "permissions": { + "can_access_gateway_api": [ + "gateway" + ], + "can_bulk_delete_reports": [ + "national_admin", + "district_admin" + ], + "can_configure": [ + "national_admin" + ], + "can_create_people": [ + "national_admin" + ], + "can_create_places": [ + "national_admin" + ], + "can_create_records": [ + "national_admin", + "district_admin", + "data_entry", + "gateway" + ], + "can_create_users": [ + "national_admin" + ], + "can_delete_contacts": [ + "national_admin", + "district_admin" + ], + "can_delete_messages": [ + "national_admin", + "district_admin" + ], + "can_delete_reports": [ + "national_admin", + "district_admin" + ], + "can_delete_users": [ + "national_admin" + ], + "can_edit": [ + "national_admin", + "district_admin", + "gateway" + ], + "can_edit_profile": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_edit_verification": [ + "national_admin", + "district_admin" + ], + "can_export_all": [ + "national_admin" + ], + "can_export_audit": [ + "national_admin" + ], + "can_export_contacts": [ + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_export_feedback": [], + "can_export_forms": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level", + "analytics" + ], + "can_export_messages": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level", + "analytics" + ], + "can_log_out_on_android": [], + "can_update_messages": [ + "national_admin", + "district_admin", + "gateway" + ], + "can_update_people": [ + "national_admin" + ], + "can_update_places": [ + "national_admin" + ], + "can_update_reports": [ + "national_admin" + ], + "can_update_users": [ + "national_admin" + ], + "can_verify_reports": [ + "national_admin", + "district_admin" + ], + "can_view_analytics": [ + "national_admin", + "analytics" + ], + "can_view_analytics_tab": [ + "national_admin" + ], + "can_view_call_action": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_view_contacts": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_view_contacts_tab": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_view_data_records": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level", + "analytics", + "gateway" + ], + "can_view_last_visited_date": [], + "can_view_message_action": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_view_messages": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_view_messages_tab": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_view_outgoing_messages": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_view_reports": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_view_reports_tab": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_view_tasks": [ + "district_admin" + ], + "can_view_tasks_tab": [ + "district_admin" + ], + "can_view_unallocated_data_records": [ + "national_admin", + "district_admin", + "district_level", + "munic_level", + "hf_level", + "gateway" + ], + "can_view_users": [ + "national_admin", + "district_level", + "munic_level", + "hf_level" + ], + "can_write_wealth_quintiles": [], + "can_export_dhis": [ + "national_admin", + "crfo" + ], + "can_view_uhc_stats": [], + "can_view_tasks_group": [ + "chw" + ], + "can_aggregate_targets": [ + "chw_supervisor" + ], + "can_upgrade": [ + "program_officer" + ], + "can_view_old_filter_and_search": [], + "can_view_old_action_bar": [] + }, + "uhc": { + "contacts_default_sort": "", + "visit_count": { + "month_start_date": 1, + "visit_count_goal": 0 + } + }, + "death_reporting": { + "mark_deceased_forms": [ + "death_report" + ], + "date_field": "fields.death_details.date_of_death", + "undo_deceased_forms": [ + "undo_death_report" + ] + }, + "contact_types": [ + { + "id": "district_hospital", + "name_key": "contact.type.district_hospital", + "group_key": "contact.type.district_hospital.plural", + "create_key": "contact.type.district_hospital.new", + "edit_key": "contact.type.place.edit", + "icon": "medic-district-hospital", + "create_form": "form:contact:district_hospital:create", + "edit_form": "form:contact:district_hospital:edit" + }, + { + "id": "health_center", + "name_key": "contact.type.health_center", + "group_key": "contact.type.health_center.plural", + "create_key": "contact.type.health_center.new", + "edit_key": "contact.type.place.edit", + "parents": [ + "district_hospital" + ], + "icon": "medic-health-center", + "create_form": "form:contact:health_center:create", + "edit_form": "form:contact:health_center:edit" + }, + { + "id": "clinic", + "name_key": "contact.type.clinic", + "group_key": "contact.type.clinic.plural", + "create_key": "contact.type.clinic.new", + "edit_key": "contact.type.place.edit", + "parents": [ + "health_center" + ], + "icon": "medic-clinic", + "create_form": "form:contact:clinic:create", + "edit_form": "form:contact:clinic:edit", + "count_visits": true + }, + { + "id": "person", + "name_key": "contact.type.person", + "group_key": "contact.type.person.plural", + "create_key": "contact.type.person.new", + "edit_key": "contact.type.person.edit", + "primary_contact_key": "clinic.field.contact", + "parents": [ + "district_hospital", + "health_center", + "clinic" + ], + "icon": "medic-person", + "create_form": "form:contact:person:create", + "edit_form": "form:contact:person:edit", + "person": true + } + ], + "setup_complete": true, + "contact_summary": "var ContactSummary = {}; !function(t,e){if('object'==typeof exports&&'object'==typeof module)module.exports=e();else if('function'==typeof define&&define.amd)define([],e);else{var n=e();for(var a in n)('object'==typeof exports?exports:t)[a]=n[a]}}(ContactSummary,(function(){return function(t){var e={};function n(a){if(e[a])return e[a].exports;var o=e[a]={i:a,l:!1,exports:{}};return t[a].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,a){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:a})},n.r=function(t){'undefined'!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:'Module'}),Object.defineProperty(t,'__esModule',{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&'object'==typeof t&&t&&t.__esModule)return t;var a=Object.create(null);if(n.r(a),Object.defineProperty(a,'default',{enumerable:!0,value:t}),2&e&&'string'!=typeof t)for(var o in t)n.d(a,o,function(e){return t[e]}.bind(null,o));return a},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,'a',e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p='',n(n.s=0)}([function(t,e,n){var a=n(1),o=n(2);t.exports=o(a,contact,reports)},function(t,e){const n={},a=[{appliesToType:'person',appliesIf:function(){return'chw'===contact.role},label:'female.community.health.volunteer',value:'',width:12},{appliesToType:'person',appliesIf:function(){return'chw'!==contact.role},label:'patient_id',value:contact.patient_id,width:6},{appliesToType:'person',appliesIf:function(){return'chw'!==contact.role},label:'contact.phone.number',value:contact.phone,width:6,filter:'phone'},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role},label:'contact.mhealth.phone.number',value:contact.phone,width:6,filter:'phone'},{appliesToType:'person',appliesIf:function(){return contact.date_of_birth},label:'contact.age',value:contact.date_of_birth,width:6,filter:'age'},{appliesToType:'person',appliesIf:function(){return!contact.date_of_birth&&contact.year_of_birth_approx},label:'contact.age',value:'duration.as.fchv',context:{year:(new Date).getFullYear()-contact.year_of_birth_approx},translate:!0,width:6},{appliesToType:'person',appliesIf:function(){return!contact.date_of_birth&&!contact.year_of_birth_approx},label:'contact.age',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role},label:'contact.education.level',value:'level.'+contact.level_of_education,width:6,translate:!0},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&''!==contact.start_date_as_fchv&&void 0!==contact.start_date_as_fchv},label:'contact.fchv.duration',value:'duration.as.fchv',context:{year:(new Date).getFullYear()-(contact.start_date_as_fchv-57)},translate:!0,width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&(''===contact.start_date_as_fchv||void 0===contact.start_date_as_fchv)},label:'contact.fchv.duration',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&''!==contact.training_date&&void 0!==contact.training_date},label:'contact.fchv.training',value:'duration.as.fchv',context:{year:(new Date).getFullYear()-(contact.training_date-57)},translate:!0,width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&(''===contact.training_date||void 0===contact.training_date)},label:'contact.fchv.training',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&''!==contact.fchv_id_number&&void 0!==contact.fchv_id_number},label:'contact.fchv.id',value:contact.fchv_id_number,width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&(''===contact.fchv_id_number||void 0===contact.fchv_id_number)},label:'contact.fchv.id',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&''!==contact.most_recent_fchv_training&&void 0!==contact.most_recent_fchv_training},label:'contact.fchv.refresher.training',value:contact.most_recent_fchv_training,filter:'relativeDay',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&(''===contact.most_recent_fchv_training||void 0===contact.most_recent_fchv_training)},label:'contact.fchv.refresher.training',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return''!==contact.phone_alternate&&void 0!==contact.phone_alternate},label:'contact.alternate.phone',value:contact.phone_alternate,width:6},{appliesToType:'person',appliesIf:function(){return''===contact.phone_alternate||void 0===contact.phone_alternate},label:'contact.alternate.phone',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return''!==contact.notes&&void 0!==contact.notes},label:'contact.notes',value:contact.notes,width:6},{appliesToType:'person',appliesIf:function(){return''===contact.notes||void 0===contact.notes},label:'contact.notes',value:'-',width:6},{appliesToType:'person',label:'contact.parent',value:lineage,filter:'lineage'},{appliesToType:'!person',label:'contact.notes',value:contact.notes,width:12},{appliesToType:'!person',appliesIf:function(){return contact.parent&&lineage[0]},label:'contact.parent',value:lineage,filter:'lineage'}];lineage[0]&&lineage[0].contact&&(n.chw_name=lineage[0].contact.name,n.chw_phone=lineage[0].contact.phone),t.exports={context:n,cards:[],fields:a}},function(t,e){function n(t){return t?Array.isArray(t)?t:[t]:[]}function a(t,e){switch(typeof t){case'undefined':return!0;case'function':return t(e);default:return t}}function o(t,e,n){if(a(t.appliesIf,n)){var o='function'==typeof t.fields?t.fields(n):t.fields.filter((function(t){return a(t.appliesIf,n)})).map((function(t){var e={};return c(t,e,'label'),c(t,e,'value'),c(t,e,'translate'),c(t,e,'filter'),c(t,e,'width'),c(t,e,'icon'),t.context&&(e.context={},c(t.context,e.context,'count'),c(t.context,e.context,'total')),e}));return t.modifyContext&&t.modifyContext(e,n),{label:t.label,fields:o}}function c(t,e,a){switch(typeof t[a]){case'undefined':return;case'function':e[a]=t[a](n);break;default:e[a]=t[a]}}}t.exports=function(t,e,a){var c=t.fields||[],r=t.context||{},i=t.cards||[],l=e&&('contact'===e.type?e.contact_type:e.type),p={cards:[],fields:c.filter((function(t){var e=n(t.appliesToType),a=e.filter((function(t){return t&&'!'===t.charAt(0)}));if((0===e.length||e.includes(l)||a.length>0&&!a.includes('!'+l))&&(!t.appliesIf||t.appliesIf()))return delete t.appliesToType,delete t.appliesIf,!0}))};return i.forEach((function(t){var e,c,i,f,u=n(t.appliesToType);if(u.includes('report')&&u.length>1)throw new Error('You cannot set appliesToType to an array which includes the type \\'report\\' and another type.');if(u.includes('report'))for(e=0;e0)return;(i=o(t,r))&&p.cards.push(i)}})),p.context=r,p}}])})); return ContactSummary;", + "tasks": { + "rules": "define Target { _id: null, contact: null, deleted: null, type: null, pass: null, date: null, groupBy: null }\ndefine Contact { contact: null, reports: null, tasks: null }\ndefine Task { _id: null, deleted: null, doc: null, contact: null, icon: null, date: null, readyStart: null, readyEnd: null, title: null, fields: null, resolved: null, priority: null, priorityLabel: null, reports: null, actions: null }\nrule GenerateEvents {\n when { c: Contact } then { !function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){'undefined'!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:'Module'}),Object.defineProperty(e,'__esModule',{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&'object'==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,'default',{enumerable:!0,value:e}),2&t&&'string'!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,'a',t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p='',n(n.s=1)}([function(e,t){e.exports=function(e){var t={};!function e(t,n){var r=Object.keys(t);for(var o in r){var a=r[o];switch(typeof t[a]){case'object':e(t[a],n);break;case'function':t[a]=t[a].bind(n)}}}(e,t),t.definition=function e(t){var n=Object.assign({},t),r=Object.keys(n);for(var o in r){var a=r[o];if(Array.isArray(n[a])){n[a]=n[a].slice(0);for(var i=0;i <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)>", + "locale": "ne" + } + ] + }, + { + "property": "last_menstrual_period", + "rule": "(integer && min(6) && max(42))", + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_name}} {{patient_id}} को दर्ता मिलेन। अन्तिम महिनावारी भएको समय ६ देखि ४२ हप्ता भित्र भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! अ {{patient_id}} <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)>", + "locale": "ne" + } + ] + } + ] + }, + "messages": [ + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}। {{patient_name}} {{patient_id}} को गर्भवस्था दर्ता गरेकोमा धन्यबाद! उहाँको प्रसुति अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}}", + "locale": "ne" + }, + { + "content": "नमस्ते {{contact.name}}! {{patient_name}} {{patient_id}} को गर्भवस्था दर्ता गरेकोमा धन्यबाद! उहाँको प्रसुति अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}}", + "locale": "ne" + } + ], + "event_type": "report_accepted", + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते! {{contact.name}} ले {{patient_name}} को गर्भवस्था दर्ता गर्नु भएको छ। उहाँको दर्ता नं {{patient_id}} र प्रसुती अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}} हो। धन्यबाद!", + "locale": "ne" + } + ], + "event_type": "report_accepted", + "recipient": "parent" + }, + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} कसैलाई पनि दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! अ <दर्ता नं (५ देखि १३ अंकको)> <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)> ", + "locale": "ne" + } + ], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "ल", + "events": [ + { + "name": "on_create", + "trigger": "add_expected_date", + "params": "lmp_date", + "bool_expr": "doc.lmp_date" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "ANC Reminders LMP", + "bool_expr": "doc.lmp_date" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "Delivery Reminders", + "bool_expr": "doc.lmp_date" + }, + { + "name": "on_create", + "trigger": "clear_schedule", + "params": "Registration Reminders", + "bool_expr": "doc.lmp_date" + } + ], + "validations": { + "join_responses": false, + "list": [ + { + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "registration.lmp_date.patient_id.incorrect" + }, + { + "property": "lmp_year", + "rule": "(integer && min(2078) && max(2090))", + "translation_key": "registration.lmp_date.year.incorrect" + }, + { + "property": "lmp_month", + "rule": "(integer && min(1) && max(12))", + "translation_key": "registration.lmp_date.month.incorrect" + }, + { + "property": "lmp_day", + "rule": "(integer && min(1) && max(32))", + "translation_key": "registration.lmp_date.day.incorrect" + }, + { + "property": "lmp_date", + "rule": "isBefore('1 month')", + "translation_key": "messages.registration.lmp_date.incorrect.before" + }, + { + "property": "lmp_date", + "rule": "isAfter('-9 months')", + "translation_key": "messages.registration.lmp_date.incorrect.after" + } + ] + }, + "messages": [ + { + "translation_key": "registration.lmp_date.ok", + "event_type": "report_accepted", + "recipient": "clinic" + }, + { + "translation_key": "registration.lmp_date.ok.health_center", + "event_type": "report_accepted", + "recipient": "health_center" + }, + { + "translation_key": "registration.lmp_date.patient_id.not_found", + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "ज", + "events": [ + { + "name": "on_create", + "trigger": "add_birth_date", + "params": "birth_date", + "bool_expr": "doc.days_since_birth" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "PNC Reminders", + "bool_expr": "doc.days_since_birth" + }, + { + "name": "on_create", + "trigger": "clear_schedule", + "params": "Delivery Reminders, ANC Reminders LMP", + "bool_expr": "doc.patient_id" + } + ], + "validations": { + "join_responses": false, + "list": [ + { + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! ज <डेलिभरी कोड><दर्ता नं (५ देखि १३ अंकको)> <डेलिभरी भएको दिन>", + "locale": "ne" + } + ] + }, + { + "property": "delivery_code", + "rule": "regex('^(स|श|ष|सद|शद|षद|घद|घ)$')", + "translation_key": "", + "message": [ + { + "content": "{{#delivery_code}} नमस्ते {{contact.name}}! {{delivery_code}} डेलिभरी कोड मिलेन। स्वास्थ्य सँस्थामा भएको जन्मको लागि ‘स’ र घरमा भएको जन्मको लागि ‘घ’ प्रयोग गर्नुहोला। धन्यवाद। {{/delivery_code}}", + "locale": "ne" + } + ] + }, + { + "property": "days_since_birth", + "rule": "(integer && min(0) && max(50))", + "translation_key": "", + "message": [ + { + "content": "{{#days_since_birth}}नमस्ते {{contact.name}}! प्रसुति पश्चातको अवधि मिलेन, सो अवधि ० देखि ५० दिन भित्र हुनुपर्दछ। तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! ज <डेलिभरी कोड> {{patient_id}} <डेलिभरी भएको दिन>{{/days_since_birth}}{{^days_since_birth}}नमस्ते {{contact.name}}! प्रसुति पश्चातको अवधि ० देखि ५० दिन भित्र हुनुपर्दछ। तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! ज <डेलिभरी कोड> {{patient_id}} <डेलिभरी भएको दिन>{{/days_since_birth}}", + "locale": "ne" + } + ] + } + ] + }, + "messages": [ + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते! {{patient_name}} ({{patient_id}}) का लागि प्रसुति प्रतिवेदन प्राप्त भयो। १,३,७-१४ र ४२ दिनमा हुनु पर्ने सुत्केरी जाँचभेट एकिन गरी SMS बाट प {{patient_id}} <जाँचको पटक> पठाउनुहोला धन्यबाद!", + "locale": "ne" + } + ], + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते! {{contact.name}} बाट {{patient_name}} ({{patient_id}}) को प्रसुति प्रतिवेदन प्राप्त भयो। १,३,७-१४ र ४२ दिनमा हुनु पर्ने सुत्केरी जाँचभेट गर्न घर भेट गर्नु होला, धन्यबाद!", + "locale": "ne" + } + ], + "recipient": "parent" + }, + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! ज <डेलिभरी कोड><दर्ता नं (५ देखि १३ अंकको)> <डेलिभरी भएको दिन>", + "locale": "ne" + } + ], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "क", + "events": [ + { + "name": "on_create", + "trigger": "add_patient_id", + "params": "", + "bool_expr": "doc.patient_name" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "Registration Reminders", + "bool_expr": "doc.patient_name" + } + ], + "validations": { + "join_responses": false, + "list": [ + { + "property": "patient_age", + "rule": "(integer && min(0) && max(125))", + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! दर्ताको ढाँचा मिलेन। कृपया तलको ढाँचाअनुसार पुनः पठाउनुहोस्: क <उमेर (बर्षमा)> <नाम (बढीमा ३० अक्षर सम्मको)>", + "locale": "ne" + } + ] + }, + { + "property": "patient_name", + "rule": "(string && lenMin(1) && lenMax(30))", + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! दर्ताको ढाँचा मिलेन। कृपया तलको ढाँचाअनुसार पुनः पठाउनुहोस्: क <उमेर (बर्षमा)> <नाम (बढीमा ३० अक्षर सम्मको)>", + "locale": "ne" + } + ] + } + ] + }, + "messages": [ + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}, {{patient_name}} ({{patient_id}}) लाई दर्ता गर्नुभएकोमा धन्यवाद! ", + "locale": "" + } + ], + "recipient": "" + } + ] + }, + { + "form": "म", + "events": [ + { + "name": "on_create", + "trigger": "clear_schedule", + "params": "Registration Reminders, ANC Reminders LMP, PNC Reminders, Delivery Reminders", + "bool_expr": "doc.patient_id" + } + ], + "validations": { + "join_responses": false, + "list": [ + { + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [ + { + "content": "{{#patient_id}}नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! म <दर्ता नं (५ देखि १३ अंकको)>{{/patient_id}}{{^patient_id}}नमस्ते {{contact.name}}! तपाईले पठाउनु भएको रिपोर्टको ढाँचा मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! म <दर्ता नं (५ देखि १३ अंकको)>{{/patient_id}}", + "locale": "ne" + } + ] + } + ] + }, + "messages": [ + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}, {{patient_name}} {{patient_id}} को मृत्युको दु:खद घटनाको सूचना दिनुभएकोमा धन्यवाद। ", + "locale": "ne" + } + ], + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [ + { + "content": "{{contact.name}} ले {{patient_age}} बर्षिया {{patient_name}} ({{patient_id}}) को मृत्यु भएको सूचना दिनुभएको छ। ", + "locale": "ne" + } + ], + "recipient": "parent" + }, + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} कसैलाई पनि दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! म <दर्ता नं (५ देखि १३ अंकको)", + "locale": "ne" + } + ], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "A", + "events": [ + { + "name": "on_create", + "trigger": "add_expected_date", + "params": "lmp_date", + "bool_expr": "doc.last_menstrual_period" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "ANC Reminders LMP", + "bool_expr": "doc.last_menstrual_period" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "Delivery Reminders", + "bool_expr": "doc.last_menstrual_period" + }, + { + "name": "on_create", + "trigger": "clear_schedule", + "params": "Registration Reminders", + "bool_expr": "doc.last_menstrual_period" + } + ], + "validations": { + "join_responses": false, + "list": [ + { + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! A <दर्ता नं (५ देखि १३ अंकको)> <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)>", + "locale": "ne" + } + ] + }, + { + "property": "last_menstrual_period", + "rule": "(integer && min(6) && max(42))", + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_name}} {{patient_id}} को दर्ता मिलेन। अन्तिम महिनावारी भएको समय ६ देखि ४२ हप्ता भित्र भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! A {{patient_id}} <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)>", + "locale": "ne" + } + ] + } + ] + }, + "messages": [ + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}। {{patient_name}} {{patient_id}} को गर्भवस्था दर्ता गरेकोमा धन्यबाद! उहाँको प्रसुति अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}}", + "locale": "ne" + } + ], + "event_type": "report_accepted", + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते! {{contact.name}} ले {{patient_name}} को गर्भवस्था दर्ता गर्नु भएको छ। उहाँको दर्ता नं {{patient_id}} र प्रसुती अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}} हो। धन्यबाद!", + "locale": "ne" + } + ], + "event_type": "report_accepted", + "recipient": "parent" + }, + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} कसैलाई पनि दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! A <दर्ता नं (५ देखि १३ अंकको)> <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)> ", + "locale": "ne" + } + ], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "L", + "events": [ + { + "name": "on_create", + "trigger": "add_expected_date", + "params": "lmp_date", + "bool_expr": "doc.lmp_date" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "ANC Reminders LMP", + "bool_expr": "doc.lmp_date" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "Delivery Reminders", + "bool_expr": "doc.lmp_date" + }, + { + "name": "on_create", + "trigger": "clear_schedule", + "params": "Registration Reminders", + "bool_expr": "doc.lmp_date" + } + ], + "validations": { + "join_responses": false, + "list": [ + { + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "registration.lmp_date.patient_id.incorrect" + }, + { + "property": "lmp_year", + "rule": "(integer && min(2078) && max(2090))", + "translation_key": "registration.lmp_date.year.incorrect" + }, + { + "property": "lmp_month", + "rule": "(integer && min(1) && max(12))", + "translation_key": "registration.lmp_date.month.incorrect" + }, + { + "property": "lmp_day", + "rule": "(integer && min(1) && max(32))", + "translation_key": "registration.lmp_date.day.incorrect" + }, + { + "property": "lmp_date", + "rule": "isBefore('1 month')", + "translation_key": "messages.registration.lmp_date.incorrect.before" + }, + { + "property": "lmp_date", + "rule": "isAfter('-9 months')", + "translation_key": "messages.registration.lmp_date.incorrect.after" + } + ] + }, + "messages": [ + { + "translation_key": "registration.lmp_date.ok", + "event_type": "report_accepted", + "recipient": "clinic" + }, + { + "translation_key": "registration.lmp_date.ok.health_center", + "event_type": "report_accepted", + "recipient": "health_center" + }, + { + "translation_key": "registration.lmp_date.patient_id.not_found", + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "J", + "events": [ + { + "name": "on_create", + "trigger": "add_birth_date", + "params": "birth_date", + "bool_expr": "doc.days_since_birth" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "PNC Reminders", + "bool_expr": "doc.days_since_birth" + }, + { + "name": "on_create", + "trigger": "clear_schedule", + "params": "Delivery Reminders, ANC Reminders LMP", + "bool_expr": "doc.patient_id" + } + ], + "validations": { + "join_responses": false, + "list": [ + { + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! J <डेलिभरी कोड><दर्ता नं (५ देखि १३ अंकको)> <डेलिभरी भएको दिन>", + "locale": "ne" + } + ] + }, + { + "property": "delivery_code", + "rule": "regex('^(S|A|s|a)$')", + "translation_key": "", + "message": [ + { + "content": "{{#delivery_code}} नमस्ते {{contact.name}}! {{delivery_code}} डेलिभरी कोड मिलेन। स्वास्थ्य सँस्थामा भएको जन्मको लागि ‘S’ र घरमा भएको जन्मको लागि ‘A’ प्रयोग गर्नुहोला। धन्यवाद। {{/delivery_code}}", + "locale": "ne" + } + ] + }, + { + "property": "days_since_birth", + "rule": "(integer && min(0) && max(50))", + "translation_key": "", + "message": [ + { + "content": "{{#days_since_birth}}नमस्ते {{contact.name}}! प्रसुति पश्चातको अवधि मिलेन, सो अवधि ० देखि ५० दिन भित्र हुनुपर्दछ। तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! J <डेलिभरी कोड> {{patient_id}} <डेलिभरी भएको दिन>{{/days_since_birth}}{{^days_since_birth}}नमस्ते {{contact.name}}! प्रसुति पश्चातको अवधि ० देखि ५० दिन भित्र हुनुपर्दछ। तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! J <डेलिभरी कोड> {{patient_id}} <डेलिभरी भएको दिन>{{/days_since_birth}}", + "locale": "ne" + } + ] + } + ] + }, + "messages": [ + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते! {{patient_name}} ({{patient_id}}) का लागि प्रसुति प्रतिवेदन प्राप्त भयो। १,३,७-१४ र ४२ दिनमा हुनु पर्ने सुत्केरी जाँचभेट एकिन गरी SMS बाट P {{patient_id}} <जाँचको पटक> पठाउनुहोला धन्यबाद!", + "locale": "ne" + } + ], + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते! {{contact.name}} बाट {{patient_name}} ({{patient_id}}) को प्रसुति प्रतिवेदन प्राप्त भयो। १,३,७-१४ र ४२ दिनमा हुनु पर्ने सुत्केरी जाँचभेट गर्न घर भेट गर्नु होला, धन्यबाद!", + "locale": "ne" + } + ], + "recipient": "parent" + }, + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! J <डेलिभरी कोड><दर्ता नं (५ देखि १३ अंकको)> <डेलिभरी भएको दिन>", + "locale": "ne" + } + ], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "K", + "events": [ + { + "name": "on_create", + "trigger": "add_patient_id", + "params": "", + "bool_expr": "doc.patient_name" + }, + { + "name": "on_create", + "trigger": "assign_schedule", + "params": "Registration Reminders", + "bool_expr": "doc.patient_name" + } + ], + "validations": { + "join_responses": false, + "list": [ + { + "property": "patient_age", + "rule": "(integer && min(0) && max(125))", + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! दर्ताको ढाँचा मिलेन। कृपया तलको ढाँचाअनुसार पुनः पठाउनुहोस्: K <उमेर (बर्षमा)> <नाम (बढीमा ३० अक्षर सम्मको)>", + "locale": "ne" + } + ] + }, + { + "property": "patient_name", + "rule": "(string && lenMin(1) && lenMax(30))", + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! दर्ताको ढाँचा मिलेन। कृपया तलको ढाँचाअनुसार पुनः पठाउनुहोस्: K <उमेर (बर्षमा)> <नाम (बढीमा ३० अक्षर सम्मको)>", + "locale": "ne" + } + ] + } + ] + }, + "messages": [ + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}, {{patient_name}} ({{patient_id}}) लाई दर्ता गर्नुभएकोमा धन्यवाद! ", + "locale": "" + } + ], + "recipient": "" + } + ] + }, + { + "form": "M", + "events": [ + { + "name": "on_create", + "trigger": "clear_schedule", + "params": "Registration Reminders, ANC Reminders LMP, PNC Reminders, Delivery Reminders", + "bool_expr": "doc.patient_id" + } + ], + "validations": { + "join_responses": false, + "list": [ + { + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [ + { + "content": "{{#patient_id}}नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! M <दर्ता नं (५ देखि १३ अंकको)>{{/patient_id}}{{^patient_id}}नमस्ते {{contact.name}}! तपाईले पठाउनु भएको रिपोर्टको ढाँचा मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! M <दर्ता नं (५ देखि १३ अंकको)>{{/patient_id}}", + "locale": "ne" + } + ] + } + ] + }, + "messages": [ + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}, {{patient_name}} {{patient_id}} को मृत्युको दु:खद घटनाको सूचना दिनुभएकोमा धन्यवाद। ", + "locale": "ne" + } + ], + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [ + { + "content": "{{contact.name}} ले {{patient_age}} बर्षिया {{patient_name}} ({{patient_id}}) को मृत्यु भएको सूचना दिनुभएको छ। ", + "locale": "ne" + } + ], + "recipient": "parent" + }, + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} कसैलाई पनि दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! म <दर्ता नं (५ देखि १३ अंकको)", + "locale": "ne" + } + ], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + } + ], + "patient_reports": [ + { + "form": "ग", + "name": "Visits", + "format": "ग ", + "silence_type": "ANC Reminders LMP", + "silence_for": "0 days", + "fields": [ + { + "field_name": "", + "title": "" + } + ], + "validations": { + "join_responses": false, + "list": [ + { + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! ग <दर्ता नं (५ देखि १३ अंकको)>", + "locale": "ne" + } + ] + } + ] + }, + "messages": [ + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_name}} ({{patient_id}}) लाई भेटेकोमा धन्यबाद!", + "locale": "ne" + } + ], + "event_type": "report_accepted", + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन।दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! ग <दर्ता नं (५ देखि १३ अंकको)>", + "locale": "ne" + } + ], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "ख", + "name": "Flag", + "format": "ख ", + "silence_type": "", + "silence_for": "", + "fields": [ + { + "field_name": "patient_id", + "title": "Patient ID" + } + ], + "validations": { + "join_responses": false, + "list": [ + { + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [ + { + "content": "{{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! ख <दर्ता नं (५ देखि १३ अंकको)>", + "locale": "ne" + } + ] + } + ] + }, + "messages": [ + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते! {{patient_name}} {{patient_id}}का लागि खतराको चिन्ह पहिचान भएको रिपोर्ट प्राप्त भयो। उहाँलाई बिशेष निगरानीमा राखी आवश्यकता अनुसार स्वास्थ्य संस्था पठाउनुहोला।", + "locale": "ne" + } + ], + "event_type": "report_accepted", + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते! {{contact.name}}ले {{patient_name}} {{patient_id}} का लागि खतराको चिन्ह पहिचान गर्नुभएको छ । म.स्वा.स्व.से.संग सोधखोज गरि आवश्यक सल्लाह दिनुहोला |", + "locale": "ne" + } + ], + "event_type": "report_accepted", + "recipient": "parent" + }, + { + "translation_key": "", + "message": [ + { + "content": "{{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी पुन: फारम पठाउनुहोला, धन्यबाद!", + "locale": "ne" + } + ], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "प", + "name": "PNC Visit", + "format": "प ", + "silence_type": "PNC Reminders", + "silence_for": "0 days", + "fields": [ + { + "field_name": "patient_id", + "title": "Patient ID" + }, + { + "field_name": "pnc_visits", + "title": "No. of PNC visits" + } + ], + "validations": { + "join_responses": false, + "list": [ + { + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} सन्देशको ढांचा मिलेन! ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! प <दर्ता नं (५ देखि १३ अंकको)> <हालसम्मको जम्मा सुत्केरी जाँच भेट संख्या>", + "locale": "ne" + } + ] + }, + { + "property": "pnc_visits", + "rule": "(integer && min(0) && max(4))", + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} को सुत्केरी जाँच भेट संख्या मिलेन। ० देखि ४ बीचको संख्या हुनु पर्दछ। मिलाएर पठाउनु होला, धन्यवाद।", + "locale": "ne" + } + ] + } + ] + }, + "messages": [ + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} को सुत्केरी जाँच प्रतिवेदन पठाउनु भएकोमा धन्यवाद।", + "locale": "ne" + } + ], + "event_type": "report_accepted", + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! सन्देशको ढांचा मिलेन! ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! प <दर्ता नं (५ देखि १३ अंकको)> <हालसम्मको जम्मा सुत्केरी जाँच भेट संख्या>", + "locale": "ne" + } + ], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "G", + "name": "Visits", + "format": "G ", + "silence_type": "ANC Reminders LMP", + "silence_for": "0 days", + "fields": [ + { + "field_name": "", + "title": "" + } + ], + "validations": { + "join_responses": false, + "list": [ + { + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! G <दर्ता नं (५ देखि १३ अंकको)>", + "locale": "ne" + } + ] + } + ] + }, + "messages": [ + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_name}} ({{patient_id}}) लाई भेटेकोमा धन्यबाद!", + "locale": "ne" + } + ], + "event_type": "report_accepted", + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन।दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! G <दर्ता नं (५ देखि १३ अंकको)>", + "locale": "ne" + } + ], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "D", + "name": "Flag", + "format": "D ", + "silence_type": "", + "silence_for": "", + "fields": [ + { + "field_name": "patient_id", + "title": "Patient ID" + } + ], + "validations": { + "join_responses": false, + "list": [ + { + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [ + { + "content": "{{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! D <दर्ता नं (५ देखि १३ अंकको)>", + "locale": "ne" + } + ] + } + ] + }, + "messages": [ + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते! {{patient_name}} {{patient_id}}का लागि खतराको चिन्ह पहिचान भएको रिपोर्ट प्राप्त भयो। उहाँलाई बिशेष निगरानीमा राखी आवश्यकता अनुसार स्वास्थ्य संस्था पठाउनुहोला।", + "locale": "ne" + } + ], + "event_type": "report_accepted", + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते! {{contact.name}}ले {{patient_name}} {{patient_id}} का लागि खतराको चिन्ह पहिचान गर्नुभएको छ । म.स्वा.स्व.से.संग सोधखोज गरि आवश्यक सल्लाह दिनुहोला |", + "locale": "ne" + } + ], + "event_type": "report_accepted", + "recipient": "parent" + }, + { + "translation_key": "", + "message": [ + { + "content": "{{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी पुन: फारम पठाउनुहोला, धन्यबाद!", + "locale": "ne" + } + ], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + }, + { + "form": "P", + "name": "PNC Visit", + "format": "P ", + "silence_type": "PNC Reminders", + "silence_for": "0 days", + "fields": [ + { + "field_name": "patient_id", + "title": "Patient ID" + }, + { + "field_name": "pnc_visits", + "title": "No. of PNC visits" + } + ], + "validations": { + "join_responses": false, + "list": [ + { + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} सन्देशको ढांचा मिलेन! ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! P <दर्ता नं (५ देखि १३ अंकको)> <हालसम्मको जम्मा सुत्केरी जाँच भेट संख्या>", + "locale": "ne" + } + ] + }, + { + "property": "pnc_visits", + "rule": "(integer && min(0) && max(4))", + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} को सुत्केरी जाँच भेट संख्या मिलेन। ० देखि ४ बीचको संख्या हुनु पर्दछ। मिलाएर पठाउनु होला, धन्यवाद।", + "locale": "ne" + } + ] + } + ] + }, + "messages": [ + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} को सुत्केरी जाँच प्रतिवेदन पठाउनु भएकोमा धन्यवाद।", + "locale": "ne" + } + ], + "event_type": "report_accepted", + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! सन्देशको ढांचा मिलेन! ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! P <दर्ता नं (५ देखि १३ अंकको)> <हालसम्मको जम्मा सुत्केरी जाँच भेट संख्या>", + "locale": "ne" + } + ], + "event_type": "registration_not_found", + "recipient": "clinic" + } + ] + } + ], + "muting": { + "mute_forms": [ + "ब", + "B" + ], + "unmute_forms": [ + "स", + "F" + ], + "validations": { + "join_responses": false, + "list": [ + { + "property": "patient_id", + "rule": "regex('^[0-9]{5,13}$')", + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! सूचना बन्द गरेको भए : ब वा B <दर्ता नं (५ देखि १३ अंकको)>, सूचना सुरु गरेको भए: स वा F <दर्ता नं (५ देखि १३ अंकको)>", + "locale": "ne" + } + ] + } + ] + }, + "messages": [ + { + "translation_key": "", + "message": [ + { + "content": "धन्यवाद, {{contact.name}}! {{patient_name}} {{patient_id}} का लागि सबै सुचना बन्द गरिएको छ। सुचारु गर्नका लागि स वा F {{patient_id}} लेखि SMS पठाउनुहोला।", + "locale": "ne" + } + ], + "event_type": "mute", + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [ + { + "content": "{{contact.name}} ले {{patient_name}} ({{patient_id}}) का लागि सबै सुचना बन्द गराउनु भएको छ।", + "locale": "ne" + } + ], + "event_type": "mute", + "recipient": "parent" + }, + { + "translation_key": "", + "message": [ + { + "content": "धन्यवाद, {{contact.name}}! {{patient_name}} ({{patient_id}}) का लागि सुचना सुचारु गरिएको छ।", + "locale": "ne" + } + ], + "event_type": "unmute", + "recipient": "clinic" + }, + { + "translation_key": "", + "message": [ + { + "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी पुन: फारम पठाउनुहोला, धन्यबाद!", + "locale": "ne" + } + ], + "event_type": "contact_not_found", + "recipient": "clinic" + } + ] + }, + "_version": "3.17.1", + "outgoing_deny_list": "1414, 9771414, +9771414, 1408, 9771408, +9771408, 1415, 9771415, +9771415, 1455, 9771455, +9771455, 9851028801, 9779851028801, +9779851028801, 1477, +9771477, NT, 34455, +97734455, 1422, +9771422", + "outgoing_deny_shorter_than": 13, + "outgoing_deny_with_alphas": true, + "multipart_sms_limit": 10, + "sms": { + "outgoing_service": "medic-gateway" + }, + "forms": { + "NP": { + "meta": { + "code": "NP", + "translation_key": "forms.np.title", + "label": { + "en": "New Person with phone_number Registration" + } + }, + "fields": { + "patient_age": { + "labels": { + "tiny": { + "en": "Person Age" + }, + "short": { + "en": "Person Age" + } + }, + "position": 0, + "type": "integer", + "length": [ + 1, + 3 + ], + "range": [ + 0, + 125 + ], + "required": true + }, + "patient_name": { + "labels": { + "tiny": { + "en": "patient_name" + }, + "description": { + "en": "Patient name" + }, + "short": { + "en": "Patient name" + } + }, + "position": 1, + "type": "string", + "length": [ + 3, + 30 + ], + "required": true + }, + "phone_number": { + "labels": { + "tiny": { + "en": "phone number" + }, + "description": { + "en": "phone number" + }, + "short": { + "en": "phone number" + } + }, + "position": 2, + "type": "phone_number", + "required": true + } + }, + "public_form": false, + "use_sentinel": true + }, + "क": { + "meta": { + "code": "क", + "translation_key": "forms.ka.title" + }, + "fields": { + "patient_age": { + "labels": { + "tiny": { + "en": "Person Age" + }, + "short": { + "en": "Person Age" + } + }, + "position": 0, + "type": "integer", + "length": [ + 1, + 3 + ], + "range": [ + 0, + 125 + ], + "required": true + }, + "patient_name": { + "labels": { + "tiny": { + "en": "patient_name" + }, + "short": { + "en": "Patient Name" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 40 + ], + "required": true + } + }, + "public_form": false, + "use_sentinel": true + }, + "अ": { + "meta": { + "code": "अ", + "translation_key": "forms.a.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "patient_id" + }, + "short": { + "en": "Patient Id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "last_menstrual_period": { + "labels": { + "tiny": { + "en": "LMP" + }, + "short": { + "en": "Weeks since LMP" + } + }, + "position": 1, + "type": "integer", + "length": [ + 1, + 2 + ], + "range": [ + 6, + 42 + ], + "required": true + } + }, + "public_form": false, + "use_sentinel": true + }, + "ग": { + "meta": { + "code": "ग", + "translation_key": "forms.ga.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "id" + }, + "short": { + "en": "ID" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 20 + ], + "required": false + } + }, + "use_sentinel": true + }, + "ज": { + "meta": { + "code": "ज", + "translation_key": "forms.ja.title" + }, + "fields": { + "delivery_code": { + "labels": { + "short": { + "en": "Delivery Code" + } + }, + "position": 0, + "type": "string", + "length": [ + 1, + 2 + ], + "required": true + }, + "patient_id": { + "labels": { + "short": { + "en": "ID" + } + }, + "position": 1, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "days_since_birth": { + "labels": { + "short": { + "en": "Days Since Birth" + }, + "tiny": { + "en": "DSB" + } + }, + "position": 2, + "type": "integer", + "length": [ + 1, + 2 + ], + "range": [ + 1, + 50 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 3, + "type": "string", + "length": [ + 1, + 20 + ], + "required": false + } + }, + "use_sentinel": true + }, + "ख": { + "meta": { + "code": "ख", + "translation_key": "forms.kha.title" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "en": "ID" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "use_sentinel": true + }, + "ब": { + "meta": { + "code": "ब", + "translation_key": "forms.ba.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "ID" + }, + "short": { + "en": "ID" + } + }, + "position": 0, + "length": [ + 5, + 13 + ], + "type": "string", + "required": true + }, + "notes": { + "labels": { + "tiny": { + "en": "r", + "sw": "r" + }, + "short": { + "en": "Reason" + } + }, + "position": 1, + "length": [ + 3, + 100 + ], + "type": "string" + } + }, + "use_sentinel": true + }, + "स": { + "meta": { + "code": "स", + "translation_key": "forms.sa.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "ID" + }, + "short": { + "en": "ID" + } + }, + "position": 0, + "length": [ + 5, + 13 + ], + "type": "string" + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "use_sentinel": true + }, + "प": { + "meta": { + "code": "प", + "translation_key": "forms.pa.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "ID" + }, + "short": { + "en": "ID" + } + }, + "position": 0, + "length": [ + 5, + 13 + ], + "type": "string", + "required": true + }, + "pnc_visits": { + "labels": { + "tiny": { + "en": "PNCV" + }, + "short": { + "en": "PNC Visits" + } + }, + "position": 1, + "length": [ + 1, + 1 + ], + "range": [ + 0, + 4 + ], + "type": "string", + "required": true + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 2, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "use_sentinel": true + }, + "म": { + "meta": { + "code": "म", + "translation_key": "forms.ma.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "id" + }, + "short": { + "en": "Patient Id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "death_notes": { + "labels": { + "tiny": { + "en": "note" + }, + "short": { + "en": "Notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 20 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "ल": { + "meta": { + "code": "ल", + "translation_key": "forms.la.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "patient_id" + }, + "short": { + "en": "Patient Id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "lmp_year": { + "labels": { + "tiny": { + "en": "LMP_Y" + }, + "short": { + "en": "LMP Year" + } + }, + "position": 1, + "type": "bsYear", + "required": true + }, + "lmp_month": { + "labels": { + "tiny": { + "en": "LMP_M" + }, + "short": { + "en": "LMP Month" + } + }, + "position": 2, + "type": "bsMonth", + "required": true + }, + "lmp_day": { + "labels": { + "tiny": { + "en": "LMP_D" + }, + "short": { + "en": "LMP Day" + } + }, + "position": 3, + "type": "bsDay", + "required": true + }, + "lmp_date": { + "labels": { + "tiny": { + "en": "LMP_DATE" + }, + "short": { + "en": "LMP Date" + } + }, + "position": 4, + "type": "bsAggreDate", + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "K": { + "meta": { + "code": "K", + "translation_key": "forms.k.title" + }, + "fields": { + "patient_age": { + "labels": { + "tiny": { + "en": "Person Age" + }, + "short": { + "en": "Person Age" + } + }, + "position": 0, + "type": "integer", + "length": [ + 1, + 3 + ], + "range": [ + 0, + 125 + ], + "required": true + }, + "patient_name": { + "labels": { + "tiny": { + "en": "patient_name" + }, + "short": { + "en": "Patient Name" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 40 + ], + "required": true + } + }, + "public_form": false, + "use_sentinel": true + }, + "A": { + "meta": { + "code": "A", + "translation_key": "forms.a_en.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "patient_id" + }, + "short": { + "en": "Patient Id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "last_menstrual_period": { + "labels": { + "tiny": { + "en": "LMP" + }, + "short": { + "en": "Weeks since LMP" + } + }, + "position": 1, + "type": "integer", + "length": [ + 1, + 2 + ], + "range": [ + 6, + 42 + ], + "required": true + } + }, + "public_form": false, + "use_sentinel": true + }, + "L": { + "meta": { + "code": "L", + "translation_key": "forms.l.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "patient_id" + }, + "short": { + "en": "Patient Id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "lmp_year": { + "labels": { + "tiny": { + "en": "LMP_Y" + }, + "short": { + "en": "LMP Year" + } + }, + "position": 1, + "type": "bsYear", + "required": true + }, + "lmp_month": { + "labels": { + "tiny": { + "en": "LMP_M" + }, + "short": { + "en": "LMP Month" + } + }, + "position": 2, + "type": "bsMonth", + "required": true + }, + "lmp_day": { + "labels": { + "tiny": { + "en": "LMP_D" + }, + "short": { + "en": "LMP Day" + } + }, + "position": 3, + "type": "bsDay", + "required": true + }, + "lmp_date": { + "labels": { + "tiny": { + "en": "LMP_DATE" + }, + "short": { + "en": "LMP Date" + } + }, + "position": 4, + "type": "bsAggreDate", + "required": false + } + }, + "public_form": false, + "use_sentinel": true + }, + "G": { + "meta": { + "code": "G", + "translation_key": "forms.g.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "id" + }, + "short": { + "en": "ID" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 20 + ], + "required": false + } + }, + "use_sentinel": true + }, + "J": { + "meta": { + "code": "J", + "translation_key": "forms.j.title" + }, + "fields": { + "delivery_code": { + "labels": { + "short": { + "en": "Delivery Code" + } + }, + "position": 0, + "type": "string", + "length": [ + 1, + 2 + ], + "required": true + }, + "patient_id": { + "labels": { + "short": { + "en": "ID" + } + }, + "position": 1, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "days_since_birth": { + "labels": { + "short": { + "en": "Days Since Birth" + }, + "tiny": { + "en": "DSB" + } + }, + "position": 2, + "type": "integer", + "length": [ + 1, + 2 + ], + "range": [ + 1, + 50 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 3, + "type": "string", + "length": [ + 1, + 20 + ], + "required": false + } + }, + "use_sentinel": true + }, + "D": { + "meta": { + "code": "D", + "translation_key": "forms.d.title" + }, + "fields": { + "patient_id": { + "labels": { + "short": { + "en": "ID" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "use_sentinel": true + }, + "B": { + "meta": { + "code": "B", + "translation_key": "forms.b.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "ID" + }, + "short": { + "en": "ID" + } + }, + "position": 0, + "length": [ + 5, + 13 + ], + "type": "string", + "required": true + }, + "notes": { + "labels": { + "tiny": { + "en": "r", + "sw": "r" + }, + "short": { + "en": "Reason" + } + }, + "position": 1, + "length": [ + 3, + 100 + ], + "type": "string" + } + }, + "use_sentinel": true + }, + "F": { + "meta": { + "code": "F", + "translation_key": "forms.f.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "ID" + }, + "short": { + "en": "ID" + } + }, + "position": 0, + "length": [ + 5, + 13 + ], + "type": "string" + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "use_sentinel": true + }, + "P": { + "meta": { + "code": "P", + "translation_key": "forms.p.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "ID" + }, + "short": { + "en": "ID" + } + }, + "position": 0, + "length": [ + 5, + 13 + ], + "type": "string", + "required": true + }, + "pnc_visits": { + "labels": { + "tiny": { + "en": "PNCV" + }, + "short": { + "en": "PNC Visits" + } + }, + "position": 1, + "length": [ + 1, + 1 + ], + "range": [ + 0, + 4 + ], + "type": "string", + "required": true + }, + "notes": { + "labels": { + "short": { + "en": "Notes" + } + }, + "position": 2, + "type": "string", + "length": [ + 1, + 100 + ], + "required": false + } + }, + "use_sentinel": true + }, + "M": { + "meta": { + "code": "M", + "translation_key": "forms.m.title" + }, + "fields": { + "patient_id": { + "labels": { + "tiny": { + "en": "id" + }, + "short": { + "en": "Patient Id" + } + }, + "position": 0, + "type": "string", + "length": [ + 5, + 13 + ], + "required": true + }, + "death_notes": { + "labels": { + "tiny": { + "en": "note" + }, + "short": { + "en": "Notes" + } + }, + "position": 1, + "type": "string", + "length": [ + 1, + 20 + ], + "required": false + } + }, + "public_form": false, + "use_sentinel": true + } + }, + "schedules": [ + { + "name": "ANC Reminders LMP", + "translation_key": "schedule.anc_lmp", + "summary": "ANC Reminders", + "description": "", + "start_from": "lmp_date", + "messages": [ + { + "translation_key": "messages.schedule.anc.reminder_1_1", + "group": 1, + "offset": "57 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_1_2", + "group": 1, + "offset": "70 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_1_not_done", + "group": 1, + "offset": "77 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_1_parent", + "group": 1, + "offset": "77 days", + "send_day": "", + "send_time": "4:30", + "recipient": "parent" + }, + { + "translation_key": "messages.schedule.anc.reminder_2_1", + "group": 2, + "offset": "85 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_2_2", + "group": 2, + "offset": "98 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_2_not_done", + "group": 2, + "offset": "105 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_2_parent", + "group": 2, + "offset": "105 days", + "send_day": "", + "send_time": "4:30", + "recipient": "parent" + }, + { + "translation_key": "messages.schedule.anc.reminder_3_1", + "group": 3, + "offset": "134 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_3_2", + "group": 3, + "offset": "148 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_3_not_done", + "group": 3, + "offset": "161 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_3_parent", + "group": 3, + "offset": "161 days", + "send_day": "", + "send_time": "4:30", + "recipient": "parent" + }, + { + "translation_key": "messages.schedule.anc.reminder_4_1", + "group": 4, + "offset": "188 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_4_not_done", + "group": 4, + "offset": "194 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_4_parent", + "group": 4, + "offset": "194 days", + "send_day": "", + "send_time": "4:30", + "recipient": "parent" + }, + { + "translation_key": "messages.schedule.anc.reminder_5_1", + "group": 5, + "offset": "216 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_5_not_done", + "group": 5, + "offset": "222 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_5_parent", + "group": 5, + "offset": "222 days", + "send_day": "", + "send_time": "4:30", + "recipient": "parent" + }, + { + "translation_key": "messages.schedule.anc.reminder_6_1", + "group": 6, + "offset": "230 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_6_not_done", + "group": 6, + "offset": "236 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_6_parent", + "group": 6, + "offset": "236 days", + "send_day": "", + "send_time": "4:30", + "recipient": "parent" + }, + { + "translation_key": "messages.schedule.anc.reminder_7_1", + "group": 7, + "offset": "244 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_7_not_done", + "group": 7, + "offset": "250 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_7_parent", + "group": 7, + "offset": "250 days", + "send_day": "", + "send_time": "4:30", + "recipient": "parent" + }, + { + "translation_key": "messages.schedule.anc.reminder_8_1", + "group": 8, + "offset": "260 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_8_not_done", + "group": 8, + "offset": "273 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.reminder_8_parent", + "group": 8, + "offset": "273 days", + "send_day": "", + "send_time": "4:30", + "recipient": "parent" + } + ] + }, + { + "name": "PNC Reminders", + "translation_key": "schedule.pnc_rem", + "summary": "", + "description": "Scheduled reminders for PNC", + "start_from": "birth_date", + "messages": [ + { + "translation_key": "messages.schedule.pnc.day_7", + "group": 1, + "offset": "7 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.pnc.day_15", + "group": 1, + "offset": "15 days", + "send_day": "", + "send_time": "4:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.pnc.day_15_HF", + "group": 1, + "offset": "15 days", + "send_day": "", + "send_time": "5:00", + "recipient": "parent" + }, + { + "translation_key": "messages.schedule.pnc.day_40", + "group": 2, + "offset": "40 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.pnc.day_45", + "group": 2, + "offset": "45 days", + "send_day": "", + "send_time": "4:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.pnc.day_45_HF", + "group": 2, + "offset": "45 days", + "send_day": "", + "send_time": "5:00", + "recipient": "parent" + } + ] + }, + { + "name": "Registration Reminders", + "translation_key": "schedule.registration.new_person", + "summary": "", + "description": "Scheduled reminders for Person Registration", + "start_from": "reported_date", + "messages": [ + { + "translation_key": "messages.schedule.registration.day_1", + "group": 1, + "offset": "1 days", + "send_day": "", + "send_time": "5:30 +05:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.registration.day_3", + "group": 2, + "offset": "3 days", + "send_day": "", + "send_time": "5:30 +05:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.registration.day_14", + "group": 3, + "offset": "14 days", + "send_day": "", + "send_time": "5:30 +05:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.registration.day_14.hf", + "group": 3, + "offset": "14 days", + "send_day": "", + "send_time": "10:30 +05:45", + "recipient": "health_center" + } + ] + }, + { + "name": "Delivery Reminders", + "translation_key": "schedule.delivery_lmp", + "summary": "", + "description": "Scheduled reminders for Delivery", + "start_from": "lmp_date", + "messages": [ + { + "translation_key": "messages.schedule.anc.delivery_1", + "group": 1, + "offset": "266 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.delivery_2", + "group": 1, + "offset": "273 days", + "send_day": "", + "send_time": "23:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.delivery_3", + "group": 2, + "offset": "287 days", + "send_day": "", + "send_time": "4:45", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.delivery_parent_1", + "group": 2, + "offset": "288 days", + "send_day": "", + "send_time": "4:45", + "recipient": "parent" + }, + { + "translation_key": "messages.schedule.anc.delivery_4", + "group": 3, + "offset": "315 days", + "send_day": "", + "send_time": "23:30", + "recipient": "clinic" + }, + { + "translation_key": "messages.schedule.anc.delivery_parent_2", + "group": 3, + "offset": "322 days", + "send_day": "", + "send_time": "4:45", + "recipient": "parent" + } + ] + } + ] } \ No newline at end of file diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 5d1e28faa93..e20542ae8e7 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -195,7 +195,28 @@ const getPhoneNumber = doc => { const setPhoneNumber = doc => { const phoneNumber = getPhoneNumber(doc); const countryCode = config.getAll().default_country_code; - doc.phone_number = countryCode.concat(phoneNumber.toString()); + // By default for a valid phone number SmsParser itself adds a country code if not provided + // So if country code is not present in phone_number throw error + if (!phoneNumber.includes(countryCode)) { + throw new Error( + `${doc.phoneNumber} does not have country code` + ); + } + else { + doc.phone_number = phoneNumber; + } +}; + +const getAge = doc => { + if (!doc || !doc.fields) { + return ''; + } + return doc.fields.age; +}; + +const setAge = doc => { + const age = getAge(doc); + doc.age = age; }; const getConfig = () => config.get('registrations'); @@ -235,6 +256,9 @@ const triggers = { add_phone_number: (options) => { return setPhoneNumber(options.doc); }, + add_age: (options) => { + return setAge(options.doc); + }, add_expected_date: (options) => { return setExpectedBirthDate(options.doc); }, From 4a89954c5cb12bcb6732caf93ccc0ecb4f22c82d Mon Sep 17 00:00:00 2001 From: root Date: Thu, 1 Jun 2023 13:11:22 +0545 Subject: [PATCH 08/56] Added sms parser tests and registration.js phone number funciton --- .../mocha/services/report/smsparser.spec.js | 34 +++++++++++++++---- .../src/transitions/registration.js | 7 ++-- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/api/tests/mocha/services/report/smsparser.spec.js b/api/tests/mocha/services/report/smsparser.spec.js index 205dd61dfc9..86a7fdd5ff3 100644 --- a/api/tests/mocha/services/report/smsparser.spec.js +++ b/api/tests/mocha/services/report/smsparser.spec.js @@ -101,7 +101,8 @@ describe('sms parser', () => { const doc = { message: '1234' }; const def = definitions.forms.NP; sinon.stub(config, 'getAll').returns({ - nepal_country_code: 977 + default_country_code: 977, + phone_validation: 'full' }); const data = smsparser.parse(def, doc); chai.expect(data.phone_number).to.equal(null); @@ -111,7 +112,8 @@ describe('sms parser', () => { const doc = { message: '+9779841161718' }; const def = definitions.forms.NP; sinon.stub(config, 'getAll').returns({ - default_country_code: 977 + default_country_code: 977, + phone_validation: 'full' }); const data = smsparser.parse(def, doc); chai.expect(data.phone_number).to.equal("+977 984-1161718"); @@ -121,7 +123,8 @@ describe('sms parser', () => { const doc = { message: '9841161718' }; const def = definitions.forms.NP; sinon.stub(config, 'getAll').returns({ - default_country_code: 977 + default_country_code: 977, + phone_validation: 'full' }); const data = smsparser.parse(def, doc); chai.expect(data.phone_number).to.equal("+977 984-1161718"); @@ -131,22 +134,41 @@ describe('sms parser', () => { const doc = { message: '+97712312' }; const def = definitions.forms.NP; sinon.stub(config, 'getAll').returns({ - default_country_code: 977 + default_country_code: 977, + phone_validation: 'full' }); const data = smsparser.parse(def, doc); chai.expect(data.phone_number).to.equal(null); }); - it('returns null if phone number is invalid for the region', () => { + it('returns null if phone number is invalid for default region', () => { const doc = { message: '8750660880' }; const def = definitions.forms.NP; sinon.stub(config, 'getAll').returns({ - default_country_code: 977 + default_country_code: 977, + phone_validation: 'full' }); const data = smsparser.parse(def, doc); chai.expect(data.phone_number).to.equal(null); }); + //India , Kenya, Tanzania Phone is accepted as contact info in Nepal region. + //Just incase we make cross region/borders tool + [['+918750660880', '+91 87506 60880'], + ['+254773087889', '+254 773 087889'], + ['+255712262987', '+255 712 262 987']].forEach(phoneNumerWithParsed => { + it('returns parsed number if valid phone of another the region', () => { + const doc = { message: phoneNumerWithParsed[0] }; + const def = definitions.forms.NP; + sinon.stub(config, 'getAll').returns({ + default_country_code: 977, + phone_validation: 'full' + }); + const data = smsparser.parse(def, doc); + chai.expect(data.phone_number).to.equal(phoneNumerWithParsed[1]); + }); + }); + it('parse month type', () => { const doc = { message: '1!FOO!10' }; const def = { diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index e20542ae8e7..bfc5011ff83 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -10,6 +10,7 @@ const acceptPatientReports = require('./accept_patient_reports'); const moment = require('moment'); const config = require('../config'); const date = require('../date'); +const phoneNumberParser = require('@medic/phone-number'); const contactTypesUtils = require('@medic/contact-types-utils'); @@ -194,12 +195,12 @@ const getPhoneNumber = doc => { const setPhoneNumber = doc => { const phoneNumber = getPhoneNumber(doc); - const countryCode = config.getAll().default_country_code; // By default for a valid phone number SmsParser itself adds a country code if not provided // So if country code is not present in phone_number throw error - if (!phoneNumber.includes(countryCode)) { + const validPhone = phoneNumberParser.validate(config.getAll(), phoneNumber); + if (!validPhone) { throw new Error( - `${doc.phoneNumber} does not have country code` + `${doc.phoneNumber} submitted by ${doc.from} is not a valid phone number` ); } else { From 397736762815956fc099c9a5597c974202c95c7c Mon Sep 17 00:00:00 2001 From: root Date: Wed, 14 Jun 2023 10:04:53 +0545 Subject: [PATCH 09/56] registration test for valid phone number --- .../sentinel/transitions/registration.spec.js | 72 +++++++++++++++++-- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/tests/integration/sentinel/transitions/registration.spec.js b/tests/integration/sentinel/transitions/registration.spec.js index 280e99843a2..9f2c172625f 100644 --- a/tests/integration/sentinel/transitions/registration.spec.js +++ b/tests/integration/sentinel/transitions/registration.spec.js @@ -3,7 +3,6 @@ const sentinelUtils = require('../../../utils/sentinel'); const uuid = require('uuid').v4; const moment = require('moment'); const chai = require('chai'); - const defaultSettings = utils.getDefaultSettings(); const contacts = [ @@ -98,6 +97,72 @@ describe('registration', () => { after(() => utils.revertDb([], true)); afterEach(() => utils.revertDb(getIds(contacts), true)); + + [['+918750660880', '+91 87506 60880'], + ['+255712262987', '+255 712 262 987']].forEach(phoneNumerWithParsed => { + console.log(phoneNumerWithParsed[0]); + it('should create new person with phone number if correct number is supplied', () => { + console.log("Phone" + phoneNumerWithParsed[0]); + const settings = { + transitions: { registration: true }, + registrations: [{ + form: 'FORM', + events: [{ + name: 'on_create', + trigger: 'add_phone_number', + params: '', + bool_expr: '' + }], + messages: [], + }], + forms: { FORM: {} } + }; + + const formWithPhone = { + _id: uuid(), + type: 'data_record', + form: 'FORM', + from: '+444999', + fields: { + patient_id: 'patient', + phone_Number: phoneNumerWithParsed[0] + }, + reported_date: moment().valueOf(), + contact: { + _id: 'person', + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + } + }; + + const docs = [formWithPhone]; + const docIds = getIds(docs); + + return utils + .updateSettings(settings, 'sentinel') + .then(() => { + utils.saveDocs(docs) + console.log(docs); + }) + .then(() => { + sentinelUtils.waitForSentinel(docIds) + console.log(docIds); + }) + .then(() => { + sentinelUtils.getInfoDocs(docIds) + }) + .then(infos => { + infos.forEach(info => { + chai.expect(info).to.deep.nested.include({ 'transitions.registration.ok': true }); + }); + }) + .then(() => utils.getDocs(docIds)) + .then(updated => { + chai.expect(updated[0].phoneNumber).to.not.be.null; + chai.expect(updated[0].phoneNumber).to.equal(phoneNumerWithParsed[1]); + }); + }) + }); + it('should be skipped when transition is disabled', () => { const settings = { transitions: { registration: false }, @@ -857,11 +922,8 @@ describe('registration', () => { }); }); - it('should create new person with when correct phone number is supplied', () => { - - }); - it('should not create new person when invalid phone number is supplied', () => { + it('should not add phone number when invalid phone number is supplied', () => { }); From 49086cfa73ffa677f32b4d7ae5e2177e52e41759 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Thu, 29 Jun 2023 11:37:19 +0545 Subject: [PATCH 10/56] added phone number and age adding code to addpatient funtion in registration --- .../transitions/src/transitions/registration.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index bfc5011ff83..2016f66de03 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -212,7 +212,7 @@ const getAge = doc => { if (!doc || !doc.fields) { return ''; } - return doc.fields.age; + return doc.fields.patient_age; }; const setAge = doc => { @@ -231,6 +231,8 @@ const getRegistrationConfig = (config, formCode) => { const triggers = { add_patient: (options) => { + debugger; + console.log(options); // if we already have a patient id then return if (options.doc.patient_id) { return; @@ -255,6 +257,8 @@ const triggers = { return triggers.add_patient(options); }, add_phone_number: (options) => { + debugger; + console.log("add_phone_number" + options); return setPhoneNumber(options.doc); }, add_age: (options) => { @@ -546,6 +550,14 @@ const addPatient = (options) => { patient.date_of_birth = doc.birth_date; } + if (doc.phone_number) { + patient.phoneNumber = doc.phone_number; + } + + if (doc.age) { + patient.age = doc.age; + } + // assign patient in doc with full parent doc - to be used in messages doc.patient = Object.assign({ parent }, patient); patient.parent = lineage.minifyLineage(parent); From 86e53ed3bb317ed2bd4c0efeec73e6d83af41909 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Thu, 6 Jul 2023 09:35:17 +0545 Subject: [PATCH 11/56] registration unit tests --- .../src/transitions/registration.js | 2 +- .../sentinel/transitions/registration.spec.js | 719 ++++++++++++++++-- 2 files changed, 660 insertions(+), 61 deletions(-) diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 2016f66de03..7a851bffa23 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -551,7 +551,7 @@ const addPatient = (options) => { } if (doc.phone_number) { - patient.phoneNumber = doc.phone_number; + patient.phone_number = doc.phone_number; } if (doc.age) { diff --git a/tests/integration/sentinel/transitions/registration.spec.js b/tests/integration/sentinel/transitions/registration.spec.js index 9f2c172625f..a94a481f363 100644 --- a/tests/integration/sentinel/transitions/registration.spec.js +++ b/tests/integration/sentinel/transitions/registration.spec.js @@ -97,72 +97,671 @@ describe('registration', () => { after(() => utils.revertDb([], true)); afterEach(() => utils.revertDb(getIds(contacts), true)); + // [['+918750660880', '+91 87506 60880'], + // ['+255712262987', '+255 712 262 987']].forEach(phoneNumerWithParsed => { + // it.only('yuvraj ko test with phone', () => { + // const settings = { + // transitions: { registration: true }, + // registrations: [{ + // form: 'FORM-A', + // events: [{ + // name: "on_create", + // trigger: "add_phone_number", + // params: "phone_number", + // bool_expr: "doc.fields.phone_number" + // }, + // { + // name: 'on_create', + // trigger: 'add_patient', + // params: '', + // bool_expr: '' + // }], + // messages: [{ + // recipient: 'reporting_unit', + // event_type: 'report_accepted', + // message: [{ + // locale: 'en', + // content: 'Patient {{patient_name}} ({{patient_id}}) added to {{clinic.name}}' + // }], + // }], + // }], + // forms: { 'FORM-A': {} } + // }; + + // const patientWithPhone = { // has just the `patient_name` field, and should create this person + // _id: uuid(), + // type: 'data_record', + // form: 'FORM-A', + // from: '+444999', + // fields: { + // patient_name: 'Minerva', + // phone_number: phoneNumerWithParsed[0] + // }, + // reported_date: moment().valueOf(), + // contact: { + // _id: 'person', + // parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + // } + // }; + + // const docs = [ + // patientWithPhone + // ]; + // const docIds = getIds(docs); + // let newPatientId; + + // return utils + // .updateSettings(settings, 'sentinel') + // .then(() => utils.saveDocs(docs)) + // .then(() => sentinelUtils.waitForSentinel(docIds)) + // .then(() => sentinelUtils.getInfoDocs(docIds)) + // .then(infos => { + // infos.forEach(info => { + // chai.expect(info).to.deep.nested.include({ 'transitions.registration.ok': true }); + // }); + // }) + // .then(() => utils.getDocs(docIds)) + // .then(updated => { + // console.log(updated); + // chai.expect(updated[0].fields.phone_number).to.equal(phoneNumerWithParsed[1]); + // chai.expect(updated[0].patient_id).not.to.equal(undefined); + // chai.expect(updated[0].tasks).to.have.lengthOf(1); + // chai.expect(updated[0].tasks[0].messages[0]).to.include({ + // to: '+444999', + // message: `Patient Minerva (${updated[0].patient_id}) added to Clinic` + // }); + + // newPatientId = updated[0].patient_id; + + // return getContactsByReference([newPatientId, 'venus']); + // }) + // .then(patients => { + // chai.expect(patients.rows[0].doc).to.deep.include({ + // patient_id: newPatientId, + // phone_number: phoneNumerWithParsed[1], + // parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } }, + // name: 'Minerva', + // type: 'person', + // created_by: 'person', + // source_id: patientWithPhone._id, + // }); + // }); + // }); + // }); + + it.only('should add valid phone to patient doc', () => { + let patient_phone = '+9779841123123'; + const settings = { + transitions: { registration: true }, + registrations: [{ + form: 'FORM-A', + events: [{ + name: 'on_create', + trigger: 'add_phone_number', + params: 'phone_number', + bool_expr: 'doc.fields.phone_number' + }, { + name: 'on_create', + trigger: 'add_patient', + params: '', + bool_expr: '' + }], + messages: [{ + recipient: 'reporting_unit', + event_type: 'report_accepted', + message: [{ + locale: 'en', + content: 'Patient {{patient_name}} ({{patient_id}}) added to {{clinic.name}}' + }], + }], + }], + forms: { 'FORM-A': {} } + }; - [['+918750660880', '+91 87506 60880'], - ['+255712262987', '+255 712 262 987']].forEach(phoneNumerWithParsed => { - console.log(phoneNumerWithParsed[0]); - it('should create new person with phone number if correct number is supplied', () => { - console.log("Phone" + phoneNumerWithParsed[0]); - const settings = { - transitions: { registration: true }, - registrations: [{ - form: 'FORM', - events: [{ - name: 'on_create', - trigger: 'add_phone_number', - params: '', - bool_expr: '' + const patientNameAndPhone = { // has just the `patient_name` field, and should create this person + _id: uuid(), + type: 'data_record', + form: 'FORM-A', + from: '+9779841212345', + fields: { + patient_name: 'Minerva', + phone_number: patient_phone + }, + reported_date: moment().valueOf(), + contact: { + _id: 'person', + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + } + }; + + const docs = [ + patientNameAndPhone + ]; + const docIds = getIds(docs); + let newPatientId; + + return utils + .updateSettings(settings, 'sentinel') + .then(utils.saveDocs(docs)) + .then(() => sentinelUtils.waitForSentinel(docIds)) + .then(() => sentinelUtils.getInfoDocs(docIds)) + .then(infos => { + infos.forEach(info => { + chai.expect(info).to.deep.nested.include({ 'transitions.registration.ok': true }); + }); + }) + .then(() => utils.getDocs(docIds)) + .then(updated => { + console.log(updated); + chai.expect(updated[0].fields.phone_number).to.equal(patient_phone); + chai.expect(updated[0].patient_id).not.to.equal(undefined); + chai.expect(updated[0].tasks).to.have.lengthOf(1); + chai.expect(updated[0].tasks[0].messages[0]).to.include({ + to: '+9779841212345', + message: `Patient Minerva (${updated[0].patient_id}) added to Clinic` + }); + + newPatientId = updated[0].patient_id; + + return getContactsByReference([newPatientId, 'venus']); + }) + .then(patients => { + console.log(patients); + chai.expect(patients.rows[0].doc).to.deep.include({ + patient_id: newPatientId, + phone_number: patient_phone, + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } }, + name: 'Minerva', + type: 'person', + created_by: 'person', + source_id: patientNameAndPhone._id, + }); + }); + }); + + it.only('should error out on invalid phone', () => { + let patient_phone = '+9779666666666'; + const settings = { + transitions: { registration: true }, + registrations: [{ + form: 'FORM-A', + events: [{ + name: 'on_create', + trigger: 'add_phone_number', + params: 'phone_number', + bool_expr: 'doc.fields.phone_number' + }, { + name: 'on_create', + trigger: 'add_patient', + params: '', + bool_expr: '' + }], + messages: [{ + recipient: 'reporting_unit', + event_type: 'report_accepted', + message: [{ + locale: 'en', + content: 'Patient {{patient_name}} ({{patient_id}}) added to {{clinic.name}}' }], - messages: [], }], - forms: { FORM: {} } - }; + }], + forms: { 'FORM-A': {} } + }; - const formWithPhone = { - _id: uuid(), - type: 'data_record', - form: 'FORM', - from: '+444999', - fields: { - patient_id: 'patient', - phone_Number: phoneNumerWithParsed[0] - }, - reported_date: moment().valueOf(), - contact: { - _id: 'person', - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } - } - }; - - const docs = [formWithPhone]; - const docIds = getIds(docs); - - return utils - .updateSettings(settings, 'sentinel') - .then(() => { - utils.saveDocs(docs) - console.log(docs); - }) - .then(() => { - sentinelUtils.waitForSentinel(docIds) - console.log(docIds); - }) - .then(() => { - sentinelUtils.getInfoDocs(docIds) - }) - .then(infos => { - infos.forEach(info => { - chai.expect(info).to.deep.nested.include({ 'transitions.registration.ok': true }); - }); - }) - .then(() => utils.getDocs(docIds)) - .then(updated => { - chai.expect(updated[0].phoneNumber).to.not.be.null; - chai.expect(updated[0].phoneNumber).to.equal(phoneNumerWithParsed[1]); - }); - }) + const patientNameAndInvalidPhone = { // has just the `patient_name` field, and should create this person + _id: uuid(), + type: 'data_record', + form: 'FORM-A', + from: '+9779841212345', + fields: { + patient_name: 'Minerva', + phone_number: patient_phone + }, + reported_date: moment().valueOf(), + contact: { + _id: 'person', + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + } + }; + + const docs = [ + patientNameAndInvalidPhone + ]; + const docIds = getIds(docs); + let newPatientId; + + return utils + .updateSettings(settings, 'sentinel') + .then(utils.saveDocs(docs)) + .then(() => sentinelUtils.waitForSentinel(docIds)) + .then(() => sentinelUtils.getInfoDocs(docIds)) + .then(infos => { + infos.forEach(info => { + chai.expect(info).to.deep.nested.include({ 'transitions.registration.ok': true }); + }); + }) + .then(() => utils.getDocs(docIds)) + .then(updated => { + console.log(updated); + chai.expect(updated[0].fields.phone_number).to.equal(undefined); + chai.expect(updated[0].patient_id).not.to.equal(undefined); + chai.expect(updated[0].errors).to.be.ok; + chai.expect(updated[0].errors).to.have.lengthOf(1); + + newPatientId = updated[0].patient_id; + + return getContactsByReference([newPatientId, 'venus']); + }) + .then(patients => { + console.log(patients); + chai.expect(patients.rows[0].doc).to.deep.include({ + patient_id: newPatientId, + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } }, + name: 'Minerva', + type: 'person', + created_by: 'person', + source_id: patientNameAndInvalidPhone._id, + }); + }); }); + // it.only('should error valid phone to patient doc', () => { + // let patient_phone = '+9779841123123'; + // const settings = { + // transitions: { registration: true }, + // registrations: [{ + // form: 'FORM-A', + // events: [{ + // name: 'on_create', + // trigger: 'add_phone_number', + // params: 'phone_number', + // bool_expr: 'doc.fields.phone_number' + // }, { + // name: 'on_create', + // trigger: 'add_patient', + // params: '', + // bool_expr: '' + // }], + // messages: [{ + // recipient: 'reporting_unit', + // event_type: 'report_accepted', + // message: [{ + // locale: 'en', + // content: 'Patient {{patient_name}} ({{patient_id}}) added to {{clinic.name}}' + // }], + // }], + // }], + // forms: { 'FORM-A': {} } + // }; + + // const patientNameAndPhone = { // has just the `patient_name` field, and should create this person + // _id: uuid(), + // type: 'data_record', + // form: 'FORM-A', + // from: '+9779841212345', + // fields: { + // patient_name: 'Minerva', + // phone_number: patient_phone + // }, + // reported_date: moment().valueOf(), + // contact: { + // _id: 'person', + // parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + // } + // }; + + // const docs = [ + // patientNameAndPhone + // ]; + // const docIds = getIds(docs); + // let newPatientId; + + // return utils + // .updateSettings(settings, 'sentinel') + // .then(utils.saveDocs(docs)) + // .then(() => sentinelUtils.waitForSentinel(docIds)) + // .then(() => sentinelUtils.getInfoDocs(docIds)) + // .then(infos => { + // infos.forEach(info => { + // chai.expect(info).to.deep.nested.include({ 'transitions.registration.ok': true }); + // }); + // }) + // .then(() => utils.getDocs(docIds)) + // .then(updated => { + // console.log(updated); + // chai.expect(updated[0].fields.phone_number).to.equal(patient_phone); + // chai.expect(updated[0].patient_id).not.to.equal(undefined); + // chai.expect(updated[0].tasks).to.have.lengthOf(1); + // chai.expect(updated[0].tasks[0].messages[0]).to.include({ + // to: '+9779841212345', + // message: `Patient Minerva (${updated[0].patient_id}) added to Clinic` + // }); + + // newPatientId = updated[0].patient_id; + + // return getContactsByReference([newPatientId, 'venus']); + // }) + // .then(patients => { + // console.log(patients); + // chai.expect(patients.rows[0].doc).to.deep.include({ + // patient_id: newPatientId, + // phone_number: patient_phone, + // parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } }, + // name: 'Minerva', + // type: 'person', + // created_by: 'person', + // source_id: patientNameAndPhone._id, + // }); + // }); + // }); + + + // [['+918750660880', '+91 87506 60880'], + // ['+255712262987', '+255 712 262 987']].forEach(phoneNumerWithParsed => { + // console.log(phoneNumerWithParsed[0]); + // it.only('should create new person with phone number if correct number is supplied', () => { + // console.log("Phone" + phoneNumerWithParsed[0]); + // const settings = { + // transitions: { registration: true }, + // registrations: [ + // { + // "form": "NP", + // "events": [ + // { + // "name": "on_create", + // "trigger": "add_phone_number", + // "params": "phone_number", + // "bool_expr": "doc.fields.phone_number" + // }, + // { + // "name": "on_create", + // "trigger": "add_patient", + // "params": "", + // "bool_expr": "" + // }, + // { + // "name": "on_create", + // "trigger": "add_age", + // "params": "patient_age", + // "bool_expr": "doc.fields.age" + // } + // ], + // "validations": { + // "join_responses": false, + // "list": [ + // { + // "property": "patient_age", + // "rule": "(integer && min(0) && max(125))", + // "translation_key": "", + // "message": [ + // { + // "content": "The registration format for {{patient_name}} is incorrect, please ensure that age is a number between 0 and 125.", + // "locale": "en" + // } + // ] + // }, + // { + // "property": "patient_name", + // "rule": "(string && lenMin(1) && lenMax(30))", + // "translation_key": "", + // "message": [ + // { + // "content": "The registration format for {{patient_name}} is incorrect, please ensure that name length is between 2 and 42.", + // "locale": "en" + // } + // ] + // }, + // { + // "property": "phone_number", + // "rule": "unique('form', 'phone_number')", + // "translation_key": "", + // "message": [ + // { + // "content": "The registration format for {{patient_name}} is incorrect, please ensure that phone number is not already registered", + // "locale": "en" + // } + // ] + // } + // ] + // }, + // "messages": [ + // { + // "translation_key": "", + // "message": [ + // { + // "content": "Thankyou{{contact.name}}, for registering {{patient_name}} with ID: ({{patient_id}})", + // "locale": "" + // } + // ], + // "recipient": "" + // } + // ] + // }], + // "forms": { + // "NP": { + // "meta": { + // "code": "NP", + // "translation_key": "forms.np.title", + // "label": { + // "en": "New Person with phone_number Registration" + // } + // }, + // "fields": { + // "patient_age": { + // "labels": { + // "tiny": { + // "en": "Person Age" + // }, + // "short": { + // "en": "Person Age" + // } + // }, + // "position": 0, + // "type": "integer", + // "length": [ + // 1, + // 3 + // ], + // "range": [ + // 0, + // 125 + // ], + // "required": true + // }, + // "patient_name": { + // "labels": { + // "tiny": { + // "en": "patient_name" + // }, + // "description": { + // "en": "Patient name" + // }, + // "short": { + // "en": "Patient name" + // } + // }, + // "position": 1, + // "type": "string", + // "length": [ + // 3, + // 30 + // ], + // "required": true + // }, + // "phone_number": { + // "labels": { + // "tiny": { + // "en": "phone number" + // }, + // "description": { + // "en": "phone number" + // }, + // "short": { + // "en": "phone number" + // } + // }, + // "position": 2, + // "type": "phone_number", + // "required": true + // } + // }, + // "public_form": false, + // "use_sentinel": true + // }, + // "forms": { + // "NP": { + // "meta": { + // "code": "NP", + // "translation_key": "forms.np.title", + // "label": { + // "en": "New Person with phone_number Registration" + // } + // }, + // "fields": { + // "patient_age": { + // "labels": { + // "tiny": { + // "en": "Person Age" + // }, + // "short": { + // "en": "Person Age" + // } + // }, + // "position": 0, + // "type": "integer", + // "length": [ + // 1, + // 3 + // ], + // "range": [ + // 0, + // 125 + // ], + // "required": true + // }, + // "patient_name": { + // "labels": { + // "tiny": { + // "en": "patient_name" + // }, + // "description": { + // "en": "Patient name" + // }, + // "short": { + // "en": "Patient name" + // } + // }, + // "position": 1, + // "type": "string", + // "length": [ + // 3, + // 30 + // ], + // "required": true + // }, + // "phone_number": { + // "labels": { + // "tiny": { + // "en": "phone number" + // }, + // "description": { + // "en": "phone number" + // }, + // "short": { + // "en": "phone number" + // } + // }, + // "position": 2, + // "type": "phone_number", + // "required": true + // } + // }, + // "public_form": false, + // "use_sentinel": true + // }, + // } + // } + // } + + // // const settings = { + // // transitions: { registration: true }, + // // registrations: [{ + // // form: 'FORM', + // // events: [], + // // messages: [{ + // // recipient: 'reporting_unit', + // // event_type: 'report_accepted', + // // message: [{ + // // locale: 'en', + // // content: 'Report accepted' + // // }], + // // }] + // // }], + // // forms: { FORM: {} } + // // }; + + // // const settings = { + // // transitions: { registration: true }, + // // registrations: [{ + // // form: 'FORM', + // // events: [{ + // // name: 'on_create', + // // trigger: 'add_phone_number', + // // params: '', + // // bool_expr: '' + // // }], + // // messages: [], + // // }], + // // forms: { FORM: {} } + // // }; + + // const formWithPhone = { + // _id: uuid(), + // type: 'data_record', + // form: 'NP', + // from: '+444999', + // fields: { + // patient_id: 'patient', + // patient_age: 20, + // phone_number: phoneNumerWithParsed[0] + // }, + // reported_date: moment().valueOf(), + // contact: { + // _id: 'person', + // parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + // } + // }; + + // const docs = [formWithPhone]; + // const docIds = getIds(docs); + + // return utils + // .updateSettings(settings, 'sentinel') + // .then(() => { + // utils.saveDocs(docs) + // console.log("printing docs"); + // console.log(docs); + // }) + // .then(() => { + // sentinelUtils.waitForSentinel(docIds) + // console.log(docIds); + // }) + // .then(() => { + // sentinelUtils.getInfoDocs(docIds) + // }) + // .then(infos => { + // infos.forEach(info => { + // chai.expect(info).to.deep.nested.include({ 'transitions.registration.ok': true }); + // }); + // }) + // .then(() => utils.getDocs(docIds)) + // .then(updated => { + // chai.expect(updated[0].phoneNumber).to.not.be.null; + // chai.expect(updated[0].phoneNumber).to.equal(phoneNumerWithParsed[1]); + // }); + // }) + // }); + it('should be skipped when transition is disabled', () => { const settings = { transitions: { registration: false }, From ae62cc204bf031d2fa32f3fcf2f50708f5e28127 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Thu, 6 Jul 2023 10:54:29 +0545 Subject: [PATCH 12/56] added unit test to test that patient isn't created when phone is wrong --- .../sentinel/transitions/registration.spec.js | 506 +++--------------- 1 file changed, 65 insertions(+), 441 deletions(-) diff --git a/tests/integration/sentinel/transitions/registration.spec.js b/tests/integration/sentinel/transitions/registration.spec.js index a94a481f363..d57f3d6c0ec 100644 --- a/tests/integration/sentinel/transitions/registration.spec.js +++ b/tests/integration/sentinel/transitions/registration.spec.js @@ -250,36 +250,9 @@ describe('registration', () => { chai.expect(info).to.deep.nested.include({ 'transitions.registration.ok': true }); }); }) - .then(() => utils.getDocs(docIds)) - .then(updated => { - console.log(updated); - chai.expect(updated[0].fields.phone_number).to.equal(patient_phone); - chai.expect(updated[0].patient_id).not.to.equal(undefined); - chai.expect(updated[0].tasks).to.have.lengthOf(1); - chai.expect(updated[0].tasks[0].messages[0]).to.include({ - to: '+9779841212345', - message: `Patient Minerva (${updated[0].patient_id}) added to Clinic` - }); - - newPatientId = updated[0].patient_id; - - return getContactsByReference([newPatientId, 'venus']); - }) - .then(patients => { - console.log(patients); - chai.expect(patients.rows[0].doc).to.deep.include({ - patient_id: newPatientId, - phone_number: patient_phone, - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } }, - name: 'Minerva', - type: 'person', - created_by: 'person', - source_id: patientNameAndPhone._id, - }); - }); }); - it.only('should error out on invalid phone', () => { + it.only('should not create patient from report doc when provided invalid phone', () => { let patient_phone = '+9779666666666'; const settings = { transitions: { registration: true }, @@ -337,430 +310,81 @@ describe('registration', () => { .then(() => sentinelUtils.getInfoDocs(docIds)) .then(infos => { infos.forEach(info => { - chai.expect(info).to.deep.nested.include({ 'transitions.registration.ok': true }); + chai.expect(info).to.deep.nested.not.include({ 'transitions.registration.ok': false }); }); - }) - .then(() => utils.getDocs(docIds)) + }).then(() => utils.getDocs(docIds)) .then(updated => { console.log(updated); - chai.expect(updated[0].fields.phone_number).to.equal(undefined); - chai.expect(updated[0].patient_id).not.to.equal(undefined); - chai.expect(updated[0].errors).to.be.ok; - chai.expect(updated[0].errors).to.have.lengthOf(1); - - newPatientId = updated[0].patient_id; - - return getContactsByReference([newPatientId, 'venus']); + chai.expect(updated[0].fields.phone_number).to.equal(patient_phone); + chai.expect(updated[0].patient_id).equal(undefined); }) .then(patients => { - console.log(patients); - chai.expect(patients.rows[0].doc).to.deep.include({ - patient_id: newPatientId, - parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } }, - name: 'Minerva', - type: 'person', - created_by: 'person', - source_id: patientNameAndInvalidPhone._id, - }); + chai.expect(patients).to.be.undefined; }); }); - // it.only('should error valid phone to patient doc', () => { - // let patient_phone = '+9779841123123'; - // const settings = { - // transitions: { registration: true }, - // registrations: [{ - // form: 'FORM-A', - // events: [{ - // name: 'on_create', - // trigger: 'add_phone_number', - // params: 'phone_number', - // bool_expr: 'doc.fields.phone_number' - // }, { - // name: 'on_create', - // trigger: 'add_patient', - // params: '', - // bool_expr: '' - // }], - // messages: [{ - // recipient: 'reporting_unit', - // event_type: 'report_accepted', - // message: [{ - // locale: 'en', - // content: 'Patient {{patient_name}} ({{patient_id}}) added to {{clinic.name}}' - // }], - // }], - // }], - // forms: { 'FORM-A': {} } - // }; - - // const patientNameAndPhone = { // has just the `patient_name` field, and should create this person - // _id: uuid(), - // type: 'data_record', - // form: 'FORM-A', - // from: '+9779841212345', - // fields: { - // patient_name: 'Minerva', - // phone_number: patient_phone - // }, - // reported_date: moment().valueOf(), - // contact: { - // _id: 'person', - // parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } - // } - // }; - - // const docs = [ - // patientNameAndPhone - // ]; - // const docIds = getIds(docs); - // let newPatientId; - - // return utils - // .updateSettings(settings, 'sentinel') - // .then(utils.saveDocs(docs)) - // .then(() => sentinelUtils.waitForSentinel(docIds)) - // .then(() => sentinelUtils.getInfoDocs(docIds)) - // .then(infos => { - // infos.forEach(info => { - // chai.expect(info).to.deep.nested.include({ 'transitions.registration.ok': true }); - // }); - // }) - // .then(() => utils.getDocs(docIds)) - // .then(updated => { - // console.log(updated); - // chai.expect(updated[0].fields.phone_number).to.equal(patient_phone); - // chai.expect(updated[0].patient_id).not.to.equal(undefined); - // chai.expect(updated[0].tasks).to.have.lengthOf(1); - // chai.expect(updated[0].tasks[0].messages[0]).to.include({ - // to: '+9779841212345', - // message: `Patient Minerva (${updated[0].patient_id}) added to Clinic` - // }); - - // newPatientId = updated[0].patient_id; - - // return getContactsByReference([newPatientId, 'venus']); - // }) - // .then(patients => { - // console.log(patients); - // chai.expect(patients.rows[0].doc).to.deep.include({ - // patient_id: newPatientId, - // phone_number: patient_phone, - // parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } }, - // name: 'Minerva', - // type: 'person', - // created_by: 'person', - // source_id: patientNameAndPhone._id, - // }); - // }); - // }); - + it.only('should fail transition on invalid phone', () => { + let patient_phone = '+9779666666666'; + const settings = { + transitions: { registration: true }, + registrations: [{ + form: 'FORM-A', + events: [{ + name: 'on_create', + trigger: 'add_phone_number', + params: 'phone_number', + bool_expr: 'doc.fields.phone_number' + }, { + name: 'on_create', + trigger: 'add_patient', + params: '', + bool_expr: '' + }], + messages: [{ + recipient: 'reporting_unit', + event_type: 'report_accepted', + message: [{ + locale: 'en', + content: 'Patient {{patient_name}} ({{patient_id}}) added to {{clinic.name}}' + }], + }], + }], + forms: { 'FORM-A': {} } + }; - // [['+918750660880', '+91 87506 60880'], - // ['+255712262987', '+255 712 262 987']].forEach(phoneNumerWithParsed => { - // console.log(phoneNumerWithParsed[0]); - // it.only('should create new person with phone number if correct number is supplied', () => { - // console.log("Phone" + phoneNumerWithParsed[0]); - // const settings = { - // transitions: { registration: true }, - // registrations: [ - // { - // "form": "NP", - // "events": [ - // { - // "name": "on_create", - // "trigger": "add_phone_number", - // "params": "phone_number", - // "bool_expr": "doc.fields.phone_number" - // }, - // { - // "name": "on_create", - // "trigger": "add_patient", - // "params": "", - // "bool_expr": "" - // }, - // { - // "name": "on_create", - // "trigger": "add_age", - // "params": "patient_age", - // "bool_expr": "doc.fields.age" - // } - // ], - // "validations": { - // "join_responses": false, - // "list": [ - // { - // "property": "patient_age", - // "rule": "(integer && min(0) && max(125))", - // "translation_key": "", - // "message": [ - // { - // "content": "The registration format for {{patient_name}} is incorrect, please ensure that age is a number between 0 and 125.", - // "locale": "en" - // } - // ] - // }, - // { - // "property": "patient_name", - // "rule": "(string && lenMin(1) && lenMax(30))", - // "translation_key": "", - // "message": [ - // { - // "content": "The registration format for {{patient_name}} is incorrect, please ensure that name length is between 2 and 42.", - // "locale": "en" - // } - // ] - // }, - // { - // "property": "phone_number", - // "rule": "unique('form', 'phone_number')", - // "translation_key": "", - // "message": [ - // { - // "content": "The registration format for {{patient_name}} is incorrect, please ensure that phone number is not already registered", - // "locale": "en" - // } - // ] - // } - // ] - // }, - // "messages": [ - // { - // "translation_key": "", - // "message": [ - // { - // "content": "Thankyou{{contact.name}}, for registering {{patient_name}} with ID: ({{patient_id}})", - // "locale": "" - // } - // ], - // "recipient": "" - // } - // ] - // }], - // "forms": { - // "NP": { - // "meta": { - // "code": "NP", - // "translation_key": "forms.np.title", - // "label": { - // "en": "New Person with phone_number Registration" - // } - // }, - // "fields": { - // "patient_age": { - // "labels": { - // "tiny": { - // "en": "Person Age" - // }, - // "short": { - // "en": "Person Age" - // } - // }, - // "position": 0, - // "type": "integer", - // "length": [ - // 1, - // 3 - // ], - // "range": [ - // 0, - // 125 - // ], - // "required": true - // }, - // "patient_name": { - // "labels": { - // "tiny": { - // "en": "patient_name" - // }, - // "description": { - // "en": "Patient name" - // }, - // "short": { - // "en": "Patient name" - // } - // }, - // "position": 1, - // "type": "string", - // "length": [ - // 3, - // 30 - // ], - // "required": true - // }, - // "phone_number": { - // "labels": { - // "tiny": { - // "en": "phone number" - // }, - // "description": { - // "en": "phone number" - // }, - // "short": { - // "en": "phone number" - // } - // }, - // "position": 2, - // "type": "phone_number", - // "required": true - // } - // }, - // "public_form": false, - // "use_sentinel": true - // }, - // "forms": { - // "NP": { - // "meta": { - // "code": "NP", - // "translation_key": "forms.np.title", - // "label": { - // "en": "New Person with phone_number Registration" - // } - // }, - // "fields": { - // "patient_age": { - // "labels": { - // "tiny": { - // "en": "Person Age" - // }, - // "short": { - // "en": "Person Age" - // } - // }, - // "position": 0, - // "type": "integer", - // "length": [ - // 1, - // 3 - // ], - // "range": [ - // 0, - // 125 - // ], - // "required": true - // }, - // "patient_name": { - // "labels": { - // "tiny": { - // "en": "patient_name" - // }, - // "description": { - // "en": "Patient name" - // }, - // "short": { - // "en": "Patient name" - // } - // }, - // "position": 1, - // "type": "string", - // "length": [ - // 3, - // 30 - // ], - // "required": true - // }, - // "phone_number": { - // "labels": { - // "tiny": { - // "en": "phone number" - // }, - // "description": { - // "en": "phone number" - // }, - // "short": { - // "en": "phone number" - // } - // }, - // "position": 2, - // "type": "phone_number", - // "required": true - // } - // }, - // "public_form": false, - // "use_sentinel": true - // }, - // } - // } - // } - - // // const settings = { - // // transitions: { registration: true }, - // // registrations: [{ - // // form: 'FORM', - // // events: [], - // // messages: [{ - // // recipient: 'reporting_unit', - // // event_type: 'report_accepted', - // // message: [{ - // // locale: 'en', - // // content: 'Report accepted' - // // }], - // // }] - // // }], - // // forms: { FORM: {} } - // // }; - - // // const settings = { - // // transitions: { registration: true }, - // // registrations: [{ - // // form: 'FORM', - // // events: [{ - // // name: 'on_create', - // // trigger: 'add_phone_number', - // // params: '', - // // bool_expr: '' - // // }], - // // messages: [], - // // }], - // // forms: { FORM: {} } - // // }; - - // const formWithPhone = { - // _id: uuid(), - // type: 'data_record', - // form: 'NP', - // from: '+444999', - // fields: { - // patient_id: 'patient', - // patient_age: 20, - // phone_number: phoneNumerWithParsed[0] - // }, - // reported_date: moment().valueOf(), - // contact: { - // _id: 'person', - // parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } - // } - // }; + const patientNameAndInvalidPhone = { // has just the `patient_name` field, and should create this person + _id: uuid(), + type: 'data_record', + form: 'FORM-A', + from: '+9779841212345', + fields: { + patient_name: 'Minerva', + phone_number: patient_phone + }, + reported_date: moment().valueOf(), + contact: { + _id: 'person', + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } + } + }; - // const docs = [formWithPhone]; - // const docIds = getIds(docs); + const docs = [ + patientNameAndInvalidPhone + ]; + const docIds = getIds(docs); + let newPatientId; - // return utils - // .updateSettings(settings, 'sentinel') - // .then(() => { - // utils.saveDocs(docs) - // console.log("printing docs"); - // console.log(docs); - // }) - // .then(() => { - // sentinelUtils.waitForSentinel(docIds) - // console.log(docIds); - // }) - // .then(() => { - // sentinelUtils.getInfoDocs(docIds) - // }) - // .then(infos => { - // infos.forEach(info => { - // chai.expect(info).to.deep.nested.include({ 'transitions.registration.ok': true }); - // }); - // }) - // .then(() => utils.getDocs(docIds)) - // .then(updated => { - // chai.expect(updated[0].phoneNumber).to.not.be.null; - // chai.expect(updated[0].phoneNumber).to.equal(phoneNumerWithParsed[1]); - // }); - // }) - // }); + return utils + .updateSettings(settings, 'sentinel') + .then(utils.saveDocs(docs)) + .then(() => sentinelUtils.waitForSentinel(docIds)) + .then(() => sentinelUtils.getInfoDocs(docIds)) + .then(infos => { + infos.forEach(info => { + chai.expect(info).to.deep.nested.not.include({ 'transitions.registration.ok': false }); + }); + }) + }); it('should be skipped when transition is disabled', () => { const settings = { From 01854622c11239542e62a3f40526720df1d218de Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Thu, 6 Jul 2023 11:00:03 +0545 Subject: [PATCH 13/56] removed debugger and console.log --- .../src/transitions/registration.js | 3 + .../sentinel/transitions/registration.spec.js | 109 ------------------ 2 files changed, 3 insertions(+), 109 deletions(-) diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 7a851bffa23..0dea7a44f81 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -195,13 +195,16 @@ const getPhoneNumber = doc => { const setPhoneNumber = doc => { const phoneNumber = getPhoneNumber(doc); + console.log(doc); // By default for a valid phone number SmsParser itself adds a country code if not provided // So if country code is not present in phone_number throw error const validPhone = phoneNumberParser.validate(config.getAll(), phoneNumber); if (!validPhone) { + console.log(`${doc.phoneNumber} submitted by ${doc.from} is not a valid phone number`); throw new Error( `${doc.phoneNumber} submitted by ${doc.from} is not a valid phone number` ); + } else { doc.phone_number = phoneNumber; diff --git a/tests/integration/sentinel/transitions/registration.spec.js b/tests/integration/sentinel/transitions/registration.spec.js index d57f3d6c0ec..77af33665f5 100644 --- a/tests/integration/sentinel/transitions/registration.spec.js +++ b/tests/integration/sentinel/transitions/registration.spec.js @@ -97,98 +97,6 @@ describe('registration', () => { after(() => utils.revertDb([], true)); afterEach(() => utils.revertDb(getIds(contacts), true)); - // [['+918750660880', '+91 87506 60880'], - // ['+255712262987', '+255 712 262 987']].forEach(phoneNumerWithParsed => { - // it.only('yuvraj ko test with phone', () => { - // const settings = { - // transitions: { registration: true }, - // registrations: [{ - // form: 'FORM-A', - // events: [{ - // name: "on_create", - // trigger: "add_phone_number", - // params: "phone_number", - // bool_expr: "doc.fields.phone_number" - // }, - // { - // name: 'on_create', - // trigger: 'add_patient', - // params: '', - // bool_expr: '' - // }], - // messages: [{ - // recipient: 'reporting_unit', - // event_type: 'report_accepted', - // message: [{ - // locale: 'en', - // content: 'Patient {{patient_name}} ({{patient_id}}) added to {{clinic.name}}' - // }], - // }], - // }], - // forms: { 'FORM-A': {} } - // }; - - // const patientWithPhone = { // has just the `patient_name` field, and should create this person - // _id: uuid(), - // type: 'data_record', - // form: 'FORM-A', - // from: '+444999', - // fields: { - // patient_name: 'Minerva', - // phone_number: phoneNumerWithParsed[0] - // }, - // reported_date: moment().valueOf(), - // contact: { - // _id: 'person', - // parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } - // } - // }; - - // const docs = [ - // patientWithPhone - // ]; - // const docIds = getIds(docs); - // let newPatientId; - - // return utils - // .updateSettings(settings, 'sentinel') - // .then(() => utils.saveDocs(docs)) - // .then(() => sentinelUtils.waitForSentinel(docIds)) - // .then(() => sentinelUtils.getInfoDocs(docIds)) - // .then(infos => { - // infos.forEach(info => { - // chai.expect(info).to.deep.nested.include({ 'transitions.registration.ok': true }); - // }); - // }) - // .then(() => utils.getDocs(docIds)) - // .then(updated => { - // console.log(updated); - // chai.expect(updated[0].fields.phone_number).to.equal(phoneNumerWithParsed[1]); - // chai.expect(updated[0].patient_id).not.to.equal(undefined); - // chai.expect(updated[0].tasks).to.have.lengthOf(1); - // chai.expect(updated[0].tasks[0].messages[0]).to.include({ - // to: '+444999', - // message: `Patient Minerva (${updated[0].patient_id}) added to Clinic` - // }); - - // newPatientId = updated[0].patient_id; - - // return getContactsByReference([newPatientId, 'venus']); - // }) - // .then(patients => { - // chai.expect(patients.rows[0].doc).to.deep.include({ - // patient_id: newPatientId, - // phone_number: phoneNumerWithParsed[1], - // parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } }, - // name: 'Minerva', - // type: 'person', - // created_by: 'person', - // source_id: patientWithPhone._id, - // }); - // }); - // }); - // }); - it.only('should add valid phone to patient doc', () => { let patient_phone = '+9779841123123'; const settings = { @@ -1145,23 +1053,6 @@ describe('registration', () => { }); }); - - it('should not add phone number when invalid phone number is supplied', () => { - - }); - - it('should append country code to to provided phone number during patient registration', () => { - - }); - - it('should append nothing if country code is not available to phone number', () => { - - }); - - it('should only accept numbers for the phone number field', () => { - - }); - it('should create people with selected parent', () => { const settings = { transitions: { registration: true }, From 614a763d7615a861c62fb05cd9ef81eba4cb7bac Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Thu, 6 Jul 2023 18:15:37 +0545 Subject: [PATCH 14/56] eslint issue fixes 1 --- api/tests/form-definitions.js | 74 ++++++------------- .../mocha/services/report/smsparser.spec.js | 4 +- .../src/transitions/registration.js | 6 -- .../sentinel/transitions/registration.spec.js | 2 - 4 files changed, 23 insertions(+), 63 deletions(-) diff --git a/api/tests/form-definitions.js b/api/tests/form-definitions.js index 3efa90228ea..d9089a0ae5d 100644 --- a/api/tests/form-definitions.js +++ b/api/tests/form-definitions.js @@ -792,69 +792,37 @@ exports.forms = { } } }, - YYYR: { - meta: { - code: 'YYYR', - label: 'Test date parts in BS calendar without bsMonth, bsDay' - }, - fields: { - patient_id: { - labels: { - long: 'Patient ID', - tiny: 'ID' - }, - type: 'string', - required: true - }, - lmp_year: { - labels: { - short: 'Year', - tiny: 'Y' - }, - type: 'bsYear', - required: true - }, - lmp_date: { - labels: { - short: 'LMP Date', - tiny: 'LMP' - }, - type: 'bsAggreDate', - required: true - } - } - }, - "NP": { - "meta": { - "code": "NP", - "translation_key": "forms.np.title", - "label": { - "en": "New Person with phone_number Registration" + 'NP': { + 'meta': { + 'code': 'NP', + 'translation_key': 'forms.np.title', + 'label': { + 'en': 'New Person with phone_number Registration' } }, - "fields": { - "phone_number": { - "labels": { - "tiny": { - "en": "phone number" + 'fields': { + 'phone_number': { + 'labels': { + 'tiny': { + 'en': 'phone number' }, - "description": { - "en": "phone number" + 'description': { + 'en': 'phone number' }, - "short": { - "en": "phone number" + 'short': { + 'en': 'phone number' } }, - "position": 2, - "type": "phone_number", - "length": [ + 'position': 2, + 'type': 'phone_number', + 'length': [ 9, 16 ], - "required": true + 'required': true } }, - "public_form": false, - "use_sentinel": true + 'public_form': false, + 'use_sentinel': true }, }; diff --git a/api/tests/mocha/services/report/smsparser.spec.js b/api/tests/mocha/services/report/smsparser.spec.js index 86a7fdd5ff3..a58e16cb518 100644 --- a/api/tests/mocha/services/report/smsparser.spec.js +++ b/api/tests/mocha/services/report/smsparser.spec.js @@ -116,7 +116,7 @@ describe('sms parser', () => { phone_validation: 'full' }); const data = smsparser.parse(def, doc); - chai.expect(data.phone_number).to.equal("+977 984-1161718"); + chai.expect(data.phone_number).to.equal('+977 984-1161718'); }); it('accepts correct phone number without extension', () => { @@ -127,7 +127,7 @@ describe('sms parser', () => { phone_validation: 'full' }); const data = smsparser.parse(def, doc); - chai.expect(data.phone_number).to.equal("+977 984-1161718"); + chai.expect(data.phone_number).to.equal('+977 984-1161718'); }); it('returns null if phone number is invalid for the region', () => { diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 0dea7a44f81..388f7d9e436 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -195,12 +195,10 @@ const getPhoneNumber = doc => { const setPhoneNumber = doc => { const phoneNumber = getPhoneNumber(doc); - console.log(doc); // By default for a valid phone number SmsParser itself adds a country code if not provided // So if country code is not present in phone_number throw error const validPhone = phoneNumberParser.validate(config.getAll(), phoneNumber); if (!validPhone) { - console.log(`${doc.phoneNumber} submitted by ${doc.from} is not a valid phone number`); throw new Error( `${doc.phoneNumber} submitted by ${doc.from} is not a valid phone number` ); @@ -234,8 +232,6 @@ const getRegistrationConfig = (config, formCode) => { const triggers = { add_patient: (options) => { - debugger; - console.log(options); // if we already have a patient id then return if (options.doc.patient_id) { return; @@ -260,8 +256,6 @@ const triggers = { return triggers.add_patient(options); }, add_phone_number: (options) => { - debugger; - console.log("add_phone_number" + options); return setPhoneNumber(options.doc); }, add_age: (options) => { diff --git a/tests/integration/sentinel/transitions/registration.spec.js b/tests/integration/sentinel/transitions/registration.spec.js index 77af33665f5..a5ddaee4ebc 100644 --- a/tests/integration/sentinel/transitions/registration.spec.js +++ b/tests/integration/sentinel/transitions/registration.spec.js @@ -209,7 +209,6 @@ describe('registration', () => { patientNameAndInvalidPhone ]; const docIds = getIds(docs); - let newPatientId; return utils .updateSettings(settings, 'sentinel') @@ -222,7 +221,6 @@ describe('registration', () => { }); }).then(() => utils.getDocs(docIds)) .then(updated => { - console.log(updated); chai.expect(updated[0].fields.phone_number).to.equal(patient_phone); chai.expect(updated[0].patient_id).equal(undefined); }) From ad94523144504b3dd833af7ca11979f05bc0a323 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Thu, 6 Jul 2023 18:46:15 +0545 Subject: [PATCH 15/56] eslint Fixes 2 --- api/src/services/report/smsparser.js | 6 ++---- .../src/transitions/registration.js | 20 +------------------ .../transitions/src/transitions/utils.js | 5 ----- .../test/unit/transitions/registration.js | 4 +++- .../sentinel/transitions/registration.spec.js | 20 +++++++++---------- 5 files changed, 15 insertions(+), 40 deletions(-) diff --git a/api/src/services/report/smsparser.js b/api/src/services/report/smsparser.js index 2c9318f05ef..1f59f659709 100644 --- a/api/src/services/report/smsparser.js +++ b/api/src/services/report/smsparser.js @@ -212,15 +212,13 @@ const fieldParsers = { return parseNum(stripInvisibleCharacters(raw)); }, phone_number: (raw) => { - let formattedAndValidatedPhone = phoneNumberParser.format(config.getAll(), raw); + const formattedAndValidatedPhone = phoneNumberParser.format(config.getAll(), raw); if (formattedAndValidatedPhone) { return formattedAndValidatedPhone; - } - else { + } else { logger.error(`The provided phone number ${raw} is invalid`); return null; } - } }; diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 388f7d9e436..bc803483bc6 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -202,25 +202,11 @@ const setPhoneNumber = doc => { throw new Error( `${doc.phoneNumber} submitted by ${doc.from} is not a valid phone number` ); - - } - else { + } else { doc.phone_number = phoneNumber; } }; -const getAge = doc => { - if (!doc || !doc.fields) { - return ''; - } - return doc.fields.patient_age; -}; - -const setAge = doc => { - const age = getAge(doc); - doc.age = age; -}; - const getConfig = () => config.get('registrations'); /* @@ -551,10 +537,6 @@ const addPatient = (options) => { patient.phone_number = doc.phone_number; } - if (doc.age) { - patient.age = doc.age; - } - // assign patient in doc with full parent doc - to be used in messages doc.patient = Object.assign({ parent }, patient); patient.parent = lineage.minifyLineage(parent); diff --git a/shared-libs/transitions/src/transitions/utils.js b/shared-libs/transitions/src/transitions/utils.js index 2d6a55167a1..08e802984cb 100644 --- a/shared-libs/transitions/src/transitions/utils.js +++ b/shared-libs/transitions/src/transitions/utils.js @@ -66,11 +66,6 @@ module.exports = { doc.patient_id = patientId; }); }, - isPhoneUnique: (phone) => { - return db.medic - .query('medic-client/contacts_by_reference', { key: ['shortcode', id] }) - .then(results => !(results && results.rows && results.rows.length)); - }, getUniqueId: () => idGenerator.next().value, hasRun: (doc, transition) => { diff --git a/shared-libs/transitions/test/unit/transitions/registration.js b/shared-libs/transitions/test/unit/transitions/registration.js index 4725b86b80b..e55465f0fc0 100644 --- a/shared-libs/transitions/test/unit/transitions/registration.js +++ b/shared-libs/transitions/test/unit/transitions/registration.js @@ -2800,7 +2800,9 @@ describe('registration', () => { transitionUtils.addRegistrationNotFoundError.callCount.should.equal(1); transitionUtils.addRegistrationNotFoundError.args[0].should.deep.equal([doc, registrationConfig]); contactTypeUtils.isPlace.callCount.should.equal(1); - 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' }] + ); }); }); diff --git a/tests/integration/sentinel/transitions/registration.spec.js b/tests/integration/sentinel/transitions/registration.spec.js index a5ddaee4ebc..f395d782bb9 100644 --- a/tests/integration/sentinel/transitions/registration.spec.js +++ b/tests/integration/sentinel/transitions/registration.spec.js @@ -97,8 +97,8 @@ describe('registration', () => { after(() => utils.revertDb([], true)); afterEach(() => utils.revertDb(getIds(contacts), true)); - it.only('should add valid phone to patient doc', () => { - let patient_phone = '+9779841123123'; + it('should add valid phone to patient doc', () => { + const patient_phone = '+9779841123123'; const settings = { transitions: { registration: true }, registrations: [{ @@ -146,7 +146,6 @@ describe('registration', () => { patientNameAndPhone ]; const docIds = getIds(docs); - let newPatientId; return utils .updateSettings(settings, 'sentinel') @@ -157,11 +156,11 @@ describe('registration', () => { infos.forEach(info => { chai.expect(info).to.deep.nested.include({ 'transitions.registration.ok': true }); }); - }) + }); }); - it.only('should not create patient from report doc when provided invalid phone', () => { - let patient_phone = '+9779666666666'; + it('should not create patient from report doc when provided invalid phone', () => { + const patient_phone = '+9779666666666'; const settings = { transitions: { registration: true }, registrations: [{ @@ -229,8 +228,8 @@ describe('registration', () => { }); }); - it.only('should fail transition on invalid phone', () => { - let patient_phone = '+9779666666666'; + it('should fail transition on invalid phone', () => { + const patient_phone = '+9779666666666'; const settings = { transitions: { registration: true }, registrations: [{ @@ -278,7 +277,6 @@ describe('registration', () => { patientNameAndInvalidPhone ]; const docIds = getIds(docs); - let newPatientId; return utils .updateSettings(settings, 'sentinel') @@ -289,10 +287,10 @@ describe('registration', () => { infos.forEach(info => { chai.expect(info).to.deep.nested.not.include({ 'transitions.registration.ok': false }); }); - }) + }); }); - it('should be skipped when transition is disabled', () => { + it(' be skipped when transition is disabled', () => { const settings = { transitions: { registration: false }, registrations: [{ From e2fa1636129c528a164c81e360ca5f1ad29eb4b4 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Mon, 10 Jul 2023 22:09:45 +0545 Subject: [PATCH 16/56] confirming to eslint standards to pass build --- api/src/services/report/smsparser.js | 30 +++++++++---------- .../src/transitions/registration.js | 3 -- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/api/src/services/report/smsparser.js b/api/src/services/report/smsparser.js index 1f59f659709..333ad7a9808 100644 --- a/api/src/services/report/smsparser.js +++ b/api/src/services/report/smsparser.js @@ -79,12 +79,12 @@ const getParser = (exports.getParser = (def, doc) => { const match = msg.match(MUVUKU_REGEX); if (match && match[1]) { switch (match[1].toUpperCase()) { - case '1': - return mpParser.parse; - case 'J1': - return javarosaParser.parse; - default: - return; + case '1': + return mpParser.parse; + case 'J1': + return javarosaParser.parse; + default: + return; } } msg = stripFormCode(code, msg); @@ -298,15 +298,15 @@ exports.parse = (def, doc) => { let bsDay = 1; for (const k of Object.keys(def.fields)) { switch (def.fields[k].type) { - case 'bsYear': - bsYear = msgData[k]; - break; - case 'bsMonth': - bsMonth = msgData[k]; - break; - case 'bsDay': - bsDay = msgData[k]; - break; + case 'bsYear': + bsYear = msgData[k]; + break; + case 'bsMonth': + bsMonth = msgData[k]; + break; + case 'bsDay': + bsDay = msgData[k]; + break; } } diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index bc803483bc6..fbd2e15b0e2 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -244,9 +244,6 @@ const triggers = { add_phone_number: (options) => { return setPhoneNumber(options.doc); }, - add_age: (options) => { - return setAge(options.doc); - }, add_expected_date: (options) => { return setExpectedBirthDate(options.doc); }, From 3e607ede9d41294cbed2b11eea4eb6e081a43279 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Tue, 11 Jul 2023 10:55:52 +0545 Subject: [PATCH 17/56] ident fix --- api/tests/mocha/services/report/smsparser.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/tests/mocha/services/report/smsparser.spec.js b/api/tests/mocha/services/report/smsparser.spec.js index a58e16cb518..655bf1ff8a5 100644 --- a/api/tests/mocha/services/report/smsparser.spec.js +++ b/api/tests/mocha/services/report/smsparser.spec.js @@ -155,8 +155,8 @@ describe('sms parser', () => { //India , Kenya, Tanzania Phone is accepted as contact info in Nepal region. //Just incase we make cross region/borders tool [['+918750660880', '+91 87506 60880'], - ['+254773087889', '+254 773 087889'], - ['+255712262987', '+255 712 262 987']].forEach(phoneNumerWithParsed => { + ['+254773087889', '+254 773 087889'], + ['+255712262987', '+255 712 262 987']].forEach(phoneNumerWithParsed => { it('returns parsed number if valid phone of another the region', () => { const doc = { message: phoneNumerWithParsed[0] }; const def = definitions.forms.NP; From 2639a197c715169fc42a7d234f8a4d4ca3ded762 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Fri, 14 Jul 2023 10:33:45 +0545 Subject: [PATCH 18/56] Reverted app settigns added phone duplicate turn of on on appsettings. --- api/tests/form-definitions.js | 85 +- config/default/app_settings.json | 3593 ++--------------- .../src/transitions/registration.js | 12 +- .../transitions/src/transitions/utils.js | 5 + 4 files changed, 497 insertions(+), 3198 deletions(-) diff --git a/api/tests/form-definitions.js b/api/tests/form-definitions.js index d9089a0ae5d..3d17b7756fe 100644 --- a/api/tests/form-definitions.js +++ b/api/tests/form-definitions.js @@ -792,37 +792,74 @@ exports.forms = { } } }, - 'NP': { - 'meta': { - 'code': 'NP', - 'translation_key': 'forms.np.title', - 'label': { - 'en': 'New Person with phone_number Registration' + "NP": { + "meta": { + "code": "NP", + "translation_key": "forms.np.title", + "label": { + "en": "New Person with phone_number Registration" } }, - 'fields': { - 'phone_number': { - 'labels': { - 'tiny': { - 'en': 'phone number' + "fields": { + "patient_age": { + "labels": { + "tiny": { + "en": "Person Age" }, - 'description': { - 'en': 'phone number' + "short": { + "en": "Person Age" + } + }, + "position": 0, + "type": "integer", + "length": [ + 1, + 3 + ], + "range": [ + 0, + 125 + ], + "required": true + }, + "phone_number": { + "labels": { + "tiny": { + "en": "phone number" }, - 'short': { - 'en': 'phone number' + "description": { + "en": "phone number" + }, + "short": { + "en": "phone number" } }, - 'position': 2, - 'type': 'phone_number', - 'length': [ - 9, - 16 + "position": 1, + "type": "phone_number", + "required": true + }, + "patient_name": { + "labels": { + "tiny": { + "en": "patient_name" + }, + "description": { + "en": "Patient name" + }, + "short": { + "en": "Patient name" + } + }, + "position": 2, + "type": "string", + "length": [ + 3, + 30 ], - 'required': true + "required": true } }, - 'public_form': false, - 'use_sentinel': true - }, + "public_form": false, + "use_sentinel": true + } }; diff --git a/config/default/app_settings.json b/config/default/app_settings.json index a4344989fa5..8b3b7aaadc0 100644 --- a/config/default/app_settings.json +++ b/config/default/app_settings.json @@ -1,3205 +1,454 @@ { - "locales": null, - "locale": "en", - "locale_outgoing": "ne", - "date_format": "ddd, MMM Do, YYYY", - "reported_date_format": "ddd, MMM Do, YYYY [at] h:mm A", - "forms_only_mode": false, - "place_hierarchy_types": [ - "district_hospital", - "health_center" - ], - "default_responses": { - "start_date": "1970-01-01" + "languages": [ + { + "locale": "bm", + "enabled": true }, - "district_admins_access_unallocated_messages": false, - "public_access": false, - "default_country_code": 977, - "gateway_number": "+9779812345678", - "dhis_data_sets": [ - { - "id": "VMuFODsyWaO", - "translation_key": "dhis.dataset.monthly.integration" - } - ], - "schedule_morning_hours": 6, - "schedule_morning_minutes": 0, - "schedule_evening_hours": 20, - "schedule_evening_minutes": 0, - "synthetic_date": "", - "contact_display_short": "clinic.name", - "roles": { - "national_admin": { - "name": "usertype.national-manager" - }, - "district_admin": { - "name": "usertype.district-manager", - "offline": true - }, - "data_entry": { - "name": "usertype.data-entry" - }, - "analytics": { - "name": "usertype.analytics" - }, - "gateway": { - "name": "usertype.gateway" - }, - "district_level": { - "name": "usertype.district_level" - }, - "munic_level": { - "name": "usertype.munic_level", - "offline": true - }, - "hf_level": { - "name": "usertype.hf_level", - "offline": true - } + { + "locale": "en", + "enabled": true }, - "permissions": { - "can_access_gateway_api": [ - "gateway" - ], - "can_bulk_delete_reports": [ - "national_admin", - "district_admin" - ], - "can_configure": [ - "national_admin" - ], - "can_create_people": [ - "national_admin" - ], - "can_create_places": [ - "national_admin" - ], - "can_create_records": [ - "national_admin", - "district_admin", - "data_entry", - "gateway" - ], - "can_create_users": [ - "national_admin" - ], - "can_delete_contacts": [ - "national_admin", - "district_admin" - ], - "can_delete_messages": [ - "national_admin", - "district_admin" - ], - "can_delete_reports": [ - "national_admin", - "district_admin" - ], - "can_delete_users": [ - "national_admin" - ], - "can_edit": [ - "national_admin", - "district_admin", - "gateway" - ], - "can_edit_profile": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_edit_verification": [ - "national_admin", - "district_admin" - ], - "can_export_all": [ - "national_admin" - ], - "can_export_audit": [ - "national_admin" - ], - "can_export_contacts": [ - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_export_feedback": [], - "can_export_forms": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level", - "analytics" - ], - "can_export_messages": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level", - "analytics" - ], - "can_log_out_on_android": [], - "can_update_messages": [ - "national_admin", - "district_admin", - "gateway" - ], - "can_update_people": [ - "national_admin" - ], - "can_update_places": [ - "national_admin" - ], - "can_update_reports": [ - "national_admin" - ], - "can_update_users": [ - "national_admin" - ], - "can_verify_reports": [ - "national_admin", - "district_admin" - ], - "can_view_analytics": [ - "national_admin", - "analytics" - ], - "can_view_analytics_tab": [ - "national_admin" - ], - "can_view_call_action": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_view_contacts": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_view_contacts_tab": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_view_data_records": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level", - "analytics", - "gateway" - ], - "can_view_last_visited_date": [], - "can_view_message_action": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_view_messages": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_view_messages_tab": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_view_outgoing_messages": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_view_reports": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_view_reports_tab": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_view_tasks": [ - "district_admin" - ], - "can_view_tasks_tab": [ - "district_admin" - ], - "can_view_unallocated_data_records": [ - "national_admin", - "district_admin", - "district_level", - "munic_level", - "hf_level", - "gateway" - ], - "can_view_users": [ - "national_admin", - "district_level", - "munic_level", - "hf_level" - ], - "can_write_wealth_quintiles": [], - "can_export_dhis": [ - "national_admin", - "crfo" - ], - "can_view_uhc_stats": [], - "can_view_tasks_group": [ - "chw" - ], - "can_aggregate_targets": [ - "chw_supervisor" - ], - "can_upgrade": [ - "program_officer" - ], - "can_view_old_filter_and_search": [], - "can_view_old_action_bar": [] + { + "locale": "es", + "enabled": true }, - "uhc": { - "contacts_default_sort": "", - "visit_count": { - "month_start_date": 1, - "visit_count_goal": 0 - } + { + "locale": "fr", + "enabled": true }, - "death_reporting": { - "mark_deceased_forms": [ - "death_report" - ], - "date_field": "fields.death_details.date_of_death", - "undo_deceased_forms": [ - "undo_death_report" - ] + { + "locale": "hi", + "enabled": true }, - "contact_types": [ - { - "id": "district_hospital", - "name_key": "contact.type.district_hospital", - "group_key": "contact.type.district_hospital.plural", - "create_key": "contact.type.district_hospital.new", - "edit_key": "contact.type.place.edit", - "icon": "medic-district-hospital", - "create_form": "form:contact:district_hospital:create", - "edit_form": "form:contact:district_hospital:edit" - }, - { - "id": "health_center", - "name_key": "contact.type.health_center", - "group_key": "contact.type.health_center.plural", - "create_key": "contact.type.health_center.new", - "edit_key": "contact.type.place.edit", - "parents": [ - "district_hospital" - ], - "icon": "medic-health-center", - "create_form": "form:contact:health_center:create", - "edit_form": "form:contact:health_center:edit" - }, - { - "id": "clinic", - "name_key": "contact.type.clinic", - "group_key": "contact.type.clinic.plural", - "create_key": "contact.type.clinic.new", - "edit_key": "contact.type.place.edit", - "parents": [ - "health_center" - ], - "icon": "medic-clinic", - "create_form": "form:contact:clinic:create", - "edit_form": "form:contact:clinic:edit", - "count_visits": true - }, - { - "id": "person", - "name_key": "contact.type.person", - "group_key": "contact.type.person.plural", - "create_key": "contact.type.person.new", - "edit_key": "contact.type.person.edit", - "primary_contact_key": "clinic.field.contact", - "parents": [ - "district_hospital", - "health_center", - "clinic" - ], - "icon": "medic-person", - "create_form": "form:contact:person:create", - "edit_form": "form:contact:person:edit", - "person": true - } - ], - "setup_complete": true, - "contact_summary": "var ContactSummary = {}; !function(t,e){if('object'==typeof exports&&'object'==typeof module)module.exports=e();else if('function'==typeof define&&define.amd)define([],e);else{var n=e();for(var a in n)('object'==typeof exports?exports:t)[a]=n[a]}}(ContactSummary,(function(){return function(t){var e={};function n(a){if(e[a])return e[a].exports;var o=e[a]={i:a,l:!1,exports:{}};return t[a].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,a){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:a})},n.r=function(t){'undefined'!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:'Module'}),Object.defineProperty(t,'__esModule',{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&'object'==typeof t&&t&&t.__esModule)return t;var a=Object.create(null);if(n.r(a),Object.defineProperty(a,'default',{enumerable:!0,value:t}),2&e&&'string'!=typeof t)for(var o in t)n.d(a,o,function(e){return t[e]}.bind(null,o));return a},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,'a',e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p='',n(n.s=0)}([function(t,e,n){var a=n(1),o=n(2);t.exports=o(a,contact,reports)},function(t,e){const n={},a=[{appliesToType:'person',appliesIf:function(){return'chw'===contact.role},label:'female.community.health.volunteer',value:'',width:12},{appliesToType:'person',appliesIf:function(){return'chw'!==contact.role},label:'patient_id',value:contact.patient_id,width:6},{appliesToType:'person',appliesIf:function(){return'chw'!==contact.role},label:'contact.phone.number',value:contact.phone,width:6,filter:'phone'},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role},label:'contact.mhealth.phone.number',value:contact.phone,width:6,filter:'phone'},{appliesToType:'person',appliesIf:function(){return contact.date_of_birth},label:'contact.age',value:contact.date_of_birth,width:6,filter:'age'},{appliesToType:'person',appliesIf:function(){return!contact.date_of_birth&&contact.year_of_birth_approx},label:'contact.age',value:'duration.as.fchv',context:{year:(new Date).getFullYear()-contact.year_of_birth_approx},translate:!0,width:6},{appliesToType:'person',appliesIf:function(){return!contact.date_of_birth&&!contact.year_of_birth_approx},label:'contact.age',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role},label:'contact.education.level',value:'level.'+contact.level_of_education,width:6,translate:!0},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&''!==contact.start_date_as_fchv&&void 0!==contact.start_date_as_fchv},label:'contact.fchv.duration',value:'duration.as.fchv',context:{year:(new Date).getFullYear()-(contact.start_date_as_fchv-57)},translate:!0,width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&(''===contact.start_date_as_fchv||void 0===contact.start_date_as_fchv)},label:'contact.fchv.duration',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&''!==contact.training_date&&void 0!==contact.training_date},label:'contact.fchv.training',value:'duration.as.fchv',context:{year:(new Date).getFullYear()-(contact.training_date-57)},translate:!0,width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&(''===contact.training_date||void 0===contact.training_date)},label:'contact.fchv.training',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&''!==contact.fchv_id_number&&void 0!==contact.fchv_id_number},label:'contact.fchv.id',value:contact.fchv_id_number,width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&(''===contact.fchv_id_number||void 0===contact.fchv_id_number)},label:'contact.fchv.id',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&''!==contact.most_recent_fchv_training&&void 0!==contact.most_recent_fchv_training},label:'contact.fchv.refresher.training',value:contact.most_recent_fchv_training,filter:'relativeDay',width:6},{appliesToType:'person',appliesIf:function(){return'chw'===contact.role&&(''===contact.most_recent_fchv_training||void 0===contact.most_recent_fchv_training)},label:'contact.fchv.refresher.training',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return''!==contact.phone_alternate&&void 0!==contact.phone_alternate},label:'contact.alternate.phone',value:contact.phone_alternate,width:6},{appliesToType:'person',appliesIf:function(){return''===contact.phone_alternate||void 0===contact.phone_alternate},label:'contact.alternate.phone',value:'-',width:6},{appliesToType:'person',appliesIf:function(){return''!==contact.notes&&void 0!==contact.notes},label:'contact.notes',value:contact.notes,width:6},{appliesToType:'person',appliesIf:function(){return''===contact.notes||void 0===contact.notes},label:'contact.notes',value:'-',width:6},{appliesToType:'person',label:'contact.parent',value:lineage,filter:'lineage'},{appliesToType:'!person',label:'contact.notes',value:contact.notes,width:12},{appliesToType:'!person',appliesIf:function(){return contact.parent&&lineage[0]},label:'contact.parent',value:lineage,filter:'lineage'}];lineage[0]&&lineage[0].contact&&(n.chw_name=lineage[0].contact.name,n.chw_phone=lineage[0].contact.phone),t.exports={context:n,cards:[],fields:a}},function(t,e){function n(t){return t?Array.isArray(t)?t:[t]:[]}function a(t,e){switch(typeof t){case'undefined':return!0;case'function':return t(e);default:return t}}function o(t,e,n){if(a(t.appliesIf,n)){var o='function'==typeof t.fields?t.fields(n):t.fields.filter((function(t){return a(t.appliesIf,n)})).map((function(t){var e={};return c(t,e,'label'),c(t,e,'value'),c(t,e,'translate'),c(t,e,'filter'),c(t,e,'width'),c(t,e,'icon'),t.context&&(e.context={},c(t.context,e.context,'count'),c(t.context,e.context,'total')),e}));return t.modifyContext&&t.modifyContext(e,n),{label:t.label,fields:o}}function c(t,e,a){switch(typeof t[a]){case'undefined':return;case'function':e[a]=t[a](n);break;default:e[a]=t[a]}}}t.exports=function(t,e,a){var c=t.fields||[],r=t.context||{},i=t.cards||[],l=e&&('contact'===e.type?e.contact_type:e.type),p={cards:[],fields:c.filter((function(t){var e=n(t.appliesToType),a=e.filter((function(t){return t&&'!'===t.charAt(0)}));if((0===e.length||e.includes(l)||a.length>0&&!a.includes('!'+l))&&(!t.appliesIf||t.appliesIf()))return delete t.appliesToType,delete t.appliesIf,!0}))};return i.forEach((function(t){var e,c,i,f,u=n(t.appliesToType);if(u.includes('report')&&u.length>1)throw new Error('You cannot set appliesToType to an array which includes the type \\'report\\' and another type.');if(u.includes('report'))for(e=0;e0)return;(i=o(t,r))&&p.cards.push(i)}})),p.context=r,p}}])})); return ContactSummary;", - "tasks": { - "rules": "define Target { _id: null, contact: null, deleted: null, type: null, pass: null, date: null, groupBy: null }\ndefine Contact { contact: null, reports: null, tasks: null }\ndefine Task { _id: null, deleted: null, doc: null, contact: null, icon: null, date: null, readyStart: null, readyEnd: null, title: null, fields: null, resolved: null, priority: null, priorityLabel: null, reports: null, actions: null }\nrule GenerateEvents {\n when { c: Contact } then { !function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){'undefined'!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:'Module'}),Object.defineProperty(e,'__esModule',{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&'object'==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,'default',{enumerable:!0,value:e}),2&t&&'string'!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,'a',t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p='',n(n.s=1)}([function(e,t){e.exports=function(e){var t={};!function e(t,n){var r=Object.keys(t);for(var o in r){var a=r[o];switch(typeof t[a]){case'object':e(t[a],n);break;case'function':t[a]=t[a].bind(n)}}}(e,t),t.definition=function e(t){var n=Object.assign({},t),r=Object.keys(n);for(var o in r){var a=r[o];if(Array.isArray(n[a])){n[a]=n[a].slice(0);for(var i=0;i <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)>", - "locale": "ne" - } - ] - }, - { - "property": "last_menstrual_period", - "rule": "(integer && min(6) && max(42))", - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_name}} {{patient_id}} को दर्ता मिलेन। अन्तिम महिनावारी भएको समय ६ देखि ४२ हप्ता भित्र भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! अ {{patient_id}} <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)>", - "locale": "ne" - } - ] - } - ] - }, - "messages": [ - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}। {{patient_name}} {{patient_id}} को गर्भवस्था दर्ता गरेकोमा धन्यबाद! उहाँको प्रसुति अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}}", - "locale": "ne" - }, - { - "content": "नमस्ते {{contact.name}}! {{patient_name}} {{patient_id}} को गर्भवस्था दर्ता गरेकोमा धन्यबाद! उहाँको प्रसुति अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}}", - "locale": "ne" - } - ], - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते! {{contact.name}} ले {{patient_name}} को गर्भवस्था दर्ता गर्नु भएको छ। उहाँको दर्ता नं {{patient_id}} र प्रसुती अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}} हो। धन्यबाद!", - "locale": "ne" - } - ], - "event_type": "report_accepted", - "recipient": "parent" - }, - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} कसैलाई पनि दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! अ <दर्ता नं (५ देखि १३ अंकको)> <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)> ", - "locale": "ne" - } - ], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "ल", - "events": [ - { - "name": "on_create", - "trigger": "add_expected_date", - "params": "lmp_date", - "bool_expr": "doc.lmp_date" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "ANC Reminders LMP", - "bool_expr": "doc.lmp_date" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "Delivery Reminders", - "bool_expr": "doc.lmp_date" - }, - { - "name": "on_create", - "trigger": "clear_schedule", - "params": "Registration Reminders", - "bool_expr": "doc.lmp_date" - } - ], - "validations": { - "join_responses": false, - "list": [ - { - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "registration.lmp_date.patient_id.incorrect" - }, - { - "property": "lmp_year", - "rule": "(integer && min(2078) && max(2090))", - "translation_key": "registration.lmp_date.year.incorrect" - }, - { - "property": "lmp_month", - "rule": "(integer && min(1) && max(12))", - "translation_key": "registration.lmp_date.month.incorrect" - }, - { - "property": "lmp_day", - "rule": "(integer && min(1) && max(32))", - "translation_key": "registration.lmp_date.day.incorrect" - }, - { - "property": "lmp_date", - "rule": "isBefore('1 month')", - "translation_key": "messages.registration.lmp_date.incorrect.before" - }, - { - "property": "lmp_date", - "rule": "isAfter('-9 months')", - "translation_key": "messages.registration.lmp_date.incorrect.after" - } - ] - }, - "messages": [ - { - "translation_key": "registration.lmp_date.ok", - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "registration.lmp_date.ok.health_center", - "event_type": "report_accepted", - "recipient": "health_center" - }, - { - "translation_key": "registration.lmp_date.patient_id.not_found", - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "ज", - "events": [ - { - "name": "on_create", - "trigger": "add_birth_date", - "params": "birth_date", - "bool_expr": "doc.days_since_birth" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "PNC Reminders", - "bool_expr": "doc.days_since_birth" - }, - { - "name": "on_create", - "trigger": "clear_schedule", - "params": "Delivery Reminders, ANC Reminders LMP", - "bool_expr": "doc.patient_id" - } - ], - "validations": { - "join_responses": false, - "list": [ - { - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! ज <डेलिभरी कोड><दर्ता नं (५ देखि १३ अंकको)> <डेलिभरी भएको दिन>", - "locale": "ne" - } - ] - }, - { - "property": "delivery_code", - "rule": "regex('^(स|श|ष|सद|शद|षद|घद|घ)$')", - "translation_key": "", - "message": [ - { - "content": "{{#delivery_code}} नमस्ते {{contact.name}}! {{delivery_code}} डेलिभरी कोड मिलेन। स्वास्थ्य सँस्थामा भएको जन्मको लागि ‘स’ र घरमा भएको जन्मको लागि ‘घ’ प्रयोग गर्नुहोला। धन्यवाद। {{/delivery_code}}", - "locale": "ne" - } - ] - }, - { - "property": "days_since_birth", - "rule": "(integer && min(0) && max(50))", - "translation_key": "", - "message": [ - { - "content": "{{#days_since_birth}}नमस्ते {{contact.name}}! प्रसुति पश्चातको अवधि मिलेन, सो अवधि ० देखि ५० दिन भित्र हुनुपर्दछ। तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! ज <डेलिभरी कोड> {{patient_id}} <डेलिभरी भएको दिन>{{/days_since_birth}}{{^days_since_birth}}नमस्ते {{contact.name}}! प्रसुति पश्चातको अवधि ० देखि ५० दिन भित्र हुनुपर्दछ। तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! ज <डेलिभरी कोड> {{patient_id}} <डेलिभरी भएको दिन>{{/days_since_birth}}", - "locale": "ne" - } - ] - } - ] - }, - "messages": [ - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते! {{patient_name}} ({{patient_id}}) का लागि प्रसुति प्रतिवेदन प्राप्त भयो। १,३,७-१४ र ४२ दिनमा हुनु पर्ने सुत्केरी जाँचभेट एकिन गरी SMS बाट प {{patient_id}} <जाँचको पटक> पठाउनुहोला धन्यबाद!", - "locale": "ne" - } - ], - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते! {{contact.name}} बाट {{patient_name}} ({{patient_id}}) को प्रसुति प्रतिवेदन प्राप्त भयो। १,३,७-१४ र ४२ दिनमा हुनु पर्ने सुत्केरी जाँचभेट गर्न घर भेट गर्नु होला, धन्यबाद!", - "locale": "ne" - } - ], - "recipient": "parent" - }, - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! ज <डेलिभरी कोड><दर्ता नं (५ देखि १३ अंकको)> <डेलिभरी भएको दिन>", - "locale": "ne" - } - ], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "क", - "events": [ - { - "name": "on_create", - "trigger": "add_patient_id", - "params": "", - "bool_expr": "doc.patient_name" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "Registration Reminders", - "bool_expr": "doc.patient_name" - } - ], - "validations": { - "join_responses": false, - "list": [ - { - "property": "patient_age", - "rule": "(integer && min(0) && max(125))", - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! दर्ताको ढाँचा मिलेन। कृपया तलको ढाँचाअनुसार पुनः पठाउनुहोस्: क <उमेर (बर्षमा)> <नाम (बढीमा ३० अक्षर सम्मको)>", - "locale": "ne" - } - ] - }, - { - "property": "patient_name", - "rule": "(string && lenMin(1) && lenMax(30))", - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! दर्ताको ढाँचा मिलेन। कृपया तलको ढाँचाअनुसार पुनः पठाउनुहोस्: क <उमेर (बर्षमा)> <नाम (बढीमा ३० अक्षर सम्मको)>", - "locale": "ne" - } - ] - } - ] - }, - "messages": [ - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}, {{patient_name}} ({{patient_id}}) लाई दर्ता गर्नुभएकोमा धन्यवाद! ", - "locale": "" - } - ], - "recipient": "" - } - ] - }, - { - "form": "म", - "events": [ - { - "name": "on_create", - "trigger": "clear_schedule", - "params": "Registration Reminders, ANC Reminders LMP, PNC Reminders, Delivery Reminders", - "bool_expr": "doc.patient_id" - } - ], - "validations": { - "join_responses": false, - "list": [ - { - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [ - { - "content": "{{#patient_id}}नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! म <दर्ता नं (५ देखि १३ अंकको)>{{/patient_id}}{{^patient_id}}नमस्ते {{contact.name}}! तपाईले पठाउनु भएको रिपोर्टको ढाँचा मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! म <दर्ता नं (५ देखि १३ अंकको)>{{/patient_id}}", - "locale": "ne" - } - ] - } - ] - }, - "messages": [ - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}, {{patient_name}} {{patient_id}} को मृत्युको दु:खद घटनाको सूचना दिनुभएकोमा धन्यवाद। ", - "locale": "ne" - } - ], - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [ - { - "content": "{{contact.name}} ले {{patient_age}} बर्षिया {{patient_name}} ({{patient_id}}) को मृत्यु भएको सूचना दिनुभएको छ। ", - "locale": "ne" - } - ], - "recipient": "parent" - }, - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} कसैलाई पनि दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! म <दर्ता नं (५ देखि १३ अंकको)", - "locale": "ne" - } - ], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "A", - "events": [ - { - "name": "on_create", - "trigger": "add_expected_date", - "params": "lmp_date", - "bool_expr": "doc.last_menstrual_period" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "ANC Reminders LMP", - "bool_expr": "doc.last_menstrual_period" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "Delivery Reminders", - "bool_expr": "doc.last_menstrual_period" - }, - { - "name": "on_create", - "trigger": "clear_schedule", - "params": "Registration Reminders", - "bool_expr": "doc.last_menstrual_period" - } - ], - "validations": { - "join_responses": false, - "list": [ - { - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! A <दर्ता नं (५ देखि १३ अंकको)> <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)>", - "locale": "ne" - } - ] - }, - { - "property": "last_menstrual_period", - "rule": "(integer && min(6) && max(42))", - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_name}} {{patient_id}} को दर्ता मिलेन। अन्तिम महिनावारी भएको समय ६ देखि ४२ हप्ता भित्र भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! A {{patient_id}} <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)>", - "locale": "ne" - } - ] - } - ] - }, - "messages": [ - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}। {{patient_name}} {{patient_id}} को गर्भवस्था दर्ता गरेकोमा धन्यबाद! उहाँको प्रसुति अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}}", - "locale": "ne" - } - ], - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते! {{contact.name}} ले {{patient_name}} को गर्भवस्था दर्ता गर्नु भएको छ। उहाँको दर्ता नं {{patient_id}} र प्रसुती अनुमानित मिति {{#bikram_sambat_date}}{{expected_date}}{{/bikram_sambat_date}} हो। धन्यबाद!", - "locale": "ne" - } - ], - "event_type": "report_accepted", - "recipient": "parent" - }, - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} कसैलाई पनि दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! A <दर्ता नं (५ देखि १३ अंकको)> <अन्तिम महिनावारी भएको हप्ता (अंकमा, ६ देखि ४२ हप्ता भित्र)> ", - "locale": "ne" - } - ], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "L", - "events": [ - { - "name": "on_create", - "trigger": "add_expected_date", - "params": "lmp_date", - "bool_expr": "doc.lmp_date" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "ANC Reminders LMP", - "bool_expr": "doc.lmp_date" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "Delivery Reminders", - "bool_expr": "doc.lmp_date" - }, - { - "name": "on_create", - "trigger": "clear_schedule", - "params": "Registration Reminders", - "bool_expr": "doc.lmp_date" - } - ], - "validations": { - "join_responses": false, - "list": [ - { - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "registration.lmp_date.patient_id.incorrect" - }, - { - "property": "lmp_year", - "rule": "(integer && min(2078) && max(2090))", - "translation_key": "registration.lmp_date.year.incorrect" - }, - { - "property": "lmp_month", - "rule": "(integer && min(1) && max(12))", - "translation_key": "registration.lmp_date.month.incorrect" - }, - { - "property": "lmp_day", - "rule": "(integer && min(1) && max(32))", - "translation_key": "registration.lmp_date.day.incorrect" - }, - { - "property": "lmp_date", - "rule": "isBefore('1 month')", - "translation_key": "messages.registration.lmp_date.incorrect.before" - }, - { - "property": "lmp_date", - "rule": "isAfter('-9 months')", - "translation_key": "messages.registration.lmp_date.incorrect.after" - } - ] - }, - "messages": [ - { - "translation_key": "registration.lmp_date.ok", - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "registration.lmp_date.ok.health_center", - "event_type": "report_accepted", - "recipient": "health_center" - }, - { - "translation_key": "registration.lmp_date.patient_id.not_found", - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "J", - "events": [ - { - "name": "on_create", - "trigger": "add_birth_date", - "params": "birth_date", - "bool_expr": "doc.days_since_birth" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "PNC Reminders", - "bool_expr": "doc.days_since_birth" - }, - { - "name": "on_create", - "trigger": "clear_schedule", - "params": "Delivery Reminders, ANC Reminders LMP", - "bool_expr": "doc.patient_id" - } - ], - "validations": { - "join_responses": false, - "list": [ - { - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! J <डेलिभरी कोड><दर्ता नं (५ देखि १३ अंकको)> <डेलिभरी भएको दिन>", - "locale": "ne" - } - ] - }, - { - "property": "delivery_code", - "rule": "regex('^(S|A|s|a)$')", - "translation_key": "", - "message": [ - { - "content": "{{#delivery_code}} नमस्ते {{contact.name}}! {{delivery_code}} डेलिभरी कोड मिलेन। स्वास्थ्य सँस्थामा भएको जन्मको लागि ‘S’ र घरमा भएको जन्मको लागि ‘A’ प्रयोग गर्नुहोला। धन्यवाद। {{/delivery_code}}", - "locale": "ne" - } - ] - }, - { - "property": "days_since_birth", - "rule": "(integer && min(0) && max(50))", - "translation_key": "", - "message": [ - { - "content": "{{#days_since_birth}}नमस्ते {{contact.name}}! प्रसुति पश्चातको अवधि मिलेन, सो अवधि ० देखि ५० दिन भित्र हुनुपर्दछ। तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! J <डेलिभरी कोड> {{patient_id}} <डेलिभरी भएको दिन>{{/days_since_birth}}{{^days_since_birth}}नमस्ते {{contact.name}}! प्रसुति पश्चातको अवधि ० देखि ५० दिन भित्र हुनुपर्दछ। तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! J <डेलिभरी कोड> {{patient_id}} <डेलिभरी भएको दिन>{{/days_since_birth}}", - "locale": "ne" - } - ] - } - ] - }, - "messages": [ - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते! {{patient_name}} ({{patient_id}}) का लागि प्रसुति प्रतिवेदन प्राप्त भयो। १,३,७-१४ र ४२ दिनमा हुनु पर्ने सुत्केरी जाँचभेट एकिन गरी SMS बाट P {{patient_id}} <जाँचको पटक> पठाउनुहोला धन्यबाद!", - "locale": "ne" - } - ], - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते! {{contact.name}} बाट {{patient_name}} ({{patient_id}}) को प्रसुति प्रतिवेदन प्राप्त भयो। १,३,७-१४ र ४२ दिनमा हुनु पर्ने सुत्केरी जाँचभेट गर्न घर भेट गर्नु होला, धन्यबाद!", - "locale": "ne" - } - ], - "recipient": "parent" - }, - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! J <डेलिभरी कोड><दर्ता नं (५ देखि १३ अंकको)> <डेलिभरी भएको दिन>", - "locale": "ne" - } - ], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "K", - "events": [ - { - "name": "on_create", - "trigger": "add_patient_id", - "params": "", - "bool_expr": "doc.patient_name" - }, - { - "name": "on_create", - "trigger": "assign_schedule", - "params": "Registration Reminders", - "bool_expr": "doc.patient_name" - } - ], - "validations": { - "join_responses": false, - "list": [ - { - "property": "patient_age", - "rule": "(integer && min(0) && max(125))", - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! दर्ताको ढाँचा मिलेन। कृपया तलको ढाँचाअनुसार पुनः पठाउनुहोस्: K <उमेर (बर्षमा)> <नाम (बढीमा ३० अक्षर सम्मको)>", - "locale": "ne" - } - ] - }, - { - "property": "patient_name", - "rule": "(string && lenMin(1) && lenMax(30))", - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! दर्ताको ढाँचा मिलेन। कृपया तलको ढाँचाअनुसार पुनः पठाउनुहोस्: K <उमेर (बर्षमा)> <नाम (बढीमा ३० अक्षर सम्मको)>", - "locale": "ne" - } - ] - } - ] - }, - "messages": [ - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}, {{patient_name}} ({{patient_id}}) लाई दर्ता गर्नुभएकोमा धन्यवाद! ", - "locale": "" - } - ], - "recipient": "" - } - ] - }, - { - "form": "M", - "events": [ - { - "name": "on_create", - "trigger": "clear_schedule", - "params": "Registration Reminders, ANC Reminders LMP, PNC Reminders, Delivery Reminders", - "bool_expr": "doc.patient_id" - } - ], - "validations": { - "join_responses": false, - "list": [ - { - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [ - { - "content": "{{#patient_id}}नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! M <दर्ता नं (५ देखि १३ अंकको)>{{/patient_id}}{{^patient_id}}नमस्ते {{contact.name}}! तपाईले पठाउनु भएको रिपोर्टको ढाँचा मिलेन। ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! M <दर्ता नं (५ देखि १३ अंकको)>{{/patient_id}}", - "locale": "ne" - } - ] - } - ] - }, - "messages": [ - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}, {{patient_name}} {{patient_id}} को मृत्युको दु:खद घटनाको सूचना दिनुभएकोमा धन्यवाद। ", - "locale": "ne" - } - ], - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [ - { - "content": "{{contact.name}} ले {{patient_age}} बर्षिया {{patient_name}} ({{patient_id}}) को मृत्यु भएको सूचना दिनुभएको छ। ", - "locale": "ne" - } - ], - "recipient": "parent" - }, - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} कसैलाई पनि दिईएको छैन। दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! म <दर्ता नं (५ देखि १३ अंकको)", - "locale": "ne" - } - ], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - } + "can_create_places": [ + "program_officer", + "chw_supervisor", + "chw" ], - "patient_reports": [ - { - "form": "ग", - "name": "Visits", - "format": "ग ", - "silence_type": "ANC Reminders LMP", - "silence_for": "0 days", - "fields": [ - { - "field_name": "", - "title": "" - } - ], - "validations": { - "join_responses": false, - "list": [ - { - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! ग <दर्ता नं (५ देखि १३ अंकको)>", - "locale": "ne" - } - ] - } - ] - }, - "messages": [ - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_name}} ({{patient_id}}) लाई भेटेकोमा धन्यबाद!", - "locale": "ne" - } - ], - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन।दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! ग <दर्ता नं (५ देखि १३ अंकको)>", - "locale": "ne" - } - ], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, - { - "form": "ख", - "name": "Flag", - "format": "ख ", - "silence_type": "", - "silence_for": "", - "fields": [ - { - "field_name": "patient_id", - "title": "Patient ID" - } - ], - "validations": { - "join_responses": false, - "list": [ - { - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [ - { - "content": "{{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! ख <दर्ता नं (५ देखि १३ अंकको)>", - "locale": "ne" - } - ] - } - ] - }, - "messages": [ - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते! {{patient_name}} {{patient_id}}का लागि खतराको चिन्ह पहिचान भएको रिपोर्ट प्राप्त भयो। उहाँलाई बिशेष निगरानीमा राखी आवश्यकता अनुसार स्वास्थ्य संस्था पठाउनुहोला।", - "locale": "ne" - } - ], - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते! {{contact.name}}ले {{patient_name}} {{patient_id}} का लागि खतराको चिन्ह पहिचान गर्नुभएको छ । म.स्वा.स्व.से.संग सोधखोज गरि आवश्यक सल्लाह दिनुहोला |", - "locale": "ne" - } - ], - "event_type": "report_accepted", - "recipient": "parent" - }, - { - "translation_key": "", - "message": [ - { - "content": "{{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी पुन: फारम पठाउनुहोला, धन्यबाद!", - "locale": "ne" - } - ], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - }, + "can_create_records": [ + "data_entry", + "gateway", + "program_officer", + "chw_supervisor", + "chw" + ], + "can_create_users": [ + "program_officer" + ], + "can_delete_contacts": [ + "program_officer", + "chw_supervisor", + "chw" + ], + "can_delete_messages": [ + "program_officer", + "chw_supervisor", + "chw" + ], + "can_delete_reports": [ + "program_officer", + "chw_supervisor", + "chw" + ], + "can_delete_users": [ + "program_officer" + ], + "can_edit": [ + "gateway", + "program_officer", + "chw_supervisor", + "chw" + ], + "can_edit_profile": [ + "program_officer", + "chw_supervisor", + "chw" + ], + "can_edit_verification": [ + "program_officer", + "chw_supervisor", + "chw" + ], + "can_export_all": [ + "program_officer", + "crfo" + ], + "can_export_contacts": [ + "program_officer", + "chw_supervisor", + "chw" + ], + "can_export_feedback": [ + "program_officer" + ], + "can_export_messages": [ + "analytics", + "program_officer", + "chw_supervisor", + "chw" + ], + "can_log_out_on_android": [], + "can_update_places": [ + "program_officer", + "chw_supervisor", + "chw" + ], + "can_update_reports": [ + "program_officer", + "chw_supervisor", + "chw" + ], + "can_update_users": [ + "program_officer" + ], + "can_export_dhis": [ + "national_admin", + "crfo" + ], + "can_verify_reports": [ + "program_officer", + "chw_supervisor", + "chw" + ], + "can_view_analytics": [ + "analytics", + "program_officer", + "chw_supervisor", + "chw" + ], + "can_view_analytics_tab": [ + "program_officer", + "chw_supervisor", + "chw" + ], + "can_view_call_action": [ + "program_officer", + "chw_supervisor", + "chw" + ], + "can_view_contacts": [ + "program_officer", + "chw_supervisor", + "chw" + ], + "can_view_contacts_tab": [ + "program_officer", + "chw_supervisor", + "chw" + ], + "can_view_last_visited_date": [], + "can_view_uhc_stats": [], + "can_view_message_action": [ + "program_officer", + "chw_supervisor", + "chw" + ], + "can_view_messages": [ + "program_officer", + "chw_supervisor", + "chw" + ], + "can_view_messages_tab": [ + "program_officer", + "chw_supervisor", + "chw" + ], + "can_view_outgoing_messages": [ + "program_officer" + ], + "can_view_reports": [ + "program_officer", + "chw_supervisor", + "chw" + ], + "can_view_reports_tab": [ + "program_officer", + "chw_supervisor", + "chw" + ], + "can_view_tasks": [ + "chw_supervisor", + "chw" + ], + "can_view_tasks_tab": [ + "chw_supervisor", + "chw" + ], + "can_view_tasks_group": [ + "chw" + ], + "can_view_unallocated_data_records": [ + "gateway", + "program_officer", + "chw_supervisor", + "chw" + ], + "can_view_users": [ + "program_officer" + ], + "can_write_wealth_quintiles": [], + "can_aggregate_targets": [ + "chw_supervisor" + ], + "can_upgrade": [ + "program_officer" + ], + "can_view_old_filter_and_search": [], + "can_view_old_action_bar": [] + }, + "uhc": { + "contacts_default_sort": "", + "visit_count": { + "month_start_date": 1, + "visit_count_goal": 0 + } + }, + "death_reporting": { + "mark_deceased_forms": [ + "death_report" + ], + "date_field": "fields.death_details.date_of_death", + "undo_deceased_forms": [ + "undo_death_report" + ] + }, + "contact_types": [ + { + "id": "district_hospital", + "name_key": "contact.type.district_hospital", + "group_key": "contact.type.district_hospital.plural", + "create_key": "contact.type.district_hospital.new", + "edit_key": "contact.type.place.edit", + "icon": "medic-district-hospital", + "create_form": "form:contact:district_hospital:create", + "edit_form": "form:contact:district_hospital:edit" + }, + { + "id": "health_center", + "name_key": "contact.type.health_center", + "group_key": "contact.type.health_center.plural", + "create_key": "contact.type.health_center.new", + "edit_key": "contact.type.place.edit", + "parents": [ + "district_hospital" + ], + "icon": "medic-health-center", + "create_form": "form:contact:health_center:create", + "edit_form": "form:contact:health_center:edit" + }, + { + "id": "clinic", + "name_key": "contact.type.clinic", + "group_key": "contact.type.clinic.plural", + "create_key": "contact.type.clinic.new", + "edit_key": "contact.type.place.edit", + "parents": [ + "health_center" + ], + "icon": "medic-clinic", + "create_form": "form:contact:clinic:create", + "edit_form": "form:contact:clinic:edit", + "count_visits": true + }, + { + "id": "person", + "name_key": "contact.type.person", + "group_key": "contact.type.person.plural", + "create_key": "contact.type.person.new", + "edit_key": "contact.type.person.edit", + "primary_contact_key": "clinic.field.contact", + "parents": [ + "district_hospital", + "health_center", + "clinic" + ], + "icon": "medic-person", + "create_form": "form:contact:person:create", + "edit_form": "form:contact:person:edit", + "person": true + } + ], + "contact_summary": "var ContactSummary = {}; !function(e,t){if('object'==typeof exports&&'object'==typeof module)module.exports=t();else if('function'==typeof define&&define.amd)define([],t);else{var n=t();for(var r in n)('object'==typeof exports?exports:e)[r]=n[r]}}(ContactSummary,(function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){'undefined'!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:'Module'}),Object.defineProperty(e,'__esModule',{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&'object'==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,'default',{enumerable:!0,value:e}),2&t&&'string'!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,'a',t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p='',n(n.s=1)}([function(e,t,n){(function(e){e.exports=function(){'use strict';var t,n;function r(){return t.apply(null,arguments)}function i(e){return e instanceof Array||'[object Array]'===Object.prototype.toString.call(e)}function s(e){return null!=e&&'[object Object]'===Object.prototype.toString.call(e)}function a(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function o(e){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(e).length;var t;for(t in e)if(a(e,t))return!1;return!0}function l(e){return void 0===e}function u(e){return'number'==typeof e||'[object Number]'===Object.prototype.toString.call(e)}function c(e){return e instanceof Date||'[object Date]'===Object.prototype.toString.call(e)}function d(e,t){var n,r=[];for(n=0;n>>0;for(t=0;t0)for(n=0;n=0?n?'+':'':'-')+Math.pow(10,Math.max(0,i)).toString().substr(1)+r}r.suppressDeprecationWarnings=!1,r.deprecationHandler=null,M=Object.keys?Object.keys:function(e){var t,n=[];for(t in e)a(e,t)&&n.push(t);return n};var P=/(\\[[^\\[]*\\])|(\\\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,R=/(\\[[^\\[]*\\])|(\\\\)?(LTS|LT|LL?L?L?|l{1,4})/g,W={},C={};function F(e,t,n,r){var i=r;'string'==typeof r&&(i=function(){return this[r]()}),e&&(C[e]=i),t&&(C[t[0]]=function(){return N(i.apply(this,arguments),t[1],t[2])}),n&&(C[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),e)})}function A(e,t){return e.isValid()?(t=E(t,e.localeData()),W[t]=W[t]||function(e){var t,n,r,i=e.match(P);for(t=0,n=i.length;t=0&&R.test(e);)e=e.replace(R,r),R.lastIndex=0,n-=1;return e}var U={};function H(e,t){var n=e.toLowerCase();U[n]=U[n+'s']=U[t]=e}function L(e){return'string'==typeof e?U[e]||U[e.toLowerCase()]:void 0}function V(e){var t,n,r={};for(n in e)a(e,n)&&(t=L(n))&&(r[t]=e[n]);return r}var I={};function j(e,t){I[e]=t}function G(e){return e%4==0&&e%100!=0||e%400==0}function Z(e){return e<0?Math.ceil(e)||0:Math.floor(e)}function z(e){var t=+e,n=0;return 0!==t&&isFinite(t)&&(n=Z(t)),n}function q(e,t){return function(n){return null!=n?(B(this,e,n),r.updateOffset(this,t),this):$(this,e)}}function $(e,t){return e.isValid()?e._d['get'+(e._isUTC?'UTC':'')+t]():NaN}function B(e,t,n){e.isValid()&&!isNaN(n)&&('FullYear'===t&&G(e.year())&&1===e.month()&&29===e.date()?(n=z(n),e._d['set'+(e._isUTC?'UTC':'')+t](n,e.month(),ke(n,e.month()))):e._d['set'+(e._isUTC?'UTC':'')+t](n))}var J,Q=/\\d/,X=/\\d\\d/,K=/\\d{3}/,ee=/\\d{4}/,te=/[+-]?\\d{6}/,ne=/\\d\\d?/,re=/\\d\\d\\d\\d?/,ie=/\\d\\d\\d\\d\\d\\d?/,se=/\\d{1,3}/,ae=/\\d{1,4}/,oe=/[+-]?\\d{1,6}/,le=/\\d+/,ue=/[+-]?\\d+/,ce=/Z|[+-]\\d\\d:?\\d\\d/gi,de=/Z|[+-]\\d\\d(?::?\\d\\d)?/gi,he=/[0-9]{0,256}['a-z\\u00A0-\\u05FF\\u0700-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFF07\\uFF10-\\uFFEF]{1,256}|[\\u0600-\\u06FF\\/]{1,256}(\\s*?[\\u0600-\\u06FF]{1,256}){1,2}/i;function fe(e,t,n){J[e]=O(t)?t:function(e,r){return e&&n?n:t}}function _e(e,t){return a(J,e)?J[e](t._strict,t._locale):new RegExp(me(e.replace('\\\\','').replace(/\\\\(\\[)|\\\\(\\])|\\[([^\\]\\[]*)\\]|\\\\(.)/g,(function(e,t,n,r,i){return t||n||r||i}))))}function me(e){return e.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g,'\\\\$&')}J={};var pe,ye={};function ge(e,t){var n,r=t;for('string'==typeof e&&(e=[e]),u(t)&&(r=function(e,n){n[t]=z(e)}),n=0;n68?1900:2e3)};var Re=q('FullYear',!0);function We(e,t,n,r,i,s,a){var o;return e<100&&e>=0?(o=new Date(e+400,t,n,r,i,s,a),isFinite(o.getFullYear())&&o.setFullYear(e)):o=new Date(e,t,n,r,i,s,a),o}function Ce(e){var t,n;return e<100&&e>=0?((n=Array.prototype.slice.call(arguments))[0]=e+400,t=new Date(Date.UTC.apply(null,n)),isFinite(t.getUTCFullYear())&&t.setUTCFullYear(e)):t=new Date(Date.UTC.apply(null,arguments)),t}function Fe(e,t,n){var r=7+t-n;return-(7+Ce(e,0,r).getUTCDay()-t)%7+r-1}function Ae(e,t,n,r,i){var s,a,o=1+7*(t-1)+(7+n-r)%7+Fe(e,r,i);return o<=0?a=Pe(s=e-1)+o:o>Pe(e)?(s=e+1,a=o-Pe(e)):(s=e,a=o),{year:s,dayOfYear:a}}function Ee(e,t,n){var r,i,s=Fe(e.year(),t,n),a=Math.floor((e.dayOfYear()-s-1)/7)+1;return a<1?r=a+Ue(i=e.year()-1,t,n):a>Ue(e.year(),t,n)?(r=a-Ue(e.year(),t,n),i=e.year()+1):(i=e.year(),r=a),{week:r,year:i}}function Ue(e,t,n){var r=Fe(e,t,n),i=Fe(e+1,t,n);return(Pe(e)-r+i)/7}function He(e,t){return e.slice(t,7).concat(e.slice(0,t))}F('w',['ww',2],'wo','week'),F('W',['WW',2],'Wo','isoWeek'),H('week','w'),H('isoWeek','W'),j('week',5),j('isoWeek',5),fe('w',ne),fe('ww',ne,X),fe('W',ne),fe('WW',ne,X),ve(['w','ww','W','WW'],(function(e,t,n,r){t[r.substr(0,1)]=z(e)})),F('d',0,'do','day'),F('dd',0,0,(function(e){return this.localeData().weekdaysMin(this,e)})),F('ddd',0,0,(function(e){return this.localeData().weekdaysShort(this,e)})),F('dddd',0,0,(function(e){return this.localeData().weekdays(this,e)})),F('e',0,0,'weekday'),F('E',0,0,'isoWeekday'),H('day','d'),H('weekday','e'),H('isoWeekday','E'),j('day',11),j('weekday',11),j('isoWeekday',11),fe('d',ne),fe('e',ne),fe('E',ne),fe('dd',(function(e,t){return t.weekdaysMinRegex(e)})),fe('ddd',(function(e,t){return t.weekdaysShortRegex(e)})),fe('dddd',(function(e,t){return t.weekdaysRegex(e)})),ve(['dd','ddd','dddd'],(function(e,t,n,r){var i=n._locale.weekdaysParse(e,r,n._strict);null!=i?t.d=i:_(n).invalidWeekday=e})),ve(['d','e','E'],(function(e,t,n,r){t[r]=z(e)}));var Le='Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),Ve='Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),Ie='Su_Mo_Tu_We_Th_Fr_Sa'.split('_'),je=he,Ge=he,Ze=he;function ze(e,t,n){var r,i,s,a=e.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],r=0;r<7;++r)s=f([2e3,1]).day(r),this._minWeekdaysParse[r]=this.weekdaysMin(s,'').toLocaleLowerCase(),this._shortWeekdaysParse[r]=this.weekdaysShort(s,'').toLocaleLowerCase(),this._weekdaysParse[r]=this.weekdays(s,'').toLocaleLowerCase();return n?'dddd'===t?-1!==(i=pe.call(this._weekdaysParse,a))?i:null:'ddd'===t?-1!==(i=pe.call(this._shortWeekdaysParse,a))?i:null:-1!==(i=pe.call(this._minWeekdaysParse,a))?i:null:'dddd'===t?-1!==(i=pe.call(this._weekdaysParse,a))||-1!==(i=pe.call(this._shortWeekdaysParse,a))||-1!==(i=pe.call(this._minWeekdaysParse,a))?i:null:'ddd'===t?-1!==(i=pe.call(this._shortWeekdaysParse,a))||-1!==(i=pe.call(this._weekdaysParse,a))||-1!==(i=pe.call(this._minWeekdaysParse,a))?i:null:-1!==(i=pe.call(this._minWeekdaysParse,a))||-1!==(i=pe.call(this._weekdaysParse,a))||-1!==(i=pe.call(this._shortWeekdaysParse,a))?i:null}function qe(){function e(e,t){return t.length-e.length}var t,n,r,i,s,a=[],o=[],l=[],u=[];for(t=0;t<7;t++)n=f([2e3,1]).day(t),r=me(this.weekdaysMin(n,'')),i=me(this.weekdaysShort(n,'')),s=me(this.weekdays(n,'')),a.push(r),o.push(i),l.push(s),u.push(r),u.push(i),u.push(s);a.sort(e),o.sort(e),l.sort(e),u.sort(e),this._weekdaysRegex=new RegExp('^('+u.join('|')+')','i'),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp('^('+l.join('|')+')','i'),this._weekdaysShortStrictRegex=new RegExp('^('+o.join('|')+')','i'),this._weekdaysMinStrictRegex=new RegExp('^('+a.join('|')+')','i')}function $e(){return this.hours()%12||12}function Be(e,t){F(e,0,0,(function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)}))}function Je(e,t){return t._meridiemParse}F('H',['HH',2],0,'hour'),F('h',['hh',2],0,$e),F('k',['kk',2],0,(function(){return this.hours()||24})),F('hmm',0,0,(function(){return''+$e.apply(this)+N(this.minutes(),2)})),F('hmmss',0,0,(function(){return''+$e.apply(this)+N(this.minutes(),2)+N(this.seconds(),2)})),F('Hmm',0,0,(function(){return''+this.hours()+N(this.minutes(),2)})),F('Hmmss',0,0,(function(){return''+this.hours()+N(this.minutes(),2)+N(this.seconds(),2)})),Be('a',!0),Be('A',!1),H('hour','h'),j('hour',13),fe('a',Je),fe('A',Je),fe('H',ne),fe('h',ne),fe('k',ne),fe('HH',ne,X),fe('hh',ne,X),fe('kk',ne,X),fe('hmm',re),fe('hmmss',ie),fe('Hmm',re),fe('Hmmss',ie),ge(['H','HH'],3),ge(['k','kk'],(function(e,t,n){var r=z(e);t[3]=24===r?0:r})),ge(['a','A'],(function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e})),ge(['h','hh'],(function(e,t,n){t[3]=z(e),_(n).bigHour=!0})),ge('hmm',(function(e,t,n){var r=e.length-2;t[3]=z(e.substr(0,r)),t[4]=z(e.substr(r)),_(n).bigHour=!0})),ge('hmmss',(function(e,t,n){var r=e.length-4,i=e.length-2;t[3]=z(e.substr(0,r)),t[4]=z(e.substr(r,2)),t[5]=z(e.substr(i)),_(n).bigHour=!0})),ge('Hmm',(function(e,t,n){var r=e.length-2;t[3]=z(e.substr(0,r)),t[4]=z(e.substr(r))})),ge('Hmmss',(function(e,t,n){var r=e.length-4,i=e.length-2;t[3]=z(e.substr(0,r)),t[4]=z(e.substr(r,2)),t[5]=z(e.substr(i))}));var Qe,Xe=q('Hours',!0),Ke={calendar:{sameDay:'[Today at] LT',nextDay:'[Tomorrow at] LT',nextWeek:'dddd [at] LT',lastDay:'[Yesterday at] LT',lastWeek:'[Last] dddd [at] LT',sameElse:'L'},longDateFormat:{LTS:'h:mm:ss A',LT:'h:mm A',L:'MM/DD/YYYY',LL:'MMMM D, YYYY',LLL:'MMMM D, YYYY h:mm A',LLLL:'dddd, MMMM D, YYYY h:mm A'},invalidDate:'Invalid date',ordinal:'%d',dayOfMonthOrdinalParse:/\\d{1,2}/,relativeTime:{future:'in %s',past:'%s ago',s:'a few seconds',ss:'%d seconds',m:'a minute',mm:'%d minutes',h:'an hour',hh:'%d hours',d:'a day',dd:'%d days',w:'a week',ww:'%d weeks',M:'a month',MM:'%d months',y:'a year',yy:'%d years'},months:De,monthsShort:be,week:{dow:0,doy:6},weekdays:Le,weekdaysMin:Ie,weekdaysShort:Ve,meridiemParse:/[ap]\\.?m?\\.?/i},et={},tt={};function nt(e,t){var n,r=Math.min(e.length,t.length);for(n=0;n0;){if(r=it(i.slice(0,t).join('-')))return r;if(n&&n.length>=t&&nt(i,n)>=t-1)break;t--}s++}return Qe}(e)}function lt(e){var t,n=e._a;return n&&-2===_(e).overflow&&(t=n[1]<0||n[1]>11?1:n[2]<1||n[2]>ke(n[0],n[1])?2:n[3]<0||n[3]>24||24===n[3]&&(0!==n[4]||0!==n[5]||0!==n[6])?3:n[4]<0||n[4]>59?4:n[5]<0||n[5]>59?5:n[6]<0||n[6]>999?6:-1,_(e)._overflowDayOfYear&&(t<0||t>2)&&(t=2),_(e)._overflowWeeks&&-1===t&&(t=7),_(e)._overflowWeekday&&-1===t&&(t=8),_(e).overflow=t),e}var ut=/^\\s*((?:[+-]\\d{6}|\\d{4})-(?:\\d\\d-\\d\\d|W\\d\\d-\\d|W\\d\\d|\\d\\d\\d|\\d\\d))(?:(T| )(\\d\\d(?::\\d\\d(?::\\d\\d(?:[.,]\\d+)?)?)?)([+-]\\d\\d(?::?\\d\\d)?|\\s*Z)?)?$/,ct=/^\\s*((?:[+-]\\d{6}|\\d{4})(?:\\d\\d\\d\\d|W\\d\\d\\d|W\\d\\d|\\d\\d\\d|\\d\\d|))(?:(T| )(\\d\\d(?:\\d\\d(?:\\d\\d(?:[.,]\\d+)?)?)?)([+-]\\d\\d(?::?\\d\\d)?|\\s*Z)?)?$/,dt=/Z|[+-]\\d\\d(?::?\\d\\d)?/,ht=[['YYYYYY-MM-DD',/[+-]\\d{6}-\\d\\d-\\d\\d/],['YYYY-MM-DD',/\\d{4}-\\d\\d-\\d\\d/],['GGGG-[W]WW-E',/\\d{4}-W\\d\\d-\\d/],['GGGG-[W]WW',/\\d{4}-W\\d\\d/,!1],['YYYY-DDD',/\\d{4}-\\d{3}/],['YYYY-MM',/\\d{4}-\\d\\d/,!1],['YYYYYYMMDD',/[+-]\\d{10}/],['YYYYMMDD',/\\d{8}/],['GGGG[W]WWE',/\\d{4}W\\d{3}/],['GGGG[W]WW',/\\d{4}W\\d{2}/,!1],['YYYYDDD',/\\d{7}/],['YYYYMM',/\\d{6}/,!1],['YYYY',/\\d{4}/,!1]],ft=[['HH:mm:ss.SSSS',/\\d\\d:\\d\\d:\\d\\d\\.\\d+/],['HH:mm:ss,SSSS',/\\d\\d:\\d\\d:\\d\\d,\\d+/],['HH:mm:ss',/\\d\\d:\\d\\d:\\d\\d/],['HH:mm',/\\d\\d:\\d\\d/],['HHmmss.SSSS',/\\d\\d\\d\\d\\d\\d\\.\\d+/],['HHmmss,SSSS',/\\d\\d\\d\\d\\d\\d,\\d+/],['HHmmss',/\\d\\d\\d\\d\\d\\d/],['HHmm',/\\d\\d\\d\\d/],['HH',/\\d\\d/]],_t=/^\\/?Date\\((-?\\d+)/i,mt=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\\s)?(\\d{1,2})\\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s(\\d{2,4})\\s(\\d\\d):(\\d\\d)(?::(\\d\\d))?\\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\\d{4}))$/,pt={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function yt(e){var t,n,r,i,s,a,o=e._i,l=ut.exec(o)||ct.exec(o);if(l){for(_(e).iso=!0,t=0,n=ht.length;t7)&&(l=!0)):(s=e._locale._week.dow,a=e._locale._week.doy,u=Ee(St(),s,a),n=wt(t.gg,e._a[0],u.year),r=wt(t.w,u.week),null!=t.d?((i=t.d)<0||i>6)&&(l=!0):null!=t.e?(i=t.e+s,(t.e<0||t.e>6)&&(l=!0)):i=s),r<1||r>Ue(n,s,a)?_(e)._overflowWeeks=!0:null!=l?_(e)._overflowWeekday=!0:(o=Ae(n,r,i,s,a),e._a[0]=o.year,e._dayOfYear=o.dayOfYear)}(e),null!=e._dayOfYear&&(a=wt(e._a[0],i[0]),(e._dayOfYear>Pe(a)||0===e._dayOfYear)&&(_(e)._overflowDayOfYear=!0),n=Ce(a,0,e._dayOfYear),e._a[1]=n.getUTCMonth(),e._a[2]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=o[t]=i[t];for(;t<7;t++)e._a[t]=o[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[3]&&0===e._a[4]&&0===e._a[5]&&0===e._a[6]&&(e._nextDay=!0,e._a[3]=0),e._d=(e._useUTC?Ce:We).apply(null,o),s=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[3]=24),e._w&&void 0!==e._w.d&&e._w.d!==s&&(_(e).weekdayMismatch=!0)}}function Dt(e){if(e._f!==r.ISO_8601)if(e._f!==r.RFC_2822){e._a=[],_(e).empty=!0;var t,n,i,s,a,o,l=''+e._i,u=l.length,c=0;for(i=E(e._f,e._locale).match(P)||[],t=0;t0&&_(e).unusedInput.push(a),l=l.slice(l.indexOf(n)+n.length),c+=n.length),C[s]?(n?_(e).empty=!1:_(e).unusedTokens.push(s),we(s,n,e)):e._strict&&!n&&_(e).unusedTokens.push(s);_(e).charsLeftOver=u-c,l.length>0&&_(e).unusedInput.push(l),e._a[3]<=12&&!0===_(e).bigHour&&e._a[3]>0&&(_(e).bigHour=void 0),_(e).parsedDateParts=e._a.slice(0),_(e).meridiem=e._meridiem,e._a[3]=function(e,t,n){var r;return null==n?t:null!=e.meridiemHour?e.meridiemHour(t,n):null!=e.isPM?((r=e.isPM(n))&&t<12&&(t+=12),r||12!==t||(t=0),t):t}(e._locale,e._a[3],e._meridiem),null!==(o=_(e).era)&&(e._a[0]=e._locale.erasConvertYear(o,e._a[0])),kt(e),lt(e)}else vt(e);else yt(e)}function bt(e){var t=e._i,n=e._f;return e._locale=e._locale||ot(e._l),null===t||void 0===n&&''===t?p({nullInput:!0}):('string'==typeof t&&(e._i=t=e._locale.preparse(t)),k(t)?new w(lt(t)):(c(t)?e._d=t:i(n)?function(e){var t,n,r,i,s,a,o=!1;if(0===e._f.length)return _(e).invalidFormat=!0,void(e._d=new Date(NaN));for(i=0;ithis?this:e:p()}));function xt(e,t){var n,r;if(1===t.length&&i(t[0])&&(t=t[0]),!t.length)return St();for(n=t[0],r=1;r=0?new Date(e+400,t,n)-126227808e5:new Date(e,t,n).valueOf()}function rn(e,t,n){return e<100&&e>=0?Date.UTC(e+400,t,n)-126227808e5:Date.UTC(e,t,n)}function sn(e,t){return t.erasAbbrRegex(e)}function an(){var e,t,n=[],r=[],i=[],s=[],a=this.eras();for(e=0,t=a.length;e(s=Ue(e,r,i))&&(t=s),un.call(this,e,t,n,r,i))}function un(e,t,n,r,i){var s=Ae(e,t,n,r,i),a=Ce(s.year,0,s.dayOfYear);return this.year(a.getUTCFullYear()),this.month(a.getUTCMonth()),this.date(a.getUTCDate()),this}F('N',0,0,'eraAbbr'),F('NN',0,0,'eraAbbr'),F('NNN',0,0,'eraAbbr'),F('NNNN',0,0,'eraName'),F('NNNNN',0,0,'eraNarrow'),F('y',['y',1],'yo','eraYear'),F('y',['yy',2],0,'eraYear'),F('y',['yyy',3],0,'eraYear'),F('y',['yyyy',4],0,'eraYear'),fe('N',sn),fe('NN',sn),fe('NNN',sn),fe('NNNN',(function(e,t){return t.erasNameRegex(e)})),fe('NNNNN',(function(e,t){return t.erasNarrowRegex(e)})),ge(['N','NN','NNN','NNNN','NNNNN'],(function(e,t,n,r){var i=n._locale.erasParse(e,r,n._strict);i?_(n).era=i:_(n).invalidEra=e})),fe('y',le),fe('yy',le),fe('yyy',le),fe('yyyy',le),fe('yo',(function(e,t){return t._eraYearOrdinalRegex||le})),ge(['y','yy','yyy','yyyy'],0),ge(['yo'],(function(e,t,n,r){var i;n._locale._eraYearOrdinalRegex&&(i=e.match(n._locale._eraYearOrdinalRegex)),n._locale.eraYearOrdinalParse?t[0]=n._locale.eraYearOrdinalParse(e,i):t[0]=parseInt(e,10)})),F(0,['gg',2],0,(function(){return this.weekYear()%100})),F(0,['GG',2],0,(function(){return this.isoWeekYear()%100})),on('gggg','weekYear'),on('ggggg','weekYear'),on('GGGG','isoWeekYear'),on('GGGGG','isoWeekYear'),H('weekYear','gg'),H('isoWeekYear','GG'),j('weekYear',1),j('isoWeekYear',1),fe('G',ue),fe('g',ue),fe('GG',ne,X),fe('gg',ne,X),fe('GGGG',ae,ee),fe('gggg',ae,ee),fe('GGGGG',oe,te),fe('ggggg',oe,te),ve(['gggg','ggggg','GGGG','GGGGG'],(function(e,t,n,r){t[r.substr(0,2)]=z(e)})),ve(['gg','GG'],(function(e,t,n,i){t[i]=r.parseTwoDigitYear(e)})),F('Q',0,'Qo','quarter'),H('quarter','Q'),j('quarter',7),fe('Q',Q),ge('Q',(function(e,t){t[1]=3*(z(e)-1)})),F('D',['DD',2],'Do','date'),H('date','D'),j('date',9),fe('D',ne),fe('DD',ne,X),fe('Do',(function(e,t){return e?t._dayOfMonthOrdinalParse||t._ordinalParse:t._dayOfMonthOrdinalParseLenient})),ge(['D','DD'],2),ge('Do',(function(e,t){t[2]=z(e.match(ne)[0])}));var cn=q('Date',!0);F('DDD',['DDDD',3],'DDDo','dayOfYear'),H('dayOfYear','DDD'),j('dayOfYear',4),fe('DDD',se),fe('DDDD',K),ge(['DDD','DDDD'],(function(e,t,n){n._dayOfYear=z(e)})),F('m',['mm',2],0,'minute'),H('minute','m'),j('minute',14),fe('m',ne),fe('mm',ne,X),ge(['m','mm'],4);var dn=q('Minutes',!1);F('s',['ss',2],0,'second'),H('second','s'),j('second',15),fe('s',ne),fe('ss',ne,X),ge(['s','ss'],5);var hn,fn,_n=q('Seconds',!1);for(F('S',0,0,(function(){return~~(this.millisecond()/100)})),F(0,['SS',2],0,(function(){return~~(this.millisecond()/10)})),F(0,['SSS',3],0,'millisecond'),F(0,['SSSS',4],0,(function(){return 10*this.millisecond()})),F(0,['SSSSS',5],0,(function(){return 100*this.millisecond()})),F(0,['SSSSSS',6],0,(function(){return 1e3*this.millisecond()})),F(0,['SSSSSSS',7],0,(function(){return 1e4*this.millisecond()})),F(0,['SSSSSSSS',8],0,(function(){return 1e5*this.millisecond()})),F(0,['SSSSSSSSS',9],0,(function(){return 1e6*this.millisecond()})),H('millisecond','ms'),j('millisecond',16),fe('S',se,Q),fe('SS',se,X),fe('SSS',se,K),hn='SSSS';hn.length<=9;hn+='S')fe(hn,le);function mn(e,t){t[6]=z(1e3*('0.'+e))}for(hn='S';hn.length<=9;hn+='S')ge(hn,mn);fn=q('Milliseconds',!1),F('z',0,0,'zoneAbbr'),F('zz',0,0,'zoneName');var pn=w.prototype;function yn(e){return e}pn.add=zt,pn.calendar=function(e,t){1===arguments.length&&(arguments[0]?Bt(arguments[0])?(e=arguments[0],t=void 0):Jt(arguments[0])&&(t=arguments[0],e=void 0):(e=void 0,t=void 0));var n=e||St(),i=At(n,this).startOf('day'),s=r.calendarFormat(this,i)||'sameElse',a=t&&(O(t[s])?t[s].call(this,n):t[s]);return this.format(a||this.localeData().calendar(s,this,St(n)))},pn.clone=function(){return new w(this)},pn.diff=function(e,t,n){var r,i,s;if(!this.isValid())return NaN;if(!(r=At(e,this)).isValid())return NaN;switch(i=6e4*(r.utcOffset()-this.utcOffset()),t=L(t)){case'year':s=Qt(this,r)/12;break;case'month':s=Qt(this,r);break;case'quarter':s=Qt(this,r)/3;break;case'second':s=(this-r)/1e3;break;case'minute':s=(this-r)/6e4;break;case'hour':s=(this-r)/36e5;break;case'day':s=(this-r-i)/864e5;break;case'week':s=(this-r-i)/6048e5;break;default:s=this-r}return n?s:Z(s)},pn.endOf=function(e){var t,n;if(void 0===(e=L(e))||'millisecond'===e||!this.isValid())return this;switch(n=this._isUTC?rn:nn,e){case'year':t=n(this.year()+1,0,1)-1;break;case'quarter':t=n(this.year(),this.month()-this.month()%3+3,1)-1;break;case'month':t=n(this.year(),this.month()+1,1)-1;break;case'week':t=n(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case'isoWeek':t=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case'day':case'date':t=n(this.year(),this.month(),this.date()+1)-1;break;case'hour':t=this._d.valueOf(),t+=36e5-tn(t+(this._isUTC?0:6e4*this.utcOffset()),36e5)-1;break;case'minute':t=this._d.valueOf(),t+=6e4-tn(t,6e4)-1;break;case'second':t=this._d.valueOf(),t+=1e3-tn(t,1e3)-1}return this._d.setTime(t),r.updateOffset(this,!0),this},pn.format=function(e){e||(e=this.isUtc()?r.defaultFormatUtc:r.defaultFormat);var t=A(this,e);return this.localeData().postformat(t)},pn.from=function(e,t){return this.isValid()&&(k(e)&&e.isValid()||St(e).isValid())?Vt({to:this,from:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},pn.fromNow=function(e){return this.from(St(),e)},pn.to=function(e,t){return this.isValid()&&(k(e)&&e.isValid()||St(e).isValid())?Vt({from:this,to:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},pn.toNow=function(e){return this.to(St(),e)},pn.get=function(e){return O(this[e=L(e)])?this[e]():this},pn.invalidAt=function(){return _(this).overflow},pn.isAfter=function(e,t){var n=k(e)?e:St(e);return!(!this.isValid()||!n.isValid())&&('millisecond'===(t=L(t)||'millisecond')?this.valueOf()>n.valueOf():n.valueOf()9999?A(n,t?'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]':'YYYYYY-MM-DD[T]HH:mm:ss.SSSZ'):O(Date.prototype.toISOString)?t?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace('Z',A(n,'Z')):A(n,t?'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]':'YYYY-MM-DD[T]HH:mm:ss.SSSZ')},pn.inspect=function(){if(!this.isValid())return'moment.invalid(/* '+this._i+' */)';var e,t,n,r='moment',i='';return this.isLocal()||(r=0===this.utcOffset()?'moment.utc':'moment.parseZone',i='Z'),e='['+r+'(\"]',t=0<=this.year()&&this.year()<=9999?'YYYY':'YYYYYY',n=i+'[\")]',this.format(e+t+'-MM-DD[T]HH:mm:ss.SSS'+n)},'undefined'!=typeof Symbol&&null!=Symbol.for&&(pn[Symbol.for('nodejs.util.inspect.custom')]=function(){return'Moment<'+this.format()+'>'}),pn.toJSON=function(){return this.isValid()?this.toISOString():null},pn.toString=function(){return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ')},pn.unix=function(){return Math.floor(this.valueOf()/1e3)},pn.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},pn.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},pn.eraName=function(){var e,t,n,r=this.localeData().eras();for(e=0,t=r.length;ethis.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},pn.isLocal=function(){return!!this.isValid()&&!this._isUTC},pn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},pn.isUtc=Ut,pn.isUTC=Ut,pn.zoneAbbr=function(){return this._isUTC?'UTC':''},pn.zoneName=function(){return this._isUTC?'Coordinated Universal Time':''},pn.dates=b('dates accessor is deprecated. Use date instead.',cn),pn.months=b('months accessor is deprecated. Use month instead',Te),pn.years=b('years accessor is deprecated. Use year instead',Re),pn.zone=b('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/',(function(e,t){return null!=e?('string'!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()})),pn.isDSTShifted=b('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information',(function(){if(!l(this._isDSTShifted))return this._isDSTShifted;var e,t={};return v(t,this),(t=bt(t))._a?(e=t._isUTC?f(t._a):St(t._a),this._isDSTShifted=this.isValid()&&function(e,t,n){var r,i=Math.min(e.length,t.length),s=Math.abs(e.length-t.length),a=0;for(r=0;r0):this._isDSTShifted=!1,this._isDSTShifted}));var gn=T.prototype;function vn(e,t,n,r){var i=ot(),s=f().set(r,t);return i[n](s,e)}function wn(e,t,n){if(u(e)&&(t=e,e=void 0),e=e||'',null!=t)return vn(e,t,n,'month');var r,i=[];for(r=0;r<12;r++)i[r]=vn(e,r,n,'month');return i}function kn(e,t,n,r){'boolean'==typeof e?(u(t)&&(n=t,t=void 0),t=t||''):(n=t=e,e=!1,u(t)&&(n=t,t=void 0),t=t||'');var i,s=ot(),a=e?s._week.dow:0,o=[];if(null!=n)return vn(t,(n+a)%7,r,'day');for(i=0;i<7;i++)o[i]=vn(t,(i+a)%7,r,'day');return o}gn.calendar=function(e,t,n){var r=this._calendar[e]||this._calendar.sameElse;return O(r)?r.call(t,n):r},gn.longDateFormat=function(e){var t=this._longDateFormat[e],n=this._longDateFormat[e.toUpperCase()];return t||!n?t:(this._longDateFormat[e]=n.match(P).map((function(e){return'MMMM'===e||'MM'===e||'DD'===e||'dddd'===e?e.slice(1):e})).join(''),this._longDateFormat[e])},gn.invalidDate=function(){return this._invalidDate},gn.ordinal=function(e){return this._ordinal.replace('%d',e)},gn.preparse=yn,gn.postformat=yn,gn.relativeTime=function(e,t,n,r){var i=this._relativeTime[n];return O(i)?i(e,t,n,r):i.replace(/%d/i,e)},gn.pastFuture=function(e,t){var n=this._relativeTime[e>0?'future':'past'];return O(n)?n(t):n.replace(/%s/i,t)},gn.set=function(e){var t,n;for(n in e)a(e,n)&&(O(t=e[n])?this[n]=t:this['_'+n]=t);this._config=e,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+'|'+/\\d{1,2}/.source)},gn.eras=function(e,t){var n,i,s,a=this._eras||ot('en')._eras;for(n=0,i=a.length;n=0)return l[r]},gn.erasConvertYear=function(e,t){var n=e.since<=e.until?1:-1;return void 0===t?r(e.since).year():r(e.since).year()+(t-e.offset)*n},gn.erasAbbrRegex=function(e){return a(this,'_erasAbbrRegex')||an.call(this),e?this._erasAbbrRegex:this._erasRegex},gn.erasNameRegex=function(e){return a(this,'_erasNameRegex')||an.call(this),e?this._erasNameRegex:this._erasRegex},gn.erasNarrowRegex=function(e){return a(this,'_erasNarrowRegex')||an.call(this),e?this._erasNarrowRegex:this._erasRegex},gn.months=function(e,t){return e?i(this._months)?this._months[e.month()]:this._months[(this._months.isFormat||Me).test(t)?'format':'standalone'][e.month()]:i(this._months)?this._months:this._months.standalone},gn.monthsShort=function(e,t){return e?i(this._monthsShort)?this._monthsShort[e.month()]:this._monthsShort[Me.test(t)?'format':'standalone'][e.month()]:i(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},gn.monthsParse=function(e,t,n){var r,i,s;if(this._monthsParseExact)return Oe.call(this,e,t,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),r=0;r<12;r++){if(i=f([2e3,r]),n&&!this._longMonthsParse[r]&&(this._longMonthsParse[r]=new RegExp('^'+this.months(i,'').replace('.','')+'$','i'),this._shortMonthsParse[r]=new RegExp('^'+this.monthsShort(i,'').replace('.','')+'$','i')),n||this._monthsParse[r]||(s='^'+this.months(i,'')+'|^'+this.monthsShort(i,''),this._monthsParse[r]=new RegExp(s.replace('.',''),'i')),n&&'MMMM'===t&&this._longMonthsParse[r].test(e))return r;if(n&&'MMM'===t&&this._shortMonthsParse[r].test(e))return r;if(!n&&this._monthsParse[r].test(e))return r}},gn.monthsRegex=function(e){return this._monthsParseExact?(a(this,'_monthsRegex')||Ne.call(this),e?this._monthsStrictRegex:this._monthsRegex):(a(this,'_monthsRegex')||(this._monthsRegex=Ye),this._monthsStrictRegex&&e?this._monthsStrictRegex:this._monthsRegex)},gn.monthsShortRegex=function(e){return this._monthsParseExact?(a(this,'_monthsRegex')||Ne.call(this),e?this._monthsShortStrictRegex:this._monthsShortRegex):(a(this,'_monthsShortRegex')||(this._monthsShortRegex=Se),this._monthsShortStrictRegex&&e?this._monthsShortStrictRegex:this._monthsShortRegex)},gn.week=function(e){return Ee(e,this._week.dow,this._week.doy).week},gn.firstDayOfYear=function(){return this._week.doy},gn.firstDayOfWeek=function(){return this._week.dow},gn.weekdays=function(e,t){var n=i(this._weekdays)?this._weekdays:this._weekdays[e&&!0!==e&&this._weekdays.isFormat.test(t)?'format':'standalone'];return!0===e?He(n,this._week.dow):e?n[e.day()]:n},gn.weekdaysMin=function(e){return!0===e?He(this._weekdaysMin,this._week.dow):e?this._weekdaysMin[e.day()]:this._weekdaysMin},gn.weekdaysShort=function(e){return!0===e?He(this._weekdaysShort,this._week.dow):e?this._weekdaysShort[e.day()]:this._weekdaysShort},gn.weekdaysParse=function(e,t,n){var r,i,s;if(this._weekdaysParseExact)return ze.call(this,e,t,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),r=0;r<7;r++){if(i=f([2e3,1]).day(r),n&&!this._fullWeekdaysParse[r]&&(this._fullWeekdaysParse[r]=new RegExp('^'+this.weekdays(i,'').replace('.','\\\\.?')+'$','i'),this._shortWeekdaysParse[r]=new RegExp('^'+this.weekdaysShort(i,'').replace('.','\\\\.?')+'$','i'),this._minWeekdaysParse[r]=new RegExp('^'+this.weekdaysMin(i,'').replace('.','\\\\.?')+'$','i')),this._weekdaysParse[r]||(s='^'+this.weekdays(i,'')+'|^'+this.weekdaysShort(i,'')+'|^'+this.weekdaysMin(i,''),this._weekdaysParse[r]=new RegExp(s.replace('.',''),'i')),n&&'dddd'===t&&this._fullWeekdaysParse[r].test(e))return r;if(n&&'ddd'===t&&this._shortWeekdaysParse[r].test(e))return r;if(n&&'dd'===t&&this._minWeekdaysParse[r].test(e))return r;if(!n&&this._weekdaysParse[r].test(e))return r}},gn.weekdaysRegex=function(e){return this._weekdaysParseExact?(a(this,'_weekdaysRegex')||qe.call(this),e?this._weekdaysStrictRegex:this._weekdaysRegex):(a(this,'_weekdaysRegex')||(this._weekdaysRegex=je),this._weekdaysStrictRegex&&e?this._weekdaysStrictRegex:this._weekdaysRegex)},gn.weekdaysShortRegex=function(e){return this._weekdaysParseExact?(a(this,'_weekdaysRegex')||qe.call(this),e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(a(this,'_weekdaysShortRegex')||(this._weekdaysShortRegex=Ge),this._weekdaysShortStrictRegex&&e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},gn.weekdaysMinRegex=function(e){return this._weekdaysParseExact?(a(this,'_weekdaysRegex')||qe.call(this),e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(a(this,'_weekdaysMinRegex')||(this._weekdaysMinRegex=Ze),this._weekdaysMinStrictRegex&&e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},gn.isPM=function(e){return'p'===(e+'').toLowerCase().charAt(0)},gn.meridiem=function(e,t,n){return e>11?n?'pm':'PM':n?'am':'AM'},st('en',{eras:[{since:'0001-01-01',until:1/0,offset:1,name:'Anno Domini',narrow:'AD',abbr:'AD'},{since:'0000-12-31',until:-1/0,offset:1,name:'Before Christ',narrow:'BC',abbr:'BC'}],dayOfMonthOrdinalParse:/\\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10;return e+(1===z(e%100/10)?'th':1===t?'st':2===t?'nd':3===t?'rd':'th')}}),r.lang=b('moment.lang is deprecated. Use moment.locale instead.',st),r.langData=b('moment.langData is deprecated. Use moment.localeData instead.',ot);var Dn=Math.abs;function bn(e,t,n,r){var i=Vt(t,n);return e._milliseconds+=r*i._milliseconds,e._days+=r*i._days,e._months+=r*i._months,e._bubble()}function Mn(e){return e<0?Math.floor(e):Math.ceil(e)}function Sn(e){return 4800*e/146097}function Yn(e){return 146097*e/4800}function On(e){return function(){return this.as(e)}}var xn=On('ms'),Tn=On('s'),Nn=On('m'),Pn=On('h'),Rn=On('d'),Wn=On('w'),Cn=On('M'),Fn=On('Q'),An=On('y');function En(e){return function(){return this.isValid()?this._data[e]:NaN}}var Un=En('milliseconds'),Hn=En('seconds'),Ln=En('minutes'),Vn=En('hours'),In=En('days'),jn=En('months'),Gn=En('years'),Zn=Math.round,zn={ss:44,s:45,m:45,h:22,d:26,w:null,M:11};function qn(e,t,n,r,i){return i.relativeTime(t||1,!!n,e,r)}var $n=Math.abs;function Bn(e){return(e>0)-(e<0)||+e}function Jn(){if(!this.isValid())return this.localeData().invalidDate();var e,t,n,r,i,s,a,o,l=$n(this._milliseconds)/1e3,u=$n(this._days),c=$n(this._months),d=this.asSeconds();return d?(e=Z(l/60),t=Z(e/60),l%=60,e%=60,n=Z(c/12),c%=12,r=l?l.toFixed(3).replace(/\\.?0+$/,''):'',i=d<0?'-':'',s=Bn(this._months)!==Bn(d)?'-':'',a=Bn(this._days)!==Bn(d)?'-':'',o=Bn(this._milliseconds)!==Bn(d)?'-':'',i+'P'+(n?s+n+'Y':'')+(c?s+c+'M':'')+(u?a+u+'D':'')+(t||e||l?'T':'')+(t?o+t+'H':'')+(e?o+e+'M':'')+(l?o+r+'S':'')):'P0D'}var Qn=Nt.prototype;return Qn.isValid=function(){return this._isValid},Qn.abs=function(){var e=this._data;return this._milliseconds=Dn(this._milliseconds),this._days=Dn(this._days),this._months=Dn(this._months),e.milliseconds=Dn(e.milliseconds),e.seconds=Dn(e.seconds),e.minutes=Dn(e.minutes),e.hours=Dn(e.hours),e.months=Dn(e.months),e.years=Dn(e.years),this},Qn.add=function(e,t){return bn(this,e,t,1)},Qn.subtract=function(e,t){return bn(this,e,t,-1)},Qn.as=function(e){if(!this.isValid())return NaN;var t,n,r=this._milliseconds;if('month'===(e=L(e))||'quarter'===e||'year'===e)switch(t=this._days+r/864e5,n=this._months+Sn(t),e){case'month':return n;case'quarter':return n/3;case'year':return n/12}else switch(t=this._days+Math.round(Yn(this._months)),e){case'week':return t/7+r/6048e5;case'day':return t+r/864e5;case'hour':return 24*t+r/36e5;case'minute':return 1440*t+r/6e4;case'second':return 86400*t+r/1e3;case'millisecond':return Math.floor(864e5*t)+r;default:throw new Error('Unknown unit '+e)}},Qn.asMilliseconds=xn,Qn.asSeconds=Tn,Qn.asMinutes=Nn,Qn.asHours=Pn,Qn.asDays=Rn,Qn.asWeeks=Wn,Qn.asMonths=Cn,Qn.asQuarters=Fn,Qn.asYears=An,Qn.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*z(this._months/12):NaN},Qn._bubble=function(){var e,t,n,r,i,s=this._milliseconds,a=this._days,o=this._months,l=this._data;return s>=0&&a>=0&&o>=0||s<=0&&a<=0&&o<=0||(s+=864e5*Mn(Yn(o)+a),a=0,o=0),l.milliseconds=s%1e3,e=Z(s/1e3),l.seconds=e%60,t=Z(e/60),l.minutes=t%60,n=Z(t/60),l.hours=n%24,a+=Z(n/24),i=Z(Sn(a)),o+=i,a-=Mn(Yn(i)),r=Z(o/12),o%=12,l.days=a,l.months=o,l.years=r,this},Qn.clone=function(){return Vt(this)},Qn.get=function(e){return e=L(e),this.isValid()?this[e+'s']():NaN},Qn.milliseconds=Un,Qn.seconds=Hn,Qn.minutes=Ln,Qn.hours=Vn,Qn.days=In,Qn.weeks=function(){return Z(this.days()/7)},Qn.months=jn,Qn.years=Gn,Qn.humanize=function(e,t){if(!this.isValid())return this.localeData().invalidDate();var n,r,i=!1,s=zn;return'object'==typeof e&&(t=e,e=!1),'boolean'==typeof e&&(i=e),'object'==typeof t&&(s=Object.assign({},zn,t),null!=t.s&&null==t.ss&&(s.ss=t.s-1)),n=this.localeData(),r=function(e,t,n,r){var i=Vt(e).abs(),s=Zn(i.as('s')),a=Zn(i.as('m')),o=Zn(i.as('h')),l=Zn(i.as('d')),u=Zn(i.as('M')),c=Zn(i.as('w')),d=Zn(i.as('y')),h=s<=n.ss&&['s',s]||s0,h[4]=r,qn.apply(null,h)}(this,!i,s,n),i&&(r=n.pastFuture(+this,r)),n.postformat(r)},Qn.toISOString=Jn,Qn.toString=Jn,Qn.toJSON=Jn,Qn.locale=Xt,Qn.localeData=en,Qn.toIsoString=b('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)',Jn),Qn.lang=Kt,F('X',0,0,'unix'),F('x',0,0,'valueOf'),fe('x',ue),fe('X',/[+-]?\\d+(\\.\\d{1,3})?/),ge('X',(function(e,t,n){n._d=new Date(1e3*parseFloat(e))})),ge('x',(function(e,t,n){n._d=new Date(z(e))})),r.version='2.29.1',t=St,r.fn=pn,r.min=function(){var e=[].slice.call(arguments,0);return xt('isBefore',e)},r.max=function(){var e=[].slice.call(arguments,0);return xt('isAfter',e)},r.now=function(){return Date.now?Date.now():+new Date},r.utc=f,r.unix=function(e){return St(1e3*e)},r.months=function(e,t){return wn(e,t,'months')},r.isDate=c,r.locale=st,r.invalid=p,r.duration=Vt,r.isMoment=k,r.weekdays=function(e,t,n){return kn(e,t,n,'weekdays')},r.parseZone=function(){return St.apply(null,arguments).parseZone()},r.localeData=ot,r.isDuration=Pt,r.monthsShort=function(e,t){return wn(e,t,'monthsShort')},r.weekdaysMin=function(e,t,n){return kn(e,t,n,'weekdaysMin')},r.defineLocale=at,r.updateLocale=function(e,t){if(null!=t){var n,r,i=Ke;null!=et[e]&&null!=et[e].parentLocale?et[e].set(x(et[e]._config,t)):(null!=(r=it(e))&&(i=r._config),t=x(i,t),null==r&&(t.abbr=e),(n=new T(t)).parentLocale=et[e],et[e]=n),st(e)}else null!=et[e]&&(null!=et[e].parentLocale?(et[e]=et[e].parentLocale,e===st()&&st(e)):null!=et[e]&&delete et[e]);return et[e]},r.locales=function(){return M(et)},r.weekdaysShort=function(e,t,n){return kn(e,t,n,'weekdaysShort')},r.normalizeUnits=L,r.relativeTimeRounding=function(e){return void 0===e?Zn:'function'==typeof e&&(Zn=e,!0)},r.relativeTimeThreshold=function(e,t){return void 0!==zn[e]&&(void 0===t?zn[e]:(zn[e]=t,'s'===e&&(zn.ss=t-1),!0))},r.calendarFormat=function(e,t){var n=e.diff(t,'days',!0);return n<-6?'sameElse':n<-1?'lastWeek':n<0?'lastDay':n<1?'sameDay':n<2?'nextDay':n<7?'nextWeek':'sameElse'},r.prototype=pn,r.HTML5_FMT={DATETIME_LOCAL:'YYYY-MM-DDTHH:mm',DATETIME_LOCAL_SECONDS:'YYYY-MM-DDTHH:mm:ss',DATETIME_LOCAL_MS:'YYYY-MM-DDTHH:mm:ss.SSS',DATE:'YYYY-MM-DD',TIME:'HH:mm',TIME_SECONDS:'HH:mm:ss',TIME_MS:'HH:mm:ss.SSS',WEEK:'GGGG-[W]WW',MONTH:'YYYY-MM'},r}()}).call(this,n(3)(e))},function(e,t,n){var r=n(2),i=n(5);e.exports=i(r,contact,reports)},function(e,t,n){const r=n(0),i=n(4),{today:s,MAX_DAYS_IN_PREGNANCY:a,isHighRiskPregnancy:o,getNewestReport:l,getSubsequentPregnancyFollowUps:u,getSubsequentDeliveries:c,isAlive:d,isReadyForNewPregnancy:h,isReadyForDelivery:f,isActivePregnancy:_,countANCFacilityVisits:m,getAllRiskFactors:p,getLatestDangerSignsForPregnancy:y,getNextANCVisitDate:g,getMostRecentLMPDateForPregnancy:v,getMostRecentEDDForPregnancy:w,getDeliveryDate:k,getFormArraySubmittedInWindow:D,getRecentANCVisitWithEvent:b,getAllRiskFactorExtra:M,getField:S}=i,Y=contact,O=lineage,x=reports,T={alive:d(Y),muted:!1,show_pregnancy_form:h(Y,x),show_delivery_form:f(Y,x)},N=[{appliesToType:'person',label:'patient_id',value:Y.patient_id,width:4},{appliesToType:'person',label:'contact.age',value:Y.date_of_birth,width:4,filter:'age'},{appliesToType:'person',label:'contact.sex',value:'contact.sex.'+Y.sex,translate:!0,width:4},{appliesToType:'person',label:'person.field.phone',value:Y.phone,width:4},{appliesToType:'person',label:'person.field.alternate_phone',value:Y.phone_alternate,width:4},{appliesToType:'person',label:'External ID',value:Y.external_id,width:4},{appliesToType:'person',label:'contact.parent',value:O,filter:'lineage'},{appliesToType:'!person',label:'contact',value:Y.contact&&Y.contact.name,width:4},{appliesToType:'!person',label:'contact.phone',value:Y.contact&&Y.contact.phone,width:4},{appliesToType:'!person',label:'External ID',value:Y.external_id,width:4},{appliesToType:'!person',appliesIf:function(){return Y.parent&&O[0]},label:'contact.parent',value:O,filter:'lineage'},{appliesToType:'person',label:'contact.notes',value:Y.notes,width:12},{appliesToType:'!person',label:'contact.notes',value:Y.notes,width:12}];Y.short_name&&N.unshift({appliesToType:'person',label:'contact.short_name',value:Y.short_name,width:4});const P=[{label:'contact.profile.pregnancy.active',appliesToType:'report',appliesIf:function(e){return _(Y,x,e)},fields:function(e){const t=[],n=p(x,e),i=M(x,e),a=y(x,e),c=o(x,e),d=l(x,['pregnancy','pregnancy_home_visit']),h=r(d.reported_date),f=v(x,e),_=w(x,e),k=g(x,e),D=f?s.diff(f,'weeks'):null;let Y=S(e,'lmp_approx'),O=e.reported_date;u(x,e).forEach((function(e){e.reported_date>O&&'yes'===S(e,'lmp_updated')&&(O=e.reported_date,S(e,'lmp_method_approx')&&(Y=S(e,'lmp_method_approx')))}));const T=b(x,e,'migrated'),N=b(x,e,'refused'),P=T||N;if(P){const e='clear_all'===S(P,'pregnancy_ended.clear_option');t.push({label:'contact.profile.change_care',value:T?'Migrated out of area':'Refusing care',width:6},{label:'contact.profile.tasks_on_off',value:e?'Off':'On',width:6})}if(t.push({label:'Weeks Pregnant',value:D||0===D?{number:D,approximate:'yes'===Y}:'contact.profile.value.unknown',translate:!D&&0!==D,filter:D||0===D?'weeksPregnant':'',width:6},{label:'contact.profile.edd',value:_?_.valueOf():'contact.profile.value.unknown',translate:!_,filter:_?'simpleDate':'',width:6}),c){let e='';e=!n&&i?i.join(', '):n.length>1||n&&i?'contact.profile.risk.multiple':'contact.profile.danger_sign.'+n[0],t.push({label:'contact.profile.risk.high',value:e,translate:!0,icon:'icon-risk',width:6})}return a.length>0&&t.push({label:'contact.profile.danger_signs.current',value:a.length>1?'contact.profile.danger_sign.multiple':'contact.profile.danger_sign.'+a[0],translate:!0,width:6}),t.push({label:'contact.profile.visit',value:'contact.profile.visits.of',context:{count:m(x,e),total:8},translate:!0,width:6},{label:'contact.profile.last_visited',value:h.valueOf(),filter:'relativeDay',width:6}),k&&k.isSameOrAfter(s)&&t.push({label:'contact.profile.anc.next',value:k.valueOf(),filter:'simpleDate',width:6}),t},modifyContext:function(e,t){let n=S(t,'lmp_date_8601'),r=S(t,'lmp_method_approx'),i=S(t,'hiv_status_known'),s=S(t,'deworming_med_received'),a=S(t,'tt_received');const o=p(x,t),l=M(x,t);let c=S(t,'t_pregnancy_follow_up_date');u(x,t).forEach((function(e){'yes'===S(e,'lmp_updated')&&(n=S(e,'lmp_date_8601'),r=S(e,'lmp_method_approx')),i=S(e,'hiv_status_known'),s=S(e,'deworming_med_received'),a=S(e,'tt_received'),'yes'===S(e,'t_pregnancy_follow_up')&&(c=S(e,'t_pregnancy_follow_up_date'))})),e.lmp_date_8601=n,e.lmp_method_approx=r,e.is_active_pregnancy=!0,e.deworming_med_received=s,e.hiv_tested_past=i,e.tt_received_past=a,e.risk_factor_codes=o.join(' '),e.risk_factor_extra=l.join('; '),e.pregnancy_follow_up_date_recent=c,e.pregnancy_uuid=t._id}},{label:'contact.profile.death.title',appliesToType:'person',appliesIf:function(){return!d(Y)},fields:function(){const e=[];let t,n;const r=l(x,['death_report']);if(r){const e=S(r,'death_details');e&&(t=e.date_of_death,n=e.place_of_death)}else Y.date_of_death&&(t=Y.date_of_death);return e.push({label:'contact.profile.death.date',value:t||'contact.profile.value.unknown',filter:t?'simpleDate':'',translate:!t,width:6},{label:'contact.profile.death.place',value:n||'contact.profile.value.unknown',translate:!0,width:6}),e}},{label:'contact.profile.pregnancy.past',appliesToType:'report',appliesIf:function(e){if('person'!==Y.type)return!1;if('delivery'===e.form)return!0;if('pregnancy'===e.form){if(b(x,e,'abortion')||b(x,e,'miscarriage'))return!0;const t=v(x,e);return t&&s.isSameOrAfter(t.clone().add(42,'weeks'))&&0===c(x,e,a).length}return!1},fields:function(e){const t=[];let n,i,l='',u=0,d=0,h=0;if('delivery'===e.form){const s=r(e.reported_date);n=D(x,['pregnancy'],s.clone().subtract(a,'days').toDate(),s.toDate())[0],S(e,'delivery_outcome')&&(i=k(e),l=S(e,'delivery_outcome.delivery_place'),u=S(e,'delivery_outcome.babies_delivered_num'),d=S(e,'delivery_outcome.babies_deceased_num'),t.push({label:'contact.profile.delivery_date',value:i?i.valueOf():'',filter:'simpleDate',width:6},{label:'contact.profile.delivery_place',value:l,translate:!0,width:6},{label:'contact.profile.delivered_babies',value:u,width:6}))}else if('pregnancy'===e.form){n=e;const o=v(x,n),l=b(x,n,'abortion'),u=b(x,n,'miscarriage');if(l||u){let e='',n=r(0),i=0;l?(e='abortion',n=r(S(l,'pregnancy_ended.abortion_date'))):(e='miscarriage',n=r(S(u,'pregnancy_ended.miscarriage_date'))),i=n.diff(o,'weeks'),t.push({label:'contact.profile.pregnancy.end_early',value:e,translate:!0,width:6},{label:'contact.profile.pregnancy.end_date',value:n.valueOf(),filter:'simpleDate',width:6},{label:'contact.profile.pregnancy.end_weeks',value:i>0?i:'contact.profile.value.unknown',translate:i<=0,width:6})}else o&&s.isSameOrAfter(o.clone().add(42,'weeks'))&&0===c(x,e,a).length&&(i=w(x,e),t.push({label:'contact.profile.delivery_date',value:i?i.valueOf():'contact.profile.value.unknown',filter:'simpleDate',translate:!i,width:6}))}if(d>0&&S(e,'baby_death')){t.push({label:'contact.profile.deceased_babies',value:d,width:6});let n=S(e,'baby_death.baby_death_repeat');n||(n=[]);let r=0;n.forEach((function(e){r>0&&t.push({label:'',value:'',width:6}),t.push({label:'contact.profile.newborn.death_date',value:e.baby_death_date,filter:'simpleDate',width:6},{label:'contact.profile.newborn.death_place',value:e.baby_death_place,translate:!0,width:6},{label:'contact.profile.delivery.stillbirthQ',value:e.stillbirth,translate:!0,width:6}),r++,r===n.length&&t.push({label:'',value:'',width:6})}))}if(n){h=m(x,n),t.push({label:'contact.profile.anc_visit',value:h,width:3});if(o(x,n)){let e='';const r=p(x,n),i=M(x,n);e=!r&&i?i.join(', '):r.length>1||r&&i?'contact.profile.risk.multiple':'contact.profile.danger_sign.'+r[0],t.push({label:'contact.profile.risk.high',value:e,translate:!0,icon:'icon-risk',width:6})}}return t}}];e.exports={context:T,cards:P,fields:N}},function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,'loaded',{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,'id',{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},function(e,t,n){const r=n(0),i=r().startOf('day'),s=['pregnancy'],a=['pregnancy_home_visit'],o=['delivery'],l=['pregnancy','pregnancy_home_visit','pregnancy_danger_sign','pregannacy_danger_sign_follow_up'],u=(e,t)=>['fields',...(t||'').split('.')].reduce((e,t)=>{if(void 0!==e)return e[t]},e);function c(e,t,n,r){return e.filter((function(e){return t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r}))}function d(e,t){let n;return e.forEach((function(e){(function(e){return!!(e.form&&e.fields&&e.reported_date)})(e)&&t.includes(e.form)&&(!n||e.reported_date>n.reported_date)&&(n=e)})),n}function h(e){return D(e)&&u(e,'lmp_date_8601')&&r(u(e,'lmp_date_8601'))}function f(e,t){let n=h(t),i=t.reported_date;return x(e,t).forEach((function(e){const t=b(s=e)&&u(s,'lmp_date_8601')&&r(u(s,'lmp_date_8601'));var s;e.reported_date>i&&'yes'===u(e,'lmp_updated')&&(i=e.reported_date,n=t)})),n}function _(e,t){const n=f(e,t);if(n)return n.clone().add(280,'days')}function m(e){return M(e)&&u(e,'delivery_outcome.delivery_date')&&r(u(e,'delivery_outcome.delivery_date'))}function p(e){const t=[];if('yes'===u(e,'t_danger_signs_referral_follow_up')){const n=u(e,'danger_signs');if(n)for(const e in n)'yes'===n[e]&&'r_danger_sign_present'!==e&&t.push(e)}return t}function y(e){const t=[];if(!D(e))return[];if('yes'===u(e,'risk_factors.r_risk_factor_present')){'yes'===u(e,'risk_factors.risk_factors_history.first_pregnancy')&&t.push('first_pregnancy'),'yes'===u(e,'risk_factors.risk_factors_history.previous_miscarriage')&&t.push('previous_miscarriage');const n=u(e,'risk_factors.risk_factors_present.primary_condition'),r=u(e,'risk_factors.risk_factors_present.secondary_condition');n&&t.push(...n.split(' ')),r&&t.push(...r.split(' '))}return t}function g(e,t){const n=y(t);return x(e,t).forEach((function(e){n.push(...function(e){const t=[];if(!b(e))return[];if('yes'===u(e,'anc_visits_hf.risk_factors.r_risk_factor_present')){const n=u(e,'anc_visits_hf.risk_factors.new_risks');n&&t.push(...n.split(' '))}return t}(e))})),n}function v(e){let t;return e&&D(e)?t=u(e,'risk_factors.risk_factors_present.additional_risk'):e&&b(e)&&(t=u(e,'anc_visits_hf.risk_factors.additional_risk')),t}function w(e,t){const n=[],r=v(t);r&&n.push(r);return x(e,t).forEach((function(e){const t=v(e);t&&n.push(t)})),n}function k(e){return e&&!e.date_of_death}function D(e){return e&&s.includes(e.form)}function b(e){return e&&a.includes(e.form)}function M(e){return e&&o.includes(e.form)}function S(e,t,n){if('person'!==e.type||!k(e)||!D(n))return!1;const r=(f(t,n)||n.reported_date)>i.clone().subtract(294,'day'),s=O(t,n,42).length>0,a=function(e,t){return e.filter((function(e){return D(e)&&e.reported_date>t.reported_date}))}(t,n).length>0;return r&&!s&&!a&&!Y(t,n,'abortion')&&!Y(t,n,'miscarriage')}function Y(e,t,n){const r=d(x(e,t),a);if(r&&u(r,'pregnancy_summary.visit_option')===n)return r}function O(e,t,n){return e.filter((function(e){return M(e)&&e.reported_date>t.reported_date&&(!n||e.reported_date>=i.clone().subtract(n,'days'))}))}function x(e,t){let n=h(t);n||(n=r(t.reported_date));return e.filter((function(e){return b(e)&&e.reported_date>t.reported_date&&r(e.reported_date)S(e))},isActivePregnancy:S,countANCFacilityVisits:function(e,t){let n=0;const r=x(e,t);return u(t,'anc_visits_hf.anc_visits_hf_past')&&!isNaN(u(t,'anc_visits_hf.anc_visits_hf_past.visited_hf_count'))&&(n+=parseInt(u(t,'anc_visits_hf.anc_visits_hf_past.visited_hf_count'))),n+=r.reduce((function(e,t){const n=u(t,'anc_visits_hf.anc_visits_hf_past');return n?(e+='yes'===n.last_visit_attended&&1,isNaN(n.visited_hf_count)?e:e+('yes'===n.report_other_visits&&parseInt(n.visited_hf_count))):0}),0),n},knowsHIVStatusInPast3Months:function(e){let t=!1;return c(e,s,i.clone().subtract(3,'months'),i).forEach((function(e){'yes'===u(e,'pregnancy_new_or_current.hiv_status.hiv_status_know')&&(t=!0)})),t},getAllRiskFactors:g,getAllRiskFactorExtra:w,getDangerSignCodes:p,getLatestDangerSignsForPregnancy:function(e,t){if(!t)return[];let n=f(e,t);n||(n=r(t.reported_date));const i=c(e,l,n.toDate(),n.clone().add(294,'days').toDate()),s=[];i.forEach(e=>{b(e)?'yes'===u(e,'pregnancy_summary.visit_option')&&s.push(e):s.push(e)});const a=d(s,l);return a?p(a):[]},getNextANCVisitDate:function(e,t){let n=u(t,'t_pregnancy_follow_up_date'),i=t.reported_date;return x(e,t).forEach((function(e){e.reported_date>i&&u(e,'t_pregnancy_follow_up_date')&&(i=e.reported_date,n=u(e,'t_pregnancy_follow_up_date'))})),r(n)},isReadyForNewPregnancy:function(e,t){if('person'!==e.type)return!1;const n=d(t,s),a=d(t,o);if(!n&&!a)return!0;if(n){if(!a||a.reported_daten.reported_date))return m(a)0&&!r.includes('!'+l))&&(!e.appliesIf||e.appliesIf()))return delete e.appliesToType,delete e.appliesIf,!0}))};return o.forEach((function(e){var t,s,o,c,d=n(e.appliesToType);if(d.includes('report')&&d.length>1)throw new Error('You cannot set appliesToType to an array which includes the type \\'report\\' and another type.');if(d.includes('report'))for(t=0;t0)return;(o=i(e,a))&&u.cards.push(o)}})),u.context=a,u}}])})); return ContactSummary;", + "tasks": { + "rules": "define Target { _id: null, contact: null, deleted: null, type: null, pass: null, date: null, groupBy: null }\ndefine Contact { contact: null, reports: null, tasks: null }\ndefine Task { _id: null, deleted: null, doc: null, contact: null, icon: null, date: null, readyStart: null, readyEnd: null, title: null, fields: null, resolved: null, priority: null, priorityLabel: null, reports: null, actions: null }\nrule GenerateEvents {\n when { c: Contact } then { !function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){'undefined'!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:'Module'}),Object.defineProperty(e,'__esModule',{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&'object'==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,'default',{enumerable:!0,value:e}),2&t&&'string'!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,'a',t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p='',n(n.s=2)}([function(e,t){const n=d(Date.now()),r=['pregnancy'],i=['delivery'],o=['pregnancy_home_visit'],a=['pregnancy','pregnancy_home_visit','pregnancy_facility_visit_reminder','pregnancy_danger_sign','pregnancy_danger_sign_follow_up','delivery'];const c=(e,t)=>['fields',...(t||'').split('.')].reduce((e,t)=>{if(void 0!==e)return e[t]},e);function s(e,t){let n;return e.forEach((function(e){t.includes(e.form)&&!e.deleted&&(!n||e.reported_date>n.reported_date)&&(n=e)})),n}function p(e){if(!e)return new Date;const t=e.split(/\\D/),n=new Date(t[0],t[1]-1,t[2]);return function(e){return e instanceof Date&&!isNaN(e)}(n)?n:new Date}function l(e){const t=new Date(e);return t.setHours(0),t.setMinutes(0),t.setSeconds(0),t.setMilliseconds(0),t}function d(e){if('string'==typeof e){if(''===e)return null;e=p(e)}return l(e).getTime()}function u(e,t){const n=l(new Date(e));return n.setDate(n.getDate()+t),n}function _(e){return r.includes(e.form)}function f(e){return o.includes(e.form)}const y=function(e,t){let n;return e.forEach((function(e){t.includes(e.form)&&(!n||e.reported_date>n.reported_date)&&(n=e)})),n},g=function(e){return _(e)&&d(c(e,'lmp_date_8601'))};function m(e,t){return e.reports.filter((function(e){let n=g(t);return n||(n=t.reported_date),f(e)&&e.reported_date>t.reported_date&&e.reported_dater&&''!==t&&t!==n&&(r=e.reported_date,n=t)})),n}e.exports={today:n,MS_IN_DAY:864e5,MAX_DAYS_IN_PREGNANCY:294,addDays:u,isAlive:function(e){return e&&e.contact&&!e.contact.date_of_death},getTimeForMidnight:l,isFormArraySubmittedInWindow:function(e,t,n,r,i){let o=!1,a=0;return e.forEach((function(e){t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r&&(o=!0,i&&a++)})),i?a>=i:o},isFormArraySubmittedInWindowExcludingThisReport:function(e,t,n,r,i,o){let a=!1,c=0;return e.forEach((function(e){t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r&&e._id!==i._id&&(a=!0,o&&c++)})),o?c>=o:a},getDateMS:d,getDateISOLocal:p,isDeliveryForm:function(e){return i.includes(e.form)},getMostRecentReport:s,getNewestPregnancyTimestamp:function(e){if(!e.contact)return;const t=s(e.reports,'pregnancy');return t?t.reported_date:0},getNewestDeliveryTimestamp:function(e){if(!e.contact)return;const t=s(e.reports,'delivery');return t?t.reported_date:0},getReportsSubmittedInWindow:function(e,t,n,r,i){const o=[];return e.forEach((function(e){t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r&&(i&&!i(e)||o.push(e))})),o},countReportsSubmittedInWindow:function(e,t,n,r,i){let o=0;return e.forEach((function(e){t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r&&(i&&!i(e)||o++)})),o},countANCFacilityVisits:function(e,t){let n=0;const r=m(e,t);return c(t,'anc_visits_hf.anc_visits_hf_past')&&!isNaN(c(t,'anc_visits_hf.anc_visits_hf_past.visited_hf_count'))&&(n+=parseInt(c(t,'anc_visits_hf.anc_visits_hf_past.visited_hf_count'))),n+=r.reduce((function(e,t){const n=c(t,'anc_visits_hf.anc_visits_hf_past');return n?(e+='yes'===n.last_visit_attended&&1,isNaN(n.visited_hf_count)?e:e+('yes'===n.report_other_visits&&parseInt(n.visited_hf_count))):0}),0),n},isFacilityDelivery:function(e,t){return!!e&&(1===arguments.length&&(t=e),'yes'===c(t,'facility_delivery'))},getMostRecentLMPDateForPregnancy:v,getNewestReport:y,getSubsequentPregnancyFollowUps:m,isActivePregnancy:function(e,t){if(!_(t))return!1;const r=(v(e,t)||t.reported_date)>n-254016e5,i=function(e,t,r){return e.reports.filter((function(e){return'delivery'===e.form&&e.reported_date>t.reported_date&&(!r||t.reported_date>=n-864e5*r)}))}(e,t,42).length>0,a=function(e,t){return e.reports.filter((function(e){return _(e)&&e.reported_date>t.reported_date}))}(e,t).length>0;return r&&!i&&!a&&!function(e,t){const n=m(e,t),r=y(n,o);return r&&'abortion'===c(r,'pregnancy_summary.visit_option')}(e,t)&&!function(e,t){const n=m(e,t),r=y(n,o);return r&&'miscarriage'===c(r,'pregnancy_summary.visit_option')}(e,t)},getRecentANCVisitWithEvent:function(e,t,n){const r=m(e,t),i=y(r,o);if(i&&c(i,'pregnancy_summary.visit_option')===n)return i},isPregnancyTaskMuted:function(e){const t=y(e.reports,a);return t&&f(t)&&'clear_all'===c(t,'pregnancy_ended.clear_option')},getField:c}},function(e,t){e.exports=function(e){var t={};!function e(t,n){var r=Object.keys(t);for(var i in r){var o=r[i];switch(typeof t[o]){case'object':e(t[o],n);break;case'function':t[o]=t[o].bind(n)}}}(e,t),t.definition=function e(t){var n=Object.assign({},t),r=Object.keys(n);for(var i in r){var o=r[i];if(Array.isArray(n[o])){n[o]=n[o].slice(0);for(var a=0;a({id:'pregnancy-home-visit-week'+e,start:t,end:n,dueDate:function(t,n,r){const i=_(n,r);return f(i||r.reported_date,7*e)}});function h(e,t,n,r){if(t.reported_date=o},resolvedIf:h,actions:[{type:'report',form:'pregnancy_home_visit',label:'Pregnancy home visit'}],events:[...Array(21).keys()].map(e=>v(2*(e+1),6,7))},{name:'anc.facility_reminder',icon:'icon-pregnancy',title:'task.anc.facility_reminder.title',appliesTo:'reports',appliesToType:['pregnancy','pregnancy_home_visit'],appliesIf:function(e,t){return m(t,'t_pregnancy_follow_up_date')},resolvedIf:function(e,t,n,r){if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date),o=f(r,n.end+1).getTime();return p(e.reports,['pregnancy_facility_visit_reminder'],i,o)},actions:[{type:'report',form:'pregnancy_facility_visit_reminder',label:'Pregnancy facility visit reminder',modifyContent:function(e,t,n){e.source_visit_date=m(n,'t_pregnancy_follow_up_date')}}],events:[{id:'pregnancy-facility-visit-reminder',start:3,end:7,dueDate:function(e,t,n){return l(m(n,'t_pregnancy_follow_up_date'))}}]},{name:'anc.pregnancy_danger_sign_followup',icon:'icon-pregnancy-danger',title:'task.anc.pregnancy_danger_sign_followup.title',appliesTo:'reports',appliesToType:['pregnancy','pregnancy_home_visit','pregnancy_danger_sign','pregnancy_danger_sign_follow_up'],appliesIf:function(e,t){return'yes'===m(t,'t_danger_signs_referral_follow_up')&&s(e)},resolvedIf:function(e,t,n,r){if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date+1),o=f(r,n.end+1).getTime();return p(e.reports,['pregnancy_danger_sign_follow_up'],i,o)},actions:[{type:'report',form:'pregnancy_danger_sign_follow_up'}],events:[{id:'pregnancy-danger-sign-follow-up',start:3,end:7,dueDate:function(e,t,n){return l(m(n,'t_danger_signs_referral_follow_up_date'))}}]},{name:'anc.delivery',icon:'icon-mother-child',title:'task.anc.delivery.title',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){const n=_(e,t);return n&&f(n,336)>=o&&s(e)},resolvedIf:function(e,t,n,r){if(y(e,t,'abortion')||y(e,t,'miscarriage'))return!0;if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date),o=f(r,n.end+1).getTime();return p(e.reports,['delivery'],i,o)},actions:[{type:'report',form:'delivery'}],events:[{id:'delivery-reminder',start:28,end:42,dueDate:function(e,t,n){return f(_(t,n),i)}}]},{name:'pnc.danger_sign_followup_mother',icon:'icon-follow-up',title:'task.pnc.danger_sign_followup_mother.title',appliesTo:'reports',appliesToType:['delivery','pnc_danger_sign_follow_up_mother'],appliesIf:function(e,t){return'yes'===m(t,'t_danger_signs_referral_follow_up')&&s(e)},resolvedIf:function(e,t,n,r){if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date+1),o=f(r,n.end+1).getTime();return p(e.reports,['pnc_danger_sign_follow_up_mother'],i,o)},actions:[{type:'report',form:'pnc_danger_sign_follow_up_mother',modifyContent:function(e,t,n){u(n)?e.delivery_uuid=n._id:e.delivery_uuid=m(n,'inputs.delivery_uuid')}}],events:[{id:'pnc-danger-sign-follow-up-mother',start:3,end:7,dueDate:function(e,t,n){return l(m(n,'t_danger_signs_referral_follow_up_date'))}}]},{name:'pnc.danger_sign_followup_baby.from_contact',icon:'icon-follow-up',title:'task.pnc.danger_sign_followup_baby.title',appliesTo:'contacts',appliesToType:['person'],appliesIf:function(e){return e.contact&&'yes'===e.contact.t_danger_signs_referral_follow_up&&s(e)},resolvedIf:function(e,t,n,r){const i=Math.max(f(r,-n.start).getTime(),e.contact.reported_date),o=f(r,n.end).getTime();return p(e.reports,['pnc_danger_sign_follow_up_baby'],i,o)},actions:[{type:'report',form:'pnc_danger_sign_follow_up_baby',modifyContent:function(e,t){e.delivery_uuid=t.contact.created_by_doc}}],events:[{id:'pnc-danger-sign-follow-up-baby',start:3,end:7,dueDate:function(e,t){return l(t.contact.t_danger_signs_referral_follow_up_date)}}]},{name:'pnc.danger_sign_followup_baby.from_report',icon:'icon-follow-up',title:'task.pnc.danger_sign_followup_baby.title',appliesTo:'reports',appliesToType:['pnc_danger_sign_follow_up_baby'],appliesIf:function(e,t){return'yes'===m(t,'t_danger_signs_referral_follow_up')&&s(e)},resolvedIf:function(e,t,n,r){if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date+1),o=f(r,n.end+1).getTime();return p(e.reports,['pnc_danger_sign_follow_up_baby'],i,o)},actions:[{type:'report',form:'pnc_danger_sign_follow_up_baby',modifyContent:function(e,t,n){e.delivery_uuid=m(n,'inputs.delivery_uuid')}}],events:[{id:'pnc-danger-sign-follow-up-baby',start:3,end:7,dueDate:function(e,t,n){return l(m(n,'t_danger_signs_referral_follow_up_date'))}}]}]},function(e,t,n){const r=n(0),{isAlive:i,getSubsequentPregnancyFollowUps:o,getMostRecentLMPDateForPregnancy:a,isActivePregnancy:c,countANCFacilityVisits:s,getField:p}=r;e.exports=[{id:'deaths-this-month',type:'count',icon:'icon-death-general',goal:0,translation_key:'targets.death_reporting.deaths.title',subtitle_translation_key:'targets.this_month.subtitle',appliesTo:'contacts',appliesToType:['person'],appliesIf:function(e){return!i(e)},date:e=>e.contact.date_of_death},{id:'pregnancy-registrations-this-month',type:'count',icon:'icon-pregnancy',goal:20,translation_key:'targets.anc.new_pregnancy_registrations.title',subtitle_translation_key:'targets.this_month.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){return!!t&&a(e,t)},date:'reported',idType:'contact'},{id:'births-this-month',type:'count',icon:'icon-infant',goal:-1,translation_key:'targets.births.title',subtitle_translation_key:'targets.this_month.subtitle',appliesTo:'contacts',appliesToType:['person'],appliesIf:function(e){return e&&e.contact&&e.contact.date_of_birth},date:e=>e.contact.date_of_birth,dhis:{dataElement:'kB0ZBFisE0e'}},{id:'active-pregnancies',type:'count',icon:'icon-pregnancy',goal:-1,translation_key:'targets.anc.active_pregnancies.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){return c(e,t)},date:'now',idType:'contact'},{id:'active-pregnancies-1+-visits',type:'count',icon:'icon-clinic',goal:-1,translation_key:'targets.anc.active_pregnancies_1p_visits.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){if(!c(e,t))return!1;return s(e,t)>0},date:'now',idType:'contact'},{id:'facility-deliveries',type:'percent',icon:'icon-mother-child',goal:-1,translation_key:'targets.anc.facility_deliveries.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['delivery'],appliesIf:function(e,t){return p(t,'delivery_outcome.delivery_place')},passesIf:function(e,t){return'health_facility'===p(t,'delivery_outcome.delivery_place')},date:'now',idType:'contact',dhis:{dataElement:'e22tIwy1nKR',categoryOptionCombo:'HllvX50cXC0',attributeOptionCombo:'HllvX50cXC0'}},{id:'active-pregnancies-4+-visits',type:'count',icon:'icon-clinic',goal:-1,translation_key:'targets.anc.active_pregnancies_4p_visits.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){if(!c(e,t))return!1;return s(e,t)>3},date:'now',idType:'contact'},{id:'active-pregnancies-8+-contacts',type:'count',icon:'icon-follow-up',goal:-1,translation_key:'targets.anc.active_pregnancies_8p_contacts.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){if(!c(e,t))return!1;return 1+(o(e,t).length||0)+(s(e,t)||0)>7},date:'now',idType:'contact'}]},function(e,t,n){var r=n(1),i=n(6);function o(e,t,n,r,i,o){var a;if(e.appliesToType){var c;if('contacts'===e.appliesTo){if(!i.contact)return;c='contact'===i.contact.type?i.contact.contact_type:i.contact.type}else{if(!o)return;c=o.form}if(-1===e.appliesToType.indexOf(c))return}if('scheduled_tasks'===e.appliesTo||!e.appliesIf||e.appliesIf(i,o))if('scheduled_tasks'===e.appliesTo){if(o&&e.appliesIf){if(!o.scheduled_tasks)return;for(a=0;a ", - "silence_type": "PNC Reminders", - "silence_for": "0 days", - "fields": [ - { - "field_name": "patient_id", - "title": "Patient ID" - }, - { - "field_name": "pnc_visits", - "title": "No. of PNC visits" - } - ], - "validations": { - "join_responses": false, - "list": [ - { - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} सन्देशको ढांचा मिलेन! ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! प <दर्ता नं (५ देखि १३ अंकको)> <हालसम्मको जम्मा सुत्केरी जाँच भेट संख्या>", - "locale": "ne" - } - ] - }, - { - "property": "pnc_visits", - "rule": "(integer && min(0) && max(4))", - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} को सुत्केरी जाँच भेट संख्या मिलेन। ० देखि ४ बीचको संख्या हुनु पर्दछ। मिलाएर पठाउनु होला, धन्यवाद।", - "locale": "ne" - } - ] - } - ] - }, - "messages": [ - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} को सुत्केरी जाँच प्रतिवेदन पठाउनु भएकोमा धन्यवाद।", - "locale": "ne" - } - ], - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! सन्देशको ढांचा मिलेन! ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! प <दर्ता नं (५ देखि १३ अंकको)> <हालसम्मको जम्मा सुत्केरी जाँच भेट संख्या>", - "locale": "ne" - } - ], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] + "id": "deaths-this-month", + "type": "count", + "goal": 0, + "translation_key": "targets.death_reporting.deaths.title", + "icon": "icon-death-general", + "subtitle_translation_key": "targets.this_month.subtitle" }, { - "form": "G", - "name": "Visits", - "format": "G ", - "silence_type": "ANC Reminders LMP", - "silence_for": "0 days", - "fields": [ - { - "field_name": "", - "title": "" - } - ], - "validations": { - "join_responses": false, - "list": [ - { - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! G <दर्ता नं (५ देखि १३ अंकको)>", - "locale": "ne" - } - ] - } - ] - }, - "messages": [ - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_name}} ({{patient_id}}) लाई भेटेकोमा धन्यबाद!", - "locale": "ne" - } - ], - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन।दर्ता नं सहि भएको एकिन गरी तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यबाद! G <दर्ता नं (५ देखि १३ अंकको)>", - "locale": "ne" - } - ], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] + "id": "pregnancy-registrations-this-month", + "type": "count", + "goal": 20, + "translation_key": "targets.anc.new_pregnancy_registrations.title", + "icon": "icon-pregnancy", + "subtitle_translation_key": "targets.this_month.subtitle" }, { - "form": "D", - "name": "Flag", - "format": "D ", - "silence_type": "", - "silence_for": "", - "fields": [ - { - "field_name": "patient_id", - "title": "Patient ID" - } - ], - "validations": { - "join_responses": false, - "list": [ - { - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [ - { - "content": "{{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! D <दर्ता नं (५ देखि १३ अंकको)>", - "locale": "ne" - } - ] - } - ] - }, - "messages": [ - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते! {{patient_name}} {{patient_id}}का लागि खतराको चिन्ह पहिचान भएको रिपोर्ट प्राप्त भयो। उहाँलाई बिशेष निगरानीमा राखी आवश्यकता अनुसार स्वास्थ्य संस्था पठाउनुहोला।", - "locale": "ne" - } - ], - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते! {{contact.name}}ले {{patient_name}} {{patient_id}} का लागि खतराको चिन्ह पहिचान गर्नुभएको छ । म.स्वा.स्व.से.संग सोधखोज गरि आवश्यक सल्लाह दिनुहोला |", - "locale": "ne" - } - ], - "event_type": "report_accepted", - "recipient": "parent" - }, - { - "translation_key": "", - "message": [ - { - "content": "{{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी पुन: फारम पठाउनुहोला, धन्यबाद!", - "locale": "ne" - } - ], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] + "id": "births-this-month", + "type": "count", + "goal": -1, + "translation_key": "targets.births.title", + "icon": "icon-infant", + "subtitle_translation_key": "targets.this_month.subtitle", + "dhis": { + "dataElement": "kB0ZBFisE0e" + } }, { - "form": "P", - "name": "PNC Visit", - "format": "P ", - "silence_type": "PNC Reminders", - "silence_for": "0 days", - "fields": [ - { - "field_name": "patient_id", - "title": "Patient ID" - }, - { - "field_name": "pnc_visits", - "title": "No. of PNC visits" - } - ], - "validations": { - "join_responses": false, - "list": [ - { - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} सन्देशको ढांचा मिलेन! ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन: फारम पठाउनुहोला, धन्यवाद! P <दर्ता नं (५ देखि १३ अंकको)> <हालसम्मको जम्मा सुत्केरी जाँच भेट संख्या>", - "locale": "ne" - } - ] - }, - { - "property": "pnc_visits", - "rule": "(integer && min(0) && max(4))", - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} को सुत्केरी जाँच भेट संख्या मिलेन। ० देखि ४ बीचको संख्या हुनु पर्दछ। मिलाएर पठाउनु होला, धन्यवाद।", - "locale": "ne" - } - ] - } - ] - }, - "messages": [ - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} को सुत्केरी जाँच प्रतिवेदन पठाउनु भएकोमा धन्यवाद।", - "locale": "ne" - } - ], - "event_type": "report_accepted", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! सन्देशको ढांचा मिलेन! ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! P <दर्ता नं (५ देखि १३ अंकको)> <हालसम्मको जम्मा सुत्केरी जाँच भेट संख्या>", - "locale": "ne" - } - ], - "event_type": "registration_not_found", - "recipient": "clinic" - } - ] - } - ], - "muting": { - "mute_forms": [ - "ब", - "B" - ], - "unmute_forms": [ - "स", - "F" - ], - "validations": { - "join_responses": false, - "list": [ - { - "property": "patient_id", - "rule": "regex('^[0-9]{5,13}$')", - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} दर्ता नं मिलेन! कृपया ५ देखि १३ अंकको सहि दर्ता नं प्रयोग गरि तलको ढांचा अनुसार पुन:फारम पठाउनुहोला, धन्यवाद! सूचना बन्द गरेको भए : ब वा B <दर्ता नं (५ देखि १३ अंकको)>, सूचना सुरु गरेको भए: स वा F <दर्ता नं (५ देखि १३ अंकको)>", - "locale": "ne" - } - ] - } - ] - }, - "messages": [ - { - "translation_key": "", - "message": [ - { - "content": "धन्यवाद, {{contact.name}}! {{patient_name}} {{patient_id}} का लागि सबै सुचना बन्द गरिएको छ। सुचारु गर्नका लागि स वा F {{patient_id}} लेखि SMS पठाउनुहोला।", - "locale": "ne" - } - ], - "event_type": "mute", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [ - { - "content": "{{contact.name}} ले {{patient_name}} ({{patient_id}}) का लागि सबै सुचना बन्द गराउनु भएको छ।", - "locale": "ne" - } - ], - "event_type": "mute", - "recipient": "parent" - }, - { - "translation_key": "", - "message": [ - { - "content": "धन्यवाद, {{contact.name}}! {{patient_name}} ({{patient_id}}) का लागि सुचना सुचारु गरिएको छ।", - "locale": "ne" - } - ], - "event_type": "unmute", - "recipient": "clinic" - }, - { - "translation_key": "", - "message": [ - { - "content": "नमस्ते {{contact.name}}! {{patient_id}} कुनै पनि महिलालाई दिईएको छैन। दर्ता नं सहि भएको एकिन गरी पुन: फारम पठाउनुहोला, धन्यबाद!", - "locale": "ne" - } - ], - "event_type": "contact_not_found", - "recipient": "clinic" - } - ] - }, - "_version": "3.17.1", - "outgoing_deny_list": "1414, 9771414, +9771414, 1408, 9771408, +9771408, 1415, 9771415, +9771415, 1455, 9771455, +9771455, 9851028801, 9779851028801, +9779851028801, 1477, +9771477, NT, 34455, +97734455, 1422, +9771422", - "outgoing_deny_shorter_than": 13, - "outgoing_deny_with_alphas": true, - "multipart_sms_limit": 10, - "sms": { - "outgoing_service": "medic-gateway" - }, - "forms": { - "NP": { - "meta": { - "code": "NP", - "translation_key": "forms.np.title", - "label": { - "en": "New Person with phone_number Registration" - } - }, - "fields": { - "patient_age": { - "labels": { - "tiny": { - "en": "Person Age" - }, - "short": { - "en": "Person Age" - } - }, - "position": 0, - "type": "integer", - "length": [ - 1, - 3 - ], - "range": [ - 0, - 125 - ], - "required": true - }, - "patient_name": { - "labels": { - "tiny": { - "en": "patient_name" - }, - "description": { - "en": "Patient name" - }, - "short": { - "en": "Patient name" - } - }, - "position": 1, - "type": "string", - "length": [ - 3, - 30 - ], - "required": true - }, - "phone_number": { - "labels": { - "tiny": { - "en": "phone number" - }, - "description": { - "en": "phone number" - }, - "short": { - "en": "phone number" - } - }, - "position": 2, - "type": "phone_number", - "required": true - } - }, - "public_form": false, - "use_sentinel": true - }, - "क": { - "meta": { - "code": "क", - "translation_key": "forms.ka.title" - }, - "fields": { - "patient_age": { - "labels": { - "tiny": { - "en": "Person Age" - }, - "short": { - "en": "Person Age" - } - }, - "position": 0, - "type": "integer", - "length": [ - 1, - 3 - ], - "range": [ - 0, - 125 - ], - "required": true - }, - "patient_name": { - "labels": { - "tiny": { - "en": "patient_name" - }, - "short": { - "en": "Patient Name" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 40 - ], - "required": true - } - }, - "public_form": false, - "use_sentinel": true - }, - "अ": { - "meta": { - "code": "अ", - "translation_key": "forms.a.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "patient_id" - }, - "short": { - "en": "Patient Id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "last_menstrual_period": { - "labels": { - "tiny": { - "en": "LMP" - }, - "short": { - "en": "Weeks since LMP" - } - }, - "position": 1, - "type": "integer", - "length": [ - 1, - 2 - ], - "range": [ - 6, - 42 - ], - "required": true - } - }, - "public_form": false, - "use_sentinel": true - }, - "ग": { - "meta": { - "code": "ग", - "translation_key": "forms.ga.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "id" - }, - "short": { - "en": "ID" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 20 - ], - "required": false - } - }, - "use_sentinel": true - }, - "ज": { - "meta": { - "code": "ज", - "translation_key": "forms.ja.title" - }, - "fields": { - "delivery_code": { - "labels": { - "short": { - "en": "Delivery Code" - } - }, - "position": 0, - "type": "string", - "length": [ - 1, - 2 - ], - "required": true - }, - "patient_id": { - "labels": { - "short": { - "en": "ID" - } - }, - "position": 1, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "days_since_birth": { - "labels": { - "short": { - "en": "Days Since Birth" - }, - "tiny": { - "en": "DSB" - } - }, - "position": 2, - "type": "integer", - "length": [ - 1, - 2 - ], - "range": [ - 1, - 50 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 3, - "type": "string", - "length": [ - 1, - 20 - ], - "required": false - } - }, - "use_sentinel": true - }, - "ख": { - "meta": { - "code": "ख", - "translation_key": "forms.kha.title" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "en": "ID" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "use_sentinel": true + "id": "active-pregnancies", + "type": "count", + "goal": -1, + "translation_key": "targets.anc.active_pregnancies.title", + "icon": "icon-pregnancy", + "subtitle_translation_key": "targets.all_time.subtitle" }, - "ब": { - "meta": { - "code": "ब", - "translation_key": "forms.ba.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "ID" - }, - "short": { - "en": "ID" - } - }, - "position": 0, - "length": [ - 5, - 13 - ], - "type": "string", - "required": true - }, - "notes": { - "labels": { - "tiny": { - "en": "r", - "sw": "r" - }, - "short": { - "en": "Reason" - } - }, - "position": 1, - "length": [ - 3, - 100 - ], - "type": "string" - } - }, - "use_sentinel": true - }, - "स": { - "meta": { - "code": "स", - "translation_key": "forms.sa.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "ID" - }, - "short": { - "en": "ID" - } - }, - "position": 0, - "length": [ - 5, - 13 - ], - "type": "string" - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "use_sentinel": true - }, - "प": { - "meta": { - "code": "प", - "translation_key": "forms.pa.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "ID" - }, - "short": { - "en": "ID" - } - }, - "position": 0, - "length": [ - 5, - 13 - ], - "type": "string", - "required": true - }, - "pnc_visits": { - "labels": { - "tiny": { - "en": "PNCV" - }, - "short": { - "en": "PNC Visits" - } - }, - "position": 1, - "length": [ - 1, - 1 - ], - "range": [ - 0, - 4 - ], - "type": "string", - "required": true - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 2, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "use_sentinel": true - }, - "म": { - "meta": { - "code": "म", - "translation_key": "forms.ma.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "id" - }, - "short": { - "en": "Patient Id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "death_notes": { - "labels": { - "tiny": { - "en": "note" - }, - "short": { - "en": "Notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 20 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "ल": { - "meta": { - "code": "ल", - "translation_key": "forms.la.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "patient_id" - }, - "short": { - "en": "Patient Id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "lmp_year": { - "labels": { - "tiny": { - "en": "LMP_Y" - }, - "short": { - "en": "LMP Year" - } - }, - "position": 1, - "type": "bsYear", - "required": true - }, - "lmp_month": { - "labels": { - "tiny": { - "en": "LMP_M" - }, - "short": { - "en": "LMP Month" - } - }, - "position": 2, - "type": "bsMonth", - "required": true - }, - "lmp_day": { - "labels": { - "tiny": { - "en": "LMP_D" - }, - "short": { - "en": "LMP Day" - } - }, - "position": 3, - "type": "bsDay", - "required": true - }, - "lmp_date": { - "labels": { - "tiny": { - "en": "LMP_DATE" - }, - "short": { - "en": "LMP Date" - } - }, - "position": 4, - "type": "bsAggreDate", - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "K": { - "meta": { - "code": "K", - "translation_key": "forms.k.title" - }, - "fields": { - "patient_age": { - "labels": { - "tiny": { - "en": "Person Age" - }, - "short": { - "en": "Person Age" - } - }, - "position": 0, - "type": "integer", - "length": [ - 1, - 3 - ], - "range": [ - 0, - 125 - ], - "required": true - }, - "patient_name": { - "labels": { - "tiny": { - "en": "patient_name" - }, - "short": { - "en": "Patient Name" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 40 - ], - "required": true - } - }, - "public_form": false, - "use_sentinel": true - }, - "A": { - "meta": { - "code": "A", - "translation_key": "forms.a_en.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "patient_id" - }, - "short": { - "en": "Patient Id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "last_menstrual_period": { - "labels": { - "tiny": { - "en": "LMP" - }, - "short": { - "en": "Weeks since LMP" - } - }, - "position": 1, - "type": "integer", - "length": [ - 1, - 2 - ], - "range": [ - 6, - 42 - ], - "required": true - } - }, - "public_form": false, - "use_sentinel": true - }, - "L": { - "meta": { - "code": "L", - "translation_key": "forms.l.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "patient_id" - }, - "short": { - "en": "Patient Id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "lmp_year": { - "labels": { - "tiny": { - "en": "LMP_Y" - }, - "short": { - "en": "LMP Year" - } - }, - "position": 1, - "type": "bsYear", - "required": true - }, - "lmp_month": { - "labels": { - "tiny": { - "en": "LMP_M" - }, - "short": { - "en": "LMP Month" - } - }, - "position": 2, - "type": "bsMonth", - "required": true - }, - "lmp_day": { - "labels": { - "tiny": { - "en": "LMP_D" - }, - "short": { - "en": "LMP Day" - } - }, - "position": 3, - "type": "bsDay", - "required": true - }, - "lmp_date": { - "labels": { - "tiny": { - "en": "LMP_DATE" - }, - "short": { - "en": "LMP Date" - } - }, - "position": 4, - "type": "bsAggreDate", - "required": false - } - }, - "public_form": false, - "use_sentinel": true - }, - "G": { - "meta": { - "code": "G", - "translation_key": "forms.g.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "id" - }, - "short": { - "en": "ID" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 20 - ], - "required": false - } - }, - "use_sentinel": true - }, - "J": { - "meta": { - "code": "J", - "translation_key": "forms.j.title" - }, - "fields": { - "delivery_code": { - "labels": { - "short": { - "en": "Delivery Code" - } - }, - "position": 0, - "type": "string", - "length": [ - 1, - 2 - ], - "required": true - }, - "patient_id": { - "labels": { - "short": { - "en": "ID" - } - }, - "position": 1, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "days_since_birth": { - "labels": { - "short": { - "en": "Days Since Birth" - }, - "tiny": { - "en": "DSB" - } - }, - "position": 2, - "type": "integer", - "length": [ - 1, - 2 - ], - "range": [ - 1, - 50 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 3, - "type": "string", - "length": [ - 1, - 20 - ], - "required": false - } - }, - "use_sentinel": true - }, - "D": { - "meta": { - "code": "D", - "translation_key": "forms.d.title" - }, - "fields": { - "patient_id": { - "labels": { - "short": { - "en": "ID" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "use_sentinel": true - }, - "B": { - "meta": { - "code": "B", - "translation_key": "forms.b.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "ID" - }, - "short": { - "en": "ID" - } - }, - "position": 0, - "length": [ - 5, - 13 - ], - "type": "string", - "required": true - }, - "notes": { - "labels": { - "tiny": { - "en": "r", - "sw": "r" - }, - "short": { - "en": "Reason" - } - }, - "position": 1, - "length": [ - 3, - 100 - ], - "type": "string" - } - }, - "use_sentinel": true - }, - "F": { - "meta": { - "code": "F", - "translation_key": "forms.f.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "ID" - }, - "short": { - "en": "ID" - } - }, - "position": 0, - "length": [ - 5, - 13 - ], - "type": "string" - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "use_sentinel": true - }, - "P": { - "meta": { - "code": "P", - "translation_key": "forms.p.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "ID" - }, - "short": { - "en": "ID" - } - }, - "position": 0, - "length": [ - 5, - 13 - ], - "type": "string", - "required": true - }, - "pnc_visits": { - "labels": { - "tiny": { - "en": "PNCV" - }, - "short": { - "en": "PNC Visits" - } - }, - "position": 1, - "length": [ - 1, - 1 - ], - "range": [ - 0, - 4 - ], - "type": "string", - "required": true - }, - "notes": { - "labels": { - "short": { - "en": "Notes" - } - }, - "position": 2, - "type": "string", - "length": [ - 1, - 100 - ], - "required": false - } - }, - "use_sentinel": true - }, - "M": { - "meta": { - "code": "M", - "translation_key": "forms.m.title" - }, - "fields": { - "patient_id": { - "labels": { - "tiny": { - "en": "id" - }, - "short": { - "en": "Patient Id" - } - }, - "position": 0, - "type": "string", - "length": [ - 5, - 13 - ], - "required": true - }, - "death_notes": { - "labels": { - "tiny": { - "en": "note" - }, - "short": { - "en": "Notes" - } - }, - "position": 1, - "type": "string", - "length": [ - 1, - 20 - ], - "required": false - } - }, - "public_form": false, - "use_sentinel": true - } - }, - "schedules": [ { - "name": "ANC Reminders LMP", - "translation_key": "schedule.anc_lmp", - "summary": "ANC Reminders", - "description": "", - "start_from": "lmp_date", - "messages": [ - { - "translation_key": "messages.schedule.anc.reminder_1_1", - "group": 1, - "offset": "57 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_1_2", - "group": 1, - "offset": "70 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_1_not_done", - "group": 1, - "offset": "77 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_1_parent", - "group": 1, - "offset": "77 days", - "send_day": "", - "send_time": "4:30", - "recipient": "parent" - }, - { - "translation_key": "messages.schedule.anc.reminder_2_1", - "group": 2, - "offset": "85 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_2_2", - "group": 2, - "offset": "98 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_2_not_done", - "group": 2, - "offset": "105 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_2_parent", - "group": 2, - "offset": "105 days", - "send_day": "", - "send_time": "4:30", - "recipient": "parent" - }, - { - "translation_key": "messages.schedule.anc.reminder_3_1", - "group": 3, - "offset": "134 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_3_2", - "group": 3, - "offset": "148 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_3_not_done", - "group": 3, - "offset": "161 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_3_parent", - "group": 3, - "offset": "161 days", - "send_day": "", - "send_time": "4:30", - "recipient": "parent" - }, - { - "translation_key": "messages.schedule.anc.reminder_4_1", - "group": 4, - "offset": "188 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_4_not_done", - "group": 4, - "offset": "194 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_4_parent", - "group": 4, - "offset": "194 days", - "send_day": "", - "send_time": "4:30", - "recipient": "parent" - }, - { - "translation_key": "messages.schedule.anc.reminder_5_1", - "group": 5, - "offset": "216 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_5_not_done", - "group": 5, - "offset": "222 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_5_parent", - "group": 5, - "offset": "222 days", - "send_day": "", - "send_time": "4:30", - "recipient": "parent" - }, - { - "translation_key": "messages.schedule.anc.reminder_6_1", - "group": 6, - "offset": "230 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_6_not_done", - "group": 6, - "offset": "236 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_6_parent", - "group": 6, - "offset": "236 days", - "send_day": "", - "send_time": "4:30", - "recipient": "parent" - }, - { - "translation_key": "messages.schedule.anc.reminder_7_1", - "group": 7, - "offset": "244 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_7_not_done", - "group": 7, - "offset": "250 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_7_parent", - "group": 7, - "offset": "250 days", - "send_day": "", - "send_time": "4:30", - "recipient": "parent" - }, - { - "translation_key": "messages.schedule.anc.reminder_8_1", - "group": 8, - "offset": "260 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_8_not_done", - "group": 8, - "offset": "273 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.reminder_8_parent", - "group": 8, - "offset": "273 days", - "send_day": "", - "send_time": "4:30", - "recipient": "parent" - } - ] + "id": "active-pregnancies-1+-visits", + "type": "count", + "goal": -1, + "translation_key": "targets.anc.active_pregnancies_1p_visits.title", + "icon": "icon-clinic", + "subtitle_translation_key": "targets.all_time.subtitle" }, { - "name": "PNC Reminders", - "translation_key": "schedule.pnc_rem", - "summary": "", - "description": "Scheduled reminders for PNC", - "start_from": "birth_date", - "messages": [ - { - "translation_key": "messages.schedule.pnc.day_7", - "group": 1, - "offset": "7 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.pnc.day_15", - "group": 1, - "offset": "15 days", - "send_day": "", - "send_time": "4:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.pnc.day_15_HF", - "group": 1, - "offset": "15 days", - "send_day": "", - "send_time": "5:00", - "recipient": "parent" - }, - { - "translation_key": "messages.schedule.pnc.day_40", - "group": 2, - "offset": "40 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.pnc.day_45", - "group": 2, - "offset": "45 days", - "send_day": "", - "send_time": "4:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.pnc.day_45_HF", - "group": 2, - "offset": "45 days", - "send_day": "", - "send_time": "5:00", - "recipient": "parent" - } - ] + "id": "facility-deliveries", + "type": "percent", + "goal": -1, + "translation_key": "targets.anc.facility_deliveries.title", + "icon": "icon-mother-child", + "subtitle_translation_key": "targets.all_time.subtitle", + "dhis": { + "dataElement": "e22tIwy1nKR", + "categoryOptionCombo": "HllvX50cXC0", + "attributeOptionCombo": "HllvX50cXC0" + } }, { - "name": "Registration Reminders", - "translation_key": "schedule.registration.new_person", - "summary": "", - "description": "Scheduled reminders for Person Registration", - "start_from": "reported_date", - "messages": [ - { - "translation_key": "messages.schedule.registration.day_1", - "group": 1, - "offset": "1 days", - "send_day": "", - "send_time": "5:30 +05:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.registration.day_3", - "group": 2, - "offset": "3 days", - "send_day": "", - "send_time": "5:30 +05:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.registration.day_14", - "group": 3, - "offset": "14 days", - "send_day": "", - "send_time": "5:30 +05:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.registration.day_14.hf", - "group": 3, - "offset": "14 days", - "send_day": "", - "send_time": "10:30 +05:45", - "recipient": "health_center" - } - ] + "id": "active-pregnancies-4+-visits", + "type": "count", + "goal": -1, + "translation_key": "targets.anc.active_pregnancies_4p_visits.title", + "icon": "icon-clinic", + "subtitle_translation_key": "targets.all_time.subtitle" }, { - "name": "Delivery Reminders", - "translation_key": "schedule.delivery_lmp", - "summary": "", - "description": "Scheduled reminders for Delivery", - "start_from": "lmp_date", - "messages": [ - { - "translation_key": "messages.schedule.anc.delivery_1", - "group": 1, - "offset": "266 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.delivery_2", - "group": 1, - "offset": "273 days", - "send_day": "", - "send_time": "23:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.delivery_3", - "group": 2, - "offset": "287 days", - "send_day": "", - "send_time": "4:45", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.delivery_parent_1", - "group": 2, - "offset": "288 days", - "send_day": "", - "send_time": "4:45", - "recipient": "parent" - }, - { - "translation_key": "messages.schedule.anc.delivery_4", - "group": 3, - "offset": "315 days", - "send_day": "", - "send_time": "23:30", - "recipient": "clinic" - }, - { - "translation_key": "messages.schedule.anc.delivery_parent_2", - "group": 3, - "offset": "322 days", - "send_day": "", - "send_time": "4:45", - "recipient": "parent" - } - ] + "id": "active-pregnancies-8+-contacts", + "type": "count", + "goal": -1, + "translation_key": "targets.anc.active_pregnancies_8p_contacts.title", + "icon": "icon-follow-up", + "subtitle_translation_key": "targets.all_time.subtitle" } + ] + } + }, + "create_user_for_contacts": { + "replace_forms": [ + "replace_user" ] -} \ No newline at end of file + }, + "transitions": { + "accept_patient_reports": false, + "conditional_alerts": false, + "default_responses": false, + "update_sent_by": false, + "registration": false, + "update_clinics": false, + "update_notifications": false, + "update_scheduled_reports": false, + "generate_patient_id_on_people": true, + "death_reporting": true, + "create_user_for_contacts": false + } +} diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index fbd2e15b0e2..e1f6b57ec62 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -197,13 +197,21 @@ const setPhoneNumber = doc => { const phoneNumber = getPhoneNumber(doc); // By default for a valid phone number SmsParser itself adds a country code if not provided // So if country code is not present in phone_number throw error - const validPhone = phoneNumberParser.validate(config.getAll(), phoneNumber); + const app_settings = config.getAll(); + const validPhone = phoneNumberParser.validate(app_settings, phoneNumber); if (!validPhone) { throw new Error( `${doc.phoneNumber} submitted by ${doc.from} is not a valid phone number` ); } else { - doc.phone_number = phoneNumber; + if (app_settings.allow_duplicate_phone && utils.isPhoneUnique(phoneNumber)) { + doc.phone_number = phoneNumber; + } + else{ + throw new Error( + `${doc.phoneNumber} submitted by ${doc.from} is already registered` + ); + } } }; diff --git a/shared-libs/transitions/src/transitions/utils.js b/shared-libs/transitions/src/transitions/utils.js index 08e802984cb..f9442bb4c21 100644 --- a/shared-libs/transitions/src/transitions/utils.js +++ b/shared-libs/transitions/src/transitions/utils.js @@ -61,6 +61,11 @@ module.exports = { .query('medic-client/contacts_by_reference', { key: ['shortcode', id] }) .then(results => !(results && results.rows && results.rows.length)); }, + isPhoneUnique: (phoneNumber) => { + return db.medic + .query('medic-client/contacts_by_phone', { key: phoneNumber }) + .then(results => !(results && results.rows && results.rows.length)); + }, addUniqueId: (doc) => { return idGenerator.next().value.then(patientId => { doc.patient_id = patientId; From 5a447b76f16f80ee0921212f25f71eb4fcd62e05 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Fri, 14 Jul 2023 22:54:16 +0545 Subject: [PATCH 19/56] registration.js false positive and negative fix --- .../src/transitions/registration.js | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index e1f6b57ec62..5012bd430f4 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -13,6 +13,7 @@ const date = require('../date'); const phoneNumberParser = require('@medic/phone-number'); const contactTypesUtils = require('@medic/contact-types-utils'); +const { sassFalse } = require('sass'); const NAME = 'registration'; const PARENT_NOT_FOUND = 'parent_not_found'; @@ -198,20 +199,26 @@ const setPhoneNumber = doc => { // By default for a valid phone number SmsParser itself adds a country code if not provided // So if country code is not present in phone_number throw error const app_settings = config.getAll(); + app_settings.allow_duplicate_phone = false; const validPhone = phoneNumberParser.validate(app_settings, phoneNumber); if (!validPhone) { throw new Error( - `${doc.phoneNumber} submitted by ${doc.from} is not a valid phone number` + `${phoneNumber} submitted by ${doc.from} is not a valid phone number` ); - } else { - if (app_settings.allow_duplicate_phone && utils.isPhoneUnique(phoneNumber)) { - doc.phone_number = phoneNumber; - } - else{ + } + if(app_settings.allow_duplicate_phone){ + doc.phone_number = phoneNumber; + } + else { + transitionUtils.isPhoneUnique(phoneNumber).then(isUnique => { + if (isUnique) { + doc.phone_number = phoneNumber; + return; + } throw new Error( - `${doc.phoneNumber} submitted by ${doc.from} is already registered` + `${phoneNumber} submitted by ${doc.from} is already registered` ); - } + }); } }; From 08f71583743a30fe54844154f52e205dfa9d7213 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Wed, 19 Jul 2023 09:31:50 +0545 Subject: [PATCH 20/56] added duplicate phone optional logic and changed phone number to normal format --- api/src/services/report/smsparser.js | 2 +- .../src/transitions/registration.js | 3 +- .../sentinel/transitions/registration.spec.js | 37 ++++++++++++++++--- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/api/src/services/report/smsparser.js b/api/src/services/report/smsparser.js index 333ad7a9808..dee6288fec8 100644 --- a/api/src/services/report/smsparser.js +++ b/api/src/services/report/smsparser.js @@ -212,7 +212,7 @@ const fieldParsers = { return parseNum(stripInvisibleCharacters(raw)); }, phone_number: (raw) => { - const formattedAndValidatedPhone = phoneNumberParser.format(config.getAll(), raw); + const formattedAndValidatedPhone = phoneNumberParser.normalize(config.getAll(), raw); if (formattedAndValidatedPhone) { return formattedAndValidatedPhone; } else { diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 5012bd430f4..1b6b3693081 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -199,7 +199,8 @@ const setPhoneNumber = doc => { // By default for a valid phone number SmsParser itself adds a country code if not provided // So if country code is not present in phone_number throw error const app_settings = config.getAll(); - app_settings.allow_duplicate_phone = false; + // check if duplicate phones are allowed and + //based upon that register the patient or throw error. const validPhone = phoneNumberParser.validate(app_settings, phoneNumber); if (!validPhone) { throw new Error( diff --git a/tests/integration/sentinel/transitions/registration.spec.js b/tests/integration/sentinel/transitions/registration.spec.js index f395d782bb9..d8cb0d8ca73 100644 --- a/tests/integration/sentinel/transitions/registration.spec.js +++ b/tests/integration/sentinel/transitions/registration.spec.js @@ -97,8 +97,8 @@ describe('registration', () => { after(() => utils.revertDb([], true)); afterEach(() => utils.revertDb(getIds(contacts), true)); - it('should add valid phone to patient doc', () => { - const patient_phone = '+9779841123123'; + it.only('should add valid phone to patient doc', () => { + let patient_phone = '+9779841123123'; const settings = { transitions: { registration: true }, registrations: [{ @@ -125,8 +125,7 @@ describe('registration', () => { }], forms: { 'FORM-A': {} } }; - - const patientNameAndPhone = { // has just the `patient_name` field, and should create this person + const patientNameAndPhone = { // has just the `patient_name`and phone so should create this person _id: uuid(), type: 'data_record', form: 'FORM-A', @@ -141,12 +140,11 @@ describe('registration', () => { parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } } } }; - const docs = [ patientNameAndPhone ]; const docIds = getIds(docs); - + let newPatientId; return utils .updateSettings(settings, 'sentinel') .then(utils.saveDocs(docs)) @@ -156,6 +154,33 @@ describe('registration', () => { infos.forEach(info => { chai.expect(info).to.deep.nested.include({ 'transitions.registration.ok': true }); }); + }) + .then(() => utils.getDocs(docIds)) + .then(updated => { + console.log(updated); + chai.expect(updated[0].fields.phone_number).to.equal(patient_phone); + chai.expect(updated[0].patient_id).not.to.equal(undefined); + chai.expect(updated[0].tasks).to.have.lengthOf(1); + chai.expect(updated[0].tasks[0].messages[0]).to.include({ + to: '+9779841212345', + message: `Patient Minerva (${updated[0].patient_id}) added to Clinic` + }); + + newPatientId = updated[0].patient_id; + + return getContactsByReference([newPatientId, 'venus']); + }) + .then(patients => { + console.log(patients); + chai.expect(patients.rows[0].doc).to.deep.include({ + patient_id: newPatientId, + phone_number: patient_phone, + parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } }, + name: 'Minerva', + type: 'person', + created_by: 'person', + source_id: patientNameAndPhone._id, + }); }); }); From 4cef416d4879a1c09eee4f910b73126f18b61aa4 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Wed, 19 Jul 2023 11:00:13 +0545 Subject: [PATCH 21/56] "making double quotes to single quote from from-definition" --- api/tests/form-definitions.js | 86 +++++++++++++++++------------------ 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/api/tests/form-definitions.js b/api/tests/form-definitions.js index 3d17b7756fe..5865f21994f 100644 --- a/api/tests/form-definitions.js +++ b/api/tests/form-definitions.js @@ -792,74 +792,74 @@ exports.forms = { } } }, - "NP": { - "meta": { - "code": "NP", - "translation_key": "forms.np.title", - "label": { - "en": "New Person with phone_number Registration" + 'NP': { + 'meta': { + 'code': 'NP', + 'translation_key': 'forms.np.title', + 'label': { + 'en': 'New Person with phone_number Registration' } }, - "fields": { - "patient_age": { - "labels": { - "tiny": { - "en": "Person Age" + 'fields': { + 'patient_age': { + 'labels': { + 'tiny': { + 'en': 'Person Age' }, - "short": { - "en": "Person Age" + 'short': { + 'en': 'Person Age' } }, - "position": 0, - "type": "integer", - "length": [ + 'position': 0, + 'type': 'integer', + 'length': [ 1, 3 ], - "range": [ + 'range': [ 0, 125 ], - "required": true + 'required': true }, - "phone_number": { - "labels": { - "tiny": { - "en": "phone number" + 'phone_number': { + 'labels': { + 'tiny': { + 'en': 'phone number' }, - "description": { - "en": "phone number" + 'description': { + 'en': 'phone number' }, - "short": { - "en": "phone number" + 'short': { + 'en': 'phone number' } }, - "position": 1, - "type": "phone_number", - "required": true + 'position': 1, + 'type': 'phone_number', + 'required': true }, - "patient_name": { - "labels": { - "tiny": { - "en": "patient_name" + 'patient_name': { + 'labels': { + 'tiny': { + 'en': 'patient_name' }, - "description": { - "en": "Patient name" + 'description': { + 'en': 'Patient name' }, - "short": { - "en": "Patient name" + 'short': { + 'en': 'Patient name' } }, - "position": 2, - "type": "string", - "length": [ + 'position': 2, + 'type': 'string', + 'length': [ 3, 30 ], - "required": true + 'required': true } }, - "public_form": false, - "use_sentinel": true + 'public_form': false, + 'use_sentinel': true } }; From b2e617c2711b5fd99c0cdd4c1203fdfb8d9874d4 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Mon, 24 Jul 2023 22:56:49 +0545 Subject: [PATCH 22/56] unique phone check in both registration and validation.js --- .../src/transitions/registration.js | 25 ++++++++++--------- shared-libs/validation/src/validation.js | 5 ++++ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 1b6b3693081..16fd23615bb 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -194,33 +194,34 @@ const getPhoneNumber = doc => { return doc.fields.phone_number; }; -const setPhoneNumber = doc => { +const setPhoneNumber = options => { + const doc = options.doc; const phoneNumber = getPhoneNumber(doc); // By default for a valid phone number SmsParser itself adds a country code if not provided // So if country code is not present in phone_number throw error const app_settings = config.getAll(); - // check if duplicate phones are allowed and + const allow_duplicate_phone = app_settings.forms[doc.form].fields.phone_number.flags.allow_duplicate + //check if duplicate phones are allowed and //based upon that register the patient or throw error. const validPhone = phoneNumberParser.validate(app_settings, phoneNumber); + app_settings.registrations.find(obj => obj.form === doc.form) if (!validPhone) { - throw new Error( - `${phoneNumber} submitted by ${doc.from} is not a valid phone number` - ); + transitionUtils.addRejectionMessage(doc, options.registrationConfig, 'provided_phone_not_unique'); + return; } - if(app_settings.allow_duplicate_phone){ + if (allow_duplicate_phone) { doc.phone_number = phoneNumber; } else { transitionUtils.isPhoneUnique(phoneNumber).then(isUnique => { if (isUnique) { doc.phone_number = phoneNumber; + } else { + transitionUtils.addRejectionMessage(doc, options.registrationConfig, 'provided_phone_not_unique'); return; } - throw new Error( - `${phoneNumber} submitted by ${doc.from} is already registered` - ); }); - } + }; }; const getConfig = () => config.get('registrations'); @@ -258,7 +259,7 @@ const triggers = { return triggers.add_patient(options); }, add_phone_number: (options) => { - return setPhoneNumber(options.doc); + return setPhoneNumber(options); }, add_expected_date: (options) => { return setExpectedBirthDate(options.doc); @@ -547,7 +548,7 @@ const addPatient = (options) => { } if (doc.phone_number) { - patient.phone_number = doc.phone_number; + patient.phone = doc.phone_number; } // assign patient in doc with full parent doc - to be used in messages diff --git a/shared-libs/validation/src/validation.js b/shared-libs/validation/src/validation.js index 248b10a9751..704d99c4900 100644 --- a/shared-libs/validation/src/validation.js +++ b/shared-libs/validation/src/validation.js @@ -163,6 +163,11 @@ module.exports = { }) .then(result => !result); }, + isPhoneUnique: (phoneNumber) => { + return db.medic + .query('medic-client/contacts_by_phone', { key: phoneNumber }) + .then(results => !(results && results.rows && results.rows.length)); + }, uniqueWithin: (doc, validation) => { const fields = [...validation.funcArgs]; const duration = _parseDuration(fields.pop()); From db5b54cd3aa07e5bb559bcaaaa547202329b7ab5 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Mon, 24 Jul 2023 23:19:05 +0545 Subject: [PATCH 23/56] Fixed validation.js function --- shared-libs/validation/src/validation.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared-libs/validation/src/validation.js b/shared-libs/validation/src/validation.js index 704d99c4900..a9c02be01fd 100644 --- a/shared-libs/validation/src/validation.js +++ b/shared-libs/validation/src/validation.js @@ -163,9 +163,9 @@ module.exports = { }) .then(result => !result); }, - isPhoneUnique: (phoneNumber) => { + uniquePhone: (doc,validation) => { return db.medic - .query('medic-client/contacts_by_phone', { key: phoneNumber }) + .query('medic-client/contacts_by_phone', { key: doc.phone_number }) .then(results => !(results && results.rows && results.rows.length)); }, uniqueWithin: (doc, validation) => { From 1abff0049d380aa5d2b470a879f4091644846972 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Tue, 25 Jul 2023 12:47:13 +0545 Subject: [PATCH 24/56] Doing validation for unique in before transition is run --- .../src/transitions/registration.js | 18 +----------------- shared-libs/validation/src/validation.js | 2 +- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 16fd23615bb..d54d414b5c5 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -200,28 +200,12 @@ const setPhoneNumber = options => { // By default for a valid phone number SmsParser itself adds a country code if not provided // So if country code is not present in phone_number throw error const app_settings = config.getAll(); - const allow_duplicate_phone = app_settings.forms[doc.form].fields.phone_number.flags.allow_duplicate - //check if duplicate phones are allowed and - //based upon that register the patient or throw error. const validPhone = phoneNumberParser.validate(app_settings, phoneNumber); - app_settings.registrations.find(obj => obj.form === doc.form) if (!validPhone) { transitionUtils.addRejectionMessage(doc, options.registrationConfig, 'provided_phone_not_unique'); return; } - if (allow_duplicate_phone) { - doc.phone_number = phoneNumber; - } - else { - transitionUtils.isPhoneUnique(phoneNumber).then(isUnique => { - if (isUnique) { - doc.phone_number = phoneNumber; - } else { - transitionUtils.addRejectionMessage(doc, options.registrationConfig, 'provided_phone_not_unique'); - return; - } - }); - }; + doc.phone_number = phoneNumber; }; const getConfig = () => config.get('registrations'); diff --git a/shared-libs/validation/src/validation.js b/shared-libs/validation/src/validation.js index a9c02be01fd..c425d75e2ff 100644 --- a/shared-libs/validation/src/validation.js +++ b/shared-libs/validation/src/validation.js @@ -165,7 +165,7 @@ module.exports = { }, uniquePhone: (doc,validation) => { return db.medic - .query('medic-client/contacts_by_phone', { key: doc.phone_number }) + .query('medic-client/contacts_by_phone', { key: doc[validation.field] }) .then(results => !(results && results.rows && results.rows.length)); }, uniqueWithin: (doc, validation) => { From 3eb6a9d9802f9addc03bab917ed06b69b308a836 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Tue, 25 Jul 2023 13:40:43 +0545 Subject: [PATCH 25/56] added tests for unique phone validation --- shared-libs/validation/test/validations.js | 54 ++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/shared-libs/validation/test/validations.js b/shared-libs/validation/test/validations.js index de8cb5806c5..d2b7ba1784f 100644 --- a/shared-libs/validation/test/validations.js +++ b/shared-libs/validation/test/validations.js @@ -248,6 +248,60 @@ describe('validations', () => { }); }); + it.only('should fail uniquePhone if doc is found in db', () => { + clock = sinon.useFakeTimers(); + sinon.stub(db.medic, 'query').resolves({ + rows: [ + { + id: 'original', + phone: '+9779841111111' + }], + }); + const validations = [ + { + property: 'phone_number', + rule: 'uniquePhone("phone_number")', + message: [ + { + content: 'Duplicate phone', + locale: 'en', + }, + ], + }, + ]; + const doc = { + _id: 'duplicate', + xyz: '+9779841111111', + }; + return validation.validate(doc, validations).then(errors => { + assert.equal(errors.length, 1); + }); + }); + + it.only('should pass uniquePhone if doc is not found in db', () => { + clock = sinon.useFakeTimers(); + sinon.stub(db.medic, 'query').resolves({undefined}); + const validations = [ + { + property: 'phone_number', + rule: 'uniquePhone("phone_number")', + message: [ + { + content: 'Duplicate phone', + locale: 'en', + }, + ], + }, + ]; + const doc = { + _id: 'duplicate', + xyz: '+9779841111111', + }; + return validation.validate(doc, validations).then(errors => { + assert.equal(errors.length, 0); + }); + }); + it('pass uniqueWithin validation on old doc', () => { clock = sinon.useFakeTimers(); sinon.stub(db.medic, 'query').resolves({ From 148033434a77c150b8564dae7b3c468e01fd128e Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Wed, 26 Jul 2023 13:52:24 +0545 Subject: [PATCH 26/56] eslint fixes --- .../src/transitions/registration.js | 1 - shared-libs/validation/src/validation.js | 20 +++++++-------- shared-libs/validation/test/validations.js | 25 ++++++++++--------- .../sentinel/transitions/registration.spec.js | 4 +-- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index d54d414b5c5..676230c7fc5 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -13,7 +13,6 @@ const date = require('../date'); const phoneNumberParser = require('@medic/phone-number'); const contactTypesUtils = require('@medic/contact-types-utils'); -const { sassFalse } = require('sass'); const NAME = 'registration'; const PARENT_NOT_FOUND = 'parent_not_found'; diff --git a/shared-libs/validation/src/validation.js b/shared-libs/validation/src/validation.js index c425d75e2ff..e0bcae2abe0 100644 --- a/shared-libs/validation/src/validation.js +++ b/shared-libs/validation/src/validation.js @@ -163,9 +163,9 @@ module.exports = { }) .then(result => !result); }, - uniquePhone: (doc,validation) => { + uniquePhone: (doc, validation) => { return db.medic - .query('medic-client/contacts_by_phone', { key: doc[validation.field] }) + .query('medic-client/contacts_by_phone', { key: doc[validation.field] }) .then(results => !(results && results.rows && results.rows.length)); }, uniqueWithin: (doc, validation) => { @@ -201,13 +201,13 @@ module.exports = { const year = yearFieldName ? doc[yearFieldName] : new Date().getFullYear(); const isValidISOWeek = - /^\d{1,2}$/.test(doc[weekFieldName]) && - /^\d{4}$/.test(year) && - doc[weekFieldName] >= 1 && - doc[weekFieldName] <= - moment() - .year(year) - .isoWeeksInYear(); + /^\d{1,2}$/.test(doc[weekFieldName]) && + /^\d{4}$/.test(year) && + doc[weekFieldName] >= 1 && + doc[weekFieldName] <= + moment() + .year(year) + .isoWeeksInYear(); if (isValidISOWeek) { return Promise.resolve(true); } @@ -264,7 +264,7 @@ module.exports = { * @param {String[]} [ignores=[]] Keys of doc that is always considered valid * @returns {Promise} Array of errors if validation failed, empty array otherwise. */ - validate: (doc, validations=[], ignores=[]) => { + validate: (doc, validations = [], ignores = []) => { if (!inited) { throw new Error('Validation module not initialized'); } diff --git a/shared-libs/validation/test/validations.js b/shared-libs/validation/test/validations.js index d2b7ba1784f..8bb45d0aaf8 100644 --- a/shared-libs/validation/test/validations.js +++ b/shared-libs/validation/test/validations.js @@ -252,10 +252,11 @@ describe('validations', () => { clock = sinon.useFakeTimers(); sinon.stub(db.medic, 'query').resolves({ rows: [ - { + { id: 'original', phone: '+9779841111111' - }], + } + ] }); const validations = [ { @@ -280,7 +281,7 @@ describe('validations', () => { it.only('should pass uniquePhone if doc is not found in db', () => { clock = sinon.useFakeTimers(); - sinon.stub(db.medic, 'query').resolves({undefined}); + sinon.stub(db.medic, 'query').resolves({ undefined }); const validations = [ { property: 'phone_number', @@ -586,7 +587,7 @@ describe('validations', () => { ]; const doc = { _id: 'same', - lmp_date: moment().subtract({weeks: 4, days: 1}).valueOf(), + lmp_date: moment().subtract({ weeks: 4, days: 1 }).valueOf(), reported_date: moment().valueOf() }; return validation.validate(doc, validations).then(errors => { @@ -602,9 +603,9 @@ describe('validations', () => { }, ]; const doc = { - _id: 'same', + _id: 'same', fields: { - lmp_date: moment().subtract({weeks: 4, days: 1}).valueOf() + lmp_date: moment().subtract({ weeks: 4, days: 1 }).valueOf() }, reported_date: moment().valueOf() }; @@ -622,7 +623,7 @@ describe('validations', () => { ]; const doc = { _id: 'same', - lmp_date: moment().subtract({weeks: 4}).valueOf(), + lmp_date: moment().subtract({ weeks: 4 }).valueOf(), reported_date: moment().valueOf() }; return validation.validate(doc, validations).then(errors => { @@ -645,7 +646,7 @@ describe('validations', () => { ]; const doc = { _id: 'same', - lmp_date: moment().subtract({weeks: 3, days: 6}).valueOf(), + lmp_date: moment().subtract({ weeks: 3, days: 6 }).valueOf(), reported_date: moment().valueOf() }; return validation.validate(doc, validations).then(errors => { @@ -701,7 +702,7 @@ describe('validations', () => { ]; const doc = { _id: 'same', - lmp_date: moment().subtract({weeks: 3, days: 6}).valueOf(), + lmp_date: moment().subtract({ weeks: 3, days: 6 }).valueOf(), reported_date: moment().valueOf() }; return validation.validate(doc, validations).then(errors => { @@ -723,7 +724,7 @@ describe('validations', () => { ]; const doc = { _id: 'same', - lmp_date: moment().subtract({weeks: 39, days: 6}).valueOf(), + lmp_date: moment().subtract({ weeks: 39, days: 6 }).valueOf(), reported_date: moment().valueOf() }; return validation.validate(doc, validations).then(errors => { @@ -740,7 +741,7 @@ describe('validations', () => { ]; const doc = { _id: 'same', - lmp_date: moment().subtract({weeks: 40}).valueOf(), + lmp_date: moment().subtract({ weeks: 40 }).valueOf(), reported_date: moment().valueOf() }; return validation.validate(doc, validations).then(errors => { @@ -763,7 +764,7 @@ describe('validations', () => { ]; const doc = { _id: 'same', - lmp_date: moment().subtract({weeks: 40, days: 1}).valueOf(), + lmp_date: moment().subtract({ weeks: 40, days: 1 }).valueOf(), reported_date: moment().valueOf() }; return validation.validate(doc, validations).then(errors => { diff --git a/tests/integration/sentinel/transitions/registration.spec.js b/tests/integration/sentinel/transitions/registration.spec.js index d8cb0d8ca73..5f514889685 100644 --- a/tests/integration/sentinel/transitions/registration.spec.js +++ b/tests/integration/sentinel/transitions/registration.spec.js @@ -97,8 +97,8 @@ describe('registration', () => { after(() => utils.revertDb([], true)); afterEach(() => utils.revertDb(getIds(contacts), true)); - it.only('should add valid phone to patient doc', () => { - let patient_phone = '+9779841123123'; + it('should add valid phone to patient doc', () => { + const patient_phone = '+9779841123123'; const settings = { transitions: { registration: true }, registrations: [{ From e7a2e666de1bc0fa86af24a49a20dfa3c73194b7 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Wed, 26 Jul 2023 14:21:39 +0545 Subject: [PATCH 27/56] Changes to smsparser after reading form from form definitions --- .../mocha/services/report/smsparser.spec.js | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/api/tests/mocha/services/report/smsparser.spec.js b/api/tests/mocha/services/report/smsparser.spec.js index 655bf1ff8a5..5d3addeed13 100644 --- a/api/tests/mocha/services/report/smsparser.spec.js +++ b/api/tests/mocha/services/report/smsparser.spec.js @@ -98,40 +98,40 @@ describe('sms parser', () => { }); it('accepts form with phone_number type', () => { - const doc = { message: '1234' }; + const doc = { message: 'NP 20 9841202020' }; const def = definitions.forms.NP; sinon.stub(config, 'getAll').returns({ default_country_code: 977, phone_validation: 'full' }); const data = smsparser.parse(def, doc); - chai.expect(data.phone_number).to.equal(null); + chai.expect(data.phone_number).to.equal('+9779841202020'); }); it('accepts phone number with extension', () => { - const doc = { message: '+9779841161718' }; + const doc = { message: 'NP 20 +9779841202020' }; const def = definitions.forms.NP; sinon.stub(config, 'getAll').returns({ default_country_code: 977, phone_validation: 'full' }); const data = smsparser.parse(def, doc); - chai.expect(data.phone_number).to.equal('+977 984-1161718'); + chai.expect(data.phone_number).to.equal('+9779841202020'); }); it('accepts correct phone number without extension', () => { - const doc = { message: '9841161718' }; + const doc = { message: 'NP 20 9841202020' }; const def = definitions.forms.NP; sinon.stub(config, 'getAll').returns({ default_country_code: 977, phone_validation: 'full' }); const data = smsparser.parse(def, doc); - chai.expect(data.phone_number).to.equal('+977 984-1161718'); + chai.expect(data.phone_number).to.equal('+9779841202020'); }); it('returns null if phone number is invalid for the region', () => { - const doc = { message: '+97712312' }; + const doc = { message: 'NP 20 +97712312 Prajwol' }; const def = definitions.forms.NP; sinon.stub(config, 'getAll').returns({ default_country_code: 977, @@ -142,7 +142,7 @@ describe('sms parser', () => { }); it('returns null if phone number is invalid for default region', () => { - const doc = { message: '8750660880' }; + const doc = { message: 'NP 20 8750660880 Prajwol' }; const def = definitions.forms.NP; sinon.stub(config, 'getAll').returns({ default_country_code: 977, @@ -154,9 +154,9 @@ describe('sms parser', () => { //India , Kenya, Tanzania Phone is accepted as contact info in Nepal region. //Just incase we make cross region/borders tool - [['+918750660880', '+91 87506 60880'], - ['+254773087889', '+254 773 087889'], - ['+255712262987', '+255 712 262 987']].forEach(phoneNumerWithParsed => { + [['NP 20 +918750660880 Prajwol', '+918750660880'], + ['NP 20 +254773087889 Prajwol', '+254773087889'], + ['NP 20 +255712262987 Prajwol', '+255712262987']].forEach(phoneNumerWithParsed => { it('returns parsed number if valid phone of another the region', () => { const doc = { message: phoneNumerWithParsed[0] }; const def = definitions.forms.NP; From 495a874c00ad2518875db624f62a4b815133b9d8 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Wed, 26 Jul 2023 18:28:31 +0545 Subject: [PATCH 28/56] Added translations and dynamic error messaging tried to fix registration test --- api/resources/translations/messages-en.properties | 1 + api/resources/translations/messages-ne.properties | 3 ++- shared-libs/transitions/src/transitions/registration.js | 2 +- .../sentinel/transitions/registration.spec.js | 9 +++++---- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/api/resources/translations/messages-en.properties b/api/resources/translations/messages-en.properties index 67086e68c58..07497163963 100644 --- a/api/resources/translations/messages-en.properties +++ b/api/resources/translations/messages-en.properties @@ -836,6 +836,7 @@ messages.f.report_accepted = Thank you for flagging {{patient_name}} ({{patient_ messages.f.report_accepted_parent = {{patient_name}} ({{patient_id}}) was flagged by {{contact.name}} ({{contact.phone}}) for further attention. messages.generic.no_provided_patient_id = External ID number support is configured, but is not found on document in the configured location. messages.generic.provided_patient_id_not_unique = The provided ID number is already used. +messages.generic.provided_phone_not_valid = The provided phone is not valid. messages.generic.registration_accepted = Thank you {{contact.name}} for registering {{patient_name}}. Their ID is {{patient_id}}. messages.generic.registration_not_found = No person with ID number '{{patient_id}}' found. Verify the ID and resend the message. messages.generic.report_accepted = Thank you {{contact.name}}, {{form}} for {{patient_name}} ({{patient_id}}) has been recorded. diff --git a/api/resources/translations/messages-ne.properties b/api/resources/translations/messages-ne.properties index cff6e69188e..152ed607439 100644 --- a/api/resources/translations/messages-ne.properties +++ b/api/resources/translations/messages-ne.properties @@ -764,7 +764,8 @@ messages.errors.patient.missing = messages.f.report_accepted = messages.f.report_accepted_parent = messages.generic.no_provided_patient_id = -messages.generic.provided_patient_id_not_unique = +messages.generic.provided_patient_id_not_unique = +messages.generic.provided_phone_not_valid = तपैले पठाउनुभएको फोन नम्बर मिलेन । फेरी प्रयास गर्नुहोस्। messages.generic.registration_accepted = messages.generic.registration_not_found = messages.generic.report_accepted = diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 676230c7fc5..5246c51553b 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -201,7 +201,7 @@ const setPhoneNumber = options => { const app_settings = config.getAll(); const validPhone = phoneNumberParser.validate(app_settings, phoneNumber); if (!validPhone) { - transitionUtils.addRejectionMessage(doc, options.registrationConfig, 'provided_phone_not_unique'); + transitionUtils.addRejectionMessage(doc, options.registrationConfig, 'provided_phone_not_valid'); return; } doc.phone_number = phoneNumber; diff --git a/tests/integration/sentinel/transitions/registration.spec.js b/tests/integration/sentinel/transitions/registration.spec.js index 5f514889685..78c03dff70b 100644 --- a/tests/integration/sentinel/transitions/registration.spec.js +++ b/tests/integration/sentinel/transitions/registration.spec.js @@ -174,7 +174,7 @@ describe('registration', () => { console.log(patients); chai.expect(patients.rows[0].doc).to.deep.include({ patient_id: newPatientId, - phone_number: patient_phone, + phone: patient_phone, parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } }, name: 'Minerva', type: 'person', @@ -184,8 +184,9 @@ describe('registration', () => { }); }); - it('should not create patient from report doc when provided invalid phone', () => { + it.only('should not create patient from report doc when provided invalid phone', () => { const patient_phone = '+9779666666666'; + const paitnet_id =uuid(); const settings = { transitions: { registration: true }, registrations: [{ @@ -214,7 +215,7 @@ describe('registration', () => { }; const patientNameAndInvalidPhone = { // has just the `patient_name` field, and should create this person - _id: uuid(), + _id: paitnet_id, type: 'data_record', form: 'FORM-A', from: '+9779841212345', @@ -246,7 +247,7 @@ describe('registration', () => { }).then(() => utils.getDocs(docIds)) .then(updated => { chai.expect(updated[0].fields.phone_number).to.equal(patient_phone); - chai.expect(updated[0].patient_id).equal(undefined); + chai.expect(updated[0].patient_id).equal(patient_id); }) .then(patients => { chai.expect(patients).to.be.undefined; From 551c4a2d8002b7538f7390a824417124e78815c4 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Wed, 26 Jul 2023 18:32:57 +0545 Subject: [PATCH 29/56] Removing it.only --- tests/integration/sentinel/transitions/registration.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/sentinel/transitions/registration.spec.js b/tests/integration/sentinel/transitions/registration.spec.js index 78c03dff70b..c7d08bf8882 100644 --- a/tests/integration/sentinel/transitions/registration.spec.js +++ b/tests/integration/sentinel/transitions/registration.spec.js @@ -184,7 +184,7 @@ describe('registration', () => { }); }); - it.only('should not create patient from report doc when provided invalid phone', () => { + it('should not create patient from report doc when provided invalid phone', () => { const patient_phone = '+9779666666666'; const paitnet_id =uuid(); const settings = { From 9c59f40ca50d2f00c9896c1171c4b561c314ffef Mon Sep 17 00:00:00 2001 From: Yauvaraj Rimal Date: Wed, 26 Jul 2023 20:08:52 +0545 Subject: [PATCH 30/56] Update tests/integration/sentinel/transitions/registration.spec.js Co-authored-by: Gareth Bowen --- tests/integration/sentinel/transitions/registration.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/sentinel/transitions/registration.spec.js b/tests/integration/sentinel/transitions/registration.spec.js index c7d08bf8882..0bd05bfe9eb 100644 --- a/tests/integration/sentinel/transitions/registration.spec.js +++ b/tests/integration/sentinel/transitions/registration.spec.js @@ -316,7 +316,7 @@ describe('registration', () => { }); }); - it(' be skipped when transition is disabled', () => { + it('should be skipped when transition is disabled', () => { const settings = { transitions: { registration: false }, registrations: [{ From 4ce8a19dd212c375cbc48d6f57bd44c947be1604 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Thu, 27 Jul 2023 11:57:57 +0545 Subject: [PATCH 31/56] code reivew changes mainly translations --- api/resources/translations/messages-ne.properties | 4 ++-- shared-libs/validation/test/validations.js | 4 ++-- tests/integration/sentinel/transitions/registration.spec.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api/resources/translations/messages-ne.properties b/api/resources/translations/messages-ne.properties index 152ed607439..2a5c9475a2c 100644 --- a/api/resources/translations/messages-ne.properties +++ b/api/resources/translations/messages-ne.properties @@ -764,8 +764,8 @@ messages.errors.patient.missing = messages.f.report_accepted = messages.f.report_accepted_parent = messages.generic.no_provided_patient_id = -messages.generic.provided_patient_id_not_unique = -messages.generic.provided_phone_not_valid = तपैले पठाउनुभएको फोन नम्बर मिलेन । फेरी प्रयास गर्नुहोस्। +messages.generic.provided_patient_id_not_unique = तपाइँले पठाउनुभएको आइडी पैलै प्रयोग मा छ। +messages.generic.provided_phone_not_valid = तपाइँले पठाउनुभएको फोन नम्बर मिलेन । फेरी प्रयास गर्नुहोस्। messages.generic.registration_accepted = messages.generic.registration_not_found = messages.generic.report_accepted = diff --git a/shared-libs/validation/test/validations.js b/shared-libs/validation/test/validations.js index 8bb45d0aaf8..c39282c746f 100644 --- a/shared-libs/validation/test/validations.js +++ b/shared-libs/validation/test/validations.js @@ -248,7 +248,7 @@ describe('validations', () => { }); }); - it.only('should fail uniquePhone if doc is found in db', () => { + it('should fail uniquePhone if phone is found in db', () => { clock = sinon.useFakeTimers(); sinon.stub(db.medic, 'query').resolves({ rows: [ @@ -279,7 +279,7 @@ describe('validations', () => { }); }); - it.only('should pass uniquePhone if doc is not found in db', () => { + it('should pass uniquePhone if phone is not found in db', () => { clock = sinon.useFakeTimers(); sinon.stub(db.medic, 'query').resolves({ undefined }); const validations = [ diff --git a/tests/integration/sentinel/transitions/registration.spec.js b/tests/integration/sentinel/transitions/registration.spec.js index c7d08bf8882..2a8e1c94185 100644 --- a/tests/integration/sentinel/transitions/registration.spec.js +++ b/tests/integration/sentinel/transitions/registration.spec.js @@ -186,7 +186,7 @@ describe('registration', () => { it('should not create patient from report doc when provided invalid phone', () => { const patient_phone = '+9779666666666'; - const paitnet_id =uuid(); + const patient_id =uuid(); const settings = { transitions: { registration: true }, registrations: [{ @@ -215,7 +215,7 @@ describe('registration', () => { }; const patientNameAndInvalidPhone = { // has just the `patient_name` field, and should create this person - _id: paitnet_id, + _id: patient_id, type: 'data_record', form: 'FORM-A', from: '+9779841212345', From ef55ff56a0bd5ed7ef8ba0c0f41db377c95fe9e1 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Thu, 27 Jul 2023 18:53:33 +0545 Subject: [PATCH 32/56] code review changes --- api/resources/translations/messages-ne.properties | 2 +- api/tests/mocha/services/report/smsparser.spec.js | 11 ----------- .../sentinel/transitions/registration.spec.js | 2 +- 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/api/resources/translations/messages-ne.properties b/api/resources/translations/messages-ne.properties index 2a5c9475a2c..3945227d765 100644 --- a/api/resources/translations/messages-ne.properties +++ b/api/resources/translations/messages-ne.properties @@ -764,7 +764,7 @@ messages.errors.patient.missing = messages.f.report_accepted = messages.f.report_accepted_parent = messages.generic.no_provided_patient_id = -messages.generic.provided_patient_id_not_unique = तपाइँले पठाउनुभएको आइडी पैलै प्रयोग मा छ। +messages.generic.provided_patient_id_not_unique = तपाइँले पठाउनुभएको आई.डी पहिल्यै प्रयोगमा छ। messages.generic.provided_phone_not_valid = तपाइँले पठाउनुभएको फोन नम्बर मिलेन । फेरी प्रयास गर्नुहोस्। messages.generic.registration_accepted = messages.generic.registration_not_found = diff --git a/api/tests/mocha/services/report/smsparser.spec.js b/api/tests/mocha/services/report/smsparser.spec.js index 5d3addeed13..a5f37c9a6f4 100644 --- a/api/tests/mocha/services/report/smsparser.spec.js +++ b/api/tests/mocha/services/report/smsparser.spec.js @@ -97,17 +97,6 @@ describe('sms parser', () => { chai.expect(smsparser.getFormCode('द CDT33')).to.equal('द'); }); - it('accepts form with phone_number type', () => { - const doc = { message: 'NP 20 9841202020' }; - const def = definitions.forms.NP; - sinon.stub(config, 'getAll').returns({ - default_country_code: 977, - phone_validation: 'full' - }); - const data = smsparser.parse(def, doc); - chai.expect(data.phone_number).to.equal('+9779841202020'); - }); - it('accepts phone number with extension', () => { const doc = { message: 'NP 20 +9779841202020' }; const def = definitions.forms.NP; diff --git a/tests/integration/sentinel/transitions/registration.spec.js b/tests/integration/sentinel/transitions/registration.spec.js index 58c31a87129..a54a029c567 100644 --- a/tests/integration/sentinel/transitions/registration.spec.js +++ b/tests/integration/sentinel/transitions/registration.spec.js @@ -247,7 +247,7 @@ describe('registration', () => { }).then(() => utils.getDocs(docIds)) .then(updated => { chai.expect(updated[0].fields.phone_number).to.equal(patient_phone); - chai.expect(updated[0].patient_id).equal(patient_id); + chai.expect(updated[0].patient_id).to.not.be.null; }) .then(patients => { chai.expect(patients).to.be.undefined; From 12c59decb6dcf61f24561f50f197d79b7c78baee Mon Sep 17 00:00:00 2001 From: Yauvaraj Rimal Date: Mon, 31 Jul 2023 11:13:38 +0545 Subject: [PATCH 33/56] Apply suggestions from code review Co-authored-by: Gareth Bowen --- api/src/services/report/smsparser.js | 2 +- api/tests/mocha/services/report/smsparser.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/services/report/smsparser.js b/api/src/services/report/smsparser.js index dee6288fec8..a06f69bbe40 100644 --- a/api/src/services/report/smsparser.js +++ b/api/src/services/report/smsparser.js @@ -216,7 +216,7 @@ const fieldParsers = { if (formattedAndValidatedPhone) { return formattedAndValidatedPhone; } else { - logger.error(`The provided phone number ${raw} is invalid`); + logger.warn(`The provided phone number ${raw} is invalid`); return null; } } diff --git a/api/tests/mocha/services/report/smsparser.spec.js b/api/tests/mocha/services/report/smsparser.spec.js index a5f37c9a6f4..6fd5f5a112c 100644 --- a/api/tests/mocha/services/report/smsparser.spec.js +++ b/api/tests/mocha/services/report/smsparser.spec.js @@ -146,7 +146,7 @@ describe('sms parser', () => { [['NP 20 +918750660880 Prajwol', '+918750660880'], ['NP 20 +254773087889 Prajwol', '+254773087889'], ['NP 20 +255712262987 Prajwol', '+255712262987']].forEach(phoneNumerWithParsed => { - it('returns parsed number if valid phone of another the region', () => { + it(`returns parsed number if valid phone of another the region: ${phoneNumerWithParsed[0]}`, () => { const doc = { message: phoneNumerWithParsed[0] }; const def = definitions.forms.NP; sinon.stub(config, 'getAll').returns({ From 222cafacba964a0d1c3f8bdcaa9eee5d319ec9c8 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Mon, 31 Jul 2023 18:59:26 +0545 Subject: [PATCH 34/56] Code reivew changes --- api/src/services/report/smsparser.js | 2 +- .../mocha/services/report/smsparser.spec.js | 2 +- .../transitions/src/transitions/utils.js | 7 +- .../sentinel/transitions/registration.spec.js | 87 +------------------ .../sentinel/transitions/test-stubs.js | 29 +++++++ 5 files changed, 36 insertions(+), 91 deletions(-) create mode 100644 tests/integration/sentinel/transitions/test-stubs.js diff --git a/api/src/services/report/smsparser.js b/api/src/services/report/smsparser.js index dee6288fec8..a06f69bbe40 100644 --- a/api/src/services/report/smsparser.js +++ b/api/src/services/report/smsparser.js @@ -216,7 +216,7 @@ const fieldParsers = { if (formattedAndValidatedPhone) { return formattedAndValidatedPhone; } else { - logger.error(`The provided phone number ${raw} is invalid`); + logger.warn(`The provided phone number ${raw} is invalid`); return null; } } diff --git a/api/tests/mocha/services/report/smsparser.spec.js b/api/tests/mocha/services/report/smsparser.spec.js index a5f37c9a6f4..defe54d79e8 100644 --- a/api/tests/mocha/services/report/smsparser.spec.js +++ b/api/tests/mocha/services/report/smsparser.spec.js @@ -146,7 +146,7 @@ describe('sms parser', () => { [['NP 20 +918750660880 Prajwol', '+918750660880'], ['NP 20 +254773087889 Prajwol', '+254773087889'], ['NP 20 +255712262987 Prajwol', '+255712262987']].forEach(phoneNumerWithParsed => { - it('returns parsed number if valid phone of another the region', () => { + it(`returns parsed number if valid phone of another the region ${phoneNumerWithParsed[1]}`, () => { const doc = { message: phoneNumerWithParsed[0] }; const def = definitions.forms.NP; sinon.stub(config, 'getAll').returns({ diff --git a/shared-libs/transitions/src/transitions/utils.js b/shared-libs/transitions/src/transitions/utils.js index f9442bb4c21..7e26c82c0ab 100644 --- a/shared-libs/transitions/src/transitions/utils.js +++ b/shared-libs/transitions/src/transitions/utils.js @@ -60,12 +60,7 @@ module.exports = { return db.medic .query('medic-client/contacts_by_reference', { key: ['shortcode', id] }) .then(results => !(results && results.rows && results.rows.length)); - }, - isPhoneUnique: (phoneNumber) => { - return db.medic - .query('medic-client/contacts_by_phone', { key: phoneNumber }) - .then(results => !(results && results.rows && results.rows.length)); - }, + }, addUniqueId: (doc) => { return idGenerator.next().value.then(patientId => { doc.patient_id = patientId; diff --git a/tests/integration/sentinel/transitions/registration.spec.js b/tests/integration/sentinel/transitions/registration.spec.js index a54a029c567..8a90255566e 100644 --- a/tests/integration/sentinel/transitions/registration.spec.js +++ b/tests/integration/sentinel/transitions/registration.spec.js @@ -4,6 +4,7 @@ const uuid = require('uuid').v4; const moment = require('moment'); const chai = require('chai'); const defaultSettings = utils.getDefaultSettings(); +const testForm = require('./test-forms'); const contacts = [ { @@ -99,32 +100,6 @@ describe('registration', () => { it('should add valid phone to patient doc', () => { const patient_phone = '+9779841123123'; - const settings = { - transitions: { registration: true }, - registrations: [{ - form: 'FORM-A', - events: [{ - name: 'on_create', - trigger: 'add_phone_number', - params: 'phone_number', - bool_expr: 'doc.fields.phone_number' - }, { - name: 'on_create', - trigger: 'add_patient', - params: '', - bool_expr: '' - }], - messages: [{ - recipient: 'reporting_unit', - event_type: 'report_accepted', - message: [{ - locale: 'en', - content: 'Patient {{patient_name}} ({{patient_id}}) added to {{clinic.name}}' - }], - }], - }], - forms: { 'FORM-A': {} } - }; const patientNameAndPhone = { // has just the `patient_name`and phone so should create this person _id: uuid(), type: 'data_record', @@ -146,7 +121,7 @@ describe('registration', () => { const docIds = getIds(docs); let newPatientId; return utils - .updateSettings(settings, 'sentinel') + .updateSettings(testForm.forms.NP, 'sentinel') .then(utils.saveDocs(docs)) .then(() => sentinelUtils.waitForSentinel(docIds)) .then(() => sentinelUtils.getInfoDocs(docIds)) @@ -171,7 +146,6 @@ describe('registration', () => { return getContactsByReference([newPatientId, 'venus']); }) .then(patients => { - console.log(patients); chai.expect(patients.rows[0].doc).to.deep.include({ patient_id: newPatientId, phone: patient_phone, @@ -187,32 +161,6 @@ describe('registration', () => { it('should not create patient from report doc when provided invalid phone', () => { const patient_phone = '+9779666666666'; const patient_id =uuid(); - const settings = { - transitions: { registration: true }, - registrations: [{ - form: 'FORM-A', - events: [{ - name: 'on_create', - trigger: 'add_phone_number', - params: 'phone_number', - bool_expr: 'doc.fields.phone_number' - }, { - name: 'on_create', - trigger: 'add_patient', - params: '', - bool_expr: '' - }], - messages: [{ - recipient: 'reporting_unit', - event_type: 'report_accepted', - message: [{ - locale: 'en', - content: 'Patient {{patient_name}} ({{patient_id}}) added to {{clinic.name}}' - }], - }], - }], - forms: { 'FORM-A': {} } - }; const patientNameAndInvalidPhone = { // has just the `patient_name` field, and should create this person _id: patient_id, @@ -236,7 +184,7 @@ describe('registration', () => { const docIds = getIds(docs); return utils - .updateSettings(settings, 'sentinel') + .updateSettings(testForm.forms.NP, 'sentinel') .then(utils.saveDocs(docs)) .then(() => sentinelUtils.waitForSentinel(docIds)) .then(() => sentinelUtils.getInfoDocs(docIds)) @@ -256,33 +204,6 @@ describe('registration', () => { it('should fail transition on invalid phone', () => { const patient_phone = '+9779666666666'; - const settings = { - transitions: { registration: true }, - registrations: [{ - form: 'FORM-A', - events: [{ - name: 'on_create', - trigger: 'add_phone_number', - params: 'phone_number', - bool_expr: 'doc.fields.phone_number' - }, { - name: 'on_create', - trigger: 'add_patient', - params: '', - bool_expr: '' - }], - messages: [{ - recipient: 'reporting_unit', - event_type: 'report_accepted', - message: [{ - locale: 'en', - content: 'Patient {{patient_name}} ({{patient_id}}) added to {{clinic.name}}' - }], - }], - }], - forms: { 'FORM-A': {} } - }; - const patientNameAndInvalidPhone = { // has just the `patient_name` field, and should create this person _id: uuid(), type: 'data_record', @@ -305,7 +226,7 @@ describe('registration', () => { const docIds = getIds(docs); return utils - .updateSettings(settings, 'sentinel') + .updateSettings(testForm.forms.NP, 'sentinel') .then(utils.saveDocs(docs)) .then(() => sentinelUtils.waitForSentinel(docIds)) .then(() => sentinelUtils.getInfoDocs(docIds)) diff --git a/tests/integration/sentinel/transitions/test-stubs.js b/tests/integration/sentinel/transitions/test-stubs.js new file mode 100644 index 00000000000..a19c93fc99c --- /dev/null +++ b/tests/integration/sentinel/transitions/test-stubs.js @@ -0,0 +1,29 @@ +exports.forms = { + NP: { + transitions: { registration: true }, + registrations: [{ + form: 'FORM-A', + events: [{ + name: 'on_create', + trigger: 'add_phone_number', + params: 'phone_number', + bool_expr: 'doc.fields.phone_number' + }, { + name: 'on_create', + trigger: 'add_patient', + params: '', + bool_expr: '' + }], + messages: [{ + recipient: 'reporting_unit', + event_type: 'report_accepted', + message: [{ + locale: 'en', + content: 'Patient {{patient_name}} ({{patient_id}}) added to {{clinic.name}}' + }], + }], + }], + forms: { 'FORM-A': {} } + } +}; + From 82c9b7d9c16404535fd140643d13c25422709619 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Mon, 31 Jul 2023 21:00:14 +0545 Subject: [PATCH 35/56] renamed file test-stubs and changed references --- tests/integration/sentinel/transitions/registration.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/sentinel/transitions/registration.spec.js b/tests/integration/sentinel/transitions/registration.spec.js index 8a90255566e..055f1ec9a13 100644 --- a/tests/integration/sentinel/transitions/registration.spec.js +++ b/tests/integration/sentinel/transitions/registration.spec.js @@ -4,7 +4,7 @@ const uuid = require('uuid').v4; const moment = require('moment'); const chai = require('chai'); const defaultSettings = utils.getDefaultSettings(); -const testForm = require('./test-forms'); +const testForm = require('./test-stubs'); const contacts = [ { From 4d629cc92bf9658498cb39635c923c8ac5f2fe17 Mon Sep 17 00:00:00 2001 From: Prajwol Shrestha Date: Wed, 2 Aug 2023 13:37:01 +0545 Subject: [PATCH 36/56] Update shared-libs/transitions/src/transitions/registration.js Camel case Co-authored-by: Gareth Bowen --- shared-libs/transitions/src/transitions/registration.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 5246c51553b..af0789ff5a2 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -198,8 +198,8 @@ const setPhoneNumber = options => { const phoneNumber = getPhoneNumber(doc); // By default for a valid phone number SmsParser itself adds a country code if not provided // So if country code is not present in phone_number throw error - const app_settings = config.getAll(); - const validPhone = phoneNumberParser.validate(app_settings, phoneNumber); + const appSettings = config.getAll(); + const validPhone = phoneNumberParser.validate(appSettings, phoneNumber); if (!validPhone) { transitionUtils.addRejectionMessage(doc, options.registrationConfig, 'provided_phone_not_valid'); return; From 1ec2430ba92b382046ce57328e8ac07d8fb7e39f Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Wed, 2 Aug 2023 21:57:59 +0545 Subject: [PATCH 37/56] Code review changes --- .../translations/messages-ne.properties | 2 +- api/tests/form-definitions.js | 14 +------------ .../mocha/services/report/smsparser.spec.js | 20 ++++++++++++++++--- config/default/app_settings.json | 4 ++-- shared-libs/phone-number/src/phone-number.js | 4 ++-- .../src/transitions/registration.js | 3 +-- shared-libs/validation/test/validations.js | 8 ++++---- 7 files changed, 28 insertions(+), 27 deletions(-) diff --git a/api/resources/translations/messages-ne.properties b/api/resources/translations/messages-ne.properties index 3945227d765..32f1a8c13bf 100644 --- a/api/resources/translations/messages-ne.properties +++ b/api/resources/translations/messages-ne.properties @@ -764,7 +764,7 @@ messages.errors.patient.missing = messages.f.report_accepted = messages.f.report_accepted_parent = messages.generic.no_provided_patient_id = -messages.generic.provided_patient_id_not_unique = तपाइँले पठाउनुभएको आई.डी पहिल्यै प्रयोगमा छ। +messages.generic.provided_patient_id_not_unique = तपाइँले पठाउनुभएको आईडी पहिल्यै प्रयोगमा छ। messages.generic.provided_phone_not_valid = तपाइँले पठाउनुभएको फोन नम्बर मिलेन । फेरी प्रयास गर्नुहोस्। messages.generic.registration_accepted = messages.generic.registration_not_found = diff --git a/api/tests/form-definitions.js b/api/tests/form-definitions.js index 5865f21994f..a1bc4e575e0 100644 --- a/api/tests/form-definitions.js +++ b/api/tests/form-definitions.js @@ -792,7 +792,7 @@ exports.forms = { } } }, - 'NP': { + NP: { 'meta': { 'code': 'NP', 'translation_key': 'forms.np.title', @@ -812,14 +812,6 @@ exports.forms = { }, 'position': 0, 'type': 'integer', - 'length': [ - 1, - 3 - ], - 'range': [ - 0, - 125 - ], 'required': true }, 'phone_number': { @@ -852,10 +844,6 @@ exports.forms = { }, 'position': 2, 'type': 'string', - 'length': [ - 3, - 30 - ], 'required': true } }, diff --git a/api/tests/mocha/services/report/smsparser.spec.js b/api/tests/mocha/services/report/smsparser.spec.js index defe54d79e8..f75bf7086c1 100644 --- a/api/tests/mocha/services/report/smsparser.spec.js +++ b/api/tests/mocha/services/report/smsparser.spec.js @@ -97,6 +97,20 @@ describe('sms parser', () => { chai.expect(smsparser.getFormCode('द CDT33')).to.equal('द'); }); + + ['full', 'none', 'partial'].forEach(validationtype => { + it('supports all type of phone validation', () => { + const doc = { message: 'NP 20 +9779841202020' }; + const def = definitions.forms.NP; + sinon.stub(config, 'getAll').returns({ + default_country_code: 977, + phone_validation: validationtype + }); + const data = smsparser.parse(def, doc); + chai.expect(data.phone_number).to.equal('+9779841202020'); + }) + }); + it('accepts phone number with extension', () => { const doc = { message: 'NP 20 +9779841202020' }; const def = definitions.forms.NP; @@ -142,10 +156,10 @@ describe('sms parser', () => { }); //India , Kenya, Tanzania Phone is accepted as contact info in Nepal region. - //Just incase we make cross region/borders tool + //Just in case we make cross region/borders tool [['NP 20 +918750660880 Prajwol', '+918750660880'], - ['NP 20 +254773087889 Prajwol', '+254773087889'], - ['NP 20 +255712262987 Prajwol', '+255712262987']].forEach(phoneNumerWithParsed => { + ['NP 20 +254773087889 Prajwol', '+254773087889'], + ['NP 20 +255712262987 Prajwol', '+255712262987']].forEach(phoneNumerWithParsed => { it(`returns parsed number if valid phone of another the region ${phoneNumerWithParsed[1]}`, () => { const doc = { message: phoneNumerWithParsed[0] }; const def = definitions.forms.NP; diff --git a/config/default/app_settings.json b/config/default/app_settings.json index cb0bd86933d..9b00522d708 100644 --- a/config/default/app_settings.json +++ b/config/default/app_settings.json @@ -353,9 +353,9 @@ "person": true } ], - "contact_summary": "var ContactSummary = {}; !function(e,t){if('object'==typeof exports&&'object'==typeof module)module.exports=t();else if('function'==typeof define&&define.amd)define([],t);else{var n=t();for(var r in n)('object'==typeof exports?exports:e)[r]=n[r]}}(ContactSummary,(function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){'undefined'!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:'Module'}),Object.defineProperty(e,'__esModule',{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&'object'==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,'default',{enumerable:!0,value:e}),2&t&&'string'!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,'a',t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p='',n(n.s=1)}([function(e,t,n){(function(e){e.exports=function(){'use strict';var t,n;function r(){return t.apply(null,arguments)}function i(e){return e instanceof Array||'[object Array]'===Object.prototype.toString.call(e)}function s(e){return null!=e&&'[object Object]'===Object.prototype.toString.call(e)}function a(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function o(e){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(e).length;var t;for(t in e)if(a(e,t))return!1;return!0}function l(e){return void 0===e}function u(e){return'number'==typeof e||'[object Number]'===Object.prototype.toString.call(e)}function c(e){return e instanceof Date||'[object Date]'===Object.prototype.toString.call(e)}function d(e,t){var n,r=[];for(n=0;n>>0;for(t=0;t0)for(n=0;n=0?n?'+':'':'-')+Math.pow(10,Math.max(0,i)).toString().substr(1)+r}r.suppressDeprecationWarnings=!1,r.deprecationHandler=null,M=Object.keys?Object.keys:function(e){var t,n=[];for(t in e)a(e,t)&&n.push(t);return n};var P=/(\\[[^\\[]*\\])|(\\\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,R=/(\\[[^\\[]*\\])|(\\\\)?(LTS|LT|LL?L?L?|l{1,4})/g,W={},C={};function F(e,t,n,r){var i=r;'string'==typeof r&&(i=function(){return this[r]()}),e&&(C[e]=i),t&&(C[t[0]]=function(){return N(i.apply(this,arguments),t[1],t[2])}),n&&(C[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),e)})}function A(e,t){return e.isValid()?(t=E(t,e.localeData()),W[t]=W[t]||function(e){var t,n,r,i=e.match(P);for(t=0,n=i.length;t=0&&R.test(e);)e=e.replace(R,r),R.lastIndex=0,n-=1;return e}var U={};function H(e,t){var n=e.toLowerCase();U[n]=U[n+'s']=U[t]=e}function L(e){return'string'==typeof e?U[e]||U[e.toLowerCase()]:void 0}function V(e){var t,n,r={};for(n in e)a(e,n)&&(t=L(n))&&(r[t]=e[n]);return r}var I={};function j(e,t){I[e]=t}function G(e){return e%4==0&&e%100!=0||e%400==0}function Z(e){return e<0?Math.ceil(e)||0:Math.floor(e)}function z(e){var t=+e,n=0;return 0!==t&&isFinite(t)&&(n=Z(t)),n}function q(e,t){return function(n){return null!=n?(B(this,e,n),r.updateOffset(this,t),this):$(this,e)}}function $(e,t){return e.isValid()?e._d['get'+(e._isUTC?'UTC':'')+t]():NaN}function B(e,t,n){e.isValid()&&!isNaN(n)&&('FullYear'===t&&G(e.year())&&1===e.month()&&29===e.date()?(n=z(n),e._d['set'+(e._isUTC?'UTC':'')+t](n,e.month(),ke(n,e.month()))):e._d['set'+(e._isUTC?'UTC':'')+t](n))}var J,Q=/\\d/,X=/\\d\\d/,K=/\\d{3}/,ee=/\\d{4}/,te=/[+-]?\\d{6}/,ne=/\\d\\d?/,re=/\\d\\d\\d\\d?/,ie=/\\d\\d\\d\\d\\d\\d?/,se=/\\d{1,3}/,ae=/\\d{1,4}/,oe=/[+-]?\\d{1,6}/,le=/\\d+/,ue=/[+-]?\\d+/,ce=/Z|[+-]\\d\\d:?\\d\\d/gi,de=/Z|[+-]\\d\\d(?::?\\d\\d)?/gi,he=/[0-9]{0,256}['a-z\\u00A0-\\u05FF\\u0700-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFF07\\uFF10-\\uFFEF]{1,256}|[\\u0600-\\u06FF\\/]{1,256}(\\s*?[\\u0600-\\u06FF]{1,256}){1,2}/i;function fe(e,t,n){J[e]=O(t)?t:function(e,r){return e&&n?n:t}}function _e(e,t){return a(J,e)?J[e](t._strict,t._locale):new RegExp(me(e.replace('\\\\','').replace(/\\\\(\\[)|\\\\(\\])|\\[([^\\]\\[]*)\\]|\\\\(.)/g,(function(e,t,n,r,i){return t||n||r||i}))))}function me(e){return e.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g,'\\\\$&')}J={};var pe,ye={};function ge(e,t){var n,r=t;for('string'==typeof e&&(e=[e]),u(t)&&(r=function(e,n){n[t]=z(e)}),n=0;n68?1900:2e3)};var Re=q('FullYear',!0);function We(e,t,n,r,i,s,a){var o;return e<100&&e>=0?(o=new Date(e+400,t,n,r,i,s,a),isFinite(o.getFullYear())&&o.setFullYear(e)):o=new Date(e,t,n,r,i,s,a),o}function Ce(e){var t,n;return e<100&&e>=0?((n=Array.prototype.slice.call(arguments))[0]=e+400,t=new Date(Date.UTC.apply(null,n)),isFinite(t.getUTCFullYear())&&t.setUTCFullYear(e)):t=new Date(Date.UTC.apply(null,arguments)),t}function Fe(e,t,n){var r=7+t-n;return-(7+Ce(e,0,r).getUTCDay()-t)%7+r-1}function Ae(e,t,n,r,i){var s,a,o=1+7*(t-1)+(7+n-r)%7+Fe(e,r,i);return o<=0?a=Pe(s=e-1)+o:o>Pe(e)?(s=e+1,a=o-Pe(e)):(s=e,a=o),{year:s,dayOfYear:a}}function Ee(e,t,n){var r,i,s=Fe(e.year(),t,n),a=Math.floor((e.dayOfYear()-s-1)/7)+1;return a<1?r=a+Ue(i=e.year()-1,t,n):a>Ue(e.year(),t,n)?(r=a-Ue(e.year(),t,n),i=e.year()+1):(i=e.year(),r=a),{week:r,year:i}}function Ue(e,t,n){var r=Fe(e,t,n),i=Fe(e+1,t,n);return(Pe(e)-r+i)/7}function He(e,t){return e.slice(t,7).concat(e.slice(0,t))}F('w',['ww',2],'wo','week'),F('W',['WW',2],'Wo','isoWeek'),H('week','w'),H('isoWeek','W'),j('week',5),j('isoWeek',5),fe('w',ne),fe('ww',ne,X),fe('W',ne),fe('WW',ne,X),ve(['w','ww','W','WW'],(function(e,t,n,r){t[r.substr(0,1)]=z(e)})),F('d',0,'do','day'),F('dd',0,0,(function(e){return this.localeData().weekdaysMin(this,e)})),F('ddd',0,0,(function(e){return this.localeData().weekdaysShort(this,e)})),F('dddd',0,0,(function(e){return this.localeData().weekdays(this,e)})),F('e',0,0,'weekday'),F('E',0,0,'isoWeekday'),H('day','d'),H('weekday','e'),H('isoWeekday','E'),j('day',11),j('weekday',11),j('isoWeekday',11),fe('d',ne),fe('e',ne),fe('E',ne),fe('dd',(function(e,t){return t.weekdaysMinRegex(e)})),fe('ddd',(function(e,t){return t.weekdaysShortRegex(e)})),fe('dddd',(function(e,t){return t.weekdaysRegex(e)})),ve(['dd','ddd','dddd'],(function(e,t,n,r){var i=n._locale.weekdaysParse(e,r,n._strict);null!=i?t.d=i:_(n).invalidWeekday=e})),ve(['d','e','E'],(function(e,t,n,r){t[r]=z(e)}));var Le='Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),Ve='Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),Ie='Su_Mo_Tu_We_Th_Fr_Sa'.split('_'),je=he,Ge=he,Ze=he;function ze(e,t,n){var r,i,s,a=e.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],r=0;r<7;++r)s=f([2e3,1]).day(r),this._minWeekdaysParse[r]=this.weekdaysMin(s,'').toLocaleLowerCase(),this._shortWeekdaysParse[r]=this.weekdaysShort(s,'').toLocaleLowerCase(),this._weekdaysParse[r]=this.weekdays(s,'').toLocaleLowerCase();return n?'dddd'===t?-1!==(i=pe.call(this._weekdaysParse,a))?i:null:'ddd'===t?-1!==(i=pe.call(this._shortWeekdaysParse,a))?i:null:-1!==(i=pe.call(this._minWeekdaysParse,a))?i:null:'dddd'===t?-1!==(i=pe.call(this._weekdaysParse,a))||-1!==(i=pe.call(this._shortWeekdaysParse,a))||-1!==(i=pe.call(this._minWeekdaysParse,a))?i:null:'ddd'===t?-1!==(i=pe.call(this._shortWeekdaysParse,a))||-1!==(i=pe.call(this._weekdaysParse,a))||-1!==(i=pe.call(this._minWeekdaysParse,a))?i:null:-1!==(i=pe.call(this._minWeekdaysParse,a))||-1!==(i=pe.call(this._weekdaysParse,a))||-1!==(i=pe.call(this._shortWeekdaysParse,a))?i:null}function qe(){function e(e,t){return t.length-e.length}var t,n,r,i,s,a=[],o=[],l=[],u=[];for(t=0;t<7;t++)n=f([2e3,1]).day(t),r=me(this.weekdaysMin(n,'')),i=me(this.weekdaysShort(n,'')),s=me(this.weekdays(n,'')),a.push(r),o.push(i),l.push(s),u.push(r),u.push(i),u.push(s);a.sort(e),o.sort(e),l.sort(e),u.sort(e),this._weekdaysRegex=new RegExp('^('+u.join('|')+')','i'),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp('^('+l.join('|')+')','i'),this._weekdaysShortStrictRegex=new RegExp('^('+o.join('|')+')','i'),this._weekdaysMinStrictRegex=new RegExp('^('+a.join('|')+')','i')}function $e(){return this.hours()%12||12}function Be(e,t){F(e,0,0,(function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)}))}function Je(e,t){return t._meridiemParse}F('H',['HH',2],0,'hour'),F('h',['hh',2],0,$e),F('k',['kk',2],0,(function(){return this.hours()||24})),F('hmm',0,0,(function(){return''+$e.apply(this)+N(this.minutes(),2)})),F('hmmss',0,0,(function(){return''+$e.apply(this)+N(this.minutes(),2)+N(this.seconds(),2)})),F('Hmm',0,0,(function(){return''+this.hours()+N(this.minutes(),2)})),F('Hmmss',0,0,(function(){return''+this.hours()+N(this.minutes(),2)+N(this.seconds(),2)})),Be('a',!0),Be('A',!1),H('hour','h'),j('hour',13),fe('a',Je),fe('A',Je),fe('H',ne),fe('h',ne),fe('k',ne),fe('HH',ne,X),fe('hh',ne,X),fe('kk',ne,X),fe('hmm',re),fe('hmmss',ie),fe('Hmm',re),fe('Hmmss',ie),ge(['H','HH'],3),ge(['k','kk'],(function(e,t,n){var r=z(e);t[3]=24===r?0:r})),ge(['a','A'],(function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e})),ge(['h','hh'],(function(e,t,n){t[3]=z(e),_(n).bigHour=!0})),ge('hmm',(function(e,t,n){var r=e.length-2;t[3]=z(e.substr(0,r)),t[4]=z(e.substr(r)),_(n).bigHour=!0})),ge('hmmss',(function(e,t,n){var r=e.length-4,i=e.length-2;t[3]=z(e.substr(0,r)),t[4]=z(e.substr(r,2)),t[5]=z(e.substr(i)),_(n).bigHour=!0})),ge('Hmm',(function(e,t,n){var r=e.length-2;t[3]=z(e.substr(0,r)),t[4]=z(e.substr(r))})),ge('Hmmss',(function(e,t,n){var r=e.length-4,i=e.length-2;t[3]=z(e.substr(0,r)),t[4]=z(e.substr(r,2)),t[5]=z(e.substr(i))}));var Qe,Xe=q('Hours',!0),Ke={calendar:{sameDay:'[Today at] LT',nextDay:'[Tomorrow at] LT',nextWeek:'dddd [at] LT',lastDay:'[Yesterday at] LT',lastWeek:'[Last] dddd [at] LT',sameElse:'L'},longDateFormat:{LTS:'h:mm:ss A',LT:'h:mm A',L:'MM/DD/YYYY',LL:'MMMM D, YYYY',LLL:'MMMM D, YYYY h:mm A',LLLL:'dddd, MMMM D, YYYY h:mm A'},invalidDate:'Invalid date',ordinal:'%d',dayOfMonthOrdinalParse:/\\d{1,2}/,relativeTime:{future:'in %s',past:'%s ago',s:'a few seconds',ss:'%d seconds',m:'a minute',mm:'%d minutes',h:'an hour',hh:'%d hours',d:'a day',dd:'%d days',w:'a week',ww:'%d weeks',M:'a month',MM:'%d months',y:'a year',yy:'%d years'},months:De,monthsShort:be,week:{dow:0,doy:6},weekdays:Le,weekdaysMin:Ie,weekdaysShort:Ve,meridiemParse:/[ap]\\.?m?\\.?/i},et={},tt={};function nt(e,t){var n,r=Math.min(e.length,t.length);for(n=0;n0;){if(r=it(i.slice(0,t).join('-')))return r;if(n&&n.length>=t&&nt(i,n)>=t-1)break;t--}s++}return Qe}(e)}function lt(e){var t,n=e._a;return n&&-2===_(e).overflow&&(t=n[1]<0||n[1]>11?1:n[2]<1||n[2]>ke(n[0],n[1])?2:n[3]<0||n[3]>24||24===n[3]&&(0!==n[4]||0!==n[5]||0!==n[6])?3:n[4]<0||n[4]>59?4:n[5]<0||n[5]>59?5:n[6]<0||n[6]>999?6:-1,_(e)._overflowDayOfYear&&(t<0||t>2)&&(t=2),_(e)._overflowWeeks&&-1===t&&(t=7),_(e)._overflowWeekday&&-1===t&&(t=8),_(e).overflow=t),e}var ut=/^\\s*((?:[+-]\\d{6}|\\d{4})-(?:\\d\\d-\\d\\d|W\\d\\d-\\d|W\\d\\d|\\d\\d\\d|\\d\\d))(?:(T| )(\\d\\d(?::\\d\\d(?::\\d\\d(?:[.,]\\d+)?)?)?)([+-]\\d\\d(?::?\\d\\d)?|\\s*Z)?)?$/,ct=/^\\s*((?:[+-]\\d{6}|\\d{4})(?:\\d\\d\\d\\d|W\\d\\d\\d|W\\d\\d|\\d\\d\\d|\\d\\d|))(?:(T| )(\\d\\d(?:\\d\\d(?:\\d\\d(?:[.,]\\d+)?)?)?)([+-]\\d\\d(?::?\\d\\d)?|\\s*Z)?)?$/,dt=/Z|[+-]\\d\\d(?::?\\d\\d)?/,ht=[['YYYYYY-MM-DD',/[+-]\\d{6}-\\d\\d-\\d\\d/],['YYYY-MM-DD',/\\d{4}-\\d\\d-\\d\\d/],['GGGG-[W]WW-E',/\\d{4}-W\\d\\d-\\d/],['GGGG-[W]WW',/\\d{4}-W\\d\\d/,!1],['YYYY-DDD',/\\d{4}-\\d{3}/],['YYYY-MM',/\\d{4}-\\d\\d/,!1],['YYYYYYMMDD',/[+-]\\d{10}/],['YYYYMMDD',/\\d{8}/],['GGGG[W]WWE',/\\d{4}W\\d{3}/],['GGGG[W]WW',/\\d{4}W\\d{2}/,!1],['YYYYDDD',/\\d{7}/],['YYYYMM',/\\d{6}/,!1],['YYYY',/\\d{4}/,!1]],ft=[['HH:mm:ss.SSSS',/\\d\\d:\\d\\d:\\d\\d\\.\\d+/],['HH:mm:ss,SSSS',/\\d\\d:\\d\\d:\\d\\d,\\d+/],['HH:mm:ss',/\\d\\d:\\d\\d:\\d\\d/],['HH:mm',/\\d\\d:\\d\\d/],['HHmmss.SSSS',/\\d\\d\\d\\d\\d\\d\\.\\d+/],['HHmmss,SSSS',/\\d\\d\\d\\d\\d\\d,\\d+/],['HHmmss',/\\d\\d\\d\\d\\d\\d/],['HHmm',/\\d\\d\\d\\d/],['HH',/\\d\\d/]],_t=/^\\/?Date\\((-?\\d+)/i,mt=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\\s)?(\\d{1,2})\\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s(\\d{2,4})\\s(\\d\\d):(\\d\\d)(?::(\\d\\d))?\\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\\d{4}))$/,pt={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function yt(e){var t,n,r,i,s,a,o=e._i,l=ut.exec(o)||ct.exec(o);if(l){for(_(e).iso=!0,t=0,n=ht.length;t7)&&(l=!0)):(s=e._locale._week.dow,a=e._locale._week.doy,u=Ee(St(),s,a),n=wt(t.gg,e._a[0],u.year),r=wt(t.w,u.week),null!=t.d?((i=t.d)<0||i>6)&&(l=!0):null!=t.e?(i=t.e+s,(t.e<0||t.e>6)&&(l=!0)):i=s),r<1||r>Ue(n,s,a)?_(e)._overflowWeeks=!0:null!=l?_(e)._overflowWeekday=!0:(o=Ae(n,r,i,s,a),e._a[0]=o.year,e._dayOfYear=o.dayOfYear)}(e),null!=e._dayOfYear&&(a=wt(e._a[0],i[0]),(e._dayOfYear>Pe(a)||0===e._dayOfYear)&&(_(e)._overflowDayOfYear=!0),n=Ce(a,0,e._dayOfYear),e._a[1]=n.getUTCMonth(),e._a[2]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=o[t]=i[t];for(;t<7;t++)e._a[t]=o[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[3]&&0===e._a[4]&&0===e._a[5]&&0===e._a[6]&&(e._nextDay=!0,e._a[3]=0),e._d=(e._useUTC?Ce:We).apply(null,o),s=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[3]=24),e._w&&void 0!==e._w.d&&e._w.d!==s&&(_(e).weekdayMismatch=!0)}}function Dt(e){if(e._f!==r.ISO_8601)if(e._f!==r.RFC_2822){e._a=[],_(e).empty=!0;var t,n,i,s,a,o,l=''+e._i,u=l.length,c=0;for(i=E(e._f,e._locale).match(P)||[],t=0;t0&&_(e).unusedInput.push(a),l=l.slice(l.indexOf(n)+n.length),c+=n.length),C[s]?(n?_(e).empty=!1:_(e).unusedTokens.push(s),we(s,n,e)):e._strict&&!n&&_(e).unusedTokens.push(s);_(e).charsLeftOver=u-c,l.length>0&&_(e).unusedInput.push(l),e._a[3]<=12&&!0===_(e).bigHour&&e._a[3]>0&&(_(e).bigHour=void 0),_(e).parsedDateParts=e._a.slice(0),_(e).meridiem=e._meridiem,e._a[3]=function(e,t,n){var r;return null==n?t:null!=e.meridiemHour?e.meridiemHour(t,n):null!=e.isPM?((r=e.isPM(n))&&t<12&&(t+=12),r||12!==t||(t=0),t):t}(e._locale,e._a[3],e._meridiem),null!==(o=_(e).era)&&(e._a[0]=e._locale.erasConvertYear(o,e._a[0])),kt(e),lt(e)}else vt(e);else yt(e)}function bt(e){var t=e._i,n=e._f;return e._locale=e._locale||ot(e._l),null===t||void 0===n&&''===t?p({nullInput:!0}):('string'==typeof t&&(e._i=t=e._locale.preparse(t)),k(t)?new w(lt(t)):(c(t)?e._d=t:i(n)?function(e){var t,n,r,i,s,a,o=!1;if(0===e._f.length)return _(e).invalidFormat=!0,void(e._d=new Date(NaN));for(i=0;ithis?this:e:p()}));function xt(e,t){var n,r;if(1===t.length&&i(t[0])&&(t=t[0]),!t.length)return St();for(n=t[0],r=1;r=0?new Date(e+400,t,n)-126227808e5:new Date(e,t,n).valueOf()}function rn(e,t,n){return e<100&&e>=0?Date.UTC(e+400,t,n)-126227808e5:Date.UTC(e,t,n)}function sn(e,t){return t.erasAbbrRegex(e)}function an(){var e,t,n=[],r=[],i=[],s=[],a=this.eras();for(e=0,t=a.length;e(s=Ue(e,r,i))&&(t=s),un.call(this,e,t,n,r,i))}function un(e,t,n,r,i){var s=Ae(e,t,n,r,i),a=Ce(s.year,0,s.dayOfYear);return this.year(a.getUTCFullYear()),this.month(a.getUTCMonth()),this.date(a.getUTCDate()),this}F('N',0,0,'eraAbbr'),F('NN',0,0,'eraAbbr'),F('NNN',0,0,'eraAbbr'),F('NNNN',0,0,'eraName'),F('NNNNN',0,0,'eraNarrow'),F('y',['y',1],'yo','eraYear'),F('y',['yy',2],0,'eraYear'),F('y',['yyy',3],0,'eraYear'),F('y',['yyyy',4],0,'eraYear'),fe('N',sn),fe('NN',sn),fe('NNN',sn),fe('NNNN',(function(e,t){return t.erasNameRegex(e)})),fe('NNNNN',(function(e,t){return t.erasNarrowRegex(e)})),ge(['N','NN','NNN','NNNN','NNNNN'],(function(e,t,n,r){var i=n._locale.erasParse(e,r,n._strict);i?_(n).era=i:_(n).invalidEra=e})),fe('y',le),fe('yy',le),fe('yyy',le),fe('yyyy',le),fe('yo',(function(e,t){return t._eraYearOrdinalRegex||le})),ge(['y','yy','yyy','yyyy'],0),ge(['yo'],(function(e,t,n,r){var i;n._locale._eraYearOrdinalRegex&&(i=e.match(n._locale._eraYearOrdinalRegex)),n._locale.eraYearOrdinalParse?t[0]=n._locale.eraYearOrdinalParse(e,i):t[0]=parseInt(e,10)})),F(0,['gg',2],0,(function(){return this.weekYear()%100})),F(0,['GG',2],0,(function(){return this.isoWeekYear()%100})),on('gggg','weekYear'),on('ggggg','weekYear'),on('GGGG','isoWeekYear'),on('GGGGG','isoWeekYear'),H('weekYear','gg'),H('isoWeekYear','GG'),j('weekYear',1),j('isoWeekYear',1),fe('G',ue),fe('g',ue),fe('GG',ne,X),fe('gg',ne,X),fe('GGGG',ae,ee),fe('gggg',ae,ee),fe('GGGGG',oe,te),fe('ggggg',oe,te),ve(['gggg','ggggg','GGGG','GGGGG'],(function(e,t,n,r){t[r.substr(0,2)]=z(e)})),ve(['gg','GG'],(function(e,t,n,i){t[i]=r.parseTwoDigitYear(e)})),F('Q',0,'Qo','quarter'),H('quarter','Q'),j('quarter',7),fe('Q',Q),ge('Q',(function(e,t){t[1]=3*(z(e)-1)})),F('D',['DD',2],'Do','date'),H('date','D'),j('date',9),fe('D',ne),fe('DD',ne,X),fe('Do',(function(e,t){return e?t._dayOfMonthOrdinalParse||t._ordinalParse:t._dayOfMonthOrdinalParseLenient})),ge(['D','DD'],2),ge('Do',(function(e,t){t[2]=z(e.match(ne)[0])}));var cn=q('Date',!0);F('DDD',['DDDD',3],'DDDo','dayOfYear'),H('dayOfYear','DDD'),j('dayOfYear',4),fe('DDD',se),fe('DDDD',K),ge(['DDD','DDDD'],(function(e,t,n){n._dayOfYear=z(e)})),F('m',['mm',2],0,'minute'),H('minute','m'),j('minute',14),fe('m',ne),fe('mm',ne,X),ge(['m','mm'],4);var dn=q('Minutes',!1);F('s',['ss',2],0,'second'),H('second','s'),j('second',15),fe('s',ne),fe('ss',ne,X),ge(['s','ss'],5);var hn,fn,_n=q('Seconds',!1);for(F('S',0,0,(function(){return~~(this.millisecond()/100)})),F(0,['SS',2],0,(function(){return~~(this.millisecond()/10)})),F(0,['SSS',3],0,'millisecond'),F(0,['SSSS',4],0,(function(){return 10*this.millisecond()})),F(0,['SSSSS',5],0,(function(){return 100*this.millisecond()})),F(0,['SSSSSS',6],0,(function(){return 1e3*this.millisecond()})),F(0,['SSSSSSS',7],0,(function(){return 1e4*this.millisecond()})),F(0,['SSSSSSSS',8],0,(function(){return 1e5*this.millisecond()})),F(0,['SSSSSSSSS',9],0,(function(){return 1e6*this.millisecond()})),H('millisecond','ms'),j('millisecond',16),fe('S',se,Q),fe('SS',se,X),fe('SSS',se,K),hn='SSSS';hn.length<=9;hn+='S')fe(hn,le);function mn(e,t){t[6]=z(1e3*('0.'+e))}for(hn='S';hn.length<=9;hn+='S')ge(hn,mn);fn=q('Milliseconds',!1),F('z',0,0,'zoneAbbr'),F('zz',0,0,'zoneName');var pn=w.prototype;function yn(e){return e}pn.add=zt,pn.calendar=function(e,t){1===arguments.length&&(arguments[0]?Bt(arguments[0])?(e=arguments[0],t=void 0):Jt(arguments[0])&&(t=arguments[0],e=void 0):(e=void 0,t=void 0));var n=e||St(),i=At(n,this).startOf('day'),s=r.calendarFormat(this,i)||'sameElse',a=t&&(O(t[s])?t[s].call(this,n):t[s]);return this.format(a||this.localeData().calendar(s,this,St(n)))},pn.clone=function(){return new w(this)},pn.diff=function(e,t,n){var r,i,s;if(!this.isValid())return NaN;if(!(r=At(e,this)).isValid())return NaN;switch(i=6e4*(r.utcOffset()-this.utcOffset()),t=L(t)){case'year':s=Qt(this,r)/12;break;case'month':s=Qt(this,r);break;case'quarter':s=Qt(this,r)/3;break;case'second':s=(this-r)/1e3;break;case'minute':s=(this-r)/6e4;break;case'hour':s=(this-r)/36e5;break;case'day':s=(this-r-i)/864e5;break;case'week':s=(this-r-i)/6048e5;break;default:s=this-r}return n?s:Z(s)},pn.endOf=function(e){var t,n;if(void 0===(e=L(e))||'millisecond'===e||!this.isValid())return this;switch(n=this._isUTC?rn:nn,e){case'year':t=n(this.year()+1,0,1)-1;break;case'quarter':t=n(this.year(),this.month()-this.month()%3+3,1)-1;break;case'month':t=n(this.year(),this.month()+1,1)-1;break;case'week':t=n(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case'isoWeek':t=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case'day':case'date':t=n(this.year(),this.month(),this.date()+1)-1;break;case'hour':t=this._d.valueOf(),t+=36e5-tn(t+(this._isUTC?0:6e4*this.utcOffset()),36e5)-1;break;case'minute':t=this._d.valueOf(),t+=6e4-tn(t,6e4)-1;break;case'second':t=this._d.valueOf(),t+=1e3-tn(t,1e3)-1}return this._d.setTime(t),r.updateOffset(this,!0),this},pn.format=function(e){e||(e=this.isUtc()?r.defaultFormatUtc:r.defaultFormat);var t=A(this,e);return this.localeData().postformat(t)},pn.from=function(e,t){return this.isValid()&&(k(e)&&e.isValid()||St(e).isValid())?Vt({to:this,from:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},pn.fromNow=function(e){return this.from(St(),e)},pn.to=function(e,t){return this.isValid()&&(k(e)&&e.isValid()||St(e).isValid())?Vt({from:this,to:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},pn.toNow=function(e){return this.to(St(),e)},pn.get=function(e){return O(this[e=L(e)])?this[e]():this},pn.invalidAt=function(){return _(this).overflow},pn.isAfter=function(e,t){var n=k(e)?e:St(e);return!(!this.isValid()||!n.isValid())&&('millisecond'===(t=L(t)||'millisecond')?this.valueOf()>n.valueOf():n.valueOf()9999?A(n,t?'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]':'YYYYYY-MM-DD[T]HH:mm:ss.SSSZ'):O(Date.prototype.toISOString)?t?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace('Z',A(n,'Z')):A(n,t?'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]':'YYYY-MM-DD[T]HH:mm:ss.SSSZ')},pn.inspect=function(){if(!this.isValid())return'moment.invalid(/* '+this._i+' */)';var e,t,n,r='moment',i='';return this.isLocal()||(r=0===this.utcOffset()?'moment.utc':'moment.parseZone',i='Z'),e='['+r+'(\"]',t=0<=this.year()&&this.year()<=9999?'YYYY':'YYYYYY',n=i+'[\")]',this.format(e+t+'-MM-DD[T]HH:mm:ss.SSS'+n)},'undefined'!=typeof Symbol&&null!=Symbol.for&&(pn[Symbol.for('nodejs.util.inspect.custom')]=function(){return'Moment<'+this.format()+'>'}),pn.toJSON=function(){return this.isValid()?this.toISOString():null},pn.toString=function(){return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ')},pn.unix=function(){return Math.floor(this.valueOf()/1e3)},pn.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},pn.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},pn.eraName=function(){var e,t,n,r=this.localeData().eras();for(e=0,t=r.length;ethis.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},pn.isLocal=function(){return!!this.isValid()&&!this._isUTC},pn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},pn.isUtc=Ut,pn.isUTC=Ut,pn.zoneAbbr=function(){return this._isUTC?'UTC':''},pn.zoneName=function(){return this._isUTC?'Coordinated Universal Time':''},pn.dates=b('dates accessor is deprecated. Use date instead.',cn),pn.months=b('months accessor is deprecated. Use month instead',Te),pn.years=b('years accessor is deprecated. Use year instead',Re),pn.zone=b('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/',(function(e,t){return null!=e?('string'!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()})),pn.isDSTShifted=b('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information',(function(){if(!l(this._isDSTShifted))return this._isDSTShifted;var e,t={};return v(t,this),(t=bt(t))._a?(e=t._isUTC?f(t._a):St(t._a),this._isDSTShifted=this.isValid()&&function(e,t,n){var r,i=Math.min(e.length,t.length),s=Math.abs(e.length-t.length),a=0;for(r=0;r0):this._isDSTShifted=!1,this._isDSTShifted}));var gn=T.prototype;function vn(e,t,n,r){var i=ot(),s=f().set(r,t);return i[n](s,e)}function wn(e,t,n){if(u(e)&&(t=e,e=void 0),e=e||'',null!=t)return vn(e,t,n,'month');var r,i=[];for(r=0;r<12;r++)i[r]=vn(e,r,n,'month');return i}function kn(e,t,n,r){'boolean'==typeof e?(u(t)&&(n=t,t=void 0),t=t||''):(n=t=e,e=!1,u(t)&&(n=t,t=void 0),t=t||'');var i,s=ot(),a=e?s._week.dow:0,o=[];if(null!=n)return vn(t,(n+a)%7,r,'day');for(i=0;i<7;i++)o[i]=vn(t,(i+a)%7,r,'day');return o}gn.calendar=function(e,t,n){var r=this._calendar[e]||this._calendar.sameElse;return O(r)?r.call(t,n):r},gn.longDateFormat=function(e){var t=this._longDateFormat[e],n=this._longDateFormat[e.toUpperCase()];return t||!n?t:(this._longDateFormat[e]=n.match(P).map((function(e){return'MMMM'===e||'MM'===e||'DD'===e||'dddd'===e?e.slice(1):e})).join(''),this._longDateFormat[e])},gn.invalidDate=function(){return this._invalidDate},gn.ordinal=function(e){return this._ordinal.replace('%d',e)},gn.preparse=yn,gn.postformat=yn,gn.relativeTime=function(e,t,n,r){var i=this._relativeTime[n];return O(i)?i(e,t,n,r):i.replace(/%d/i,e)},gn.pastFuture=function(e,t){var n=this._relativeTime[e>0?'future':'past'];return O(n)?n(t):n.replace(/%s/i,t)},gn.set=function(e){var t,n;for(n in e)a(e,n)&&(O(t=e[n])?this[n]=t:this['_'+n]=t);this._config=e,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+'|'+/\\d{1,2}/.source)},gn.eras=function(e,t){var n,i,s,a=this._eras||ot('en')._eras;for(n=0,i=a.length;n=0)return l[r]},gn.erasConvertYear=function(e,t){var n=e.since<=e.until?1:-1;return void 0===t?r(e.since).year():r(e.since).year()+(t-e.offset)*n},gn.erasAbbrRegex=function(e){return a(this,'_erasAbbrRegex')||an.call(this),e?this._erasAbbrRegex:this._erasRegex},gn.erasNameRegex=function(e){return a(this,'_erasNameRegex')||an.call(this),e?this._erasNameRegex:this._erasRegex},gn.erasNarrowRegex=function(e){return a(this,'_erasNarrowRegex')||an.call(this),e?this._erasNarrowRegex:this._erasRegex},gn.months=function(e,t){return e?i(this._months)?this._months[e.month()]:this._months[(this._months.isFormat||Me).test(t)?'format':'standalone'][e.month()]:i(this._months)?this._months:this._months.standalone},gn.monthsShort=function(e,t){return e?i(this._monthsShort)?this._monthsShort[e.month()]:this._monthsShort[Me.test(t)?'format':'standalone'][e.month()]:i(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},gn.monthsParse=function(e,t,n){var r,i,s;if(this._monthsParseExact)return Oe.call(this,e,t,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),r=0;r<12;r++){if(i=f([2e3,r]),n&&!this._longMonthsParse[r]&&(this._longMonthsParse[r]=new RegExp('^'+this.months(i,'').replace('.','')+'$','i'),this._shortMonthsParse[r]=new RegExp('^'+this.monthsShort(i,'').replace('.','')+'$','i')),n||this._monthsParse[r]||(s='^'+this.months(i,'')+'|^'+this.monthsShort(i,''),this._monthsParse[r]=new RegExp(s.replace('.',''),'i')),n&&'MMMM'===t&&this._longMonthsParse[r].test(e))return r;if(n&&'MMM'===t&&this._shortMonthsParse[r].test(e))return r;if(!n&&this._monthsParse[r].test(e))return r}},gn.monthsRegex=function(e){return this._monthsParseExact?(a(this,'_monthsRegex')||Ne.call(this),e?this._monthsStrictRegex:this._monthsRegex):(a(this,'_monthsRegex')||(this._monthsRegex=Ye),this._monthsStrictRegex&&e?this._monthsStrictRegex:this._monthsRegex)},gn.monthsShortRegex=function(e){return this._monthsParseExact?(a(this,'_monthsRegex')||Ne.call(this),e?this._monthsShortStrictRegex:this._monthsShortRegex):(a(this,'_monthsShortRegex')||(this._monthsShortRegex=Se),this._monthsShortStrictRegex&&e?this._monthsShortStrictRegex:this._monthsShortRegex)},gn.week=function(e){return Ee(e,this._week.dow,this._week.doy).week},gn.firstDayOfYear=function(){return this._week.doy},gn.firstDayOfWeek=function(){return this._week.dow},gn.weekdays=function(e,t){var n=i(this._weekdays)?this._weekdays:this._weekdays[e&&!0!==e&&this._weekdays.isFormat.test(t)?'format':'standalone'];return!0===e?He(n,this._week.dow):e?n[e.day()]:n},gn.weekdaysMin=function(e){return!0===e?He(this._weekdaysMin,this._week.dow):e?this._weekdaysMin[e.day()]:this._weekdaysMin},gn.weekdaysShort=function(e){return!0===e?He(this._weekdaysShort,this._week.dow):e?this._weekdaysShort[e.day()]:this._weekdaysShort},gn.weekdaysParse=function(e,t,n){var r,i,s;if(this._weekdaysParseExact)return ze.call(this,e,t,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),r=0;r<7;r++){if(i=f([2e3,1]).day(r),n&&!this._fullWeekdaysParse[r]&&(this._fullWeekdaysParse[r]=new RegExp('^'+this.weekdays(i,'').replace('.','\\\\.?')+'$','i'),this._shortWeekdaysParse[r]=new RegExp('^'+this.weekdaysShort(i,'').replace('.','\\\\.?')+'$','i'),this._minWeekdaysParse[r]=new RegExp('^'+this.weekdaysMin(i,'').replace('.','\\\\.?')+'$','i')),this._weekdaysParse[r]||(s='^'+this.weekdays(i,'')+'|^'+this.weekdaysShort(i,'')+'|^'+this.weekdaysMin(i,''),this._weekdaysParse[r]=new RegExp(s.replace('.',''),'i')),n&&'dddd'===t&&this._fullWeekdaysParse[r].test(e))return r;if(n&&'ddd'===t&&this._shortWeekdaysParse[r].test(e))return r;if(n&&'dd'===t&&this._minWeekdaysParse[r].test(e))return r;if(!n&&this._weekdaysParse[r].test(e))return r}},gn.weekdaysRegex=function(e){return this._weekdaysParseExact?(a(this,'_weekdaysRegex')||qe.call(this),e?this._weekdaysStrictRegex:this._weekdaysRegex):(a(this,'_weekdaysRegex')||(this._weekdaysRegex=je),this._weekdaysStrictRegex&&e?this._weekdaysStrictRegex:this._weekdaysRegex)},gn.weekdaysShortRegex=function(e){return this._weekdaysParseExact?(a(this,'_weekdaysRegex')||qe.call(this),e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(a(this,'_weekdaysShortRegex')||(this._weekdaysShortRegex=Ge),this._weekdaysShortStrictRegex&&e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},gn.weekdaysMinRegex=function(e){return this._weekdaysParseExact?(a(this,'_weekdaysRegex')||qe.call(this),e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(a(this,'_weekdaysMinRegex')||(this._weekdaysMinRegex=Ze),this._weekdaysMinStrictRegex&&e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},gn.isPM=function(e){return'p'===(e+'').toLowerCase().charAt(0)},gn.meridiem=function(e,t,n){return e>11?n?'pm':'PM':n?'am':'AM'},st('en',{eras:[{since:'0001-01-01',until:1/0,offset:1,name:'Anno Domini',narrow:'AD',abbr:'AD'},{since:'0000-12-31',until:-1/0,offset:1,name:'Before Christ',narrow:'BC',abbr:'BC'}],dayOfMonthOrdinalParse:/\\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10;return e+(1===z(e%100/10)?'th':1===t?'st':2===t?'nd':3===t?'rd':'th')}}),r.lang=b('moment.lang is deprecated. Use moment.locale instead.',st),r.langData=b('moment.langData is deprecated. Use moment.localeData instead.',ot);var Dn=Math.abs;function bn(e,t,n,r){var i=Vt(t,n);return e._milliseconds+=r*i._milliseconds,e._days+=r*i._days,e._months+=r*i._months,e._bubble()}function Mn(e){return e<0?Math.floor(e):Math.ceil(e)}function Sn(e){return 4800*e/146097}function Yn(e){return 146097*e/4800}function On(e){return function(){return this.as(e)}}var xn=On('ms'),Tn=On('s'),Nn=On('m'),Pn=On('h'),Rn=On('d'),Wn=On('w'),Cn=On('M'),Fn=On('Q'),An=On('y');function En(e){return function(){return this.isValid()?this._data[e]:NaN}}var Un=En('milliseconds'),Hn=En('seconds'),Ln=En('minutes'),Vn=En('hours'),In=En('days'),jn=En('months'),Gn=En('years'),Zn=Math.round,zn={ss:44,s:45,m:45,h:22,d:26,w:null,M:11};function qn(e,t,n,r,i){return i.relativeTime(t||1,!!n,e,r)}var $n=Math.abs;function Bn(e){return(e>0)-(e<0)||+e}function Jn(){if(!this.isValid())return this.localeData().invalidDate();var e,t,n,r,i,s,a,o,l=$n(this._milliseconds)/1e3,u=$n(this._days),c=$n(this._months),d=this.asSeconds();return d?(e=Z(l/60),t=Z(e/60),l%=60,e%=60,n=Z(c/12),c%=12,r=l?l.toFixed(3).replace(/\\.?0+$/,''):'',i=d<0?'-':'',s=Bn(this._months)!==Bn(d)?'-':'',a=Bn(this._days)!==Bn(d)?'-':'',o=Bn(this._milliseconds)!==Bn(d)?'-':'',i+'P'+(n?s+n+'Y':'')+(c?s+c+'M':'')+(u?a+u+'D':'')+(t||e||l?'T':'')+(t?o+t+'H':'')+(e?o+e+'M':'')+(l?o+r+'S':'')):'P0D'}var Qn=Nt.prototype;return Qn.isValid=function(){return this._isValid},Qn.abs=function(){var e=this._data;return this._milliseconds=Dn(this._milliseconds),this._days=Dn(this._days),this._months=Dn(this._months),e.milliseconds=Dn(e.milliseconds),e.seconds=Dn(e.seconds),e.minutes=Dn(e.minutes),e.hours=Dn(e.hours),e.months=Dn(e.months),e.years=Dn(e.years),this},Qn.add=function(e,t){return bn(this,e,t,1)},Qn.subtract=function(e,t){return bn(this,e,t,-1)},Qn.as=function(e){if(!this.isValid())return NaN;var t,n,r=this._milliseconds;if('month'===(e=L(e))||'quarter'===e||'year'===e)switch(t=this._days+r/864e5,n=this._months+Sn(t),e){case'month':return n;case'quarter':return n/3;case'year':return n/12}else switch(t=this._days+Math.round(Yn(this._months)),e){case'week':return t/7+r/6048e5;case'day':return t+r/864e5;case'hour':return 24*t+r/36e5;case'minute':return 1440*t+r/6e4;case'second':return 86400*t+r/1e3;case'millisecond':return Math.floor(864e5*t)+r;default:throw new Error('Unknown unit '+e)}},Qn.asMilliseconds=xn,Qn.asSeconds=Tn,Qn.asMinutes=Nn,Qn.asHours=Pn,Qn.asDays=Rn,Qn.asWeeks=Wn,Qn.asMonths=Cn,Qn.asQuarters=Fn,Qn.asYears=An,Qn.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*z(this._months/12):NaN},Qn._bubble=function(){var e,t,n,r,i,s=this._milliseconds,a=this._days,o=this._months,l=this._data;return s>=0&&a>=0&&o>=0||s<=0&&a<=0&&o<=0||(s+=864e5*Mn(Yn(o)+a),a=0,o=0),l.milliseconds=s%1e3,e=Z(s/1e3),l.seconds=e%60,t=Z(e/60),l.minutes=t%60,n=Z(t/60),l.hours=n%24,a+=Z(n/24),i=Z(Sn(a)),o+=i,a-=Mn(Yn(i)),r=Z(o/12),o%=12,l.days=a,l.months=o,l.years=r,this},Qn.clone=function(){return Vt(this)},Qn.get=function(e){return e=L(e),this.isValid()?this[e+'s']():NaN},Qn.milliseconds=Un,Qn.seconds=Hn,Qn.minutes=Ln,Qn.hours=Vn,Qn.days=In,Qn.weeks=function(){return Z(this.days()/7)},Qn.months=jn,Qn.years=Gn,Qn.humanize=function(e,t){if(!this.isValid())return this.localeData().invalidDate();var n,r,i=!1,s=zn;return'object'==typeof e&&(t=e,e=!1),'boolean'==typeof e&&(i=e),'object'==typeof t&&(s=Object.assign({},zn,t),null!=t.s&&null==t.ss&&(s.ss=t.s-1)),n=this.localeData(),r=function(e,t,n,r){var i=Vt(e).abs(),s=Zn(i.as('s')),a=Zn(i.as('m')),o=Zn(i.as('h')),l=Zn(i.as('d')),u=Zn(i.as('M')),c=Zn(i.as('w')),d=Zn(i.as('y')),h=s<=n.ss&&['s',s]||s0,h[4]=r,qn.apply(null,h)}(this,!i,s,n),i&&(r=n.pastFuture(+this,r)),n.postformat(r)},Qn.toISOString=Jn,Qn.toString=Jn,Qn.toJSON=Jn,Qn.locale=Xt,Qn.localeData=en,Qn.toIsoString=b('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)',Jn),Qn.lang=Kt,F('X',0,0,'unix'),F('x',0,0,'valueOf'),fe('x',ue),fe('X',/[+-]?\\d+(\\.\\d{1,3})?/),ge('X',(function(e,t,n){n._d=new Date(1e3*parseFloat(e))})),ge('x',(function(e,t,n){n._d=new Date(z(e))})),r.version='2.29.1',t=St,r.fn=pn,r.min=function(){var e=[].slice.call(arguments,0);return xt('isBefore',e)},r.max=function(){var e=[].slice.call(arguments,0);return xt('isAfter',e)},r.now=function(){return Date.now?Date.now():+new Date},r.utc=f,r.unix=function(e){return St(1e3*e)},r.months=function(e,t){return wn(e,t,'months')},r.isDate=c,r.locale=st,r.invalid=p,r.duration=Vt,r.isMoment=k,r.weekdays=function(e,t,n){return kn(e,t,n,'weekdays')},r.parseZone=function(){return St.apply(null,arguments).parseZone()},r.localeData=ot,r.isDuration=Pt,r.monthsShort=function(e,t){return wn(e,t,'monthsShort')},r.weekdaysMin=function(e,t,n){return kn(e,t,n,'weekdaysMin')},r.defineLocale=at,r.updateLocale=function(e,t){if(null!=t){var n,r,i=Ke;null!=et[e]&&null!=et[e].parentLocale?et[e].set(x(et[e]._config,t)):(null!=(r=it(e))&&(i=r._config),t=x(i,t),null==r&&(t.abbr=e),(n=new T(t)).parentLocale=et[e],et[e]=n),st(e)}else null!=et[e]&&(null!=et[e].parentLocale?(et[e]=et[e].parentLocale,e===st()&&st(e)):null!=et[e]&&delete et[e]);return et[e]},r.locales=function(){return M(et)},r.weekdaysShort=function(e,t,n){return kn(e,t,n,'weekdaysShort')},r.normalizeUnits=L,r.relativeTimeRounding=function(e){return void 0===e?Zn:'function'==typeof e&&(Zn=e,!0)},r.relativeTimeThreshold=function(e,t){return void 0!==zn[e]&&(void 0===t?zn[e]:(zn[e]=t,'s'===e&&(zn.ss=t-1),!0))},r.calendarFormat=function(e,t){var n=e.diff(t,'days',!0);return n<-6?'sameElse':n<-1?'lastWeek':n<0?'lastDay':n<1?'sameDay':n<2?'nextDay':n<7?'nextWeek':'sameElse'},r.prototype=pn,r.HTML5_FMT={DATETIME_LOCAL:'YYYY-MM-DDTHH:mm',DATETIME_LOCAL_SECONDS:'YYYY-MM-DDTHH:mm:ss',DATETIME_LOCAL_MS:'YYYY-MM-DDTHH:mm:ss.SSS',DATE:'YYYY-MM-DD',TIME:'HH:mm',TIME_SECONDS:'HH:mm:ss',TIME_MS:'HH:mm:ss.SSS',WEEK:'GGGG-[W]WW',MONTH:'YYYY-MM'},r}()}).call(this,n(3)(e))},function(e,t,n){var r=n(2),i=n(5);e.exports=i(r,contact,reports)},function(e,t,n){const r=n(0),i=n(4),{today:s,MAX_DAYS_IN_PREGNANCY:a,isHighRiskPregnancy:o,getNewestReport:l,getSubsequentPregnancyFollowUps:u,getSubsequentDeliveries:c,isAlive:d,isReadyForNewPregnancy:h,isReadyForDelivery:f,isActivePregnancy:_,countANCFacilityVisits:m,getAllRiskFactors:p,getLatestDangerSignsForPregnancy:y,getNextANCVisitDate:g,getMostRecentLMPDateForPregnancy:v,getMostRecentEDDForPregnancy:w,getDeliveryDate:k,getFormArraySubmittedInWindow:D,getRecentANCVisitWithEvent:b,getAllRiskFactorExtra:M,getField:S}=i,Y=contact,O=lineage,x=reports,T={alive:d(Y),muted:!1,show_pregnancy_form:h(Y,x),show_delivery_form:f(Y,x)},N=[{appliesToType:'person',label:'patient_id',value:Y.patient_id,width:4},{appliesToType:'person',label:'contact.age',value:Y.date_of_birth,width:4,filter:'age'},{appliesToType:'person',label:'contact.sex',value:'contact.sex.'+Y.sex,translate:!0,width:4},{appliesToType:'person',label:'person.field.phone',value:Y.phone,width:4},{appliesToType:'person',label:'person.field.alternate_phone',value:Y.phone_alternate,width:4},{appliesToType:'person',label:'External ID',value:Y.external_id,width:4},{appliesToType:'person',label:'contact.parent',value:O,filter:'lineage'},{appliesToType:'!person',label:'contact',value:Y.contact&&Y.contact.name,width:4},{appliesToType:'!person',label:'contact.phone',value:Y.contact&&Y.contact.phone,width:4},{appliesToType:'!person',label:'External ID',value:Y.external_id,width:4},{appliesToType:'!person',appliesIf:function(){return Y.parent&&O[0]},label:'contact.parent',value:O,filter:'lineage'},{appliesToType:'person',label:'contact.notes',value:Y.notes,width:12},{appliesToType:'!person',label:'contact.notes',value:Y.notes,width:12}];Y.short_name&&N.unshift({appliesToType:'person',label:'contact.short_name',value:Y.short_name,width:4});const P=[{label:'contact.profile.pregnancy.active',appliesToType:'report',appliesIf:function(e){return _(Y,x,e)},fields:function(e){const t=[],n=p(x,e),i=M(x,e),a=y(x,e),c=o(x,e),d=l(x,['pregnancy','pregnancy_home_visit']),h=r(d.reported_date),f=v(x,e),_=w(x,e),k=g(x,e),D=f?s.diff(f,'weeks'):null;let Y=S(e,'lmp_approx'),O=e.reported_date;u(x,e).forEach((function(e){e.reported_date>O&&'yes'===S(e,'lmp_updated')&&(O=e.reported_date,S(e,'lmp_method_approx')&&(Y=S(e,'lmp_method_approx')))}));const T=b(x,e,'migrated'),N=b(x,e,'refused'),P=T||N;if(P){const e='clear_all'===S(P,'pregnancy_ended.clear_option');t.push({label:'contact.profile.change_care',value:T?'Migrated out of area':'Refusing care',width:6},{label:'contact.profile.tasks_on_off',value:e?'Off':'On',width:6})}if(t.push({label:'Weeks Pregnant',value:D||0===D?{number:D,approximate:'yes'===Y}:'contact.profile.value.unknown',translate:!D&&0!==D,filter:D||0===D?'weeksPregnant':'',width:6},{label:'contact.profile.edd',value:_?_.valueOf():'contact.profile.value.unknown',translate:!_,filter:_?'simpleDate':'',width:6}),c){let e='';e=!n&&i?i.join(', '):n.length>1||n&&i?'contact.profile.risk.multiple':'contact.profile.danger_sign.'+n[0],t.push({label:'contact.profile.risk.high',value:e,translate:!0,icon:'icon-risk',width:6})}return a.length>0&&t.push({label:'contact.profile.danger_signs.current',value:a.length>1?'contact.profile.danger_sign.multiple':'contact.profile.danger_sign.'+a[0],translate:!0,width:6}),t.push({label:'contact.profile.visit',value:'contact.profile.visits.of',context:{count:m(x,e),total:8},translate:!0,width:6},{label:'contact.profile.last_visited',value:h.valueOf(),filter:'relativeDay',width:6}),k&&k.isSameOrAfter(s)&&t.push({label:'contact.profile.anc.next',value:k.valueOf(),filter:'simpleDate',width:6}),t},modifyContext:function(e,t){let n=S(t,'lmp_date_8601'),r=S(t,'lmp_method_approx'),i=S(t,'hiv_status_known'),s=S(t,'deworming_med_received'),a=S(t,'tt_received');const o=p(x,t),l=M(x,t);let c=S(t,'t_pregnancy_follow_up_date');u(x,t).forEach((function(e){'yes'===S(e,'lmp_updated')&&(n=S(e,'lmp_date_8601'),r=S(e,'lmp_method_approx')),i=S(e,'hiv_status_known'),s=S(e,'deworming_med_received'),a=S(e,'tt_received'),'yes'===S(e,'t_pregnancy_follow_up')&&(c=S(e,'t_pregnancy_follow_up_date'))})),e.lmp_date_8601=n,e.lmp_method_approx=r,e.is_active_pregnancy=!0,e.deworming_med_received=s,e.hiv_tested_past=i,e.tt_received_past=a,e.risk_factor_codes=o.join(' '),e.risk_factor_extra=l.join('; '),e.pregnancy_follow_up_date_recent=c,e.pregnancy_uuid=t._id}},{label:'contact.profile.death.title',appliesToType:'person',appliesIf:function(){return!d(Y)},fields:function(){const e=[];let t,n;const r=l(x,['death_report']);if(r){const e=S(r,'death_details');e&&(t=e.date_of_death,n=e.place_of_death)}else Y.date_of_death&&(t=Y.date_of_death);return e.push({label:'contact.profile.death.date',value:t||'contact.profile.value.unknown',filter:t?'simpleDate':'',translate:!t,width:6},{label:'contact.profile.death.place',value:n||'contact.profile.value.unknown',translate:!0,width:6}),e}},{label:'contact.profile.pregnancy.past',appliesToType:'report',appliesIf:function(e){if('person'!==Y.type)return!1;if('delivery'===e.form)return!0;if('pregnancy'===e.form){if(b(x,e,'abortion')||b(x,e,'miscarriage'))return!0;const t=v(x,e);return t&&s.isSameOrAfter(t.clone().add(42,'weeks'))&&0===c(x,e,a).length}return!1},fields:function(e){const t=[];let n,i,l='',u=0,d=0,h=0;if('delivery'===e.form){const s=r(e.reported_date);n=D(x,['pregnancy'],s.clone().subtract(a,'days').toDate(),s.toDate())[0],S(e,'delivery_outcome')&&(i=k(e),l=S(e,'delivery_outcome.delivery_place'),u=S(e,'delivery_outcome.babies_delivered_num'),d=S(e,'delivery_outcome.babies_deceased_num'),t.push({label:'contact.profile.delivery_date',value:i?i.valueOf():'',filter:'simpleDate',width:6},{label:'contact.profile.delivery_place',value:l,translate:!0,width:6},{label:'contact.profile.delivered_babies',value:u,width:6}))}else if('pregnancy'===e.form){n=e;const o=v(x,n),l=b(x,n,'abortion'),u=b(x,n,'miscarriage');if(l||u){let e='',n=r(0),i=0;l?(e='abortion',n=r(S(l,'pregnancy_ended.abortion_date'))):(e='miscarriage',n=r(S(u,'pregnancy_ended.miscarriage_date'))),i=n.diff(o,'weeks'),t.push({label:'contact.profile.pregnancy.end_early',value:e,translate:!0,width:6},{label:'contact.profile.pregnancy.end_date',value:n.valueOf(),filter:'simpleDate',width:6},{label:'contact.profile.pregnancy.end_weeks',value:i>0?i:'contact.profile.value.unknown',translate:i<=0,width:6})}else o&&s.isSameOrAfter(o.clone().add(42,'weeks'))&&0===c(x,e,a).length&&(i=w(x,e),t.push({label:'contact.profile.delivery_date',value:i?i.valueOf():'contact.profile.value.unknown',filter:'simpleDate',translate:!i,width:6}))}if(d>0&&S(e,'baby_death')){t.push({label:'contact.profile.deceased_babies',value:d,width:6});let n=S(e,'baby_death.baby_death_repeat');n||(n=[]);let r=0;n.forEach((function(e){r>0&&t.push({label:'',value:'',width:6}),t.push({label:'contact.profile.newborn.death_date',value:e.baby_death_date,filter:'simpleDate',width:6},{label:'contact.profile.newborn.death_place',value:e.baby_death_place,translate:!0,width:6},{label:'contact.profile.delivery.stillbirthQ',value:e.stillbirth,translate:!0,width:6}),r++,r===n.length&&t.push({label:'',value:'',width:6})}))}if(n){h=m(x,n),t.push({label:'contact.profile.anc_visit',value:h,width:3});if(o(x,n)){let e='';const r=p(x,n),i=M(x,n);e=!r&&i?i.join(', '):r.length>1||r&&i?'contact.profile.risk.multiple':'contact.profile.danger_sign.'+r[0],t.push({label:'contact.profile.risk.high',value:e,translate:!0,icon:'icon-risk',width:6})}}return t}}];e.exports={context:T,cards:P,fields:N}},function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,'loaded',{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,'id',{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},function(e,t,n){const r=n(0),i=r().startOf('day'),s=['pregnancy'],a=['pregnancy_home_visit'],o=['delivery'],l=['pregnancy','pregnancy_home_visit','pregnancy_danger_sign','pregannacy_danger_sign_follow_up'],u=(e,t)=>['fields',...(t||'').split('.')].reduce((e,t)=>{if(void 0!==e)return e[t]},e);function c(e,t,n,r){return e.filter((function(e){return t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r}))}function d(e,t){let n;return e.forEach((function(e){(function(e){return!!(e.form&&e.fields&&e.reported_date)})(e)&&t.includes(e.form)&&(!n||e.reported_date>n.reported_date)&&(n=e)})),n}function h(e){return D(e)&&u(e,'lmp_date_8601')&&r(u(e,'lmp_date_8601'))}function f(e,t){let n=h(t),i=t.reported_date;return x(e,t).forEach((function(e){const t=b(s=e)&&u(s,'lmp_date_8601')&&r(u(s,'lmp_date_8601'));var s;e.reported_date>i&&'yes'===u(e,'lmp_updated')&&(i=e.reported_date,n=t)})),n}function _(e,t){const n=f(e,t);if(n)return n.clone().add(280,'days')}function m(e){return M(e)&&u(e,'delivery_outcome.delivery_date')&&r(u(e,'delivery_outcome.delivery_date'))}function p(e){const t=[];if('yes'===u(e,'t_danger_signs_referral_follow_up')){const n=u(e,'danger_signs');if(n)for(const e in n)'yes'===n[e]&&'r_danger_sign_present'!==e&&t.push(e)}return t}function y(e){const t=[];if(!D(e))return[];if('yes'===u(e,'risk_factors.r_risk_factor_present')){'yes'===u(e,'risk_factors.risk_factors_history.first_pregnancy')&&t.push('first_pregnancy'),'yes'===u(e,'risk_factors.risk_factors_history.previous_miscarriage')&&t.push('previous_miscarriage');const n=u(e,'risk_factors.risk_factors_present.primary_condition'),r=u(e,'risk_factors.risk_factors_present.secondary_condition');n&&t.push(...n.split(' ')),r&&t.push(...r.split(' '))}return t}function g(e,t){const n=y(t);return x(e,t).forEach((function(e){n.push(...function(e){const t=[];if(!b(e))return[];if('yes'===u(e,'anc_visits_hf.risk_factors.r_risk_factor_present')){const n=u(e,'anc_visits_hf.risk_factors.new_risks');n&&t.push(...n.split(' '))}return t}(e))})),n}function v(e){let t;return e&&D(e)?t=u(e,'risk_factors.risk_factors_present.additional_risk'):e&&b(e)&&(t=u(e,'anc_visits_hf.risk_factors.additional_risk')),t}function w(e,t){const n=[],r=v(t);r&&n.push(r);return x(e,t).forEach((function(e){const t=v(e);t&&n.push(t)})),n}function k(e){return e&&!e.date_of_death}function D(e){return e&&s.includes(e.form)}function b(e){return e&&a.includes(e.form)}function M(e){return e&&o.includes(e.form)}function S(e,t,n){if('person'!==e.type||!k(e)||!D(n))return!1;const r=(f(t,n)||n.reported_date)>i.clone().subtract(294,'day'),s=O(t,n,42).length>0,a=function(e,t){return e.filter((function(e){return D(e)&&e.reported_date>t.reported_date}))}(t,n).length>0;return r&&!s&&!a&&!Y(t,n,'abortion')&&!Y(t,n,'miscarriage')}function Y(e,t,n){const r=d(x(e,t),a);if(r&&u(r,'pregnancy_summary.visit_option')===n)return r}function O(e,t,n){return e.filter((function(e){return M(e)&&e.reported_date>t.reported_date&&(!n||e.reported_date>=i.clone().subtract(n,'days'))}))}function x(e,t){let n=h(t);n||(n=r(t.reported_date));return e.filter((function(e){return b(e)&&e.reported_date>t.reported_date&&r(e.reported_date)S(e))},isActivePregnancy:S,countANCFacilityVisits:function(e,t){let n=0;const r=x(e,t);return u(t,'anc_visits_hf.anc_visits_hf_past')&&!isNaN(u(t,'anc_visits_hf.anc_visits_hf_past.visited_hf_count'))&&(n+=parseInt(u(t,'anc_visits_hf.anc_visits_hf_past.visited_hf_count'))),n+=r.reduce((function(e,t){const n=u(t,'anc_visits_hf.anc_visits_hf_past');return n?(e+='yes'===n.last_visit_attended&&1,isNaN(n.visited_hf_count)?e:e+('yes'===n.report_other_visits&&parseInt(n.visited_hf_count))):0}),0),n},knowsHIVStatusInPast3Months:function(e){let t=!1;return c(e,s,i.clone().subtract(3,'months'),i).forEach((function(e){'yes'===u(e,'pregnancy_new_or_current.hiv_status.hiv_status_know')&&(t=!0)})),t},getAllRiskFactors:g,getAllRiskFactorExtra:w,getDangerSignCodes:p,getLatestDangerSignsForPregnancy:function(e,t){if(!t)return[];let n=f(e,t);n||(n=r(t.reported_date));const i=c(e,l,n.toDate(),n.clone().add(294,'days').toDate()),s=[];i.forEach(e=>{b(e)?'yes'===u(e,'pregnancy_summary.visit_option')&&s.push(e):s.push(e)});const a=d(s,l);return a?p(a):[]},getNextANCVisitDate:function(e,t){let n=u(t,'t_pregnancy_follow_up_date'),i=t.reported_date;return x(e,t).forEach((function(e){e.reported_date>i&&u(e,'t_pregnancy_follow_up_date')&&(i=e.reported_date,n=u(e,'t_pregnancy_follow_up_date'))})),r(n)},isReadyForNewPregnancy:function(e,t){if('person'!==e.type)return!1;const n=d(t,s),a=d(t,o);if(!n&&!a)return!0;if(n){if(!a||a.reported_daten.reported_date))return m(a)0&&!r.includes('!'+l))&&(!e.appliesIf||e.appliesIf()))return delete e.appliesToType,delete e.appliesIf,!0}))};return o.forEach((function(e){var t,s,o,c,d=n(e.appliesToType);if(d.includes('report')&&d.length>1)throw new Error('You cannot set appliesToType to an array which includes the type \\'report\\' and another type.');if(d.includes('report'))for(t=0;t0)return;(o=i(e,a))&&u.cards.push(o)}})),u.context=a,u}}])})); return ContactSummary;", + "contact_summary": "var ContactSummary = {}; !function(e,t){if('object'==typeof exports&&'object'==typeof module)module.exports=t();else if('function'==typeof define&&define.amd)define([],t);else{var n=t();for(var r in n)('object'==typeof exports?exports:e)[r]=n[r]}}(ContactSummary,(function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){'undefined'!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:'Module'}),Object.defineProperty(e,'__esModule',{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&'object'==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,'default',{enumerable:!0,value:e}),2&t&&'string'!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,'a',t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p='',n(n.s=1)}([function(e,t,n){(function(e){e.exports=function(){'use strict';var t,n;function r(){return t.apply(null,arguments)}function i(e){return e instanceof Array||'[object Array]'===Object.prototype.toString.call(e)}function s(e){return null!=e&&'[object Object]'===Object.prototype.toString.call(e)}function a(e){return void 0===e}function o(e){return'number'==typeof e||'[object Number]'===Object.prototype.toString.call(e)}function l(e){return e instanceof Date||'[object Date]'===Object.prototype.toString.call(e)}function u(e,t){var n,r=[];for(n=0;n>>0,r=0;r0)for(n=0;n=0?n?'+':'':'-')+Math.pow(10,Math.max(0,i)).toString().substr(1)+r}var L=/(\\[[^\\[]*\\])|(\\\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,A=/(\\[[^\\[]*\\])|(\\\\)?(LTS|LT|LL?L?L?|l{1,4})/g,V={},I={};function G(e,t,n,r){var i=r;'string'==typeof r&&(i=function(){return this[r]()}),e&&(I[e]=i),t&&(I[t[0]]=function(){return E(i.apply(this,arguments),t[1],t[2])}),n&&(I[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),e)})}function j(e,t){return e.isValid()?(t=Z(t,e.localeData()),V[t]=V[t]||function(e){var t,n,r,i=e.match(L);for(t=0,n=i.length;t=0&&A.test(e);)e=e.replace(A,r),A.lastIndex=0,n-=1;return e}var z=/\\d/,q=/\\d\\d/,$=/\\d{3}/,J=/\\d{4}/,B=/[+-]?\\d{6}/,Q=/\\d\\d?/,X=/\\d\\d\\d\\d?/,K=/\\d\\d\\d\\d\\d\\d?/,ee=/\\d{1,3}/,te=/\\d{1,4}/,ne=/[+-]?\\d{1,6}/,re=/\\d+/,ie=/[+-]?\\d+/,se=/Z|[+-]\\d\\d:?\\d\\d/gi,ae=/Z|[+-]\\d\\d(?::?\\d\\d)?/gi,oe=/[0-9]{0,256}['a-z\\u00A0-\\u05FF\\u0700-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFF07\\uFF10-\\uFFEF]{1,256}|[\\u0600-\\u06FF\\/]{1,256}(\\s*?[\\u0600-\\u06FF]{1,256}){1,2}/i,le={};function ue(e,t,n){le[e]=x(t)?t:function(e,r){return e&&n?n:t}}function de(e,t){return d(le,e)?le[e](t._strict,t._locale):new RegExp(ce(e.replace('\\\\','').replace(/\\\\(\\[)|\\\\(\\])|\\[([^\\]\\[]*)\\]|\\\\(.)/g,(function(e,t,n,r,i){return t||n||r||i}))))}function ce(e){return e.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g,'\\\\$&')}var he={};function fe(e,t){var n,r=t;for('string'==typeof e&&(e=[e]),o(t)&&(r=function(e,n){n[t]=D(e)}),n=0;n68?1900:2e3)};var ge,ve=we('FullYear',!0);function we(e,t){return function(n){return null!=n?(De(this,e,n),r.updateOffset(this,t),this):ke(this,e)}}function ke(e,t){return e.isValid()?e._d['get'+(e._isUTC?'UTC':'')+t]():NaN}function De(e,t,n){e.isValid()&&!isNaN(n)&&('FullYear'===t&&ye(e.year())&&1===e.month()&&29===e.date()?e._d['set'+(e._isUTC?'UTC':'')+t](n,e.month(),Me(n,e.month())):e._d['set'+(e._isUTC?'UTC':'')+t](n))}function Me(e,t){if(isNaN(e)||isNaN(t))return NaN;var n,r=(t%(n=12)+n)%n;return e+=(t-r)/12,1===r?ye(e)?29:28:31-r%7%2}ge=Array.prototype.indexOf?Array.prototype.indexOf:function(e){var t;for(t=0;t=0?(o=new Date(e+400,t,n,r,i,s,a),isFinite(o.getFullYear())&&o.setFullYear(e)):o=new Date(e,t,n,r,i,s,a),o}function Fe(e){var t;if(e<100&&e>=0){var n=Array.prototype.slice.call(arguments);n[0]=e+400,t=new Date(Date.UTC.apply(null,n)),isFinite(t.getUTCFullYear())&&t.setUTCFullYear(e)}else t=new Date(Date.UTC.apply(null,arguments));return t}function Ne(e,t,n){var r=7+t-n;return-(7+Fe(e,0,r).getUTCDay()-t)%7+r-1}function He(e,t,n,r,i){var s,a,o=1+7*(t-1)+(7+n-r)%7+Ne(e,r,i);return o<=0?a=pe(s=e-1)+o:o>pe(e)?(s=e+1,a=o-pe(e)):(s=e,a=o),{year:s,dayOfYear:a}}function Ue(e,t,n){var r,i,s=Ne(e.year(),t,n),a=Math.floor((e.dayOfYear()-s-1)/7)+1;return a<1?r=a+Ee(i=e.year()-1,t,n):a>Ee(e.year(),t,n)?(r=a-Ee(e.year(),t,n),i=e.year()+1):(i=e.year(),r=a),{week:r,year:i}}function Ee(e,t,n){var r=Ne(e,t,n),i=Ne(e+1,t,n);return(pe(e)-r+i)/7}function Le(e,t){return e.slice(t,7).concat(e.slice(0,t))}G('w',['ww',2],'wo','week'),G('W',['WW',2],'Wo','isoWeek'),C('week','w'),C('isoWeek','W'),U('week',5),U('isoWeek',5),ue('w',Q),ue('ww',Q,q),ue('W',Q),ue('WW',Q,q),_e(['w','ww','W','WW'],(function(e,t,n,r){t[r.substr(0,1)]=D(e)})),G('d',0,'do','day'),G('dd',0,0,(function(e){return this.localeData().weekdaysMin(this,e)})),G('ddd',0,0,(function(e){return this.localeData().weekdaysShort(this,e)})),G('dddd',0,0,(function(e){return this.localeData().weekdays(this,e)})),G('e',0,0,'weekday'),G('E',0,0,'isoWeekday'),C('day','d'),C('weekday','e'),C('isoWeekday','E'),U('day',11),U('weekday',11),U('isoWeekday',11),ue('d',Q),ue('e',Q),ue('E',Q),ue('dd',(function(e,t){return t.weekdaysMinRegex(e)})),ue('ddd',(function(e,t){return t.weekdaysShortRegex(e)})),ue('dddd',(function(e,t){return t.weekdaysRegex(e)})),_e(['dd','ddd','dddd'],(function(e,t,n,r){var i=n._locale.weekdaysParse(e,r,n._strict);null!=i?t.d=i:f(n).invalidWeekday=e})),_e(['d','e','E'],(function(e,t,n,r){t[r]=D(e)}));var Ae='Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),Ve='Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),Ie='Su_Mo_Tu_We_Th_Fr_Sa'.split('_');function Ge(e,t,n){var r,i,s,a=e.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],r=0;r<7;++r)s=h([2e3,1]).day(r),this._minWeekdaysParse[r]=this.weekdaysMin(s,'').toLocaleLowerCase(),this._shortWeekdaysParse[r]=this.weekdaysShort(s,'').toLocaleLowerCase(),this._weekdaysParse[r]=this.weekdays(s,'').toLocaleLowerCase();return n?'dddd'===t?-1!==(i=ge.call(this._weekdaysParse,a))?i:null:'ddd'===t?-1!==(i=ge.call(this._shortWeekdaysParse,a))?i:null:-1!==(i=ge.call(this._minWeekdaysParse,a))?i:null:'dddd'===t?-1!==(i=ge.call(this._weekdaysParse,a))||-1!==(i=ge.call(this._shortWeekdaysParse,a))||-1!==(i=ge.call(this._minWeekdaysParse,a))?i:null:'ddd'===t?-1!==(i=ge.call(this._shortWeekdaysParse,a))||-1!==(i=ge.call(this._weekdaysParse,a))||-1!==(i=ge.call(this._minWeekdaysParse,a))?i:null:-1!==(i=ge.call(this._minWeekdaysParse,a))||-1!==(i=ge.call(this._weekdaysParse,a))||-1!==(i=ge.call(this._shortWeekdaysParse,a))?i:null}var je=oe,Ze=oe,ze=oe;function qe(){function e(e,t){return t.length-e.length}var t,n,r,i,s,a=[],o=[],l=[],u=[];for(t=0;t<7;t++)n=h([2e3,1]).day(t),r=this.weekdaysMin(n,''),i=this.weekdaysShort(n,''),s=this.weekdays(n,''),a.push(r),o.push(i),l.push(s),u.push(r),u.push(i),u.push(s);for(a.sort(e),o.sort(e),l.sort(e),u.sort(e),t=0;t<7;t++)o[t]=ce(o[t]),l[t]=ce(l[t]),u[t]=ce(u[t]);this._weekdaysRegex=new RegExp('^('+u.join('|')+')','i'),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp('^('+l.join('|')+')','i'),this._weekdaysShortStrictRegex=new RegExp('^('+o.join('|')+')','i'),this._weekdaysMinStrictRegex=new RegExp('^('+a.join('|')+')','i')}function $e(){return this.hours()%12||12}function Je(e,t){G(e,0,0,(function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)}))}function Be(e,t){return t._meridiemParse}G('H',['HH',2],0,'hour'),G('h',['hh',2],0,$e),G('k',['kk',2],0,(function(){return this.hours()||24})),G('hmm',0,0,(function(){return''+$e.apply(this)+E(this.minutes(),2)})),G('hmmss',0,0,(function(){return''+$e.apply(this)+E(this.minutes(),2)+E(this.seconds(),2)})),G('Hmm',0,0,(function(){return''+this.hours()+E(this.minutes(),2)})),G('Hmmss',0,0,(function(){return''+this.hours()+E(this.minutes(),2)+E(this.seconds(),2)})),Je('a',!0),Je('A',!1),C('hour','h'),U('hour',13),ue('a',Be),ue('A',Be),ue('H',Q),ue('h',Q),ue('k',Q),ue('HH',Q,q),ue('hh',Q,q),ue('kk',Q,q),ue('hmm',X),ue('hmmss',K),ue('Hmm',X),ue('Hmmss',K),fe(['H','HH'],3),fe(['k','kk'],(function(e,t,n){var r=D(e);t[3]=24===r?0:r})),fe(['a','A'],(function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e})),fe(['h','hh'],(function(e,t,n){t[3]=D(e),f(n).bigHour=!0})),fe('hmm',(function(e,t,n){var r=e.length-2;t[3]=D(e.substr(0,r)),t[4]=D(e.substr(r)),f(n).bigHour=!0})),fe('hmmss',(function(e,t,n){var r=e.length-4,i=e.length-2;t[3]=D(e.substr(0,r)),t[4]=D(e.substr(r,2)),t[5]=D(e.substr(i)),f(n).bigHour=!0})),fe('Hmm',(function(e,t,n){var r=e.length-2;t[3]=D(e.substr(0,r)),t[4]=D(e.substr(r))})),fe('Hmmss',(function(e,t,n){var r=e.length-4,i=e.length-2;t[3]=D(e.substr(0,r)),t[4]=D(e.substr(r,2)),t[5]=D(e.substr(i))}));var Qe,Xe=we('Hours',!0),Ke={calendar:{sameDay:'[Today at] LT',nextDay:'[Tomorrow at] LT',nextWeek:'dddd [at] LT',lastDay:'[Yesterday at] LT',lastWeek:'[Last] dddd [at] LT',sameElse:'L'},longDateFormat:{LTS:'h:mm:ss A',LT:'h:mm A',L:'MM/DD/YYYY',LL:'MMMM D, YYYY',LLL:'MMMM D, YYYY h:mm A',LLLL:'dddd, MMMM D, YYYY h:mm A'},invalidDate:'Invalid date',ordinal:'%d',dayOfMonthOrdinalParse:/\\d{1,2}/,relativeTime:{future:'in %s',past:'%s ago',s:'a few seconds',ss:'%d seconds',m:'a minute',mm:'%d minutes',h:'an hour',hh:'%d hours',d:'a day',dd:'%d days',M:'a month',MM:'%d months',y:'a year',yy:'%d years'},months:be,monthsShort:Ye,week:{dow:0,doy:6},weekdays:Ae,weekdaysMin:Ie,weekdaysShort:Ve,meridiemParse:/[ap]\\.?m?\\.?/i},et={},tt={};function nt(e){return e?e.toLowerCase().replace('_','-'):e}function rt(t){var n=null;if(!et[t]&&void 0!==e&&e&&e.exports)try{n=Qe._abbr,!function(){var e=new Error('Cannot find module \\'undefined\\'');throw e.code='MODULE_NOT_FOUND',e}(),it(n)}catch(e){}return et[t]}function it(e,t){var n;return e&&((n=a(t)?at(e):st(e,t))?Qe=n:'undefined'!=typeof console&&console.warn&&console.warn('Locale '+e+' not found. Did you forget to load it?')),Qe._abbr}function st(e,t){if(null!==t){var n,r=Ke;if(t.abbr=e,null!=et[e])O('defineLocaleOverride','use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'),r=et[e]._config;else if(null!=t.parentLocale)if(null!=et[t.parentLocale])r=et[t.parentLocale]._config;else{if(null==(n=rt(t.parentLocale)))return tt[t.parentLocale]||(tt[t.parentLocale]=[]),tt[t.parentLocale].push({name:e,config:t}),null;r=n._config}return et[e]=new W(P(r,t)),tt[e]&&tt[e].forEach((function(e){st(e.name,e.config)})),it(e),et[e]}return delete et[e],null}function at(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return Qe;if(!i(e)){if(t=rt(e))return t;e=[e]}return function(e){for(var t,n,r,i,s=0;s0;){if(r=rt(i.slice(0,t).join('-')))return r;if(n&&n.length>=t&&M(i,n,!0)>=t-1)break;t--}s++}return Qe}(e)}function ot(e){var t,n=e._a;return n&&-2===f(e).overflow&&(t=n[1]<0||n[1]>11?1:n[2]<1||n[2]>Me(n[0],n[1])?2:n[3]<0||n[3]>24||24===n[3]&&(0!==n[4]||0!==n[5]||0!==n[6])?3:n[4]<0||n[4]>59?4:n[5]<0||n[5]>59?5:n[6]<0||n[6]>999?6:-1,f(e)._overflowDayOfYear&&(t<0||t>2)&&(t=2),f(e)._overflowWeeks&&-1===t&&(t=7),f(e)._overflowWeekday&&-1===t&&(t=8),f(e).overflow=t),e}function lt(e,t,n){return null!=e?e:null!=t?t:n}function ut(e){var t,n,i,s,a,o=[];if(!e._d){for(i=function(e){var t=new Date(r.now());return e._useUTC?[t.getUTCFullYear(),t.getUTCMonth(),t.getUTCDate()]:[t.getFullYear(),t.getMonth(),t.getDate()]}(e),e._w&&null==e._a[2]&&null==e._a[1]&&function(e){var t,n,r,i,s,a,o,l;if(null!=(t=e._w).GG||null!=t.W||null!=t.E)s=1,a=4,n=lt(t.GG,e._a[0],Ue(St(),1,4).year),r=lt(t.W,1),((i=lt(t.E,1))<1||i>7)&&(l=!0);else{s=e._locale._week.dow,a=e._locale._week.doy;var u=Ue(St(),s,a);n=lt(t.gg,e._a[0],u.year),r=lt(t.w,u.week),null!=t.d?((i=t.d)<0||i>6)&&(l=!0):null!=t.e?(i=t.e+s,(t.e<0||t.e>6)&&(l=!0)):i=s}r<1||r>Ee(n,s,a)?f(e)._overflowWeeks=!0:null!=l?f(e)._overflowWeekday=!0:(o=He(n,r,i,s,a),e._a[0]=o.year,e._dayOfYear=o.dayOfYear)}(e),null!=e._dayOfYear&&(a=lt(e._a[0],i[0]),(e._dayOfYear>pe(a)||0===e._dayOfYear)&&(f(e)._overflowDayOfYear=!0),n=Fe(a,0,e._dayOfYear),e._a[1]=n.getUTCMonth(),e._a[2]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=o[t]=i[t];for(;t<7;t++)e._a[t]=o[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[3]&&0===e._a[4]&&0===e._a[5]&&0===e._a[6]&&(e._nextDay=!0,e._a[3]=0),e._d=(e._useUTC?Fe:Ce).apply(null,o),s=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[3]=24),e._w&&void 0!==e._w.d&&e._w.d!==s&&(f(e).weekdayMismatch=!0)}}var dt=/^\\s*((?:[+-]\\d{6}|\\d{4})-(?:\\d\\d-\\d\\d|W\\d\\d-\\d|W\\d\\d|\\d\\d\\d|\\d\\d))(?:(T| )(\\d\\d(?::\\d\\d(?::\\d\\d(?:[.,]\\d+)?)?)?)([\\+\\-]\\d\\d(?::?\\d\\d)?|\\s*Z)?)?$/,ct=/^\\s*((?:[+-]\\d{6}|\\d{4})(?:\\d\\d\\d\\d|W\\d\\d\\d|W\\d\\d|\\d\\d\\d|\\d\\d))(?:(T| )(\\d\\d(?:\\d\\d(?:\\d\\d(?:[.,]\\d+)?)?)?)([\\+\\-]\\d\\d(?::?\\d\\d)?|\\s*Z)?)?$/,ht=/Z|[+-]\\d\\d(?::?\\d\\d)?/,ft=[['YYYYYY-MM-DD',/[+-]\\d{6}-\\d\\d-\\d\\d/],['YYYY-MM-DD',/\\d{4}-\\d\\d-\\d\\d/],['GGGG-[W]WW-E',/\\d{4}-W\\d\\d-\\d/],['GGGG-[W]WW',/\\d{4}-W\\d\\d/,!1],['YYYY-DDD',/\\d{4}-\\d{3}/],['YYYY-MM',/\\d{4}-\\d\\d/,!1],['YYYYYYMMDD',/[+-]\\d{10}/],['YYYYMMDD',/\\d{8}/],['GGGG[W]WWE',/\\d{4}W\\d{3}/],['GGGG[W]WW',/\\d{4}W\\d{2}/,!1],['YYYYDDD',/\\d{7}/]],_t=[['HH:mm:ss.SSSS',/\\d\\d:\\d\\d:\\d\\d\\.\\d+/],['HH:mm:ss,SSSS',/\\d\\d:\\d\\d:\\d\\d,\\d+/],['HH:mm:ss',/\\d\\d:\\d\\d:\\d\\d/],['HH:mm',/\\d\\d:\\d\\d/],['HHmmss.SSSS',/\\d\\d\\d\\d\\d\\d\\.\\d+/],['HHmmss,SSSS',/\\d\\d\\d\\d\\d\\d,\\d+/],['HHmmss',/\\d\\d\\d\\d\\d\\d/],['HHmm',/\\d\\d\\d\\d/],['HH',/\\d\\d/]],mt=/^\\/?Date\\((\\-?\\d+)/i;function pt(e){var t,n,r,i,s,a,o=e._i,l=dt.exec(o)||ct.exec(o);if(l){for(f(e).iso=!0,t=0,n=ft.length;t0&&f(e).unusedInput.push(a),o=o.slice(o.indexOf(n)+n.length),u+=n.length),I[s]?(n?f(e).empty=!1:f(e).unusedTokens.push(s),me(s,n,e)):e._strict&&!n&&f(e).unusedTokens.push(s);f(e).charsLeftOver=l-u,o.length>0&&f(e).unusedInput.push(o),e._a[3]<=12&&!0===f(e).bigHour&&e._a[3]>0&&(f(e).bigHour=void 0),f(e).parsedDateParts=e._a.slice(0),f(e).meridiem=e._meridiem,e._a[3]=function(e,t,n){var r;return null==n?t:null!=e.meridiemHour?e.meridiemHour(t,n):null!=e.isPM?((r=e.isPM(n))&&t<12&&(t+=12),r||12!==t||(t=0),t):t}(e._locale,e._a[3],e._meridiem),ut(e),ot(e)}else wt(e);else pt(e)}function Dt(e){var t=e._i,n=e._f;return e._locale=e._locale||at(e._l),null===t||void 0===n&&''===t?m({nullInput:!0}):('string'==typeof t&&(e._i=t=e._locale.preparse(t)),w(t)?new v(ot(t)):(l(t)?e._d=t:i(n)?function(e){var t,n,r,i,s;if(0===e._f.length)return f(e).invalidFormat=!0,void(e._d=new Date(NaN));for(i=0;ithis?this:e:m()}));function Tt(e,t){var n,r;if(1===t.length&&i(t[0])&&(t=t[0]),!t.length)return St();for(n=t[0],r=1;r=0?new Date(e+400,t,n)-126227808e5:new Date(e,t,n).valueOf()}function Kt(e,t,n){return e<100&&e>=0?Date.UTC(e+400,t,n)-126227808e5:Date.UTC(e,t,n)}function en(e,t){G(0,[e,e.length],0,t)}function tn(e,t,n,r,i){var s;return null==e?Ue(this,r,i).year:(t>(s=Ee(e,r,i))&&(t=s),nn.call(this,e,t,n,r,i))}function nn(e,t,n,r,i){var s=He(e,t,n,r,i),a=Fe(s.year,0,s.dayOfYear);return this.year(a.getUTCFullYear()),this.month(a.getUTCMonth()),this.date(a.getUTCDate()),this}G(0,['gg',2],0,(function(){return this.weekYear()%100})),G(0,['GG',2],0,(function(){return this.isoWeekYear()%100})),en('gggg','weekYear'),en('ggggg','weekYear'),en('GGGG','isoWeekYear'),en('GGGGG','isoWeekYear'),C('weekYear','gg'),C('isoWeekYear','GG'),U('weekYear',1),U('isoWeekYear',1),ue('G',ie),ue('g',ie),ue('GG',Q,q),ue('gg',Q,q),ue('GGGG',te,J),ue('gggg',te,J),ue('GGGGG',ne,B),ue('ggggg',ne,B),_e(['gggg','ggggg','GGGG','GGGGG'],(function(e,t,n,r){t[r.substr(0,2)]=D(e)})),_e(['gg','GG'],(function(e,t,n,i){t[i]=r.parseTwoDigitYear(e)})),G('Q',0,'Qo','quarter'),C('quarter','Q'),U('quarter',7),ue('Q',z),fe('Q',(function(e,t){t[1]=3*(D(e)-1)})),G('D',['DD',2],'Do','date'),C('date','D'),U('date',9),ue('D',Q),ue('DD',Q,q),ue('Do',(function(e,t){return e?t._dayOfMonthOrdinalParse||t._ordinalParse:t._dayOfMonthOrdinalParseLenient})),fe(['D','DD'],2),fe('Do',(function(e,t){t[2]=D(e.match(Q)[0])}));var rn=we('Date',!0);G('DDD',['DDDD',3],'DDDo','dayOfYear'),C('dayOfYear','DDD'),U('dayOfYear',4),ue('DDD',ee),ue('DDDD',$),fe(['DDD','DDDD'],(function(e,t,n){n._dayOfYear=D(e)})),G('m',['mm',2],0,'minute'),C('minute','m'),U('minute',14),ue('m',Q),ue('mm',Q,q),fe(['m','mm'],4);var sn=we('Minutes',!1);G('s',['ss',2],0,'second'),C('second','s'),U('second',15),ue('s',Q),ue('ss',Q,q),fe(['s','ss'],5);var an,on=we('Seconds',!1);for(G('S',0,0,(function(){return~~(this.millisecond()/100)})),G(0,['SS',2],0,(function(){return~~(this.millisecond()/10)})),G(0,['SSS',3],0,'millisecond'),G(0,['SSSS',4],0,(function(){return 10*this.millisecond()})),G(0,['SSSSS',5],0,(function(){return 100*this.millisecond()})),G(0,['SSSSSS',6],0,(function(){return 1e3*this.millisecond()})),G(0,['SSSSSSS',7],0,(function(){return 1e4*this.millisecond()})),G(0,['SSSSSSSS',8],0,(function(){return 1e5*this.millisecond()})),G(0,['SSSSSSSSS',9],0,(function(){return 1e6*this.millisecond()})),C('millisecond','ms'),U('millisecond',16),ue('S',ee,z),ue('SS',ee,q),ue('SSS',ee,$),an='SSSS';an.length<=9;an+='S')ue(an,re);function ln(e,t){t[6]=D(1e3*('0.'+e))}for(an='S';an.length<=9;an+='S')fe(an,ln);var un=we('Milliseconds',!1);G('z',0,0,'zoneAbbr'),G('zz',0,0,'zoneName');var dn=v.prototype;function cn(e){return e}dn.add=Zt,dn.calendar=function(e,t){var n=e||St(),i=Nt(n,this).startOf('day'),s=r.calendarFormat(this,i)||'sameElse',a=t&&(x(t[s])?t[s].call(this,n):t[s]);return this.format(a||this.localeData().calendar(s,this,St(n)))},dn.clone=function(){return new v(this)},dn.diff=function(e,t,n){var r,i,s;if(!this.isValid())return NaN;if(!(r=Nt(e,this)).isValid())return NaN;switch(i=6e4*(r.utcOffset()-this.utcOffset()),t=F(t)){case'year':s=qt(this,r)/12;break;case'month':s=qt(this,r);break;case'quarter':s=qt(this,r)/3;break;case'second':s=(this-r)/1e3;break;case'minute':s=(this-r)/6e4;break;case'hour':s=(this-r)/36e5;break;case'day':s=(this-r-i)/864e5;break;case'week':s=(this-r-i)/6048e5;break;default:s=this-r}return n?s:k(s)},dn.endOf=function(e){var t;if(void 0===(e=F(e))||'millisecond'===e||!this.isValid())return this;var n=this._isUTC?Kt:Xt;switch(e){case'year':t=n(this.year()+1,0,1)-1;break;case'quarter':t=n(this.year(),this.month()-this.month()%3+3,1)-1;break;case'month':t=n(this.year(),this.month()+1,1)-1;break;case'week':t=n(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case'isoWeek':t=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case'day':case'date':t=n(this.year(),this.month(),this.date()+1)-1;break;case'hour':t=this._d.valueOf(),t+=36e5-Qt(t+(this._isUTC?0:6e4*this.utcOffset()),36e5)-1;break;case'minute':t=this._d.valueOf(),t+=6e4-Qt(t,6e4)-1;break;case'second':t=this._d.valueOf(),t+=1e3-Qt(t,1e3)-1}return this._d.setTime(t),r.updateOffset(this,!0),this},dn.format=function(e){e||(e=this.isUtc()?r.defaultFormatUtc:r.defaultFormat);var t=j(this,e);return this.localeData().postformat(t)},dn.from=function(e,t){return this.isValid()&&(w(e)&&e.isValid()||St(e).isValid())?At({to:this,from:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},dn.fromNow=function(e){return this.from(St(),e)},dn.to=function(e,t){return this.isValid()&&(w(e)&&e.isValid()||St(e).isValid())?At({from:this,to:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},dn.toNow=function(e){return this.to(St(),e)},dn.get=function(e){return x(this[e=F(e)])?this[e]():this},dn.invalidAt=function(){return f(this).overflow},dn.isAfter=function(e,t){var n=w(e)?e:St(e);return!(!this.isValid()||!n.isValid())&&('millisecond'===(t=F(t)||'millisecond')?this.valueOf()>n.valueOf():n.valueOf()9999?j(n,t?'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]':'YYYYYY-MM-DD[T]HH:mm:ss.SSSZ'):x(Date.prototype.toISOString)?t?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace('Z',j(n,'Z')):j(n,t?'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]':'YYYY-MM-DD[T]HH:mm:ss.SSSZ')},dn.inspect=function(){if(!this.isValid())return'moment.invalid(/* '+this._i+' */)';var e='moment',t='';this.isLocal()||(e=0===this.utcOffset()?'moment.utc':'moment.parseZone',t='Z');var n='['+e+'(\"]',r=0<=this.year()&&this.year()<=9999?'YYYY':'YYYYYY',i=t+'[\")]';return this.format(n+r+'-MM-DD[T]HH:mm:ss.SSS'+i)},dn.toJSON=function(){return this.isValid()?this.toISOString():null},dn.toString=function(){return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ')},dn.unix=function(){return Math.floor(this.valueOf()/1e3)},dn.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},dn.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},dn.year=ve,dn.isLeapYear=function(){return ye(this.year())},dn.weekYear=function(e){return tn.call(this,e,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)},dn.isoWeekYear=function(e){return tn.call(this,e,this.isoWeek(),this.isoWeekday(),1,4)},dn.quarter=dn.quarters=function(e){return null==e?Math.ceil((this.month()+1)/3):this.month(3*(e-1)+this.month()%3)},dn.month=xe,dn.daysInMonth=function(){return Me(this.year(),this.month())},dn.week=dn.weeks=function(e){var t=this.localeData().week(this);return null==e?t:this.add(7*(e-t),'d')},dn.isoWeek=dn.isoWeeks=function(e){var t=Ue(this,1,4).week;return null==e?t:this.add(7*(e-t),'d')},dn.weeksInYear=function(){var e=this.localeData()._week;return Ee(this.year(),e.dow,e.doy)},dn.isoWeeksInYear=function(){return Ee(this.year(),1,4)},dn.date=rn,dn.day=dn.days=function(e){if(!this.isValid())return null!=e?this:NaN;var t=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=e?(e=function(e,t){return'string'!=typeof e?e:isNaN(e)?'number'==typeof(e=t.weekdaysParse(e))?e:null:parseInt(e,10)}(e,this.localeData()),this.add(e-t,'d')):t},dn.weekday=function(e){if(!this.isValid())return null!=e?this:NaN;var t=(this.day()+7-this.localeData()._week.dow)%7;return null==e?t:this.add(e-t,'d')},dn.isoWeekday=function(e){if(!this.isValid())return null!=e?this:NaN;if(null!=e){var t=function(e,t){return'string'==typeof e?t.weekdaysParse(e)%7||7:isNaN(e)?null:e}(e,this.localeData());return this.day(this.day()%7?t:t-7)}return this.day()||7},dn.dayOfYear=function(e){var t=Math.round((this.clone().startOf('day')-this.clone().startOf('year'))/864e5)+1;return null==e?t:this.add(e-t,'d')},dn.hour=dn.hours=Xe,dn.minute=dn.minutes=sn,dn.second=dn.seconds=on,dn.millisecond=dn.milliseconds=un,dn.utcOffset=function(e,t,n){var i,s=this._offset||0;if(!this.isValid())return null!=e?this:NaN;if(null!=e){if('string'==typeof e){if(null===(e=Ft(ae,e)))return this}else Math.abs(e)<16&&!n&&(e*=60);return!this._isUTC&&t&&(i=Ht(this)),this._offset=e,this._isUTC=!0,null!=i&&this.add(i,'m'),s!==e&&(!t||this._changeInProgress?jt(this,At(e-s,'m'),1,!1):this._changeInProgress||(this._changeInProgress=!0,r.updateOffset(this,!0),this._changeInProgress=null)),this}return this._isUTC?s:Ht(this)},dn.utc=function(e){return this.utcOffset(0,e)},dn.local=function(e){return this._isUTC&&(this.utcOffset(0,e),this._isUTC=!1,e&&this.subtract(Ht(this),'m')),this},dn.parseZone=function(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if('string'==typeof this._i){var e=Ft(se,this._i);null!=e?this.utcOffset(e):this.utcOffset(0,!0)}return this},dn.hasAlignedHourOffset=function(e){return!!this.isValid()&&(e=e?St(e).utcOffset():0,(this.utcOffset()-e)%60==0)},dn.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},dn.isLocal=function(){return!!this.isValid()&&!this._isUTC},dn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},dn.isUtc=Ut,dn.isUTC=Ut,dn.zoneAbbr=function(){return this._isUTC?'UTC':''},dn.zoneName=function(){return this._isUTC?'Coordinated Universal Time':''},dn.dates=b('dates accessor is deprecated. Use date instead.',rn),dn.months=b('months accessor is deprecated. Use month instead',xe),dn.years=b('years accessor is deprecated. Use year instead',ve),dn.zone=b('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/',(function(e,t){return null!=e?('string'!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()})),dn.isDSTShifted=b('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information',(function(){if(!a(this._isDSTShifted))return this._isDSTShifted;var e={};if(y(e,this),(e=Dt(e))._a){var t=e._isUTC?h(e._a):St(e._a);this._isDSTShifted=this.isValid()&&M(e._a,t.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}));var hn=W.prototype;function fn(e,t,n,r){var i=at(),s=h().set(r,t);return i[n](s,e)}function _n(e,t,n){if(o(e)&&(t=e,e=void 0),e=e||'',null!=t)return fn(e,t,n,'month');var r,i=[];for(r=0;r<12;r++)i[r]=fn(e,r,n,'month');return i}function mn(e,t,n,r){'boolean'==typeof e?(o(t)&&(n=t,t=void 0),t=t||''):(n=t=e,e=!1,o(t)&&(n=t,t=void 0),t=t||'');var i,s=at(),a=e?s._week.dow:0;if(null!=n)return fn(t,(n+a)%7,r,'day');var l=[];for(i=0;i<7;i++)l[i]=fn(t,(i+a)%7,r,'day');return l}hn.calendar=function(e,t,n){var r=this._calendar[e]||this._calendar.sameElse;return x(r)?r.call(t,n):r},hn.longDateFormat=function(e){var t=this._longDateFormat[e],n=this._longDateFormat[e.toUpperCase()];return t||!n?t:(this._longDateFormat[e]=n.replace(/MMMM|MM|DD|dddd/g,(function(e){return e.slice(1)})),this._longDateFormat[e])},hn.invalidDate=function(){return this._invalidDate},hn.ordinal=function(e){return this._ordinal.replace('%d',e)},hn.preparse=cn,hn.postformat=cn,hn.relativeTime=function(e,t,n,r){var i=this._relativeTime[n];return x(i)?i(e,t,n,r):i.replace(/%d/i,e)},hn.pastFuture=function(e,t){var n=this._relativeTime[e>0?'future':'past'];return x(n)?n(t):n.replace(/%s/i,t)},hn.set=function(e){var t,n;for(n in e)x(t=e[n])?this[n]=t:this['_'+n]=t;this._config=e,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+'|'+/\\d{1,2}/.source)},hn.months=function(e,t){return e?i(this._months)?this._months[e.month()]:this._months[(this._months.isFormat||Se).test(t)?'format':'standalone'][e.month()]:i(this._months)?this._months:this._months.standalone},hn.monthsShort=function(e,t){return e?i(this._monthsShort)?this._monthsShort[e.month()]:this._monthsShort[Se.test(t)?'format':'standalone'][e.month()]:i(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},hn.monthsParse=function(e,t,n){var r,i,s;if(this._monthsParseExact)return Te.call(this,e,t,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),r=0;r<12;r++){if(i=h([2e3,r]),n&&!this._longMonthsParse[r]&&(this._longMonthsParse[r]=new RegExp('^'+this.months(i,'').replace('.','')+'$','i'),this._shortMonthsParse[r]=new RegExp('^'+this.monthsShort(i,'').replace('.','')+'$','i')),n||this._monthsParse[r]||(s='^'+this.months(i,'')+'|^'+this.monthsShort(i,''),this._monthsParse[r]=new RegExp(s.replace('.',''),'i')),n&&'MMMM'===t&&this._longMonthsParse[r].test(e))return r;if(n&&'MMM'===t&&this._shortMonthsParse[r].test(e))return r;if(!n&&this._monthsParse[r].test(e))return r}},hn.monthsRegex=function(e){return this._monthsParseExact?(d(this,'_monthsRegex')||Re.call(this),e?this._monthsStrictRegex:this._monthsRegex):(d(this,'_monthsRegex')||(this._monthsRegex=We),this._monthsStrictRegex&&e?this._monthsStrictRegex:this._monthsRegex)},hn.monthsShortRegex=function(e){return this._monthsParseExact?(d(this,'_monthsRegex')||Re.call(this),e?this._monthsShortStrictRegex:this._monthsShortRegex):(d(this,'_monthsShortRegex')||(this._monthsShortRegex=Pe),this._monthsShortStrictRegex&&e?this._monthsShortStrictRegex:this._monthsShortRegex)},hn.week=function(e){return Ue(e,this._week.dow,this._week.doy).week},hn.firstDayOfYear=function(){return this._week.doy},hn.firstDayOfWeek=function(){return this._week.dow},hn.weekdays=function(e,t){var n=i(this._weekdays)?this._weekdays:this._weekdays[e&&!0!==e&&this._weekdays.isFormat.test(t)?'format':'standalone'];return!0===e?Le(n,this._week.dow):e?n[e.day()]:n},hn.weekdaysMin=function(e){return!0===e?Le(this._weekdaysMin,this._week.dow):e?this._weekdaysMin[e.day()]:this._weekdaysMin},hn.weekdaysShort=function(e){return!0===e?Le(this._weekdaysShort,this._week.dow):e?this._weekdaysShort[e.day()]:this._weekdaysShort},hn.weekdaysParse=function(e,t,n){var r,i,s;if(this._weekdaysParseExact)return Ge.call(this,e,t,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),r=0;r<7;r++){if(i=h([2e3,1]).day(r),n&&!this._fullWeekdaysParse[r]&&(this._fullWeekdaysParse[r]=new RegExp('^'+this.weekdays(i,'').replace('.','\\\\.?')+'$','i'),this._shortWeekdaysParse[r]=new RegExp('^'+this.weekdaysShort(i,'').replace('.','\\\\.?')+'$','i'),this._minWeekdaysParse[r]=new RegExp('^'+this.weekdaysMin(i,'').replace('.','\\\\.?')+'$','i')),this._weekdaysParse[r]||(s='^'+this.weekdays(i,'')+'|^'+this.weekdaysShort(i,'')+'|^'+this.weekdaysMin(i,''),this._weekdaysParse[r]=new RegExp(s.replace('.',''),'i')),n&&'dddd'===t&&this._fullWeekdaysParse[r].test(e))return r;if(n&&'ddd'===t&&this._shortWeekdaysParse[r].test(e))return r;if(n&&'dd'===t&&this._minWeekdaysParse[r].test(e))return r;if(!n&&this._weekdaysParse[r].test(e))return r}},hn.weekdaysRegex=function(e){return this._weekdaysParseExact?(d(this,'_weekdaysRegex')||qe.call(this),e?this._weekdaysStrictRegex:this._weekdaysRegex):(d(this,'_weekdaysRegex')||(this._weekdaysRegex=je),this._weekdaysStrictRegex&&e?this._weekdaysStrictRegex:this._weekdaysRegex)},hn.weekdaysShortRegex=function(e){return this._weekdaysParseExact?(d(this,'_weekdaysRegex')||qe.call(this),e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(d(this,'_weekdaysShortRegex')||(this._weekdaysShortRegex=Ze),this._weekdaysShortStrictRegex&&e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},hn.weekdaysMinRegex=function(e){return this._weekdaysParseExact?(d(this,'_weekdaysRegex')||qe.call(this),e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(d(this,'_weekdaysMinRegex')||(this._weekdaysMinRegex=ze),this._weekdaysMinStrictRegex&&e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},hn.isPM=function(e){return'p'===(e+'').toLowerCase().charAt(0)},hn.meridiem=function(e,t,n){return e>11?n?'pm':'PM':n?'am':'AM'},it('en',{dayOfMonthOrdinalParse:/\\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10;return e+(1===D(e%100/10)?'th':1===t?'st':2===t?'nd':3===t?'rd':'th')}}),r.lang=b('moment.lang is deprecated. Use moment.locale instead.',it),r.langData=b('moment.langData is deprecated. Use moment.localeData instead.',at);var pn=Math.abs;function yn(e,t,n,r){var i=At(t,n);return e._milliseconds+=r*i._milliseconds,e._days+=r*i._days,e._months+=r*i._months,e._bubble()}function gn(e){return e<0?Math.floor(e):Math.ceil(e)}function vn(e){return 4800*e/146097}function wn(e){return 146097*e/4800}function kn(e){return function(){return this.as(e)}}var Dn=kn('ms'),Mn=kn('s'),Sn=kn('m'),bn=kn('h'),Yn=kn('d'),Tn=kn('w'),On=kn('M'),xn=kn('Q'),Pn=kn('y');function Wn(e){return function(){return this.isValid()?this._data[e]:NaN}}var Rn=Wn('milliseconds'),Cn=Wn('seconds'),Fn=Wn('minutes'),Nn=Wn('hours'),Hn=Wn('days'),Un=Wn('months'),En=Wn('years'),Ln=Math.round,An={ss:44,s:45,m:45,h:22,d:26,M:11};function Vn(e,t,n,r,i){return i.relativeTime(t||1,!!n,e,r)}var In=Math.abs;function Gn(e){return(e>0)-(e<0)||+e}function jn(){if(!this.isValid())return this.localeData().invalidDate();var e,t,n=In(this._milliseconds)/1e3,r=In(this._days),i=In(this._months);e=k(n/60),t=k(e/60),n%=60,e%=60;var s=k(i/12),a=i%=12,o=r,l=t,u=e,d=n?n.toFixed(3).replace(/\\.?0+$/,''):'',c=this.asSeconds();if(!c)return'P0D';var h=c<0?'-':'',f=Gn(this._months)!==Gn(c)?'-':'',_=Gn(this._days)!==Gn(c)?'-':'',m=Gn(this._milliseconds)!==Gn(c)?'-':'';return h+'P'+(s?f+s+'Y':'')+(a?f+a+'M':'')+(o?_+o+'D':'')+(l||u||d?'T':'')+(l?m+l+'H':'')+(u?m+u+'M':'')+(d?m+d+'S':'')}var Zn=xt.prototype;return Zn.isValid=function(){return this._isValid},Zn.abs=function(){var e=this._data;return this._milliseconds=pn(this._milliseconds),this._days=pn(this._days),this._months=pn(this._months),e.milliseconds=pn(e.milliseconds),e.seconds=pn(e.seconds),e.minutes=pn(e.minutes),e.hours=pn(e.hours),e.months=pn(e.months),e.years=pn(e.years),this},Zn.add=function(e,t){return yn(this,e,t,1)},Zn.subtract=function(e,t){return yn(this,e,t,-1)},Zn.as=function(e){if(!this.isValid())return NaN;var t,n,r=this._milliseconds;if('month'===(e=F(e))||'quarter'===e||'year'===e)switch(t=this._days+r/864e5,n=this._months+vn(t),e){case'month':return n;case'quarter':return n/3;case'year':return n/12}else switch(t=this._days+Math.round(wn(this._months)),e){case'week':return t/7+r/6048e5;case'day':return t+r/864e5;case'hour':return 24*t+r/36e5;case'minute':return 1440*t+r/6e4;case'second':return 86400*t+r/1e3;case'millisecond':return Math.floor(864e5*t)+r;default:throw new Error('Unknown unit '+e)}},Zn.asMilliseconds=Dn,Zn.asSeconds=Mn,Zn.asMinutes=Sn,Zn.asHours=bn,Zn.asDays=Yn,Zn.asWeeks=Tn,Zn.asMonths=On,Zn.asQuarters=xn,Zn.asYears=Pn,Zn.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*D(this._months/12):NaN},Zn._bubble=function(){var e,t,n,r,i,s=this._milliseconds,a=this._days,o=this._months,l=this._data;return s>=0&&a>=0&&o>=0||s<=0&&a<=0&&o<=0||(s+=864e5*gn(wn(o)+a),a=0,o=0),l.milliseconds=s%1e3,e=k(s/1e3),l.seconds=e%60,t=k(e/60),l.minutes=t%60,n=k(t/60),l.hours=n%24,a+=k(n/24),i=k(vn(a)),o+=i,a-=gn(wn(i)),r=k(o/12),o%=12,l.days=a,l.months=o,l.years=r,this},Zn.clone=function(){return At(this)},Zn.get=function(e){return e=F(e),this.isValid()?this[e+'s']():NaN},Zn.milliseconds=Rn,Zn.seconds=Cn,Zn.minutes=Fn,Zn.hours=Nn,Zn.days=Hn,Zn.weeks=function(){return k(this.days()/7)},Zn.months=Un,Zn.years=En,Zn.humanize=function(e){if(!this.isValid())return this.localeData().invalidDate();var t=this.localeData(),n=function(e,t,n){var r=At(e).abs(),i=Ln(r.as('s')),s=Ln(r.as('m')),a=Ln(r.as('h')),o=Ln(r.as('d')),l=Ln(r.as('M')),u=Ln(r.as('y')),d=i<=An.ss&&['s',i]||i0,d[4]=n,Vn.apply(null,d)}(this,!e,t);return e&&(n=t.pastFuture(+this,n)),t.postformat(n)},Zn.toISOString=jn,Zn.toString=jn,Zn.toJSON=jn,Zn.locale=$t,Zn.localeData=Bt,Zn.toIsoString=b('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)',jn),Zn.lang=Jt,G('X',0,0,'unix'),G('x',0,0,'valueOf'),ue('x',ie),ue('X',/[+-]?\\d+(\\.\\d{1,3})?/),fe('X',(function(e,t,n){n._d=new Date(1e3*parseFloat(e,10))})),fe('x',(function(e,t,n){n._d=new Date(D(e))})),r.version='2.24.0',t=St,r.fn=dn,r.min=function(){var e=[].slice.call(arguments,0);return Tt('isBefore',e)},r.max=function(){var e=[].slice.call(arguments,0);return Tt('isAfter',e)},r.now=function(){return Date.now?Date.now():+new Date},r.utc=h,r.unix=function(e){return St(1e3*e)},r.months=function(e,t){return _n(e,t,'months')},r.isDate=l,r.locale=it,r.invalid=m,r.duration=At,r.isMoment=w,r.weekdays=function(e,t,n){return mn(e,t,n,'weekdays')},r.parseZone=function(){return St.apply(null,arguments).parseZone()},r.localeData=at,r.isDuration=Pt,r.monthsShort=function(e,t){return _n(e,t,'monthsShort')},r.weekdaysMin=function(e,t,n){return mn(e,t,n,'weekdaysMin')},r.defineLocale=st,r.updateLocale=function(e,t){if(null!=t){var n,r,i=Ke;null!=(r=rt(e))&&(i=r._config),t=P(i,t),(n=new W(t)).parentLocale=et[e],et[e]=n,it(e)}else null!=et[e]&&(null!=et[e].parentLocale?et[e]=et[e].parentLocale:null!=et[e]&&delete et[e]);return et[e]},r.locales=function(){return Y(et)},r.weekdaysShort=function(e,t,n){return mn(e,t,n,'weekdaysShort')},r.normalizeUnits=F,r.relativeTimeRounding=function(e){return void 0===e?Ln:'function'==typeof e&&(Ln=e,!0)},r.relativeTimeThreshold=function(e,t){return void 0!==An[e]&&(void 0===t?An[e]:(An[e]=t,'s'===e&&(An.ss=t-1),!0))},r.calendarFormat=function(e,t){var n=e.diff(t,'days',!0);return n<-6?'sameElse':n<-1?'lastWeek':n<0?'lastDay':n<1?'sameDay':n<2?'nextDay':n<7?'nextWeek':'sameElse'},r.prototype=dn,r.HTML5_FMT={DATETIME_LOCAL:'YYYY-MM-DDTHH:mm',DATETIME_LOCAL_SECONDS:'YYYY-MM-DDTHH:mm:ss',DATETIME_LOCAL_MS:'YYYY-MM-DDTHH:mm:ss.SSS',DATE:'YYYY-MM-DD',TIME:'HH:mm',TIME_SECONDS:'HH:mm:ss',TIME_MS:'HH:mm:ss.SSS',WEEK:'GGGG-[W]WW',MONTH:'YYYY-MM'},r}()}).call(this,n(3)(e))},function(e,t,n){var r=n(2),i=n(5);e.exports=i(r,contact,reports)},function(e,t,n){const r=n(0),i=n(4),{today:s,MAX_DAYS_IN_PREGNANCY:a,isHighRiskPregnancy:o,getNewestReport:l,getSubsequentPregnancyFollowUps:u,getSubsequentDeliveries:d,isAlive:c,isReadyForNewPregnancy:h,isReadyForDelivery:f,isActivePregnancy:_,countANCFacilityVisits:m,getAllRiskFactors:p,getLatestDangerSignsForPregnancy:y,getNextANCVisitDate:g,getMostRecentLMPDateForPregnancy:v,getMostRecentEDDForPregnancy:w,getDeliveryDate:k,getFormArraySubmittedInWindow:D,getRecentANCVisitWithEvent:M,getAllRiskFactorExtra:S,getField:b}=i,Y=contact,T=lineage,O=reports,x={alive:c(Y),muted:!1,show_pregnancy_form:h(Y,O),show_delivery_form:f(Y,O)},P=[{appliesToType:'person',label:'patient_id',value:Y.patient_id,width:4},{appliesToType:'person',label:'contact.age',value:Y.date_of_birth,width:4,filter:'age'},{appliesToType:'person',label:'contact.sex',value:'contact.sex.'+Y.sex,translate:!0,width:4},{appliesToType:'person',label:'person.field.phone',value:Y.phone,width:4},{appliesToType:'person',label:'person.field.alternate_phone',value:Y.phone_alternate,width:4},{appliesToType:'person',label:'External ID',value:Y.external_id,width:4},{appliesToType:'person',label:'contact.parent',value:T,filter:'lineage'},{appliesToType:'!person',label:'contact',value:Y.contact&&Y.contact.name,width:4},{appliesToType:'!person',label:'contact.phone',value:Y.contact&&Y.contact.phone,width:4},{appliesToType:'!person',label:'External ID',value:Y.external_id,width:4},{appliesToType:'!person',appliesIf:function(){return Y.parent&&T[0]},label:'contact.parent',value:T,filter:'lineage'},{appliesToType:'person',label:'contact.notes',value:Y.notes,width:12},{appliesToType:'!person',label:'contact.notes',value:Y.notes,width:12}];Y.short_name&&P.unshift({appliesToType:'person',label:'contact.short_name',value:Y.short_name,width:4});const W=[{label:'contact.profile.pregnancy.active',appliesToType:'report',appliesIf:function(e){return _(Y,O,e)},fields:function(e){const t=[],n=p(O,e),i=S(O,e),a=y(O,e),d=o(O,e),c=l(O,['pregnancy','pregnancy_home_visit']),h=r(c.reported_date),f=v(O,e),_=w(O,e),k=g(O,e),D=f?s.diff(f,'weeks'):null;let Y=b(e,'lmp_approx'),T=e.reported_date;u(O,e).forEach((function(e){e.reported_date>T&&'yes'===b(e,'lmp_updated')&&(T=e.reported_date,b(e,'lmp_method_approx')&&(Y=b(e,'lmp_method_approx')))}));const x=M(O,e,'migrated'),P=M(O,e,'refused'),W=x||P;if(W){const e='clear_all'===b(W,'pregnancy_ended.clear_option');t.push({label:'contact.profile.change_care',value:x?'Migrated out of area':'Refusing care',width:6},{label:'contact.profile.tasks_on_off',value:e?'Off':'On',width:6})}if(t.push({label:'Weeks Pregnant',value:D||0===D?{number:D,approximate:'yes'===Y}:'contact.profile.value.unknown',translate:!D&&0!==D,filter:D||0===D?'weeksPregnant':'',width:6},{label:'contact.profile.edd',value:_?_.valueOf():'contact.profile.value.unknown',translate:!_,filter:_?'simpleDate':'',width:6}),d){let e='';e=!n&&i?i.join(', '):n.length>1||n&&i?'contact.profile.risk.multiple':'contact.profile.danger_sign.'+n[0],t.push({label:'contact.profile.risk.high',value:e,translate:!0,icon:'icon-risk',width:6})}return a.length>0&&t.push({label:'contact.profile.danger_signs.current',value:a.length>1?'contact.profile.danger_sign.multiple':'contact.profile.danger_sign.'+a[0],translate:!0,width:6}),t.push({label:'contact.profile.visit',value:'contact.profile.visits.of',context:{count:m(O,e),total:8},translate:!0,width:6},{label:'contact.profile.last_visited',value:h.valueOf(),filter:'relativeDay',width:6}),k&&k.isSameOrAfter(s)&&t.push({label:'contact.profile.anc.next',value:k.valueOf(),filter:'simpleDate',width:6}),t},modifyContext:function(e,t){let n=b(t,'lmp_date_8601'),r=b(t,'lmp_method_approx'),i=b(t,'hiv_status_known'),s=b(t,'deworming_med_received'),a=b(t,'tt_received');const o=p(O,t),l=S(O,t);let d=b(t,'t_pregnancy_follow_up_date');u(O,t).forEach((function(e){'yes'===b(e,'lmp_updated')&&(n=b(e,'lmp_date_8601'),r=b(e,'lmp_method_approx')),i=b(e,'hiv_status_known'),s=b(e,'deworming_med_received'),a=b(e,'tt_received'),'yes'===b(e,'t_pregnancy_follow_up')&&(d=b(e,'t_pregnancy_follow_up_date'))})),e.lmp_date_8601=n,e.lmp_method_approx=r,e.is_active_pregnancy=!0,e.deworming_med_received=s,e.hiv_tested_past=i,e.tt_received_past=a,e.risk_factor_codes=o.join(' '),e.risk_factor_extra=l.join('; '),e.pregnancy_follow_up_date_recent=d,e.pregnancy_uuid=t._id}},{label:'contact.profile.death.title',appliesToType:'person',appliesIf:function(){return!c(Y)},fields:function(){const e=[];let t,n;const r=l(O,['death_report']);if(r){const e=b(r,'death_details');e&&(t=e.date_of_death,n=e.place_of_death)}else Y.date_of_death&&(t=Y.date_of_death);return e.push({label:'contact.profile.death.date',value:t||'contact.profile.value.unknown',filter:t?'simpleDate':'',translate:!t,width:6},{label:'contact.profile.death.place',value:n||'contact.profile.value.unknown',translate:!0,width:6}),e}},{label:'contact.profile.pregnancy.past',appliesToType:'report',appliesIf:function(e){if('person'!==Y.type)return!1;if('delivery'===e.form)return!0;if('pregnancy'===e.form){if(M(O,e,'abortion')||M(O,e,'miscarriage'))return!0;const t=v(O,e);return t&&s.isSameOrAfter(t.clone().add(42,'weeks'))&&0===d(O,e,a).length}return!1},fields:function(e){const t=[];let n,i,l='',u=0,c=0,h=0;if('delivery'===e.form){const s=r(e.reported_date);n=D(O,['pregnancy'],s.clone().subtract(a,'days').toDate(),s.toDate())[0],b(e,'delivery_outcome')&&(i=k(e),l=b(e,'delivery_outcome.delivery_place'),u=b(e,'delivery_outcome.babies_delivered_num'),c=b(e,'delivery_outcome.babies_deceased_num'),t.push({label:'contact.profile.delivery_date',value:i?i.valueOf():'',filter:'simpleDate',width:6},{label:'contact.profile.delivery_place',value:l,translate:!0,width:6},{label:'contact.profile.delivered_babies',value:u,width:6}))}else if('pregnancy'===e.form){n=e;const o=v(O,n),l=M(O,n,'abortion'),u=M(O,n,'miscarriage');if(l||u){let e='',n=r(0),i=0;l?(e='abortion',n=r(b(l,'pregnancy_ended.abortion_date'))):(e='miscarriage',n=r(b(u,'pregnancy_ended.miscarriage_date'))),i=n.diff(o,'weeks'),t.push({label:'contact.profile.pregnancy.end_early',value:e,translate:!0,width:6},{label:'contact.profile.pregnancy.end_date',value:n.valueOf(),filter:'simpleDate',width:6},{label:'contact.profile.pregnancy.end_weeks',value:i>0?i:'contact.profile.value.unknown',translate:i<=0,width:6})}else o&&s.isSameOrAfter(o.clone().add(42,'weeks'))&&0===d(O,e,a).length&&(i=w(O,e),t.push({label:'contact.profile.delivery_date',value:i?i.valueOf():'contact.profile.value.unknown',filter:'simpleDate',translate:!i,width:6}))}if(c>0&&b(e,'baby_death')){t.push({label:'contact.profile.deceased_babies',value:c,width:6});let n=b(e,'baby_death.baby_death_repeat');n||(n=[]);let r=0;n.forEach((function(e){r>0&&t.push({label:'',value:'',width:6}),t.push({label:'contact.profile.newborn.death_date',value:e.baby_death_date,filter:'simpleDate',width:6},{label:'contact.profile.newborn.death_place',value:e.baby_death_place,translate:!0,width:6},{label:'contact.profile.delivery.stillbirthQ',value:e.stillbirth,translate:!0,width:6}),r++,r===n.length&&t.push({label:'',value:'',width:6})}))}if(n){h=m(O,n),t.push({label:'contact.profile.anc_visit',value:h,width:3});if(o(O,n)){let e='';const r=p(O,n),i=S(O,n);e=!r&&i?i.join(', '):r.length>1||r&&i?'contact.profile.risk.multiple':'contact.profile.danger_sign.'+r[0],t.push({label:'contact.profile.risk.high',value:e,translate:!0,icon:'icon-risk',width:6})}}return t}}];e.exports={context:x,cards:W,fields:P}},function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,'loaded',{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,'id',{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},function(e,t,n){const r=n(0),i=r().startOf('day'),s=['pregnancy'],a=['pregnancy_home_visit'],o=['delivery'],l=['pregnancy','pregnancy_home_visit','pregnancy_danger_sign','pregannacy_danger_sign_follow_up'],u=(e,t)=>['fields',...(t||'').split('.')].reduce((e,t)=>{if(void 0!==e)return e[t]},e);function d(e,t,n,r){return e.filter((function(e){return t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r}))}function c(e,t){let n;return e.forEach((function(e){(function(e){return!!(e.form&&e.fields&&e.reported_date)})(e)&&t.includes(e.form)&&(!n||e.reported_date>n.reported_date)&&(n=e)})),n}function h(e){return k(e)&&u(e,'lmp_date_8601')&&r(u(e,'lmp_date_8601'))}function f(e,t){let n=h(t),i=t.reported_date;return T(e,t).forEach((function(e){const t=D(s=e)&&u(s,'lmp_date_8601')&&r(u(s,'lmp_date_8601'));var s;e.reported_date>i&&'yes'===u(e,'lmp_updated')&&(i=e.reported_date,n=t)})),n}function _(e,t){const n=f(e,t);if(n)return n.clone().add(280,'days')}function m(e){return M(e)&&u(e,'delivery_outcome.delivery_date')&&r(u(e,'delivery_outcome.delivery_date'))}function p(e){const t=[];if('yes'===u(e,'t_danger_signs_referral_follow_up')){const n=u(e,'danger_signs');if(n)for(const e in n)'yes'===n[e]&&'r_danger_sign_present'!==e&&t.push(e)}return t}function y(e,t){const n=function(e){const t=[];if(!k(e))return[];if('yes'===u(e,'risk_factors.r_risk_factor_present')){'yes'===u(e,'risk_factors.risk_factors_history.first_pregnancy')&&t.push('first_pregnancy'),'yes'===u(e,'risk_factors.risk_factors_history.previous_miscarriage')&&t.push('previous_miscarriage');const n=u(e,'risk_factors.risk_factors_present.primary_condition'),r=u(e,'risk_factors.risk_factors_present.secondary_condition');n?t.push(...n.split(' ')):r&&t.push(...r.split(' '))}return t}(t);return T(e,t).forEach((function(e){n.push(...function(e){const t=[];if(!D(e))return[];if('yes'===u(e,'anc_visits_hf.risk_factors.r_risk_factor_present')){const n=u(e,'anc_visits_hf.risk_factors.new_risks');n&&t.push(...n.split(' '))}return t}(e))})),n}function g(e){let t;return e&&k(e)?t=u(e,'risk_factors.risk_factors_present.additional_risk'):e&&D(e)&&(t=u(e,'anc_visits_hf.risk_factors.additional_risk')),t}function v(e,t){const n=[],r=g(t);r&&n.push(r);return T(e,t).forEach((function(e){const t=g(e);t&&n.push(t)})),n}function w(e){return e&&!e.date_of_death}function k(e){return e&&s.includes(e.form)}function D(e){return e&&a.includes(e.form)}function M(e){return e&&o.includes(e.form)}function S(e,t,n){if('person'!==e.type||!w(e)||!k(n))return!1;const r=(f(t,n)||n.reported_date)>i.clone().subtract(294,'day'),s=Y(t,n,42).length>0,a=function(e,t){return e.filter((function(e){return k(e)&&e.reported_date>t.reported_date}))}(t,n).length>0;return r&&!s&&!a&&!b(t,n,'abortion')&&!b(t,n,'miscarriage')}function b(e,t,n){const r=c(T(e,t),a);if(r&&u(r,'pregnancy_summary.visit_option')===n)return r}function Y(e,t,n){return e.filter((function(e){return M(e)&&e.reported_date>t.reported_date&&(!n||e.reported_date>=i.clone().subtract(n,'days'))}))}function T(e,t){let n=h(t);n||(n=r(t.reported_date));return e.filter((function(e){return D(e)&&e.reported_date>t.reported_date&&r(e.reported_date)S(e))},isActivePregnancy:S,countANCFacilityVisits:function(e,t){let n=0;const r=T(e,t);return u(t,'anc_visits_hf.anc_visits_hf_past')&&!isNaN(u(t,'anc_visits_hf.anc_visits_hf_past.visited_hf_count'))&&(n+=parseInt(u(t,'anc_visits_hf.anc_visits_hf_past.visited_hf_count'))),n+=r.reduce((function(e,t){const n=u(t,'anc_visits_hf.anc_visits_hf_past');return n?(e+='yes'===n.last_visit_attended&&1,isNaN(n.visited_hf_count)?e:e+('yes'===n.report_other_visits&&parseInt(n.visited_hf_count))):0}),0),n},knowsHIVStatusInPast3Months:function(e){let t=!1;return d(e,s,i.clone().subtract(3,'months'),i).forEach((function(e){'yes'===u(e,'pregnancy_new_or_current.hiv_status.hiv_status_know')&&(t=!0)})),t},getAllRiskFactors:y,getAllRiskFactorExtra:v,getDangerSignCodes:p,getLatestDangerSignsForPregnancy:function(e,t){if(!t)return[];let n=f(e,t);n||(n=r(t.reported_date));const i=d(e,l,n.toDate(),n.clone().add(294,'days').toDate()),s=[];i.forEach(e=>{D(e)?'yes'===u(e,'pregnancy_summary.visit_option')&&s.push(e):s.push(e)});const a=c(s,l);return a?p(a):[]},getNextANCVisitDate:function(e,t){let n=u(t,'t_pregnancy_follow_up_date'),i=t.reported_date;return T(e,t).forEach((function(e){e.reported_date>i&&u(e,'t_pregnancy_follow_up_date')&&(i=e.reported_date,n=u(e,'t_pregnancy_follow_up_date'))})),r(n)},isReadyForNewPregnancy:function(e,t){if('person'!==e.type)return!1;const n=c(t,s),a=c(t,o);if(!n&&!a)return!0;if(n){if(!a||a.reported_daten.reported_date))return m(a)0&&!r.includes('!'+l))&&(!e.appliesIf||e.appliesIf()))return delete e.appliesToType,delete e.appliesIf,!0}))};return o.forEach((function(e){var t,s,o,d,c=n(e.appliesToType);if(c.includes('report')&&c.length>1)throw new Error('You cannot set appliesToType to an array which includes the type \\'report\\' and another type.');if(c.includes('report'))for(t=0;t0)return;(o=i(e,a))&&u.cards.push(o)}})),u.context=a,u}}])})); return ContactSummary;", "tasks": { - "rules": "define Target { _id: null, contact: null, deleted: null, type: null, pass: null, date: null, groupBy: null }\ndefine Contact { contact: null, reports: null, tasks: null }\ndefine Task { _id: null, deleted: null, doc: null, contact: null, icon: null, date: null, readyStart: null, readyEnd: null, title: null, fields: null, resolved: null, priority: null, priorityLabel: null, reports: null, actions: null }\nrule GenerateEvents {\n when { c: Contact } then { !function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){'undefined'!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:'Module'}),Object.defineProperty(e,'__esModule',{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&'object'==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,'default',{enumerable:!0,value:e}),2&t&&'string'!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,'a',t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p='',n(n.s=2)}([function(e,t){const n=d(Date.now()),r=['pregnancy'],i=['delivery'],o=['pregnancy_home_visit'],a=['pregnancy','pregnancy_home_visit','pregnancy_facility_visit_reminder','pregnancy_danger_sign','pregnancy_danger_sign_follow_up','delivery'];const c=(e,t)=>['fields',...(t||'').split('.')].reduce((e,t)=>{if(void 0!==e)return e[t]},e);function s(e,t){let n;return e.forEach((function(e){t.includes(e.form)&&!e.deleted&&(!n||e.reported_date>n.reported_date)&&(n=e)})),n}function p(e){if(!e)return new Date;const t=e.split(/\\D/),n=new Date(t[0],t[1]-1,t[2]);return function(e){return e instanceof Date&&!isNaN(e)}(n)?n:new Date}function l(e){const t=new Date(e);return t.setHours(0),t.setMinutes(0),t.setSeconds(0),t.setMilliseconds(0),t}function d(e){if('string'==typeof e){if(''===e)return null;e=p(e)}return l(e).getTime()}function u(e,t){const n=l(new Date(e));return n.setDate(n.getDate()+t),n}function _(e){return r.includes(e.form)}function f(e){return o.includes(e.form)}const y=function(e,t){let n;return e.forEach((function(e){t.includes(e.form)&&(!n||e.reported_date>n.reported_date)&&(n=e)})),n},g=function(e){return _(e)&&d(c(e,'lmp_date_8601'))};function m(e,t){return e.reports.filter((function(e){let n=g(t);return n||(n=t.reported_date),f(e)&&e.reported_date>t.reported_date&&e.reported_dater&&''!==t&&t!==n&&(r=e.reported_date,n=t)})),n}e.exports={today:n,MS_IN_DAY:864e5,MAX_DAYS_IN_PREGNANCY:294,addDays:u,isAlive:function(e){return e&&e.contact&&!e.contact.date_of_death},getTimeForMidnight:l,isFormArraySubmittedInWindow:function(e,t,n,r,i){let o=!1,a=0;return e.forEach((function(e){t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r&&(o=!0,i&&a++)})),i?a>=i:o},isFormArraySubmittedInWindowExcludingThisReport:function(e,t,n,r,i,o){let a=!1,c=0;return e.forEach((function(e){t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r&&e._id!==i._id&&(a=!0,o&&c++)})),o?c>=o:a},getDateMS:d,getDateISOLocal:p,isDeliveryForm:function(e){return i.includes(e.form)},getMostRecentReport:s,getNewestPregnancyTimestamp:function(e){if(!e.contact)return;const t=s(e.reports,'pregnancy');return t?t.reported_date:0},getNewestDeliveryTimestamp:function(e){if(!e.contact)return;const t=s(e.reports,'delivery');return t?t.reported_date:0},getReportsSubmittedInWindow:function(e,t,n,r,i){const o=[];return e.forEach((function(e){t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r&&(i&&!i(e)||o.push(e))})),o},countReportsSubmittedInWindow:function(e,t,n,r,i){let o=0;return e.forEach((function(e){t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r&&(i&&!i(e)||o++)})),o},countANCFacilityVisits:function(e,t){let n=0;const r=m(e,t);return c(t,'anc_visits_hf.anc_visits_hf_past')&&!isNaN(c(t,'anc_visits_hf.anc_visits_hf_past.visited_hf_count'))&&(n+=parseInt(c(t,'anc_visits_hf.anc_visits_hf_past.visited_hf_count'))),n+=r.reduce((function(e,t){const n=c(t,'anc_visits_hf.anc_visits_hf_past');return n?(e+='yes'===n.last_visit_attended&&1,isNaN(n.visited_hf_count)?e:e+('yes'===n.report_other_visits&&parseInt(n.visited_hf_count))):0}),0),n},isFacilityDelivery:function(e,t){return!!e&&(1===arguments.length&&(t=e),'yes'===c(t,'facility_delivery'))},getMostRecentLMPDateForPregnancy:v,getNewestReport:y,getSubsequentPregnancyFollowUps:m,isActivePregnancy:function(e,t){if(!_(t))return!1;const r=(v(e,t)||t.reported_date)>n-254016e5,i=function(e,t,r){return e.reports.filter((function(e){return'delivery'===e.form&&e.reported_date>t.reported_date&&(!r||t.reported_date>=n-864e5*r)}))}(e,t,42).length>0,a=function(e,t){return e.reports.filter((function(e){return _(e)&&e.reported_date>t.reported_date}))}(e,t).length>0;return r&&!i&&!a&&!function(e,t){const n=m(e,t),r=y(n,o);return r&&'abortion'===c(r,'pregnancy_summary.visit_option')}(e,t)&&!function(e,t){const n=m(e,t),r=y(n,o);return r&&'miscarriage'===c(r,'pregnancy_summary.visit_option')}(e,t)},getRecentANCVisitWithEvent:function(e,t,n){const r=m(e,t),i=y(r,o);if(i&&c(i,'pregnancy_summary.visit_option')===n)return i},isPregnancyTaskMuted:function(e){const t=y(e.reports,a);return t&&f(t)&&'clear_all'===c(t,'pregnancy_ended.clear_option')},getField:c}},function(e,t){e.exports=function(e){var t={};!function e(t,n){var r=Object.keys(t);for(var i in r){var o=r[i];switch(typeof t[o]){case'object':e(t[o],n);break;case'function':t[o]=t[o].bind(n)}}}(e,t),t.definition=function e(t){var n=Object.assign({},t),r=Object.keys(n);for(var i in r){var o=r[i];if(Array.isArray(n[o])){n[o]=n[o].slice(0);for(var a=0;a({id:'pregnancy-home-visit-week'+e,start:t,end:n,dueDate:function(t,n,r){const i=_(n,r);return f(i||r.reported_date,7*e)}});function h(e,t,n,r){if(t.reported_date=o},resolvedIf:h,actions:[{type:'report',form:'pregnancy_home_visit',label:'Pregnancy home visit'}],events:[...Array(21).keys()].map(e=>v(2*(e+1),6,7))},{name:'anc.facility_reminder',icon:'icon-pregnancy',title:'task.anc.facility_reminder.title',appliesTo:'reports',appliesToType:['pregnancy','pregnancy_home_visit'],appliesIf:function(e,t){return m(t,'t_pregnancy_follow_up_date')},resolvedIf:function(e,t,n,r){if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date),o=f(r,n.end+1).getTime();return p(e.reports,['pregnancy_facility_visit_reminder'],i,o)},actions:[{type:'report',form:'pregnancy_facility_visit_reminder',label:'Pregnancy facility visit reminder',modifyContent:function(e,t,n){e.source_visit_date=m(n,'t_pregnancy_follow_up_date')}}],events:[{id:'pregnancy-facility-visit-reminder',start:3,end:7,dueDate:function(e,t,n){return l(m(n,'t_pregnancy_follow_up_date'))}}]},{name:'anc.pregnancy_danger_sign_followup',icon:'icon-pregnancy-danger',title:'task.anc.pregnancy_danger_sign_followup.title',appliesTo:'reports',appliesToType:['pregnancy','pregnancy_home_visit','pregnancy_danger_sign','pregnancy_danger_sign_follow_up'],appliesIf:function(e,t){return'yes'===m(t,'t_danger_signs_referral_follow_up')&&s(e)},resolvedIf:function(e,t,n,r){if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date+1),o=f(r,n.end+1).getTime();return p(e.reports,['pregnancy_danger_sign_follow_up'],i,o)},actions:[{type:'report',form:'pregnancy_danger_sign_follow_up'}],events:[{id:'pregnancy-danger-sign-follow-up',start:3,end:7,dueDate:function(e,t,n){return l(m(n,'t_danger_signs_referral_follow_up_date'))}}]},{name:'anc.delivery',icon:'icon-mother-child',title:'task.anc.delivery.title',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){const n=_(e,t);return n&&f(n,336)>=o&&s(e)},resolvedIf:function(e,t,n,r){if(y(e,t,'abortion')||y(e,t,'miscarriage'))return!0;if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date),o=f(r,n.end+1).getTime();return p(e.reports,['delivery'],i,o)},actions:[{type:'report',form:'delivery'}],events:[{id:'delivery-reminder',start:28,end:42,dueDate:function(e,t,n){return f(_(t,n),i)}}]},{name:'pnc.danger_sign_followup_mother',icon:'icon-follow-up',title:'task.pnc.danger_sign_followup_mother.title',appliesTo:'reports',appliesToType:['delivery','pnc_danger_sign_follow_up_mother'],appliesIf:function(e,t){return'yes'===m(t,'t_danger_signs_referral_follow_up')&&s(e)},resolvedIf:function(e,t,n,r){if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date+1),o=f(r,n.end+1).getTime();return p(e.reports,['pnc_danger_sign_follow_up_mother'],i,o)},actions:[{type:'report',form:'pnc_danger_sign_follow_up_mother',modifyContent:function(e,t,n){u(n)?e.delivery_uuid=n._id:e.delivery_uuid=m(n,'inputs.delivery_uuid')}}],events:[{id:'pnc-danger-sign-follow-up-mother',start:3,end:7,dueDate:function(e,t,n){return l(m(n,'t_danger_signs_referral_follow_up_date'))}}]},{name:'pnc.danger_sign_followup_baby.from_contact',icon:'icon-follow-up',title:'task.pnc.danger_sign_followup_baby.title',appliesTo:'contacts',appliesToType:['person'],appliesIf:function(e){return e.contact&&'yes'===e.contact.t_danger_signs_referral_follow_up&&s(e)},resolvedIf:function(e,t,n,r){const i=Math.max(f(r,-n.start).getTime(),e.contact.reported_date),o=f(r,n.end).getTime();return p(e.reports,['pnc_danger_sign_follow_up_baby'],i,o)},actions:[{type:'report',form:'pnc_danger_sign_follow_up_baby',modifyContent:function(e,t){e.delivery_uuid=t.contact.created_by_doc}}],events:[{id:'pnc-danger-sign-follow-up-baby',start:3,end:7,dueDate:function(e,t){return l(t.contact.t_danger_signs_referral_follow_up_date)}}]},{name:'pnc.danger_sign_followup_baby.from_report',icon:'icon-follow-up',title:'task.pnc.danger_sign_followup_baby.title',appliesTo:'reports',appliesToType:['pnc_danger_sign_follow_up_baby'],appliesIf:function(e,t){return'yes'===m(t,'t_danger_signs_referral_follow_up')&&s(e)},resolvedIf:function(e,t,n,r){if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date+1),o=f(r,n.end+1).getTime();return p(e.reports,['pnc_danger_sign_follow_up_baby'],i,o)},actions:[{type:'report',form:'pnc_danger_sign_follow_up_baby',modifyContent:function(e,t,n){e.delivery_uuid=m(n,'inputs.delivery_uuid')}}],events:[{id:'pnc-danger-sign-follow-up-baby',start:3,end:7,dueDate:function(e,t,n){return l(m(n,'t_danger_signs_referral_follow_up_date'))}}]}]},function(e,t,n){const r=n(0),{isAlive:i,getSubsequentPregnancyFollowUps:o,getMostRecentLMPDateForPregnancy:a,isActivePregnancy:c,countANCFacilityVisits:s,getField:p}=r;e.exports=[{id:'deaths-this-month',type:'count',icon:'icon-death-general',goal:0,translation_key:'targets.death_reporting.deaths.title',subtitle_translation_key:'targets.this_month.subtitle',appliesTo:'contacts',appliesToType:['person'],appliesIf:function(e){return!i(e)},date:e=>e.contact.date_of_death},{id:'pregnancy-registrations-this-month',type:'count',icon:'icon-pregnancy',goal:20,translation_key:'targets.anc.new_pregnancy_registrations.title',subtitle_translation_key:'targets.this_month.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){return!!t&&a(e,t)},date:'reported',idType:'contact'},{id:'births-this-month',type:'count',icon:'icon-infant',goal:-1,translation_key:'targets.births.title',subtitle_translation_key:'targets.this_month.subtitle',appliesTo:'contacts',appliesToType:['person'],appliesIf:function(e){return e&&e.contact&&e.contact.date_of_birth},date:e=>e.contact.date_of_birth,dhis:{dataElement:'kB0ZBFisE0e'}},{id:'active-pregnancies',type:'count',icon:'icon-pregnancy',goal:-1,translation_key:'targets.anc.active_pregnancies.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){return c(e,t)},date:'now',idType:'contact'},{id:'active-pregnancies-1+-visits',type:'count',icon:'icon-clinic',goal:-1,translation_key:'targets.anc.active_pregnancies_1p_visits.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){if(!c(e,t))return!1;return s(e,t)>0},date:'now',idType:'contact'},{id:'facility-deliveries',type:'percent',icon:'icon-mother-child',goal:-1,translation_key:'targets.anc.facility_deliveries.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['delivery'],appliesIf:function(e,t){return p(t,'delivery_outcome.delivery_place')},passesIf:function(e,t){return'health_facility'===p(t,'delivery_outcome.delivery_place')},date:'now',idType:'contact',dhis:{dataElement:'e22tIwy1nKR',categoryOptionCombo:'HllvX50cXC0',attributeOptionCombo:'HllvX50cXC0'}},{id:'active-pregnancies-4+-visits',type:'count',icon:'icon-clinic',goal:-1,translation_key:'targets.anc.active_pregnancies_4p_visits.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){if(!c(e,t))return!1;return s(e,t)>3},date:'now',idType:'contact'},{id:'active-pregnancies-8+-contacts',type:'count',icon:'icon-follow-up',goal:-1,translation_key:'targets.anc.active_pregnancies_8p_contacts.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){if(!c(e,t))return!1;return 1+(o(e,t).length||0)+(s(e,t)||0)>7},date:'now',idType:'contact'}]},function(e,t,n){var r=n(1),i=n(6);function o(e,t,n,r,i,o){var a;if(e.appliesToType){var c;if('contacts'===e.appliesTo){if(!i.contact)return;c='contact'===i.contact.type?i.contact.contact_type:i.contact.type}else{if(!o)return;c=o.form}if(-1===e.appliesToType.indexOf(c))return}if('scheduled_tasks'===e.appliesTo||!e.appliesIf||e.appliesIf(i,o))if('scheduled_tasks'===e.appliesTo){if(o&&e.appliesIf){if(!o.scheduled_tasks)return;for(a=0;a['fields',...(t||'').split('.')].reduce((e,t)=>{if(void 0!==e)return e[t]},e);function s(e,t){let n;return e.forEach((function(e){t.includes(e.form)&&!e.deleted&&(!n||e.reported_date>n.reported_date)&&(n=e)})),n}function p(e){if(!e)return new Date;const t=e.split(/\\D/),n=new Date(t[0],t[1]-1,t[2]);return function(e){return e instanceof Date&&!isNaN(e)}(n)?n:new Date}function l(e){const t=new Date(e);return t.setHours(0),t.setMinutes(0),t.setSeconds(0),t.setMilliseconds(0),t}function d(e){if('string'==typeof e){if(''===e)return null;e=p(e)}return l(e).getTime()}function u(e,t){const n=l(new Date(e));return n.setDate(n.getDate()+t),n}function _(e){return r.includes(e.form)}function f(e){return o.includes(e.form)}const y=function(e,t){let n;return e.forEach((function(e){t.includes(e.form)&&(!n||e.reported_date>n.reported_date)&&(n=e)})),n},g=function(e){return _(e)&&d(c(e,'lmp_date_8601'))};function m(e,t){return e.reports.filter((function(e){let n=g(t);return n||(n=t.reported_date),f(e)&&e.reported_date>t.reported_date&&e.reported_dater&&''!==t&&t!==n&&(r=e.reported_date,n=t)})),n}e.exports={today:n,MS_IN_DAY:864e5,MAX_DAYS_IN_PREGNANCY:294,addDays:u,isAlive:function(e){return e&&e.contact&&!e.contact.date_of_death},getTimeForMidnight:l,isFormArraySubmittedInWindow:function(e,t,n,r,i){let o=!1,a=0;return e.forEach((function(e){t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r&&(o=!0,i&&a++)})),i?a>=i:o},isFormArraySubmittedInWindowExcludingThisReport:function(e,t,n,r,i,o){let a=!1,c=0;return e.forEach((function(e){t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r&&e._id!==i._id&&(a=!0,o&&c++)})),o?c>=o:a},getDateMS:d,getDateISOLocal:p,isDeliveryForm:function(e){return i.includes(e.form)},getMostRecentReport:s,getNewestPregnancyTimestamp:function(e){if(!e.contact)return;const t=s(e.reports,'pregnancy');return t?t.reported_date:0},getNewestDeliveryTimestamp:function(e){if(!e.contact)return;const t=s(e.reports,'delivery');return t?t.reported_date:0},getReportsSubmittedInWindow:function(e,t,n,r,i){const o=[];return e.forEach((function(e){t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r&&(i&&!i(e)||o.push(e))})),o},countReportsSubmittedInWindow:function(e,t,n,r,i){let o=0;return e.forEach((function(e){t.includes(e.form)&&e.reported_date>=n&&e.reported_date<=r&&(i&&!i(e)||o++)})),o},countANCFacilityVisits:function(e,t){let n=0;const r=m(e,t);return c(t,'anc_visits_hf.anc_visits_hf_past')&&!isNaN(c(t,'anc_visits_hf.anc_visits_hf_past.visited_hf_count'))&&(n+=parseInt(c(t,'anc_visits_hf.anc_visits_hf_past.visited_hf_count'))),n+=r.reduce((function(e,t){const n=c(t,'anc_visits_hf.anc_visits_hf_past');return n?(e+='yes'===n.last_visit_attended&&1,isNaN(n.visited_hf_count)?e:e+('yes'===n.report_other_visits&&parseInt(n.visited_hf_count))):0}),0),n},isFacilityDelivery:function(e,t){return!!e&&(1===arguments.length&&(t=e),'yes'===c(t,'facility_delivery'))},getMostRecentLMPDateForPregnancy:v,getNewestReport:y,getSubsequentPregnancyFollowUps:m,isActivePregnancy:function(e,t){if(!_(t))return!1;const r=(v(e,t)||t.reported_date)>n-254016e5,i=function(e,t,r){return e.reports.filter((function(e){return'delivery'===e.form&&e.reported_date>t.reported_date&&(!r||t.reported_date>=n-864e5*r)}))}(e,t,42).length>0,a=function(e,t){return e.reports.filter((function(e){return _(e)&&e.reported_date>t.reported_date}))}(e,t).length>0;return r&&!i&&!a&&!function(e,t){const n=m(e,t),r=y(n,o);return r&&'abortion'===c(r,'pregnancy_summary.visit_option')}(e,t)&&!function(e,t){const n=m(e,t),r=y(n,o);return r&&'miscarriage'===c(r,'pregnancy_summary.visit_option')}(e,t)},getRecentANCVisitWithEvent:function(e,t,n){const r=m(e,t),i=y(r,o);if(i&&c(i,'pregnancy_summary.visit_option')===n)return i},isPregnancyTaskMuted:function(e){const t=y(e.reports,a);return t&&f(t)&&'clear_all'===c(t,'pregnancy_ended.clear_option')},getField:c}},function(e,t){e.exports=function(e){var t={};!function e(t,n){var r=Object.keys(t);for(var i in r){var o=r[i];switch(typeof t[o]){case'object':e(t[o],n);break;case'function':t[o]=t[o].bind(n)}}}(e,t),t.definition=function e(t){var n=Object.assign({},t),r=Object.keys(n);for(var i in r){var o=r[i];if(Array.isArray(n[o])){n[o]=n[o].slice(0);for(var a=0;a({id:'pregnancy-home-visit-week'+e,start:t,end:n,dueDate:function(t,n,r){const i=_(n,r);return f(i||r.reported_date,7*e)}});function h(e,t,n,r){if(t.reported_date=o},resolvedIf:h,actions:[{type:'report',form:'pregnancy_home_visit',label:'Pregnancy home visit'}],events:[...Array(21).keys()].map(e=>v(2*(e+1),6,7))},{name:'anc.facility_reminder',icon:'icon-pregnancy',title:'task.anc.facility_reminder.title',appliesTo:'reports',appliesToType:['pregnancy','pregnancy_home_visit'],appliesIf:function(e,t){return m(t,'t_pregnancy_follow_up_date')},resolvedIf:function(e,t,n,r){if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date),o=f(r,n.end+1).getTime();return p(e.reports,['pregnancy_facility_visit_reminder'],i,o)},actions:[{type:'report',form:'pregnancy_facility_visit_reminder',label:'Pregnancy facility visit reminder',modifyContent:function(e,t,n){e.source_visit_date=m(n,'t_pregnancy_follow_up_date')}}],events:[{id:'pregnancy-facility-visit-reminder',start:3,end:7,dueDate:function(e,t,n){return l(m(n,'t_pregnancy_follow_up_date'))}}]},{name:'anc.pregnancy_danger_sign_followup',icon:'icon-pregnancy-danger',title:'task.anc.pregnancy_danger_sign_followup.title',appliesTo:'reports',appliesToType:['pregnancy','pregnancy_home_visit','pregnancy_danger_sign','pregnancy_danger_sign_follow_up'],appliesIf:function(e,t){return'yes'===m(t,'t_danger_signs_referral_follow_up')&&s(e)},resolvedIf:function(e,t,n,r){if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date+1),o=f(r,n.end+1).getTime();return p(e.reports,['pregnancy_danger_sign_follow_up'],i,o)},actions:[{type:'report',form:'pregnancy_danger_sign_follow_up'}],events:[{id:'pregnancy-danger-sign-follow-up',start:3,end:7,dueDate:function(e,t,n){return l(m(n,'t_danger_signs_referral_follow_up_date'))}}]},{name:'anc.delivery',icon:'icon-mother-child',title:'task.anc.delivery.title',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){const n=_(e,t);return n&&f(n,336)>=o&&s(e)},resolvedIf:function(e,t,n,r){if(y(e,t,'abortion')||y(e,t,'miscarriage'))return!0;if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date),o=f(r,n.end+1).getTime();return p(e.reports,['delivery'],i,o)},actions:[{type:'report',form:'delivery'}],events:[{id:'delivery-reminder',start:28,end:42,dueDate:function(e,t,n){return f(_(t,n),i)}}]},{name:'pnc.danger_sign_followup_mother',icon:'icon-follow-up',title:'task.pnc.danger_sign_followup_mother.title',appliesTo:'reports',appliesToType:['delivery','pnc_danger_sign_follow_up_mother'],appliesIf:function(e,t){return'yes'===m(t,'t_danger_signs_referral_follow_up')&&s(e)},resolvedIf:function(e,t,n,r){if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date+1),o=f(r,n.end+1).getTime();return p(e.reports,['pnc_danger_sign_follow_up_mother'],i,o)},actions:[{type:'report',form:'pnc_danger_sign_follow_up_mother',modifyContent:function(e,t,n){u(n)?e.delivery_uuid=n._id:e.delivery_uuid=m(n,'inputs.delivery_uuid')}}],events:[{id:'pnc-danger-sign-follow-up-mother',start:3,end:7,dueDate:function(e,t,n){return l(m(n,'t_danger_signs_referral_follow_up_date'))}}]},{name:'pnc.danger_sign_followup_baby.from_contact',icon:'icon-follow-up',title:'task.pnc.danger_sign_followup_baby.title',appliesTo:'contacts',appliesToType:['person'],appliesIf:function(e){return e.contact&&'yes'===e.contact.t_danger_signs_referral_follow_up&&s(e)},resolvedIf:function(e,t,n,r){const i=Math.max(f(r,-n.start).getTime(),e.contact.reported_date),o=f(r,n.end).getTime();return p(e.reports,['pnc_danger_sign_follow_up_baby'],i,o)},actions:[{type:'report',form:'pnc_danger_sign_follow_up_baby',modifyContent:function(e,t){e.delivery_uuid=t.contact.created_by_doc}}],events:[{id:'pnc-danger-sign-follow-up-baby',start:3,end:7,dueDate:function(e,t){return l(t.contact.t_danger_signs_referral_follow_up_date)}}]},{name:'pnc.danger_sign_followup_baby.from_report',icon:'icon-follow-up',title:'task.pnc.danger_sign_followup_baby.title',appliesTo:'reports',appliesToType:['pnc_danger_sign_follow_up_baby'],appliesIf:function(e,t){return'yes'===m(t,'t_danger_signs_referral_follow_up')&&s(e)},resolvedIf:function(e,t,n,r){if(g(e))return!0;const i=Math.max(f(r,-n.start).getTime(),t.reported_date+1),o=f(r,n.end+1).getTime();return p(e.reports,['pnc_danger_sign_follow_up_baby'],i,o)},actions:[{type:'report',form:'pnc_danger_sign_follow_up_baby',modifyContent:function(e,t,n){e.delivery_uuid=m(n,'inputs.delivery_uuid')}}],events:[{id:'pnc-danger-sign-follow-up-baby',start:3,end:7,dueDate:function(e,t,n){return l(m(n,'t_danger_signs_referral_follow_up_date'))}}]}]},function(e,t,n){const r=n(0),{isAlive:i,getSubsequentPregnancyFollowUps:o,getMostRecentLMPDateForPregnancy:a,isActivePregnancy:c,countANCFacilityVisits:s,getField:p}=r;e.exports=[{id:'deaths-this-month',type:'count',icon:'icon-death-general',goal:0,translation_key:'targets.death_reporting.deaths.title',subtitle_translation_key:'targets.this_month.subtitle',appliesTo:'contacts',appliesToType:['person'],appliesIf:function(e){return!i(e)},date:e=>e.contact.date_of_death},{id:'pregnancy-registrations-this-month',type:'count',icon:'icon-pregnancy',goal:20,translation_key:'targets.anc.new_pregnancy_registrations.title',subtitle_translation_key:'targets.this_month.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){return!!t&&a(e,t)},date:'reported',idType:'contact'},{id:'births-this-month',type:'count',icon:'icon-infant',goal:-1,translation_key:'targets.births.title',subtitle_translation_key:'targets.this_month.subtitle',appliesTo:'contacts',appliesToType:['person'],appliesIf:function(e){return e&&e.contact&&e.contact.date_of_birth},date:e=>e.contact.date_of_birth,dhis:{dataElement:'kB0ZBFisE0e'}},{id:'active-pregnancies',type:'count',icon:'icon-pregnancy',goal:-1,translation_key:'targets.anc.active_pregnancies.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){return c(e,t)},date:'now',idType:'contact'},{id:'active-pregnancies-1+-visits',type:'count',icon:'icon-clinic',goal:-1,translation_key:'targets.anc.active_pregnancies_1p_visits.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){if(!c(e,t))return!1;return s(e,t)>0},date:'now',idType:'contact'},{id:'facility-deliveries',type:'percent',icon:'icon-mother-child',goal:-1,translation_key:'targets.anc.facility_deliveries.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['delivery'],appliesIf:function(e,t){return p(t,'delivery_outcome.delivery_place')},passesIf:function(e,t){return'health_facility'===p(t,'delivery_outcome.delivery_place')},date:'now',idType:'contact',dhis:{dataElement:'e22tIwy1nKR',categoryOptionCombo:'HllvX50cXC0',attributeOptionCombo:'HllvX50cXC0'}},{id:'active-pregnancies-4+-visits',type:'count',icon:'icon-clinic',goal:-1,translation_key:'targets.anc.active_pregnancies_4p_visits.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){if(!c(e,t))return!1;return s(e,t)>3},date:'now',idType:'contact'},{id:'active-pregnancies-8+-contacts',type:'count',icon:'icon-follow-up',goal:-1,translation_key:'targets.anc.active_pregnancies_8p_contacts.title',subtitle_translation_key:'targets.all_time.subtitle',appliesTo:'reports',appliesToType:['pregnancy'],appliesIf:function(e,t){if(!c(e,t))return!1;return 1+(o(e,t).length||0)+(s(e,t)||0)>7},date:'now',idType:'contact'}]},function(e,t,n){var r=n(1),i=n(6);function o(e,t,n,r,i,o){var a;if(e.appliesToType){var c;if('contacts'===e.appliesTo){if(!i.contact)return;c='contact'===i.contact.type?i.contact.contact_type:i.contact.type}else{if(!o)return;c=o.form}if(-1===e.appliesToType.indexOf(c))return}if('scheduled_tasks'===e.appliesTo||!e.appliesIf||e.appliesIf(i,o))if('scheduled_tasks'===e.appliesTo){if(o&&e.appliesIf){if(!o.scheduled_tasks)return;for(a=0;a { if (validationType === 'partial') { - //quickly guesses whether a number is a possible phone number by using only the length information, + //Quickly guesses whether a number is a possible phone number by using only the length information, return instance.isPossibleNumber(parsed); } if (validationType === 'none') { return true; } - //Does full validation of a phone number for a region using length and prefix information. + // Does full validation of a phone number for a region using length and prefix information. return instance.isValidNumber(parsed); }; diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index af0789ff5a2..0e7a957b61b 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -196,8 +196,7 @@ const getPhoneNumber = doc => { const setPhoneNumber = options => { const doc = options.doc; const phoneNumber = getPhoneNumber(doc); - // By default for a valid phone number SmsParser itself adds a country code if not provided - // So if country code is not present in phone_number throw error + //sms parser already validate the phone this is a just in case check if by any chance it has become invalid. const appSettings = config.getAll(); const validPhone = phoneNumberParser.validate(appSettings, phoneNumber); if (!validPhone) { diff --git a/shared-libs/validation/test/validations.js b/shared-libs/validation/test/validations.js index c39282c746f..f53d09e2fe2 100644 --- a/shared-libs/validation/test/validations.js +++ b/shared-libs/validation/test/validations.js @@ -248,7 +248,7 @@ describe('validations', () => { }); }); - it('should fail uniquePhone if phone is found in db', () => { + it('unique phone validation should fail if db query for phone returns doc', () => { clock = sinon.useFakeTimers(); sinon.stub(db.medic, 'query').resolves({ rows: [ @@ -279,7 +279,7 @@ describe('validations', () => { }); }); - it('should pass uniquePhone if phone is not found in db', () => { + it('unique phone validation should pass if db query for phone does not return any doc', () => { clock = sinon.useFakeTimers(); sinon.stub(db.medic, 'query').resolves({ undefined }); const validations = [ @@ -288,14 +288,14 @@ describe('validations', () => { rule: 'uniquePhone("phone_number")', message: [ { - content: 'Duplicate phone', + content: 'unique phone', locale: 'en', }, ], }, ]; const doc = { - _id: 'duplicate', + _id: 'unique', xyz: '+9779841111111', }; return validation.validate(doc, validations).then(errors => { From 95374aa177407a3bd3dcb49f5cbc116ba8829c21 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Thu, 3 Aug 2023 12:29:16 +0545 Subject: [PATCH 38/56] eslint fixes --- api/tests/mocha/services/report/smsparser.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/tests/mocha/services/report/smsparser.spec.js b/api/tests/mocha/services/report/smsparser.spec.js index f75bf7086c1..cec5cb2b9c5 100644 --- a/api/tests/mocha/services/report/smsparser.spec.js +++ b/api/tests/mocha/services/report/smsparser.spec.js @@ -108,7 +108,7 @@ describe('sms parser', () => { }); const data = smsparser.parse(def, doc); chai.expect(data.phone_number).to.equal('+9779841202020'); - }) + }); }); it('accepts phone number with extension', () => { @@ -158,8 +158,8 @@ describe('sms parser', () => { //India , Kenya, Tanzania Phone is accepted as contact info in Nepal region. //Just in case we make cross region/borders tool [['NP 20 +918750660880 Prajwol', '+918750660880'], - ['NP 20 +254773087889 Prajwol', '+254773087889'], - ['NP 20 +255712262987 Prajwol', '+255712262987']].forEach(phoneNumerWithParsed => { + ['NP 20 +254773087889 Prajwol', '+254773087889'], + ['NP 20 +255712262987 Prajwol', '+255712262987']].forEach(phoneNumerWithParsed => { it(`returns parsed number if valid phone of another the region ${phoneNumerWithParsed[1]}`, () => { const doc = { message: phoneNumerWithParsed[0] }; const def = definitions.forms.NP; From 46a4f2ad2a4c12bc8b9dd20785ea0119cafd070e Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Fri, 4 Aug 2023 15:25:15 +0545 Subject: [PATCH 39/56] Code reivew changes form-definitions and code comments --- api/tests/form-definitions.js | 61 +++---------------- .../mocha/services/report/smsparser.spec.js | 10 +-- shared-libs/phone-number/src/phone-number.js | 2 +- .../src/transitions/registration.js | 2 +- 4 files changed, 15 insertions(+), 60 deletions(-) diff --git a/api/tests/form-definitions.js b/api/tests/form-definitions.js index a1bc4e575e0..0ca2c33a995 100644 --- a/api/tests/form-definitions.js +++ b/api/tests/form-definitions.js @@ -793,61 +793,16 @@ exports.forms = { } }, NP: { - 'meta': { - 'code': 'NP', - 'translation_key': 'forms.np.title', - 'label': { - 'en': 'New Person with phone_number Registration' - } + meta: { + code: 'NP' }, - 'fields': { - 'patient_age': { - 'labels': { - 'tiny': { - 'en': 'Person Age' - }, - 'short': { - 'en': 'Person Age' - } - }, - 'position': 0, - 'type': 'integer', - 'required': true - }, - 'phone_number': { - 'labels': { - 'tiny': { - 'en': 'phone number' - }, - 'description': { - 'en': 'phone number' - }, - 'short': { - 'en': 'phone number' - } - }, - 'position': 1, - 'type': 'phone_number', - 'required': true + fields: { + patient_age: { + type: 'integer' }, - 'patient_name': { - 'labels': { - 'tiny': { - 'en': 'patient_name' - }, - 'description': { - 'en': 'Patient name' - }, - 'short': { - 'en': 'Patient name' - } - }, - 'position': 2, - 'type': 'string', - 'required': true + phone_number: { + type: 'phone_number' } - }, - 'public_form': false, - 'use_sentinel': true + } } }; diff --git a/api/tests/mocha/services/report/smsparser.spec.js b/api/tests/mocha/services/report/smsparser.spec.js index cec5cb2b9c5..caa0ad0f0e1 100644 --- a/api/tests/mocha/services/report/smsparser.spec.js +++ b/api/tests/mocha/services/report/smsparser.spec.js @@ -134,7 +134,7 @@ describe('sms parser', () => { }); it('returns null if phone number is invalid for the region', () => { - const doc = { message: 'NP 20 +97712312 Prajwol' }; + const doc = { message: 'NP 20 +97712312' }; const def = definitions.forms.NP; sinon.stub(config, 'getAll').returns({ default_country_code: 977, @@ -145,7 +145,7 @@ describe('sms parser', () => { }); it('returns null if phone number is invalid for default region', () => { - const doc = { message: 'NP 20 8750660880 Prajwol' }; + const doc = { message: 'NP 20 8750660880' }; const def = definitions.forms.NP; sinon.stub(config, 'getAll').returns({ default_country_code: 977, @@ -157,9 +157,9 @@ describe('sms parser', () => { //India , Kenya, Tanzania Phone is accepted as contact info in Nepal region. //Just in case we make cross region/borders tool - [['NP 20 +918750660880 Prajwol', '+918750660880'], - ['NP 20 +254773087889 Prajwol', '+254773087889'], - ['NP 20 +255712262987 Prajwol', '+255712262987']].forEach(phoneNumerWithParsed => { + [['NP 20 +918750660880', '+918750660880'], + ['NP 20 +254773087889', '+254773087889'], + ['NP 20 +255712262987', '+255712262987']].forEach(phoneNumerWithParsed => { it(`returns parsed number if valid phone of another the region ${phoneNumerWithParsed[1]}`, () => { const doc = { message: phoneNumerWithParsed[0] }; const def = definitions.forms.NP; diff --git a/shared-libs/phone-number/src/phone-number.js b/shared-libs/phone-number/src/phone-number.js index fdd38b67183..179aff14158 100644 --- a/shared-libs/phone-number/src/phone-number.js +++ b/shared-libs/phone-number/src/phone-number.js @@ -15,7 +15,7 @@ const _init = function (settings, phone) { const validPhone = () => { if (validationType === 'partial') { - //Quickly guesses whether a number is a possible phone number by using only the length information, + // Quickly guesses whether a number is a possible phone number by using only the length information, return instance.isPossibleNumber(parsed); } if (validationType === 'none') { diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 0e7a957b61b..1ddf9cf1583 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -196,7 +196,7 @@ const getPhoneNumber = doc => { const setPhoneNumber = options => { const doc = options.doc; const phoneNumber = getPhoneNumber(doc); - //sms parser already validate the phone this is a just in case check if by any chance it has become invalid. + // Sms parser already validate the phone this is a just in case check if by any chance it has become invalid. const appSettings = config.getAll(); const validPhone = phoneNumberParser.validate(appSettings, phoneNumber); if (!validPhone) { From a4b55afa5a480149825e0959616522d92c2d0cb3 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Tue, 8 Aug 2023 14:55:29 +0545 Subject: [PATCH 40/56] passed invalid phone number form sms parser and added valid phone validation to be used in app_settings.json in form's phone validatoin --- api/src/services/report/smsparser.js | 2 +- shared-libs/validation/src/validation.js | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/api/src/services/report/smsparser.js b/api/src/services/report/smsparser.js index a06f69bbe40..a20aeb703f3 100644 --- a/api/src/services/report/smsparser.js +++ b/api/src/services/report/smsparser.js @@ -217,7 +217,7 @@ const fieldParsers = { return formattedAndValidatedPhone; } else { logger.warn(`The provided phone number ${raw} is invalid`); - return null; + return raw; } } }; diff --git a/shared-libs/validation/src/validation.js b/shared-libs/validation/src/validation.js index e0bcae2abe0..d311c7a019e 100644 --- a/shared-libs/validation/src/validation.js +++ b/shared-libs/validation/src/validation.js @@ -2,6 +2,8 @@ const _ = require('lodash/core'); const moment = require('moment'); const pupil = require('./pupil/src/pupil'); const messages = require('@medic/message-utils'); +const config = require('../../transitions/src/config') +const phoneNumberParser = require('@medic/phone-number'); let db; let logger = console; @@ -168,6 +170,11 @@ module.exports = { .query('medic-client/contacts_by_phone', { key: doc[validation.field] }) .then(results => !(results && results.rows && results.rows.length)); }, + validPhone: (doc, validation) => { + const appSettings = config.getAll(); + const validPhone = phoneNumberParser.validate(appSettings, doc[validation.field]); + return Promise.resolve(validPhone); + }, uniqueWithin: (doc, validation) => { const fields = [...validation.funcArgs]; const duration = _parseDuration(fields.pop()); From b5378f464fae2961a6df0878f61b510bf42c32a7 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Tue, 8 Aug 2023 16:33:40 +0545 Subject: [PATCH 41/56] phone validation in add_patient itself --- .../src/transitions/registration.js | 38 +++++++++++-------- shared-libs/validation/src/validation.js | 1 + 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 1ddf9cf1583..33a1f7fdfd3 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -41,6 +41,20 @@ const getNameField = (params, prefix) => { return defaultNameField; }; +const getPatientPhoneField = (settings,currentForm) => { + // Get the current form for which we are running transition form + formDef = settings.forms[currentForm]; + + // Get the phone field i.e.the field with type phone_number in the form + const phoneField = (Object.keys(formDef.fields).filter(key => formDef.fields[key].type === 'phone_number')) + + // Return the phone field + if(phoneField && phoneField[0]){ + return phoneField[0]; + } +} + + const parseParams = params => { if (!params) { return {}; @@ -193,19 +207,6 @@ const getPhoneNumber = doc => { return doc.fields.phone_number; }; -const setPhoneNumber = options => { - const doc = options.doc; - const phoneNumber = getPhoneNumber(doc); - // Sms parser already validate the phone this is a just in case check if by any chance it has become invalid. - const appSettings = config.getAll(); - const validPhone = phoneNumberParser.validate(appSettings, phoneNumber); - if (!validPhone) { - transitionUtils.addRejectionMessage(doc, options.registrationConfig, 'provided_phone_not_valid'); - return; - } - doc.phone_number = phoneNumber; -}; - const getConfig = () => config.get('registrations'); /* @@ -485,6 +486,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); // create a new patient with this patient_id const patient = { @@ -501,6 +503,8 @@ const addPatient = (options) => { patient.type = 'person'; } + + return utils .getContactUuid(patientShortcode) .then(patientContactId => { @@ -529,8 +533,12 @@ const addPatient = (options) => { patient.date_of_birth = doc.birth_date; } - if (doc.phone_number) { - patient.phone = doc.phone_number; + if (patientPhoneField && doc.fields[patientPhoneField]) { + if (!phoneNumberParser.validate(config.getAll(), (doc.fields[patientPhoneField]))) { + transitionUtils.addRejectionMessage(doc, options.registrationConfig, 'provided_phone_not_valid'); + return; + } + patient.phone = doc.fields[patientPhoneField]; } // assign patient in doc with full parent doc - to be used in messages diff --git a/shared-libs/validation/src/validation.js b/shared-libs/validation/src/validation.js index d311c7a019e..e9f3dccdf45 100644 --- a/shared-libs/validation/src/validation.js +++ b/shared-libs/validation/src/validation.js @@ -171,6 +171,7 @@ module.exports = { .then(results => !(results && results.rows && results.rows.length)); }, validPhone: (doc, validation) => { + return Promise.resolve(true); const appSettings = config.getAll(); const validPhone = phoneNumberParser.validate(appSettings, doc[validation.field]); return Promise.resolve(validPhone); From a88e85bd19af5dec7cf961876ff4eccb680da2a1 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Tue, 8 Aug 2023 17:58:26 +0545 Subject: [PATCH 42/56] Implemented suggestion to remove add_phone_number trigger and do the patient creation based upon valid/invalid phone in addPatient trigger --- .../src/transitions/registration.js | 26 ++++++------------- shared-libs/validation/src/validation.js | 8 ------ 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 33a1f7fdfd3..1c56afee94d 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -41,18 +41,18 @@ const getNameField = (params, prefix) => { return defaultNameField; }; -const getPatientPhoneField = (settings,currentForm) => { +const getPatientPhoneField = (settings, currentForm) => { // Get the current form for which we are running transition form - formDef = settings.forms[currentForm]; - + const formDef = settings.forms[currentForm]; + // Get the phone field i.e.the field with type phone_number in the form - const phoneField = (Object.keys(formDef.fields).filter(key => formDef.fields[key].type === 'phone_number')) + const phoneField = (Object.keys(formDef.fields).filter(key => formDef.fields[key].type === 'phone_number')); // Return the phone field - if(phoneField && phoneField[0]){ + if (phoneField && phoneField[0]) { return phoneField[0]; } -} +}; const parseParams = params => { @@ -200,13 +200,6 @@ const setBirthDate = doc => { } }; -const getPhoneNumber = doc => { - if (!doc || !doc.fields) { - return ''; - } - return doc.fields.phone_number; -}; - const getConfig = () => config.get('registrations'); /* @@ -241,9 +234,6 @@ const triggers = { logger.warn('Use of add_patient_id trigger. This is deprecated in favour of add_patient.'); return triggers.add_patient(options); }, - add_phone_number: (options) => { - return setPhoneNumber(options); - }, add_expected_date: (options) => { return setExpectedBirthDate(options.doc); }, @@ -486,7 +476,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); + const patientPhoneField = getPatientPhoneField(config.getAll(), doc.form); // create a new patient with this patient_id const patient = { @@ -503,7 +493,7 @@ const addPatient = (options) => { patient.type = 'person'; } - + return utils .getContactUuid(patientShortcode) diff --git a/shared-libs/validation/src/validation.js b/shared-libs/validation/src/validation.js index e9f3dccdf45..e0bcae2abe0 100644 --- a/shared-libs/validation/src/validation.js +++ b/shared-libs/validation/src/validation.js @@ -2,8 +2,6 @@ const _ = require('lodash/core'); const moment = require('moment'); const pupil = require('./pupil/src/pupil'); const messages = require('@medic/message-utils'); -const config = require('../../transitions/src/config') -const phoneNumberParser = require('@medic/phone-number'); let db; let logger = console; @@ -170,12 +168,6 @@ module.exports = { .query('medic-client/contacts_by_phone', { key: doc[validation.field] }) .then(results => !(results && results.rows && results.rows.length)); }, - validPhone: (doc, validation) => { - return Promise.resolve(true); - const appSettings = config.getAll(); - const validPhone = phoneNumberParser.validate(appSettings, doc[validation.field]); - return Promise.resolve(validPhone); - }, uniqueWithin: (doc, validation) => { const fields = [...validation.funcArgs]; const duration = _parseDuration(fields.pop()); From 807caf4c73223ffb3387513d2dd200a4c16d77c3 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Tue, 8 Aug 2023 18:20:53 +0545 Subject: [PATCH 43/56] Changed sms parser tests and made sms parser return invalid number instead of null --- api/src/services/report/smsparser.js | 4 ++++ api/tests/mocha/services/report/smsparser.spec.js | 12 +++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/api/src/services/report/smsparser.js b/api/src/services/report/smsparser.js index a20aeb703f3..f828571a51a 100644 --- a/api/src/services/report/smsparser.js +++ b/api/src/services/report/smsparser.js @@ -217,6 +217,10 @@ const fieldParsers = { return formattedAndValidatedPhone; } else { logger.warn(`The provided phone number ${raw} is invalid`); + + // Returning raw here becuase what to do with invalid phone + // is defined in transitions so error will be thrown there if required. + // Warning is logged just in case. return raw; } } diff --git a/api/tests/mocha/services/report/smsparser.spec.js b/api/tests/mocha/services/report/smsparser.spec.js index caa0ad0f0e1..d2cf9abf080 100644 --- a/api/tests/mocha/services/report/smsparser.spec.js +++ b/api/tests/mocha/services/report/smsparser.spec.js @@ -133,7 +133,9 @@ describe('sms parser', () => { chai.expect(data.phone_number).to.equal('+9779841202020'); }); - it('returns null if phone number is invalid for the region', () => { + it('returns the exact invalid number if phone number is invalid for the region', () => { + // Looks counter intuitive but validation needs to be done agian in transition so it can + // act against the valid or invalid result. Warning is logged just in case. const doc = { message: 'NP 20 +97712312' }; const def = definitions.forms.NP; sinon.stub(config, 'getAll').returns({ @@ -141,18 +143,18 @@ describe('sms parser', () => { phone_validation: 'full' }); const data = smsparser.parse(def, doc); - chai.expect(data.phone_number).to.equal(null); + chai.expect(data.phone_number).to.equal('+97712312'); }); - it('returns null if phone number is invalid for default region', () => { - const doc = { message: 'NP 20 8750660880' }; + it('should add extension to given number if not provided', () => { + const doc = { message: 'NP 20 9841202020' }; const def = definitions.forms.NP; sinon.stub(config, 'getAll').returns({ default_country_code: 977, phone_validation: 'full' }); const data = smsparser.parse(def, doc); - chai.expect(data.phone_number).to.equal(null); + chai.expect(data.phone_number).to.equal('+9779841202020'); }); //India , Kenya, Tanzania Phone is accepted as contact info in Nepal region. From 1bf6514ae984fb6c2e48d12fe59287bdb6bfbfa4 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Tue, 8 Aug 2023 18:52:08 +0545 Subject: [PATCH 44/56] added null checks on getpatientphonefield function --- .../transitions/src/transitions/registration.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 1c56afee94d..43ede06736b 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -45,13 +45,14 @@ const getPatientPhoneField = (settings, currentForm) => { // Get the current form for which we are running transition form const formDef = settings.forms[currentForm]; - // Get the phone field i.e.the field with type phone_number in the form - const phoneField = (Object.keys(formDef.fields).filter(key => formDef.fields[key].type === 'phone_number')); - - // Return the phone field - if (phoneField && phoneField[0]) { - return phoneField[0]; - } + if (formDef && formDef.fields) { + // Get the phone field i.e.the field with type phone_number in the form + const phoneField = (Object.keys(formDef.fields).filter(key => formDef.fields[key].type === 'phone_number')); + // Return the phone field + if (phoneField && phoneField[0]) { + return phoneField[0]; + } + } }; From 3f35ad1de77604208c118ab1e62a54881b400673 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Tue, 8 Aug 2023 20:08:46 +0545 Subject: [PATCH 45/56] added null check on formdef --- .../src/transitions/registration.js | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 43ede06736b..1868ee83875 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -43,16 +43,18 @@ const getNameField = (params, prefix) => { const getPatientPhoneField = (settings, currentForm) => { // Get the current form for which we are running transition form - const formDef = settings.forms[currentForm]; - - if (formDef && formDef.fields) { - // Get the phone field i.e.the field with type phone_number in the form - const phoneField = (Object.keys(formDef.fields).filter(key => formDef.fields[key].type === 'phone_number')); - // Return the phone field - if (phoneField && phoneField[0]) { - return phoneField[0]; - } - } + if(settings && currentForm && settings.forms) { + const formDef = settings.forms[currentForm]; + + if (formDef && formDef.fields) { + // Get the phone field i.e.the field with type phone_number in the form + const phoneField = (Object.keys(formDef.fields).filter(key => formDef.fields[key].type === 'phone_number')); + // Return the phone field + if (phoneField && phoneField[0]) { + return phoneField[0]; + } + } + } }; From 85d2065045c715d9d1d82653e16746cadc9c2200 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Tue, 8 Aug 2023 23:05:28 +0545 Subject: [PATCH 46/56] Checking if updated config fixes problem, cannot run unit tests locally --- .../sentinel/transitions/test-stubs.js | 53 ++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/tests/integration/sentinel/transitions/test-stubs.js b/tests/integration/sentinel/transitions/test-stubs.js index a19c93fc99c..961c53aa02e 100644 --- a/tests/integration/sentinel/transitions/test-stubs.js +++ b/tests/integration/sentinel/transitions/test-stubs.js @@ -4,11 +4,6 @@ exports.forms = { registrations: [{ form: 'FORM-A', events: [{ - name: 'on_create', - trigger: 'add_phone_number', - params: 'phone_number', - bool_expr: 'doc.fields.phone_number' - }, { name: 'on_create', trigger: 'add_patient', params: '', @@ -23,7 +18,53 @@ exports.forms = { }], }], }], - forms: { 'FORM-A': {} } + forms: { + 'FORM-A': { + fields: { + phone_number: { + labels: { + tiny: { + en: 'phone number' + }, + description: { + en: 'phone number' + }, + short: { + en: 'phone number' + } + }, + position: 0, + flags: { + allow_duplicate: false + }, + type: 'phone_number', + required: true + }, + patient_name: { + labels: { + tiny: { + en: 'patient_name' + }, + description: { + en: 'Patient name' + }, + short: { + 'en': 'Patient name' + } + }, + position: 1, + type: 'string', + length: [ + 3, + 30 + ], + required: true + } + }, + public_form: false, + use_sentinel: true + } + } } }; From 470601c7a067e165ceb95ba7789c0d9693472047 Mon Sep 17 00:00:00 2001 From: Prajwol Shrestha Date: Wed, 9 Aug 2023 21:45:01 +0545 Subject: [PATCH 47/56] Apply suggestions from Diana's code review Co-authored-by: Diana Barsan <35681649+dianabarsan@users.noreply.github.com> --- .../src/transitions/registration.js | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 1868ee83875..4febd5335ff 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -41,23 +41,16 @@ const getNameField = (params, prefix) => { return defaultNameField; }; -const getPatientPhoneField = (settings, currentForm) => { - // Get the current form for which we are running transition form - if(settings && currentForm && settings.forms) { - const formDef = settings.forms[currentForm]; - - if (formDef && formDef.fields) { - // Get the phone field i.e.the field with type phone_number in the form - const phoneField = (Object.keys(formDef.fields).filter(key => formDef.fields[key].type === 'phone_number')); - // Return the phone field - if (phoneField && phoneField[0]) { - return phoneField[0]; - } - } +const getPatientPhoneField = (currentForm) => { + const formDefinition = utils.getForm(currentForm); + if (!formDefinition) { + return; } + + const phoneField = Object.keys(formDef.fields).find(key => formDef.fields[key].type === 'phone_number'); + return phoneField; }; - const parseParams = params => { if (!params) { return {}; From 7f266e20c9a5262118cdd8e2c2f1a90d115c0347 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Wed, 9 Aug 2023 23:06:17 +0545 Subject: [PATCH 48/56] code review changes --- api/src/services/report/smsparser.js | 15 +++++++-------- .../transitions/src/transitions/registration.js | 4 ++-- shared-libs/validation/test/validations.js | 2 -- .../sentinel/transitions/registration.spec.js | 13 +++++++++---- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/api/src/services/report/smsparser.js b/api/src/services/report/smsparser.js index f828571a51a..fc91f2d324b 100644 --- a/api/src/services/report/smsparser.js +++ b/api/src/services/report/smsparser.js @@ -215,14 +215,13 @@ const fieldParsers = { const formattedAndValidatedPhone = phoneNumberParser.normalize(config.getAll(), raw); if (formattedAndValidatedPhone) { return formattedAndValidatedPhone; - } else { - logger.warn(`The provided phone number ${raw} is invalid`); - - // Returning raw here becuase what to do with invalid phone - // is defined in transitions so error will be thrown there if required. - // Warning is logged just in case. - return raw; - } + } + logger.warn(`The provided phone number ${raw} is invalid`); + + // Returning raw here becuase what to do with invalid phone + // is defined in transitions so error will be thrown there if required. + // Warning is logged just in case. + return raw; } }; diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 4febd5335ff..4379330cae2 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -47,7 +47,7 @@ const getPatientPhoneField = (currentForm) => { return; } - const phoneField = Object.keys(formDef.fields).find(key => formDef.fields[key].type === 'phone_number'); + const phoneField = Object.keys(formDefinition.fields).find(key => formDefinition.fields[key].type === 'phone_number'); return phoneField; }; @@ -472,7 +472,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); + const patientPhoneField = getPatientPhoneField(doc.form); // create a new patient with this patient_id const patient = { diff --git a/shared-libs/validation/test/validations.js b/shared-libs/validation/test/validations.js index f53d09e2fe2..a1964bc813f 100644 --- a/shared-libs/validation/test/validations.js +++ b/shared-libs/validation/test/validations.js @@ -249,7 +249,6 @@ describe('validations', () => { }); it('unique phone validation should fail if db query for phone returns doc', () => { - clock = sinon.useFakeTimers(); sinon.stub(db.medic, 'query').resolves({ rows: [ { @@ -280,7 +279,6 @@ describe('validations', () => { }); it('unique phone validation should pass if db query for phone does not return any doc', () => { - clock = sinon.useFakeTimers(); sinon.stub(db.medic, 'query').resolves({ undefined }); const validations = [ { diff --git a/tests/integration/sentinel/transitions/registration.spec.js b/tests/integration/sentinel/transitions/registration.spec.js index 5e86a6bef0a..b638882cbed 100644 --- a/tests/integration/sentinel/transitions/registration.spec.js +++ b/tests/integration/sentinel/transitions/registration.spec.js @@ -132,7 +132,6 @@ describe('registration', () => { }) .then(() => utils.getDocs(docIds)) .then(updated => { - console.log(updated); chai.expect(updated[0].fields.phone_number).to.equal(patient_phone); chai.expect(updated[0].patient_id).not.to.equal(undefined); chai.expect(updated[0].tasks).to.have.lengthOf(1); @@ -159,7 +158,7 @@ describe('registration', () => { }); it('should not create patient from report doc when provided invalid phone', () => { - const patient_phone = '+9779666666666'; + const patient_phone = '+97796666'; const patient_id =uuid(); const patientNameAndInvalidPhone = { // has just the `patient_name` field, and should create this person @@ -196,14 +195,16 @@ describe('registration', () => { .then(updated => { chai.expect(updated[0].fields.phone_number).to.equal(patient_phone); chai.expect(updated[0].patient_id).to.not.be.null; + const newPatientId = updated[0].patient_id; + return getContactsByReference([newPatientId, 'venus']); }) .then(patients => { - chai.expect(patients).to.be.undefined; + chai.expect(patients[0]).to.be.undefined; }); }); it('should fail transition on invalid phone', () => { - const patient_phone = '+9779666666666'; + const patient_phone = '+97796666'; const patientNameAndInvalidPhone = { // has just the `patient_name` field, and should create this person _id: uuid(), type: 'data_record', @@ -234,6 +235,10 @@ describe('registration', () => { infos.forEach(info => { chai.expect(info).to.deep.nested.not.include({ 'transitions.registration.ok': false }); }); + }) + .then(() => utils.getDocs(docIds)) + .then(updated => { + chai.expect(updated.patient_id).to.be.undefined; }); }); From 5123c8a4e5df1e7210795880d942ae07c9f0b082 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Thu, 10 Aug 2023 09:30:56 +0545 Subject: [PATCH 49/56] added null checks to see if it passes tests --- .../transitions/src/transitions/registration.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 4379330cae2..0802d690324 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -42,13 +42,19 @@ const getNameField = (params, prefix) => { }; const getPatientPhoneField = (currentForm) => { - const formDefinition = utils.getForm(currentForm); - if (!formDefinition) { + const formDef = utils.getForm(currentForm); + if (!formDef) { return; } - - const phoneField = Object.keys(formDefinition.fields).find(key => formDefinition.fields[key].type === 'phone_number'); - return phoneField; + + if (formDef && formDef.fields) { + // Get the phone field i.e.the field with type phone_number in the form + const phoneField = (Object.keys(formDef.fields).filter(key => formDef.fields[key].type === 'phone_number')); + // Return the phone field + if (phoneField && phoneField[0]) { + return phoneField[0]; + } + } }; const parseParams = params => { From f076a6372709e645ccd3395c3ddd1ffc015cbad7 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Thu, 10 Aug 2023 22:20:25 +0545 Subject: [PATCH 50/56] Added tests to test add_patient trigger changes --- .../test/unit/transitions/registration.js | 218 ++++++++++++++++++ 1 file changed, 218 insertions(+) diff --git a/shared-libs/transitions/test/unit/transitions/registration.js b/shared-libs/transitions/test/unit/transitions/registration.js index e55465f0fc0..5e2d4321e30 100644 --- a/shared-libs/transitions/test/unit/transitions/registration.js +++ b/shared-libs/transitions/test/unit/transitions/registration.js @@ -7,6 +7,7 @@ const utils = require('../../../src/lib/utils'); const config = require('../../../src/config'); const validation = require('@medic/validation'); const contactTypeUtils = require('@medic/contact-types-utils'); +const phoneNumberParser = require('@medic/phone-number'); let schedules; let transitionUtils; @@ -126,6 +127,223 @@ describe('registration', () => { }); }); + it('add_patient trigger only creates a new patient with phone if form has phone field and phone is valid', () => { + // Form with phone field + const formDef = { + fields: { + patient_name: { + type: 'string' + }, + phone_number: { + type: 'phone_number' + } + } + }; + // Stubbing that the phone is valid + sinon.stub(phoneNumberParser, 'validate').returns(true); + const patientName = 'jack'; + const submitterId = 'abc'; + const parentId = 'papa'; + const patientId = '05649'; + const reportId = 'def'; + const senderPhoneNumber = '9841202020'; + const patientPhoneNumber = '9841000000'; + const dob = '2017-03-31T01:15:09.000Z'; + const change = { + doc: { + _id: reportId, + type: 'data_record', + form: 'R', + reported_date: 53, + from: senderPhoneNumber, + fields: { patient_name: patientName, phone_number: patientPhoneNumber }, + birth_date: dob, + }, + }; + const getContactUuid = sinon.stub(utils, 'getContactUuid').resolves(); + // return expected view results when searching for contacts_by_phone + const view = sinon.stub(db.medic, 'query').resolves({ + rows: [ + { + doc: { + _id: submitterId, + parent: { _id: parentId }, + }, + }, + ], + }); + sinon.stub(db.medic, 'get').withArgs('papa').resolves({ _id: parentId, type: 'contact', contact_type: 'place' }); + const saveDoc = sinon.stub(db.medic, 'post').resolves(); + + const eventConfig = { + form: 'R', + events: [{ name: 'on_create', trigger: 'add_patient' }], + }; + config.get.returns([eventConfig]); + sinon.stub(validation, 'validate').resolves(null); + sinon.stub(utils, 'getRegistrations').resolves([]); + sinon.stub(utils, 'getForm').returns(formDef); + sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); + config.getAll.returns(settings); + + return transition.onMatch(change).then(() => { + getContactUuid.callCount.should.equal(1); + view.callCount.should.equal(1); + view.args[0][0].should.equal('medic-client/contacts_by_phone'); + view.args[0][1].key.should.equal(senderPhoneNumber); + view.args[0][1].include_docs.should.equal(true); + saveDoc.callCount.should.equal(1); + saveDoc.args[0][0].name.should.equal(patientName); + saveDoc.args[0][0].phone.should.equal(patientPhoneNumber); + saveDoc.args[0][0].parent._id.should.equal(parentId); + saveDoc.args[0][0].reported_date.should.equal(53); + saveDoc.args[0][0].type.should.equal('person'); + saveDoc.args[0][0].patient_id.should.equal(patientId); + saveDoc.args[0][0].date_of_birth.should.equal(dob); + saveDoc.args[0][0].source_id.should.equal(reportId); + saveDoc.args[0][0].created_by.should.equal(submitterId); + }); + }); + + it('add_patient trigger should not create patient if form has phone field and phone is invalid', () => { + // Form with phone field + const formDef = { + fields: { + patient_name: { + type: 'string' + }, + phone_number: { + type: 'phone_number' + } + } + }; + // Stubbing that phone is invalid + sinon.stub(phoneNumberParser, 'validate').returns(false); + + const patientName = 'jack'; + const submitterId = 'abc'; + const parentId = 'papa'; + const patientId = '05649'; + const reportId = 'def'; + const senderPhoneNumber = '9841202020'; + // We are stubbing that phone number validator returns false this is just a placeholder + const patientPhoneNumber = '98410'; + const dob = '2017-03-31T01:15:09.000Z'; + const change = { + doc: { + _id: reportId, + type: 'data_record', + form: 'R', + reported_date: 53, + from: senderPhoneNumber, + fields: { patient_name: patientName, phone_number: patientPhoneNumber }, + birth_date: dob, + }, + }; + sinon.stub(utils, 'getContactUuid').resolves(); + sinon.stub(db.medic, 'query').resolves({ + rows: [ + { + doc: { + _id: submitterId, + parent: { _id: parentId }, + }, + }, + ], + }); + sinon.stub(db.medic, 'get').withArgs('papa').resolves({ _id: parentId, type: 'contact', contact_type: 'place' }); + const saveDoc = sinon.stub(db.medic, 'post').resolves(); + + const eventConfig = { + form: 'R', + events: [{ name: 'on_create', trigger: 'add_patient' }], + }; + + config.get.returns([eventConfig]); + sinon.stub(validation, 'validate').resolves(null); + sinon.stub(utils, 'getRegistrations').resolves([]); + sinon.stub(utils, 'getForm').returns(formDef); + sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); + config.getAll.returns(settings); + + return transition.onMatch(change).then(() => { + saveDoc.callCount.should.equal(0); + }); + }); + + it('add_patient trigger doesnot add patient phone if form does not have phone field', () => { + // Form without phone field + const formDef = { + fields: { + patient_name: { + type: 'string' + } + } + }; + const patientName = 'jack'; + const submitterId = 'abc'; + const parentId = 'papa'; + const patientId = '05649'; + const reportId = 'def'; + const senderPhoneNumber = '9841202020'; + const patientPhoneNumber = '9841000000'; + const dob = '2017-03-31T01:15:09.000Z'; + const change = { + doc: { + _id: reportId, + type: 'data_record', + form: 'R', + reported_date: 53, + from: senderPhoneNumber, + fields: { patient_name: patientName, phone_number: patientPhoneNumber }, + birth_date: dob, + }, + }; + const getContactUuid = sinon.stub(utils, 'getContactUuid').resolves(); + // return expected view results when searching for contacts_by_phone + const view = sinon.stub(db.medic, 'query').resolves({ + rows: [ + { + doc: { + _id: submitterId, + parent: { _id: parentId }, + }, + }, + ], + }); + sinon.stub(db.medic, 'get').withArgs('papa').resolves({ _id: parentId, type: 'contact', contact_type: 'place' }); + const saveDoc = sinon.stub(db.medic, 'post').resolves(); + + const eventConfig = { + form: 'R', + events: [{ name: 'on_create', trigger: 'add_patient' }], + }; + config.get.returns([eventConfig]); + sinon.stub(validation, 'validate').resolves(null); + sinon.stub(utils, 'getRegistrations').resolves([]); + sinon.stub(utils, 'getForm').returns(formDef); + sinon.stub(transitionUtils, 'getUniqueId').resolves(patientId); + config.getAll.returns(settings); + + return transition.onMatch(change).then(() => { + getContactUuid.callCount.should.equal(1); + view.callCount.should.equal(1); + view.args[0][0].should.equal('medic-client/contacts_by_phone'); + view.args[0][1].key.should.equal(senderPhoneNumber); + view.args[0][1].include_docs.should.equal(true); + saveDoc.callCount.should.equal(1); + saveDoc.args[0][0].name.should.equal(patientName); + (typeof saveDoc.args[0][0].phone).should.be.equal('undefined'); + saveDoc.args[0][0].parent._id.should.equal(parentId); + saveDoc.args[0][0].reported_date.should.equal(53); + saveDoc.args[0][0].type.should.equal('person'); + saveDoc.args[0][0].patient_id.should.equal(patientId); + saveDoc.args[0][0].date_of_birth.should.equal(dob); + saveDoc.args[0][0].source_id.should.equal(reportId); + saveDoc.args[0][0].created_by.should.equal(submitterId); + }); + }); + it('does nothing when patient already added', () => { const patientId = '05649'; const change = { From 325efed518d21067412a83704af72256b35c86f8 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Thu, 10 Aug 2023 23:27:03 +0545 Subject: [PATCH 51/56] Code review feedbacks and added unit test for getphonefield function in registration.js --- .../src/transitions/registration.js | 18 +++--- .../test/unit/transitions/registration.js | 57 +++++++++++++++++-- .../sentinel/transitions/registration.spec.js | 30 +++++----- 3 files changed, 73 insertions(+), 32 deletions(-) diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 0802d690324..9ac56abb263 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -43,18 +43,13 @@ const getNameField = (params, prefix) => { const getPatientPhoneField = (currentForm) => { const formDef = utils.getForm(currentForm); - if (!formDef) { + if (!formDef?.fields) { return; } - - if (formDef && formDef.fields) { - // Get the phone field i.e.the field with type phone_number in the form - const phoneField = (Object.keys(formDef.fields).filter(key => formDef.fields[key].type === 'phone_number')); - // Return the phone field - if (phoneField && phoneField[0]) { - return phoneField[0]; - } - } + + return Object + .keys(formDef.fields) + .find(key => formDef.fields[key].type === 'phone_number'); }; const parseParams = params => { @@ -526,7 +521,8 @@ const addPatient = (options) => { } if (patientPhoneField && doc.fields[patientPhoneField]) { - if (!phoneNumberParser.validate(config.getAll(), (doc.fields[patientPhoneField]))) { + const patientPhone = doc.fields[patientPhoneField]; + if (!phoneNumberParser.validate(config.getAll(), patientPhone)) { transitionUtils.addRejectionMessage(doc, options.registrationConfig, 'provided_phone_not_valid'); return; } diff --git a/shared-libs/transitions/test/unit/transitions/registration.js b/shared-libs/transitions/test/unit/transitions/registration.js index 5e2d4321e30..5ab4b2ad0e5 100644 --- a/shared-libs/transitions/test/unit/transitions/registration.js +++ b/shared-libs/transitions/test/unit/transitions/registration.js @@ -65,6 +65,55 @@ describe('registration', () => { }); }); + describe('getPatientPhoneField', () => { + beforeEach(() => { + transition.getPatientPhoneField = transition.__get__('getPatientPhoneField'); + }); + + it('should return field name if form has field', () => { + const form = 'ph'; + const formDef = { + fields: { + patient_name: { + type: 'string' + }, + phone_number: { + type: 'phone_number' + } + } + }; + sinon.stub(utils, 'getForm').returns(formDef); + transition.getPatientPhoneField(form).should.equal('phone_number'); + }); + + it('should return undefined if form does not have phone_number field', () => { + const form = 'ph'; + const formDef = { + fields: { + patient_name: { + type: 'string' + } + } + }; + sinon.stub(utils, 'getForm').returns(formDef); + (typeof transition.getPatientPhoneField(form)).should.equal('undefined'); + }); + + it('should return undefined if form is not found', () => { + const form = 'ph'; + sinon.stub(utils, 'getForm').returns(undefined); + (typeof transition.getPatientPhoneField(form)).should.equal('undefined'); + }); + + it('should return undefined if form has no fields', () => { + const form = 'ph'; + const formDef = { + }; + sinon.stub(utils, 'getForm').returns(formDef); + (typeof transition.getPatientPhoneField(form)).should.equal('undefined'); + }); + }); + describe('addPatient', () => { it('trigger creates a new patient', () => { const patientName = 'jack'; @@ -127,7 +176,7 @@ describe('registration', () => { }); }); - it('add_patient trigger only creates a new patient with phone if form has phone field and phone is valid', () => { + it('should only create a new patient with phone if form has phone field and phone is valid', () => { // Form with phone field const formDef = { fields: { @@ -205,7 +254,7 @@ describe('registration', () => { }); }); - it('add_patient trigger should not create patient if form has phone field and phone is invalid', () => { + it('should not create patient if form has phone field and phone is invalid', () => { // Form with phone field const formDef = { fields: { @@ -219,7 +268,7 @@ describe('registration', () => { }; // Stubbing that phone is invalid sinon.stub(phoneNumberParser, 'validate').returns(false); - + const patientName = 'jack'; const submitterId = 'abc'; const parentId = 'papa'; @@ -271,7 +320,7 @@ describe('registration', () => { }); }); - it('add_patient trigger doesnot add patient phone if form does not have phone field', () => { + it('should not add patient phone if form does not have phone field', () => { // Form without phone field const formDef = { fields: { diff --git a/tests/integration/sentinel/transitions/registration.spec.js b/tests/integration/sentinel/transitions/registration.spec.js index b638882cbed..c137afa9029 100644 --- a/tests/integration/sentinel/transitions/registration.spec.js +++ b/tests/integration/sentinel/transitions/registration.spec.js @@ -99,7 +99,7 @@ describe('registration', () => { afterEach(() => utils.revertDb(getIds(contacts), true)); it('should add valid phone to patient doc', () => { - const patient_phone = '+9779841123123'; + const patientPhone = '+9779841123123'; const patientNameAndPhone = { // has just the `patient_name`and phone so should create this person _id: uuid(), type: 'data_record', @@ -107,7 +107,7 @@ describe('registration', () => { from: '+9779841212345', fields: { patient_name: 'Minerva', - phone_number: patient_phone + phone_number: patientPhone }, reported_date: moment().valueOf(), contact: { @@ -122,7 +122,7 @@ describe('registration', () => { let newPatientId; return utils .updateSettings(testForm.forms.NP, 'sentinel') - .then(utils.saveDocs(docs)) + .then(() => utils.saveDocs(docs)) .then(() => sentinelUtils.waitForSentinel(docIds)) .then(() => sentinelUtils.getInfoDocs(docIds)) .then(infos => { @@ -132,7 +132,7 @@ describe('registration', () => { }) .then(() => utils.getDocs(docIds)) .then(updated => { - chai.expect(updated[0].fields.phone_number).to.equal(patient_phone); + chai.expect(updated[0].fields.phone_number).to.equal(patientPhone); chai.expect(updated[0].patient_id).not.to.equal(undefined); chai.expect(updated[0].tasks).to.have.lengthOf(1); chai.expect(updated[0].tasks[0].messages[0]).to.include({ @@ -147,7 +147,7 @@ describe('registration', () => { .then(patients => { chai.expect(patients.rows[0].doc).to.deep.include({ patient_id: newPatientId, - phone: patient_phone, + phone: patientPhone, parent: { _id: 'clinic', parent: { _id: 'health_center', parent: { _id: 'district_hospital' } } }, name: 'Minerva', type: 'person', @@ -158,7 +158,7 @@ describe('registration', () => { }); it('should not create patient from report doc when provided invalid phone', () => { - const patient_phone = '+97796666'; + const patientPhone = '+97796666'; const patient_id =uuid(); const patientNameAndInvalidPhone = { // has just the `patient_name` field, and should create this person @@ -168,7 +168,7 @@ describe('registration', () => { from: '+9779841212345', fields: { patient_name: 'Minerva', - phone_number: patient_phone + phone_number: patientPhone }, reported_date: moment().valueOf(), contact: { @@ -184,7 +184,7 @@ describe('registration', () => { return utils .updateSettings(testForm.forms.NP, 'sentinel') - .then(utils.saveDocs(docs)) + .then(() => utils.saveDocs(docs)) .then(() => sentinelUtils.waitForSentinel(docIds)) .then(() => sentinelUtils.getInfoDocs(docIds)) .then(infos => { @@ -193,18 +193,18 @@ describe('registration', () => { }); }).then(() => utils.getDocs(docIds)) .then(updated => { - chai.expect(updated[0].fields.phone_number).to.equal(patient_phone); + chai.expect(updated[0].fields.phone_number).to.equal(patientPhone); chai.expect(updated[0].patient_id).to.not.be.null; const newPatientId = updated[0].patient_id; return getContactsByReference([newPatientId, 'venus']); }) .then(patients => { - chai.expect(patients[0]).to.be.undefined; + chai.expect(patients.rows.length).to.equal(0); }); }); it('should fail transition on invalid phone', () => { - const patient_phone = '+97796666'; + const patientPhone = '+97796666'; const patientNameAndInvalidPhone = { // has just the `patient_name` field, and should create this person _id: uuid(), type: 'data_record', @@ -212,7 +212,7 @@ describe('registration', () => { from: '+9779841212345', fields: { patient_name: 'Minerva', - phone_number: patient_phone + phone_number: patientPhone }, reported_date: moment().valueOf(), contact: { @@ -228,7 +228,7 @@ describe('registration', () => { return utils .updateSettings(testForm.forms.NP, 'sentinel') - .then(utils.saveDocs(docs)) + .then(() => utils.saveDocs(docs)) .then(() => sentinelUtils.waitForSentinel(docIds)) .then(() => sentinelUtils.getInfoDocs(docIds)) .then(infos => { @@ -236,10 +236,6 @@ describe('registration', () => { chai.expect(info).to.deep.nested.not.include({ 'transitions.registration.ok': false }); }); }) - .then(() => utils.getDocs(docIds)) - .then(updated => { - chai.expect(updated.patient_id).to.be.undefined; - }); }); it('should be skipped when transition is disabled', () => { From c05ab7a0c6c39fed3a4d7e19d90f7f319a086b17 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Thu, 10 Aug 2023 23:51:40 +0545 Subject: [PATCH 52/56] Upgraded eslint versoin to ecma for optional chaining support, added more tests --- shared-libs/.eslintrc | 2 +- .../test/unit/transitions/registration.js | 19 +++++++++++++++++++ .../sentinel/transitions/registration.spec.js | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/shared-libs/.eslintrc b/shared-libs/.eslintrc index 148e99b20c1..5fdb8f63d8d 100644 --- a/shared-libs/.eslintrc +++ b/shared-libs/.eslintrc @@ -3,6 +3,6 @@ "node": true }, "parserOptions": { - "ecmaVersion": 2018 + "ecmaVersion": 2020 } } diff --git a/shared-libs/transitions/test/unit/transitions/registration.js b/shared-libs/transitions/test/unit/transitions/registration.js index 5ab4b2ad0e5..fabfa3be75d 100644 --- a/shared-libs/transitions/test/unit/transitions/registration.js +++ b/shared-libs/transitions/test/unit/transitions/registration.js @@ -112,6 +112,25 @@ describe('registration', () => { sinon.stub(utils, 'getForm').returns(formDef); (typeof transition.getPatientPhoneField(form)).should.equal('undefined'); }); + + it('should return field by type not by name', () => { + const form = 'ph'; + const formDef = { + fields: { + patient_name: { + type: 'string' + }, + phone_number: { + type: 'not_phone_number_type' + }, + custom_name: { + type: 'phone_number' + } + } + }; + sinon.stub(utils, 'getForm').returns(formDef); + transition.getPatientPhoneField(form).should.equal('custom_name'); + }); }); describe('addPatient', () => { diff --git a/tests/integration/sentinel/transitions/registration.spec.js b/tests/integration/sentinel/transitions/registration.spec.js index c137afa9029..c25e52c3385 100644 --- a/tests/integration/sentinel/transitions/registration.spec.js +++ b/tests/integration/sentinel/transitions/registration.spec.js @@ -235,7 +235,7 @@ describe('registration', () => { infos.forEach(info => { chai.expect(info).to.deep.nested.not.include({ 'transitions.registration.ok': false }); }); - }) + }); }); it('should be skipped when transition is disabled', () => { From 45d74b5f49f8bae340b04dd17beaced7e03c2095 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Tue, 15 Aug 2023 14:41:19 +0545 Subject: [PATCH 53/56] Resolve commit --- api/src/services/report/smsparser.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/src/services/report/smsparser.js b/api/src/services/report/smsparser.js index e89cdb1d93a..f4dbe4b651b 100644 --- a/api/src/services/report/smsparser.js +++ b/api/src/services/report/smsparser.js @@ -221,7 +221,8 @@ const fieldParsers = { // Returning raw here becuase what to do with invalid phone // is defined in transitions so error will be thrown there if required. // Warning is logged just in case. - return raw; + return raw; + }, bsYear: (raw) => { return standardiseDigits(raw); }, From c5eacafd8eafc49cfac174de725178f77f08d14c Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Tue, 15 Aug 2023 15:46:58 +0545 Subject: [PATCH 54/56] Aded nepali phone number support --- api/src/services/report/smsparser.js | 1 + .../mocha/services/report/smsparser.spec.js | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/api/src/services/report/smsparser.js b/api/src/services/report/smsparser.js index f4dbe4b651b..52acb81b3bd 100644 --- a/api/src/services/report/smsparser.js +++ b/api/src/services/report/smsparser.js @@ -212,6 +212,7 @@ const fieldParsers = { return parseNum(stripInvisibleCharacters(raw)); }, phone_number: (raw) => { + raw = standardiseDigits(raw); const formattedAndValidatedPhone = phoneNumberParser.normalize(config.getAll(), raw); if (formattedAndValidatedPhone) { return formattedAndValidatedPhone; diff --git a/api/tests/mocha/services/report/smsparser.spec.js b/api/tests/mocha/services/report/smsparser.spec.js index 07318fca1df..5d2eccecda4 100644 --- a/api/tests/mocha/services/report/smsparser.spec.js +++ b/api/tests/mocha/services/report/smsparser.spec.js @@ -122,6 +122,42 @@ describe('sms parser', () => { chai.expect(data.phone_number).to.equal('+9779841202020'); }); + it('accepts valid phone number in Nepali langauge and saves by converting it to english', () => { + const doc = { message: 'NP 20 ९८४१२३२३२३' }; + const def = definitions.forms.NP; + sinon.stub(config, 'getAll').returns({ + default_country_code: 977, + phone_validation: 'full' + }); + const data = smsparser.parse(def, doc); + chai.expect(data.phone_number).to.equal('+9779841232323'); + }); + + it('accepts valid phone number with extension in Nepali langauge', () => { + const doc = { message: 'NP 20 +९७७९८४१२३२३२३' }; + const def = definitions.forms.NP; + sinon.stub(config, 'getAll').returns({ + default_country_code: 977, + phone_validation: 'full' + }); + const data = smsparser.parse(def, doc); + chai.expect(data.phone_number).to.equal('+9779841232323'); + }); + + it('returns provided Nepali number converted ot english if number is invalid', () => { + // Looks counter intuitive but validation needs to be done agian in transition so it can + // act against the valid or invalid result. Warning is logged just in case. + const doc = { message: 'NP 20 ९८४१२३' }; + const def = definitions.forms.NP; + sinon.stub(config, 'getAll').returns({ + default_country_code: 977, + phone_validation: 'full' + }); + const data = smsparser.parse(def, doc); + chai.expect(data.phone_number).to.equal('984123'); + }); + + it('accepts correct phone number without extension', () => { const doc = { message: 'NP 20 9841202020' }; const def = definitions.forms.NP; From 49a05b3c8f0e7cc8f35be0b582172ebf536bb854 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Tue, 15 Aug 2023 16:36:23 +0545 Subject: [PATCH 55/56] used const value instead or reading from object --- shared-libs/transitions/src/transitions/registration.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-libs/transitions/src/transitions/registration.js b/shared-libs/transitions/src/transitions/registration.js index 6a0c79dc173..08ddb0df43d 100644 --- a/shared-libs/transitions/src/transitions/registration.js +++ b/shared-libs/transitions/src/transitions/registration.js @@ -526,7 +526,7 @@ const addPatient = (options) => { transitionUtils.addRejectionMessage(doc, options.registrationConfig, 'provided_phone_not_valid'); return; } - patient.phone = doc.fields[patientPhoneField]; + patient.phone = patientPhone; } // assign patient in doc with full parent doc - to be used in messages From 9e3116f843f9961f11c3f198a739c6b2bc349641 Mon Sep 17 00:00:00 2001 From: PrjShrestha Date: Wed, 16 Aug 2023 10:58:02 +0545 Subject: [PATCH 56/56] Adding comment to trigger build run --- api/src/services/report/smsparser.js | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/services/report/smsparser.js b/api/src/services/report/smsparser.js index 52acb81b3bd..ae47ee51a1f 100644 --- a/api/src/services/report/smsparser.js +++ b/api/src/services/report/smsparser.js @@ -212,6 +212,7 @@ const fieldParsers = { return parseNum(stripInvisibleCharacters(raw)); }, phone_number: (raw) => { + //standardiseDigits ensures that Nepali digits are also supported. raw = standardiseDigits(raw); const formattedAndValidatedPhone = phoneNumberParser.normalize(config.getAll(), raw); if (formattedAndValidatedPhone) {