diff --git a/packages/openchs-android/package-lock.json b/packages/openchs-android/package-lock.json index 722b8266c..a4be8d02f 100644 --- a/packages/openchs-android/package-lock.json +++ b/packages/openchs-android/package-lock.json @@ -19,7 +19,7 @@ "@react-native-firebase/analytics": "15.2.0", "@react-native-firebase/app": "15.2.0", "amazon-cognito-identity-js": "6.3.1", - "avni-health-modules": "^0.0.19", + "avni-health-modules": "^0.0.22", "base-64": "^1.0.0", "bugsnag-react-native": "2.23.10", "color": "4.2.3", @@ -33,7 +33,7 @@ "lodash": "4.17.21", "moment": "2.29.4", "native-base": "3.4.9", - "openchs-models": "1.30.86", + "openchs-models": "1.30.88", "prop-types": "15.8.1", "react": "18.2.0", "react-native": "0.72.3", @@ -5754,9 +5754,9 @@ } }, "node_modules/avni-health-modules": { - "version": "0.0.19", - "resolved": "https://registry.npmjs.org/avni-health-modules/-/avni-health-modules-0.0.19.tgz", - "integrity": "sha512-Q0JmKC/K5uIIWP9nmmKozF/EmoKG9bviVfUfYl2OFoh/zRqYZ4stRLwlGn70APBIOuyfiB1GGHsQpXmSxFXjPg==", + "version": "0.0.22", + "resolved": "https://registry.npmjs.org/avni-health-modules/-/avni-health-modules-0.0.22.tgz", + "integrity": "sha512-GzKDk8c22Eucx4gB9SdY8h4kSg/jV8T8tCOiVKnSbIyzcozJfWH7oEHNJ03p3O0kfasdKrsIZNOtQNwFG9wxcw==", "peerDependencies": { "lodash": "*", "moment": "*", @@ -16431,9 +16431,9 @@ } }, "node_modules/openchs-models": { - "version": "1.30.86", - "resolved": "https://registry.npmjs.org/openchs-models/-/openchs-models-1.30.86.tgz", - "integrity": "sha512-brNeVZqLvQqvtaCjtfu84OW6kNfgxjgJaE0nL4kTso0CtYPWmAXauCfFfaN+QdoiFs0fBHeUm8i9+571Zz/fig==", + "version": "1.30.88", + "resolved": "https://registry.npmjs.org/openchs-models/-/openchs-models-1.30.88.tgz", + "integrity": "sha512-ZHiQuPGyz+f7cf+jSQCti3wnGTIz53oZxI3rLMJ4H/IRMnfVQBeN14wtNlUE0UXkoItVqimL7fVog2q4apbY/g==", "peerDependencies": { "lodash": "*", "moment": "*" @@ -26863,9 +26863,9 @@ "dev": true }, "avni-health-modules": { - "version": "0.0.19", - "resolved": "https://registry.npmjs.org/avni-health-modules/-/avni-health-modules-0.0.19.tgz", - "integrity": "sha512-Q0JmKC/K5uIIWP9nmmKozF/EmoKG9bviVfUfYl2OFoh/zRqYZ4stRLwlGn70APBIOuyfiB1GGHsQpXmSxFXjPg==" + "version": "0.0.22", + "resolved": "https://registry.npmjs.org/avni-health-modules/-/avni-health-modules-0.0.22.tgz", + "integrity": "sha512-GzKDk8c22Eucx4gB9SdY8h4kSg/jV8T8tCOiVKnSbIyzcozJfWH7oEHNJ03p3O0kfasdKrsIZNOtQNwFG9wxcw==" }, "aws-sdk": { "version": "2.1258.0", @@ -35131,9 +35131,9 @@ } }, "openchs-models": { - "version": "1.30.86", - "resolved": "https://registry.npmjs.org/openchs-models/-/openchs-models-1.30.86.tgz", - "integrity": "sha512-brNeVZqLvQqvtaCjtfu84OW6kNfgxjgJaE0nL4kTso0CtYPWmAXauCfFfaN+QdoiFs0fBHeUm8i9+571Zz/fig==" + "version": "1.30.88", + "resolved": "https://registry.npmjs.org/openchs-models/-/openchs-models-1.30.88.tgz", + "integrity": "sha512-ZHiQuPGyz+f7cf+jSQCti3wnGTIz53oZxI3rLMJ4H/IRMnfVQBeN14wtNlUE0UXkoItVqimL7fVog2q4apbY/g==" }, "opencollective-postinstall": { "version": "2.0.3", diff --git a/packages/openchs-android/package.json b/packages/openchs-android/package.json index f99024e6b..3d8edb011 100644 --- a/packages/openchs-android/package.json +++ b/packages/openchs-android/package.json @@ -57,7 +57,7 @@ "lodash": "4.17.21", "moment": "2.29.4", "native-base": "3.4.9", - "openchs-models": "1.30.88", + "openchs-models": "1.30.89", "prop-types": "15.8.1", "react": "18.2.0", "react-native": "0.72.3", diff --git a/packages/openchs-android/src/service/IndividualService.js b/packages/openchs-android/src/service/IndividualService.js index dc01a4083..e4675c0a6 100644 --- a/packages/openchs-android/src/service/IndividualService.js +++ b/packages/openchs-android/src/service/IndividualService.js @@ -592,19 +592,21 @@ class IndividualService extends BaseService { nextScheduledVisits.map(nsv => { if ((!_.isEmpty(nsv.subjectUUID) && individual.uuid !== nsv.subjectUUID) || (!_.isEmpty(nsv.programEnrolment) && individual.uuid !== nsv.programEnrolment.individual.uuid)) { + const subject = !_.isEmpty(nsv.programEnrolment) ? nsv.programEnrolment.individual : this.findByUUID(nsv.subjectUUID); try { - const subject = !_.isEmpty(nsv.programEnrolment) ? nsv.programEnrolment.individual : this.findByUUID(nsv.subjectUUID); if (_.isEmpty(subject)) { throw Error(`Attempted to schedule visit for non-existent subject with uuid ${nsv.subjectUUID}`) } if (!this.unVoided(subject)) { - throw Error(`Attempted to schedule visit for voided subject with uuid: ${nsv.subjectUUID}`); + throw Error(`Attempted to schedule visit for voided subject with uuid: ${subject.uuid}`); } nsv.subject = subject; filteredNextScheduledVisits.push(nsv); } catch (e) { General.logDebug("Rule-Failure", `Error while saving visit schedule for other subject: ${nsv.subjectUUID}`); - this.getService(RuleEvaluationService).saveFailedRules(e, nsv.subjectUUID, individual.uuid); + const subjectTypeUUID = _.isEmpty(subject) ? individual.subjectType.uuid : subject.subjectType.uuid; + this.getService(RuleEvaluationService).saveFailedRules(e, subjectTypeUUID, individual.uuid, + 'VisitSchedule', individual.subjectType.uuid, !_.isEmpty(nsv.programEnrolment) ? 'ProgramEncounter':'Encounter', nsv.uuid); } } else { //not setting nsv.subject here to differentiate these from visits being scheduled for other subjects diff --git a/packages/openchs-android/src/service/RuleEvaluationService.js b/packages/openchs-android/src/service/RuleEvaluationService.js index 4749c66f8..1216d2f29 100644 --- a/packages/openchs-android/src/service/RuleEvaluationService.js +++ b/packages/openchs-android/src/service/RuleEvaluationService.js @@ -136,7 +136,8 @@ class RuleEvaluationService extends BaseService { return trimmedDecisions; } catch (e) { General.logDebug("RuleEvaluationService",`form.uuid: ${form.uuid} entityName: ${entityName}`); - this.saveFailedRules(e, form.uuid, individualUUID); + this.saveFailedRules(e, form.uuid, individualUUID, + 'Decision', form.uuid, entityName, entity.uuid); } } else { const decisionsMap = rulesFromTheBundle.reduce((decisions, rule) => { @@ -182,7 +183,8 @@ class RuleEvaluationService extends BaseService { this.conceptService.findConcept(conceptName); return true; } catch (error) { - this.saveFailedRules(error, ruleUUID, individualUUID); + this.saveFailedRules(error, ruleUUID, individualUUID, + 'Validation', ruleUUID, 'Individual', individualUUID); return false; } } @@ -206,7 +208,8 @@ class RuleEvaluationService extends BaseService { }); } catch (e) { General.logDebug("Rule-Failure", `New worklist updation rule failed ${orgConfig.uuid} `); - this.saveFailedRules(e, orgConfig.uuid, this.getIndividualUUID(workLists, "WorkList")); + this.saveFailedRules(e, orgConfig.uuid, this.getIndividualUUID(workLists, "WorkList"), + 'WorkListUpdation', orgConfig.uuid, entityName, context.entity.uuid); } } else { const additionalRules = this.getService(RuleService).getRulesByType('WorkListUpdation'); @@ -228,28 +231,36 @@ class RuleEvaluationService extends BaseService { } } catch (error) { General.logDebug("Rule-Failure", `Rule failed: ${rule.name}, uuid: ${rule.uuid}`); - this.saveFailedRules(error, rule.uuid, this.getIndividualUUID(entity, entityName)); + this.saveFailedRules(error, rule.uuid, this.getIndividualUUID(entity, entityName), + 'Decision', rule.uuid, entityName, entity.uuid); return ruleTypeValue; } } - failedRuleExistsInDB(ruleUUID, errorMessage, individualUUID) { + failedRuleExistsInDB(ruleUUID, errorMessage, individualUUID, sourceId, entityId) { return this.getAll(RuleFailureTelemetry.schema.name) - .filtered('ruleUuid=$0 AND errorMessage=$1 AND individualUuid=$2', + .filtered('ruleUuid=$0 AND errorMessage=$1 AND individualUuid=$2 AND sourceId=$3 AND entityId=$4', ruleUUID, errorMessage, - individualUUID + individualUUID, + sourceId, + entityId ).length > 0; } - saveFailedRules(error, ruleUUID, individualUUID) { - if (!this.failedRuleExistsInDB(ruleUUID, error.message, individualUUID)) { + saveFailedRules(error, ruleUUID, individualUUID, sourceType, sourceUUID, entityType, entityUUID) { + if (!this.failedRuleExistsInDB(ruleUUID, error.message, individualUUID, sourceUUID, entityUUID)) { const entityService = this.getService(EntityService); let ruleFailureTelemetry = RuleFailureTelemetry.create({ errorMessage: error.message, stacktrace: error.stack, ruleUUID: ruleUUID, individualUUID: individualUUID, + sourceType: sourceType, + sourceId: sourceUUID, + entityType: entityType, + entityId: entityUUID, + appType: 'Android' }); entityService.saveAndPushToEntityQueue(ruleFailureTelemetry, RuleFailureTelemetry.schema.name); } @@ -264,7 +275,8 @@ class RuleEvaluationService extends BaseService { try { return this.entityRulesMap.get(entityName).getEnrolmentSummary(enrolment, context); } catch (error) { - this.saveFailedRules(error, '', this.getIndividualUUID(enrolment, entityName)); + this.saveFailedRules(error, '', this.getIndividualUUID(enrolment, entityName), + 'EnrolmentSummary', enrolment.program.uuid, entityName, enrolment.uuid); return []; } } @@ -298,7 +310,8 @@ class RuleEvaluationService extends BaseService { } catch (e) { General.logDebug("Rule-Failure", `Subject Summary Rule failed for: ${subjectType.name} Subject type`); - this.saveFailedRules(e, subjectType.uuid, this.getIndividualUUID(individual, entityName)); + this.saveFailedRules(e, subjectType.uuid, this.getIndividualUUID(individual, entityName), + 'SubjectSummary', subjectType.uuid, entityName, individual.uuid); return []; } } @@ -329,7 +342,8 @@ class RuleEvaluationService extends BaseService { } catch (e) { General.logDebug("Rule-Failure", `Subject Program Eligibility Rule failed for: ${subjectType.name} Subject type ${e.message} ${e.stack}`); - this.saveFailedRules(e, subjectType.uuid, this.getIndividualUUID(individual, 'Individual')); + this.saveFailedRules(e, subjectType.uuid, this.getIndividualUUID(individual, 'Individual'), + 'EnrolmentEligibilityCheck', subjectType.uuid, 'Individual', individual.uuid); throw Error(e.message); } } @@ -367,7 +381,8 @@ class RuleEvaluationService extends BaseService { } catch (e) { General.logDebug("Rule-Failure", `New Enrolment Summary Rule failed for: ${enrolment.program.name} program`); - this.saveFailedRules(e, enrolment.uuid, this.getIndividualUUID(enrolment, entityName)); + this.saveFailedRules(e, program.uuid, this.getIndividualUUID(enrolment, entityName), + 'EnrolmentSummary', program.uuid, entityName, enrolment.uuid); return []; } } @@ -386,8 +401,9 @@ class RuleEvaluationService extends BaseService { }); } catch (e) { console.log(e); - General.logDebug("Rule-Failure", `New enrolment decision failed for: ${form.name} form name`); - this.saveFailedRules(e, form.uuid, this.getIndividualUUID(entity, entityName)); + General.logDebug("Rule-Failure", `Validation failed for: ${form.name} form name`); + this.saveFailedRules(e, form.uuid, this.getIndividualUUID(entity, entityName), + 'Validation', form.uuid, entityName, entity.uuid); } } else if (!_.isEmpty(rulesFromTheBundle)) { const validationErrors = rulesFromTheBundle.reduce( @@ -445,7 +461,8 @@ class RuleEvaluationService extends BaseService { return allChecklists; } catch (e) { General.logDebug("Rule-Failure", `New checklist rule failed for form: ${form.uuid}`); - this.saveFailedRules(e, form.uuid, this.getIndividualUUID(entity, entityName)); + this.saveFailedRules(e, form.uuid, this.getIndividualUUID(entity, entityName), + 'Checklist', form.uuid, entityName, entity.uuid); } } else { const allChecklists = this.getAllRuleItemsFor(form, "Checklists", "Form") @@ -481,7 +498,8 @@ class RuleEvaluationService extends BaseService { }); } catch (e) { General.logDebug("Rule-Failure", `New form element group rule failed for: ${formElementGroup.uuid}`); - this.saveFailedRules(e, formElementGroup.uuid, this.getIndividualUUID(entity, entityName)); + this.saveFailedRules(e, formElementGroup.uuid, this.getIndividualUUID(entity, entityName), + 'FormElementGroup', formElementGroup.uuid, entityName, entity.uuid); } } @@ -568,7 +586,8 @@ class RuleEvaluationService extends BaseService { }); } catch (e) { General.logDebug("Rule-Failure", `New Rule failed for: ${formElement.name}`); - this.saveFailedRules(e, formElement.uuid, this.getIndividualUUID(entity, entityName)); + this.saveFailedRules(e, formElement.uuid, this.getIndividualUUID(entity, entityName), + 'FormElement', formElement.uuid, entityName, entity.uuid); return null; } } @@ -609,7 +628,8 @@ class RuleEvaluationService extends BaseService { } catch (e) { General.logDebug("Rule-Faiure", e); General.logDebug("Rule-Failure", `New encounter eligibility failed for: ${encounterType.name} encounter name`); - this.saveFailedRules(e, encounterType.uuid, this.getIndividualUUID(encounterType)); + this.saveFailedRules(e, encounterType.uuid, individual.uuid, + 'EncounterEligibilityCheck', encounterType.uuid, 'Individual', individual.uuid); } } else if (!_.isEmpty(rulesFromTheBundle)) { return this.runRuleAndSaveFailure(_.last(rulesFromTheBundle), 'Encounter', {individual}, true); @@ -631,7 +651,8 @@ class RuleEvaluationService extends BaseService { } catch (e) { General.logDebug("Rule-Failure", e); General.logDebug("Rule-Failure", `New enrolment eligibility failed for: ${program.name} program name`); - this.saveFailedRules(e, program.uuid, this.getIndividualUUID(program)); + this.saveFailedRules(e, program.uuid, individual.uuid, + 'EnrolmentEligibilityCheck', program.uuid, 'Individual', individual.uuid); } } else if (!_.isEmpty(rulesFromTheBundle)) { return this.runRuleAndSaveFailure(_.last(rulesFromTheBundle), 'Encounter', {individual}, true); @@ -651,7 +672,8 @@ class RuleEvaluationService extends BaseService { } catch (e) { General.logDebug("Rule-Failure", e); General.logDebug("Rule-Failure", `Manual enrolment eligibility failed for: ${program.name} program name`); - this.saveFailedRules(e, program.uuid, this.getIndividualUUID(program)); + this.saveFailedRules(e, program.uuid, subject.uuid, + 'ManualEnrolmentEligibilityCheckRule', program.uuid, 'Individual', subject.uuid); } } @@ -669,6 +691,8 @@ class RuleEvaluationService extends BaseService { } catch (error) { General.logError("Rule-Failure", `DashboardCard report card rule failed for uuid: ${reportCard.uuid}, name: ${reportCard.name}`); General.logError("Rule-Failure", error); + this.saveFailedRules(error, reportCard.uuid, '', + 'ReportCard', reportCard.uuid, null, null); return {primaryValue: this.I18n.t("Error"), lineListFunction: _.noop()}; } }