diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..8fa52dc56
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,97 @@
+/tools/target
+
+# Scripts for installer
+/tools/src/main/resources/upgrade/*.sql
+/tools/src/main/resources/new_install/*.sql
+
+# Created by .ignore support plugin (hsz.mobi)
+.DS_Store
+### JetBrains template
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
+
+*.iml
+
+## Directory-based project format:
+.idea/
+# if you remove the above rule, at least ignore the following:
+
+# User-specific stuff:
+# .idea/workspace.xml
+# .idea/tasks.xml
+# .idea/dictionaries
+
+# Sensitive or high-churn files:
+# .idea/dataSources.ids
+# .idea/dataSources.xml
+# .idea/sqlDataSources.xml
+# .idea/dynamic.xml
+# .idea/uiDesigner.xml
+
+# Gradle:
+# .idea/gradle.xml
+# .idea/libraries
+
+# Mongo Explorer plugin:
+# .idea/mongoSettings.xml
+
+## File-based project format:
+*.ipr
+*.iws
+
+## Plugin-specific files:
+
+# IntelliJ
+/out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+### Eclipse template
+*.pydevproject
+.metadata
+.gradle
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.settings/
+.loadpath
+
+# Eclipse Core
+.project
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# CDT-specific
+.cproject
+
+# JDT-specific (Eclipse Java Development Tools)
+.classpath
+
+# Java annotation processor (APT)
+.factorypath
+
+# PDT-specific
+.buildpath
+
+# sbteclipse plugin
+.target
+
+# TeXlipse plugin
+.texlipse
+
+# Created by .ignore support plugin (hsz.mobi)
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 000000000..d649eb5e8
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,11 @@
+dist: trusty
+language: java
+jdk:
+- oraclejdk8
+script:
+- if [ "$TRAVIS_BRANCH" = "main" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
+ mvn deploy --settings maven-settings.xml; else mvn install; fi
+env:
+ global:
+ - secure: X/+P/TL0nQnk91mrhVmTikTRjlBHbkY1MaoVWu2Hg+u8dzmXa9HjywpsQQquYAj9Nar9/gBpmNe3SYVV2RXPmv8ERUmonBqSkGW4JpXQSrie1gB18XoX1VTP7arCqeY3SCzUy0sBDCPnZXzsCnAcgDxBTbKrgQHvCwV+snXJjSNsGsJoIwTdlnz+kOHFZncyMgbFAh2lnU1UsyZ9pPSTPEn2DCxg6co6ZcLgeXCO1gauGHmNlaTE4NU2ExaUXEPd32sYqD6OTqkeFeLUQxWiNtElCXC70SpjCqsCnN5VEFPTeEOYlWAR19PvfCPoquiHXD+y4uRaf1avGfQGtjfbRuy749OAD+Aa5KUMKrQ44KZlD3FM2VYVHqhCvxlVqch/yom3wqPhMrnC68wgRY2v7VZxwm/WxWI/KQzJVYsAK+5rVyzCqcAc4tTyohtrX8BpBi8iDXEdYRkudvB/5ba/eNTSOgIVBnZc24t4kRRqODDBV1P/Q2BFe+Td4bXmOHsGB4/KpPue2H9Ik7Vk+uzS5eJGhRB8uIXOLKVHyPRDYjWJDOBsGwK4NZyl7JXIRunTCMx4VlVh6rjCJz0CHd1Ri85Rc6QgfRTf/obJKGjTv8Zv131atm66ZJHfVwuf+Q6N2LTK4UKwh/xdH16+tSrCY6Qr+Deu4xCtaX+QK7pvF78=
+ - secure: fax3oJAMhG8992NqrYo+RRQWHqYMpjddARoCPEoi2TA7lPBAsd5FJkSQW+o/dXtOjQs05udE7G4/hWAQtYSV2DSOxSVPJBHfMvSjIrMmuY0pTowFsPRqtDeBzawBDbM6HFJkDIDuCa26hehipgtiWkeubU7MdGMUyhPfBG3fnq7TUenACI4RBxJelft5WFTMsBo+RqwKh7AJSRDbgwszIYu+XuUjxqKIpZx5p1zth/idQN5aIxM7UVgVzY1bXdKQd345d5DFAxb2MyX1g15Xa9weDCTHLqmtZuAtHRFVA7Ig7yUr8k33sqEtm/ExlyaTThflPvBBZm/nyh4fou/lJolAnxtyR7+8No5mRX0S8MH5H1x97s9FM48ldZ1llw3SmA332evlm95Qo/9pDzFv5RfeRwhXHb4p5NrCw6ayIa6MWBa04Jn2thylCxSvTUEtFwJC7aGVrCEMWSzS9RmyxO2aFDL4zqr0Rk5cUygonl/NBGl6yCeqvqHLLBMOgIc+t2pGCq9Iw9YXvn/tV7gtRBoPqphJn5sNOad5h55x9DxxcYrhTV/jcNvzaS9n5luF145vNb1+uQekwkpB65Tv0kVHhqltg0kZ+uEUfotJDJWqs669hQ+Hzt0+Pcxtsc+ZacRzFPcp8+FP/TY7xLFHYTFSzyKKa+PMJulD6vO7Q6c=
\ No newline at end of file
diff --git a/api/.gitignore b/api/.gitignore
new file mode 100644
index 000000000..b83d22266
--- /dev/null
+++ b/api/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/api/pom.xml b/api/pom.xml
new file mode 100644
index 000000000..a3ae4724e
--- /dev/null
+++ b/api/pom.xml
@@ -0,0 +1,240 @@
+
+ 4.0.0
+
+
+ org.openmrs.module
+ ugandaemr
+ 3.1.4-SNAPSHOT
+
+
+ ugandaemr-api
+ jar
+ Ugandaemr Module API
+ API project for ugandaemr
+
+
+
+
+
+
+ javax.servlet
+ javax.servlet-api
+
+
+
+
+
+ org.openmrs.api
+ openmrs-api
+ jar
+
+
+
+ org.openmrs.web
+ openmrs-web
+ jar
+
+
+
+ org.openmrs.api
+ openmrs-api
+ test-jar
+ test
+
+
+
+ org.openmrs.test
+ openmrs-test
+ pom
+ test
+
+
+
+
+ org.openmrs.module
+ addresshierarchy-api
+
+
+
+ org.openmrs.module
+ appframework-api
+
+
+
+ org.openmrs.module
+ calculation-api
+
+
+
+ org.openmrs.module
+ dataexchange-api
+
+
+
+ org.openmrs.module
+ dataintegrity-api
+
+
+
+ org.openmrs.module
+ emrapi-api
+
+
+
+ org.openmrs.module
+ emrapi-api-1.12
+
+
+
+ org.openmrs.module
+ emrapi-api-pre2.2
+
+
+
+ org.openmrs
+ event-api
+
+
+
+ org.openmrs.module
+ formentryapp-omod
+
+
+
+ org.openmrs.module
+ htmlformentry-api
+
+
+
+ org.openmrs.module
+ htmlformentry-api-1.10
+
+
+
+ org.openmrs.module
+ htmlformentry-api-2.0
+
+
+
+ org.openmrs.module
+ htmlformentry-api-2.3
+
+
+
+ org.openmrs.module
+ htmlformentryui-api
+
+
+
+ org.openmrs.module
+ idgen-api
+
+
+
+ pl.pragmatists
+ JUnitParams
+ test
+
+
+
+ org.openmrs.module
+ legacyui-omod
+
+
+
+ org.openmrs.module
+ metadatadeploy-api
+
+
+
+ org.openmrs.module
+ metadatamapping-api
+
+
+
+ org.openmrs.module
+ metadatasharing-api
+
+
+
+ org.openmrs.module
+ providermanagement-api
+
+
+
+ org.openmrs.module
+ patientflags-api
+
+
+
+ org.openmrs.module
+ registrationcore-api
+
+
+
+ org.openmrs.module
+ registrationapp-api
+
+
+
+ org.openmrs.module
+ reporting-api
+
+
+
+ org.openmrs.module
+ serialization.xstream-api
+
+
+
+ org.openmrs.module
+ serialization.xstream-api-2.0
+
+
+
+ org.openmrs.module
+ uiframework-api
+
+
+ org.openmrs.module
+ patientqueueing-api
+
+
+
+
+
+
+ src/main/resources
+ true
+
+
+ *.zip
+ metadata/*.zip
+
+
+
+
+ src/main/resources
+ false
+
+ *.zip
+ metadata/*.zip
+
+
+
+
+
+
+ src/test/resources
+ true
+
+
+
+
+
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/PublicHoliday.java b/api/src/main/java/org/openmrs/module/ugandaemr/PublicHoliday.java
new file mode 100644
index 000000000..d8307c6ca
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/PublicHoliday.java
@@ -0,0 +1,80 @@
+/**
+ * The contents of this file are subject to the OpenMRS Public License
+ * Version 1.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://license.openmrs.org
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * Copyright (C) OpenMRS, LLC. All Rights Reserved.
+ */
+package org.openmrs.module.ugandaemr;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import org.openmrs.BaseOpenmrsData;
+import org.springframework.stereotype.Component;
+
+@Component
+@Entity(name = "public_holiday")
+@Table(name = "public_holiday")
+public class PublicHoliday extends BaseOpenmrsData{
+
+ @Id
+ @GeneratedValue
+ @Column(name = "public_holiday_id")
+ private int publicHolidayId;
+
+ @Column(name = "name")
+ private String name;
+
+ @Column(name = "public_holiday_date")
+ private Date date;
+
+ @Column(name = "description")
+ private String description;
+
+ @Override
+ public Integer getId() {
+ return publicHolidayId;
+ }
+
+ @Override
+ public void setId(Integer id) {
+
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
\ No newline at end of file
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/UgandaEMRActivator.java b/api/src/main/java/org/openmrs/module/ugandaemr/UgandaEMRActivator.java
new file mode 100644
index 000000000..47fcd47c2
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/UgandaEMRActivator.java
@@ -0,0 +1,414 @@
+/**
+ * The contents of this file are subject to the OpenMRS Public License
+ * Version 1.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://license.openmrs.org
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * Copyright (C) OpenMRS, LLC. All Rights Reserved.
+ */
+package org.openmrs.module.ugandaemr;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.openmrs.*;
+import org.openmrs.api.AdministrationService;
+import org.openmrs.api.ConceptService;
+import org.openmrs.api.LocationService;
+import org.openmrs.api.PatientService;
+import org.openmrs.api.context.Context;
+import org.openmrs.module.Module;
+import org.openmrs.module.ModuleActivator;
+import org.openmrs.module.ModuleFactory;
+import org.openmrs.module.ugandaemr.activator.AppConfigurationInitializer;
+import org.openmrs.module.ugandaemr.activator.HtmlFormsInitializer;
+import org.openmrs.module.ugandaemr.activator.Initializer;
+import org.openmrs.module.ugandaemr.api.deploy.bundle.CommonMetadataBundle;
+import org.openmrs.module.ugandaemr.api.deploy.bundle.UgandaAddressMetadataBundle;
+import org.openmrs.module.ugandaemr.api.deploy.bundle.UgandaEMRPatientFlagMetadataBundle;
+import org.openmrs.module.ugandaemr.metadata.core.PatientIdentifierTypes;
+import org.openmrs.module.appframework.service.AppFrameworkService;
+import org.openmrs.module.dataexchange.DataImporter;
+import org.openmrs.module.emrapi.EmrApiConstants;
+import org.openmrs.module.emrapi.utils.MetadataUtil;
+import org.openmrs.module.idgen.IdentifierSource;
+import org.openmrs.module.idgen.service.IdentifierSourceService;
+import org.openmrs.module.metadatadeploy.api.MetadataDeployService;
+import org.openmrs.module.metadatamapping.MetadataTermMapping;
+import org.openmrs.module.metadatamapping.api.MetadataMappingService;
+import org.openmrs.notification.AlertService;
+import org.openmrs.util.OpenmrsUtil;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * This class contains the logic that is run every time this module is either started or stopped.
+ *
+ * TODO: Refactor the whole class to use initializers like
+ */
+public class UgandaEMRActivator extends org.openmrs.module.BaseModuleActivator {
+
+ protected Log log = LogFactory.getLog(getClass());
+
+ /**
+ * @see ModuleActivator#willRefreshContext()
+ */
+ public void willRefreshContext() {
+ log.info("Refreshing ugandaemr Module");
+ }
+
+ /**
+ * @see ModuleActivator#contextRefreshed()
+ */
+ public void contextRefreshed() {
+ log.info("ugandaemr Module refreshed");
+ }
+
+ /**
+ * @see ModuleActivator#willStart()
+ */
+ public void willStart() {
+ log.info("Starting ugandaemr Module");
+ }
+
+ /**
+ * @see ModuleActivator#started()
+ */
+ public void started() {
+ AdministrationService administrationService = Context.getAdministrationService();
+ AppFrameworkService appFrameworkService = Context.getService(AppFrameworkService.class);
+ MetadataDeployService deployService = Context.getService(MetadataDeployService.class);
+ ConceptService conceptService = Context.getConceptService();
+ LocationService locationService = Context.getLocationService();
+
+ try {
+ // disable the reference app registration page
+ appFrameworkService.disableApp("referenceapplication.registrationapp.registerPatient");
+ // disable the start visit app since all data is retrospective
+ appFrameworkService.disableExtension("org.openmrs.module.coreapps.createVisit");
+ // the extension to the edit person details
+ appFrameworkService.disableExtension("org.openmrs.module.registrationapp.editPatientDemographics");
+
+ // disable apps on the Clinican facing dashboard added through coreapps 1.12.0
+ appFrameworkService.disableApp("coreapps.mostRecentVitals");
+ appFrameworkService.disableApp("coreapps.diagnoses");
+ appFrameworkService.disableApp("coreapps.latestObsForConceptList");
+ appFrameworkService.disableApp("coreapps.obsAcrossEncounters");
+ appFrameworkService.disableApp("coreapps.obsGraph");
+ appFrameworkService.enableApp("coreapps.visitByEncounterType");
+ appFrameworkService.disableApp("coreapps.dataIntegrityViolations");
+ appFrameworkService.disableApp("coreapps.conditionlist");
+ appFrameworkService.disableApp("fingerprint.findPatient");
+ appFrameworkService.disableApp("ugandaemr.registrationapp.registerPatient");
+
+ // enable the relationships dashboard widget
+ appFrameworkService.enableApp("coreapps.relationships");
+
+ // Remove the BIRT reports app since it is no longer supported
+ appFrameworkService.disableApp("ugandaemr.referenceapplication.birtReports");
+
+ // Home page apps clean up
+ appFrameworkService.disableApp("referenceapplication.vitals"); // Capture Vitals
+ appFrameworkService.disableApp("coreapps.activeVisits"); // Active Visits
+
+ // form entry app on the home page
+ appFrameworkService.disableApp("xforms.formentry");
+ // disable the default find patient app to provide one which allows searching for patients at the footer of the search for patients page
+ appFrameworkService.disableApp("coreapps.findPatient");
+ // form entry extension in active visits
+ appFrameworkService.disableExtension("xforms.formentry.cfpd");
+
+ // install concepts
+ DataImporter dataImporter = Context.getRegisteredComponent("dataImporter", DataImporter.class);
+
+ log.info("Start import of Custom Concepts");
+ dataImporter.importData("metadata/Custom_Concepts.xml");
+ dataImporter.importData("metadata/Drug_Concepts.xml");
+ log.info("Custom Concepts imported");
+
+ log.info("Start import of person attributes");
+ // TODO: Replace this with metadata deploy to be consistent with other person attribute types
+ dataImporter.importData("metadata/Person_Attribute_Types.xml");
+ log.info("Person Attributes imported");
+
+ log.info("Start import of UgandaEMR Privileges");
+ dataImporter.importData("metadata/Role_Privilege.xml");
+ log.info("UgandaEMR Privileges Imported");
+
+ log.info("Start import of UgandaEMR Visits");
+ dataImporter.importData("metadata/VisitTypes.xml");
+ log.info("UgandaEMR Visits Imported");
+
+
+
+ // install commonly used metadata
+ installCommonMetadata(deployService);
+
+ log.info("Start import of Program related objects");
+ dataImporter.importData("metadata/Programs.xml");
+ log.info(" Program related objects Imported");
+
+ // run the initializers
+ for (Initializer initializer : getInitializers()) {
+ initializer.started();
+ }
+
+ // save defined global properties
+ administrationService.saveGlobalProperties(configureGlobalProperties());
+
+ // update the name of the default health center with that stored in the global property
+ Location healthCenter = locationService.getLocationByUuid("629d78e9-93e5-43b0-ad8a-48313fd99117");
+ healthCenter.setName(administrationService.getGlobalProperty(UgandaEMRConstants.GP_HEALTH_CENTER_NAME));
+ locationService.saveLocation(healthCenter);
+
+ String flagstatus = administrationService.getGlobalProperty("ugandaemr.patientflags.disabledFlags");
+
+ if (flagstatus != null) {
+ flagstatus=("'"+flagstatus.trim().replace(",","','")+"'").replace(",''","").replace("' ","'");
+ administrationService.executeSQL("update patientflags_flag set enabled=0 where name in (" + flagstatus.trim() + ")", false);
+ }
+
+ // cleanup liquibase change logs to enable installation of data integrity module
+ removeOldChangeLocksForDataIntegrityModule();
+
+ // generate OpenMRS ID for patients without the identifier
+ generateOpenMRSIdentifierForPatientsWithout();
+
+ //update concept name for concept id 163017 to ABC-3TC-LPV/r as fully specified
+ administrationService.executeSQL("UPDATE concept_name SET concept_name_type = 'FULLY_SPECIFIED',locale_preferred = 1 WHERE concept_name_id = 134334 and concept_id=163017", false);
+ administrationService.executeSQL("UPDATE concept_name SET locale_preferred = 0,concept_name_type = null WHERE concept_name_id = 134333 and concept_id=163017", false);
+
+ log.info("ugandaemr Module started");
+
+ } catch (Exception e) {
+ Module mod = ModuleFactory.getModuleById("ugandaemr");
+ ModuleFactory.stopModule(mod);
+ throw new RuntimeException("failed to setup the module ", e);
+ }
+
+
+ }
+
+ /**
+ * Generate patientIdentifier for old OpenMRS Migration to the new
+ */
+
+ protected PatientIdentifier generatePatientIdentifier() {
+ IdentifierSourceService iss = Context.getService(IdentifierSourceService.class);
+ IdentifierSource idSource = iss.getIdentifierSource(1); // this is the default OpenMRS identifier source
+ PatientService patientService = Context.getPatientService();
+
+ UUID uuid = UUID.randomUUID();
+
+ PatientIdentifierType patientIdentifierType = patientService.getPatientIdentifierTypeByUuid("05a29f94-c0ed-11e2-94be-8c13b969e334");
+
+ PatientIdentifier pid = new PatientIdentifier();
+ pid.setIdentifierType(patientIdentifierType);
+ String identifier = iss.generateIdentifier(idSource, "New OpenMRS ID with CheckDigit");
+ pid.setIdentifier(identifier);
+ pid.setPreferred(true);
+ pid.setUuid(String.valueOf(uuid));
+
+ return pid;
+
+ }
+
+ /**
+ * Generate an OpenMRS ID for patients who do not have one due to a migration from an old OpenMRS ID to a new one which contains a check-digit
+ **/
+ private void generateOpenMRSIdentifierForPatientsWithout() {
+ PatientService patientService = Context.getPatientService();
+ AdministrationService as = Context.getAdministrationService();
+ AlertService alertService = Context.getAlertService();
+
+ List> patientIds = as.executeSQL("SELECT patient_id FROM patient_identifier WHERE patient_id NOT IN (SELECT patient_id FROM patient_identifier p INNER JOIN patient_identifier_type pt ON (p.identifier_type = pt.patient_identifier_type_id AND pt.uuid = '05a29f94-c0ed-11e2-94be-8c13b969e334'))", true);
+
+ if (patientIds.size() == 0) {
+ // no patients to process
+ return;
+ }
+ // get the identifier source copied from RegistrationCoreServiceImpl
+
+ for (List row : patientIds) {
+ Patient p = patientService.getPatient((Integer) row.get(0));
+ // Create new Patient Identifier
+ PatientIdentifier pid = generatePatientIdentifier();
+ pid.setPatient(p);
+ try {
+ log.info("Adding OpenMRS ID " + pid.getIdentifier() + " to patient with id " + p.getPatientId());
+ // Save the patient Identifier
+ patientService.savePatientIdentifier(pid);
+ } catch (Exception e) {
+ // log the error to the alert service but do not rethrow the exception since the module has to start
+ log.error("Error updating OpenMRS identifier for patient #" + p.getPatientId(), e);
+ }
+ }
+ log.info("All patients updated with new OpenMRS ID");
+ }
+
+ /**
+ * Configure the global properties for the expected functionality
+ *
+ * @return
+ */
+ private List configureGlobalProperties() {
+ List properties = new ArrayList();
+ // The primary identifier type now uses metadata mapping instead of a global property
+ MetadataMappingService metadataMappingService = Context.getService(MetadataMappingService.class);
+ MetadataTermMapping primaryIdentifierTypeMapping = metadataMappingService.getMetadataTermMapping(EmrApiConstants.EMR_METADATA_SOURCE_NAME, EmrApiConstants.PRIMARY_IDENTIFIER_TYPE);
+ PatientIdentifierType openmrsIdType = Context.getPatientService().getPatientIdentifierTypeByUuid(PatientIdentifierTypes.NATIONAL_ID.uuid());
+
+ //overwrite if not set yet
+ if (!openmrsIdType.getUuid().equals(primaryIdentifierTypeMapping.getMetadataUuid())) {
+ primaryIdentifierTypeMapping.setMappedObject(openmrsIdType);
+ metadataMappingService.saveMetadataTermMapping(primaryIdentifierTypeMapping);
+ }
+
+ String ART_Patient_Number_Identifier = "";
+ // check if the ART patient number is to be displayed then add it here
+ if (Context.getAdministrationService().getGlobalProperty("ugandaemr.showARTPatientNumberIdentifier").equals("true")) {
+ log.info("Adding ART patient number to extra identifier types property");
+ ART_Patient_Number_Identifier = "," + PatientIdentifierTypes.ART_PATIENT_NUMBER.uuid();
+ }
+
+ String Research_Patient_Identifier = "";
+ // check if the ART patient number is to be displayed then add it here
+ if (Context.getAdministrationService().getGlobalProperty("ugandaemr.showResearchPatientIdentifier").equals("true")) {
+ log.info("Adding research patient number to extra identifier types property");
+ Research_Patient_Identifier = "," + PatientIdentifierTypes.RESEARCH_PATIENT_ID.uuid();
+ }
+
+ String Refugee_Identifier = "";
+ // check if the ART patient number is to be displayed then add it here
+ if (Context.getAdministrationService().getGlobalProperty("ugandaemr.showRefugeeIdentifier").equals("true")) {
+ log.info("Adding refugee identifier to extra identifier types property");
+ Refugee_Identifier = "," + PatientIdentifierTypes.REFUGEE_IDENTIFICATION_NUMBER.uuid();
+ }
+
+ // set the HIV care number and EID number as additional identifiers that can be searched for
+ properties.add(new GlobalProperty(EmrApiConstants.GP_EXTRA_PATIENT_IDENTIFIER_TYPES, PatientIdentifierTypes.HIV_CARE_NUMBER.uuid() + "," + PatientIdentifierTypes.EXPOSED_INFANT_NUMBER.uuid() + "," + PatientIdentifierTypes.IPD_NUMBER.uuid() + "," + PatientIdentifierTypes.ANC_NUMBER.uuid() + "," + PatientIdentifierTypes.PNC_NUMBER.uuid() + "," + ART_Patient_Number_Identifier + Research_Patient_Identifier + Refugee_Identifier));
+
+ // set the name of the application
+ properties.add(new GlobalProperty("application.name", "UgandaEMR - Uganda eHealth Solution"));
+
+ // Remove the regular expression to validate names
+ properties.add(new GlobalProperty("patient.nameValidationRegex", ""));
+
+ // the search mode for patients to enable searching any part of names rather than the beginning
+ properties.add(new GlobalProperty("patientSearch.matchMode", "ANYWHERE"));
+
+ // enable searching on parts of the patient identifier
+ // the prefix and suffix provide a % round the entered search term with a like
+ properties.add(new GlobalProperty("patient.identifierPrefix", "%"));
+ properties.add(new GlobalProperty("patient.identifierSuffix", "%"));
+
+ // the RegeX and Search patterns should be empty so that the prefix and suffix matching above can work
+ properties.add(new GlobalProperty("patient.identifierRegex", ""));
+ properties.add(new GlobalProperty("patient.identifierSearchPattern", ""));
+ // add telephone number and Marital status to the search widget
+ properties.add(new GlobalProperty("patient.listingAttributeTypes", "Telephone Number,Marital Status"));
+
+ // Form Entry Settings
+ properties.add(new GlobalProperty("FormEntry.enableDashboardTab", "true")); // show as a tab on the patient dashboard
+ properties.add(new GlobalProperty("FormEntry.FormEntry.enableOnEncounterTab", "true"));
+
+ //HTML Form Entry Settings
+ properties.add(new GlobalProperty("htmlformentry.showDateFormat", "false"));//Disable date format display on form entry
+
+ //Birt Settings
+ properties.add(new GlobalProperty("birt.alwaysUseOpenmrsJdbcProperties", "false"));
+ properties.add(new GlobalProperty("birt.birtHome", OpenmrsUtil.getApplicationDataDirectory() + "birt" + File.separator + "birt-runtime-2_3_2" + File.separator + "ReportEngine"));
+ properties.add(new GlobalProperty("birt.datasetDir", OpenmrsUtil.getApplicationDataDirectory() + "birt" + File.separator + "reports" + File.separator + "datasets"));
+ properties.add(new GlobalProperty("birt.loggingDir", OpenmrsUtil.getApplicationDataDirectory() + "birt" + File.separator + "logs"));
+ properties.add(new GlobalProperty("birt.defaultReportDesignFile", "default.rptdesign"));
+ properties.add(new GlobalProperty("birt.loggingLevel", "OFF"));
+ properties.add(new GlobalProperty("birt.mandatory", "false"));
+ properties.add(new GlobalProperty("birt.outputDir", OpenmrsUtil.getApplicationDataDirectory() + "birt" + File.separator + "reports" + File.separator + "output"));
+ properties.add(new GlobalProperty("birt.reportDir", OpenmrsUtil.getApplicationDataDirectory() + "birt" + File.separator + "reports"));
+ properties.add(new GlobalProperty("birt.reportOutputFile", OpenmrsUtil.getApplicationDataDirectory() + "birt" + File.separator + "reports" + File.separator + "output" + File.separator + "ReportOutput.pdf"));
+ properties.add(new GlobalProperty("birt.reportOutputFormat", "pdf"));
+ properties.add(new GlobalProperty("birt.reportPreviewFile", OpenmrsUtil.getApplicationDataDirectory() + "birt" + File.separator + "reports" + File.separator + "output" + File.separator + "ReportPreview.pdf"));
+
+ // disable the appointmentshedulingui which currently has issues
+ properties.add(new GlobalProperty("appointmentschedulingui.started", "false"));
+
+ // Exclude temporary reporting tables by database backup module
+ properties.add(new GlobalProperty("databasebackup.tablesExcluded", "ugandaemr_105_eid,ugandaemr_106a1a"));
+
+ // the name of the custom registration app
+ properties.add(new GlobalProperty("registrationapp.customRegistrationAppId", "ugandaemr.registrationapp.registerPatient"));
+
+ // enable the register patient button to appear on the search widget
+ properties.add(new GlobalProperty("coreapps.showRegisterPatientOnSearchWidget", "true"));
+
+ // mapping for creating visits without encounters to the default facility visit type
+ properties.add(new GlobalProperty("emrapi.EmrApiVisitAssignmentHandler.encounterTypeToNewVisitTypeMap", "default:7b0f5697-27e3-40c4-8bae-f4049abfb4ed"));
+ return properties;
+ }
+
+ private void installCommonMetadata(MetadataDeployService deployService) {
+ try {
+ log.info("Installing standard metadata using the packages.xml file");
+ MetadataUtil.setupStandardMetadata(getClass().getClassLoader());
+ log.info("Standard metadata installed");
+
+ log.info("Installing metadata");
+ log.info("Installing commonly used metadata");
+ deployService.installBundle(Context.getRegisteredComponents(CommonMetadataBundle.class).get(0));
+ log.info("Finished installing commonly used metadata");
+ log.info("Installing address hierarchy");
+ deployService.installBundle(Context.getRegisteredComponents(UgandaAddressMetadataBundle.class).get(0));
+ log.info("Finished installing addresshierarchy");
+
+ // install concepts
+ log.info("Installing patient flags");
+ deployService.installBundle(Context.getRegisteredComponents(UgandaEMRPatientFlagMetadataBundle.class).get(0));
+ log.info("Finished installing patient flags");
+
+ } catch (Exception e) {
+ Module mod = ModuleFactory.getModuleById("ugandaemr");
+ ModuleFactory.stopModule(mod);
+ throw new RuntimeException("failed to install the common metadata ", e);
+ }
+ }
+
+ private void removeOldChangeLocksForDataIntegrityModule() {
+ String gpVal = Context.getAdministrationService().getGlobalProperty("dataintegrity.database_version");
+ // remove data integrity locks for an version below 4
+ // some gymnastics to get the major version number from semver like 2.5.3
+ if ((gpVal == null) || new Integer(gpVal.substring(0, gpVal.indexOf("."))).intValue() < 4) {
+ AdministrationService as = Context.getAdministrationService();
+ log.warn("Removing liquibase change log locks for previously installed data integrity instance");
+ as.executeSQL("delete from liquibasechangelog WHERE ID like 'dataintegrity%';", false);
+ }
+ }
+
+ /**
+ * @see ModuleActivator#willStop()
+ */
+ public void willStop() {
+ log.info("Stopping ugandaemr Module");
+ }
+
+ /**
+ * @see ModuleActivator#stopped()
+ */
+ public void stopped() {
+
+ log.info("ugandaemr Module stopped");
+ }
+
+ private List getInitializers() {
+ List l = new ArrayList();
+ l.add(new AppConfigurationInitializer());
+ l.add(new HtmlFormsInitializer(UgandaEMRConstants.MODULE_ID));
+ return l;
+ }
+}
\ No newline at end of file
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/UgandaEMRConstants.java b/api/src/main/java/org/openmrs/module/ugandaemr/UgandaEMRConstants.java
new file mode 100644
index 000000000..17fbd9efc
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/UgandaEMRConstants.java
@@ -0,0 +1,146 @@
+package org.openmrs.module.ugandaemr;
+
+/**
+ * Uganda customization specific constants
+ * Created by ssmusoke on 06/01/2016.
+ */
+public class UgandaEMRConstants {
+ /**
+ * Module ID
+ */
+ public static final String MODULE_ID = "ugandaemr";
+ public static final String UGANDAEMR_MODULE_ID = "ugandaemr";
+
+ /**
+ * Global property names
+ */
+ public static final String MODULE_PRIVILEGE = "UgandaEMR Privilege";
+ public static final String GP_HEALTH_CENTER_NAME = MODULE_ID + ".healthCenterName";
+ public static final String GP_HEALTH_CENTER_NAME_VALUE = "Health Center Name";
+ public static final String GP_HEALTH_CENTER_NAME_DEFAULT_ALERT_MESSAGE="The Health Center Name is not set please go to admin then Settings then Ugandaemr and set it";
+
+ public static final String GP_DHIS2= "ugandaemr.dhis2.organizationuuid";
+ public static final String GP_DHIS2_VALUE = "eg d06ace3e-9c46-11e7-abc4-cec278b6b50a";
+ public static final String GP_NHPI = MODULE_ID + ".nhpi";
+ public static final String GP_NHPI_VALUE = "HFT2ZBPE8";
+ public static final String GP_NHPI_DESCRIPTION = "Allows one to set the National Health Provider Identifier";
+ public static final String GP_NHPI_DEFAULT_ALERT_MESSAGE="The National Health Provider Identifier is not set please go to admin then Settings then Ugandaemr and set it";
+ public static final String GP_DHIS2_DEFAULT_ALERT_MESSAGE="The organization code in DHIS2 is not set please go to admin then Settings then Ugandaemr and set it";
+
+ public static final String GP_DSDM_PROGRAM_UUID_NAME="ugandaemr.dsdm.programsuuid";
+
+ public static final String GP_DSDM_CONCEPT_ID="ugandaemr.dsdm.conceptId";
+
+
+ /**x
+ * Concepts
+ */
+ public static final String UNIT_TB_NUMBER = "304df0d0-afe4-4a61-a917-d684b100a65a";
+
+ public static final String HSD_TB_NUMBER = "d1cda288-4853-4450-afbc-76bd4e65ea70";
+
+ public static final String DISTRICT_TB_NUMBER = "67e9ec2f-4c72-408b-8122-3706909d77ec";
+
+ public static final String TRANSFER_OUT_DATE_CONCEPT_ID="99165";
+ public static final String TRANSFER_OUT_PLACE_CONCEPT_ID="90211";
+ public static final String TRANSFER_FROM_CLINIC_CONCEPT_ID="164993";
+ public static final String ART_CLINIC_CONCEPT_ID="165047";
+
+ public static final String TRANSFER_IN_CONCEPT_ID="99110";
+ public static final String TRANSFER_IN_FROM_CONCEPT_ID="165047";
+ public static final String TRANSFER_IN_FROM_PLACE_CONCEPT_ID="99109";
+
+
+ public static final String FACILITY_VISIT_TYPE_UUID="7b0f5697-27e3-40c4-8bae-f4049abfb4ed";
+ public static final String ART_TRANSFER_SCHEDULE_NAME="ART Summary Patient Transfer Migration";
+ public static final String CHANGE_MESSAGE_FOR_TRANSFERS = "New Observation on patient transfers";
+
+ public static final String PATIENT_TRANSERRED_OUT ="transferredOut";
+ public static final String PATIENT_TRANSFERED_OUT_LOCATION ="transferredOutTo";
+ public static final String PATIENT_TRANSFERED_OUT_DATE = "dateTransferredOut";
+
+ public static final String PATIENT_TRANSERRED_IN ="transferredIn";
+ public static final String PATIENT_TRANSFERED_IN_LOCATION ="transferredInTo";
+ public static final String PATIENT_TRANSFERED_IN_DATE = "dateTransferredIn";
+
+
+ public static final String TRIAGE_LOCATION_UUID = "ff01eaab-561e-40c6-bf24-539206b521ce";
+ public static final String PHARMACY_LOCATION_UUID = "3ec8ff90-3ec1-408e-bf8c-22e4553d6e17";
+ public static final String LAB_LOCATION_UUID = "ba158c33-dc43-4306-9a4a-b4075751d36c";
+
+ public static final String DRUG_SET_CLASS = "Drug";
+ public static final String LAB_SET_CLASS = "LabSet";
+ public static final String TEST_SET_CLASS = "Test";
+
+ public static final String ORDER_TYPE_DRUG_UUID = "131168f4-15f5-102d-96e4-000c29c2a5d7";
+ public static final String ORDER_TYPE_LAB_UUID = "52a447d3-a64a-11e3-9aeb-50e549534c5e";
+
+ public static final int MEDICATION_ORDER_CONCEPT_ID = 1282;
+ public static final int MEDICATION_STRENGTH_CONCEPT_ID = 1444;
+ public static final int MEDICATION_QUANTITY_CONCEPT_ID = 160856;
+ public static final int ARV_MEDICATION_QUANTITY_CONCEPT_ID = 99038;
+ public static final int MEDICATION_QUANTITY_UNIT_CONCEPT_ID = 165791;
+ public static final int MEDICATION_FORM_CONCEPT_ID = 1519;
+ public static final int MEDICATION_START_DATE_CONCEPT_ID = 1190;
+ public static final int MEDICATION_END_DATE_CONCEPT_ID = 1191;
+ public static final int MEDICATION_INDICATOR_CONCEPT_ID = 160742;
+ public static final int MEDICATION_DURATION_CONCEPT_ID = 159368;
+ public static final int ARV_MEDICATION_DURATION_CONCEPT_ID = 99036;
+ public static final int MEDICATION_DURATION_UNIT_CONCEPT_ID = 1732;
+ public static final int CURRENTLY_TAKING_MEDICATION_CONCEPT_ID = 159367;
+ public static final int MEDICATION_COMMENT_CONCEPT_ID = 160632;
+ public static final int DEFALUT_ROUTE_CONCEPT_ID = 160240;
+ public static final int DEFALUT_DOSE_UNIT_CONCEPT_ID = 162366;
+ public static final int DEFALUT_DISPENSING_UNIT_CONCEPT_ID = 162399;
+ public static final int DEFALUT_DURATION_UNIT_CONCEPT_ID = 1072;
+
+ public static final int MEDICATION_DISPENSE_SET = 163711;
+ public static final int MEDICATION_DISPENSE_QUANTITY = 1443;
+ public static final int MEDICATION_DISPENSE_RECEIVED_AT_VIST = 1276;
+
+ public static final int CONCEPT_ID_NEXT_APPOINTMENT = 5096;
+ public static final int CONCEPT_ID_TRANSFERED_OUT = 90306;
+
+ public static final String DEFALUT_ORDER_FREQUECNY_UUID = "160862OFAAAAAAAAAAAAAAA";
+
+ public static final String DAY_START_TIME = "00:00:00";
+ public static final String DAY_END_TIME = "23:59:59";
+
+
+
+ public static final String QUEUE_STATUS_PENDING = "pending";
+ public static final String QUEUE_STATUS_COMPLETED = "completed";
+ public static final String QUEUE_STATUS_ACTIVE = "active";
+ public static final String QUEUE_STATUS_SENT_TO_LAB = "sent to lab";
+ public static final String QUEUE_STATUS_FROM_LAB = "from lab";
+ public static final String QUEUE_STATUS_SENT_TO_PHARMACY = "sent to pharmacy";
+ public static final String QUEUE_STATUS_HAS_RESULTS = "has results";
+
+
+ public static final String PROCESSED_ORDER_WITH_RESULT_QUERY = "select orders.order_id from orders inner join test_order on (test_order.order_id=orders.order_id) inner join obs on (orders.order_id=obs.order_id) where orders.accession_number!=\"\" and specimen_source!=\"\" AND orders.date_created BETWEEN \"%s\" AND \"%s\"";
+
+ public static final String PROCESSED_ORDER_WITH_RESULT_OF_ENCOUNTER_QUERY = "select orders.order_id from orders inner join test_order on (test_order.order_id=orders.order_id) inner join obs on (orders.order_id=obs.order_id) where orders.accession_number!=\"\" and specimen_source!=\"\" AND orders.encounter_id=%s";
+
+ public static final String PROCESSED_ORDER_WITHOUT_RESULT_QUERY = "select orders.order_id from orders inner join test_order on (test_order.order_id=orders.order_id) where accession_number!=\"\" and specimen_source!=\"\" AND orders.date_created BETWEEN \"%s\" AND \"%s\"";
+
+ public static final String PROCESSED_ORDER_WITHOUT_NO_DATE_FILTER_RESULT_QUERY = "select orders.order_id from orders inner join test_order on (test_order.order_id=orders.order_id) where accession_number!=\"\" and specimen_source!=\"\"";
+
+ public static final String ENCOUNTER_ROLE = "240b26f9-dd88-4172-823d-4a8bfeb7841f";
+ public static final String ENCOUNTER_ROLE_PHARMACIST= "8da340f3-c690-439f-b21f-3a8367ff4057";
+
+ public static final String ENCOUNTER_TYPE_DISPENSE_UUID="22902411-19c1-4a02-b19a-bf1a9c24fd51";
+ public static final String DISPENSE_FORM_UUID="340fe8d8-4984-11ea-b77f-2e728ce88125";
+
+ public static final String CARE_SETTING_OPD = "Outpatient";
+
+
+ public static final int TB_HEALTH_UNIT_NUMBER_CONCEPT_ID = 165826;
+ public static final String TB_HEALTH_UNIT_NUMBER_PROGRAM_ATTRIBUTE_TYPE_UUID = "af9baf06-d245-11ea-87d0-0242ac130003";
+ public static final int TB_DISTRICT_NUMBER_CONCEPT_ID = 99031;
+ public static final String TB_DISTRICT_NUMBER_PROGRAM_ATTRIBUTE_TYPE_UUID = "af9bac5e-d245-11ea-87d0-0242ac130003";
+ public static final int TB_REGIMEN_CONCEPT_ID = 159958;
+ public static final String TB_REGIMEN_PROGRAM_ATTRIBUTE_TYPE_UUID = "af9bb0c8-d245-11ea-87d0-0242ac130003";
+ public static final int DR_TB_NUMBER_PROGRAM_CONCEPT_ID = 165843;
+ public static final String DR_TB_NUMBER_PROGRAM_ATTRIBUTE_TYPE_UUID = "1628fa4e-fda2-11ea-adc1-0242ac120002";
+
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/activator/AlertConfigurationInitializer.java b/api/src/main/java/org/openmrs/module/ugandaemr/activator/AlertConfigurationInitializer.java
new file mode 100644
index 000000000..aacf2bce2
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/activator/AlertConfigurationInitializer.java
@@ -0,0 +1,53 @@
+package org.openmrs.module.ugandaemr.activator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.openmrs.GlobalProperty;
+import org.openmrs.api.AdministrationService;
+import org.openmrs.module.ugandaemr.api.UgandaEMRService;
+
+import static org.openmrs.module.ugandaemr.UgandaEMRConstants.*;
+import static org.openmrs.module.ugandaemr.UgandaEMRConstants.GP_DHIS2_DEFAULT_ALERT_MESSAGE;
+import static org.openmrs.module.ugandaemr.UgandaEMRConstants.GP_DHIS2_VALUE;
+
+public class AlertConfigurationInitializer implements Initializer {
+ protected Log log = LogFactory.getLog(AlertConfigurationInitializer.class);
+
+ @Override
+ public void started() {
+
+
+ // set alert if National Health Provider Identifier is not set
+ createAlertWhenDefaultIsNotSet(GP_NHPI, GP_NHPI_VALUE, GP_NHPI_DEFAULT_ALERT_MESSAGE);
+ // set alert if Health Center Name is not set
+ createAlertWhenDefaultIsNotSet(GP_HEALTH_CENTER_NAME, GP_HEALTH_CENTER_NAME_VALUE, GP_HEALTH_CENTER_NAME_DEFAULT_ALERT_MESSAGE);
+ // set alert if DHIS2 Organization UUID is not set
+ createAlertWhenDefaultIsNotSet(GP_DHIS2, GP_DHIS2_VALUE, GP_DHIS2_DEFAULT_ALERT_MESSAGE);
+ }
+
+ @Override
+ public void stopped() {
+
+ }
+
+ /**
+ * @param globalPropertyName
+ * @param globalPropertyDefault
+ * @param messageAlert
+ */
+ private void createAlertWhenDefaultIsNotSet(String globalPropertyName, String globalPropertyDefault, String messageAlert) {
+ UgandaEMRService ugandaemrService = org.openmrs.api.context.Context.getService(UgandaEMRService.class);
+ AdministrationService administrationService = org.openmrs.api.context.Context.getAdministrationService();
+ GlobalProperty globalProperty = new GlobalProperty();
+ globalProperty = administrationService.getGlobalPropertyObject(globalPropertyName);
+ if (globalProperty.getPropertyValue() == null) {
+ ugandaemrService.setAlertForAllUsers(messageAlert);
+ log.info("Creating alert " + messageAlert);
+ } else {
+ if (globalProperty.getPropertyValue().equalsIgnoreCase(globalPropertyDefault)) {
+ ugandaemrService.setAlertForAllUsers(messageAlert);
+ log.info("Creating alert " + messageAlert);
+ }
+ }
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/activator/AppConfigurationInitializer.java b/api/src/main/java/org/openmrs/module/ugandaemr/activator/AppConfigurationInitializer.java
new file mode 100644
index 000000000..7160a98d9
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/activator/AppConfigurationInitializer.java
@@ -0,0 +1,62 @@
+package org.openmrs.module.ugandaemr.activator;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.openmrs.api.context.Context;
+import org.openmrs.module.ugandaemr.UgandaEMRConstants;
+import org.openmrs.scheduler.SchedulerService;
+import org.openmrs.scheduler.TaskDefinition;
+
+/**
+ * Custom application configurations
+ */
+public class AppConfigurationInitializer implements Initializer {
+
+ protected Log log = LogFactory.getLog(getClass());
+
+ @Override
+ public void started() {
+ log.info("Setting administrative configurations for " + UgandaEMRConstants.MODULE_ID);
+
+ SchedulerService schedulerService = Context.getSchedulerService();
+
+ try {
+ // set the AutoClose visits tasks to start automatically
+ TaskDefinition autoCloseVisitsTask = (TaskDefinition) schedulerService.getTaskByName("Auto Close Visits Task");
+ autoCloseVisitsTask.setStartOnStartup(true);
+ schedulerService.saveTaskDefinition(autoCloseVisitsTask);
+
+ // check the Database Backup Task
+ TaskDefinition backupDatabase = (TaskDefinition) schedulerService.getTaskByName("Database Backup Task");
+ if (backupDatabase != null) {
+ // only execute if the task exists
+ backupDatabase.setStartOnStartup(true);
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy MMM dd HH:mm:ss");
+ Calendar taskDefinitionCalendar = new GregorianCalendar(2016, 8, 28, 23, 59, 59);
+ Calendar ugandaEMRCalendar = new GregorianCalendar(2016, 8, 28, 15, 59, 59);
+
+ // change the start date
+ if(sdf.format(taskDefinitionCalendar.getTime()).equals(sdf.format(backupDatabase.getStartTime()))) {
+ // set it to the new time for Uganda
+ backupDatabase.setStartTime(ugandaEMRCalendar.getTime());
+ log.info("UgandaEMR backup time set");
+ }
+ schedulerService.saveTaskDefinition(backupDatabase);
+ log.info("Database Backup Task set to start on Startup");
+ }
+ }
+ catch (Exception e) {
+ log.error("Failed to setup scheduled tasks ", e);
+ }
+
+ }
+
+ @Override
+ public void stopped() {
+
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/activator/HtmlFormsInitializer.java b/api/src/main/java/org/openmrs/module/ugandaemr/activator/HtmlFormsInitializer.java
new file mode 100644
index 000000000..d4a05b5d4
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/activator/HtmlFormsInitializer.java
@@ -0,0 +1,104 @@
+package org.openmrs.module.ugandaemr.activator;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.openmrs.api.FormService;
+import org.openmrs.api.context.Context;
+import org.openmrs.module.ugandaemr.utils.ExtensionFormUtil;
+import org.openmrs.module.formentryapp.FormEntryAppService;
+import org.openmrs.module.formentryapp.FormManager;
+import org.openmrs.module.formentryapp.page.controller.forms.ExtensionForm;
+import org.openmrs.module.htmlformentry.HtmlForm;
+import org.openmrs.module.htmlformentry.HtmlFormEntryService;
+import org.openmrs.module.htmlformentryui.HtmlFormUtil;
+import org.openmrs.ui.framework.resource.ResourceFactory;
+import org.openmrs.ui.framework.resource.ResourceProvider;
+
+/**
+ * Sets up the HFE forms
+ * 1) Scans the webapp/resources/htmlforms folder
+ * 2) Attempts to create an HFE form from each of the files
+ * 3) Adds the forms as in Configure Metadata \ Manage Forms
+ */
+public class HtmlFormsInitializer implements Initializer {
+
+ protected static final Log log = LogFactory.getLog(HtmlFormsInitializer.class);
+
+ protected static final String formsPath = "htmlforms/";
+
+ protected String providerName;
+
+ public HtmlFormsInitializer(String newProviderName) {
+ this.providerName = newProviderName;
+ }
+
+ /**
+ * @see Initializer#started()
+ */
+ public synchronized void started() {
+ log.info("Setting HFE forms for " + getProviderName());
+
+ final ResourceFactory resourceFactory = ResourceFactory.getInstance();
+ final ResourceProvider resourceProvider = resourceFactory.getResourceProviders().get(getProviderName());
+
+ // Scanning the forms resources folder
+ final List formPaths = new ArrayList();
+ final File formsDir = resourceProvider.getResource(formsPath); // The ResourceFactory can't return File instances, hence the ResourceProvider need
+ if (formsDir == null || formsDir.isDirectory() == false) {
+ log.error("No HTML forms could be retrieved from the provided folder: " + getProviderName() + ":" + formsPath);
+ return;
+ }
+ for (File file : formsDir.listFiles())
+ formPaths.add(formsPath + file.getName()); // Adding each file's path to the list
+
+ // Save form + add its meta data
+ final FormManager formManager = Context.getRegisteredComponent("formManager", FormManager.class);
+ final FormEntryAppService hfeAppService = Context.getRegisteredComponent("formEntryAppService", FormEntryAppService.class);
+ final FormService formService = Context.getFormService();
+ final HtmlFormEntryService hfeService = Context.getService(HtmlFormEntryService.class);
+ for (String formPath : formPaths) {
+ // Save form
+ HtmlForm htmlForm = null;
+ try {
+ htmlForm = HtmlFormUtil.getHtmlFormFromUiResource(resourceFactory, formService, hfeService, getProviderName(), formPath);
+ try {
+ // Adds meta data
+ ExtensionForm extensionForm = ExtensionFormUtil.getExtensionFormFromUiResourceAndForm(resourceFactory, getProviderName(), formPath, hfeAppService, formManager, htmlForm.getForm());
+ log.info("The form at " + formPath + " has been successfully loaded with its metadata");
+ } catch (Exception e) {
+ log.error("The form was created but its extension point could not be created in Manage Forms \\ Configure Metadata: " + formPath, e);
+ throw new RuntimeException("The form was created but its extension point could not be created in Manage Forms \\ Configure Metadata: " + formPath, e);
+ }
+ } catch (IOException e) {
+ log.error("Could not generate HTML form from the following resource file: " + formPath, e);
+ throw new RuntimeException("Could not generate HTML form from the following resource file: " + formPath, e);
+ } catch (IllegalArgumentException e) {
+ log.error("Error while parsing the form's XML: " + formPath, e);
+ throw new IllegalArgumentException("Error while parsing the form's XML: " + formPath, e);
+ }
+
+ }
+ }
+
+ /**
+ * @see Initializer#stopped()
+ */
+ public void stopped() {
+ //TODO: Perhaps disable the forms?
+ }
+
+ /**
+ * Get the name of the provider where the forms are located - useful when extending this functionlality
+ *
+ * @return the name of the provider
+ */
+ public String getProviderName() {
+ return this.providerName;
+ }
+
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/activator/Initializer.java b/api/src/main/java/org/openmrs/module/ugandaemr/activator/Initializer.java
new file mode 100644
index 000000000..e4f6745b1
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/activator/Initializer.java
@@ -0,0 +1,33 @@
+package org.openmrs.module.ugandaemr.activator;
+
+/**
+ * The contents of this file are subject to the OpenMRS Public License
+ * Version 1.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://license.openmrs.org
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * Copyright (C) OpenMRS, LLC. All Rights Reserved.
+ */
+
+
+/**
+ * Interface for code to be run during the module activation process
+ * @see {@link https://raw.githubusercontent.com/PIH/openmrs-module-pihmalawi/master/api/src/main/java/org/openmrs/module/pihmalawi/activator/Initializer.java}
+ */
+public interface Initializer {
+
+ /**
+ * Run during the activator started method
+ */
+ void started();
+
+ /**
+ * Run during the activator stopped method
+ */
+ void stopped();
+}
\ No newline at end of file
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/PatientQueueVisitMapper.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/PatientQueueVisitMapper.java
new file mode 100644
index 000000000..a17fccaec
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/PatientQueueVisitMapper.java
@@ -0,0 +1,17 @@
+package org.openmrs.module.ugandaemr.api;
+
+import org.openmrs.module.patientqueueing.mapper.PatientQueueMapper;
+
+import java.io.Serializable;
+
+public class PatientQueueVisitMapper extends PatientQueueMapper implements Serializable {
+ Integer visitId;
+
+ public Integer getVisitId() {
+ return visitId;
+ }
+
+ public void setVisitId(Integer visitId) {
+ this.visitId = visitId;
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/UgandaEMRService.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/UgandaEMRService.java
new file mode 100644
index 000000000..b576a4c51
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/UgandaEMRService.java
@@ -0,0 +1,347 @@
+/**
+ * The contents of this file are subject to the OpenMRS Public License
+ * Version 1.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://license.openmrs.org
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * Copyright (C) OpenMRS, LLC. All Rights Reserved.
+ */
+package org.openmrs.module.ugandaemr.api;
+
+import org.openmrs.*;
+import org.openmrs.api.APIException;
+import org.openmrs.api.OpenmrsService;
+import org.openmrs.module.htmlformentry.FormEntrySession;
+import org.openmrs.module.ugandaemr.PublicHoliday;
+import org.springframework.transaction.annotation.Transactional;
+import org.openmrs.api.OpenmrsService;
+import org.openmrs.module.htmlformentry.FormEntrySession;
+import org.openmrs.module.patientqueueing.mapper.PatientQueueMapper;
+import org.openmrs.module.patientqueueing.model.PatientQueue;
+import org.openmrs.module.ugandaemr.api.lab.mapper.OrderMapper;
+import org.openmrs.module.ugandaemr.api.lab.util.TestResultModel;
+import org.openmrs.module.ugandaemr.pharmacy.DispensingModelWrapper;
+import org.openmrs.module.ugandaemr.pharmacy.mapper.PharmacyMapper;
+import org.openmrs.ui.framework.SimpleObject;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This service exposes module's core functionality. It is a Spring managed bean which is configured in moduleApplicationContext.xml.
+ *
+ * It can be accessed only via Context:
+ *
+ * Context.getService(ugandaemrService.class).someMethod();
+ *
+ *
+ * @see org.openmrs.api.context.Context
+ */
+@Transactional
+public interface UgandaEMRService extends OpenmrsService {
+
+ /*
+ * Link the infant with the A
+ *
+ */
+ public void linkExposedInfantToMotherViaARTNumber(Patient infant, String motherARTNumber);
+ public void linkExposedInfantToMotherViaARTNumber(Person infant, String motherARTNumber);
+ public void setAlertForAllUsers(String alertMessage);
+
+ /*
+ * This method generates Unique identification Code
+ * for all patients that do not have that id
+ * It is generated based on the person demographics
+ * submitted during patient registration
+ * This has been designed to run as an automatic task
+ * that run once a day for any patient that may not have the UIC already existing */
+
+ /**
+ * Generates a patients UIC (Unique Identifier Code) out of patient demographics
+ * @param patient the patient to be generated a UIC for
+ * @return String the UIC that has been generated
+ */
+ public String generatePatientUIC(Patient patient);
+
+ /**
+ * This method when called generates and saves UIC (Unique Identifier Code) for all patients who dont have the UIC
+ */
+ public void generateAndSaveUICForPatientsWithOut();
+
+ /**
+ * This Method stops all active out patient visits
+ */
+ public void stopActiveOutPatientVisits();
+
+
+ /**
+ * Gets transfer out encounters map.
+ * @param patient the patient whose transfer out encounters are being queried
+ * @param date the date of the transfer out it can be null
+ * @return map of transfer out encounters for a patient.
+ */
+ public Map transferredOut(Patient patient, Date date);
+
+ /**
+ * Gets transfer in encounters.
+ * @param patient the patient whose transfer in encounters are being queried
+ * @param date the date of the transfer in it can be null
+ * @return map of transfer in encounters for a patient.
+ */
+ public Map transferredIn(Patient patient,Date date);
+
+ /**
+ * Check if Patient is transferred out. This method depends on transferredOut(Patient patient) method
+ * @param patient
+ * @return boolean
+ */
+ public boolean isTransferredOut(Patient patient, Date date);
+
+
+ /**
+ * Check if Patient is a transfer in. This method depends on transferredIn(Patient patient) method
+ * @param patient
+ * @return boolean
+ */
+ public boolean isTransferredIn(Patient patient,Date date);
+
+ /**
+ * Transfer Information for patient
+ * @param patient
+ * @return Map
+ */
+ public List getTransferHistory(Patient patient);
+
+ public List getAllPublicHolidays() throws APIException;
+
+ public PublicHoliday getPublicHolidayByDate(Date publicHolidayDate) throws APIException;
+
+ public List getPublicHolidaysByDate(Date publicHolidayDate) throws APIException;
+
+ public PublicHoliday savePublicHoliday(PublicHoliday publicHoliday);
+
+ public PublicHoliday getPublicHolidaybyUuid(String uuid);
+
+
+ /**
+ * This method is used to create an HIV Summary encounter based on values from another encounter
+ * @param formEntrySession the formEntrySession where
+ * @return
+ */
+ public Encounter createPatientHIVSummaryEncounterOnTransferIn(FormEntrySession formEntrySession);
+
+ /**
+ * Checks id a patient has an HIV Summary page
+ *
+ * @param patient the patient to be changed
+ * @param encounterTypeUUID the uuid for the HIV encounter Type
+ * @return boolean
+ */
+ public Encounter hasHIVSummaryPage(Patient patient, String encounterTypeUUID);
+
+ /**
+ * Generates observation from an existing Observation
+ * @param observations a list of observation to look into for a specific concept
+ * @param lookUpConceptId the concept which will be used to lookup for an observation to be used to create another obs
+ * @param conceptIDForNewObs the concept id which will be the concept for the new observation.
+ * @param encounter the target encounter where the observation will be saved.
+ * @return an observation with a encounter, value and a concept.
+ */
+ public Obs generateObsFromObs(Set observations, Integer lookUpConceptId, Integer conceptIDForNewObs, Encounter encounter);
+
+ /**
+ * Helper Method to create Obs
+ * @param concept the concept
+ * @param encounter the encounter where the obs will be created
+ * @return a created obs
+ */
+ public Obs createNewObs(Concept concept, Encounter encounter);
+
+
+ /**
+ * @param patientQueueList
+ * @return
+ */
+ public List mapPatientQueueToMapper(List patientQueueList);
+
+ /**
+ * Render Tests
+ * @param test
+ * @return
+ */
+ public Set renderTests(Order test);
+
+ /**
+ * Check if Sample ID exists
+ * @param sampleId
+ * @param orderNumber
+ * @return
+ * @throws ParseException
+ */
+ public boolean isSampleIdExisting(String sampleId, String orderNumber) throws ParseException;
+
+ /**
+ * Process Orders
+ * @param query
+ * @param asOfDate
+ * @param includeProccesed
+ * @return
+ * @throws ParseException
+ * @throws IOException
+ */
+ public SimpleObject getProcessedOrders(String query, Date asOfDate, boolean includeProccesed) throws ParseException,
+ IOException;
+
+ /**
+ * Convert Orders to OrderMappers
+ *
+ * @param orders
+ * @param fiterOutProccessed
+ * @return
+ */
+ public Set processOrders(Set orders, boolean fiterOutProccessed);
+
+ /**
+ * @param encounter
+ * @param testConcept
+ * @param testGroupConcept
+ * @param result
+ * @param test
+ */
+ public void addLaboratoryTestObservation(Encounter encounter, Concept testConcept, Concept testGroupConcept,
+ String result, Order test);
+ /**
+ * With Orders
+ * @param patientQueueList
+ * @return
+ */
+ public List mapPatientQueueToMapperWithOrders(List patientQueueList);
+
+
+
+ /**
+ * With Orders
+ *
+ * @param patientQueueList
+ * @return
+ */
+ public List mapPatientQueueToMapperWithDrugOrders(List patientQueueList);
+
+ /**
+ * Process Orders
+ * @param formSession
+ * @return
+ */
+ public Encounter processLabTestOrdersFromEncounterObs(FormEntrySession formSession, boolean completePreviousQueue);
+
+
+ /**
+ * Process Orders
+ *
+ * @param formSession
+ * @return
+ */
+ public Encounter processDrugOrdersFromEncounterObs(FormEntrySession formSession, boolean completePreviousQueue);
+
+
+ /**
+ * Send Patient To Lab
+ * @param session
+ */
+ public void sendPatientToNextLocation(FormEntrySession session, String locationUUID, String locationFromUUID, PatientQueue.Status nextQueueStatus, boolean completePreviousQueue);
+
+
+
+ /**
+ * @param encounter
+ * @return
+ */
+ Provider getProviderFromEncounter(Encounter encounter);
+
+ /**
+ * @param query
+ * @param encounterId
+ * @param includeProccesed
+ * @return
+ * @throws ParseException
+ * @throws IOException
+ */
+ public SimpleObject getOrderResultsOnEncounter(String query, int encounterId, boolean includeProccesed)
+ throws ParseException, IOException;
+
+ /**
+ * @param encounter
+ * @param locationTo
+ * @return
+ * @throws ParseException
+ */
+ public boolean patientQueueExists(Encounter encounter, Location locationTo, Location locationFrom, PatientQueue.Status status) throws ParseException;
+
+ /**
+ * Complete Previous Queue of Patient
+ * @param patient
+ * @param location
+ * @param searchStatus
+ * @return
+ */
+ public PatientQueue completePreviousQueue(Patient patient, Location location, PatientQueue.Status searchStatus);
+
+
+ /**
+ * @param patient
+ * @param location
+ * @return
+ */
+ public PatientQueue getPreviousQueue(Patient patient, Location location, PatientQueue.Status status);
+
+
+ /**
+ * This Method completes all facility out patient active patient visits found.
+ * @param patient the patient whose visits are to be completed
+ */
+ public void completePatientActiveVisit(Patient patient);
+
+
+
+ /**
+ * Dispenses medications in pharmacy
+ * @param resultWrapper the data object containing dispensing information
+ * @param provider the provider dispensing the medication
+ * @param location the location where the medication is being dispensed from
+ * @return simple object containing information about that status of dispensing
+ */
+ public SimpleObject dispenseMedication(DispensingModelWrapper resultWrapper, Provider provider, Location location);
+
+ /**
+ * This Method creates a patient Program Attribute for a given patient in a given program
+ * @param programAttributeType the programAttribute Type which will be created
+ * @param patientProgram The Patient Program where the program attribute will be added to
+ * @param value the value of the Program attribute
+ * @return
+ */
+ public PatientProgramAttribute generatePatientProgramAttribute(ProgramAttributeType programAttributeType, PatientProgram patientProgram, String value);
+
+ /**
+ * This creates program attribute from an observation list
+ * @param patientProgram the patient program where the programAttribute will be added
+ * @param observations an observation list where the programAttribute will be generated
+ * @param conceptID the conceptId which will bw used to match the target observation
+ * @param programAttributeUUID the uuid of the programAttribute to be created.
+ * @return
+ */
+ public PatientProgramAttribute generatePatientProgramAttributeFromObservation(PatientProgram patientProgram, Set observations, Integer conceptID, String programAttributeUUID);
+ /**
+ * @param patientQueueList
+ * @return
+ */
+
+}
\ No newline at end of file
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/db/UgandaEMRDAO.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/db/UgandaEMRDAO.java
new file mode 100644
index 000000000..e85004d4c
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/db/UgandaEMRDAO.java
@@ -0,0 +1,41 @@
+/**
+ * The contents of this file are subject to the OpenMRS Public License
+ * Version 1.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://license.openmrs.org
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * Copyright (C) OpenMRS, LLC. All Rights Reserved.
+ */
+package org.openmrs.module.ugandaemr.api.db;
+
+import java.util.Date;
+import java.util.List;
+
+import org.openmrs.module.ugandaemr.api.UgandaEMRService;
+import org.openmrs.module.ugandaemr.PublicHoliday;
+
+/**
+ * Database methods for {@link UgandaEMRService}.
+ */
+public interface UgandaEMRDAO {
+
+ /*
+ * Add DAO methods here
+ */
+
+ public List getAllPublicHolidays();
+
+ public PublicHoliday getPublicHolidayByDate(Date publicHolidayDate);
+
+ public PublicHoliday savePublicHoliday(PublicHoliday publicHolidays);
+
+ public PublicHoliday getPublicHolidaybyUuid(String uuid);
+
+ public List getPublicHolidaysByDate(Date publicHolidayDate);
+
+}
\ No newline at end of file
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/db/hibernate/HibernateUgandaEMRDAO.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/db/hibernate/HibernateUgandaEMRDAO.java
new file mode 100644
index 000000000..8be4d862d
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/db/hibernate/HibernateUgandaEMRDAO.java
@@ -0,0 +1,73 @@
+/**
+ * The contents of this file are subject to the OpenMRS Public License
+ * Version 1.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://license.openmrs.org
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * Copyright (C) OpenMRS, LLC. All Rights Reserved.
+ */
+package org.openmrs.module.ugandaemr.api.db.hibernate;
+
+import java.util.Date;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.SessionFactory;
+import org.hibernate.criterion.Restrictions;
+import org.openmrs.api.APIException;
+import org.openmrs.module.ugandaemr.api.db.UgandaEMRDAO;
+import org.openmrs.module.ugandaemr.PublicHoliday;
+
+/**
+ * It is a default implementation of {@link UgandaEMRDAO}.
+ */
+public class HibernateUgandaEMRDAO implements UgandaEMRDAO {
+ protected final Log log = LogFactory.getLog(this.getClass());
+
+ private SessionFactory sessionFactory;
+
+ /**
+ * @param sessionFactory the sessionFactory to set
+ */
+ public void setSessionFactory(SessionFactory sessionFactory) {
+ this.sessionFactory = sessionFactory;
+ }
+
+ /**
+ * @return the sessionFactory
+ */
+ public SessionFactory getSessionFactory() {
+ return sessionFactory;
+ }
+
+ public List getAllPublicHolidays() {
+ return (List) getSessionFactory().getCurrentSession().createCriteria(PublicHoliday.class).list();
+ }
+
+ public PublicHoliday getPublicHolidayByDate(Date publicHolidayDate) throws APIException {
+ return (PublicHoliday) getSessionFactory().getCurrentSession().createCriteria(PublicHoliday.class).add(Restrictions.eq("date", publicHolidayDate)).add(Restrictions.eq("voided", false)).uniqueResult();
+ }
+
+ public PublicHoliday savePublicHoliday(PublicHoliday publicHoliday) {
+ getSessionFactory().getCurrentSession().saveOrUpdate(publicHoliday);
+ return publicHoliday;
+ }
+
+ @Override
+ public PublicHoliday getPublicHolidaybyUuid(String uuid) {
+ return (PublicHoliday) getSessionFactory().getCurrentSession().createCriteria(PublicHoliday.class).add(Restrictions.eq("uuid", uuid))
+ .uniqueResult();
+ }
+
+ @Override
+ public List getPublicHolidaysByDate(Date publicHolidayDate) {
+ return (List) getSessionFactory().getCurrentSession().createCriteria(PublicHoliday.class).add(Restrictions.eq("date", publicHolidayDate)).list();
+ }
+
+}
\ No newline at end of file
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/deploy/bundle/AddressComponent.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/deploy/bundle/AddressComponent.java
new file mode 100644
index 000000000..c89cbd5f5
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/deploy/bundle/AddressComponent.java
@@ -0,0 +1,66 @@
+package org.openmrs.module.ugandaemr.api.deploy.bundle;
+
+import org.openmrs.module.addresshierarchy.AddressField;
+
+/**
+ * Represents a component of a hierarchical address
+ */
+public class AddressComponent {
+
+ private AddressField field;
+ private String nameMapping;
+ private int sizeMapping;
+ private String elementDefault;
+ private boolean requiredInHierarchy;
+
+ public AddressComponent() {
+ }
+
+ public AddressComponent(AddressField field, String nameMapping, int sizeMapping, String elementDefault, boolean requiredInHierarchy) {
+ this.field = field;
+ this.nameMapping = nameMapping;
+ this.sizeMapping = sizeMapping;
+ this.elementDefault = elementDefault;
+ this.requiredInHierarchy = requiredInHierarchy;
+ }
+
+ public AddressField getField() {
+ return field;
+ }
+
+ public void setField(AddressField field) {
+ this.field = field;
+ }
+
+ public String getNameMapping() {
+ return nameMapping;
+ }
+
+ public void setNameMapping(String nameMapping) {
+ this.nameMapping = nameMapping;
+ }
+
+ public int getSizeMapping() {
+ return sizeMapping;
+ }
+
+ public void setSizeMapping(int sizeMapping) {
+ this.sizeMapping = sizeMapping;
+ }
+
+ public String getElementDefault() {
+ return elementDefault;
+ }
+
+ public void setElementDefault(String elementDefault) {
+ this.elementDefault = elementDefault;
+ }
+
+ public boolean isRequiredInHierarchy() {
+ return requiredInHierarchy;
+ }
+
+ public void setRequiredInHierarchy(boolean requiredInHierarchy) {
+ this.requiredInHierarchy = requiredInHierarchy;
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/deploy/bundle/AddressMetadataBundle.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/deploy/bundle/AddressMetadataBundle.java
new file mode 100644
index 000000000..39d2b7b63
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/deploy/bundle/AddressMetadataBundle.java
@@ -0,0 +1,161 @@
+package org.openmrs.module.ugandaemr.api.deploy.bundle;
+
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.beanutils.MethodUtils;
+import org.apache.commons.io.IOUtils;
+import org.openmrs.api.APIException;
+import org.openmrs.api.SerializationService;
+import org.openmrs.api.context.Context;
+import org.openmrs.module.addresshierarchy.AddressHierarchyLevel;
+import org.openmrs.module.addresshierarchy.service.AddressHierarchyService;
+import org.openmrs.module.addresshierarchy.util.AddressHierarchyImportUtil;
+import org.openmrs.module.metadatadeploy.bundle.VersionedMetadataBundle;
+import org.openmrs.util.OpenmrsConstants;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public abstract class AddressMetadataBundle extends VersionedMetadataBundle {
+
+ @Autowired
+ SerializationService serializationService;
+
+ /**
+ * @return the ordered list of address components that make up the address configuration
+ */
+ public abstract List getAddressComponents();
+
+ /**
+ * @return the line-by-line format needed by the address template
+ */
+ public abstract List getLineByLineFormat();
+
+ /**
+ * @return the location on the classpath from which to load the address hierarchy entries
+ */
+ public abstract String getAddressHierarchyEntryPath();
+
+ @Override
+ protected void installEveryTime() throws Exception {
+ installAddressTemplate();
+ }
+
+ @Override
+ protected void installNewVersion() throws Exception {
+
+ AddressHierarchyService service = Context.getService(AddressHierarchyService.class);
+
+ // currently we only install the levels if they haven't been installed; no built-in way to edit anything other than "required" at this point
+ int numberOfLevels = service.getAddressHierarchyLevelsCount();
+ if (numberOfLevels == 0) {
+ installAddressHierarchyLevels();
+ } else {
+ updateRequiredProperty();
+ }
+
+ installAddressHierarchyEntries();
+ }
+
+ /**
+ * Install the appropriate address template
+ */
+ public void installAddressTemplate() throws Exception {
+ log.info("Installing Address Template");
+ String template = serializationService.getDefaultSerializer().serialize(getAddressTemplate());
+ setGlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_ADDRESS_TEMPLATE, template);
+ }
+
+ /**
+ * @return a new AddressTemplate instance for the given configuration
+ */
+ public Object getAddressTemplate() {
+ Object addressTemplate = null;
+ try {
+ Constructor constructor = Context.loadClass("org.openmrs.layout.web.address.AddressTemplate").getConstructor(String.class);
+ addressTemplate = constructor.newInstance("");
+ }
+ catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
+ try {
+ Constructor constructor = Context.loadClass("org.openmrs.layout.address.AddressTemplate").getConstructor(String.class);
+ addressTemplate = constructor.newInstance("");
+ }
+ catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) {
+ throw new APIException("Error while getting address template", ex);
+ }
+ }
+
+ Map nameMappings = new HashMap();
+ Map sizeMappings = new HashMap();
+ Map elementDefaults = new HashMap();
+ for (AddressComponent c : getAddressComponents()) {
+ nameMappings.put(c.getField().getName(), c.getNameMapping());
+ sizeMappings.put(c.getField().getName(), Integer.toString(c.getSizeMapping()));
+ if (c.getElementDefault() != null) {
+ elementDefaults.put(c.getField().getName(), c.getElementDefault());
+ }
+ }
+
+ try {
+ MethodUtils.invokeExactMethod(addressTemplate, "setNameMappings", new Object[]{ nameMappings }, new Class[] { Map.class });
+ MethodUtils.invokeExactMethod(addressTemplate, "setSizeMappings", new Object[]{ sizeMappings }, new Class[] { Map.class });
+ MethodUtils.invokeExactMethod(addressTemplate, "setElementDefaults", new Object[]{ elementDefaults }, new Class[] { Map.class });
+ MethodUtils.invokeExactMethod(addressTemplate, "setLineByLineFormat", new Object[]{ getLineByLineFormat() }, new Class[] { List.class });
+ }
+ catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+ throw new APIException("Error while getting address template", e);
+ }
+
+ return addressTemplate;
+ }
+
+ /**
+ * Installed the address hierarchy levels
+ */
+ public void installAddressHierarchyLevels() {
+ AddressHierarchyService service = Context.getService(AddressHierarchyService.class);
+ log.info("Installing Address Levels");
+ AddressHierarchyLevel lastLevel = null;
+ for (AddressComponent component : getAddressComponents()) {
+ AddressHierarchyLevel level = new AddressHierarchyLevel();
+ level.setAddressField(component.getField());
+ level.setRequired(component.isRequiredInHierarchy());
+ level.setParent(lastLevel);
+ service.saveAddressHierarchyLevel(level);
+ lastLevel = level;
+ }
+ }
+
+ /**
+ * Iterate through the levels and update the "required" property in case it has changed
+ */
+ public void updateRequiredProperty() {
+ AddressHierarchyService service = Context.getService(AddressHierarchyService.class);
+ log.info("Installing Address Levels");
+ for (AddressComponent component : getAddressComponents()) {
+ AddressHierarchyLevel level = service.getAddressHierarchyLevelByAddressField(component.getField());
+ level.setRequired(component.isRequiredInHierarchy());
+ service.saveAddressHierarchyLevel(level);
+ }
+ }
+
+
+ /**
+ * Installs a new version of the address hierarchy entries
+ */
+ public void installAddressHierarchyEntries() {
+ log.info("Installing Address Hierarchy Entries");
+ Context.getService(AddressHierarchyService.class).deleteAllAddressHierarchyEntries();
+ InputStream is = null;
+ try {
+ is = getClass().getClassLoader().getResourceAsStream(getAddressHierarchyEntryPath());
+ AddressHierarchyImportUtil.importAddressHierarchyFile(is, "\\|", "\\^");
+ } finally {
+ IOUtils.closeQuietly(is);
+ }
+ }
+
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/deploy/bundle/CommonMetadataBundle.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/deploy/bundle/CommonMetadataBundle.java
new file mode 100644
index 000000000..991b50464
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/deploy/bundle/CommonMetadataBundle.java
@@ -0,0 +1,98 @@
+package org.openmrs.module.ugandaemr.api.deploy.bundle;
+
+import org.openmrs.module.ugandaemr.metadata.core.*;
+import org.openmrs.module.metadatadeploy.bundle.AbstractMetadataBundle;
+import static org.openmrs.module.metadatadeploy.bundle.CoreConstructors.encounterRole;
+import org.springframework.stereotype.Component;
+
+/**
+ * Installs the common metadata
+ */
+@Component
+public class CommonMetadataBundle extends AbstractMetadataBundle {
+
+ /**
+ * @see org.openmrs.module.metadatadeploy.bundle.AbstractMetadataBundle#install()
+ */
+ public void install() throws Exception {
+ // install the patient identifier types
+ log.info("Installing PatientIdentifierTypes");
+ install(PatientIdentifierTypes.HIV_CARE_NUMBER);
+ install(PatientIdentifierTypes.OLD_OPENMRS_IDENTIFICATION_NUMBER);
+ install(PatientIdentifierTypes.OPENMRS_ID);
+ install(PatientIdentifierTypes.OPENMRS_IDENTIFICATION_NUMBER);
+ install(PatientIdentifierTypes.EXPOSED_INFANT_NUMBER);
+ install(PatientIdentifierTypes.ANC_NUMBER);
+ install(PatientIdentifierTypes.PNC_NUMBER);
+ install(PatientIdentifierTypes.IPD_NUMBER);
+ install(PatientIdentifierTypes.NATIONAL_ID);
+ install(PatientIdentifierTypes.ART_PATIENT_NUMBER);
+ install(PatientIdentifierTypes.RESEARCH_PATIENT_ID);
+ install(PatientIdentifierTypes.SMC_CLIENT_NUMBER);
+ install(PatientIdentifierTypes.REFUGEE_IDENTIFICATION_NUMBER);
+ install(PatientIdentifierTypes.PATIENT_IUC_HEALTH_ID);
+ log.info("Patient IdentifierTypes installed");
+
+ // install person attribute types
+ log.info("Installing PatientAttributeTypes");
+ install(PersonAttributeTypes.MARITAL_STATUS);
+ install(PersonAttributeTypes.HEALTH_CENTER);
+ install(PersonAttributeTypes.HEALTH_FACILITY_DISTRICT);
+ install(PersonAttributeTypes.TELEPHONE_NUMBER_2);
+ install(PersonAttributeTypes.TELEPHONE_NUMBER_3);
+ install(PersonAttributeTypes.OCCUPATION);
+ install(PersonAttributeTypes.NATIONALITY);
+ log.info("Person AttributeTypes installed");
+
+ // install roles
+ log.info("Installing roles");
+ install(Roles.MID_WIFE);
+ log.info("Roles installed");
+
+
+ //Install Encounter Type
+ log.info("Installing EncounterTypes");
+ install(EncounterTypes.PNC_ENCOUNTER_TYPE);
+ install(EncounterTypes.SMC_FOLLOW_UP_ENCOUNTER);
+ install(EncounterTypes.OPD_ENCOUNTER);
+ install(EncounterTypes.TB_SUMMARY_ENCOUNTER);
+ install(EncounterTypes.TB_FOLLOWUP_ENCOUNTER);
+ install(EncounterTypes.VIRAL_LOAD_NON_SUPPRESSED);
+ install(EncounterTypes.APPOINTMENT_FOLLOW_UP);
+ install(EncounterTypes.TRIAGE);
+ install(EncounterTypes.MEDICATION_DISPENSE);
+ install(EncounterTypes.MISSED_APPOINTMENT_TRACKING);
+ install(EncounterTypes.TRANSFER_IN);
+ install(EncounterTypes.TRANSFER_OUT);
+ install(EncounterTypes.DR_TB_SUMMARY_ENCOUNTER);
+ install(EncounterTypes.DR_TB_FOLLOWUP_ENCOUNTER);
+
+ //installing programs metadata
+ log.info("Installing Programs");
+ install(Programs.HIV_PROGRAM);
+ install(Programs.TB_PROGRAM);
+ install(Programs.MCH_PROGRAM);
+ install(Programs.NUTRITION_PROGRAM);
+ install(Programs.FBIM_PROGRAM);
+ install(Programs.FBG_PROGRAM);
+ install(Programs.FTR_PROGRAM);
+ install(Programs.CCLAD_PROGRAM);
+ install(Programs.CDDP_PROGRAM);
+
+ //install Locations
+ log.info("Installing Locations");
+ install(Locations.TB_CLINIC);
+ install(Locations.OPD_CLINIC);
+ install(Locations.UNKNOWN);
+ install(Locations.PHARMACY);
+ install(Locations.RECEPTION);
+ install(Locations.TRIAGE);
+ install(Locations.COUNSELING_CENTER);
+ install(Locations.Community);
+
+
+ // Install Encounter Role
+ install(encounterRole(EncounterRoles.ASSISTANT_CIRCUMCISER_NAME,EncounterRoles.ASSISTANT_CIRCUMCISER_DESCRIPTION,EncounterRoles.ASSISTANT_CIRCUMCISER_UUID));
+ install(encounterRole(EncounterRoles.PHARMACIST_NAME,EncounterRoles.PHARMACIST_DESCRIPTION,EncounterRoles.PHARMACIST_UUID));
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/deploy/bundle/UgandaAddressMetadataBundle.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/deploy/bundle/UgandaAddressMetadataBundle.java
new file mode 100644
index 000000000..e737dce79
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/deploy/bundle/UgandaAddressMetadataBundle.java
@@ -0,0 +1,64 @@
+package org.openmrs.module.ugandaemr.api.deploy.bundle;
+
+import org.springframework.stereotype.Component;
+import org.openmrs.module.addresshierarchy.AddressField;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by ssmusoke on 07/01/2016.
+ */
+@Component
+public class UgandaAddressMetadataBundle extends AddressMetadataBundle {
+ /**
+ * @return the ordered list of address components that make up the address configuration
+ */
+ @Override
+ public List getAddressComponents() {
+ List l = new ArrayList();
+ l.add(new AddressComponent(AddressField.COUNTRY, "ugandaemr.address.country", 40, "Uganda", true));
+ l.add(new AddressComponent(AddressField.COUNTY_DISTRICT, "ugandaemr.address.district", 40, null, true));
+ l.add(new AddressComponent(AddressField.STATE_PROVINCE, "ugandaemr.address.county", 40, null, true));
+ l.add(new AddressComponent(AddressField.ADDRESS_3, "ugandaemr.address.subcounty", 60, null, true));
+ l.add(new AddressComponent(AddressField.ADDRESS_4, "ugandaemr.address.parish", 60, null, true));
+ l.add(new AddressComponent(AddressField.ADDRESS_5, "ugandaemr.address.village", 60, null, false));
+
+ return l;
+ }
+
+ /**
+ * @return the line-by-line format needed by the address template
+ */
+ @Override
+ public List getLineByLineFormat() {
+ List l = new ArrayList();
+ l.add("country");
+ l.add("countyDistrict");
+ l.add("stateProvince");
+ l.add("address3");
+ l.add("address4");
+ l.add("address5");
+ return l;
+ }
+
+ /**
+ * @return the location on the classpath from which to load the address hierarchy entries
+ */
+ @Override
+ public String getAddressHierarchyEntryPath() {
+ // for each new version rename the file being used see getVersion()
+ return "metadata/address_hierarchy_entries_" + getVersion() + ".csv";
+ }
+
+ /**
+ * You need to increment this every time you commit a new change to the bundle, so that the infrastructure knows
+ * whether or not to call #installNewVersion() (based on checking a global property)
+ *
+ * @return
+ */
+ @Override
+ public int getVersion() {
+ return 4;
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/deploy/bundle/UgandaEMRPatientFlagMetadataBundle.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/deploy/bundle/UgandaEMRPatientFlagMetadataBundle.java
new file mode 100644
index 000000000..9a3207b4c
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/deploy/bundle/UgandaEMRPatientFlagMetadataBundle.java
@@ -0,0 +1,54 @@
+package org.openmrs.module.ugandaemr.api.deploy.bundle;
+
+import org.openmrs.module.ugandaemr.metadata.core.Flags;
+import org.openmrs.module.ugandaemr.metadata.core.Priorites;
+import org.openmrs.module.ugandaemr.metadata.core.Tags;
+import org.openmrs.module.metadatadeploy.bundle.AbstractMetadataBundle;
+import org.openmrs.module.patientflags.metadatadeploy.bundle.PatientFlagMetadataBundle;
+import org.springframework.stereotype.Component;
+
+
+/**
+ * Installs metadata for Patient Flags
+ */
+@Component
+public class UgandaEMRPatientFlagMetadataBundle extends PatientFlagMetadataBundle {
+
+ /**
+ * @see AbstractMetadataBundle#install()
+ */
+ public void install() throws Exception {
+ // Tags
+ log.info("Installing Patient flag tags");
+ install(Tags.PATIENT_STATUS);
+
+ // Priorites
+ log.info("Installing patient flag priorities");
+ install(Priorites.GREEN);
+ install(Priorites.RED);
+ install(Priorites.ORANGE);
+
+ // Flags
+ log.info("Installing flags");
+ install(Flags.DUE_FOR_FIRST_VIRAL_LOAD);
+ install(Flags.OVERDUE_FOR_FIRST_VIRAL_LOAD);
+ install(Flags.MISSED_APPOINTMENT);
+ install(Flags.UPCOMING_APPOINTMENT);
+ install(Flags.PATIENT_LOST);
+ install(Flags.PATIENT_LOST_TO_FOLLOWUP);
+ install(Flags.DUE_FOR_ROUTINE_VIRAL_LOAD);
+ install(Flags.OVERDUE_FOR_ROUTINE_VIRAL_LOAD);
+ install(Flags.DUE_FOR_FIRST_DNA_PCR);
+ install(Flags.OVERDUE_FOR_FIRST_DNA_PCR);
+ install(Flags.DUE_FOR_SECOND_DNA_PCR);
+ install(Flags.OVERDUE_FOR_SECOND_DNA_PCR);
+ install(Flags.DUE_FOR_RAPID_TEST);
+ install(Flags.DUE_FOR_THIRD_DNA_PCR);
+ install(Flags.OVERDUE_FOR_THIRD_DNA_PCR);
+ install(Flags.OVERDUE_FOR_RAPID_TEST);
+ install(Flags.HAS_DETECTABLE_VIRAL_LOAD);
+ install(Flags.PATIENT_TRANSFERED_OUT);
+ }
+
+
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/deploy/sync/EncounterTypeSynchronization.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/deploy/sync/EncounterTypeSynchronization.java
new file mode 100644
index 000000000..1ab9ffedd
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/deploy/sync/EncounterTypeSynchronization.java
@@ -0,0 +1,48 @@
+package org.openmrs.module.ugandaemr.api.deploy.sync;
+
+import org.openmrs.EncounterType;
+import org.openmrs.api.EncounterService;
+import org.openmrs.module.metadatadeploy.sync.ObjectSynchronization;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * Created by ssmusoke on 06/01/2016.
+ */
+@Component
+public class EncounterTypeSynchronization implements ObjectSynchronization {
+ @Autowired
+ private EncounterService encounterService;
+
+ /**
+ * Fetches all existing encounter types
+ *
+ * @return the existing encounter types
+ */
+ public List fetchAllExisting() {
+ return encounterService.getAllEncounterTypes(true);
+ }
+
+ /**
+ * Gets the synchronization key of the given object
+ *
+ * @param obj the object
+ * @return the synchronization key
+ */
+ public Object getObjectSyncKey(EncounterType obj) {
+ return obj.getUuid();
+ }
+
+ /**
+ * Compares two objects and returns true if an update is required because there are differences
+ *
+ * @param incoming the incoming object
+ * @param existing the existing object
+ * @return true is there are differences
+ */
+ public boolean updateRequired(EncounterType incoming, EncounterType existing) {
+ return true; // Always update existing object (not very efficient, but is what we want to do)
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/impl/UgandaEMRServiceImpl.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/impl/UgandaEMRServiceImpl.java
new file mode 100644
index 000000000..438deb8be
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/impl/UgandaEMRServiceImpl.java
@@ -0,0 +1,1632 @@
+package org.openmrs.module.ugandaemr.api.impl;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.openmrs.*;
+import org.openmrs.api.*;
+import org.openmrs.api.context.Context;
+import org.openmrs.api.impl.BaseOpenmrsService;
+import org.openmrs.module.htmlformentry.FormEntrySession;
+import org.openmrs.module.patientqueueing.api.PatientQueueingService;
+import org.openmrs.module.patientqueueing.mapper.PatientQueueMapper;
+import org.openmrs.module.patientqueueing.model.PatientQueue;
+import org.openmrs.module.ugandaemr.PublicHoliday;
+import org.openmrs.module.ugandaemr.UgandaEMRConstants;
+import org.openmrs.module.ugandaemr.api.PatientQueueVisitMapper;
+import org.openmrs.module.ugandaemr.api.UgandaEMRService;
+import org.openmrs.module.ugandaemr.api.db.UgandaEMRDAO;
+import org.openmrs.module.ugandaemr.api.lab.mapper.LabQueueMapper;
+import org.openmrs.module.ugandaemr.api.lab.mapper.OrderMapper;
+import org.openmrs.module.ugandaemr.api.lab.util.LaboratoryUtil;
+import org.openmrs.module.ugandaemr.api.lab.util.TestResultModel;
+import org.openmrs.module.ugandaemr.metadata.core.Locations;
+import org.openmrs.module.ugandaemr.metadata.core.PatientIdentifierTypes;
+import org.openmrs.module.ugandaemr.pharmacy.DispensingModelWrapper;
+import org.openmrs.module.ugandaemr.pharmacy.mapper.DrugOrderMapper;
+import org.openmrs.module.ugandaemr.pharmacy.mapper.PharmacyMapper;
+import org.openmrs.notification.Alert;
+import org.openmrs.order.OrderUtil;
+import org.openmrs.parameter.EncounterSearchCriteria;
+import org.openmrs.parameter.EncounterSearchCriteriaBuilder;
+import org.openmrs.ui.framework.SimpleObject;
+import org.openmrs.util.OpenmrsUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+import static org.openmrs.module.ugandaemr.UgandaEMRConstants.*;
+import static org.openmrs.module.ugandaemr.metadata.core.EncounterTypes.TRANSFER_IN;
+import static org.openmrs.module.ugandaemr.metadata.core.EncounterTypes.TRANSFER_OUT;
+
+public class UgandaEMRServiceImpl extends BaseOpenmrsService implements UgandaEMRService {
+
+ protected final Log log = LogFactory.getLog(this.getClass());
+
+ private UgandaEMRDAO dao;
+
+ @Autowired
+ private PatientService patientService;
+
+ @Autowired
+ private PersonService personService;
+
+ /**
+ * @param dao the dao to set
+ */
+ public void setDao(UgandaEMRDAO dao) {
+ this.dao = dao;
+ }
+
+ /**
+ * @return the dao
+ */
+ public UgandaEMRDAO getDao() {
+ return dao;
+ }
+
+ private String ordersListLabel = "ordersList";
+
+ @Override
+ public void linkExposedInfantToMotherViaARTNumber(Person infant, String motherARTNumber) {
+ log.debug("Linking infant with ID " + infant.getPersonId() + " to mother with ART Number " + motherARTNumber);
+ List artNumberPatientidentifierTypes = new ArrayList<>();
+ artNumberPatientidentifierTypes.add(Context.getPatientService().getPatientIdentifierTypeByUuid(PatientIdentifierTypes.ART_PATIENT_NUMBER.uuid()));
+ artNumberPatientidentifierTypes.add(Context.getPatientService().getPatientIdentifierTypeByUuid(PatientIdentifierTypes.HIV_CARE_NUMBER.uuid()));
+ // find the mother by identifier
+ List mothers = patientService.getPatients(null, // name of the person
+ motherARTNumber, //mother ART number
+ artNumberPatientidentifierTypes, // ART Number and HIV Clinic number
+ true); // match Identifier exactly
+ if (mothers.size() != 0) {
+ Person potentialMother = mothers.get(0).getPerson();
+ // mothers have to be female and above 12 years of age
+ if (potentialMother.getAge() != null && potentialMother.getAge() > 12 & potentialMother.getGender().equals("F")) {
+ Relationship relationship = new Relationship();
+ relationship.setRelationshipType(personService.getRelationshipTypeByUuid("8d91a210-c2cc-11de-8d13-0010c6dffd0f"));
+ relationship.setPersonA(potentialMother);
+ relationship.setPersonB(infant);
+ personService.saveRelationship(relationship);
+ log.debug("Infant with ID " + infant.getPersonId() + " linked to mother with ID " + potentialMother.getPersonId());
+ }
+ }
+ }
+
+ @Override
+ public void linkExposedInfantToMotherViaARTNumber(Patient infant, String motherARTNumber) {
+ linkExposedInfantToMotherViaARTNumber(infant.getPerson(), motherARTNumber);
+ }
+
+ public void setAlertForAllUsers(String alertMessage) {
+ List userList = Context.getUserService().getAllUsers();
+ Alert alert = new Alert();
+ for (User user : userList) {
+ alert.addRecipient(user);
+ }
+ alert.setText(alertMessage);
+ Context.getAlertService().saveAlert(alert);
+ }
+
+ /**
+ * @see org.openmrs.module.ugandaemr.api.UgandaEMRService#generatePatientUIC(org.openmrs.Patient)
+ */
+ @Override
+ public String generatePatientUIC(Patient patient) {
+ String familyNameCode = "";
+ String givenNameCode = "";
+ String middleNameCode = "";
+ String countryCode = "";
+ String genderCode = "";
+ Date dob = patient.getBirthdate();
+
+ if (dob == null) {
+ return null;
+ }
+
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(dob);
+ String monthCode = "";
+ String year = (cal.get(Calendar.YEAR) + "").substring(2, 4);
+
+
+ if (cal.get(Calendar.MONTH) <= 8) {
+ monthCode = "0" + (cal.get(Calendar.MONTH)+1);
+ } else {
+ monthCode = "" + (cal.get(Calendar.MONTH)+1);
+ }
+
+ if (patient.getGender().equals("F")) {
+ genderCode = "2";
+ } else {
+ genderCode = "1";
+ }
+
+ if (patient.getPerson().getPersonAddress() != null && !patient.getPerson().getPersonAddress().getCountry().isEmpty()) {
+ countryCode = patient.getPerson().getPersonAddress().getCountry().substring(0, 1);
+ } else {
+ countryCode = "X";
+ }
+
+ if (patient.getFamilyName()!=null&&!patient.getFamilyName().isEmpty()) {
+ String firstLetter = replaceLettersWithNumber(patient.getFamilyName().substring(0, 1));
+ String secondLetter = replaceLettersWithNumber(patient.getFamilyName().substring(1, 2));
+ String thirdLetter = replaceLettersWithNumber(patient.getFamilyName().substring(2, 3));
+ familyNameCode = firstLetter + secondLetter + thirdLetter;
+ } else {
+ familyNameCode = "X";
+ }
+
+ if (patient.getGivenName()!=null&& !patient.getGivenName().isEmpty()) {
+ String firstLetter = replaceLettersWithNumber(patient.getGivenName().substring(0, 1));
+ String secondLetter = replaceLettersWithNumber(patient.getGivenName().substring(1, 2));
+ String thirdLetter = replaceLettersWithNumber(patient.getGivenName().substring(2, 3));
+ givenNameCode = firstLetter + secondLetter + thirdLetter;
+ } else {
+ givenNameCode = "X";
+ }
+
+ if (patient.getMiddleName()!=null&&!patient.getMiddleName().isEmpty()) {
+ middleNameCode = replaceLettersWithNumber(patient.getMiddleName().substring(0, 1));
+ } else {
+ middleNameCode = "X";
+ }
+
+
+ return countryCode + "-" + monthCode + year + "-" + genderCode + "-" + givenNameCode + familyNameCode + middleNameCode;
+ }
+
+ /**
+ * @see org.openmrs.module.ugandaemr.api.UgandaEMRService#generateAndSaveUICForPatientsWithOut()
+ */
+ @Override
+ public void generateAndSaveUICForPatientsWithOut() {
+ PatientService patientService = Context.getPatientService();
+ List list = Context.getAdministrationService().executeSQL("select patient.patient_id from patient where patient_id NOT IN(select patient.patient_id from patient inner join patient_identifier pi on (patient.patient_id = pi.patient_id) inner join patient_identifier_type pit on (pi.identifier_type = pit.patient_identifier_type_id) where pit.uuid='877169c4-92c6-4cc9-bf45-1ab95faea242')", true);
+ PatientIdentifierType patientIdentifierType = patientService.getPatientIdentifierTypeByUuid("877169c4-92c6-4cc9-bf45-1ab95faea242");
+ for (Object object : list) {
+ Integer patientId = (Integer) ((ArrayList) object).get(0);
+ Patient patient = patientService.getPatient(patientId);
+
+ String uniqueIdentifierCode = generatePatientUIC(patient);
+
+ if (uniqueIdentifierCode != null) {
+ PatientIdentifier patientIdentifier = new PatientIdentifier();
+ patientIdentifier.setIdentifier(uniqueIdentifierCode);
+ patientIdentifier.setIdentifierType(patientIdentifierType);
+ patientIdentifier.setLocation(Context.getLocationService().getLocationByUuid(Locations.PARENT.uuid()));
+ patientIdentifier.setCreator(Context.getUserService().getUser(1));
+ patientIdentifier.setPreferred(false);
+ patientIdentifier.setDateCreated(new Date());
+ patientIdentifier.setPatient(patient);
+ try {
+ patientService.savePatientIdentifier(patientIdentifier);
+ }catch (Exception e){
+ log.error("Failed to Save UIC for patient #"+patient.getPatientId(),e);
+ }
+
+ }
+ }
+ }
+
+ /**
+ * This Method replaces letters with number position in the alphabet
+ * @param letter the alphabetical letter
+ * @return number that matches the alphabetical letter position
+ */
+ private String replaceLettersWithNumber(String letter) {
+ String numberToReturn = "X";
+
+ switch (letter.toUpperCase()) {
+ case "A":
+ numberToReturn = "01";
+ break;
+ case "B":
+ numberToReturn = "02";
+ break;
+ case "C":
+ numberToReturn = "03";
+ break;
+ case "D":
+ numberToReturn = "04";
+ break;
+ case "E":
+ numberToReturn = "05";
+ break;
+ case "F":
+ numberToReturn = "06";
+ break;
+ case "G":
+ numberToReturn = "07";
+ break;
+ case "H":
+ numberToReturn = "08";
+ break;
+ case "I":
+ numberToReturn = "09";
+ break;
+ case "J":
+ numberToReturn = "10";
+ break;
+ case "K":
+ numberToReturn = "11";
+ break;
+ case "L":
+ numberToReturn = "12";
+ break;
+ case "M":
+ numberToReturn = "13";
+ break;
+ case "N":
+ numberToReturn = "14";
+ break;
+ case "O":
+ numberToReturn = "15";
+ break;
+ case "P":
+ numberToReturn = "16";
+ break;
+ case "Q":
+ numberToReturn = "17";
+ break;
+ case "R":
+ numberToReturn = "18";
+ break;
+ case "S":
+ numberToReturn = "19";
+ break;
+ case "T":
+ numberToReturn = "20";
+ break;
+ case "U":
+ numberToReturn = "21";
+ break;
+ case "V":
+ numberToReturn = "22";
+ break;
+ case "W":
+ numberToReturn = "23";
+ break;
+ case "X":
+ numberToReturn = "24";
+ break;
+ case "Y":
+ numberToReturn = "25";
+ break;
+ case "Z":
+ numberToReturn = "26";
+ break;
+
+ default:
+ numberToReturn = "X";
+ }
+ return numberToReturn;
+ }
+ /**
+ * @see org.openmrs.module.ugandaemr.api.UgandaEMRService#stopActiveOutPatientVisits()
+ */
+ public void stopActiveOutPatientVisits() {
+
+ SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+
+ SimpleDateFormat formatterExt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+ String currentDate = formatterExt.format(OpenmrsUtil.firstSecondOfDay(new Date()));
+
+ //TODO Change AdministrationService to Autowired
+ AdministrationService administrationService = Context.getAdministrationService();
+
+ String visitTypeUUID =administrationService.getGlobalProperty("ugandaemr.autoCloseVisit.visitTypeUUID");
+
+ VisitService visitService = Context.getVisitService();
+
+ List activeVisitList = null;
+ activeVisitList = administrationService.executeSQL("select visit.visit_id from visit inner join visit_type on (visit.visit_type_id = visit_type.visit_type_id) where visit_type.uuid='"+visitTypeUUID+"' AND visit.date_stopped IS NULL AND visit.date_started < '" + currentDate + "'", true);
+
+ for (Object object : activeVisitList) {
+ ArrayList integers = (ArrayList) object;
+ Visit visit = visitService.getVisit(integers.get(0));
+ try{
+ Date largestEncounterDate = OpenmrsUtil.getLastMomentOfDay(visit.getStartDatetime());
+
+ for (Encounter encounter : visit.getEncounters()) {
+ if (encounter.getEncounterDatetime().after(largestEncounterDate)) {
+ largestEncounterDate = OpenmrsUtil.getLastMomentOfDay(encounter.getEncounterDatetime());
+ }
+ }
+ visitService.endVisit(visit, OpenmrsUtil.getLastMomentOfDay(largestEncounterDate));
+ }catch (Exception e){
+ log.error("Failed to auto close visit",e);
+ }
+
+ }
+ }
+
+ /**
+ * @see org.openmrs.module.ugandaemr.api.UgandaEMRService#transferredOut(org.openmrs.Patient,java.util.Date)
+ */
+ @Override
+ public Map transferredOut(Patient patient, Date date) {
+ Map map = new HashMap();
+ EncounterService encounterService = Context.getEncounterService();
+ List encounterTypes = encounterService.findEncounterTypes(TRANSFER_OUT.name());
+
+ EncounterSearchCriteria encounterSearchCriteria = new EncounterSearchCriteriaBuilder().setPatient(patient).setIncludeVoided(false).setEncounterTypes(encounterTypes).setFromDate(date).createEncounterSearchCriteria();
+
+ List encounters = encounterService.getEncounters(encounterSearchCriteria);
+
+ Collections.reverse(encounters);
+ if (encounters.size() > 0) {
+ Encounter encounter = encounters.get(0);
+ if (encounters.get(0).getEncounterType() == Context.getEncounterService().getEncounterType(TRANSFER_OUT.name())) {
+ List encounters1 = new ArrayList<>();
+ List transferOutPlaceConceptList = new ArrayList<>();
+ transferOutPlaceConceptList.add(Context.getConceptService().getConcept(UgandaEMRConstants.TRANSFER_OUT_PLACE_CONCEPT_ID));
+ encounters1.add(encounter);
+ map.put(PATIENT_TRANSERRED_OUT, true);
+ map.put(PATIENT_TRANSFERED_OUT_DATE, encounter.getEncounterDatetime());
+ List people = new ArrayList<>();
+ people.add(patient.getPerson());
+ List obsList = Context.getObsService().getObservations(people, encounters, transferOutPlaceConceptList, null, null, null, null, 1, null, null, null, false);
+ if (obsList.size() > 0) {
+ map.put(PATIENT_TRANSFERED_OUT_LOCATION, obsList.get(0).getValueText());
+ }
+ } else {
+ map.put(PATIENT_TRANSERRED_OUT, false);
+ }
+ } else {
+ map.put(PATIENT_TRANSERRED_OUT, false);
+ }
+ return map;
+ }
+
+ /**
+ * @see org.openmrs.module.ugandaemr.api.UgandaEMRService#transferredIn(org.openmrs.Patient,java.util.Date)
+ */
+ @Override
+ public Map transferredIn(Patient patient, Date date) {
+ Map map = new HashMap();
+
+ EncounterService encounterService = Context.getEncounterService();
+
+ Collection encounterTypes = encounterService.findEncounterTypes(TRANSFER_IN.name());
+
+ EncounterSearchCriteria encounterSearchCriteria = new EncounterSearchCriteriaBuilder().setPatient(patient).setIncludeVoided(false).setEncounterTypes(encounterTypes).setFromDate(date).createEncounterSearchCriteria();
+
+ List encounters = encounterService.getEncounters(encounterSearchCriteria);
+
+ if (encounters.size() > 0) {
+ map.put(PATIENT_TRANSERRED_IN, true);
+ List transferInPlaceConceptList = new ArrayList<>();
+ List people = new ArrayList<>();
+ people.add(patient.getPerson());
+ transferInPlaceConceptList.add(Context.getConceptService().getConcept(TRANSFER_IN_FROM_PLACE_CONCEPT_ID));
+ List obsList = Context.getObsService().getObservations(people, encounters, transferInPlaceConceptList, null, null, null, null, 1, null, null, null, false);
+ if (obsList.size() > 0) {
+ map.put(PATIENT_TRANSFERED_IN_LOCATION, obsList.get(0).getValueText());
+ map.put(PATIENT_TRANSFERED_IN_DATE, obsList.get(0).getEncounter().getEncounterDatetime());
+ } else {
+ map.put(PATIENT_TRANSFERED_IN_DATE, encounters.get(0).getEncounterDatetime());
+ }
+ } else {
+ map.put(PATIENT_TRANSERRED_IN, false);
+ }
+ return map;
+ }
+
+ public boolean isTransferredOut(Patient patient, Date date) {
+ return (boolean) transferredOut(patient, date).get(PATIENT_TRANSERRED_OUT);
+ }
+
+ @Override
+ public boolean isTransferredIn(Patient patient, Date date) {
+ return (boolean) transferredOut(patient, date).get(PATIENT_TRANSERRED_OUT);
+ }
+
+ @Override
+ public List getTransferHistory(Patient patient) {
+
+ EncounterService encounterService = Context.getEncounterService();
+ Collection encounterTypes = new ArrayList<>();
+
+ encounterTypes.add(encounterService.getEncounterTypeByUuid(TRANSFER_IN.uuid()));
+ encounterTypes.add(encounterService.getEncounterTypeByUuid(TRANSFER_OUT.uuid()));
+
+ EncounterSearchCriteria encounterSearchCriteria = new EncounterSearchCriteriaBuilder().setPatient(patient).setIncludeVoided(false).setEncounterTypes(encounterTypes).createEncounterSearchCriteria();
+
+ List encounters = encounterService.getEncounters(encounterSearchCriteria);
+
+ return encounters;
+ }
+
+ @Override
+ public List getAllPublicHolidays() throws APIException {
+ return dao.getAllPublicHolidays();
+ }
+
+ @Override
+ public PublicHoliday getPublicHolidayByDate(Date publicHolidayDate) throws APIException {
+ return dao.getPublicHolidayByDate(publicHolidayDate);
+ }
+
+ @Override
+ public PublicHoliday savePublicHoliday(PublicHoliday publicHoliday) {
+ return dao.savePublicHoliday(publicHoliday);
+ }
+
+ @Override
+ public PublicHoliday getPublicHolidaybyUuid(String uuid) {
+ return dao.getPublicHolidaybyUuid(uuid);
+ }
+
+ @Override
+ public List getPublicHolidaysByDate(Date publicHolidayDate) throws APIException {
+ return dao.getPublicHolidaysByDate(publicHolidayDate);
+ }
+
+ /**
+ * @see org.openmrs.module.ugandaemr.api.UgandaEMRService#createPatientHIVSummaryEncounterOnTransferIn(org.openmrs.module.htmlformentry.FormEntrySession)
+ */
+ public Encounter createPatientHIVSummaryEncounterOnTransferIn(FormEntrySession formEntrySession) {
+ ConceptService conceptService = Context.getConceptService();
+ EncounterService encounterService = Context.getEncounterService();
+ Encounter encounter;
+
+ if (hasHIVSummaryPage(formEntrySession.getPatient(), "8d5b27bc-c2cc-11de-8d13-0010c6dffd0f") == null) {
+ encounter = new Encounter();
+ encounter.setEncounterType(encounterService.getEncounterTypeByUuid("8d5b27bc-c2cc-11de-8d13-0010c6dffd0f"));
+ encounter.setLocation(formEntrySession.getEncounter().getLocation());
+ encounter.setPatient(formEntrySession.getPatient());
+ encounter.setEncounterDatetime(new Date());
+ encounter.setForm(Context.getFormService().getFormByUuid("52653a60-8300-4c13-be4d-4b746da06fee"));
+
+
+ //*ART Start in information or Baseline Information//.
+ Obs baselineRegimenObsGroup = createNewObs(conceptService.getConcept(99162), encounter);
+ encounter.addObs(baselineRegimenObsGroup);
+
+ Obs artStartDate = generateObsFromObs(formEntrySession.getEncounter().getAllObs(), 99161, 99161, encounter);
+ baselineRegimenObsGroup.addGroupMember(artStartDate);
+ encounter.addObs(artStartDate);
+
+ Obs artStartRegimen = generateObsFromObs(formEntrySession.getEncounter().getAllObs(), 99061, 99061, encounter);
+ baselineRegimenObsGroup.addGroupMember(artStartRegimen);
+ encounter.addObs(artStartRegimen);
+
+ Obs baselineWeight = generateObsFromObs(formEntrySession.getEncounter().getAllObs(), 99069, 99069, encounter);
+ baselineRegimenObsGroup.addGroupMember(baselineWeight);
+ encounter.addObs(baselineWeight);
+
+ Obs baselineCD4 = generateObsFromObs(formEntrySession.getEncounter().getAllObs(), 99071, 99071, encounter);
+ baselineRegimenObsGroup.addGroupMember(baselineCD4);
+ encounter.addObs(baselineCD4);
+
+ Obs baselineWHOClinicStage = generateObsFromObs(formEntrySession.getEncounter().getAllObs(), 163026, 99070, encounter);
+ baselineRegimenObsGroup.addGroupMember(baselineWHOClinicStage);
+ encounter.addObs(baselineWHOClinicStage);
+
+ } else {
+ encounter = hasHIVSummaryPage(formEntrySession.getPatient(), "8d5b27bc-c2cc-11de-8d13-0010c6dffd0f");
+ }
+
+ //*Transfer in information//.
+ Obs transferInObsGroup = createNewObs(conceptService.getConcept(99065), encounter);
+ encounter.addObs(transferInObsGroup);
+
+ Obs transferInDate = createNewObs(conceptService.getConcept(99160), encounter);
+ transferInDate.setValueDate(new Date());
+ transferInObsGroup.addGroupMember(transferInDate);
+ encounter.addObs(transferInDate);
+
+ Obs transferInFromObs = generateObsFromObs(formEntrySession.getEncounter().getAllObs(), 99109, 90206, encounter);
+ transferInObsGroup.addGroupMember(transferInFromObs);
+ encounter.addObs(transferInFromObs);
+
+ Obs transferInRegimen = generateObsFromObs(formEntrySession.getEncounter().getAllObs(), 90315, 99064, encounter);
+ transferInObsGroup.addGroupMember(transferInRegimen);
+ encounter.addObs(transferInRegimen);
+
+
+ encounterService.saveEncounter(encounter);
+
+ return encounter;
+ }
+
+
+ /**
+ * @see org.openmrs.module.ugandaemr.api.UgandaEMRService#hasHIVSummaryPage(org.openmrs.Patient, java.lang.String)
+ */
+ public Encounter hasHIVSummaryPage(Patient patient, String encounterTypeUUID) {
+ EncounterService encounterService = Context.getEncounterService();
+ Collection encounterTypes = new ArrayList<>();
+ encounterTypes.add(encounterService.getEncounterTypeByUuid(encounterTypeUUID));
+ EncounterSearchCriteria encounterSearchCriteria = new EncounterSearchCriteriaBuilder().setPatient(patient).setIncludeVoided(false).setEncounterTypes(encounterTypes).createEncounterSearchCriteria();
+ List encounters = encounterService.getEncounters(encounterSearchCriteria);
+ if (!encounters.isEmpty()) {
+ for (Encounter encounter : encounters) {
+ if (encounterTypeUUID.equals(encounter.getEncounterType().getUuid())) {
+ return encounter;
+ }
+ }
+ }
+
+ return null;
+ }
+
+
+ /**
+ * @see org.openmrs.module.ugandaemr.api.UgandaEMRService#generateObsFromObs(java.util.Set, java.lang.Integer, java.lang.Integer, org.openmrs.Encounter)
+ */
+ public Obs generateObsFromObs(Set observations, Integer lookUpConceptId, Integer conceptIDForNewObs, Encounter encounter) {
+ for (Obs obs : observations) {
+ if (lookUpConceptId.equals(obs.getConcept().getConceptId())) {
+ Obs newObs = createNewObs(Context.getConceptService().getConcept(conceptIDForNewObs), encounter);
+ newObs.setValueBoolean(obs.getValueBoolean());
+ newObs.setValueCoded(obs.getValueCoded());
+ newObs.setValueDate(obs.getValueDate());
+ newObs.setValueDatetime(obs.getValueDatetime());
+ newObs.setValueNumeric(obs.getValueNumeric());
+ newObs.setValueText(obs.getValueText());
+ return newObs;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @see org.openmrs.module.ugandaemr.api.UgandaEMRService#createNewObs(org.openmrs.Concept, org.openmrs.Encounter)
+ */
+ public Obs createNewObs(Concept concept, Encounter encounter) {
+ Obs obs = new Obs();
+ obs.setObsDatetime(encounter.getEncounterDatetime());
+ obs.setPerson(encounter.getPatient());
+ obs.setLocation(encounter.getLocation());
+ obs.setEncounter(encounter);
+ obs.setConcept(concept);
+ return obs;
+ }
+ @Override
+ public List mapPatientQueueToMapper(List patientQueueList) {
+ List patientQueueMappers = new ArrayList<>();
+
+ for (PatientQueue patientQueue : patientQueueList) {
+ String names = patientQueue.getPatient().getFamilyName() + " " + patientQueue.getPatient().getGivenName() + " " + patientQueue.getPatient().getMiddleName();
+ PatientQueueVisitMapper patientQueueVisitMapper = new PatientQueueVisitMapper();
+ patientQueueVisitMapper.setId(patientQueue.getId());
+ patientQueueVisitMapper.setPatientNames(names.replace("null", ""));
+ patientQueueVisitMapper.setPatientId(patientQueue.getPatient().getPatientId());
+ patientQueueVisitMapper.setLocationFrom(patientQueue.getLocationFrom().getName());
+ patientQueueVisitMapper.setLocationTo(patientQueue.getLocationTo().getName());
+ patientQueueVisitMapper.setVisitNumber(patientQueue.getVisitNumber());
+
+ if (patientQueue.getProvider() != null) {
+ patientQueueVisitMapper.setProviderNames(patientQueue.getProvider().getName());
+ }
+
+ if (patientQueue.getCreator() != null) {
+ patientQueueVisitMapper.setCreatorNames(patientQueue.getCreator().getDisplayString());
+ }
+
+ if (patientQueue.getEncounter() != null) {
+ patientQueueVisitMapper.setEncounterId(patientQueue.getEncounter().getEncounterId().toString());
+ }
+
+ if (patientQueue.getStatus() == PatientQueue.Status.PENDING && patientQueue.getLocationFrom().getUuid().equals(LAB_LOCATION_UUID)) {
+ patientQueueVisitMapper.setStatus(QUEUE_STATUS_FROM_LAB);
+ } else {
+ patientQueueVisitMapper.setStatus(patientQueue.getStatus().name());
+ }
+
+ Visit visit = getPatientCurrentVisit(patientQueue.getPatient());
+ if (visit != null) {
+ patientQueueVisitMapper.setVisitId(visit.getVisitId());
+ }
+
+
+ patientQueueVisitMapper.setAge(patientQueue.getPatient().getAge().toString());
+ patientQueueVisitMapper.setGender(patientQueue.getPatient().getGender());
+ patientQueueVisitMapper.setDateCreated(patientQueue.getDateCreated().toString());
+ if (patientQueue.getDateChanged() != null) {
+ patientQueueVisitMapper.setDateChanged(patientQueue.getDateChanged().toString());
+ }
+ patientQueueMappers.add(patientQueueVisitMapper);
+ }
+ return patientQueueMappers;
+ }
+
+
+ /**
+ * Checks if Sample ID genereated is already issued out
+ *
+ * @param sampleId
+ * @param orderNumber
+ * @return
+ * @throws ParseException
+ */
+ public boolean isSampleIdExisting(String sampleId, String orderNumber) throws ParseException {
+ List list = Context.getAdministrationService().executeSQL(String.format("select * from orders where accession_number=\"%s\"", sampleId), true);
+ boolean exists = false;
+ if (!list.isEmpty()) {
+ exists = true;
+ }
+ return exists;
+ }
+
+ /**
+ * @param test
+ * @return
+ */
+ public Set renderTests(Order test) {
+ Set trms = new HashSet<>();
+ if (test.getEncounter() != null) {
+ Encounter encounter = test.getEncounter();
+ for (Obs obs : encounter.getAllObs()) {
+ if (obs.getOrder() != null) {
+ if (obs.hasGroupMembers()) {
+ for (Obs groupMemberObs : obs.getGroupMembers()) {
+ TestResultModel trm = new TestResultModel();
+ trm.setInvestigation(test.getConcept().getDisplayString());
+ trm.setSet(obs.getConcept().getDisplayString());
+ trm.setConcept(obs.getConcept());
+ setTestResultModelValue(groupMemberObs, trm);
+ trms.add(trm);
+ }
+ } else if (obs.getObsGroup() == null) {
+ TestResultModel trm = new TestResultModel();
+ trm.setInvestigation(test.getConcept().getName().getName());
+ trm.setSet(test.getConcept().getDatatype().getName());
+ trm.setConcept(obs.getConcept());
+ setTestResultModelValue(obs, trm);
+ trms.add(trm);
+ }
+ }
+ }
+ }
+ return trms;
+ }
+
+ /**
+ * Process Lab Orders
+ *
+ * @param query
+ * @param asOfDate
+ * @return
+ * @throws ParseException
+ * @throws IOException
+ */
+ public SimpleObject getProcessedOrders(String query, Date asOfDate, boolean includeProccesed) throws ParseException, IOException {
+ Date date;
+ SimpleObject simpleObject = new SimpleObject();
+ ObjectMapper objectMapper = new ObjectMapper();
+ OrderService orderService = Context.getOrderService();
+
+ if (asOfDate != null) {
+ query = String.format(query, org.openmrs.module.ugandaemr.utils.DateFormatUtil.dateFormtterString(asOfDate, DAY_START_TIME), org.openmrs.module.ugandaemr.utils.DateFormatUtil.dateFormtterString(asOfDate, DAY_END_TIME));
+ }
+
+
+ List list = Context.getAdministrationService().executeSQL(query, true);
+ Set unProcesedOrderList = new HashSet<>();
+
+ Set proccesedOrderList = new HashSet<>();
+
+ if (!list.isEmpty()) {
+ for (Object o : list) {
+ Order order = orderService.getOrder(Integer.parseUnsignedInt(((ArrayList) o).get(0).toString()));
+ if (order.getAccessionNumber() == null) {
+ unProcesedOrderList.add(order);
+ }
+ proccesedOrderList.add(order);
+ }
+ }
+
+ if (includeProccesed && !proccesedOrderList.isEmpty()) {
+ simpleObject.put(ordersListLabel, objectMapper.writeValueAsString(processOrders(proccesedOrderList, true)));
+ } else if (!unProcesedOrderList.isEmpty() && !includeProccesed) {
+ simpleObject.put(ordersListLabel, objectMapper.writeValueAsString(processOrders(unProcesedOrderList, true)));
+ }
+ return simpleObject;
+ }
+
+ /**
+ * Process Lab Orders
+ *
+ * @param query
+ * @param encounterId
+ * @return
+ * @throws ParseException
+ * @throws IOException
+ */
+ public SimpleObject getOrderResultsOnEncounter(String query, int encounterId, boolean includeProccesed) throws ParseException, IOException {
+ SimpleObject simpleObject = new SimpleObject();
+ ObjectMapper objectMapper = new ObjectMapper();
+ OrderService orderService = Context.getOrderService();
+
+ query = String.format(query, encounterId);
+
+ List list = Context.getAdministrationService().executeSQL(query, true);
+ Set unProcesedOrderList = new HashSet<>();
+
+ Set proccesedOrderList = new HashSet<>();
+
+ if (!list.isEmpty()) {
+ for (Object o : list) {
+ Order order = orderService.getOrder(Integer.parseUnsignedInt(((ArrayList) o).get(0).toString()));
+ if (order.getAccessionNumber() == null) {
+ unProcesedOrderList.add(order);
+ }
+ proccesedOrderList.add(order);
+ }
+ }
+
+ if (includeProccesed && !proccesedOrderList.isEmpty()) {
+ simpleObject.put(ordersListLabel, objectMapper.writeValueAsString(processOrders(proccesedOrderList, true)));
+ } else if (!unProcesedOrderList.isEmpty() && !includeProccesed) {
+ simpleObject.put(ordersListLabel, objectMapper.writeValueAsString(processOrders(unProcesedOrderList, true)));
+ }
+ return simpleObject;
+ }
+
+ /**
+ * Processes Orders from Encounter to Order Mapper
+ *
+ * @param orders
+ * @return
+ */
+
+ public Set processOrders(Set orders, boolean fiterOutProccessed) {
+ Set orderMappers = new HashSet<>();
+ for (Order order : orders) {
+ if (order.getOrderType().equals(Context.getOrderService().getOrderTypeByUuid(ORDER_TYPE_LAB_UUID))) {
+ String names = order.getPatient().getFamilyName() + " " + order.getPatient().getGivenName() + " " + order.getPatient().getMiddleName();
+ OrderMapper orderMapper = new OrderMapper();
+ orderMapper.setAccessionNumber(order.getAccessionNumber());
+ orderMapper.setCareSetting(order.getCareSetting().getName());
+ orderMapper.setConcept(order.getConcept().getConceptId().toString());
+ orderMapper.setConceptName(order.getConcept().getDisplayString());
+ orderMapper.setDateActivated(order.getDateActivated().toString());
+ orderMapper.setOrderer(order.getOrderer().getName());
+ orderMapper.setOrderNumber(order.getOrderNumber());
+ orderMapper.setPatientId(order.getPatient().getPatientId());
+ orderMapper.setInstructions(order.getInstructions());
+ orderMapper.setUrgency(order.getUrgency().name());
+ orderMapper.setPatient(names.replace("null", ""));
+ orderMapper.setOrderId(order.getOrderId());
+ orderMapper.setEncounterId(order.getEncounter().getEncounterId());
+ if (order.isActive()) {
+ orderMapper.setStatus(QUEUE_STATUS_ACTIVE);
+ }
+ if (orderHasResults(order)) {
+ orderMapper.setStatus(QUEUE_STATUS_HAS_RESULTS);
+ }
+ orderMappers.add(orderMapper);
+ }
+ }
+ return orderMappers;
+ }
+
+
+ /**
+ * Processes Orders from Encounter to Order Mapper
+ *
+ * @param orders
+ * @return
+ */
+
+ public Set processDrugOrders(Set orders) {
+ Set orderMappers = new HashSet<>();
+
+ for (Order order : orders) {
+ if (order.getOrderType().equals(Context.getOrderService().getOrderTypeByUuid(ORDER_TYPE_DRUG_UUID)) && order.isActive()) {
+ DrugOrder drugOrder = (DrugOrder) order;
+ String names = order.getPatient().getFamilyName() + " " + order.getPatient().getGivenName() + " " + order.getPatient().getMiddleName();
+ DrugOrderMapper drugOrderMapper = new DrugOrderMapper();
+
+ drugOrderMapper.setAsNeeded(drugOrder.getAsNeeded());
+ drugOrderMapper.setAsNeededCondition(drugOrder.getAsNeededCondition());
+ drugOrderMapper.setBrandName(drugOrder.getBrandName());
+ drugOrderMapper.setDose(drugOrder.getDose());
+ drugOrderMapper.setDoseUnits(drugOrder.getDoseUnits().getDisplayString());
+ drugOrderMapper.setDrug(drugOrder.getConcept().getDisplayString());
+ drugOrderMapper.setDuration(drugOrder.getDuration());
+ drugOrderMapper.setDurationUnits(drugOrder.getDurationUnits().getDisplayString());
+ drugOrderMapper.setDrugNonCoded(drugOrder.getDrugNonCoded());
+ drugOrderMapper.setFrequency(drugOrder.getFrequency().getName());
+ drugOrderMapper.setNumRefills(drugOrder.getNumRefills());
+ drugOrderMapper.setQuantity(drugOrder.getQuantity());
+ drugOrderMapper.setQuantityUnits(drugOrder.getQuantityUnits().getDisplayString());
+ drugOrderMapper.setStrength(getDrugStrength(drugOrder));
+ drugOrderMapper.setRoute(drugOrder.getRoute().getDisplayString());
+ drugOrderMapper.setAccessionNumber(drugOrder.getAccessionNumber());
+ drugOrderMapper.setCareSetting(drugOrder.getCareSetting().getName());
+ drugOrderMapper.setConcept(drugOrder.getConcept().getConceptId().toString());
+ drugOrderMapper.setConceptName(drugOrder.getConcept().getDisplayString());
+ drugOrderMapper.setDateActivated(drugOrder.getDateActivated().toString());
+ drugOrderMapper.setOrderer(drugOrder.getOrderer().getName());
+ drugOrderMapper.setOrderNumber(drugOrder.getOrderNumber());
+ drugOrderMapper.setPatientId(drugOrder.getPatient().getPatientId());
+ drugOrderMapper.setInstructions(drugOrder.getInstructions());
+ drugOrderMapper.setUrgency(drugOrder.getUrgency().name());
+ drugOrderMapper.setPatient(names.replace("null", ""));
+ drugOrderMapper.setOrderId(drugOrder.getOrderId());
+ drugOrderMapper.setEncounterId(drugOrder.getEncounter().getEncounterId());
+ if (order.isActive()) {
+ drugOrderMapper.setStatus(QUEUE_STATUS_ACTIVE);
+ }
+ if (orderHasResults(order)) {
+ drugOrderMapper.setStatus(QUEUE_STATUS_HAS_RESULTS);
+ }
+ orderMappers.add(drugOrderMapper);
+ }
+ }
+ return orderMappers;
+ }
+
+
+ /**
+ * Get Medication Strength from the drug order
+ * @param drugOrder the drug order where the drug strength has to be picked
+ * @return the
+ */
+ private String getDrugStrength(DrugOrder drugOrder){
+ List concepts=new ArrayList<>();
+ List encounters= new ArrayList<>();
+ List personList=new ArrayList<>();
+ personList.add(drugOrder.getPatient().getPerson());
+ concepts.add(drugOrder.getConcept());
+ encounters.add(drugOrder.getEncounter());
+ String medicationStrength="";
+ List obs= Context.getObsService().getObservations(personList,encounters,null,concepts,null,null,null,null,null,null,null,false);
+ if(!obs.isEmpty()){
+ Set groupMembers=obs.get(0).getObsGroup().getGroupMembers();
+ for (Obs groupMember:groupMembers) {
+ if(groupMember.getConcept().getConceptId()==MEDICATION_STRENGTH_CONCEPT_ID){
+ medicationStrength=groupMember.getValueText();
+ return medicationStrength;
+ }
+ }
+ }
+ return medicationStrength;
+ }
+
+ /**
+ * Set Results Model
+ *
+ * @param obs
+ * @param trm
+ */
+ private void setTestResultModelValue(Obs obs, TestResultModel trm) {
+ Concept concept = obs.getConcept();
+ trm.setTest(obs.getConcept().getDisplayString());
+ if (concept != null) {
+ String datatype = concept.getDatatype().getName();
+ if (datatype.equalsIgnoreCase("Text")) {
+ trm.setValue(obs.getValueText());
+ } else if (datatype.equalsIgnoreCase("Numeric")) {
+ if (obs.getValueText() != null) {
+ trm.setValue(obs.getValueText());
+ } else if (obs.getValueNumeric() != null) {
+ trm.setValue(obs.getValueNumeric().toString());
+ }
+ ConceptNumeric cn = Context.getConceptService().getConceptNumeric(concept.getConceptId());
+ trm.setUnit(cn.getUnits());
+ if (cn.getLowNormal() != null) trm.setLowNormal(cn.getLowNormal().toString());
+
+ if (cn.getHiNormal() != null) trm.setHiNormal(cn.getHiNormal().toString());
+
+ if (cn.getHiAbsolute() != null) {
+ trm.setHiAbsolute(cn.getHiAbsolute().toString());
+ }
+
+ if (cn.getHiCritical() != null) {
+ trm.setHiCritical(cn.getHiCritical().toString());
+ }
+
+ if (cn.getLowAbsolute() != null) {
+ trm.setLowAbsolute(cn.getLowAbsolute().toString());
+ }
+
+ if (cn.getLowCritical() != null) {
+ trm.setLowCritical(cn.getLowCritical().toString());
+ }
+
+ } else if (datatype.equalsIgnoreCase("Coded")) {
+ trm.setValue(obs.getValueCoded().getName().getName());
+ }
+ }
+ }
+
+ private boolean orderHasResults(Order order) {
+ boolean hasOrder = false;
+
+ List list = Context.getAdministrationService().executeSQL("select obs_id from obs where order_id=" + order.getOrderId() + "", true);
+
+ if (!list.isEmpty()) {
+ hasOrder = true;
+ }
+ return hasOrder;
+ }
+
+ /**
+ * Add Lab Results Observation to Encounter
+ *
+ * @param encounter
+ * @param testConcept
+ * @param testGroupConcept
+ * @param result
+ * @param test
+ */
+ public void addLaboratoryTestObservation(Encounter encounter, Concept testConcept, Concept testGroupConcept, String result, Order test) {
+ log.warn("testConceptId=" + testConcept);
+ log.warn("testGroupConceptId=" + testGroupConcept);
+ Obs obs = null;
+ obs = getObs(encounter, testConcept, testGroupConcept);
+ setObsAttributes(obs, encounter);
+ obs.setConcept(testConcept);
+ obs.setOrder(test);
+
+ if (testConcept.getDatatype().getName().equalsIgnoreCase("Text")) {
+ obs.setValueText(result);
+ } else if (testConcept.getDatatype().getName().equalsIgnoreCase("Numeric")) {
+ if (StringUtils.isNotBlank(result)) {
+ obs.setValueNumeric(Double.parseDouble(result));
+ }
+ } else if (testConcept.getDatatype().getName().equalsIgnoreCase("Coded")) {
+ Concept answerConcept = LaboratoryUtil.searchConcept(result);
+ obs.setValueCoded(answerConcept);
+ }
+ if (testGroupConcept != null) {
+ Obs testGroupObs = getObs(encounter, testGroupConcept, null);
+ if (testGroupObs.getConcept() == null) {
+ testGroupObs.setConcept(testGroupConcept);
+ testGroupObs.setOrder(test);
+ setObsAttributes(testGroupObs, encounter);
+ encounter.addObs(testGroupObs);
+ }
+ log.warn("Adding obs[concept=" + obs.getConcept() + ",uuid=" + obs.getUuid() + "] to obsgroup[concept=" + testGroupObs.getConcept() + ", uuid=" + testGroupObs.getUuid() + "]");
+ testGroupObs.addGroupMember(obs);
+ } else {
+ encounter.addObs(obs);
+ }
+
+ log.warn("Obs size is: " + encounter.getObs().size());
+ }
+
+ /**
+ * Convert PatientQueue List to PatientQueueMapping
+ *
+ * @param patientQueueList
+ * @return
+ */
+ public List mapPatientQueueToMapperWithOrders(List patientQueueList) {
+ List patientQueueMappers = new ArrayList<>();
+
+ for (PatientQueue patientQueue : patientQueueList) {
+ if (patientQueue.getEncounter() != null && !patientQueue.getEncounter().getOrders().isEmpty()) {
+ String names = patientQueue.getPatient().getFamilyName() + " " + patientQueue.getPatient().getGivenName() + " " + patientQueue.getPatient().getMiddleName();
+ LabQueueMapper labQueueMapper = new LabQueueMapper();
+ labQueueMapper.setId(patientQueue.getId());
+ labQueueMapper.setPatientNames(names.replace("null", ""));
+ labQueueMapper.setPatientId(patientQueue.getPatient().getPatientId());
+ labQueueMapper.setLocationFrom(patientQueue.getLocationFrom().getName());
+ labQueueMapper.setLocationTo(patientQueue.getLocationTo().getName());
+ labQueueMapper.setProviderNames(patientQueue.getProvider().getName());
+ labQueueMapper.setStatus(patientQueue.getStatus().name());
+ labQueueMapper.setAge(patientQueue.getPatient().getAge().toString());
+ labQueueMapper.setDateCreated(patientQueue.getDateCreated().toString());
+ if (patientQueue.getDateChanged() != null) {
+ labQueueMapper.setDateChanged(patientQueue.getDateChanged().toString());
+ }
+ labQueueMapper.setEncounterId(patientQueue.getEncounter().getEncounterId().toString());
+ labQueueMapper.setVisitNumber(patientQueue.getVisitNumber());
+ if (patientQueue.getEncounter() != null) {
+ labQueueMapper.setOrderMapper(Context.getService(UgandaEMRService.class).processOrders(patientQueue.getEncounter().getOrders(), true));
+ }
+ patientQueueMappers.add(labQueueMapper);
+ }
+ }
+ return patientQueueMappers;
+ }
+
+ /**
+ * Convert PatientQueue List to PatientQueueMapping
+ *
+ * @param patientQueueList
+ * @return
+ */
+ public List mapPatientQueueToMapperWithDrugOrders(List patientQueueList) {
+ List patientQueueMappers = new ArrayList<>();
+
+ for (PatientQueue patientQueue : patientQueueList) {
+ String names = patientQueue.getPatient().getFamilyName() + " " + patientQueue.getPatient().getGivenName() + " " + patientQueue.getPatient().getMiddleName();
+ PharmacyMapper pharmacyMapper = new PharmacyMapper();
+ pharmacyMapper.setId(patientQueue.getId());
+ pharmacyMapper.setPatientNames(names.replace("null", ""));
+ pharmacyMapper.setPatientId(patientQueue.getPatient().getPatientId());
+ pharmacyMapper.setVisitNumber(patientQueue.getVisitNumber());
+
+ if (patientQueue.getLocationFrom() != null) {
+ pharmacyMapper.setLocationFrom(patientQueue.getLocationFrom().getName());
+ }
+
+ if (patientQueue.getLocationTo() != null) {
+ pharmacyMapper.setLocationTo(patientQueue.getLocationTo().getName());
+ }
+
+ if (patientQueue.getProvider() != null) {
+ pharmacyMapper.setProviderNames(patientQueue.getProvider().getName());
+ }
+
+ pharmacyMapper.setStatus(patientQueue.getStatus().name());
+ pharmacyMapper.setAge(patientQueue.getPatient().getAge().toString());
+ pharmacyMapper.setDateCreated(patientQueue.getDateCreated().toString());
+
+ if (patientQueue.getDateChanged() != null) {
+ pharmacyMapper.setDateChanged(patientQueue.getDateChanged().toString());
+ }
+
+ Visit visit = getPatientCurrentVisit(patientQueue.getPatient());
+
+ if (visit != null) {
+ pharmacyMapper.setVisitId(visit.getVisitId());
+ }
+
+
+ if (patientQueue.getEncounter() != null) {
+ pharmacyMapper.setEncounterId(patientQueue.getEncounter().getEncounterId().toString());
+ pharmacyMapper.setDrugOrderMapper(processDrugOrders(patientQueue.getEncounter().getOrders()));
+ }
+ patientQueueMappers.add(pharmacyMapper);
+ }
+ return patientQueueMappers;
+ }
+
+ /**
+ * Set Attributes for Observation
+ *
+ * @param obs
+ * @param encounter
+ */
+ private void setObsAttributes(Obs obs, Encounter encounter) {
+ obs.setObsDatetime(encounter.getEncounterDatetime());
+ obs.setPerson(encounter.getPatient());
+ obs.setLocation(encounter.getLocation());
+ obs.setEncounter(encounter);
+ }
+
+ /**
+ * Get Existing Observation of The Encounter Which the results are going to be returned
+ *
+ * @param encounter
+ * @param concept
+ * @param groupingConcept
+ * @return
+ */
+ private Obs getObs(Encounter encounter, Concept concept, Concept groupingConcept) {
+ for (Obs obs : encounter.getAllObs()) {
+ if (groupingConcept != null) {
+ Obs obsGroup = getObs(encounter, groupingConcept, null);
+ if (obsGroup.getGroupMembers() != null) {
+ for (Obs member : obsGroup.getGroupMembers()) {
+ if (member.getConcept().equals(concept)) {
+ return member;
+ }
+ }
+ }
+ } else if (obs.getConcept().equals(concept)) {
+ return obs;
+ }
+ }
+ return new Obs();
+ }
+
+
+ /**
+ * @param session
+ * @param locationToUUID
+ * @param nextQueueStatus
+ * @param completePreviousQueue
+ */
+ public void sendPatientToNextLocation(FormEntrySession session, String locationToUUID, String locationFromUUID, PatientQueue.Status nextQueueStatus, boolean completePreviousQueue) {
+ PatientQueue patientQueue = new PatientQueue();
+ PatientQueueingService patientQueueingService = Context.getService(PatientQueueingService.class);
+ Location locationTo = Context.getLocationService().getLocationByUuid(locationToUUID);
+ Location locationFrom = Context.getLocationService().getLocationByUuid(locationFromUUID);
+ Provider provider = getProviderFromEncounter(session.getEncounter());
+
+
+ try {
+ if (!patientQueueExists(session.getEncounter(), locationTo, locationFrom, nextQueueStatus)) {
+ PatientQueue previousQueue = null;
+ if (completePreviousQueue) {
+ previousQueue = completePreviousQueue(session.getPatient(), session.getEncounter().getLocation(), PatientQueue.Status.PENDING);
+ }
+ patientQueue.setLocationFrom(session.getEncounter().getLocation());
+ patientQueue.setPatient(session.getEncounter().getPatient());
+ patientQueue.setLocationTo(locationTo);
+ patientQueue.setProvider(provider);
+ patientQueue.setEncounter(session.getEncounter());
+ patientQueue.setStatus(nextQueueStatus);
+ patientQueue.setCreator(Context.getUserService().getUsersByPerson(provider.getPerson(), false).get(0));
+ patientQueue.setDateCreated(new Date());
+ patientQueueingService.assignVisitNumberForToday(patientQueue);
+ patientQueueingService.savePatientQue(patientQueue);
+ }
+ } catch (ParseException e) {
+ log.error(e);
+ }
+
+
+ }
+
+ public Encounter processLabTestOrdersFromEncounterObs(FormEntrySession session, boolean completePreviousQueue) {
+
+ if (isRetrospective(session.getEncounter())) {
+ return session.getEncounter();
+ }
+
+ EncounterService encounterService = Context.getEncounterService();
+ Set orders = new HashSet<>();
+ Encounter encounter = session.getEncounter();
+ CareSetting careSetting = Context.getOrderService().getCareSettingByName(CARE_SETTING_OPD);
+ Set obsList = encounter.getObs();
+
+ for (Obs obs : obsList) {
+ if ((obs.getValueCoded() != null && (obs.getValueCoded().getConceptClass().getName().equals(LAB_SET_CLASS) || obs.getValueCoded().getConceptClass().getName().equals(TEST_SET_CLASS))) && !orderExists(obs.getValueCoded(), obs.getEncounter())) {
+ TestOrder testOrder = new TestOrder();
+ testOrder.setConcept(obs.getValueCoded());
+ testOrder.setEncounter(obs.getEncounter());
+ testOrder.setOrderer(getProviderFromEncounter(obs.getEncounter()));
+ testOrder.setPatient(obs.getEncounter().getPatient());
+ testOrder.setUrgency(Order.Urgency.STAT);
+ testOrder.setCareSetting(careSetting);
+ orders.add(testOrder);
+ }
+ }
+
+ if (!orders.isEmpty()) {
+ encounter.setOrders(orders);
+ encounterService.saveEncounter(encounter);
+ if (!session.getEncounter().getOrders().isEmpty()) {
+ sendPatientToNextLocation(session, LAB_LOCATION_UUID, encounter.getLocation().getUuid(), PatientQueue.Status.PENDING, completePreviousQueue);
+ }
+ }
+ return encounter;
+ }
+
+ private boolean orderExists(Concept concept, Encounter encounter) {
+ List list = Context.getAdministrationService().executeSQL("select order_id from orders where concept_id=" + concept.getConceptId() + " AND encounter_id=" + encounter.getEncounterId(), true);
+ boolean orderExists = false;
+ if (!list.isEmpty()) {
+ orderExists = true;
+ }
+ return orderExists;
+ }
+
+
+ public boolean patientQueueExists(Encounter encounter, Location locationTo, Location locationFrom, PatientQueue.Status status) throws ParseException {
+ List list = Context.getAdministrationService().executeSQL("select patient_queue_id from patient_queue where encounter_id=" + encounter.getEncounterId() + " AND status='" + status.name() + "' AND location_to=" + locationTo.getLocationId() + " AND location_from=" + locationFrom.getLocationId() + " AND date_created BETWEEN \"" + org.openmrs.module.ugandaemr.utils.DateFormatUtil.dateFormtterString(encounter.getEncounterDatetime(), DAY_START_TIME) + "\" AND \"" + org.openmrs.module.ugandaemr.utils.DateFormatUtil.dateFormtterString(encounter.getEncounterDatetime(), DAY_END_TIME) + "\"", true);
+ boolean orderExists = false;
+ if (!list.isEmpty()) {
+ orderExists = true;
+ }
+ return orderExists;
+ }
+
+
+ public Encounter processDrugOrdersFromEncounterObs(FormEntrySession session, boolean completePreviousQueue) {
+
+ if (isRetrospective(session.getEncounter())) {
+ return session.getEncounter();
+ }
+
+ EncounterService encounterService = Context.getEncounterService();
+ ConceptService conceptService = Context.getConceptService();
+ Set orders = new HashSet<>();
+ Encounter encounter = session.getEncounter();
+ CareSetting careSetting = Context.getOrderService().getCareSettingByName(CARE_SETTING_OPD);
+ Set obsList = encounter.getObs();
+ OrderService orderService = Context.getOrderService();
+ for (Obs obs : obsList) {
+ if ((obs.getValueCoded() != null && (obs.getValueCoded().getConceptClass().getName().equals(DRUG_SET_CLASS))) && !orderExists(obs.getValueCoded(), obs.getEncounter())) {
+ DrugOrder drugOrder = new DrugOrder();
+ Set obsGroupMembers = new HashSet<>();
+ if (obs.getObsGroup() != null) {
+ obsGroupMembers.addAll((obs.getObsGroup().getGroupMembers()));
+
+ for (Obs groupMember : obsGroupMembers) {
+ switch (groupMember.getConcept().getConceptId()) {
+ case MEDICATION_QUANTITY_CONCEPT_ID:
+ case ARV_MEDICATION_QUANTITY_CONCEPT_ID:
+ drugOrder.setQuantity(groupMember.getValueNumeric());
+ drugOrder.setDose(groupMember.getValueNumeric());
+ break;
+ case MEDICATION_DURATION_CONCEPT_ID:
+ case ARV_MEDICATION_DURATION_CONCEPT_ID:
+ drugOrder.setDuration(groupMember.getValueNumeric().intValue());
+ break;
+ case MEDICATION_QUANTITY_UNIT_CONCEPT_ID:
+ drugOrder.setQuantityUnits(groupMember.getValueCoded());
+ break;
+ case MEDICATION_DURATION_UNIT_CONCEPT_ID:
+ drugOrder.setDurationUnits(groupMember.getValueCoded());
+ break;
+ case MEDICATION_COMMENT_CONCEPT_ID:
+ drugOrder.setCommentToFulfiller(groupMember.getValueText());
+ break;
+ default:
+ }
+ }
+
+ if (drugOrder.getDose() == null) {
+ drugOrder.setDose(0.0);
+ }
+
+ if (drugOrder.getDoseUnits() == null) {
+ drugOrder.setDoseUnits(conceptService.getConcept(DEFALUT_DOSE_UNIT_CONCEPT_ID));
+ }
+
+ if (drugOrder.getRoute() == null) {
+ drugOrder.setRoute(conceptService.getConcept(DEFALUT_ROUTE_CONCEPT_ID));
+ }
+
+ if (drugOrder.getDurationUnits() == null) {
+ drugOrder.setDurationUnits(conceptService.getConcept(DEFALUT_DURATION_UNIT_CONCEPT_ID));
+ }
+
+ if (drugOrder.getFrequency() == null) {
+ drugOrder.setFrequency(Context.getOrderService().getOrderFrequencyByUuid(DEFALUT_ORDER_FREQUECNY_UUID));
+ }
+
+ if (drugOrder.getQuantityUnits() == null) {
+ drugOrder.setQuantityUnits(conceptService.getConcept(DEFALUT_DISPENSING_UNIT_CONCEPT_ID));
+ }
+
+ drugOrder.setNumRefills(1);
+ drugOrder.setEncounter(obs.getEncounter());
+ drugOrder.setOrderer(getProviderFromEncounter(obs.getEncounter()));
+ drugOrder.setPatient(obs.getEncounter().getPatient());
+ drugOrder.setUrgency(Order.Urgency.STAT);
+ drugOrder.setCareSetting(careSetting);
+ drugOrder.setConcept(obs.getValueCoded());
+ discontinueOverLappingDrugOrders(drugOrder);
+
+ if (isValidDrugOrder(drugOrder)) {
+ orders.add(drugOrder);
+ }
+
+ }
+ }
+ }
+
+ if (!orders.isEmpty()) {
+ encounter.setOrders(orders);
+ encounterService.saveEncounter(encounter);
+ if (!session.getEncounter().getOrders().isEmpty()) {
+ sendPatientToNextLocation(session, PHARMACY_LOCATION_UUID, encounter.getLocation().getUuid(), PatientQueue.Status.PENDING, completePreviousQueue);
+ completePreviousQueue(session.getPatient(), session.getEncounter().getLocation(), PatientQueue.Status.PENDING);
+ }
+ }
+ return encounter;
+ }
+
+ /**
+ * This Validates a drug order
+ *
+ * @param drugOrder the drug order to validate
+ * @return returns true or false basing on the validation
+ */
+ private boolean isValidDrugOrder(DrugOrder drugOrder) {
+ if (drugOrder.getDuration() == null || drugOrder.getQuantity() == null || drugOrder.getFrequency() == null || drugOrder.getQuantityUnits() == null || drugOrder.getRoute() == null || drugOrder.getDose() == null || drugOrder.getDoseUnits() == null) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ public Provider getProviderFromEncounter(Encounter encounter) {
+ EncounterRole encounterRole = Context.getEncounterService().getEncounterRoleByUuid(ENCOUNTER_ROLE);
+
+ Set providers = encounter.getProvidersByRole(encounterRole);
+ List providerList = new ArrayList<>();
+ for (Provider provider : providers) {
+ providerList.add(provider);
+ }
+
+ if (!providerList.isEmpty()) {
+ return providerList.get(0);
+ } else {
+ return null;
+ }
+ }
+
+ public PatientQueue completePreviousQueue(Patient patient, Location location, PatientQueue.Status searchStatus) {
+ PatientQueueingService patientQueueingService = Context.getService(PatientQueueingService.class);
+ PatientQueue patientQueue = getPreviousQueue(patient, location, searchStatus);
+ if (patientQueue != null) {
+ patientQueueingService.completePatientQueue(patientQueue);
+ }
+ return patientQueue;
+ }
+
+ public PatientQueue getPreviousQueue(Patient patient, Location location, PatientQueue.Status status) {
+ PatientQueueingService patientQueueingService = Context.getService(PatientQueueingService.class);
+ PatientQueue previousQueue = null;
+
+ List patientQueueList = patientQueueingService.getPatientQueueList(null, OpenmrsUtil.firstSecondOfDay(new Date()), OpenmrsUtil.getLastMomentOfDay(new Date()), location, null, patient, null);
+
+ if (!patientQueueList.isEmpty()) {
+ previousQueue = patientQueueList.get(0);
+ }
+ return previousQueue;
+ }
+
+
+ private boolean isRetrospective(Encounter encounter) {
+ return encounter.getEncounterDatetime().before(OpenmrsUtil.firstSecondOfDay(new Date()));
+ }
+
+
+ /**
+ * This Method gets the latest current visit for a patient
+ *
+ * @param patient the patient whose current visit will be retrived.
+ * @return Visit the active visit for a patient.
+ */
+ private Visit getPatientCurrentVisit(Patient patient) {
+ List visitList = Context.getVisitService().getActiveVisitsByPatient(patient);
+ for (Visit visit : visitList) {
+ if (visit.getStartDatetime().after(OpenmrsUtil.firstSecondOfDay(new Date())) && visit.getStartDatetime().before(OpenmrsUtil.getLastMomentOfDay(new Date()))) {
+ return visit;
+ }
+ }
+ return null;
+ }
+
+ public void completePatientActiveVisit(Patient patient) {
+ VisitService visitService = Context.getVisitService();
+ List activeVisitsByPatient = visitService.getActiveVisitsByPatient(patient);
+ for (Visit visit : activeVisitsByPatient) {
+ if (visit.getVisitType().equals(visitService.getVisitTypeByUuid("7b0f5697-27e3-40c4-8bae-f4049abfb4ed"))) {
+ try {
+ visitService.endVisit(visit, OpenmrsUtil.getLastMomentOfDay(visit.getStartDatetime()));
+ } catch (Exception e) {
+ log.error("Competition of Patient Visit #" + visit.getVisitId() + " failed.", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * @see org.openmrs.module.ugandaemr.api.UgandaEMRService#dispenseMedication(org.openmrs.module.ugandaemr.pharmacy.DispensingModelWrapper, org.openmrs.Provider, org.openmrs.Location)
+ */
+ public SimpleObject dispenseMedication(DispensingModelWrapper resultWrapper, Provider provider, Location location) {
+
+ EncounterService encounterService = Context.getEncounterService();
+ PatientQueueingService patientQueueingService = Context.getService(PatientQueueingService.class);
+
+ Encounter previousEncounter = encounterService.getEncounter(resultWrapper.getEncounterId());
+ PatientQueue patientQueue = patientQueueingService.getPatientQueueById(resultWrapper.getPatientQueueId());
+
+ Encounter encounter = new Encounter();
+ encounter.setEncounterType(encounterService.getEncounterTypeByUuid(ENCOUNTER_TYPE_DISPENSE_UUID));
+ encounter.setProvider(Context.getEncounterService().getEncounterRoleByUuid(ENCOUNTER_ROLE_PHARMACIST), provider);
+ encounter.setLocation(location);
+ encounter.setPatient(previousEncounter.getPatient());
+ encounter.setVisit(previousEncounter.getVisit());
+ encounter.setEncounterDatetime(previousEncounter.getEncounterDatetime());
+ encounter.setForm(Context.getFormService().getFormByUuid(DISPENSE_FORM_UUID));
+
+ List referredOutPrescriptions = new ArrayList<>();
+ Set obs = new HashSet<>();
+
+ for (DrugOrderMapper drugOrderMapper : resultWrapper.getDrugOrderMappers()) {
+ DrugOrder drugOrder = (DrugOrder) Context.getOrderService().getOrder(drugOrderMapper.getOrderId());
+
+ if (drugOrderMapper.getOrderReasonNonCoded() != null && drugOrderMapper.getOrderReasonNonCoded().equals("REFERREDOUT")) {
+ try {
+ obs.addAll(processDispensingObservation(encounter, drugOrderMapper, false));
+ } catch (ParseException e) {
+ log.error(e);
+ }
+ drugOrderMapper.setQuantity(calculatePrescriptionDispenseDifference(drugOrderMapper, drugOrder));
+ drugOrderMapper.setPatientAge(drugOrder.getPatient().getAge());
+ referredOutPrescriptions.add(drugOrderMapper);
+ } else {
+ try {
+ obs.addAll(processDispensingObservation(encounter, drugOrderMapper, true));
+ } catch (ParseException e) {
+ log.error(e);
+ }
+ }
+
+ try {
+ Context.getOrderService().discontinueOrder(drugOrder, "Completed", new Date(), provider, previousEncounter);
+ } catch (Exception e) {
+ log.error(e);
+ }
+
+ Context.getService(UgandaEMRService.class).completePatientActiveVisit(patientQueue.getPatient());
+ }
+
+ encounter.setObs(obs);
+ encounterService.saveEncounter(encounter);
+
+ patientQueue.setEncounter(encounter);
+ patientQueueingService.savePatientQue(patientQueue);
+ patientQueueingService.completePatientQueue(patientQueue);
+
+ ObjectMapper objectMapper = new ObjectMapper();
+
+ SimpleObject simpleObject = new SimpleObject();
+
+ if (!referredOutPrescriptions.isEmpty()) {
+ try {
+ simpleObject.put("referredOutPrescriptions", objectMapper.writeValueAsString(referredOutPrescriptions));
+ } catch (IOException e) {
+ log.error(e);
+ }
+ } else {
+ simpleObject = SimpleObject.create("status", "success", "message", "Saved!");
+ }
+ return simpleObject;
+ }
+
+ /**
+ * This Method processes dispensing observations
+ *
+ * @param encounter encounter where the obs will be saved
+ * @param drugOrderMapper the data for the drugs that are being dispensed
+ * @param receivedAtFacility boolean to check if the drugs were dispensed at facility or not
+ * @return a set of drug dispensing observations
+ */
+ private Set processDispensingObservation(Encounter encounter, DrugOrderMapper drugOrderMapper, Boolean receivedAtFacility) throws ParseException {
+
+ ConceptService conceptService = Context.getConceptService();
+ Set obs = new HashSet<>();
+ Order order = null;
+ if (drugOrderMapper.getOrderId() != null) {
+ order = Context.getOrderService().getOrder(drugOrderMapper.getOrderId());
+ }
+ //Grouping Observation
+ Obs parentObs = createDispensingObs(encounter, conceptService.getConcept(MEDICATION_DISPENSE_SET), null, null, order);
+ obs.add(parentObs);
+
+ //Drug Observation
+ if (drugOrderMapper.getConcept() != null) {
+ Obs drug = createDispensingObs(encounter, conceptService.getConcept(MEDICATION_ORDER_CONCEPT_ID), drugOrderMapper.getConcept(), "coded", order);
+ parentObs.addGroupMember(drug);
+ obs.add(drug);
+ }
+
+ //Quantity Observation
+ if (drugOrderMapper.getQuantity() != null) {
+ Obs drugQuantity = createDispensingObs(encounter, conceptService.getConcept(MEDICATION_DISPENSE_QUANTITY), drugOrderMapper.getQuantity().toString(), "numeric", order);
+ parentObs.addGroupMember(drugQuantity);
+ obs.add(drugQuantity);
+ }
+
+ //Duration Observation
+ if (drugOrderMapper.getDuration() != null) {
+ Obs periodDispensed = createDispensingObs(encounter, conceptService.getConcept(MEDICATION_DURATION_CONCEPT_ID), drugOrderMapper.getDuration().toString(), "numeric", order);
+ parentObs.addGroupMember(periodDispensed);
+ obs.add(periodDispensed);
+ }
+
+
+ //Duration Observation
+ if (!drugOrderMapper.getStrength().equals("")) {
+ Obs drugStrength = createDispensingObs(encounter, conceptService.getConcept(MEDICATION_STRENGTH_CONCEPT_ID), drugOrderMapper.getStrength(), "string", order);
+ parentObs.addGroupMember(drugStrength);
+ obs.add(drugStrength);
+ }
+
+ //check if issued at facility
+
+ Obs dispensedAtFacility = createDispensingObs(encounter, conceptService.getConcept(MEDICATION_DISPENSE_RECEIVED_AT_VIST), null, null, order);
+ dispensedAtFacility.setValueBoolean(receivedAtFacility);
+ parentObs.addGroupMember(dispensedAtFacility);
+ obs.add(dispensedAtFacility);
+
+ return obs;
+ }
+
+ /**
+ * This method helps create an observation
+ *
+ * @param encounter observation encounter
+ * @param concept question for observation
+ * @param value value for the observation
+ * @param valueType datatype for the observation
+ * @param order observation order
+ * @return an observation
+ * @throws ParseException
+ */
+ private Obs createDispensingObs(Encounter encounter, Concept concept, String value, String valueType, Order order) throws ParseException {
+ Obs obs = new Obs();
+ obs.setObsDatetime(encounter.getEncounterDatetime());
+ obs.setPerson(encounter.getPatient());
+ obs.setLocation(encounter.getLocation());
+ obs.setEncounter(encounter);
+ obs.setOrder(order);
+ obs.setConcept(concept);
+ if (valueType != null) {
+ if (valueType.equals("string")) {
+ obs.setValueAsString(value);
+ } else if (valueType.equals("numeric")) {
+ obs.setValueNumeric(Double.parseDouble(value));
+ } else if (valueType.equals("coded")) {
+ obs.setValueCoded(Context.getConceptService().getConcept(value));
+ } else if (valueType.equals("groupId")) {
+ obs.setValueGroupId(Integer.parseInt(value));
+ }
+ }
+ return obs;
+ }
+
+ /**
+ * Calculates the balance after dispensing medication to patient.
+ *
+ * @param drugOrderMapper the object that contains the data of dispensing
+ * @param drugOrder the object that contains prescription data
+ * @return
+ */
+ private Double calculatePrescriptionDispenseDifference(DrugOrderMapper drugOrderMapper, DrugOrder drugOrder) {
+ Double quantityBalance = 0.0;
+ if (drugOrderMapper.getQuantity() != null && drugOrder.getQuantity() != null) {
+ quantityBalance = drugOrder.getQuantity() - drugOrderMapper.getQuantity();
+ } else if (drugOrder.getQuantity() != null && drugOrderMapper.getQuantity() == null) {
+ quantityBalance = drugOrder.getQuantity();
+ }
+ return quantityBalance;
+ }
+
+ /**
+ * Check if there is a similar active drug order and discontinues it.
+ *
+ * @param order the order to be checked if it is similar to any
+ */
+ private void discontinueOverLappingDrugOrders(Order order) {
+ OrderService orderService = Context.getOrderService();
+ List activeOrders = orderService.getActiveOrders(order.getPatient(), null, order.getCareSetting(), new Date());
+ for (Order activeOrder : activeOrders) {
+ if (order.hasSameOrderableAs(activeOrder)
+ && !OpenmrsUtil.nullSafeEquals(order.getPreviousOrder(), activeOrder)
+ && OrderUtil.checkScheduleOverlap(order, activeOrder) && activeOrder.getOrderType()
+ .equals(Context.getOrderService().getOrderTypeByUuid(OrderType.DRUG_ORDER_TYPE_UUID))) {
+ try {
+ orderService.discontinueOrder(activeOrder, "Incomplete with new similar order", OpenmrsUtil.getLastMomentOfDay(activeOrder.getDateActivated()), order.getOrderer(), activeOrder.getEncounter());
+ } catch (Exception e) {
+ log.error("failed to discontinue order #" + activeOrder.getOrderId(), e);
+ }
+ }
+ }
+ }
+ /**
+ * @see org.openmrs.module.ugandaemr.api.UgandaEMRService#generatePatientProgramAttribute(org.openmrs.ProgramAttributeType, org.openmrs.PatientProgram, java.lang.String)
+ */
+ public PatientProgramAttribute generatePatientProgramAttribute(ProgramAttributeType programAttributeType, PatientProgram patientProgram, String value) {
+ PatientProgramAttribute patientProgramAttribute = new PatientProgramAttribute();
+ patientProgramAttribute.setAttributeType(programAttributeType);
+ patientProgramAttribute.setValueReferenceInternal(value);
+
+ return patientProgramAttribute;
+ }
+
+ /**
+ * @see org.openmrs.module.ugandaemr.api.UgandaEMRService#generatePatientProgramAttributeFromObservation(org.openmrs.PatientProgram, java.util.Set, java.lang.Integer, java.lang.String)
+ */
+ public PatientProgramAttribute generatePatientProgramAttributeFromObservation(PatientProgram patientProgram, Set observations, Integer conceptID, String programAttributeUUID) {
+ UgandaEMRService ugandaEMRService = Context.getService(UgandaEMRService.class);
+ for (Obs obs : observations) {
+ if (conceptID.equals(obs.getConcept().getConceptId())) {
+ ProgramAttributeType programAttributeType = Context.getProgramWorkflowService().getProgramAttributeTypeByUuid(programAttributeUUID);
+ return ugandaEMRService.generatePatientProgramAttribute(programAttributeType, patientProgram, obs.getValueAsString(Locale.ENGLISH));
+ }
+ }
+ return null;
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/mapper/LabQueueMapper.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/mapper/LabQueueMapper.java
new file mode 100644
index 000000000..66e364aad
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/mapper/LabQueueMapper.java
@@ -0,0 +1,22 @@
+package org.openmrs.module.ugandaemr.api.lab.mapper;
+
+import org.openmrs.module.patientqueueing.mapper.PatientQueueMapper;
+
+import java.io.Serializable;
+import java.util.Set;
+
+public class LabQueueMapper extends PatientQueueMapper implements Serializable {
+
+ Set orderMapper;
+
+ public LabQueueMapper() {
+ }
+
+ public Set getOrderMapper() {
+ return orderMapper;
+ }
+
+ public void setOrderMapper(Set orderMapper) {
+ this.orderMapper = orderMapper;
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/mapper/OrderMapper.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/mapper/OrderMapper.java
new file mode 100644
index 000000000..ceabe8800
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/mapper/OrderMapper.java
@@ -0,0 +1,239 @@
+package org.openmrs.module.ugandaemr.api.lab.mapper;
+
+import java.io.Serializable;
+
+public class OrderMapper implements Serializable {
+
+ private Integer encounterId;
+
+ public OrderMapper() {
+ }
+
+ private Integer orderId;
+
+ private String patient;
+
+ private Integer patientAge;
+
+ private Integer patientId;
+
+ private String orderType;
+
+ private String concept;
+
+ private String conceptName;
+
+ private String instructions;
+
+ private String dateActivated;
+
+ private String autoExpireDate;
+
+ private String encounter;
+
+ private String orderer;
+
+ private String dateStopped;
+
+ private String orderReason;
+
+ private String accessionNumber;
+
+ private String orderReasonNonCoded;
+
+ private String urgency;
+
+ private String orderNumber;
+
+ private String commentToFulfiller;
+
+ private String careSetting;
+
+ private String scheduledDate;
+
+ private String status;
+
+ public Integer getEncounterId() {
+ return encounterId;
+ }
+
+ public void setEncounterId(Integer encounterId) {
+ this.encounterId = encounterId;
+ }
+
+ public Integer getOrderId() {
+ return orderId;
+ }
+
+ public void setOrderId(Integer orderId) {
+ this.orderId = orderId;
+ }
+
+ public String getPatient() {
+ return patient;
+ }
+
+ public void setPatient(String patient) {
+ this.patient = patient;
+ }
+
+ public String getOrderType() {
+ return orderType;
+ }
+
+ public void setOrderType(String orderType) {
+ this.orderType = orderType;
+ }
+
+ public String getConcept() {
+ return concept;
+ }
+
+ public void setConcept(String concept) {
+ this.concept = concept;
+ }
+
+ public String getInstructions() {
+ return instructions;
+ }
+
+ public void setInstructions(String instructions) {
+ this.instructions = instructions;
+ }
+
+ public String getDateActivated() {
+ return dateActivated;
+ }
+
+ public void setDateActivated(String dateActivated) {
+ this.dateActivated = dateActivated;
+ }
+
+ public String getAutoExpireDate() {
+ return autoExpireDate;
+ }
+
+ public void setAutoExpireDate(String autoExpireDate) {
+ this.autoExpireDate = autoExpireDate;
+ }
+
+ public String getEncounter() {
+ return encounter;
+ }
+
+ public void setEncounter(String encounter) {
+ this.encounter = encounter;
+ }
+
+ public String getOrderer() {
+ return orderer;
+ }
+
+ public void setOrderer(String orderer) {
+ this.orderer = orderer;
+ }
+
+ public String getDateStopped() {
+ return dateStopped;
+ }
+
+ public void setDateStopped(String dateStopped) {
+ this.dateStopped = dateStopped;
+ }
+
+ public String getOrderReason() {
+ return orderReason;
+ }
+
+ public void setOrderReason(String orderReason) {
+ this.orderReason = orderReason;
+ }
+
+ public String getAccessionNumber() {
+ return accessionNumber;
+ }
+
+ public void setAccessionNumber(String accessionNumber) {
+ this.accessionNumber = accessionNumber;
+ }
+
+ public String getOrderReasonNonCoded() {
+ return orderReasonNonCoded;
+ }
+
+ public void setOrderReasonNonCoded(String orderReasonNonCoded) {
+ this.orderReasonNonCoded = orderReasonNonCoded;
+ }
+
+ public String getUrgency() {
+ return urgency;
+ }
+
+ public void setUrgency(String urgency) {
+ this.urgency = urgency;
+ }
+
+ public String getOrderNumber() {
+ return orderNumber;
+ }
+
+ public void setOrderNumber(String orderNumber) {
+ this.orderNumber = orderNumber;
+ }
+
+ public String getCommentToFulfiller() {
+ return commentToFulfiller;
+ }
+
+ public void setCommentToFulfiller(String commentToFulfiller) {
+ this.commentToFulfiller = commentToFulfiller;
+ }
+
+ public String getCareSetting() {
+ return careSetting;
+ }
+
+ public void setCareSetting(String careSetting) {
+ this.careSetting = careSetting;
+ }
+
+ public String getScheduledDate() {
+ return scheduledDate;
+ }
+
+ public void setScheduledDate(String scheduledDate) {
+ this.scheduledDate = scheduledDate;
+ }
+
+ public String getConceptName() {
+ return conceptName;
+ }
+
+ public void setConceptName(String conceptName) {
+ this.conceptName = conceptName;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public int getPatientId() {
+ return patientId;
+ }
+
+ public void setPatientId(int patientId) {
+ this.patientId = patientId;
+ }
+
+ public Integer getPatientAge() {
+ return patientAge;
+ }
+
+ public void setPatientAge(Integer patientAge) {
+ this.patientAge = patientAge;
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/LaboratoryUtil.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/LaboratoryUtil.java
new file mode 100644
index 000000000..e17ab7bb4
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/LaboratoryUtil.java
@@ -0,0 +1,173 @@
+/**
+ * Copyright 2011 Society for Health Information Systems Programmes, India (HISP India)
+ *
+ * This file is part of Laboratory module.
+ *
+ * Laboratory module is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Laboratory module is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Laboratory module. If not, see .
+ **/
+
+package org.openmrs.module.ugandaemr.api.lab.util;
+
+import org.openmrs.*;
+import org.openmrs.api.context.Context;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import static org.openmrs.module.ugandaemr.UgandaEMRConstants.LAB_SET_CLASS;
+
+public class LaboratoryUtil {
+
+ /**
+ * Generate parameter models
+ *
+ * @param parameters
+ * @param concept
+ */
+ public static void generateParameterModels(List parameters, Concept concept, Concept parentConcept,
+ Order order) {
+ if (concept.getConceptClass().getName().equalsIgnoreCase(LAB_SET_CLASS)) {
+ List concepts = getParameterConcepts(concept);
+ for (Concept c : concepts) {
+ generateParameterModels(parameters, c, concept, order);
+ }
+ } else {
+ ParameterModel parameter = generateParameterModel(concept, parentConcept, order);
+ parameters.add(parameter);
+ }
+ }
+
+ private static List getParameterConcepts(Concept concept) {
+
+ List concepts = new ArrayList();
+ for (ConceptSet cs : concept.getConceptSets()) {
+ Concept c = cs.getConcept();
+ concepts.add(c);
+ }
+ return concepts;
+ }
+
+ private static ParameterModel generateParameterModel(Concept concept, Concept parentConcept, Order order) {
+ ParameterModel parameter = new ParameterModel();
+ parameter.setId(concept.getConceptId().toString());
+ if (parentConcept != null) {
+ parameter.setContainer(parentConcept.getDisplayString());
+ parameter.setContainerId(parentConcept.getId());
+ }
+ setDefaultParameterValue(concept, parentConcept, order.getEncounter(), parameter);
+ if (concept.getDatatype().getName().equalsIgnoreCase("Text")) {
+ parameter.setType("text");
+ } else if (concept.getDatatype().getName().equalsIgnoreCase("Numeric")) {
+ parameter.setType("number");
+ parameter.setUnit(getUnit(concept));
+ } else if (concept.getDatatype().getName().equalsIgnoreCase("Coded")) {
+ parameter.setType("select");
+
+ for (ConceptAnswer ca : concept.getAnswers()) {
+ Concept c = ca.getAnswerConcept();
+ parameter.addOption(new ParameterOption(c.getName().getName(), c.getId().toString()));
+ }
+ }
+ parameter.setValidator(" required");
+ parameter.setTitle(concept.getName().getName());
+ return parameter;
+ }
+
+ /**
+ * Generate list of test models using tests
+ *
+ * @param tests
+ * @return
+ */
+ public static List generateModelsFromTests(Order tests) {
+
+ List models = new ArrayList();
+ TestModel tm = generateModel(tests);
+ models.add(tm);
+ return models;
+ }
+
+ private static void setDefaultParameterValue(Concept concept, Concept parentConcept, Encounter encounter,
+ ParameterModel parameter) {
+ if (encounter != null) {
+ for (Obs obs : encounter.getAllObs()) {
+ if (parentConcept != null && obs.getObsGroup() != null
+ && obs.getObsGroup().getConcept().equals(parentConcept) && obs.getConcept().equals(concept)) {
+ parameter.setDefaultValue(obs.getValueAsString(Context.getLocale()));
+ break;
+ } else if (concept.equals(obs.getConcept()) && obs.getObsGroup() == null) {
+ parameter.setDefaultValue(obs.getValueAsString(Context.getLocale()));
+ break;
+ }
+ }
+ }
+ }
+
+ private static TestModel generateModel(Order order) {
+ SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
+ TestModel tm = new TestModel();
+ tm.setStartDate(sdf.format(order.getDateActivated()));
+ tm.setPatientId(order.getPatient().getPatientId());
+ tm.setPatientIdentifier(order.getPatient().getPatientIdentifier().getIdentifier());
+ tm.setPatientName(order.getPatient().getFamilyName());
+ tm.setGender(order.getPatient().getGender());
+ tm.setAge(order.getPatient().getAge());
+ tm.setTest(order.getConcept());
+ tm.setOrderId(order.getOrderId());
+
+ if (order != null) {
+
+ tm.setTestId(order.getOrderId());
+ tm.setAcceptedDate(sdf.format(order.getDateActivated()));
+ tm.setConceptId(order.getConcept().getConceptId());
+ tm.setSampleId(order.getAccessionNumber());
+ if (order.getEncounter() != null)
+ tm.setEncounterId(order.getEncounter().getEncounterId());
+ } else {
+ tm.setStatus(null);
+ }
+
+ // get investigation from test tree map
+
+ tm.setInvestigation(order.getConcept().getName().getName());
+
+ return tm;
+ }
+
+ /**
+ * Search for concept using name
+ *
+ * @param name
+ * @return
+ */
+ @SuppressWarnings("deprecation")
+ public static Concept searchConcept(String name) {
+ Concept concept = Context.getConceptService().getConcept(name);
+ if (concept != null) {
+ return concept;
+ } else {
+ List cws = Context.getConceptService().getConceptsByName(name, new Locale("en"), false);
+ if (!cws.isEmpty())
+ return cws.get(0);
+ }
+ return null;
+ }
+
+ private static String getUnit(Concept concept) {
+ ConceptNumeric cn = Context.getConceptService().getConceptNumeric(concept.getConceptId());
+ return cn.getUnits();
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/ParameterModel.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/ParameterModel.java
new file mode 100644
index 000000000..2b257e8e5
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/ParameterModel.java
@@ -0,0 +1,131 @@
+/**
+ * Copyright 2011 Society for Health Information Systems Programmes, India (HISP India)
+ *
+ * This file is part of Laboratory module.
+ *
+ * Laboratory module is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+
+ * Laboratory module is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Laboratory module. If not, see .
+ *
+ **/
+
+package org.openmrs.module.ugandaemr.api.lab.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ParameterModel implements Comparable {
+
+ public String id;
+
+ public String type;
+
+ public String title;
+
+ public String container;
+
+ public Integer containerId;
+
+ public List options = new ArrayList();
+
+ public String defaultValue;
+
+ public String unit;
+
+ public String validator;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public List getOptions() {
+ return options;
+ }
+
+ public void addOption(ParameterOption option) {
+ this.options.add(option);
+ }
+
+ public String getDefaultValue() {
+ return this.defaultValue;
+ }
+
+ public void setDefaultValue(String value) {
+ this.defaultValue = value;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getContainer() {
+ return container;
+ }
+
+ public void setContainer(String container) {
+ this.container = container;
+ }
+
+ public Integer getContainerId() {
+ return containerId;
+ }
+
+ public void setContainerId(Integer containerId) {
+ this.containerId = containerId;
+ }
+
+ public String getUnit() {
+ return unit;
+ }
+
+ public void setUnit(String unit) {
+ this.unit = unit;
+ }
+
+ public String getValidator() {
+ return validator;
+ }
+
+ public void setValidator(String validator) {
+ this.validator = validator;
+ }
+
+ public int compareTo(ParameterModel otherParameterModel) {
+ if (otherParameterModel.getContainerId() == null)
+ return 1;
+ if (this.getContainerId() == null)
+ return -1;
+ Integer thisContainerId = containerId;
+ Integer otherContainerId = otherParameterModel.getContainerId();
+ return thisContainerId.compareTo(otherContainerId);
+ }
+
+ public String toString() {
+ return "ParameterModel [id=" + id + "]";
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/ParameterOption.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/ParameterOption.java
new file mode 100644
index 000000000..2ccc1af8b
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/ParameterOption.java
@@ -0,0 +1,31 @@
+package org.openmrs.module.ugandaemr.api.lab.util;
+
+public class ParameterOption {
+
+ public ParameterOption(String label, String value) {
+ super();
+ this.label = label;
+ this.value = value;
+ }
+
+ private String label;
+
+ private String value;
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/ResultModel.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/ResultModel.java
new file mode 100644
index 000000000..14c74062d
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/ResultModel.java
@@ -0,0 +1,36 @@
+package org.openmrs.module.ugandaemr.api.lab.util;
+
+import java.io.Serializable;
+
+public class ResultModel implements Serializable {
+
+ private String conceptName;
+
+ private String selectedOption;
+
+ private String value;
+
+ public String getConceptName() {
+ return conceptName;
+ }
+
+ public void setConceptName(String conceptName) {
+ this.conceptName = conceptName;
+ }
+
+ public String getSelectedOption() {
+ return selectedOption;
+ }
+
+ public void setSelectedOption(String selectedOption) {
+ this.selectedOption = selectedOption;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/ResultModelWrapper.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/ResultModelWrapper.java
new file mode 100644
index 000000000..eb3585d71
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/ResultModelWrapper.java
@@ -0,0 +1,26 @@
+package org.openmrs.module.ugandaemr.api.lab.util;
+
+import java.util.List;
+
+public class ResultModelWrapper {
+
+ private Integer testId;
+
+ private List results;
+
+ public Integer getTestId() {
+ return testId;
+ }
+
+ public void setTestId(Integer testId) {
+ this.testId = testId;
+ }
+
+ public List getResults() {
+ return results;
+ }
+
+ public void setResults(List results) {
+ this.results = results;
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/TestModel.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/TestModel.java
new file mode 100644
index 000000000..44b7aa0c3
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/TestModel.java
@@ -0,0 +1,209 @@
+/**
+ * Copyright 2011 Society for Health Information Systems Programmes, India (HISP India)
+ *
+ * This file is part of Laboratory module.
+ *
+ * Laboratory module is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Laboratory module is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Laboratory module. If not, see .
+ **/
+
+package org.openmrs.module.ugandaemr.api.lab.util;
+
+import org.openmrs.Concept;
+
+import java.util.Comparator;
+
+public class TestModel implements Comparator, Comparable {
+
+ private String startDate;
+
+ private String patientIdentifier;
+
+ private Integer patientId;
+
+ private String patientName;
+
+ private String gender;
+
+ private Integer age;
+
+ private Concept test;
+
+ private Concept testName;
+
+ private Integer orderId;
+
+ private String status;
+
+ private Integer testId;
+
+ private String acceptedDate;
+
+ private String investigation;
+
+ private Integer encounterId;
+
+ private Integer conceptId;
+
+ private String sampleId;
+
+ public String value;
+
+ public TestModel() {
+ }
+
+ public String getStartDate() {
+ return startDate;
+ }
+
+ public void setStartDate(String startDate) {
+ this.startDate = startDate;
+ }
+
+ public Integer getPatientId() {
+ return patientId;
+ }
+
+ public void setPatientId(Integer patientId) {
+ this.patientId = patientId;
+ }
+
+ public String getPatientIdentifier() {
+ return patientIdentifier;
+ }
+
+ public void setPatientIdentifier(String patientIdentifier) {
+ this.patientIdentifier = patientIdentifier;
+ }
+
+ public String getPatientName() {
+ return patientName;
+ }
+
+ public void setPatientName(String patientName) {
+ this.patientName = patientName;
+ }
+
+ public String getGender() {
+ return gender;
+ }
+
+ public void setGender(String gender) {
+ this.gender = gender;
+ }
+
+ public Integer getAge() {
+ return age;
+ }
+
+ public void setAge(Integer age) {
+ this.age = age;
+ }
+
+ public Concept getTest() {
+ return test;
+ }
+
+ public void setTest(Concept test) {
+ this.test = test;
+ }
+
+ public Concept getTestName() {
+ return testName;
+ }
+
+ public void setTestName(Concept testName) {
+ this.testName = testName;
+ }
+
+ public Integer getOrderId() {
+ return orderId;
+ }
+
+ public void setOrderId(Integer orderId) {
+ this.orderId = orderId;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public Integer getTestId() {
+ return testId;
+ }
+
+ public void setTestId(Integer testId) {
+ this.testId = testId;
+ }
+
+ public String getAcceptedDate() {
+ return acceptedDate;
+ }
+
+ public void setAcceptedDate(String acceptedDate) {
+ this.acceptedDate = acceptedDate;
+ }
+
+ public String getInvestigation() {
+ return investigation;
+ }
+
+ public void setInvestigation(String investigation) {
+ this.investigation = investigation;
+ }
+
+ public Integer getEncounterId() {
+ return encounterId;
+ }
+
+ public void setEncounterId(Integer encounterId) {
+ this.encounterId = encounterId;
+ }
+
+ public Integer getConceptId() {
+ return conceptId;
+ }
+
+ public void setConceptId(Integer conceptId) {
+ this.conceptId = conceptId;
+ }
+
+ public String getSampleId() {
+ return sampleId;
+ }
+
+ public void setSampleId(String sampleId) {
+ this.sampleId = sampleId;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public int compareTo(TestModel t) {
+ return (this.patientName).compareTo(t.patientName);
+ }
+
+ public int compare(TestModel t, TestModel t1) {
+ return 0;
+ }
+
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/TestResultModel.java b/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/TestResultModel.java
new file mode 100644
index 000000000..cdfb8ad3d
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/api/lab/util/TestResultModel.java
@@ -0,0 +1,205 @@
+/**
+ * Copyright 2011 Society for Health Information Systems Programmes, India (HISP India)
+ *
+ * This file is part of Laboratory module.
+ *
+ * Laboratory module is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+
+ * Laboratory module is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Laboratory module. If not, see .
+ *
+ **/
+
+package org.openmrs.module.ugandaemr.api.lab.util;
+
+import org.openmrs.Concept;
+
+public class TestResultModel implements Comparable {
+
+ public static final String LEVEL_INVESTIGATION = "LEVEL_INVESTIGATION";
+
+ public static final String LEVEL_SET = "LEVEL_SET";
+
+ public static final String LEVEL_TEST = "LEVEL_TEST";
+
+ public static final String LEVEL_RESULT = "LEVEL_RESULT";
+
+ private String investigation;
+
+ private String set;
+
+ private String test;
+
+ private String value;
+
+ private String hiNormal;
+
+ private String lowNormal;
+
+ private String lowAbsolute;
+
+ private String hiCritical;
+
+ private String lowCritical;
+
+ private String unit;
+
+ private String level = LEVEL_TEST;
+
+ private Concept concept;
+
+ private Integer encounterId;
+
+ private Integer testId;
+
+ public String hiAbsolute;
+
+ public String getHiAbsolute() {
+ return hiAbsolute;
+ }
+
+ public void setHiAbsolute(String hiAbsolute) {
+ this.hiAbsolute = hiAbsolute;
+ }
+
+ public String getLowAbsolute() {
+ return lowAbsolute;
+ }
+
+ public void setLowAbsolute(String lowAbsolute) {
+ this.lowAbsolute = lowAbsolute;
+ }
+
+ public String getHiCritical() {
+ return hiCritical;
+ }
+
+ public void setHiCritical(String hiCritical) {
+ this.hiCritical = hiCritical;
+ }
+
+ public String getLowCritical() {
+ return lowCritical;
+ }
+
+ public void setLowCritical(String lowCritical) {
+ this.lowCritical = lowCritical;
+ }
+
+ public String getInvestigation() {
+ return investigation;
+ }
+
+ public void setInvestigation(String investigation) {
+ this.investigation = investigation;
+ }
+
+ public String getTest() {
+ return test;
+ }
+
+ public void setTest(String test) {
+ this.test = test;
+ }
+
+ public String getHiNormal() {
+ return hiNormal;
+ }
+
+ public void setHiNormal(String hiNormal) {
+ this.hiNormal = hiNormal;
+ }
+
+ public String getLowNormal() {
+ return lowNormal;
+ }
+
+ public void setLowNormal(String lowNormal) {
+ this.lowNormal = lowNormal;
+ }
+
+ public String getUnit() {
+ return unit;
+ }
+
+ public void setUnit(String unit) {
+ this.unit = unit;
+ }
+
+ public String getSet() {
+ return set;
+ }
+
+ public void setSet(String set) {
+ this.set = set;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String getLevel() {
+ return level;
+ }
+
+ public void setLevel(String level) {
+ this.level = level;
+ }
+
+ public Concept getConcept() {
+ return concept;
+ }
+
+ public void setConcept(Concept concept) {
+ this.concept = concept;
+ }
+
+ public Integer getEncounterId() {
+ return encounterId;
+ }
+
+ public void setEncounterId(Integer encounterId) {
+ this.encounterId = encounterId;
+ }
+
+ public Integer getTestId() {
+ return testId;
+ }
+
+ public void setTestId(Integer testId) {
+ this.testId = testId;
+ }
+
+ public int compareTo(TestResultModel o) {
+ if (o == null)
+ return 1;
+ String tInvestigation = this.getInvestigation();
+ String tSet = this.getSet();
+ String tTest = this.getTest();
+ String oInvestigation = o.getInvestigation();
+ String oSet = o.getSet();
+ String oTest = o.getTest();
+ int investigationCompare = tInvestigation.compareToIgnoreCase(oInvestigation);
+ int setCompare = tSet.compareToIgnoreCase(oSet);
+ int testCompare = tTest.compareToIgnoreCase(oTest);
+ if (investigationCompare != 0) {
+ return investigationCompare;
+ } else if (setCompare != 0) {
+ return setCompare;
+ } else {
+ return testCompare;
+ }
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/dataintegrity/BasePatientRuleDefinition.java b/api/src/main/java/org/openmrs/module/ugandaemr/dataintegrity/BasePatientRuleDefinition.java
new file mode 100644
index 000000000..020876bb4
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/dataintegrity/BasePatientRuleDefinition.java
@@ -0,0 +1,131 @@
+package org.openmrs.module.ugandaemr.dataintegrity;
+
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.openmrs.Concept;
+import org.openmrs.Encounter;
+import org.openmrs.Obs;
+import org.openmrs.Patient;
+import org.openmrs.PatientIdentifierType;
+import org.openmrs.api.PatientService;
+import org.openmrs.api.context.Context;
+import org.openmrs.module.ugandaemr.UgandaEMRConstants;
+import org.openmrs.module.ugandaemr.metadata.core.PatientIdentifierTypes;
+import org.openmrs.module.dataintegrity.rule.RuleDefinition;
+
+/**
+ * Base class for RuleDefinitions
+ */
+public abstract class BasePatientRuleDefinition implements RuleDefinition {
+
+
+ protected Log log = LogFactory.getLog(getClass());
+
+ /**
+ * Return the HIV Clinic number for the patient
+ *
+ * @param patient
+ * @return
+ */
+ public String getHIVClinicNumber(Patient patient) {
+ PatientService patientService = Context.getPatientService();
+ PatientIdentifierType pit = patientService.getPatientIdentifierTypeByUuid(PatientIdentifierTypes.HIV_CARE_NUMBER.uuid());
+ return patient.getPatientIdentifier(pit) == null ? "" : patient.getPatientIdentifier(pit).toString();
+ }
+
+ /**
+ * Exposed Infant number for the patient
+ *
+ * @param patient
+ * @return
+ */
+ public String getExposedInfantNumber(Patient patient) {
+ PatientService patientService = Context.getPatientService();
+ PatientIdentifierType pit = patientService.getPatientIdentifierTypeByUuid(PatientIdentifierTypes.EXPOSED_INFANT_NUMBER.uuid());
+ return patient.getPatientIdentifier(pit) == null ? "" : patient.getPatientIdentifier(pit).toString();
+ }
+
+ /**
+ * TB number for the patient
+ *
+ * @param patient
+ * @return
+ */
+ public String getTbNumber(Patient patient, Encounter encounter, String identifierConceptUuid) { //To be done
+
+ //Iterate through the possible TB identifiers and return the first occurrence of a TB identifier
+ ArrayList tbIdentifierConceptUuids = new ArrayList();
+ if (identifierConceptUuid == null || identifierConceptUuid.length() == 0 ) {
+ tbIdentifierConceptUuids.add(UgandaEMRConstants.UNIT_TB_NUMBER);
+ tbIdentifierConceptUuids.add(UgandaEMRConstants.HSD_TB_NUMBER);
+ tbIdentifierConceptUuids.add(UgandaEMRConstants.DISTRICT_TB_NUMBER);
+ }
+ else{
+ tbIdentifierConceptUuids.add(identifierConceptUuid);
+ }
+
+ for (String tbIdentifierConceptUuid : tbIdentifierConceptUuids) {
+
+ Concept tbIdentifierConcept = Context.getConceptService().getConceptByUuid(tbIdentifierConceptUuid);
+
+ Set patientObs = encounter.getObs();
+
+ for (Obs obs : patientObs) {
+
+ if (obs.getConcept().equals(tbIdentifierConcept)) {
+
+ if (obs.getValueText() != null) {
+
+ return obs.getValueText();
+
+ }
+
+ }
+
+ }
+
+ }
+
+ String openmrsId = getOpenMrsId(patient);
+
+ return openmrsId;
+ }
+
+ /**
+ * OpenMRS Id for the patient
+ *
+ * @param patient
+ * @return
+ */
+ public String getOpenMrsId(Patient patient) { //To be done
+
+ PatientService patientService = Context.getPatientService();
+ PatientIdentifierType pit = patientService.getPatientIdentifierTypeByUuid(PatientIdentifierTypes.OPENMRS_ID.uuid());
+
+ return patient.getPatientIdentifier(pit) == null ? "" : patient.getPatientIdentifier(pit).toString();
+ }
+
+ /**
+ * A Session instance used by sub-classes
+ *
+ * @return
+ */
+ public Session getSession() {
+ return Context.getRegisteredComponent("sessionFactory", SessionFactory.class).getCurrentSession();
+ }
+
+ /**
+ * A formatter for dates
+ *
+ * @return
+ */
+ public DateFormat getDateFormatter() {
+ return Context.getDateFormat();
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/dataintegrity/IncompleteARTInformation.java b/api/src/main/java/org/openmrs/module/ugandaemr/dataintegrity/IncompleteARTInformation.java
new file mode 100644
index 000000000..22aebffed
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/dataintegrity/IncompleteARTInformation.java
@@ -0,0 +1,138 @@
+package org.openmrs.module.ugandaemr.dataintegrity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hibernate.Query;
+import org.openmrs.Encounter;
+import org.openmrs.Patient;
+import org.openmrs.module.dataintegrity.DataIntegrityRule;
+import org.openmrs.module.dataintegrity.rule.RuleResult;
+import org.springframework.stereotype.Component;
+
+/**
+ * Incomplete ART information data
+ *
+ *
+ * Patients with ART regimen but no ART Start date
+ * Encounters with other ART regimen to be cleaned out
+ * Encounters without ART regimen after ART start date
+ *
+ *
+ * */
+@Component
+public class IncompleteARTInformation extends BasePatientRuleDefinition {
+
+ @Override
+ public List> evaluate() {
+ List> ruleResults = new ArrayList<>();
+ ruleResults.addAll(patientsOnARTWithoutARTStartDate());
+ ruleResults.addAll(patientsOnARTWithoutStartRegimen());
+ ruleResults.addAll(patientsOnARTWithOtherRegimen());
+
+ return ruleResults;
+ }
+
+ /**
+ * Patients on ART without an ART start date
+ * @return
+ */
+ private List> patientsOnARTWithoutARTStartDate() {
+ log.info("Executing rule to find patients on ART without ART start date");
+ String queryString = "SELECT o.encounter from Obs o join o.person as patient WHERE o.voided = false AND o.concept.conceptId = 90315 AND o.person.dead=0 AND o.person.personId NOT IN (SELECT oo.person.personId FROM Obs oo WHERE oo.voided = false AND oo.concept.conceptId = 99161) GROUP BY o.person.personId";
+
+ Query query = getSession().createQuery(queryString);
+
+ List encounterList = query.list();
+ log.info("There are " + encounterList.size() + " patients on ART without an ART start date");
+
+ List> ruleResults = new ArrayList<>();
+ for (Encounter encounter : encounterList) {
+ RuleResult ruleResult = new RuleResult<>();
+ String actionUrl = "htmlformentryui/htmlform/enterHtmlFormWithStandardUi.page?formUuid=52653a60-8300-4c13-be4d-4b746da06fee&patientId=" + encounter.getPatient().getPatientId();
+ if (encounter.getVisit() != null) {
+ actionUrl = actionUrl +"&visitId=" + encounter.getVisit().getId();
+ }
+ ruleResult.setActionUrl(actionUrl);
+ ruleResult.setNotes("Client #" + getHIVClinicNumber(encounter.getPatient()) + " has no ART Start Date");
+ ruleResult.setEntity(encounter.getPatient());
+
+ ruleResults.add(ruleResult);
+ }
+
+ return ruleResults;
+ }
+
+ /**
+ * Patients on ART without an Start Regimen
+ * @return
+ */
+ private List> patientsOnARTWithoutStartRegimen() {
+ log.info("Executing rule to find patients on ART without start regimen");
+ String queryString = "SELECT o.encounter from Obs o join o.person as patient WHERE o.voided = false AND o.person.dead=0 AND o.concept.conceptId = 90315 AND o.person.personId NOT IN (SELECT oo.person.personId FROM Obs oo WHERE oo.voided = false AND oo.concept.conceptId = 99161) GROUP BY o.person.personId";
+
+ Query query = getSession().createQuery(queryString);
+
+ List encounterList = query.list();
+ log.info("There are " + encounterList.size() + " patients on ART without a start regimen");
+
+ List> ruleResults = new ArrayList<>();
+ for (Encounter encounter : encounterList) {
+ RuleResult ruleResult = new RuleResult<>();
+ String actionUrl = "htmlformentryui/htmlform/enterHtmlFormWithStandardUi.page?formUuid=52653a60-8300-4c13-be4d-4b746da06fee&patientId=" + encounter.getPatient().getPatientId();
+ if (encounter.getVisit() != null) {
+ actionUrl = actionUrl +"&visitId=" + encounter.getVisit().getId();
+ }
+ ruleResult.setActionUrl(actionUrl);
+ ruleResult.setNotes("Client #" + getHIVClinicNumber(encounter.getPatient()) + " has no Baseline Regimen at start of ART");
+ ruleResult.setEntity(encounter.getPatient());
+
+ ruleResults.add(ruleResult);
+ }
+
+ return ruleResults;
+ }
+
+ /**
+ * Patients on ART without Other Regimen
+ * @return
+ */
+ private List> patientsOnARTWithOtherRegimen() {
+ log.info("Executing rule to find patients on ART with Other as a regimen");
+ String queryString = "SELECT o.encounter from Obs o WHERE o.voided = false AND o.person.dead=0 AND o.concept.conceptId = 90315 AND o.valueCoded.conceptId = 90002 GROUP BY o.person.personId";
+
+ Query query = getSession().createQuery(queryString);
+
+ List encounterList = query.list();
+ log.info("There are " + encounterList.size() + " patients on ART with other as regimen");
+
+ List> ruleResults = new ArrayList<>();
+ for (Encounter encounter : encounterList) {
+ RuleResult ruleResult = new RuleResult<>();
+ Patient patient = encounter.getPatient();
+ // link to the Encounter page
+ String actionUrl = "htmlformentryui/htmlform/editHtmlFormWithStandardUi.page?formUuid=12de5bc5-352e-4faf-9961-a2125085a75c&encounterId=" + encounter.getEncounterId() + "&patientId=" + patient.getId();
+ if (encounter.getVisit() != null) {
+ actionUrl = actionUrl + "&visitId=" + encounter.getVisit().getId();
+ }
+ ruleResult.setActionUrl(actionUrl);
+ ruleResult.setNotes("Client #" + getHIVClinicNumber(patient) + " has ART regimen Other for visit on " + encounter.getEncounterDatetime());
+ ruleResult.setEntity(patient);
+
+ ruleResults.add(ruleResult);
+ }
+
+ return ruleResults;
+ }
+
+ @Override
+ public DataIntegrityRule getRule() {
+ DataIntegrityRule rule = new DataIntegrityRule();
+ rule.setRuleCategory("patient");
+ rule.setHandlerConfig("java");
+ rule.setHandlerClassname(getClass().getName());
+ rule.setRuleName("Incomplete ART Information");
+ rule.setUuid("cff2fc9e-329c-4c08-a432-e90b428268e3");
+ return rule;
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/dataintegrity/IncompleteExposedInfantInformation.java b/api/src/main/java/org/openmrs/module/ugandaemr/dataintegrity/IncompleteExposedInfantInformation.java
new file mode 100644
index 000000000..96dff2037
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/dataintegrity/IncompleteExposedInfantInformation.java
@@ -0,0 +1,197 @@
+package org.openmrs.module.ugandaemr.dataintegrity;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.List;
+
+import org.hibernate.Query;
+import org.joda.time.DateTime;
+import org.joda.time.Months;
+import org.openmrs.Encounter;
+import org.openmrs.Patient;
+import org.openmrs.module.dataintegrity.DataIntegrityRule;
+import org.openmrs.module.dataintegrity.rule.RuleResult;
+import org.springframework.stereotype.Component;
+
+/**
+ * Incomplete information for Exposed Infants
+ *
+ *
+ * EID with Encounters and no summary page
+ * EID with summary page and no encounters
+ * No final outcome at 2 years of age
+ * TODO: Missing PCR tests after enrollment
+ *
+ *
+ * */
+@Component
+public class IncompleteExposedInfantInformation extends BasePatientRuleDefinition {
+
+ @Override
+ public List> evaluate() {
+ List> ruleResults = new ArrayList<>();
+
+ ruleResults.addAll(exposedInfantsWithEncountersAndNOSummaryPage());
+ ruleResults.addAll(exposedInfantsWithSummaryPageNoEncounters());
+ ruleResults.addAll(exposedInfantsOlderThan18MonthsWithNoFinalOutcome());
+ ruleResults.addAll(exposedInfantsWithNoMotherARTNumber());
+ ruleResults.addAll(exposedInfantsWithMotherARTNumberButNotLinked());
+ return ruleResults;
+ }
+
+ /**
+ * Exposed infants with Encounters but no summary page
+ * @return
+ */
+ public List> exposedInfantsWithEncountersAndNOSummaryPage() {
+ log.info("Executing rule to find exposed infants with encounters but no summary page");
+ String queryString = "SELECT patient FROM Encounter e WHERE e.voided = false AND e.patient.dead=0 AND e.encounterType.uuid = '4345dacb-909d-429c-99aa-045f2db77e2b'"
+ + " AND e.patient.patientId NOT IN (SELECT ee.patient.patientId FROM Encounter ee WHERE ee.encounterType.uuid = '9fcfcc91-ad60-4d84-9710-11cc25258719') GROUP BY e.patient.patientId";
+
+ Query query = getSession().createQuery(queryString);
+
+ List patientList = query.list();
+ log.info("There are " + patientList.size() + " exposed infants with encounters and no Summary page");
+
+ List> ruleResults = new ArrayList<>();
+ for (Patient patient : patientList) {
+ RuleResult ruleResult = new RuleResult<>();
+ ruleResult.setActionUrl("coreapps/patientdashboard/patientDashboard.page?patientId=" + patient.getUuid());
+ ruleResult.setNotes("Exposed Infant #" + getExposedInfantNumber(patient) + " has no Summary Page");
+ ruleResult.setEntity(patient);
+
+ ruleResults.add(ruleResult);
+ }
+
+ return ruleResults;
+ }
+
+ /**
+ * Exposed infants with Summary page but no encounters
+ * @return
+ */
+ public List> exposedInfantsWithSummaryPageNoEncounters() {
+ log.info("Executing rule to find exposed infants a summary page and no encounters");
+ String queryString = "SELECT patient FROM Encounter e WHERE e.voided = false AND e.patient.dead=0 AND e.encounterType.uuid = '9fcfcc91-ad60-4d84-9710-11cc25258719' AND e.patient.patientId NOT IN (SELECT ee.patient.patientId FROM Encounter ee WHERE ee.encounterType.uuid = '4345dacb-909d-429c-99aa-045f2db77e2b') GROUP BY e.patient.patientId";
+
+ Query query = getSession().createQuery(queryString);
+
+ List patientList = query.list();
+ log.info("There are " + patientList.size() + " exposed infants with a summary page and no encounters");
+
+ List> ruleResults = new ArrayList<>();
+ for (Patient patient : patientList) {
+ RuleResult ruleResult = new RuleResult<>();
+ ruleResult.setActionUrl("coreapps/patientdashboard/patientDashboard.page?patientId=" + patient.getUuid());
+ ruleResult.setNotes("Exposed Infant #" + getExposedInfantNumber(patient) + " has no Encounters");
+ ruleResult.setEntity(patient);
+
+ ruleResults.add(ruleResult);
+ }
+
+ return ruleResults;
+ }
+
+ /**
+ * Exposed infants older than 2 years with Summary page but no final outcome
+ * @return
+ */
+ public List> exposedInfantsOlderThan18MonthsWithNoFinalOutcome() {
+ String queryString = "SELECT encounter FROM Obs o WHERE o.voided = false AND o.encounter.voided = false AND o.person.dead=0 AND o.encounter.encounterType.uuid = '9fcfcc91-ad60-4d84-9710-11cc25258719' AND o.person.personId NOT IN (SELECT o.person.personId FROM Obs o WHERE o.voided = false AND o.concept.conceptId = 99428) GROUP BY o.person.personId";
+
+ Query query = getSession().createQuery(queryString);
+
+ List encounterList = query.list();
+
+ List> ruleResults = new ArrayList<>();
+ Calendar today = new GregorianCalendar();
+ for (Encounter encounter : encounterList) {
+ // this rule only applies to infants below 18 months
+ if (Months.monthsBetween(new DateTime(encounter.getPatient().getBirthdate().getTime()), new DateTime()).getMonths() > 18) {
+ RuleResult ruleResult = new RuleResult<>();
+ String actionUrl =
+ "htmlformentryui/htmlform/enterHtmlFormWithStandardUi.page?formUuid=860c5f2f-cf3c-4c3f-b0c4-9958b6a5a938&patientId="
+ + encounter.getPatient().getUuid() + "&encounterId=" + encounter.getEncounterId();
+ if (encounter.getVisit() != null) {
+ actionUrl = actionUrl + "&visitId=" + encounter.getVisit().getId();
+ }
+ ruleResult.setActionUrl(actionUrl);
+ ruleResult.setNotes("Exposed Infant #" + getExposedInfantNumber(encounter.getPatient())
+ + " is over 18 months with no final outcome");
+ ruleResult.setEntity(encounter.getPatient());
+
+ ruleResults.add(ruleResult);
+ }
+ }
+
+ return ruleResults;
+ }
+
+ @Override
+ public DataIntegrityRule getRule() {
+ DataIntegrityRule rule = new DataIntegrityRule();
+ rule.setRuleCategory("patient");
+ rule.setHandlerConfig("java");
+ rule.setHandlerClassname(getClass().getName());
+ rule.setRuleName("Incomplete Exposed Infant information");
+ rule.setUuid("e0e6cb8d-8492-4bed-bf3f-08a3ecf3bedb");
+ return rule;
+ }
+
+ public List> exposedInfantsWithNoMotherARTNumber() {
+ String queryString = "SELECT encounter FROM Obs o WHERE o.voided = false AND o.encounter.voided = false AND o.person.dead=0 AND o.encounter.encounterType.uuid = '9fcfcc91-ad60-4d84-9710-11cc25258719' AND o.person.personId NOT IN (SELECT o.person.personId FROM Obs o WHERE o.voided = false AND o.concept.conceptId = 162874) GROUP BY o.person.personId";
+
+ Query query = getSession().createQuery(queryString);
+
+ List encounterList = query.list();
+
+ List> ruleResults = new ArrayList<>();
+ Calendar today = new GregorianCalendar();
+ for (Encounter encounter : encounterList) {
+ RuleResult ruleResult = new RuleResult<>();
+ String actionUrl =
+ "htmlformentryui/htmlform/enterHtmlFormWithStandardUi.page?formUuid=860c5f2f-cf3c-4c3f-b0c4-9958b6a5a938&patientId="
+ + encounter.getPatient().getUuid() + "&encounterId=" + encounter.getEncounterId();
+ if (encounter.getVisit() != null) {
+ actionUrl = actionUrl + "&visitId=" + encounter.getVisit().getId();
+ }
+ ruleResult.setActionUrl(actionUrl);
+ ruleResult.setNotes("Exposed Infant #" + getExposedInfantNumber(encounter.getPatient())
+ + " has no ART Number for mother");
+ ruleResult.setEntity(encounter.getPatient());
+
+ ruleResults.add(ruleResult);
+ }
+
+ return ruleResults;
+ }
+
+ public List> exposedInfantsWithMotherARTNumberButNotLinked() {
+ String queryString = "SELECT encounter FROM Obs o WHERE o.voided = false AND o.encounter.voided = false AND o.person.dead=0 AND o.encounter.encounterType.uuid = '9fcfcc91-ad60-4d84-9710-11cc25258719' AND o.person.personId IN (SELECT o.person.personId FROM Obs o WHERE o.voided = false AND o.concept.conceptId = 162874) AND o.person.personId NOT IN (SELECT r.personB.personId FROM Relationship r WHERE r.relationshipType.uuid = '8d91a210-c2cc-11de-8d13-0010c6dffd0f') GROUP BY o.person.personId";
+
+ Query query = getSession().createQuery(queryString);
+
+ List encounterList = query.list();
+
+ List> ruleResults = new ArrayList<>();
+ Calendar today = new GregorianCalendar();
+ for (Encounter encounter : encounterList) {
+ RuleResult ruleResult = new RuleResult<>();
+ String actionUrl =
+ "htmlformentryui/htmlform/enterHtmlFormWithStandardUi.page?formUuid=860c5f2f-cf3c-4c3f-b0c4-9958b6a5a938&patientId="
+ + encounter.getPatient().getUuid() + "&encounterId=" + encounter.getEncounterId();
+ if (encounter.getVisit() != null) {
+ actionUrl = actionUrl + "&visitId=" + encounter.getVisit().getId();
+ }
+ ruleResult.setActionUrl(actionUrl);
+ ruleResult.setNotes("Exposed Infant #" + getExposedInfantNumber(encounter.getPatient())
+ + " has an ART Number for mother but is not linked to mother");
+ ruleResult.setEntity(encounter.getPatient());
+
+ ruleResults.add(ruleResult);
+ }
+
+ return ruleResults;
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/dataintegrity/InvalidARTEncounters.java b/api/src/main/java/org/openmrs/module/ugandaemr/dataintegrity/InvalidARTEncounters.java
new file mode 100644
index 000000000..6d4fc8b7b
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/dataintegrity/InvalidARTEncounters.java
@@ -0,0 +1,150 @@
+package org.openmrs.module.ugandaemr.dataintegrity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hibernate.Query;
+import org.openmrs.Patient;
+import org.openmrs.Visit;
+import org.openmrs.module.dataintegrity.DataIntegrityRule;
+import org.openmrs.module.dataintegrity.rule.RuleResult;
+import org.springframework.stereotype.Component;
+
+/**
+ * Data integrity rules for the ART Summary page which are:
+ *
+ * Patients Without An ART Summary Page yet have Health Education and Encounters
+ * Patients With More Than One Summary Page
+ * Patients with ART summary page and no encounters
+ * Patients with more than one encounter on the same date
+ *
+ */
+@Component
+public class InvalidARTEncounters extends BasePatientRuleDefinition {
+
+ @Override
+ public List> evaluate() {
+ List> ruleResults = new ArrayList<>();
+
+ ruleResults.addAll(patientsWithMoreThanOneSummaryARTPage());
+ ruleResults.addAll(patientsWithEncountersAndNoSummaryPage());
+ ruleResults.addAll(patientsWithSummaryPageAndNoEncounters());
+ ruleResults.addAll(patientsWithMoreThanOneEncounterOnTheSameDate());
+ return ruleResults;
+ }
+
+ /**
+ * Patients with more than one ART Summary Page
+ * @return
+ */
+ private List> patientsWithMoreThanOneSummaryARTPage() {
+ log.info("Executing rule to find patients with more than one summary page");
+ String queryString = "SELECT patient FROM Encounter e WHERE e.voided = false AND e.encounterType.uuid = '8d5b27bc-c2cc-11de-8d13-0010c6dffd0f' GROUP BY e.patient.patientId HAVING COUNT(e.patient.patientId) > 1";
+
+ Query query = getSession().createQuery(queryString);
+
+ List patientList = query.list();
+ log.info("There are " + patientList.size() + " patients with more than one summary page");
+
+ List> ruleResults = new ArrayList<>();
+ for (Patient patient : patientList) {
+ RuleResult ruleResult = new RuleResult<>();
+ ruleResult.setActionUrl("coreapps/patientdashboard/patientDashboard.page?patientId=" + patient.getUuid());
+ ruleResult.setNotes("Client #" + getHIVClinicNumber(patient) + " has more than one ART Summary Page");
+ ruleResult.setEntity(patient);
+
+ ruleResults.add(ruleResult);
+ }
+
+ return ruleResults;
+ }
+
+ /**
+ * Patients with ART Encounter and Health Education encounters but no ART summary page
+ * @return
+ */
+ private List> patientsWithEncountersAndNoSummaryPage() {
+ log.info("Executing rule to find patients with encounters and no ART summary page");
+ String queryString = "SELECT patient FROM Encounter e WHERE e.voided = false AND ( e.encounterType.uuid = '8d5b2be0-c2cc-11de-8d13-0010c6dffd0f' OR e.encounterType.uuid = '6d88e370-f2ba-476b-bf1b-d8eaf3b1b67e') AND e.patient.patientId NOT IN (SELECT ee.patient.patientId FROM Encounter ee WHERE ee.encounterType.uuid = '8d5b27bc-c2cc-11de-8d13-0010c6dffd0f') GROUP BY e.patient.patientId";
+
+ Query query = getSession().createQuery(queryString);
+
+ List patientList = query.list();
+ log.info("There are " + patientList.size() + " patients with encounters and no ART summary page");
+
+ List> ruleResults = new ArrayList<>();
+ for (Patient patient : patientList) {
+ RuleResult ruleResult = new RuleResult<>();
+ ruleResult.setActionUrl("coreapps/patientdashboard/patientDashboard.page?patientId=" + patient.getUuid());
+ ruleResult.setNotes("Client #" + getHIVClinicNumber(patient) + " has encounters but no ART Summary Page");
+ ruleResult.setEntity(patient);
+
+ ruleResults.add(ruleResult);
+ }
+
+ return ruleResults;
+ }
+
+ /**
+ * Patients with an ART Summary page but no ART Encounter or Health Education encounters
+ * @return
+ */
+ private List> patientsWithSummaryPageAndNoEncounters() {
+ log.info("Executing rule to find patients with a summary page and no encounters");
+ String queryString = "SELECT patient FROM Encounter e WHERE e.voided = false AND ( e.encounterType.uuid = '8d5b27bc-c2cc-11de-8d13-0010c6dffd0f' ) AND e.patient.patientId NOT IN (SELECT ee.patient.patientId FROM Encounter ee WHERE ee.encounterType.uuid = '8d5b2be0-c2cc-11de-8d13-0010c6dffd0f' OR e.encounterType.uuid = '6d88e370-f2ba-476b-bf1b-d8eaf3b1b67e') GROUP BY e.patient.patientId";
+
+ Query query = getSession().createQuery(queryString);
+
+ List patientList = query.list();
+ log.info("There are " + patientList.size() + " patients with an ART summary page and no encounters");
+
+ List> ruleResults = new ArrayList<>();
+ for (Patient patient : patientList) {
+ RuleResult ruleResult = new RuleResult<>();
+ ruleResult.setActionUrl("coreapps/patientdashboard/patientDashboard.page?patientId=" + patient.getUuid());
+ ruleResult.setNotes("Client #" + getHIVClinicNumber(patient) + " has an ART Summary page but no Encounter or Health Education");
+ ruleResult.setEntity(patient);
+
+ ruleResults.add(ruleResult);
+ }
+
+ return ruleResults;
+ }
+
+ /**
+ * Patients more than one ART encounter on the same day
+ * @return
+ */
+ private List> patientsWithMoreThanOneEncounterOnTheSameDate() {
+ log.info("Executing rule to find patients with more than one encounters on a single date");
+ String queryString = "SELECT visit FROM Encounter e WHERE e.voided = false AND e.encounterType.uuid='8d5b2be0-c2cc-11de-8d13-0010c6dffd0f' GROUP BY e.encounterType.encounterTypeId, e.visit.visitId HAVING count(e.visit.visitId) > 1";
+
+ Query query = getSession().createQuery(queryString);
+
+ List visitList = query.list();
+ log.info("There are " + visitList.size() + " visits with more than one encounter page");
+
+ List> ruleResults = new ArrayList<>();
+ for (Visit visit : visitList) {
+ RuleResult ruleResult = new RuleResult<>();
+ ruleResult.setActionUrl("coreapps/patientdashboard/patientDashboard.page?patientId=" + visit.getPatient().getPatientId() + "&visitId=" + visit.getVisitId());
+ ruleResult.setNotes("Client #" + getHIVClinicNumber(visit.getPatient()) + " has more than one encounter on " + getDateFormatter().format(visit.getStartDatetime()));
+ ruleResult.setEntity(visit.getPatient());
+
+ ruleResults.add(ruleResult);
+ }
+
+ return ruleResults;
+ }
+
+ @Override
+ public DataIntegrityRule getRule() {
+ DataIntegrityRule rule = new DataIntegrityRule();
+ rule.setRuleCategory("patient");
+ rule.setHandlerConfig("java");
+ rule.setHandlerClassname(getClass().getName());
+ rule.setRuleName("Invalid ART Encounter pages");
+ rule.setUuid("c57c3b5a-9019-11e6-85aa-b75ca9392202");
+ return rule;
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/dataintegrity/InvalidTBEncounters.java b/api/src/main/java/org/openmrs/module/ugandaemr/dataintegrity/InvalidTBEncounters.java
new file mode 100644
index 000000000..13b011639
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/dataintegrity/InvalidTBEncounters.java
@@ -0,0 +1,223 @@
+package org.openmrs.module.ugandaemr.dataintegrity;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.hibernate.Query;
+import org.joda.time.DateTime;
+import org.openmrs.Encounter;
+import org.openmrs.Obs;
+import org.openmrs.Patient;
+import org.openmrs.Program;
+import org.openmrs.api.context.Context;
+import org.openmrs.module.ugandaemr.UgandaEMRConstants;
+import org.openmrs.module.ugandaemr.metadata.core.EncounterTypes;
+import org.openmrs.module.ugandaemr.metadata.core.Programs;
+import org.openmrs.module.dataintegrity.DataIntegrityRule;
+import org.openmrs.module.dataintegrity.rule.RuleResult;
+import org.springframework.stereotype.Component;
+
+/**
+ * Data integrity rules for the TB page which are:
+ *
+ * Patients with missing TB identifiers
+ * Patients with similar TB identifiers
+ * Patients with no final outcome after 9 months at start of treatment
+ *
+ */
+@Component
+public class InvalidTBEncounters extends BasePatientRuleDefinition {
+
+
+
+ @Override
+ public List> evaluate() {
+ List> ruleResults = new ArrayList<>();
+
+ ruleResults.addAll(patientsWithMissingTBNumbers(UgandaEMRConstants.UNIT_TB_NUMBER, "Unit TB Number"));
+ ruleResults.addAll(patientsWithMissingTBNumbers(UgandaEMRConstants.HSD_TB_NUMBER, "HSD TB Number"));
+ ruleResults.addAll(patientsWithMissingTBNumbers(UgandaEMRConstants.DISTRICT_TB_NUMBER, "District TB Number"));
+ ruleResults.addAll(singlePatientWithDuplicateTBNumberAcrossMultipleTreatmentPrograms(UgandaEMRConstants.DISTRICT_TB_NUMBER, "District TB Number"));
+ ruleResults.addAll(singlePatientWithDuplicateTBNumberAcrossMultipleTreatmentPrograms(UgandaEMRConstants.UNIT_TB_NUMBER, "Unit TB Number"));
+ ruleResults.addAll(singlePatientWithDuplicateTBNumberAcrossMultipleTreatmentPrograms(UgandaEMRConstants.HSD_TB_NUMBER, "HSD TB Number"));
+ ruleResults.addAll(multiplePatientsWithTheSameTBIdentifiers(UgandaEMRConstants.DISTRICT_TB_NUMBER, "District TB Number"));
+ ruleResults.addAll(multiplePatientsWithTheSameTBIdentifiers(UgandaEMRConstants.UNIT_TB_NUMBER, "Unit TB Number"));
+ ruleResults.addAll(multiplePatientsWithTheSameTBIdentifiers(UgandaEMRConstants.HSD_TB_NUMBER, "HSD TB Number"));
+ ruleResults.addAll(patientsWithNoFinalOutcomeNineMonthsAfterStartOfTreatment(DateTime.now()));
+ return ruleResults;
+ }
+
+ /**
+ * Patients with similar TB identifiers: duplicated across multiple patients
+ * @return List>
+ */
+ public List> multiplePatientsWithTheSameTBIdentifiers(String identifierConceptUuid, String identifierTitle) {
+ log.info("Executing rule to find Patients with similar TB identifiers: duplicated across multiple patients");
+ String queryString = "SELECT obs FROM Obs obs WHERE obs.obsId IN ("
+ + "SELECT min(o.obsId) FROM Obs o WHERE o.voided = false AND o.encounter.patient.dead = 0 AND o.encounter.voided = 0 AND o.concept.uuid = :identifierConceptUuid AND o.valueText IN "
+ + " (SELECT ob.valueText FROM Obs ob WHERE ob.concept.uuid = :identifierConceptUuid AND ob.voided = 0 GROUP BY ob.concept.conceptId, ob.valueText HAVING COUNT(ob.valueText) > 1)"
+ + " GROUP BY o.person.personId, o.concept.id, o.valueText HAVING count(*) = 1)";
+ Query query = getSession().createQuery(queryString);
+ query.setParameter("identifierConceptUuid", identifierConceptUuid);
+
+ List obsList = query.list();
+ Set uniquePatientList = new HashSet();
+ List> ruleResults = new ArrayList<>();
+ for (Obs obs : obsList) {
+ Patient patient = obs.getEncounter().getPatient();
+
+ RuleResult ruleResult = new RuleResult<>();
+
+ if (uniquePatientList.contains(patient) == false) {
+ ruleResult.setActionUrl("htmlformentryui/htmlform/editHtmlFormWithStandardUi.page?patientId=" + patient.getUuid() + "&encounterId=" + obs.getEncounter().getId());
+ ruleResult.setNotes("The " + identifierTitle + " " + getTbNumber(patient, obs.getEncounter(), identifierConceptUuid) + " is being used by another patient");
+ ruleResult.setEntity(patient);
+
+ ruleResults.add(ruleResult);
+ }
+
+ uniquePatientList.add(patient);
+ }
+
+ log.info("There are " + uniquePatientList.size() + " Patients with similar " + identifierTitle);
+
+ return ruleResults;
+ }
+
+ /**
+ * Patients with similar TB identifiers: duplicated for a single patient,
+ * @return List>
+ */
+ public List> singlePatientWithDuplicateTBNumberAcrossMultipleTreatmentPrograms(String identifierConceptUuid, String identifierTitle) {
+ log.info("Executing rule to find Patients with similar TB identifiers: duplicated for a single patient");
+ String queryString = "SELECT o FROM Obs o WHERE o.voided = false AND o.encounter.patient.dead = 0 AND o.encounter.voided = 0 AND o.concept.uuid = :identifierConceptUuid AND o.obsId IN "
+ + " (SELECT min(ob.obsId) FROM Obs ob WHERE ob.concept.uuid = :identifierConceptUuid AND ob.voided = 0 GROUP BY ob.person.personId, ob.concept.id, ob.valueText HAVING COUNT(ob.valueText) > 1)";
+
+ Query query = getSession().createQuery(queryString);
+ query.setParameter("identifierConceptUuid", identifierConceptUuid);
+
+ List obsList = query.list();
+ Set uniquePatientList = new HashSet();
+
+ List> ruleResults = new ArrayList<>();
+ for (Obs obs : obsList) {
+ Patient patient = obs.getEncounter().getPatient();
+ RuleResult ruleResult = new RuleResult<>();
+
+ if (uniquePatientList.contains(patient) == false) {
+ ruleResult.setActionUrl("htmlformentryui/htmlform/editHtmlFormWithStandardUi.page?patientId=" + patient.getUuid() + "&encounterId=" + obs.getEncounter().getId());
+ ruleResult.setNotes("The " + identifierTitle + " " + getTbNumber(patient, obs.getEncounter(), identifierConceptUuid) + " is used by the same patient across multiple treatment programs");
+ ruleResult.setEntity(patient);
+
+ ruleResults.add(ruleResult);
+ }
+
+ uniquePatientList.add(patient);
+ }
+
+ log.info("There are " + uniquePatientList.size() + " Sharing the same " + identifierTitle);
+
+ return ruleResults;
+ }
+
+ /**
+ * Patients with missing TB Number identifiers
+ * @return List>
+ */
+ public List> patientsWithMissingTBNumbers(String tbIdentifierConceptUuid, String identifierTitle) {
+ log.info("Executing rule to find patients with missing " + identifierTitle + " TB identifiers");
+
+ String queryString = "SELECT pp.patient FROM PatientProgram pp WHERE pp.voided = 0 AND pp.program.uuid = :tbProgramUuid GROUP BY pp.patient.patientId";
+
+ Query query = getSession().createQuery(queryString);
+ query.setParameter("tbProgramUuid", Programs.TB_PROGRAM.uuid());
+
+ List patientList = query.list();
+ log.info("There are " + patientList.size() + " Patients Enrolled in TB program");
+
+ List> ruleResults = new ArrayList<>();
+ Program tbProgram = Context.getProgramWorkflowService().getProgramByUuid(Programs.TB_PROGRAM.uuid());
+
+ //For each patient enrolled in the TB program, look for encounters with missing identifier
+ for (Patient patient : patientList) {
+ String encounterQueryString = "FROM Encounter e WHERE e.voided = false AND e.patient.id = :patientId AND e.encounterType.uuid = :encounterType AND e.id NOT IN (SELECT o.encounter.id FROM Obs o WHERE o.concept.uuid = :conceptUuid)";
+
+ Query encounterQuery = getSession().createQuery(encounterQueryString);
+ encounterQuery.setParameter("patientId", patient.getId());
+ encounterQuery.setParameter("encounterType", EncounterTypes.TB_SUMMARY_ENCOUNTER.uuid());
+ encounterQuery.setParameter("conceptUuid", tbIdentifierConceptUuid);
+
+ List patientEncounters = encounterQuery.list();
+
+ for (Encounter encounter : patientEncounters) {
+ RuleResult ruleResult = new RuleResult<>();
+ ruleResult.setActionUrl("htmlformentryui/htmlform/editHtmlFormWithStandardUi.page?patientId=" + patient.getUuid() + "&encounterId=" + encounter.getId());
+ ruleResult.setNotes("Patient #" + getTbNumber(patient, encounter, null) + " has a missing " + identifierTitle );
+ ruleResult.setEntity(patient);
+
+ ruleResults.add(ruleResult);
+ }
+ }
+
+ return ruleResults;
+ }
+
+ /**
+ * Patients with no final outcome after 9 months at start of treatment
+ * @return List>
+ */
+ public List> patientsWithNoFinalOutcomeNineMonthsAfterStartOfTreatment(DateTime date) {
+ log.info("Executing rule to find patients with no final TB Outcome 9 months after start of treatment");
+
+ String queryString = "SELECT pp.patient FROM PatientProgram pp WHERE pp.voided = 0 AND pp.dateEnrolled <= :date AND pp.outcome IS NULL AND pp.program.uuid = '" + Programs.TB_PROGRAM.uuid() + "'";
+ Query query = getSession().createQuery(queryString);
+ DateTime nineMonthsBefore = date.plusMonths(-9);
+ query.setParameter("date", nineMonthsBefore.toDate());
+
+ List patientsInProgramList = query.list();
+
+ Set uniquePatientList = new HashSet(patientsInProgramList);
+
+ List> ruleResults = new ArrayList<>();
+ for (Patient patient : uniquePatientList) {
+ //Fetch the latest encounter for this patient
+ String encounterQueryString = "FROM Encounter e WHERE e.voided = false AND e.patient.id = :patientId AND e.voided = 0 AND e.encounterType.uuid = :encounterTypeUuid ORDER BY e.encounterDatetime DESC";
+ Query encounterQuery = getSession().createQuery(encounterQueryString);
+ encounterQuery.setMaxResults(1);
+ encounterQuery.setParameter("patientId", patient.getId());
+ encounterQuery.setParameter("encounterTypeUuid", EncounterTypes.TB_SUMMARY_ENCOUNTER.uuid());
+
+ RuleResult ruleResult = new RuleResult<>();
+
+ List encounterList = encounterQuery.list();
+ if (encounterList.size() > 0) {
+ Encounter encounter = encounterList.get(0);
+ ruleResult.setActionUrl("htmlformentryui/htmlform/editHtmlFormWithStandardUi.page?patientId=" + patient.getUuid() + "&encounterId=" + encounter);
+ ruleResult.setNotes("Patient #" + getTbNumber(patient, encounter, null) + " has no final TB Outcome 9 months after start of treatment");
+ } else {
+ ruleResult.setActionUrl("coreapps/patientdashboard/patientDashboard.page?patientId=" + patient.getId());
+ ruleResult.setNotes("Patient #" + getOpenMrsId(patient) + " has no final TB Outcome 9 months after start of treatment");
+ }
+ ruleResult.setEntity(patient);
+
+ ruleResults.add(ruleResult);
+ }
+
+ return ruleResults;
+ }
+
+
+ @Override
+ public DataIntegrityRule getRule() {
+ DataIntegrityRule rule = new DataIntegrityRule();
+ rule.setRuleCategory("patient");
+ rule.setHandlerConfig("java");
+ rule.setHandlerClassname(getClass().getName());
+ rule.setRuleName("Invalid TB Encounters");
+ rule.setUuid("94e9a389-b872-4b74-b72c-df9fa7197e39");
+ return rule;
+ }
+
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/datatype/UgandaEMRLocationDatatype.java b/api/src/main/java/org/openmrs/module/ugandaemr/datatype/UgandaEMRLocationDatatype.java
new file mode 100644
index 000000000..808a9b1c2
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/datatype/UgandaEMRLocationDatatype.java
@@ -0,0 +1,53 @@
+/**
+ * The contents of this file are subject to the OpenMRS Public License
+ * Version 1.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://license.openmrs.org
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * Copyright (C) OpenMRS, LLC. All Rights Reserved.
+ */
+
+package org.openmrs.module.ugandaemr.datatype;
+
+import org.apache.commons.lang.StringUtils;
+import org.openmrs.Location;
+import org.openmrs.api.context.Context;
+import org.openmrs.customdatatype.SerializingCustomDatatype;
+import org.springframework.stereotype.Component;
+
+/**
+ * Custom datatype for {@link Location}.
+ * (This should be moved to the OpenMRS core.)
+ */
+@Component
+public class UgandaEMRLocationDatatype extends SerializingCustomDatatype {
+
+ /**
+ * @see SerializingCustomDatatype#deserialize(String)
+ */
+ @Override
+ public Location deserialize(String serializedValue) {
+ if (StringUtils.isEmpty(serializedValue)) {
+ return null;
+ }
+
+ return Context.getLocationService().getLocation(Integer.valueOf(serializedValue));
+ }
+
+ /**
+ * @see SerializingCustomDatatype#serialize(Object)
+ */
+ @Override
+ public String serialize(Location typedValue) {
+ if (typedValue == null) {
+ return null;
+ }
+
+ return typedValue.getLocationId().toString();
+ }
+}
\ No newline at end of file
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/identifier/EIDIdentifierValidator.java b/api/src/main/java/org/openmrs/module/ugandaemr/identifier/EIDIdentifierValidator.java
new file mode 100644
index 000000000..65b14daad
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/identifier/EIDIdentifierValidator.java
@@ -0,0 +1,125 @@
+package org.openmrs.module.ugandaemr.identifier;
+
+import java.util.Calendar;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.openmrs.patient.IdentifierValidator;
+import org.openmrs.patient.UnallowedIdentifierException;
+
+/**
+ * Validate the Exposed Infant Diagnosis and Birth Cohort Number identifiers
+ *
+ *
+ * EXP Number format EXP12345
+ * New Birth Cohort number MM/YY/XXX - 2 digits of the month, two digits of the year, and 3 numbers
+ *
+ *
+ *
+ * Created by ssmusoke on 23/02/2016.
+ */
+public class EIDIdentifierValidator implements IdentifierValidator {
+
+ /**
+ * @return The name of this validator
+ */
+ public String getName() {
+ return "Exposed Infant (EXP) Number validtor ";
+ }
+
+ /**
+ * @param identifier The Identifier to check.
+ * @return Whether this identifier is valid according to the validator.
+ * @throws UnallowedIdentifierException if the identifier contains unallowed characters or is
+ * otherwise not appropriate for this validator.
+ */
+ public boolean isValid(String identifier) throws UnallowedIdentifierException {
+ // check the EXP number first
+ String exp_regex = "[E][X][P][\\/][0-9][0-9][0-9][0-9]";
+
+ Pattern pattern = Pattern.compile(exp_regex);
+ Matcher matcher = pattern.matcher(identifier);
+ if (matcher.matches()) {
+ return true;
+ }
+
+ // validate the identifier as Birth Cohort Number
+ return validateBirthCohortFormat(identifier);
+ }
+
+ /**
+ * @param validIdentifier The identifier prior to being given a check digit or other form
+ * of validation.
+ * @return The identifier after the check digit or other form of validation has been applied.
+ * @throws UnallowedIdentifierException if the identifier contains unallowed characters or is
+ * otherwise not appropriate for this validator.
+ */
+ public String getValidIdentifier(String validIdentifier) throws UnallowedIdentifierException {
+ if (validIdentifier != null) {
+ validIdentifier = validIdentifier.toUpperCase().trim();
+ }
+ return validIdentifier;
+ }
+
+ /**
+ * Validate the Birth Cohort identifier
+ *
+ * @param identifier
+ * @return whether the birth cohort identifier value is valid or not
+ * @throws UnallowedIdentifierException
+ */
+ public boolean validateBirthCohortFormat(String identifier) throws UnallowedIdentifierException {
+ // check that the length is 9
+ if (identifier.length() < 9) {
+ throw new UnallowedIdentifierException("The identifier is less than 9 characters including slashes");
+ }
+ if (identifier.length() > 9) {
+ throw new UnallowedIdentifierException("The identifier is more than 9 characters");
+ }
+ // slashes at position 3 and 6
+ if (identifier.charAt(2) != '/') {
+ throw new UnallowedIdentifierException("Missing / at index 3");
+ }
+ if (identifier.charAt(5) != '/') {
+ throw new UnallowedIdentifierException("Missing / at index 6");
+ }
+ // the first 2 digits are valid months
+ int months = Integer.parseInt(identifier.substring(0, 2));
+ if (months < 1) {
+ throw new UnallowedIdentifierException("The months '" + months + "' in the first two digits of the identifier "
+ + "are "
+ + "invalid");
+ }
+ if (months > 12) {
+ throw new UnallowedIdentifierException("The months '" + months + "' in the first two digits of the identifier "
+ + "are "
+ + "invalid");
+ }
+
+ // position 4 and 5 are years greater than 14 (2014)
+ int years = Integer.parseInt(identifier.substring(3, 5));
+ // get the 2 digit representation of the current year
+ int current_year = Calendar.getInstance().get(Calendar.YEAR) % 100;
+ if (years < 15) {
+ throw new UnallowedIdentifierException("The year '" + years + "' in the identifier is less than 15");
+ }
+ if (years > current_year) {
+ throw new UnallowedIdentifierException("The year '" + years + "' in the identifier is greater than the "
+ + "current year");
+ }
+ // the last 3 digits are a valid number greater than 0
+ int counter = Integer.parseInt(identifier.substring(6));
+ if (counter > 0) {
+ return true;
+ } else {
+ throw new UnallowedIdentifierException("The counter in the identifier is not 3 digits");
+ }
+ }
+
+ /**
+ * @return A string containing all the characters allowed in this type of identifier validation.
+ */
+ public String getAllowedCharacters() {
+ return "0123456789EXP/";
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/identifier/NINIdentifierValidator.java b/api/src/main/java/org/openmrs/module/ugandaemr/identifier/NINIdentifierValidator.java
new file mode 100644
index 000000000..75d6e397c
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/identifier/NINIdentifierValidator.java
@@ -0,0 +1,62 @@
+package org.openmrs.module.ugandaemr.identifier;
+
+import org.openmrs.patient.IdentifierValidator;
+import org.openmrs.patient.UnallowedIdentifierException;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Validate the NIN
+ *
+ *
+ * NIN format CM131521234X
+ *
+ *
+ *
+ */
+public class NINIdentifierValidator implements IdentifierValidator {
+
+ /**
+ * @return The name of this validator
+ */
+ public String getName() {
+ return "National ID Validator validator ";
+ }
+
+ /**
+ * @param identifier The Identifier to check.
+ * @return Whether this identifier is valid according to the validator.
+ * @throws UnallowedIdentifierException if the identifier contains unallowed characters or is
+ * otherwise not appropriate for this validator.
+ */
+ public boolean isValid(String identifier) throws UnallowedIdentifierException {
+ // check the NIN number first
+ String exp_regex = "^$|^[A-Z][FM]\\d{5}([A-Z0-9]){7}$";
+ Pattern pattern = Pattern.compile(exp_regex);
+ Matcher matcher = pattern.matcher(identifier);
+ return matcher.matches();
+
+ }
+
+ /**
+ * @param validIdentifier The identifier prior to being given a check digit or other form
+ * of validation.
+ * @return The identifier after the check digit or other form of validation has been applied.
+ * @throws UnallowedIdentifierException if the identifier contains unallowed characters or is
+ * otherwise not appropriate for this validator.
+ */
+ public String getValidIdentifier(String validIdentifier) throws UnallowedIdentifierException {
+ if (validIdentifier != null) {
+ validIdentifier = validIdentifier.toUpperCase().trim();
+ }
+ return validIdentifier;
+ }
+
+ /**
+ * @return A string containing all the characters allowed in this type of identifier validation.
+ */
+ public String getAllowedCharacters() {
+ return "0123456789ABCDEFGHIJKLMNOPQRSTUVWYZ";
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/metadata/core/EncounterRoles.java b/api/src/main/java/org/openmrs/module/ugandaemr/metadata/core/EncounterRoles.java
new file mode 100644
index 000000000..3bf2423c5
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/metadata/core/EncounterRoles.java
@@ -0,0 +1,15 @@
+package org.openmrs.module.ugandaemr.metadata.core;
+
+/**
+ * Created by lubwamasamuel on 13/01/2017.
+ */
+public class EncounterRoles {
+
+ public static final String ASSISTANT_CIRCUMCISER_NAME = "Assistant Circumciser";
+ public static final String ASSISTANT_CIRCUMCISER_DESCRIPTION = "A role for a person who Assists During a Circumcision";
+ public static final String ASSISTANT_CIRCUMCISER_UUID = "22498b0c-f509-4ff6-9982-4c654c1e4dc3";
+
+ public static final String PHARMACIST_NAME = "Pharmacist";
+ public static final String PHARMACIST_DESCRIPTION = "This role is for persons who dispense medication fo patients";
+ public static final String PHARMACIST_UUID = "8da340f3-c690-439f-b21f-3a8367ff4057";
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/metadata/core/EncounterTypes.java b/api/src/main/java/org/openmrs/module/ugandaemr/metadata/core/EncounterTypes.java
new file mode 100644
index 000000000..52167045f
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/metadata/core/EncounterTypes.java
@@ -0,0 +1,234 @@
+package org.openmrs.module.ugandaemr.metadata.core;
+
+import org.openmrs.module.metadatadeploy.descriptor.EncounterTypeDescriptor;
+
+/**
+ * Created by lubwamasamuel on 18/10/16.
+ */
+public class EncounterTypes {
+ public static EncounterTypeDescriptor PNC_ENCOUNTER_TYPE = new EncounterTypeDescriptor() {
+ @Override
+ public String name() {
+ return "PNC - Encounter";
+ }
+
+ @Override
+ public String description() {
+ return "An encounter when a patient gets PNC services";
+ }
+
+ public String uuid() {
+ return "fa6f3ff5-b784-43fb-ab35-a08ab7dbf074";
+ }
+ };
+
+ public static EncounterTypeDescriptor SMC_FOLLOW_UP_ENCOUNTER = new EncounterTypeDescriptor() {
+ @Override
+ public String name() {
+ return "SMC FOLLOW UP - Encounter";
+ }
+
+ @Override
+ public String description() {
+ return "An encounter for SMC Follow up";
+ }
+
+ public String uuid() {
+ return "d0f9e0b7-f336-43bd-bf50-0a7243857fa6";
+ }
+ };
+
+ public static EncounterTypeDescriptor OPD_ENCOUNTER = new EncounterTypeDescriptor() {
+ @Override
+ public String name() {
+ return "OPD Encounter";
+ }
+
+ @Override
+ public String description() {
+ return "Outpatient Clinical Ecnounter";
+ }
+
+ public String uuid() {
+ return "ee4780f5-b5eb-423b-932f-00b5879df5ab";
+ }
+ };
+
+ public static EncounterTypeDescriptor TB_SUMMARY_ENCOUNTER = new EncounterTypeDescriptor() {
+ @Override
+ public String name() {
+ return "TB Summary (Enrollment)";
+ }
+
+ @Override
+ public String description() {
+ return "An encounter for the initial visit to the TB clinic";
+ }
+
+ public String uuid() {
+ return "334bf97e-28e2-4a27-8727-a5ce31c7cd66";
+ }
+ };
+
+ public static EncounterTypeDescriptor TB_FOLLOWUP_ENCOUNTER = new EncounterTypeDescriptor() {
+ @Override
+ public String name() {
+ return "TB Encounter (Followup)";
+ }
+
+ @Override
+ public String description() {
+ return "An encounter for a return visit to the TB clinic";
+ }
+
+ public String uuid() {
+ return "455bad1f-5e97-4ee9-9558-ff1df8808732";
+ }
+ };
+
+
+ public static EncounterTypeDescriptor DR_TB_SUMMARY_ENCOUNTER = new EncounterTypeDescriptor() {
+ @Override
+ public String name() {
+ return "DR TB Summary (Enrollment)";
+ }
+
+ @Override
+ public String description() {
+ return "An encounter for the initial visit to the Drug Resistance TB Program";
+ }
+
+ public String uuid() {
+ return "0271ee3d-f274-49d1-b376-c842f075413f";
+ }
+ };
+
+ public static EncounterTypeDescriptor DR_TB_FOLLOWUP_ENCOUNTER = new EncounterTypeDescriptor() {
+ @Override
+ public String name() {
+ return "DR TB Encounter (Followup)";
+ }
+
+ @Override
+ public String description() {
+ return "An encounter for a return visit to the Drug Resistance TB Program";
+ }
+
+ public String uuid() {
+ return "41f8609d-e13b-4dff-8379-47ac5876512e";
+ }
+ };
+
+
+ public static EncounterTypeDescriptor VIRAL_LOAD_NON_SUPPRESSED = new EncounterTypeDescriptor() {
+ @Override
+ public String name() {
+ return "Viral Load Non Suppressed Encounter";
+ }
+
+ @Override
+ public String description() {
+ return "Viral Load Non Suppressed Follow up";
+ }
+
+ public String uuid() {
+ return "38cb2232-30fc-4b1f-8df1-47c795771ee9";
+ }
+ };
+
+ public static EncounterTypeDescriptor APPOINTMENT_FOLLOW_UP = new EncounterTypeDescriptor() {
+ @Override
+ public String name() {
+ return "Appointment Follow-up ";
+ }
+
+ @Override
+ public String description() {
+ return "Followup actions for patients especially after missing a facility visit";
+ }
+
+ public String uuid() {
+ return "dc551efc-024d-4c40-aeb8-2147c4033778";
+ }
+ };
+
+ public static EncounterTypeDescriptor TRIAGE = new EncounterTypeDescriptor() {
+ @Override
+ public String name() {
+ return "Triage";
+ }
+
+ @Override
+ public String description() {
+ return "This is a form to capture information on triage. It include Vitals, global security indicators etc....";
+ }
+
+ public String uuid() {
+ return "0f1ec66d-61db-4575-8248-94e10a88178f";
+ }
+ };
+
+ public static EncounterTypeDescriptor MEDICATION_DISPENSE = new EncounterTypeDescriptor() {
+ @Override
+ public String name() {
+ return "Medication Dispense";
+ }
+
+ @Override
+ public String description() {
+ return "This encounter type is for dispensing of medication at facility";
+ }
+
+ public String uuid() {
+ return "22902411-19c1-4a02-b19a-bf1a9c24fd51";
+ }
+ };
+
+ public static EncounterTypeDescriptor MISSED_APPOINTMENT_TRACKING = new EncounterTypeDescriptor() {
+ @Override
+ public String name() {
+ return "Missed Appointment Tracking";
+ }
+
+ @Override
+ public String description() {
+ return "This encounter type is for tracking followup for missed appointments";
+ }
+
+ public String uuid() {
+ return "791faefd-36b8-482f-ab78-20c297b03851";
+ }
+ };
+
+ public static EncounterTypeDescriptor TRANSFER_IN = new EncounterTypeDescriptor() {
+ @Override
+ public String name() {
+ return "Transfer In";
+ }
+
+ @Override
+ public String description() {
+ return "Transfer in encounter";
+ }
+
+ public String uuid() {
+ return "3e8354f7-31b3-4862-a52e-ff41a1ee60af";
+ }
+ };
+
+ public static EncounterTypeDescriptor TRANSFER_OUT = new EncounterTypeDescriptor() {
+ @Override
+ public String name() {
+ return "Transfer Out";
+ }
+
+ @Override
+ public String description() {
+ return "Transfer out encounter";
+ }
+
+ public String uuid() {
+ return "e305d98a-d6a2-45ba-ba2a-682b497ce27c";
+ }
+ };
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/metadata/core/Flags.java b/api/src/main/java/org/openmrs/module/ugandaemr/metadata/core/Flags.java
new file mode 100644
index 000000000..7734de927
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/metadata/core/Flags.java
@@ -0,0 +1,801 @@
+package org.openmrs.module.ugandaemr.metadata.core;
+
+import org.openmrs.module.patientflags.metadatadeploy.descriptor.FlagDescriptor;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class Flags {
+
+ public static FlagDescriptor DUE_FOR_FIRST_VIRAL_LOAD = new FlagDescriptor() {
+ @Override
+ public String criteria() {
+ return "SELECT p.patient_id, DATE_FORMAT(DATE_ADD(o.value_datetime, INTERVAL 6 MONTH), '%d.%b.%Y') FROM patient p \n" +
+ " INNER JOIN obs o ON p.patient_id = o.person_id INNER JOIN encounter e ON o.encounter_id = e.encounter_id \n" +
+ " INNER JOIN encounter_type et ON e.encounter_type = et.encounter_type_id \n" +
+ " INNER JOIN person pp ON pp.person_id=p.patient_id WHERE pp.dead =false \n" +
+ " AND ((o.concept_id = 99161 AND o.voided = FALSE AND e.voided = FALSE AND ((CURRENT_DATE() BETWEEN DATE_ADD(o.value_datetime, INTERVAL 5 MONTH) AND DATE_ADD(o.value_datetime, INTERVAL 6 MONTH)) AND et.uuid='8d5b27bc-c2cc-11de-8d13-0010c6dffd0f'))) AND o.person_id \n" +
+ " NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 1305 AND oo.voided = FALSE)\n" +
+ " AND p.patient_id NOT IN (select o.person_id from obs o where concept_id=90306 and o.voided=FALSE)";
+ }
+
+ @Override
+ public String message() {
+ return "Due for 1st Viral on ${1}";
+ }
+
+ @Override
+ public String priority() {
+ return Priorites.GREEN.uuid();
+ }
+
+ @Override
+ public List tags() {
+ return Arrays.asList(Tags.PATIENT_STATUS.uuid());
+ }
+
+ @Override
+ public String name() {
+ return "Due for 1st Viral Load";
+ }
+
+ @Override
+ public String description() {
+ return "Patients who are due for their first viral load after enrollment into HIV Care";
+ }
+
+ @Override
+ public String uuid() {
+ return "7376f82e-225c-4340-9a8d-22e679532f37";
+ }
+ };
+
+ public static FlagDescriptor OVERDUE_FOR_FIRST_VIRAL_LOAD = new FlagDescriptor() {
+ @Override
+ public String criteria() {
+ return "SELECT p.patient_id, DATE_FORMAT(DATE_ADD(o.value_datetime, INTERVAL 6 MONTH), '%d.%b.%Y') FROM patient p\n" +
+ " INNER JOIN obs o ON p.patient_id = o.person_id\n" +
+ " INNER JOIN person pp ON pp.person_id = p.patient_id WHERE pp.dead = FALSE \n" +
+ " AND o.concept_id = 99161 AND o.voided = FALSE AND CURRENT_DATE() >= DATE_ADD(o.value_datetime, INTERVAL 6 MONTH)\n" +
+ " AND o.person_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 1305 AND oo.voided = FALSE)\n" +
+ " AND p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 90306 AND oo.voided = FALSE)";
+ }
+
+ @Override
+ public String message() {
+ return "First Viral Load Overdue from ${1}";
+ }
+
+ @Override
+ public String priority() {
+ return Priorites.RED.uuid();
+ }
+
+ @Override
+ public List tags() {
+ return Arrays.asList(Tags.PATIENT_STATUS.uuid());
+ }
+
+ @Override
+ public String name() {
+ return "Overdue for First Viral Load";
+ }
+
+ @Override
+ public String description() {
+ return "Patients who are overdue for their first viral load after enrollment into HIV Care";
+ }
+
+ @Override
+ public String uuid() {
+ return "6ce583d1-a4d7-41a6-902f-9a5debea1ec7";
+ }
+ };
+
+ public static FlagDescriptor DUE_FOR_ROUTINE_VIRAL_LOAD = new FlagDescriptor() {
+ @Override
+ public String criteria() {
+ return "SELECT p.patient_id, DATE_FORMAT(IF(TIMESTAMPDIFF(YEAR, pe.birthdate, CURDATE()) < 16, DATE_ADD(MAX(o.value_datetime), INTERVAL 6 MONTH), DATE_ADD(MAX(o.value_datetime), INTERVAL 12 MONTH)), '%d.%b.%Y') FROM patient p\n" +
+ "INNER JOIN obs o ON p.patient_id = o.person_id \n" +
+ "INNER JOIN person pe ON o.person_id = pe.person_id \n" +
+ "WHERE pe.dead=FALSE\n" +
+ "AND o.concept_id = 163023 AND o.voided = FALSE \n" +
+ "GROUP BY pe.person_id, pe.birthdate \n" +
+ "HAVING DATEDIFF(IF(TIMESTAMPDIFF(YEAR, pe.birthdate, CURDATE()) < 16, DATE_ADD(MAX(o.value_datetime), INTERVAL 6 MONTH), DATE_ADD(MAX(o.value_datetime), INTERVAL 12 MONTH)), CURRENT_DATE()) BETWEEN 0 AND 30\n" +
+ "AND p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 90306 AND oo.voided = FALSE)";
+ }
+
+ @Override
+ public String message() {
+ return "Due for Routine Viral Load on ${1}";
+ }
+
+ @Override
+ public String priority() {
+ return Priorites.GREEN.uuid();
+ }
+
+ @Override
+ public List tags() {
+ return Arrays.asList(Tags.PATIENT_STATUS.uuid());
+ }
+
+ @Override
+ public String name() {
+ return "Due for Routine Viral Load";
+ }
+
+ @Override
+ public String description() {
+ return "Patients who are due for their routine viral load";
+ }
+
+ @Override
+ public String uuid() {
+ return "5bc66261-7ad6-4c66-97d9-eb3c511a9274";
+ }
+ };
+
+ public static FlagDescriptor OVERDUE_FOR_ROUTINE_VIRAL_LOAD = new FlagDescriptor() {
+ @Override
+ public String criteria() {
+ return "SELECT p.patient_id, DATE_FORMAT(IF(TIMESTAMPDIFF(YEAR, pe.birthdate, CURDATE()) < 16, DATE_ADD(MAX(o.value_datetime), INTERVAL 6 MONTH), DATE_ADD(MAX(o.value_datetime), INTERVAL 12 MONTH)), '%d.%b.%Y') FROM patient p \n" +
+ "INNER JOIN obs o ON p.patient_id = o.person_id\n" +
+ "INNER JOIN person pe ON o.person_id = pe.person_id \n" +
+ " WHERE pe.dead=FALSE\n" +
+ "AND o.concept_id = 163023 AND o.voided = FALSE\n" +
+ "GROUP BY pe.person_id, pe.birthdate \n" +
+ "HAVING CURRENT_DATE() > IF(TIMESTAMPDIFF(YEAR, pe.birthdate, CURDATE()) < 16, DATE_ADD(MAX(o.value_datetime), INTERVAL 6 MONTH), DATE_ADD(MAX(o.value_datetime), INTERVAL 12 MONTH))\n" +
+ "AND p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 90306 AND oo.voided = FALSE)";
+ }
+
+ @Override
+ public String message() {
+ return "Overdue for Routine Viral Load from ${1}";
+ }
+
+ @Override
+ public String priority() {
+ return Priorites.RED.uuid();
+ }
+
+ @Override
+ public List tags() {
+ return Arrays.asList(Tags.PATIENT_STATUS.uuid());
+ }
+
+ @Override
+ public String name() {
+ return "Overdue for Routine Viral Load";
+ }
+
+ @Override
+ public String description() {
+ return "Patients who are overdue for their routine viral load";
+ }
+
+ @Override
+ public String uuid() {
+ return "ffa7a845-bcdd-4727-a68a-3bad8c09692d";
+ }
+ };
+
+ public static FlagDescriptor UPCOMING_APPOINTMENT = new FlagDescriptor() {
+ @Override
+ public String criteria() {
+ return "SELECT p.patient_id, DATE_FORMAT(MAX(o.value_datetime), '%d.%b.%Y') FROM patient p \n" +
+ "INNER JOIN obs o ON p.patient_id = o.person_id \n" +
+ "INNER JOIN person pe on pe.person_id=p.patient_id\n" +
+ " WHERE pe.dead=FALSE\n" +
+ "AND o.concept_id = 5096 AND o.voided = FALSE GROUP BY o.person_id HAVING MAX(o.value_datetime) >= CURRENT_DATE()\n" +
+ "AND p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 90306 AND oo.voided = FALSE)";
+ }
+
+ @Override
+ public String message() {
+ return "Upcoming appointment on ${1}";
+ }
+
+ @Override
+ public String priority() {
+ return Priorites.GREEN.uuid();
+ }
+
+ @Override
+ public List tags() {
+ return Arrays.asList(Tags.PATIENT_STATUS.uuid());
+ }
+
+ @Override
+ public String name() {
+ return "Upcoming appointment";
+ }
+
+ @Override
+ public String description() {
+ return "Patients who have an upcoming appointment";
+ }
+
+ @Override
+ public String uuid() {
+ return "1cbc86cf-8a5a-4402-b56a-6489aa4d4f2d";
+ }
+ };
+
+ public static FlagDescriptor MISSED_APPOINTMENT = new FlagDescriptor() {
+ @Override
+ public String criteria() {
+ return "SELECT p.patient_id, DATE_FORMAT(MAX(o.value_datetime), '%d.%b.%Y') \n" +
+ "FROM patient p\n" +
+ " INNER JOIN obs o ON p.patient_id = o.person_id \n" +
+ " INNER JOIN person pe on pe.person_id=p.patient_id\n" +
+ " WHERE pe.dead=FALSE\n" +
+ " AND o.concept_id = 5096 AND o.voided = FALSE GROUP BY o.person_id \n" +
+ " HAVING MAX(o.value_datetime) BETWEEN DATE_SUB(CURRENT_DATE(), INTERVAL 29 DAY) AND DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY)\n" +
+ " AND p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 90306 AND oo.voided = FALSE)";
+ }
+
+ @Override
+ public String message() {
+ return "Missed appointment on ${1}";
+ }
+
+ @Override
+ public String priority() {
+ return Priorites.ORANGE.uuid();
+ }
+
+ @Override
+ public List tags() {
+ return Arrays.asList(Tags.PATIENT_STATUS.uuid());
+ }
+
+ @Override
+ public String name() {
+ return "Missed appointment";
+ }
+
+ @Override
+ public String description() {
+ return "Patients who have missed appointment - this is between 7 to 29 days";
+ }
+
+ @Override
+ public String uuid() {
+ return "a248d8ca-9e60-4a54-a417-fcf00302fdb2";
+ }
+ };
+
+ public static FlagDescriptor PATIENT_LOST = new FlagDescriptor() {
+ @Override
+ public String criteria() {
+ return "SELECT p.patient_id, DATE_FORMAT(DATE_ADD(MAX(o.value_datetime), INTERVAL 30 DAY), '%d.%b.%Y') FROM patient p\n" +
+ " INNER JOIN obs o ON p.patient_id = o.person_id\n" +
+ " INNER JOIN person pe on pe.person_id=p.patient_id\n" +
+ " WHERE pe.dead=FALSE \n" +
+ " AND o.concept_id = 5096 AND o.voided = FALSE GROUP BY o.person_id \n" +
+ " HAVING MAX(o.value_datetime) BETWEEN DATE_SUB(CURRENT_DATE(), INTERVAL 89 DAY) AND DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)\n" +
+ " AND p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 90306 AND oo.voided = FALSE)";
+ }
+
+ @Override
+ public String message() {
+ return "Lost since ${1}";
+ }
+
+ @Override
+ public String priority() {
+ return Priorites.RED.uuid();
+ }
+
+ @Override
+ public List tags() {
+ return Arrays.asList(Tags.PATIENT_STATUS.uuid());
+ }
+
+ @Override
+ public String name() {
+ return "Lost";
+ }
+
+ @Override
+ public String description() {
+ return "Patients who have missed appointment - this is between 30 to 89 days";
+ }
+
+ @Override
+ public String uuid() {
+ return "4d10da66-1ede-4a92-aa71-a8dedb13a0ba";
+ }
+ };
+
+ public static FlagDescriptor PATIENT_LOST_TO_FOLLOWUP = new FlagDescriptor() {
+ @Override
+ public String criteria() {
+ return "SELECT p.patient_id, DATE_FORMAT(DATE_ADD(MAX(o.value_datetime), INTERVAL 90 DAY), '%d.%b.%Y') FROM patient p\n" +
+ " INNER JOIN obs o ON p.patient_id = o.person_id \n" +
+ "INNER JOIN person pp ON pp.person_id=p.patient_id \n" +
+ "WHERE pp.dead =false AND o.concept_id = 5096 AND o.voided = FALSE GROUP BY o.person_id \n" +
+ "HAVING MAX(o.value_datetime) <= DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY)\n" +
+ "AND p.patient_id NOT IN (select o.person_id from obs o where concept_id=90306 and o.voided=false)";
+ }
+
+ @Override
+ public String message() {
+ return "Lost to follow-up since ${1}";
+ }
+
+ @Override
+ public String priority() {
+ return Priorites.RED.uuid();
+ }
+
+ @Override
+ public List tags() {
+ return Arrays.asList(Tags.PATIENT_STATUS.uuid());
+ }
+
+ @Override
+ public String name() {
+ return "Lost to Followup ";
+ }
+
+ @Override
+ public String description() {
+ return "Patients who have spent more than 90 days since their expected return date";
+ }
+
+ @Override
+ public String uuid() {
+ return "5a1f8283-9d5a-4efe-89a3-5634e01c8083";
+ }
+ };
+
+ public static FlagDescriptor DUE_FOR_FIRST_DNA_PCR = new FlagDescriptor() {
+ @Override
+ public String criteria() {
+ return "SELECT p.patient_id , DATE_FORMAT(DATE_ADD(pe.birthdate, INTERVAL 6 WEEK), '%d.%b.%Y') FROM patient p \n" +
+ " INNER JOIN obs o ON p.patient_id = o.person_id \n" +
+ " INNER JOIN person pe ON p.patient_id = pe.person_id \n" +
+ " INNER JOIN encounter e ON o.encounter_id = e.encounter_id \n" +
+ " INNER JOIN encounter_type et ON e.encounter_type = et.encounter_type_id \n" +
+ " WHERE pe.dead=FALSE \n" +
+ " AND (TIMESTAMPDIFF(WEEK, pe.birthdate, CURDATE()) BETWEEN 6 AND 9) AND et.uuid='9fcfcc91-ad60-4d84-9710-11cc25258719'\n" +
+ " AND p.patient_id NOT IN (SELECT ee.patient_id FROM encounter ee INNER JOIN encounter_type ete ON ee.encounter_type = ete.encounter_type_id WHERE ete.uuid = '8d5b27bc-c2cc-11de-8d13-0010c6dffd0f' AND ee.voided = FALSE) GROUP BY p.patient_id \n" +
+ " HAVING p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 99606 AND oo.voided = FALSE)\n" +
+ "AND p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 90306 AND oo.voided = FALSE) ";
+ }
+
+ @Override
+ public String message() {
+ return "Due for 1st DNA PCR on ${1}";
+ }
+
+ @Override
+ public String priority() {
+ return Priorites.GREEN.uuid();
+ }
+
+ @Override
+ public List tags() {
+ return Arrays.asList(Tags.PATIENT_STATUS.uuid());
+ }
+
+ @Override
+ public String name() {
+ return "Due for 1st DNA PCR";
+ }
+
+ @Override
+ public String description() {
+ return "Exposed infants who are due for their first DNA PCR, between 6 to 9 weeks";
+ }
+
+ @Override
+ public String uuid() {
+ return "e8141fdf-4359-461d-840a-216442d45392";
+ }
+ };
+
+ public static FlagDescriptor OVERDUE_FOR_FIRST_DNA_PCR = new FlagDescriptor() {
+ @Override
+ public String criteria() {
+ return "SELECT p.patient_id , DATE_FORMAT(DATE_ADD(pe.birthdate, INTERVAL 6 WEEK), '%d.%b.%Y') FROM patient p \n" +
+ " INNER JOIN obs o ON p.patient_id = o.person_id \n" +
+ " INNER JOIN person pe ON p.patient_id = pe.person_id \n" +
+ " INNER JOIN encounter e ON o.encounter_id = e.encounter_id \n" +
+ " INNER JOIN encounter_type et ON e.encounter_type = et.encounter_type_id \n" +
+ "WHERE pe.dead=FALSE \n" +
+ " AND (TIMESTAMPDIFF(WEEK, pe.birthdate, CURDATE()) BETWEEN 10 AND 24) AND et.uuid='9fcfcc91-ad60-4d84-9710-11cc25258719' AND p.patient_id \n" +
+ " NOT IN (SELECT ee.patient_id FROM encounter ee INNER JOIN encounter_type ete ON ee.encounter_type = ete.encounter_type_id WHERE ete.uuid = '8d5b27bc-c2cc-11de-8d13-0010c6dffd0f' AND ee.voided = FALSE) GROUP BY p.patient_id HAVING p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 99606 AND oo.voided = FALSE)\n" +
+ " AND p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 90306 AND oo.voided = FALSE)";
+ }
+
+ @Override
+ public String message() {
+ return "Overdue for 1st DNA PCR from ${1}";
+ }
+
+ @Override
+ public String priority() {
+ return Priorites.RED.uuid();
+ }
+
+ @Override
+ public List tags() {
+ return Arrays.asList(Tags.PATIENT_STATUS.uuid());
+ }
+
+ @Override
+ public String name() {
+ return "Overdue for 1st DNA PCR";
+ }
+
+ @Override
+ public String description() {
+ return "Exposed infants who are overdue for their first DNA PCR who are aged between 10 to 24 weeks";
+ }
+
+ @Override
+ public String uuid() {
+ return "162e4459-24b9-4a0e-ae10-08413d48aecb";
+ }
+ };
+
+ public static FlagDescriptor DUE_FOR_SECOND_DNA_PCR = new FlagDescriptor() {
+ @Override
+ public String criteria() {
+ return "SELECT p.patient_id , DATE_FORMAT(DATE_ADD(pe.birthdate, INTERVAL 9 MONTH), '%d.%b.%Y') FROM patient p \n" +
+ " INNER JOIN obs o ON p.patient_id = o.person_id INNER JOIN person pe ON p.patient_id = pe.person_id \n" +
+ " INNER JOIN encounter e ON o.encounter_id = e.encounter_id INNER JOIN encounter_type et ON e.encounter_type = et.encounter_type_id \n" +
+ " WHERE pe.dead=FALSE \n" +
+ " AND (TIMESTAMPDIFF(MONTH, pe.birthdate, CURDATE()) BETWEEN 9 AND 10) AND et.uuid='9fcfcc91-ad60-4d84-9710-11cc25258719' \n" +
+ " AND p.patient_id NOT IN (SELECT ee.patient_id FROM encounter ee INNER JOIN encounter_type ete ON ee.encounter_type = ete.encounter_type_id \n" +
+ " WHERE ete.uuid = '8d5b27bc-c2cc-11de-8d13-0010c6dffd0f' AND ee.voided = FALSE) GROUP BY p.patient_id \n" +
+ " HAVING p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 99436 AND oo.voided = FALSE)\n" +
+ " AND p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 90306 AND oo.voided = FALSE)";
+ }
+
+ @Override
+ public String message() {
+ return "Due for 2nd DNA PCR on ${1}";
+ }
+
+ @Override
+ public String priority() {
+ return Priorites.GREEN.uuid();
+ }
+
+ @Override
+ public List tags() {
+ return Arrays.asList(Tags.PATIENT_STATUS.uuid());
+ }
+
+ @Override
+ public String name() {
+ return "Due for 2nd DNA PCR";
+ }
+
+ @Override
+ public String description() {
+ return "Exposed infants who are due for their second DNA PCR aged between 13 and 14 months";
+ }
+
+ @Override
+ public String uuid() {
+ return "4e0527ed-1a5f-4c83-b372-031829eb334b";
+ }
+ };
+
+ public static FlagDescriptor OVERDUE_FOR_SECOND_DNA_PCR = new FlagDescriptor() {
+ @Override
+ public String criteria() {
+ return "SELECT p.patient_id , DATE_FORMAT(DATE_ADD(pe.birthdate, INTERVAL 9 MONTH), '%d.%b.%Y') FROM patient p \n" +
+ " INNER JOIN obs o ON p.patient_id = o.person_id INNER JOIN person pe ON p.patient_id = pe.person_id \n" +
+ " INNER JOIN encounter e ON o.encounter_id = e.encounter_id INNER JOIN encounter_type et ON e.encounter_type = et.encounter_type_id \n" +
+ " WHERE pe.dead=FALSE \n" +
+ " AND (TIMESTAMPDIFF(MONTH, pe.birthdate, CURDATE()) BETWEEN 11 AND 13) AND et.uuid='9fcfcc91-ad60-4d84-9710-11cc25258719' \n" +
+ " AND p.patient_id NOT IN (SELECT ee.patient_id FROM encounter ee INNER JOIN encounter_type ete ON ee.encounter_type = ete.encounter_type_id \n" +
+ " WHERE ete.uuid = '8d5b27bc-c2cc-11de-8d13-0010c6dffd0f' AND ee.voided = FALSE) GROUP BY p.patient_id \n" +
+ " HAVING p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 99436 AND oo.voided = FALSE)\n" +
+ " AND p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 90306 AND oo.voided = FALSE)";
+ }
+
+ @Override
+ public String message() {
+ return "Overdue for 2nd DNA PCR from ${1}";
+ }
+
+ @Override
+ public String priority() {
+ return Priorites.RED.uuid();
+ }
+
+ @Override
+ public List tags() {
+ return Arrays.asList(Tags.PATIENT_STATUS.uuid());
+ }
+
+ @Override
+ public String name() {
+ return "Overue for 2nd DNA PCR";
+ }
+
+ @Override
+ public String description() {
+ return "Exposed infants who are overdue for their second DNA PCR who are between 15 and 17 months since Rapid Test is due at 18 months";
+ }
+
+ @Override
+ public String uuid() {
+ return "356357b4-c285-4a92-82b2-ddcfe6cdf3f4";
+ }
+ };
+
+ public static FlagDescriptor DUE_FOR_THIRD_DNA_PCR = new FlagDescriptor() {
+ @Override
+ public String criteria() {
+ return "SELECT p.patient_id , DATE_FORMAT(DATE_ADD(pe.birthdate, INTERVAL 13 MONTH), '%d.%b.%Y') FROM patient p \n" +
+ " INNER JOIN obs o ON p.patient_id = o.person_id INNER JOIN person pe ON p.patient_id = pe.person_id \n" +
+ " INNER JOIN encounter e ON o.encounter_id = e.encounter_id INNER JOIN encounter_type et ON e.encounter_type = et.encounter_type_id \n" +
+ " WHERE pe.dead=FALSE \n" +
+ " AND (TIMESTAMPDIFF(MONTH, pe.birthdate, CURDATE()) BETWEEN 13 AND 14) AND et.uuid='9fcfcc91-ad60-4d84-9710-11cc25258719' \n" +
+ " AND p.patient_id NOT IN (SELECT ee.patient_id FROM encounter ee INNER JOIN encounter_type ete ON ee.encounter_type = ete.encounter_type_id \n" +
+ " WHERE ete.uuid = '8d5b27bc-c2cc-11de-8d13-0010c6dffd0f' AND ee.voided = FALSE) GROUP BY p.patient_id \n" +
+ " HAVING p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 99436 AND oo.voided = FALSE)\n" +
+ " AND p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 90306 AND oo.voided = FALSE)";
+ }
+
+ @Override
+ public String message() {
+ return "Due for 3rd DNA PCR on ${1}";
+ }
+
+ @Override
+ public String priority() {
+ return Priorites.GREEN.uuid();
+ }
+
+ @Override
+ public List tags() {
+ return Arrays.asList(Tags.PATIENT_STATUS.uuid());
+ }
+
+ @Override
+ public String name() {
+ return "Due for 3rd DNA PCR";
+ }
+
+ @Override
+ public String description() {
+ return "Exposed infants who are due for their third DNA PCR aged between 13 and 14 months";
+ }
+
+ @Override
+ public String uuid() {
+ return "636a9bd2-9f13-45ca-886b-9380a959965";
+ }
+ };
+
+
+ public static FlagDescriptor OVERDUE_FOR_THIRD_DNA_PCR = new FlagDescriptor() {
+ @Override
+ public String criteria() {
+ return "SELECT p.patient_id , DATE_FORMAT(DATE_ADD(pe.birthdate, INTERVAL 13 MONTH), '%d.%b.%Y') FROM patient p\n" +
+ " INNER JOIN obs o ON p.patient_id = o.person_id INNER JOIN person pe ON p.patient_id = pe.person_id\n" +
+ " INNER JOIN encounter e ON o.encounter_id = e.encounter_id INNER JOIN encounter_type et ON e.encounter_type = et.encounter_type_id\n" +
+ " WHERE pe.dead=FALSE\n" +
+ " AND (TIMESTAMPDIFF(MONTH, pe.birthdate, CURDATE()) BETWEEN 15 AND 17) AND et.uuid='9fcfcc91-ad60-4d84-9710-11cc25258719'\n" +
+ " AND p.patient_id NOT IN (SELECT ee.patient_id FROM encounter ee INNER JOIN encounter_type ete ON ee.encounter_type = ete.encounter_type_id\n" +
+ " WHERE ete.uuid = '8d5b27bc-c2cc-11de-8d13-0010c6dffd0f' AND ee.voided = FALSE) GROUP BY p.patient_id\n" +
+ " HAVING p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 99436 AND oo.voided = FALSE)\n" +
+ " AND p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 90306 AND oo.voided = FALSE)";
+ }
+
+ @Override
+ public String message() {
+ return "OverDue for 3rd DNA PCR on ${1}";
+ }
+
+ @Override
+ public String priority() {
+ return Priorites.RED.uuid();
+ }
+
+ @Override
+ public List tags() {
+ return Arrays.asList(Tags.PATIENT_STATUS.uuid());
+ }
+
+ @Override
+ public String name() {
+ return "OverDue for 3rd DNA PCR";
+ }
+
+ @Override
+ public String description() {
+ return "Exposed infants who are overdue for their third DNA PCR, 6 weeks after breastfeeding and having taken a 2nd DNA PCR";
+ }
+
+ @Override
+ public String uuid() {
+ return "65f4da17-5fa3-497e-848f-cc515b69ff81";
+ }
+ };
+
+ public static FlagDescriptor DUE_FOR_RAPID_TEST = new FlagDescriptor() {
+ @Override
+ public String criteria() {
+ return "SELECT p.patient_id , DATE_FORMAT(DATE_ADD(pe.birthdate, INTERVAL 18 MONTH), '%d.%b.%Y') FROM patient p \n" +
+ " INNER JOIN obs o ON p.patient_id = o.person_id INNER JOIN person pe ON p.patient_id = pe.person_id \n" +
+ " INNER JOIN encounter e ON o.encounter_id = e.encounter_id INNER JOIN encounter_type et ON e.encounter_type = et.encounter_type_id \n" +
+ " WHERE pe.dead=FALSE \n" +
+ " AND TIMESTAMPDIFF(MONTH, pe.birthdate, CURDATE()) = 18 AND et.uuid='9fcfcc91-ad60-4d84-9710-11cc25258719' \n" +
+ " AND p.patient_id NOT IN (SELECT ee.patient_id FROM encounter ee INNER JOIN encounter_type ete ON ee.encounter_type = ete.encounter_type_id \n" +
+ " WHERE ete.uuid = '8d5b27bc-c2cc-11de-8d13-0010c6dffd0f' AND ee.voided = FALSE) GROUP BY p.patient_id \n" +
+ " HAVING p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 162879 AND oo.voided = FALSE)\n" +
+ " AND p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 90306 AND oo.voided = FALSE)";
+ }
+
+ @Override
+ public String message() {
+ return "Due for Rapid Test from ${1}";
+ }
+
+ @Override
+ public String priority() {
+ return Priorites.GREEN.uuid();
+ }
+
+ @Override
+ public List tags() {
+ return Arrays.asList(Tags.PATIENT_STATUS.uuid());
+ }
+
+ @Override
+ public String name() {
+ return "Due for Rapid Test";
+ }
+
+ @Override
+ public String description() {
+ return "Exposed infants who are due for an 18 month rapid test";
+ }
+
+ @Override
+ public String uuid() {
+ return "f3fb9704-5460-4041-9e77-6d5e9e34c202";
+ }
+ };
+
+ public static FlagDescriptor OVERDUE_FOR_RAPID_TEST = new FlagDescriptor() {
+ @Override
+ public String criteria() {
+ return "SELECT p.patient_id , DATE_FORMAT(DATE_ADD(pe.birthdate, INTERVAL 18 MONTH), '%d.%b.%Y') FROM patient p \n" +
+ " INNER JOIN obs o ON p.patient_id = o.person_id INNER JOIN person pe ON p.patient_id = pe.person_id \n" +
+ " INNER JOIN encounter e ON o.encounter_id = e.encounter_id INNER JOIN encounter_type et ON e.encounter_type = et.encounter_type_id \n" +
+ " WHERE pe.dead=FALSE \n" +
+ " AND (TIMESTAMPDIFF(MONTH, pe.birthdate, CURDATE()) BETWEEN 19 AND 24) AND et.uuid='9fcfcc91-ad60-4d84-9710-11cc25258719' \n" +
+ " AND p.patient_id NOT IN (SELECT ee.patient_id FROM encounter ee \n" +
+ " INNER JOIN encounter_type ete ON ee.encounter_type = ete.encounter_type_id \n" +
+ " WHERE ete.uuid = '8d5b27bc-c2cc-11de-8d13-0010c6dffd0f' AND ee.voided = FALSE) GROUP BY p.patient_id \n" +
+ " HAVING p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 162879 AND oo.voided = FALSE)\n" +
+ " AND p.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 90306 AND oo.voided = FALSE) ";
+ }
+
+ @Override
+ public String message() {
+ return "Overdue for Rapid Test from ${1}";
+ }
+
+ @Override
+ public String priority() {
+ return Priorites.GREEN.uuid();
+ }
+
+ @Override
+ public List tags() {
+ return Arrays.asList(Tags.PATIENT_STATUS.uuid());
+ }
+
+ @Override
+ public String name() {
+ return "Overdue for Rapid Test";
+ }
+
+ @Override
+ public String description() {
+ return "Exposed infants who are overdue for an 18 month rapid test but not older than 24 months by which a final outcome is expected";
+ }
+
+ @Override
+ public String uuid() {
+ return "d144893e-b17b-4b77-916e-d3d2b3733378";
+ }
+ };
+
+ public static FlagDescriptor HAS_DETECTABLE_VIRAL_LOAD = new FlagDescriptor() {
+ @Override
+ public String criteria() {
+ return "SELECT non_suppressed.patient_id, non_suppressed.value_numeric, DATE_FORMAT((non_suppressed.obs_datetime), '%d. %b. %Y')\n" +
+ " FROM (SELECT person_id, MAX(obs_datetime) as dt FROM obs b\n" +
+ " WHERE b.concept_id = 856 AND b.voided = 0 group by person_id) latest_vl\n" +
+ " INNER JOIN (SELECT c.person_id as patient_id, obs_datetime, value_numeric\n" +
+ " FROM obs c WHERE c.concept_id = 856 AND c.voided = 0 AND c.value_numeric > 1000) non_suppressed\n" +
+ " ON (latest_vl.person_id = non_suppressed.patient_id and latest_vl.dt = non_suppressed.obs_datetime)\n" +
+ " INNER JOIN person pe ON pe.person_id = non_suppressed.patient_id WHERE pe.dead=FALSE\n" +
+ " AND non_suppressed.patient_id NOT IN (SELECT oo.person_id FROM obs oo WHERE oo.concept_id = 90306 AND oo.voided = FALSE)";
+ }
+
+ @Override
+ public String message() {
+ return "Detectable Viral Load of ${1} from ${2} may be due for IAC";
+ }
+
+ @Override
+ public String priority() {
+ return Priorites.RED.uuid();
+ }
+
+ @Override
+ public List tags() {
+ return Arrays.asList(Tags.PATIENT_STATUS.uuid());
+ }
+
+ @Override
+ public String name() {
+ return "Un-supressed Viral Load";
+ }
+
+ @Override
+ public String description() {
+ return "Patients who are have un-supressed viral load";
+ }
+
+ @Override
+ public String uuid() {
+ return "151801e2-0742-457f-8610-95530a4db232";
+ }
+ };
+
+ public static FlagDescriptor PATIENT_TRANSFERED_OUT = new FlagDescriptor() {
+ @Override
+ public String criteria() {
+ return "Select p.patient_id,ooo.value_text ,DATE_FORMAT(oo.value_datetime,'%d. %b. %Y')\n" +
+ "from obs o INNER JOIN obs oo ON o.person_id=oo.person_id\n" +
+ "INNER JOIN patient p on p.patient_id =o.person_id\n" +
+ " INNER JOIN obs ooo ON o.person_id = ooo.person_id WHERE o.concept_id =90306 AND o.voided= FALSE \n" +
+ " and oo.concept_id =99165 AND oo.voided = FALSE AND ooo.voided = FALSE and ooo.concept_id = 90211";
+ }
+
+ @Override
+ public String message() {
+ return "Patient Transfered Out to ${1} on ${2}";
+ }
+
+ @Override
+ public String priority() {
+ return Priorites.RED.uuid();
+ }
+
+ @Override
+ public List tags() {
+ return Arrays.asList(Tags.PATIENT_STATUS.uuid());
+ }
+
+ @Override
+ public String name() {
+ return "Transfered Out Patient";
+ }
+
+ @Override
+ public String description() {
+ return "Patients who are transfered Out to Another Facility";
+ }
+
+ @Override
+ public String uuid() {
+ return "c5cae7e7-d6e3-4d5f-b684-ea888b5a8a7c";
+ }
+ };
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/metadata/core/LocationTags.java b/api/src/main/java/org/openmrs/module/ugandaemr/metadata/core/LocationTags.java
new file mode 100644
index 000000000..12d369828
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/metadata/core/LocationTags.java
@@ -0,0 +1,45 @@
+package org.openmrs.module.ugandaemr.metadata.core;
+
+import org.openmrs.module.metadatadeploy.descriptor.LocationTagDescriptor;
+
+public class LocationTags {
+
+ public static LocationTagDescriptor LOGIN_LOCATION = new LocationTagDescriptor(){
+
+ @Override
+ public String uuid() {
+ return "b8bbf83e-645f-451f-8efe-a0db56f09676";
+ }
+
+ @Override
+ public String name() {
+ return "Login Location";
+ }
+
+ @Override
+ public String description() {
+ return "When a user logs in and chooses a session location, they may only choose one with this tag";
+ }
+
+ };
+
+ public static LocationTagDescriptor VISIT_LOCATION = new LocationTagDescriptor(){
+
+ @Override
+ public String uuid() {
+ return "37dd4458-dc9e-4ae6-a1f1-789c1162d37b";
+ }
+
+ @Override
+ public String name() {
+ return "Visit Location";
+ }
+
+ @Override
+ public String description() {
+ return "Visits are only allowed to happen at locations tagged with this location tag or at locations that descend from a location tagged with this tag.";
+ }
+
+ };
+
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/metadata/core/Locations.java b/api/src/main/java/org/openmrs/module/ugandaemr/metadata/core/Locations.java
new file mode 100644
index 000000000..6e064d7b4
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/metadata/core/Locations.java
@@ -0,0 +1,302 @@
+package org.openmrs.module.ugandaemr.metadata.core;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.openmrs.api.context.Context;
+import org.openmrs.module.metadatadeploy.descriptor.LocationDescriptor;
+import org.openmrs.module.metadatadeploy.descriptor.LocationTagDescriptor;
+
+public class Locations {
+
+ public static LocationDescriptor PARENT = new LocationDescriptor(){
+
+ @Override
+ public String uuid() {
+ return "629d78e9-93e5-43b0-ad8a-48313fd99117";
+ }
+
+ @Override
+ public String description() {
+ return "Health Center Location";
+ }
+
+ @Override
+ public String name() {
+ return Context.getLocationService().getLocationByUuid("629d78e9-93e5-43b0-ad8a-48313fd99117").getName();
+ }
+
+ @Override
+ public List tags() {
+
+ return Arrays.asList(
+ LocationTags.LOGIN_LOCATION,
+ LocationTags.VISIT_LOCATION
+ );
+
+ }
+
+ };
+
+ public static LocationDescriptor TB_CLINIC = new LocationDescriptor(){
+
+ @Override
+ public String uuid() {
+ return "8ab22b55-9a17-4121-bf08-6134a9a2439f";
+ }
+
+ @Override
+ public String description() {
+ return "Clinic where TB Care and Treatment Services are provided";
+ }
+
+ @Override
+ public String name() {
+ return "TB Clinic";
+ }
+
+ @Override
+ public LocationDescriptor parent() {
+ return PARENT;
+ }
+
+ @Override
+ public List tags() {
+
+ return Arrays.asList(
+ LocationTags.LOGIN_LOCATION,
+ LocationTags.VISIT_LOCATION
+ );
+
+ }
+
+ };
+
+ public static LocationDescriptor OPD_CLINIC = new LocationDescriptor(){
+
+ @Override
+ public String uuid() {
+ return "11d5d2b8-0fdd-42e0-9f53-257c760bb9a3";
+ }
+
+ @Override
+ public String description() {
+ return "Clinic where Out-Patient Services are provided";
+ }
+
+ @Override
+ public String name() {
+ return "OPD Clinic";
+ }
+
+ @Override
+ public LocationDescriptor parent() {
+ return PARENT;
+ }
+
+ @Override
+ public List tags() {
+
+ return Arrays.asList(
+ LocationTags.LOGIN_LOCATION,
+ LocationTags.VISIT_LOCATION
+ );
+
+ }
+
+ };
+
+ public static LocationDescriptor UNKNOWN = new LocationDescriptor(){
+
+ @Override
+ public String uuid() {
+ return "8d6c993e-c2cc-11de-8d13-0010c6dffd0f";
+ }
+
+ @Override
+ public String description() {
+ return "Unknown location";
+ }
+
+ @Override
+ public String name() {
+ return "Unknown";
+ }
+
+ @Override
+ public LocationDescriptor parent() {
+ return PARENT;
+ }
+
+ };
+
+ public static LocationDescriptor TRIAGE = new LocationDescriptor(){
+
+ @Override
+ public String uuid() {
+ return "ff01eaab-561e-40c6-bf24-539206b521ce";
+ }
+
+ @Override
+ public String description() {
+ return "A location for categorization of patients";
+ }
+
+ @Override
+ public String name() {
+ return "Triage";
+ }
+
+ @Override
+ public LocationDescriptor parent() {
+ return PARENT;
+ }
+
+ @Override
+ public List tags() {
+
+ return Arrays.asList(
+ LocationTags.LOGIN_LOCATION,
+ LocationTags.VISIT_LOCATION
+ );
+
+ }
+
+ };
+
+ public static LocationDescriptor RECEPTION = new LocationDescriptor(){
+
+ @Override
+ public String uuid() {
+ return "4501e132-07a2-4201-9dc8-2f6769b6d412";
+ }
+
+ @Override
+ public String description() {
+ return "A Location for registering patients";
+ }
+
+ @Override
+ public String name() {
+ return "Reception";
+ }
+
+ @Override
+ public LocationDescriptor parent() {
+ return PARENT;
+ }
+
+ @Override
+ public List tags() {
+
+ return Arrays.asList(
+ LocationTags.LOGIN_LOCATION,
+ LocationTags.VISIT_LOCATION
+ );
+
+ }
+
+ };
+
+ public static LocationDescriptor PHARMACY = new LocationDescriptor(){
+
+ @Override
+ public String uuid() {
+ return "3ec8ff90-3ec1-408e-bf8c-22e4553d6e17";
+ }
+
+ @Override
+ public String description() {
+ return "A place for preparing, dispensing, and reviewing drugs and providing additional clinical services";
+ }
+
+ @Override
+ public String name() {
+ return "Pharmacy";
+ }
+
+ @Override
+ public LocationDescriptor parent() {
+ return PARENT;
+ }
+
+ @Override
+ public List tags() {
+
+ return Arrays.asList(
+ LocationTags.LOGIN_LOCATION,
+ LocationTags.VISIT_LOCATION
+ );
+
+ }
+
+ };
+
+ public static LocationDescriptor COUNSELING_CENTER = new LocationDescriptor(){
+
+ @Override
+ public String uuid() {
+ return "7c231e1a-1db5-11ea-978f-2e728ce88125";
+ }
+
+ @Override
+ public String description() {
+ return "A location where counseling and screening is done for a patient";
+ }
+
+ @Override
+ public String name() {
+ return "Counseling Center";
+ }
+
+ @Override
+ public LocationDescriptor parent() {
+ return PARENT;
+ }
+
+ @Override
+ public List tags() {
+
+ return Arrays.asList(
+ LocationTags.LOGIN_LOCATION,
+ LocationTags.VISIT_LOCATION
+ );
+
+ }
+
+ };
+
+ public static LocationDescriptor Community = new LocationDescriptor(){
+
+ @Override
+ public String uuid() {
+ return "841cb8d9-b662-41ad-9e7f-d476caac48aa";
+ }
+
+ @Override
+ public String description() {
+ return "This is a location that caters for all clients served in the community ";
+ }
+
+ @Override
+ public String name() {
+ return "Community";
+ }
+
+ @Override
+ public LocationDescriptor parent() {
+ return PARENT;
+ }
+
+ @Override
+ public List tags() {
+
+ return Arrays.asList(
+ LocationTags.VISIT_LOCATION
+ );
+
+ }
+
+ };
+
+}
diff --git a/api/src/main/java/org/openmrs/module/ugandaemr/metadata/core/PatientIdentifierTypes.java b/api/src/main/java/org/openmrs/module/ugandaemr/metadata/core/PatientIdentifierTypes.java
new file mode 100644
index 000000000..c653a9380
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/ugandaemr/metadata/core/PatientIdentifierTypes.java
@@ -0,0 +1,317 @@
+package org.openmrs.module.ugandaemr.metadata.core;
+
+import org.openmrs.module.ugandaemr.identifier.NINIdentifierValidator;
+import org.openmrs.module.idgen.validator.LuhnMod30IdentifierValidator;
+import org.openmrs.module.idgen.validator.LuhnModNIdentifierValidator;
+import org.openmrs.module.metadatadeploy.descriptor.PatientIdentifierTypeDescriptor;
+import org.openmrs.patient.IdentifierValidator;
+
+/**
+ * Constants for defined patient identifier types
+ *