Creating concepts using the Web service api

Platform Version: 1.11.2 OpenMRS Version: 2.2 Question:

Has anybody been able to successfully create concepts using the web service API?

Based on the reference here: REST Web Service Resources in OpenMRS 1.9#Concept To create a concepts requires the ‘names’ property to be populated. I believe names will accept UUIDs from the ConceptName table. So this led me to believe, that I first need to create ConceptName before creating a Concept. Based on the reference documentation to create a concept name I would use the following: POST /ws/rest/v1/concept/{parentUuid}/name

The problem is that concept name requires the parent UUID (which is the UUID of the concept), but in order to create the concept, I first need to create the concep name. So I have a chicken/egg problem. Which comes first?

Any help appreciated…thanks

Isn’t it amazing when you can answer your own question?

I’ve figured it out. To send a concept in JSON format you can use the following:

 {"conceptClass":"8d4918b0-c2cc-11de-8d13-0010c6dffd0f","names":[{"conceptNameType":"FULLY_SPECIFIED","locale":"en","name":"Brown-Kelly-Paterson syndrome"}],"datatype":"8d4a4c94-c2cc-11de-8d13-0010c6dffd0f"}

Here is some sample code i’ve written:

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

Hi phillycheese,

What is the JSON format for mapping while creating the concept ?

I think it’s something like this:

...
mappings: [
  {
    "conceptReferenceTerm": "83f931b9-94d2-3da0-a7f9-8c41b96842a7",
    "conceptMapType": "35543629-7d8c-11e1-909d-c80aa9edcf4e"
  }
],
...

You would first need to have created a concept reference term.

The OpenMRS concept dictionary doesn’t really work like this (i.e. searching for a set doesn’t show you the set members).

Also, typically you shouldn’t be loading ICD codes into OpenMRS directly as concepts; ICD should be “reference terms” in OpenMRS, that are mapped to the concepts. Have you considered using the CIEL concept dictionary?