From ab8c5d1a7d321c939aa455bd53557c01be6fab4f Mon Sep 17 00:00:00 2001 From: anubhavBeehyv <123312249+anubhavBeehyv@users.noreply.github.com> Date: Fri, 20 Oct 2023 11:15:32 +0530 Subject: [PATCH] BAH-2962 | Added sms sending functionality . (#231) * Added SMS sending functionality on patient registration. --- bahmnicore-api/pom.xml | 4 + .../module/bahmnicore/events/BahmniEvent.java | 26 +++++++ .../bahmnicore/events/BahmniEventType.java | 16 ++++ .../bahmnicore/events/EncounterEvent.java | 19 +++++ .../bahmnicore/events/PatientEvent.java | 24 ++++++ .../events/advice/EncounterAdvice.java | 61 +++++++++++++++ .../events/advice/PatientAdvice.java | 61 +++++++++++++++ .../PatientSmsEventListener.java | 78 +++++++++++++++++++ .../eventPublisher/BahmniEventPublisher.java | 21 +++++ .../util/BahmniAsyncThreadExecutor.java | 28 +++++++ ...java => EmailCommunicationController.java} | 9 +-- bahmnicore-omod/src/main/resources/config.xml | 21 ++++- pom.xml | 2 +- 13 files changed, 361 insertions(+), 9 deletions(-) create mode 100644 bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/BahmniEvent.java create mode 100644 bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/BahmniEventType.java create mode 100644 bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/EncounterEvent.java create mode 100644 bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/PatientEvent.java create mode 100644 bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/advice/EncounterAdvice.java create mode 100644 bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/advice/PatientAdvice.java create mode 100644 bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/eventListener/PatientSmsEventListener.java create mode 100644 bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/eventPublisher/BahmniEventPublisher.java create mode 100644 bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/util/BahmniAsyncThreadExecutor.java rename bahmnicore-omod/src/main/java/org/bahmni/module/bahmnicore/web/v1_0/controller/{TransmissionController.java => EmailCommunicationController.java} (93%) diff --git a/bahmnicore-api/pom.xml b/bahmnicore-api/pom.xml index 2b447e3a7e..4aaf0cf22a 100644 --- a/bahmnicore-api/pom.xml +++ b/bahmnicore-api/pom.xml @@ -83,6 +83,10 @@ joda-time 2.0 + + org.bahmni.module + communication-api + org.bahmni.module bahmni-emr-api diff --git a/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/BahmniEvent.java b/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/BahmniEvent.java new file mode 100644 index 0000000000..1681c1e017 --- /dev/null +++ b/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/BahmniEvent.java @@ -0,0 +1,26 @@ +package org.bahmni.module.bahmnicore.events; + +import org.openmrs.api.context.Context; +import org.openmrs.api.context.UserContext; + +import java.time.LocalDateTime; +import java.util.UUID; + +public class BahmniEvent { + + private static final long version = 1L; + public UserContext userContext; + public String eventId; + public BahmniEventType eventType; + public String payloadId; + public LocalDateTime publishedDateTime; + + public BahmniEvent(BahmniEventType bahmniEventType) { + this.eventType = bahmniEventType; + this.eventId = UUID.randomUUID().toString(); + this.publishedDateTime = LocalDateTime.now(); + this.userContext= Context.getUserContext(); + this.payloadId=""; + } +} + diff --git a/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/BahmniEventType.java b/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/BahmniEventType.java new file mode 100644 index 0000000000..0f2dd642ce --- /dev/null +++ b/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/BahmniEventType.java @@ -0,0 +1,16 @@ +package org.bahmni.module.bahmnicore.events; + +public enum BahmniEventType { + BAHMNI_PATIENT_CREATED("bahmni-patient"), + BAHMNI_PATIENT_UPDATED("bahmni-patient"), + BAHMNI_ENCOUNTER_CREATED("bahmni-encounter"), + BAHMNI_ENCOUNTER_UPDATED("bahmni-encounter"); + + private final String topic; + BahmniEventType(String topic) { + this.topic = topic; + } + public String topic() { + return topic; + } +} \ No newline at end of file diff --git a/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/EncounterEvent.java b/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/EncounterEvent.java new file mode 100644 index 0000000000..a3b0107902 --- /dev/null +++ b/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/EncounterEvent.java @@ -0,0 +1,19 @@ +package org.bahmni.module.bahmnicore.events; + +import org.openmrs.Encounter; + +public class EncounterEvent extends BahmniEvent { + + private Encounter encounter; + + public EncounterEvent(BahmniEventType bahmniEventType, Encounter encounter) { + super(bahmniEventType); + this.encounter = encounter; + this.payloadId=encounter.getUuid(); + } + + public Encounter getEncounter() { + return encounter; + } +} + diff --git a/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/PatientEvent.java b/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/PatientEvent.java new file mode 100644 index 0000000000..dba79af640 --- /dev/null +++ b/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/PatientEvent.java @@ -0,0 +1,24 @@ +package org.bahmni.module.bahmnicore.events; + +import org.openmrs.Patient; +import org.openmrs.api.context.Context; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.UUID; + +public class PatientEvent extends BahmniEvent { + + private Patient patient; + + public PatientEvent(BahmniEventType bahmniEventType, Patient patient) { + super(bahmniEventType); + this.patient = patient; + this.payloadId=patient.getUuid(); + } + + public Patient getPatient() { + return patient; + } +} + diff --git a/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/advice/EncounterAdvice.java b/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/advice/EncounterAdvice.java new file mode 100644 index 0000000000..649a21f563 --- /dev/null +++ b/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/advice/EncounterAdvice.java @@ -0,0 +1,61 @@ +package org.bahmni.module.bahmnicore.events.advice; + +import com.google.common.collect.Sets; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bahmni.module.bahmnicore.events.BahmniEventType; +import org.bahmni.module.bahmnicore.events.EncounterEvent; +import org.bahmni.module.bahmnicore.events.eventPublisher.BahmniEventPublisher; +import org.openmrs.Encounter; +import org.openmrs.api.context.Context; +import org.springframework.aop.AfterReturningAdvice; +import org.springframework.aop.MethodBeforeAdvice; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import static org.bahmni.module.bahmnicore.events.BahmniEventType.BAHMNI_ENCOUNTER_CREATED; +import static org.bahmni.module.bahmnicore.events.BahmniEventType.BAHMNI_ENCOUNTER_UPDATED; + + +public class EncounterAdvice implements AfterReturningAdvice, MethodBeforeAdvice { + + private final Logger log = LogManager.getLogger(this.getClass()); + private final BahmniEventPublisher eventPublisher; + private final ThreadLocal> threadLocal = new ThreadLocal<>(); + private final String ENCOUNTER_ID_KEY = "encounterId"; + private final Set adviceMethodNames = Sets.newHashSet("saveEncounter"); + + public EncounterAdvice() { + this.eventPublisher = Context.getRegisteredComponent("bahmniEventPublisher", BahmniEventPublisher.class); + } + + @Override + public void afterReturning(Object returnValue, Method method, Object[] arguments, Object target) { + if (adviceMethodNames.contains(method.getName())) { + Map encounterInfo = threadLocal.get(); + if (encounterInfo != null) { + BahmniEventType eventType = encounterInfo.get(ENCOUNTER_ID_KEY) == null ? BAHMNI_ENCOUNTER_CREATED : BAHMNI_ENCOUNTER_UPDATED; + threadLocal.remove(); + + Encounter encounter = (Encounter) returnValue; + EncounterEvent encounterEvent = new EncounterEvent(eventType, encounter); + eventPublisher.publishEvent(encounterEvent); + + log.info("Successfully published event with uuid : " + encounter.getUuid()); + } + } + } + + @Override + public void before(Method method, Object[] objects, Object o) { + if (adviceMethodNames.contains(method.getName())) { + Encounter encounter = (Encounter) objects[0]; + Map encounterInfo = new HashMap<>(1); + encounterInfo.put(ENCOUNTER_ID_KEY, encounter.getId()); + threadLocal.set(encounterInfo); + } + } +} diff --git a/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/advice/PatientAdvice.java b/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/advice/PatientAdvice.java new file mode 100644 index 0000000000..50bbd77e02 --- /dev/null +++ b/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/advice/PatientAdvice.java @@ -0,0 +1,61 @@ +package org.bahmni.module.bahmnicore.events.advice; + +import com.google.common.collect.Sets; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bahmni.module.bahmnicore.events.BahmniEventType; +import org.bahmni.module.bahmnicore.events.PatientEvent; +import org.bahmni.module.bahmnicore.events.eventPublisher.BahmniEventPublisher; +import org.openmrs.Patient; +import org.openmrs.api.context.Context; +import org.springframework.aop.AfterReturningAdvice; +import org.springframework.aop.MethodBeforeAdvice; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import static org.bahmni.module.bahmnicore.events.BahmniEventType.BAHMNI_PATIENT_CREATED; +import static org.bahmni.module.bahmnicore.events.BahmniEventType.BAHMNI_PATIENT_UPDATED; + + +public class PatientAdvice implements AfterReturningAdvice, MethodBeforeAdvice { + + private final Logger log = LogManager.getLogger(PatientAdvice.class); + private final BahmniEventPublisher eventPublisher; + private final ThreadLocal> threadLocal = new ThreadLocal<>(); + private final String PATIENT_ID_KEY = "patientId"; + private final Set adviceMethodNames = Sets.newHashSet("savePatient"); + + public PatientAdvice() { + this.eventPublisher = Context.getRegisteredComponent("bahmniEventPublisher", BahmniEventPublisher.class); + } + + @Override + public void afterReturning(Object returnValue, Method method, Object[] arguments, Object target) { + if (adviceMethodNames.contains(method.getName())) { + Map patientInfo = threadLocal.get(); + if (patientInfo != null) { + BahmniEventType eventType = patientInfo.get(PATIENT_ID_KEY) == null ? BAHMNI_PATIENT_CREATED : BAHMNI_PATIENT_UPDATED; + threadLocal.remove(); + + Patient patient = (Patient) returnValue; + PatientEvent patientEvent =new PatientEvent(eventType,patient); + eventPublisher.publishEvent(patientEvent); + + log.info("Successfully published event with uuid : " + patient.getUuid()); + } + } + } + @Override + public void before(Method method, Object[] objects, Object o) { + if (adviceMethodNames.contains(method.getName())) { + Patient patient = (Patient) objects[0]; + + Map patientInfo = new HashMap<>(1); + patientInfo.put(PATIENT_ID_KEY, patient.getId()); + threadLocal.set(patientInfo); + } + } +} diff --git a/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/eventListener/PatientSmsEventListener.java b/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/eventListener/PatientSmsEventListener.java new file mode 100644 index 0000000000..0a527134a8 --- /dev/null +++ b/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/eventListener/PatientSmsEventListener.java @@ -0,0 +1,78 @@ +package org.bahmni.module.bahmnicore.events.eventListener; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.bahmni.module.bahmnicore.events.BahmniEventType; +import org.bahmni.module.bahmnicore.events.PatientEvent; +import org.bahmni.module.communication.service.CommunicationService; +import org.bahmni.module.communication.service.MessageBuilderService; +import org.openmrs.Patient; +import org.openmrs.PersonAttribute; +import org.openmrs.api.AdministrationService; +import org.openmrs.api.context.Context; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +@Component +public class PatientSmsEventListener { + + private final Log log = LogFactory.getLog(this.getClass()); + + + @Async("BahmniAsyncThreadExecutor") + @EventListener + public void onApplicationEvent(PatientEvent event) { + try { + Context.openSession(); + Context.setUserContext(event.userContext); + if (event.eventType == BahmniEventType.BAHMNI_PATIENT_CREATED) { + handlePatientCreatedEvent(event.getPatient()); + } + } catch(Exception e){ + log.error("Exception occurred during event processing", e); + } finally{ + Context.closeSession(); + } + } + + private void handlePatientCreatedEvent(Patient patient) { + AdministrationService administrationService = Context.getService(AdministrationService.class); + boolean patientRegistrationSMSProperty = Boolean.parseBoolean(administrationService.getGlobalProperty("sms.enableRegistrationSMSAlert","false")); + if (!patientRegistrationSMSProperty) + return; + String phoneNumber = getPhoneNumber(patient); + if (phoneNumber != null) { + MessageBuilderService messageBuilderService = Context.getRegisteredComponent("messageBuilderService", MessageBuilderService.class); + CommunicationService communicationService = Context.getRegisteredComponent("communicationService", CommunicationService.class); + String message=messageBuilderService.getRegistrationMessage(createArgumentsMapForPatientRegistration(patient)); + communicationService.sendSMS(phoneNumber, message); + } + } + + public Map createArgumentsMapForPatientRegistration(Patient patient) { + String helpdeskNumber = Context.getAdministrationService().getGlobalPropertyObject("clinic.helpDeskNumber").getPropertyValue(); + Map arguments = new HashMap<>(); + arguments.put("location", Context.getUserContext().getLocation().getName()); + arguments.put("identifier", patient.getPatientIdentifier().getIdentifier()); + arguments.put("patientname", patient.getGivenName() + " " + patient.getFamilyName()); + arguments.put("gender", patient.getGender()); + arguments.put("age", patient.getAge().toString()); + arguments.put("helpdesknumber", helpdeskNumber); + return arguments; + } + + private String getPhoneNumber(Patient patient) { + PersonAttribute phoneNumber = patient.getAttribute("phoneNumber"); + if (phoneNumber == null) { + log.info("No mobile number found for the patient. SMS not sent."); + return null; + } + return phoneNumber.getValue(); + } +} + + diff --git a/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/eventPublisher/BahmniEventPublisher.java b/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/eventPublisher/BahmniEventPublisher.java new file mode 100644 index 0000000000..0ff4b9bdbb --- /dev/null +++ b/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/events/eventPublisher/BahmniEventPublisher.java @@ -0,0 +1,21 @@ +package org.bahmni.module.bahmnicore.events.eventPublisher; + +import org.bahmni.module.bahmnicore.events.BahmniEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; + +@Component +public class BahmniEventPublisher implements ApplicationEventPublisherAware { + + private ApplicationEventPublisher eventPublisher; + + @Override + public void setApplicationEventPublisher(@NonNull ApplicationEventPublisher applicationEventPublisher) { + this.eventPublisher = applicationEventPublisher; + } + public void publishEvent(BahmniEvent event) { + this.eventPublisher.publishEvent(event); + } +} \ No newline at end of file diff --git a/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/util/BahmniAsyncThreadExecutor.java b/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/util/BahmniAsyncThreadExecutor.java new file mode 100644 index 0000000000..db49d734e7 --- /dev/null +++ b/bahmnicore-api/src/main/java/org/bahmni/module/bahmnicore/util/BahmniAsyncThreadExecutor.java @@ -0,0 +1,28 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.bahmni.module.bahmnicore.util; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; + +@Configuration +@EnableAsync +public class BahmniAsyncThreadExecutor { + + @Bean(name = "BahmniAsyncThreadExecutor") + public Executor threadPoolTaskExecutor() { + ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); + return threadPoolTaskExecutor; + } +} diff --git a/bahmnicore-omod/src/main/java/org/bahmni/module/bahmnicore/web/v1_0/controller/TransmissionController.java b/bahmnicore-omod/src/main/java/org/bahmni/module/bahmnicore/web/v1_0/controller/EmailCommunicationController.java similarity index 93% rename from bahmnicore-omod/src/main/java/org/bahmni/module/bahmnicore/web/v1_0/controller/TransmissionController.java rename to bahmnicore-omod/src/main/java/org/bahmni/module/bahmnicore/web/v1_0/controller/EmailCommunicationController.java index b348113ebe..28cac1ad84 100644 --- a/bahmnicore-omod/src/main/java/org/bahmni/module/bahmnicore/web/v1_0/controller/TransmissionController.java +++ b/bahmnicore-omod/src/main/java/org/bahmni/module/bahmnicore/web/v1_0/controller/EmailCommunicationController.java @@ -9,9 +9,8 @@ import org.apache.http.impl.DefaultHttpResponseFactory; import org.apache.http.message.BasicStatusLine; import org.bahmni.module.bahmnicore.web.v1_0.contract.BahmniMailContent; -import org.bahmni.module.communication.api.CommunicationService; -import org.bahmni.module.communication.model.MailContent; import org.bahmni.module.communication.model.Recipient; +import org.bahmni.module.communication.service.CommunicationService; import org.openmrs.Patient; import org.openmrs.api.PatientService; import org.openmrs.api.context.Context; @@ -26,8 +25,8 @@ import org.springframework.web.bind.annotation.PathVariable; @Controller -@RequestMapping(value = "/rest/" + RestConstants.VERSION_1 + "/patient/{patientUuid}/send/") -public class TransmissionController extends BaseRestController { +@RequestMapping(value = "/rest/" + RestConstants.VERSION_1 + "/patient/{patientUuid}/send/")//change to send-prescription-email +public class EmailCommunicationController extends BaseRestController { private final Log log = LogFactory.getLog(this.getClass()); @@ -58,4 +57,4 @@ public Object sendEmail(@RequestBody BahmniMailContent bahmniMailContent, @PathV return response; } -} +} \ No newline at end of file diff --git a/bahmnicore-omod/src/main/resources/config.xml b/bahmnicore-omod/src/main/resources/config.xml index e916179e24..d33c63144f 100644 --- a/bahmnicore-omod/src/main/resources/config.xml +++ b/bahmnicore-omod/src/main/resources/config.xml @@ -136,7 +136,7 @@ BahmniConfig.hbm.xml EntityMapping.hbm.xml EntityMappingType.hbm.xml - + @@ -146,7 +146,7 @@ bahmni.relationshipTypeMap - Relationship Type Map format Eg:{ "patient": ["Sibling", "Parent"],"provider": ["Doctor"]}.If no value is specified default is patient relationship. + Relationship Type Map format Eg:{ "patient": ["Sibling", "Parent"],"provider": ["Doctor"]}.If no value is specified default is patient relationship. @@ -188,7 +188,22 @@ bahmni.diagnosisSetForNewDiagnosisConcepts - UUID of set member of diagnosis set of sets. New Diagnosis concepts from terminology server will be added to this set + UUID of set member of diagnosis set of sets. New Diagnosis concepts from terminology server will be added to this set + + sms.enableRegistrationSMSAlert + false + Boolean to enable sending sms when patient registers + + + + + org.openmrs.api.PatientService + org.bahmni.module.bahmnicore.events.advice.PatientAdvice + + + org.openmrs.api.EncounterService + org.bahmni.module.bahmnicore.events.advice.EncounterAdvice + diff --git a/pom.xml b/pom.xml index 8438c5fc2b..06d7e780de 100644 --- a/pom.xml +++ b/pom.xml @@ -573,7 +573,7 @@ org.bahmni.module communication-api - 1.1.0 + 1.2.0-SNAPSHOT jar provided