diff --git a/README.md b/README.md
index 99e8e3cb4d..1de7918a54 100644
--- a/README.md
+++ b/README.md
@@ -65,28 +65,42 @@ Resources to build the following docker images can be found in the [package](/pa
## Prevent Search Engines from Indexing the homepage
+
>⚠️ : Search Engines are able to index the Bahmni App homepage.
This behaviour can be prevented by:
-1. **Adding a “noindex” metatag:**
-
+1. **Adding a “noindex” metatag:**
+
The following tag should be inserted in the `
` section of the homepage's HTML markup:
+
```
```
+
Additionally, in order to both _de-index_ the homepage and not follow the links, use the `noindex` with the `nofollow` metatag:
+
```
```
- > The same is already done [here](https://github.com/Bahmni/openmrs-module-bahmniapps/blob/master/package/docker/index.html#L5)
+ > The same is already done [here](https://github.com/Bahmni/openmrs-module-bahmniapps/blob/master/package/docker/index.html#L5)
+ >
2. **Using an X-ROBOTS-TAG HTTP HEADER:**
An `X-Robots-Tag` can be added to the HTTP response header. It has basically the same effect as a `noindex` tag, but with the additional options to specify conditions for different search engines. For more information, please see Google’s guide [here](https://developers.google.com/search/docs/advanced/robots/robots_meta_tag). Here are examples of X-Robots-Tag for specific functions:
-
+
- To de-index a web page:
+
```
Header set X-Robots-Tag "noindex, nofollow"
```
+
+ > The same is already done [here](https://github.com/Bahmni/openmrs-module-bahmniapps/blob/master/package/docker/httpd.conf#L32)
+ >
+
+### SNOMED Integration Support
+
+openmrs-module-bahmniapps also integrates with SNOMED for terminology lookup and CDSS. More details can be found in [this](https://bahmni.atlassian.net/wiki/spaces/BAH/pages/3132686337/SNOMED+FHIR+Terminology+Server+Integration+with+Bahmni) Wiki link
+=======
> The same is already done [here](https://github.com/Bahmni/openmrs-module-bahmniapps/blob/master/package/docker/httpd.conf#L32)
diff --git a/ui/app/clinical/common/models/drugOrderViewModel.js b/ui/app/clinical/common/models/drugOrderViewModel.js
index 8b10fdb50b..496ef8b1bf 100644
--- a/ui/app/clinical/common/models/drugOrderViewModel.js
+++ b/ui/app/clinical/common/models/drugOrderViewModel.js
@@ -707,6 +707,7 @@ Bahmni.Clinical.DrugOrderViewModel.createFromContract = function (drugOrderRespo
};
}
viewModel.instructions = administrationInstructions.instructions;
+ viewModel.audit = drugOrderResponse.audit;
viewModel.additionalInstructions = administrationInstructions.additionalInstructions;
viewModel.quantity = drugOrderResponse.dosingInstructions.quantity;
viewModel.quantityUnit = drugOrderResponse.dosingInstructions.quantityUnits;
diff --git a/ui/app/clinical/consultation/controllers/addTreatmentController.js b/ui/app/clinical/consultation/controllers/addTreatmentController.js
index 56405c2481..0b63395f8d 100644
--- a/ui/app/clinical/consultation/controllers/addTreatmentController.js
+++ b/ui/app/clinical/consultation/controllers/addTreatmentController.js
@@ -15,6 +15,8 @@ angular.module('bahmni.clinical')
$scope.addTreatment = true;
$scope.canOrderSetBeAdded = true;
$scope.isSearchDisabled = false;
+ $scope.cdssEnabled = false;
+ $scope.conceptSource = localStorage.getItem("conceptSource") || "";
$scope.getFilteredOrderSets = function (searchTerm) {
if (searchTerm && searchTerm.length >= 3) {
@@ -56,7 +58,7 @@ angular.module('bahmni.clinical')
$scope.showDoseFractions = treatmentConfig.inputOptionsConfig.showDoseFractions;
$scope.isDoseFractionsAvailable = function () {
- return $scope.doseFractions && !_.isEmpty($scope.doseFractions) ? true : false;
+ return !!($scope.doseFractions && !_.isEmpty($scope.doseFractions));
};
$scope.isSelected = function (drug) {
@@ -68,6 +70,24 @@ angular.module('bahmni.clinical')
$scope.onSelect($scope.treatment.selectedItem);
};
+ $scope.auditOptions = function () {
+ return appService
+ .getAppDescriptor()
+ .getConfigValue('cdssDismissalOptionsToDisplay');
+ };
+
+ $scope.submitAudit = function (index) {
+ var patientUuid = $scope.patient.uuid;
+ var message = $scope.cdssaAlerts[index].summary.replace(/"/g, '');
+ var eventType = 'Dismissed: ' + $scope.treatment.audit;
+ $scope.cdssaAlerts.splice(index, 1);
+ return drugService
+ .cdssAudit(patientUuid, eventType, message, 'CDSS')
+ .then(function () {
+ $scope.treatment.audit = '';
+ });
+ };
+
var markVariable = function (variable) {
$scope[variable] = true;
$timeout(function () {
@@ -127,6 +147,12 @@ angular.module('bahmni.clinical')
durationUnit: treatment.durationUnit
};
};
+ var getCdssEnabled = function () {
+ drugService.getCdssEnabled().then(function (response) {
+ $scope.cdssEnabled = response.data;
+ });
+ };
+ getCdssEnabled();
var isSameDrugBeingDiscontinuedAndOrdered = function () {
var existingTreatment = false;
@@ -188,9 +214,13 @@ angular.module('bahmni.clinical')
_.each(refilledOrderGroupOrders, function (drugOrder) {
_.each(orderSetMembersOfMatchedOrderSet, function (orderSetMember) {
if (orderSetMember.orderTemplate.drug) {
- if (orderSetMember.orderTemplate.drug.uuid === _.get(drugOrder, 'drug.uuid')) { matchedMembers.push(orderSetMember); }
+ if (orderSetMember.orderTemplate.drug.uuid === _.get(drugOrder, 'drug.uuid')) {
+ matchedMembers.push(orderSetMember);
+ }
} else {
- if (orderSetMember.concept.uuid === drugOrder.concept.uuid) { matchedMembers.push(orderSetMember); }
+ if (orderSetMember.concept.uuid === drugOrder.concept.uuid) {
+ matchedMembers.push(orderSetMember);
+ }
}
});
});
@@ -201,11 +231,11 @@ angular.module('bahmni.clinical')
var baseDose = eachMember.orderTemplate.dosingInstructions.dose;
var drugName = eachMember.orderTemplate.concept.name;
return orderSetService.getCalculatedDose($scope.patient.uuid, drugName, baseDose, doseUnits, $scope.newOrderSet.name)
- .then(function (calculatedDosage) {
- refilledOrderGroupOrders[index].uniformDosingType.dose = calculatedDosage.dose;
- refilledOrderGroupOrders[index].uniformDosingType.doseUnits = calculatedDosage.doseUnit;
- refilledOrderGroupOrders[index].calculateQuantityAndUnit();
- });
+ .then(function (calculatedDosage) {
+ refilledOrderGroupOrders[index].uniformDosingType.dose = calculatedDosage.dose;
+ refilledOrderGroupOrders[index].uniformDosingType.doseUnits = calculatedDosage.doseUnit;
+ refilledOrderGroupOrders[index].calculateQuantityAndUnit();
+ });
}
});
@@ -466,7 +496,10 @@ angular.module('bahmni.clinical')
var contextChange = function () {
var errorMessages = Bahmni.Clinical.Constants.errorMessages;
if (isSameDrugBeingDiscontinuedAndOrdered()) {
- return {allow: false, errorMessage: $translate.instant(errorMessages.discontinuingAndOrderingSameDrug)};
+ return {
+ allow: false,
+ errorMessage: $translate.instant(errorMessages.discontinuingAndOrderingSameDrug)
+ };
}
if ($scope.incompleteDrugOrders()) {
$scope.formInvalid = true;
@@ -482,6 +515,17 @@ angular.module('bahmni.clinical')
treatment.isBeingEdited = false;
};
+ $scope.closeAlert = function (index) {
+ $scope.cdssaAlerts = $scope.cdssaAlerts.filter(function (alert, alertIndex) {
+ return alertIndex !== index;
+ });
+ };
+
+ $scope.toggleAlertDetails = function (index) {
+ $scope.cdssaAlerts[index].showDetails =
+ !$scope.cdssaAlerts[index].showDetails;
+ };
+
$scope.getDataResults = function (drugs) {
var searchString = $scope.treatment.drugNameDisplay;
var listOfDrugSynonyms = _.map(drugs, function (drug) {
@@ -489,12 +533,175 @@ angular.module('bahmni.clinical')
});
return _.flatten(listOfDrugSynonyms);
};
+ var createMedicationRequest = function (medication, patientUuid) {
+ return extractCodeInfo(medication).then(function (coding) {
+ var medicationRequest = {
+ resourceType: 'MedicationRequest',
+ id: medication.uuid,
+ status: 'active',
+ intent: 'order',
+ subject: {
+ reference: 'Patient/' + patientUuid
+ },
+ medicationCodeableConcept: {
+ id: medication.drug.uuid,
+ coding: [
+ coding
+ ],
+ text: medication.drugNameDisplay
+ }
+ };
+ return {
+ resource: medicationRequest
+ };
+ });
+ };
+ var extractCodeInfo = function (medication) {
+ if (!(medication.drug.drugReferenceMaps && medication.drug.drugReferenceMaps.length > 0)) {
+ return Promise.resolve({
+ code: medication.drug.uuid,
+ display: medication.drug.name
+ });
+ } else {
+ var drugReferenceMap = medication.drug.drugReferenceMaps[0];
+ if (!$scope.conceptSource) {
+ return drugService.getDrugConceptSourceMapping(medication.drug.uuid).then(function (response) {
+ var bundle = response.data;
+ var code = bundle.entry && bundle.entry.length > 0 && bundle.entry[0].resource.code;
+ var conceptCode = code.coding.find(function (coding) {
+ return coding.system;
+ });
+ if (conceptCode) {
+ localStorage.setItem("conceptSource", conceptCode.system);
+ $scope.conceptSource = conceptCode.system;
+ return {
+ system: $scope.conceptSource,
+ code: drugReferenceMap.conceptReferenceTerm && drugReferenceMap.conceptReferenceTerm.display && drugReferenceMap.conceptReferenceTerm.display.split(':')[1].trim(),
+ display: medication.drug.name
+ };
+ } else {
+ return {
+ code: medication.drug.uuid,
+ display: medication.drug.name
+ };
+ }
+ });
+ } else {
+ return Promise.resolve({
+ system: $scope.conceptSource,
+ code: drugReferenceMap.conceptReferenceTerm && drugReferenceMap.conceptReferenceTerm.display && drugReferenceMap.conceptReferenceTerm.display.split(':')[1].trim(),
+ display: medication.drug.name
+ });
+ }
+ }
+ };
+
+ var createConditionResource = function (condition, patientUuid, isDiagnosis) {
+ var conceptLimitIndex = isDiagnosis ? -1 : condition.concept.uuid.lastIndexOf('/');
+ var conditionStatus = condition.status || condition.certainty;
+ var activeConditions = ['CONFIRMED', 'PRESUMED', 'ACTIVE'];
+ var status = activeConditions.indexOf(conditionStatus) > -1 ? 'active' : 'inactive';
+ var conditionResource = {
+ resourceType: 'Condition',
+ id: condition.uuid,
+ clinicalStatus: {
+ coding: [
+ {
+ code: status,
+ display: status.charAt(0).toUpperCase() + status.slice(1),
+ system: 'http://terminology.hl7.org/CodeSystem/condition-clinical'
+ }
+ ]
+ },
+ code: {
+ coding: [
+ {
+ system: isDiagnosis ? condition.codedAnswer.conceptSystem : (conceptLimitIndex > -1 ? (condition.concept.uuid.substring(0, conceptLimitIndex) || '') : ''),
+ code: isDiagnosis ? condition.codedAnswer.uuid : (conceptLimitIndex > -1 ? condition.concept.uuid.substring(conceptLimitIndex + 1) : condition.concept.uuid),
+ display: isDiagnosis ? condition.codedAnswer.name : condition.concept.name
+ }
+ ],
+ text: isDiagnosis ? condition.codedAnswer.name : condition.concept.name
+ },
+ subject: {
+ reference: 'Patient/' + patientUuid
+ }
+ };
+ if (angular.isNumber(condition.onSetDate) === 'number') {
+ conditionResource.onsetDateTime = new Date(condition.onSetDate).toLocaleDateString('en-CA');
+ }
+ if (!conditionResource.onsetDateTime) {
+ delete conditionResource.onsetDateTime;
+ }
+ return {
+ resource: conditionResource
+ };
+ };
+
+ $scope.createFhirBundle = function (patient, conditions, medications, diagnosis) {
+ var encounterResource = conditions.filter(function (condition) {
+ return !condition.uuid;
+ }).map(function (condition) {
+ return createConditionResource(condition, patient.uuid, false);
+ });
+ encounterResource = encounterResource.concat(diagnosis.map(function (condition) {
+ return createConditionResource(condition, patient.uuid, true);
+ }));
+
+ return Promise.all(medications.map(function (medication) {
+ return createMedicationRequest(medication, patient.uuid).then(function (medicationResource) {
+ return medicationResource;
+ });
+ })).then(function (medicationResources) {
+ return {
+ resourceType: 'Bundle',
+ type: 'collection',
+ entry: [].concat(encounterResource, medicationResources)
+ };
+ });
+ };
+
+ var createParams = function (consultationData) {
+ var patient = consultationData.patient;
+ var conditions = consultationData.conditions;
+ var diagnosis = consultationData.newlyAddedDiagnoses;
+ var medications = consultationData.draftDrug;
+ return {
+ patient: patient,
+ conditions: conditions,
+ diagnosis: diagnosis,
+ medications: medications
+ };
+ };
+
+ function sortInteractionsByStatus (arr) {
+ var order = { "critical": 0, "warning": 1, "info": 2 };
+ return arr.sort(function (a, b) {
+ return order[a.indicator] - order[b.indicator];
+ });
+ }
(function () {
var selectedItem;
$scope.onSelect = function (item) {
selectedItem = item;
$scope.onChange();
+ if ($scope.cdssEnabled) {
+ var consultationData = angular.copy($scope.consultation);
+ consultationData.patient = $scope.patient;
+
+ consultationData.draftDrug = [$scope.treatment].concat(
+ consultationData.newlyAddedTabTreatments ? consultationData.newlyAddedTabTreatments.allMedicationTabConfig.treatments : []
+ );
+ var params = createParams(consultationData);
+ $scope.createFhirBundle(params.patient, params.conditions, params.medications, params.diagnosis)
+ .then(function (bundle) {
+ var cdssaAlerts = drugService.sendDiagnosisDrugBundle(bundle);
+ cdssaAlerts.then(function (response) {
+ $scope.cdssaAlerts = sortInteractionsByStatus(response.data);
+ });
+ });
+ }
};
$scope.onAccept = function () {
$scope.treatment.acceptedItem = $scope.treatment.drugNameDisplay;
@@ -508,7 +715,8 @@ angular.module('bahmni.clinical')
$scope.treatment.changeDrug({
name: selectedItem.drug.name,
form: selectedItem.drug.dosageForm && selectedItem.drug.dosageForm.display,
- uuid: selectedItem.drug.uuid
+ uuid: selectedItem.drug.uuid,
+ drugReferenceMaps: selectedItem.drug.drugReferenceMaps
});
selectedItem = null;
return;
@@ -521,12 +729,14 @@ angular.module('bahmni.clinical')
return;
}
delete $scope.treatment.drug;
+ $scope.cdssaAlerts = [];
};
})();
$scope.clearForm = function () {
$scope.treatment = newTreatment();
$scope.formInvalid = false;
+ $scope.cdssaAlerts = [];
clearHighlights();
markVariable("startNewDrugEntry");
};
@@ -553,7 +763,7 @@ angular.module('bahmni.clinical')
};
$scope.toggleDrugOrderAttribute = function (orderAttribute) {
- orderAttribute.value = orderAttribute.value ? false : true;
+ orderAttribute.value = !orderAttribute.value;
};
contextChangeHandler.add(contextChange);
@@ -644,7 +854,9 @@ angular.module('bahmni.clinical')
$scope.newOrderSet.name = orderSet.name;
var orderSetMemberTemplates = _.map(orderSet.orderSetMembers, 'orderTemplate');
var promisesToCalculateDose = _.map(orderSetMemberTemplates, putCalculatedDose);
- var returnOrderSet = function () { return orderSet; };
+ var returnOrderSet = function () {
+ return orderSet;
+ };
return $q.all(promisesToCalculateDose).then(returnOrderSet);
};
var createDrugOrderViewModel = function (orderTemplate) {
diff --git a/ui/app/clinical/consultation/controllers/diagnosisController.js b/ui/app/clinical/consultation/controllers/diagnosisController.js
index 89a6032031..2fda0d49dd 100644
--- a/ui/app/clinical/consultation/controllers/diagnosisController.js
+++ b/ui/app/clinical/consultation/controllers/diagnosisController.js
@@ -143,11 +143,13 @@ angular.module('bahmni.clinical')
value: concept.matchedName || concept.conceptName,
concept: {
name: concept.conceptName,
- uuid: concept.conceptUuid
+ uuid: concept.conceptUuid,
+ conceptSystem: concept.conceptSystem
},
lookup: {
name: concept.matchedName || concept.conceptName,
- uuid: concept.conceptUuid
+ uuid: concept.conceptUuid,
+ conceptSystem: concept.conceptSystem
}
};
@@ -163,6 +165,8 @@ angular.module('bahmni.clinical')
$scope.getAddConditionMethod = function () {
return function (item) {
+ var conceptSystem = item.lookup.conceptSystem ? item.lookup.conceptSystem + "/" : "";
+ item.lookup.uuid = conceptSystem + item.lookup.uuid;
$scope.consultation.condition.concept.uuid = item.lookup.uuid;
item.value = $scope.consultation.condition.concept.name = item.lookup.name;
};
@@ -270,9 +274,9 @@ angular.module('bahmni.clinical')
};
$scope.cleanOutDiagnosisList = function (allDiagnoses) {
- return allDiagnoses.filter(function (diagnosis) {
+ return allDiagnoses ? allDiagnoses.filter(function (diagnosis) {
return !alreadyAddedToDiagnosis(diagnosis);
- });
+ }) : [];
};
var alreadyAddedToDiagnosis = function (diagnosis) {
diff --git a/ui/app/clinical/consultation/mappers/encounterTransactionMapper.js b/ui/app/clinical/consultation/mappers/encounterTransactionMapper.js
index 97ef4ea65e..9063a9584a 100644
--- a/ui/app/clinical/consultation/mappers/encounterTransactionMapper.js
+++ b/ui/app/clinical/consultation/mappers/encounterTransactionMapper.js
@@ -41,8 +41,9 @@ Bahmni.Clinical.EncounterTransactionMapper = function () {
if (consultation.newlyAddedDiagnoses && consultation.newlyAddedDiagnoses.length > 0) {
encounterData.bahmniDiagnoses = consultation.newlyAddedDiagnoses.map(function (diagnosis) {
+ var conceptSystem = diagnosis.codedAnswer.conceptSystem ? diagnosis.codedAnswer.conceptSystem + "/" : "";
return {
- codedAnswer: { uuid: !diagnosis.isNonCodedAnswer ? diagnosis.codedAnswer.uuid : undefined},
+ codedAnswer: { uuid: !diagnosis.isNonCodedAnswer ? conceptSystem + diagnosis.codedAnswer.uuid : undefined},
freeTextAnswer: diagnosis.isNonCodedAnswer ? diagnosis.codedAnswer.name : undefined,
order: diagnosis.order,
certainty: diagnosis.certainty,
diff --git a/ui/app/clinical/consultation/models/drugSearchResult.js b/ui/app/clinical/consultation/models/drugSearchResult.js
index 48bca0d935..f0bf48f9c4 100644
--- a/ui/app/clinical/consultation/models/drugSearchResult.js
+++ b/ui/app/clinical/consultation/models/drugSearchResult.js
@@ -18,7 +18,9 @@ Bahmni.Clinical.DrugSearchResult = (function () {
var getMatcher = function (searchString) {
return function (value) {
// return value.search(new RegExp(searchString, "i")) !== -1
- return _.includes(value.toLowerCase(), searchString.toLowerCase());
+ return _.every(searchString.split(" "), function (word) {
+ return _.includes(value.toLowerCase(), word.toLowerCase());
+ });
};
};
var getSynonymCreator = function (drug) {
diff --git a/ui/app/clinical/consultation/views/treatmentSections/addTreatment.html b/ui/app/clinical/consultation/views/treatmentSections/addTreatment.html
index 6b4356fba7..433feded7c 100644
--- a/ui/app/clinical/consultation/views/treatmentSections/addTreatment.html
+++ b/ui/app/clinical/consultation/views/treatmentSections/addTreatment.html
@@ -259,9 +259,54 @@