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()
}
}