Integrating Chemaxon external and 3rd party tools using IJC

    Example 1: The first example shows a 'datatree level' script written in the Groovy language. This script shows an example of how you can execute an external tool using the current set of data rows selected in IJC. In this example we use the Chemaxon Evaluator command line tool to determine a ring count but we can already do this easily using Chemical terms in IJC so really you should never need to do this, it is a simple example to assist you to access any tools that are not directly available in IJC. You might wish to complete calculations on molecules using other CXN tools or indeed any are using 3rd party command line tools whose output you wish to integrate into your schema. In this example a single calculated value is generated into the SDF file and is named "CALC". Prior to running the call a field was added to the entity to accept the pending update statement. This field can be of type text or integer and the script can be edited to handle either. Clearly modification of this approach with different call outs must consider carefully the results returned by external tools however it is easy to package up all the return values and add them to your entity. This script has three conceptual parts which are executed in succession:

    • Export of the selected rows as an SDF file for input into your process.

    • Building an external command to execute, that uses the above as SDF input.

    • Integration of the generated results SDF back into your entity in the correct row using the CD_ID.

    
    import chemaxon.formats.MolExporter
    import chemaxon.formats.MolImporter
    import chemaxon.struc.Molecule
    import com.im.commons.progress.DFEnvironmentRO
    // dbutler@chemaxon.com
    // Sep 2012
    // Use of https://docs.chemaxon.com/display/chemicalterms/Chemical+Terms+Evaluator
    
    def evaluate = "C:/Program Files/Chemaxon/JChem/bin/evaluate.bat -e"
    def expression = """\"ringCount()\""""
    def inputSDF = "C:/SDF/test.sdf"
    def options = " -S -o "
    def outputSDF = inputSDF.replace(".sdf","Results.sdf") //imported as results
    def command = evaluate + " " + expression + options + " " + outputSDF + " " + inputSDF
    // INSERT a call to export an SDF from IJC result set to suitable directory
    // Must export CD_ID as handle
    def ety = dataTree.rootVertex.entity
    def edp = ety.schema.dataProvider.getEntityDataProvider(ety)
    def structureFld = ety.fields.items.find { it.name == 'Structure' }
    def cdidFld = ety.fields.items.find { it.name == 'CdId' }
    def calcFld = ety.fields.items.find { it.name == 'CALC' }
    //Create the exporter, the file, and define the file type as SDF
    def exporter = new MolExporter(inputSDF, 'sdf')
    try {
        def rs = ety.schema.dataProvider.getDefaultResultSet(dataTree, false,DFEnvironmentRO.DEV_NULL)
        def rootVS = rs.getVertexState(dataTree.rootVertex)
        // Get the selected IDs
        List ids = rootVS.selectedRowsIds
        ids.each { id ->
            // Define the data fetching method from the root verstexstate
            def data = rootVS.getData([id], DFEnvironmentRO.DEV_NULL)
            def row = data[id]
            // Get the molecule from the table, and the affiliated properties
            def mol = row[structureFld.id]
            String cdid = row[cdidFld.id]
            // Clone the molecule to prevent modifying the original
            def expMol = mol.native.cloneMolecule()
            expMol.clearProperties()
            expMol.setProperty("cdid", cdid)
            // Set the properties for the molecule
            // Write the molecule to the file
            exporter.write(expMol)
        }
    } finally {
        //Important! Be sure to flush and close the exporter so that the file is closed.
        exporter.flush()
        exporter.close()
    }
    println command
    def proc = command.execute()
    proc.waitFor()
    println "return code: ${ proc.exitValue()}"
    println "stderr: ${proc.err.text}"
    println "stdout: ${proc.in.text}"
    // UPDATE using CD_ID handle?
    edp.lockable.withLock('Updating'){ envRW ->
        MolImporter importer = new MolImporter(outputSDF)
        importer.grabbingEnabled = true
        Molecule mols = importer.createMol()
        while (importer.read(mols))
        {
            def String molStr = importer.grabbedMoleculeString
            def molcdid = mols.getProperty("cdid")
            println molcdid
            def molcalc = mols.getProperty("calc")
            println molcalc
            def vals = [:]
            vals[calcFld.id] = molcalc
            //if you define CALC as integer may need to switch to below
            //vals[calcFld.id] = molcalc.toInteger()
            def ud = DFUpdateDescription.create(ety,molcdid.toInteger(),vals)
            def submitList = Collections.singletonList(ud)
            edp.update(submitList, DFUndoConfig.OFF, envRW)
        }
    }

    Example 2: The second example shows a 'datatree level' script written in the Groovy language. This script shows an example of how you can execute an external tool using the current set of data rows selected in IJC. In this example we use the Chemaxon CXCalc command line tool to generate sets of conformers which is something not currently possible in IJC. The output of the process, which is a 3D CTAB and the associated cd_id for the input record can be accessed and further processed using a virtual screening method of your choice. Clearly the script can be appended with further syntax to implement CXN Screen tools, a third party screening tool or indeed your own grooved up scripted virtual screening method that requires some 3D coordinates as input. (Clearly you also need to define your query or reference molecule as well in the script in order to complete a comparison). This script has two conceptual parts (you must add the third!) which are executed in succession:

    • Export of the selected rows to a 2D SDF file for input into your process (we presume the source table is 2D).

    • Building an external command to execute the above SDF as input and generates accessible 3D coordinates.

    • You can now append your own virtual screening methods and get predicting new drugs!

    
    import chemaxon.formats.MolExporter
    import chemaxon.formats.MolImporter
    import chemaxon.struc.Molecule
    import com.im.commons.progress.DFEnvironmentRO
    
    // dbutler@chemaxon.com
    // Sep 2012
    //https://docs.chemaxon.com/display/CALCPLUGS/cxcalc+command+line+tool
    
    def cxcalc = "C:/Program Files/Chemaxon/JChem/bin/cxcalc.bat"
    def inputSDF = "C:/SDF/test.sdf"
    //def options = " -m 100 " // generate N confs
    def options = " leconformer " // generate single low energy 
    def command = cxcalc + options + inputSDF
    // INSERT a call to export an SDF from IJC result set to suitable directory
    // Must export CD_ID as handle
    def ety = dataTree.rootVertex.entity
    def edp = ety.schema.dataProvider.getEntityDataProvider(ety)
    def structureFld = ety.fields.items.find { it.name == 'Structure' }
    def cdidFld = ety.fields.items.find { it.name == 'CdId' }
    def exporter = new MolExporter(inputSDF, 'sdf')
    
    try {
        def rs = ety.schema.dataProvider.getDefaultResultSet(dataTree, false,DFEnvironmentRO.DEV_NULL)
        def rootVS = rs.getVertexState(dataTree.rootVertex)
        // Get the selected IDs
        List ids = rootVS.selectedRowsIds
        ids.each { id ->
            // Define the data fetching method from the root verstexstate
            def data = rootVS.getData([id], DFEnvironmentRO.DEV_NULL)
            def row = data[id]
            // Get the molecule from the table, and the affiliated properties
            def mol = row[structureFld.id]
            String cdid = row[cdidFld.id]
            // Clone the molecule to prevent modifying the original
            def expMol = mol.native.cloneMolecule()
            expMol.clearProperties()
            expMol.setProperty("cdid", cdid)
            // Set the properties for the molecule
            // Write the molecule to the file
            exporter.write(expMol)
        }
    } finally {
        //Important! Be sure to flush and close the exporter so that the file is closed.
        exporter.flush()
        exporter.close()
    }
    
    println command
    def initialSize = 4096
    def outStream = new ByteArrayOutputStream(initialSize)
    def errStream = new ByteArrayOutputStream(initialSize)
    def proc = command.execute()    
    proc.waitForProcessOutput(outStream,errStream)
    println "return code: ${ proc.exitValue()}"
    println 'out:\n' + outStream
    println 'err:\n' + errStream
    // You might want to do somthing with this like extract 3D ctab and use in virtual screening !?