Sunday, August 2, 2015

Setting up Grails 3 in Spring Tool Suite (STS) or Eclipse

Steps to follow

1) Download grails 3.0.4
3) Add grails bin folder to your PATH environment variable
2) Reconfigure eclipse following the instructions here for Gradle support: https://tedvinke.wordpress.com/2015/04/10/grails-3-released-installing-gradle-and-groovy-2-4-support-in-eclipseggts/
3) Import from bitbucket
4) Learn how to run application following documentation notes: http://grails.github.io/grails-doc/3.0.4/guide/gettingStarted.html#aHelloWorldExample
5) Open command-line, then change directory to the project folder, type 'grails run-app'

The other alternative is to try intelliJ

To use intelliJ with Grails, the following link can be helpful:

IntelliJ + Grails

1) Download IntelliJ community edition
2) Download Git SCM
3)  Open IntelliJ and click 'Check out from Version Control' and select 'Git'
 

4)  Copy and paste the Git URL and click Clone



5) Enter your password to access bitbucket

 Even IntelliJ suffers from the same problems as STS. So you will still have to use the command-line to invoke grails specific commands as mentioned in the following link:

http://stackoverflow.com/questions/28261484/intellij-idea-grails-3-0-error














Thursday, July 16, 2015

Importing SNOMED CT concepts into Openmrs 2.2 using the web services API



The documentation on using the openmrs web services is sufficiently enough to know how to query the database using GET methods. However, when trying to create new records using POST, it's a lot of guesswork. For anybody else that's interested in creating concepts using the web services API, an example is shown below written in Groovy code:

Openmrs Reference API


 package anu.jcmsr.mml.human  
 import grails.converters.JSON  
 import grails.plugins.rest.client.RestBuilder  
 import java.nio.charset.Charset  
 import org.apache.log4j.Logger  
 import org.codehaus.groovy.grails.web.json.JSONArray  
 import org.codehaus.groovy.grails.web.json.JSONObject  
 import org.springframework.http.HttpStatus  
 import org.springframework.http.converter.StringHttpMessageConverter  
 /**  
  * Service class for interacting with openmrs database  
  * @author Philip Wu  
  *  
  */  
 //@Transactional  
 class OpenMrsService {  
      Logger logger = Logger.getLogger(OpenMrsService.class)  
      String conceptUrl = "http://localhost:8989/openmrs/ws/rest/v1/concept"  
      String conceptReferenceTermUrl = "http://localhost:8989/openmrs/ws/rest/v1/conceptreferenceterm"  
      String username = "Admin"  
      String password = "Admin123"  
      /**  
       * Following rest api reference   
       * https://wiki.openmrs.org/display/docs/REST+Web+Service+Resources+in+OpenMRS+1.9#RESTWebServiceResourcesinOpenMRS1.9-Concept  
       *   
       */  
      def importSnomedCtConcepts() {  
           System.out.println("Start importSnomedCtConcepts")  
           // File columns (of interest)            
           final int INDEX_ID = 0  
           final int INDEX_ACTIVE = 2  
           final int INDEX_CONCEPT_ID = 4  
           final int INDEX_TYPE_ID = 6  
           final int INDEX_TERM = 7  
           File file = new File ("E:/snomedCT/NEHTA_2068_2015_SNOMEDCT-AU_ReleaseFileBundle_v20150531/SnomedCT_Release_AU1000036_20150531/RF2Release/Full/Terminology/sct2_Description_Full-en-AU_AU1000036_20150531.txt")  
           // Parse each line  
           file.eachLine { String line, Integer i ->                 
                String[] lineParts = line.split("\t", -1)  
                Boolean active = (lineParts[INDEX_ACTIVE] == '1') ? Boolean.TRUE : Boolean.FALSE  
                // Only import if the concept is active  
                if (active) {  
                     System.out.println("line="+line)  
                     String id = lineParts[INDEX_ID]  
                     String conceptId = lineParts[INDEX_CONCEPT_ID]  
                     String typeId = lineParts[INDEX_TYPE_ID]  
                     String term = lineParts[INDEX_TERM]  
                     /** Reference term */  
                     // Check that the reference term doesn't already exist  
                     String conceptRefTermUuid = conceptReferenceTermExists(conceptId)  
                     //System.out.println("conceptRefTermUuid="+conceptRefTermUuid)  
                     // If no existing record could be found, create a new one  
                     if (conceptRefTermUuid == null) {  
                          conceptRefTermUuid = saveConceptReferenceTerm(conceptId, term)  
                     } else {  
                          System.out.println("Existing conceptRefTerm uuid="+conceptRefTermUuid)  
                     }  
                     try {  
                          /** Concept */  
                          // Check that the concept doesn't already exist  
                          String conceptUuid = conceptExists(term)  
                          // If it doesn't exist, create it  
                          if (conceptUuid == null) {  
                               conceptUuid = saveConcept(term)  
                               System.out.println("Saved concept uuid="+conceptUuid)  
                          }  
                          /** Concept mapping */  
                          // Map the concept to snomed reference term   
                          // Check that the mapping doesn't already exist  
                          String conceptMappingUuid = conceptMappingExists(conceptUuid)  
                          if (conceptMappingUuid == null) {     // create a new mapping  
                               conceptMappingUuid = saveConceptMapping(conceptUuid, conceptRefTermUuid)  
                          }  
                     } catch (Exception ex) {  
                          // Workaround: Handle concepts with special character encoding such as, Déjerine-Roussy syndrome   
                          if (ex.message?.contains("DuplicateConceptNameException")) {  
                               // ignore  
                          } else {  
                               // rethrow  
                               throw ex  
                          }  
                     }                      
                }  
           }  
           System.out.println("End importSnomedCtConcepts")  
      }  
      /**  
       * Construct json object for concept reference term       
       * @param code  
       * @param conceptSourceUuid  
       * @return  
       */  
      private JSONObject jsonConceptReferenceTerm(String code, String term, String conceptSourceUuid) {  
           JSONObject jsonConceptReferenceTerm = new JSONObject()  
           jsonConceptReferenceTerm.put("code", code)  
           jsonConceptReferenceTerm.put("name", term)  
           jsonConceptReferenceTerm.put("conceptSource", conceptSourceUuid)            
           return jsonConceptReferenceTerm  
      }  
      /**  
       * Create the concept represented as a JSON object from the given term  
       * @param term  
       * @return  
       */  
      private JSONObject jsonConcept(String term) {  
           // Construct the json object  
           JSONObject jsonConcept = new JSONObject()  
           // Name  
           JSONArray jsonNames = new JSONArray()  
           JSONObject jsonName = new JSONObject()  
           jsonName.put("name", term)            
           jsonName.put("locale", "en")  
           jsonName.put("conceptNameType", "FULLY_SPECIFIED")  
           jsonNames.add(jsonName)            
           jsonConcept.put("names", jsonNames)  
           // Datatype  
           jsonConcept.put("datatype", '8d4a4c94-c2cc-11de-8d13-0010c6dffd0f')  
           // Concept class  
           jsonConcept.put("conceptClass", "8d4918b0-c2cc-11de-8d13-0010c6dffd0f")  
           return jsonConcept  
      }  
      /**  
       * Using the webservices, create the new concept reference term       
       * @param conceptId  
       * @param term  
       * @return  
       */  
      private String saveConceptReferenceTerm(String conceptId, String term) {  
           // the UUID of the newly created reference term  
           String conceptRefTermUuid = null  
           // Create a new concept reference term  
           JSONObject jsonConceptReferenceTerm = jsonConceptReferenceTerm(conceptId, term, '2b3c054a-768a-102f-83f4-12313b04a615')            
           def resp = postJson(conceptReferenceTermUrl, jsonConceptReferenceTerm)  
           return resp.json.uuid  
      }  
      /**  
       * Using web services, save the concept name  
       * @param term  
       * @return  
       */  
      private String saveConceptName(String term, String parentUuid) {  
           // Create the json object  
           JSONObject jsonConceptName = new JSONObject()  
           jsonConceptName.put("name", term)  
           jsonConceptName.put("locale", "en")  
           String uuid  
           String conceptNameUrl = conceptUrl + "/"+parentUuid+"/name"  
           def resp = postJson(conceptNameUrl, jsonConceptName)   
           return resp.json.uuid  
      }  
      /**  
       * Post JSON object using the given url  
       * @param url  
       * @param jsonObj  
       * @return  
       */  
      private Object postJson(String url, JSONObject jsonObj) {  
           System.out.println("Posting: "+jsonObj.toString())  
           RestBuilder rest = new RestBuilder()  
           def resp = rest.post(url) {  
                auth 'Admin', 'Admin123'  
                contentType "application/json;charset=UTF-8"  
                json jsonObj as JSON  
           }  
           //System.out.println("savingConceptName status code="+resp.statusCode)  
           if (resp.statusCode == HttpStatus.CREATED) {     // created  
                return resp  
           } else if (resp.statusCode == HttpStatus.INTERNAL_SERVER_ERROR) {  
                throw new Exception("OpenMRS internal error: "+resp.text)  
           } else if (resp.statusCode == HttpStatus.BAD_REQUEST) {  
                throw new Exception("OpenMRS bad request: "+resp.text)  
           }  
      }  
      /**  
       * Using web services, save the concept to the database  
       * @param term  
       * @return  
       */  
      private String saveConcept(String term) {  
           String uuid = null  
           JSONObject jsonConcept = jsonConcept(term)  
           def resp = postJson(conceptUrl, jsonConcept)  
           return resp.json.uuid  
      }  
      /**  
       * Using web services, save the mapping between the concept and the reference term (mapping to snomed CT)  
       * @param conceptUuid  
       * @param conceptRefTermUuid  
       * @return  
       */  
      private String saveConceptMapping(String conceptUuid, String conceptRefTermUuid) {  
           String conceptMappingUrl = conceptUrl + "/" + conceptUuid + "/mapping"  
           JSONObject jsonMapping = new JSONObject()  
           jsonMapping.put("conceptMapType", '35543629-7d8c-11e1-909d-c80aa9edcf4e')     // SAME-AS  
           jsonMapping.put("conceptReferenceTerm", conceptRefTermUuid)  
           def resp = postJson(conceptMappingUrl, jsonMapping)  
           return resp.json.uuid  
      }  
      /**  
       * Check to see if the given concept code already exists as a reference term  
       * If it does, return the uuid  
       * @param code  
       * @return  
       */  
      private String conceptReferenceTermExists(String conceptCode) {  
           String checkConceptRefTermUrl = conceptReferenceTermUrl + "?q="+conceptCode+"&v=default"  
           RestBuilder rest = new RestBuilder()  
           //rest.restTemplate.setMessageConverters([new StringHttpMessageConverter(Charset.forName("UTF-8"))])  
           def resp = rest.get(checkConceptRefTermUrl) {  
                auth username, password                 
           }  
           //System.out.println("check conceptCode"+conceptCode+": "+resp.text)  
           for (Object conceptRefTerm: resp.json.results) {            
                if (conceptRefTerm.code?.equals(conceptCode)) {  
                     System.out.println("found existing ref term: "+conceptRefTerm.uuid)  
                     return conceptRefTerm.uuid  
                }  
           }       
           return null  
      }  
      /**  
       * Check to see if the given concept term already exists  
       * @param term  
       * @return  
       */  
      private String conceptExists(String term) {  
           String checkConceptUrl = conceptUrl + "?v=custom:(uuid,name)&q="+term  
           System.out.println("conceptUrl: "+checkConceptUrl)  
           // Terms can contains UTF-8 characters  
           //checkConceptUrl = URLEncoder.encode(checkConceptUrl, "UTF-8")  
           //System.out.println("encoded url = "+checkConceptUrl)  
           RestBuilder rest = new RestBuilder()  
           //rest.restTemplate.setMessageConverters([new StringHttpMessageConverter(Charset.forName("UTF-8"))])  
           def resp = rest.get(checkConceptUrl) {  
                auth username, password                 
           }  
           //System.out.println("conceptExists: "+resp.text)  
           for (Object concept: resp.json.results) {  
                if (concept.name?.name?.equals(term)) {  
                     //System.out.println("found existing concept: "+concept.uuid)  
                     return concept.uuid  
                }  
           }  
           return null  
      }  
      /**  
       * Check to see if the concept with the given UUID already has an existing mapping to snomed  
       * @return  
       */  
      private String conceptMappingExists(String conceptUuid) {  
           String conceptMappingUrl = conceptUrl + "/" + conceptUuid + "/mapping?v=custom:(uuid,conceptMapType,conceptReferenceTerm)"  
           RestBuilder rest = new RestBuilder()  
           //rest.restTemplate.setMessageConverters([new StringHttpMessageConverter(Charset.forName("UTF-8"))])  
           def resp = rest.get(conceptMappingUrl) {  
                auth username, password                 
           }  
           //System.out.println("conceptMappingExists: "+resp.text)  
           for (Object conceptMapping: resp.json.results) {  
                // If it is a SNOMED CT mapping, then return the UUID  
                String conceptSource = conceptMapping.conceptReferenceTerm?.conceptSource?.display  
                String conceptMapType = conceptMapping.conceptMapType?.name  
                if (conceptSource?.equals("SNOMED CT") && conceptMapType?.equals("SAME-AS")) {  
                     return conceptMapping.uuid  
                }  
           }  
           return null  
      }  
      /**  
       * Testing locally  
       * @param args  
       */  
      public static void main (String[] args) {  
           OpenMrsService service = new OpenMrsService()  
           service.importSnomedCtConcepts()  
      }  
 }  

Tuesday, June 30, 2015

Names of MongoDB nodes are important for web application connections using connectionString parameter



The names given to the mongoDb nodes are very important. These names are used by the web application to establish connections. If we do rs.status() as a mongo client, we may see the following names highlighted in red:


rs0:PRIMARY> rs.status()
{
        "set" : "rs0",
        "date" : ISODate("2015-07-01T03:29:11.718Z"),
        "myState" : 1,
        "members" : [
                {
                        "_id" : 0,
                        "name" : "mongodb-node-3.novalocal:27017",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 1215292,
                        "optime" : Timestamp(1435130014, 6611),
                        "optimeDate" : ISODate("2015-06-24T07:13:34Z"),
                        "lastHeartbeat" : ISODate("2015-07-01T03:29:11.654Z"),
                        "lastHeartbeatRecv" : ISODate("2015-07-01T03:29:10.310Z"),
                        "pingMs" : 0,
                        "configVersion" : 3
                },
                {
                        "_id" : 1,
                        "name" : "mongodb-node-1:27017",
                        "health" : 0,
                        "state" : 8,
                        "stateStr" : "(not reachable/healthy)",
                        "uptime" : 0,
                        "optime" : Timestamp(0, 0),
                        "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
                        "lastHeartbeat" : ISODate("2015-07-01T03:29:10.406Z"),
                        "lastHeartbeatRecv" : ISODate("2015-07-01T01:37:53.542Z"),
                        "pingMs" : 0,
                        "lastHeartbeatMessage" : "Failed attempt to connect to mongodb-node-1:27017; couldn't connect to server mongodb-node-1:27017 (10.0.0.15), connection attempt failed",
                        "configVersion" : -1
                },
                {
                        "_id" : 2,
                        "name" : "mongodb-node-2:27017",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 1831066,
                        "optime" : Timestamp(1435130014, 6611),
                        "optimeDate" : ISODate("2015-06-24T07:13:34Z"),
                        "electionTime" : Timestamp(1435714575, 1),
                        "electionDate" : ISODate("2015-07-01T01:36:15Z"),
                        "configVersion" : 3,
                        "self" : true
                }
        ],
        "ok" : 1
}

On the application side, we may see a connectionString parameter such as:

connectionString = 'mongodb://mongodb-node-3.novalocal:27017,mongodb-node-1:27017,mongodb-node-2:27017/cpi?replicaSet=rs0&connectTimeoutMS=1000'

However these names don't actually resolve to an IP address. So take it work, I've modified the /etc/hosts file on the web server as follows


10.0.1.x mongodb-node-2
10.0.1.y mongodb-node-1
10.0.1.z mongodb-node-3.novalocal




To test the mapping works, try the following command on the web server:



ping mongodb-node-2


Reference:
http://qnalist.com/questions/5216546/mongotimeoutexception-trying-to-connect-to-a-replica-set