From ae755fbb06c3dda0740e9bb9323fbdfe3f2788a4 Mon Sep 17 00:00:00 2001 From: Johannes Ranke Date: Thu, 18 Nov 2021 23:03:16 +0100 Subject: Use reticulate instead of PythonInR, alanwood has moved Because the pesticide compendium at alanwood.net has moved to bcpc.org, we need to depend on a development branch of webchem at the moment. --- .travis.yml | 1 + ChangeLog | 9 ++++ DESCRIPTION | 10 ++--- R/chent.R | 136 +++++++++++++++++++++++++++-------------------------------- R/zzz.R | 13 ++++++ man/chent.Rd | 20 +++++++-- man/pai.Rd | 13 ++++-- 7 files changed, 118 insertions(+), 84 deletions(-) create mode 100644 R/zzz.R diff --git a/.travis.yml b/.travis.yml index 712829e..841fe6d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,5 +5,6 @@ r: - devel github_packages: - r-lib/covr + - jranke/webchem@bcpc after_success: - Rscript -e 'covr::codecov()' diff --git a/ChangeLog b/ChangeLog index 011b28d..69b201d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +commit 898d598bcbb022ac767e0105e868798130f2c74c +Author: Johannes Ranke +Date: 2020-10-13 15:10:03 +0200 + + Adapt to changes in webchem + + Unfortunately, tests only pass sometimes, it seems PubChem is not always + responding + commit 6937e41d317b953d8246203814500166dbe89470 Author: Johannes Ranke Date: 2020-10-13 11:26:03 +0200 diff --git a/DESCRIPTION b/DESCRIPTION index a00e9a4..041999c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,13 +1,13 @@ Package: chents Type: Package Title: Chemical Entities as R Objects -Version: 0.2-9 -Date: 2020-10-13 +Version: 0.3.1 +Date: 2021-11-18 Authors@R: c(person("Johannes", "Ranke", role = c("aut", "cre", "cph"), email = "jranke@uni-bremen.de")) Description: Utilities for dealing with chemical entities and associated data as R objects. If Python and RDKit (> 2015.03) are installed and - configured for use with 'PythonInR', some basic chemoinformatics functions + configured for use with 'reticulate', some basic chemoinformatics functions like the calculation of molecular weight and plotting of chemical structures in R graphics are available. For plotting, you need grConvert (https://sjp.co.nz/projects/grconvert) which is not on CRAN. @@ -20,7 +20,7 @@ Imports: Suggests: knitr, testthat, - PythonInR, + reticulate, covr, devEMF License: GPL @@ -28,4 +28,4 @@ LazyLoad: yes LazyData: yes Encoding: UTF-8 URL: https://github.com/jranke/chents -RoxygenNote: 7.1.1.9000 +RoxygenNote: 7.1.2 diff --git a/R/chent.R b/R/chent.R index b2c2e4a..d988e97 100644 --- a/R/chent.R +++ b/R/chent.R @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2019 Johannes Ranke +# Copyright (C) 2016-2021 Johannes Ranke # Contact: jranke@uni-bremen.de # This file is part of the R package chents @@ -19,7 +19,7 @@ #' #' The class is initialised with an identifier. Chemical information is retrieved from #' the internet. Additionally, it can be generated using RDKit if RDKit and its -#' python bindings are installed and configured for use with PythonInR. +#' python bindings are installed. #' #' @export #' @format An \code{\link{R6Class}} generator object @@ -33,8 +33,8 @@ #' @field smiles SMILES code, with attribute 'source' #' @field mw Molecular weight, with attribute 'source' #' @field pubchem List of information retreived from PubChem -#' @field rdkit List of information obtained with RDKit, if installed and -#' configured for use with PythonInR +#' @field rdkit List of information obtained with RDKit +#' @field mol object #' @field svg SVG code #' @field Picture Graph as a \code{\link{picture}} object obtained using grImport #' @field Pict_font_size Font size as extracted from the intermediate PostScript file @@ -47,6 +47,18 @@ #' @field soil_sorption Dataframe of soil sorption data #' @field PUF Plant uptake factor #' @keywords data +#' @examples +#' oct <- chent$new("1-octanol", smiles = "CCCCCCCCO") +#' print(oct) +#' if (!is.null(oct$Picture)) { +#' plot(oct) +#' } +#' +#' caffeine <- chent$new("caffeine") +#' print(caffeine) +#' if (!is.null(caffeine$Picture)) { +#' plot(caffeine) +#' } chent <- R6Class("chent", public <- list( @@ -56,6 +68,7 @@ chent <- R6Class("chent", mw = NULL, pubchem = NULL, rdkit = NULL, + mol = NULL, svg = NULL, Picture = NULL, Pict_font_size = NULL, @@ -86,7 +99,7 @@ chent <- R6Class("chent", } if (rdkit) { - if(rdkit_available()) { + if(rdkit_available) { if (is.null(self$smiles)) { message("RDKit would need a SMILES code") } else { @@ -98,7 +111,7 @@ chent <- R6Class("chent", attr(self$mw, "source") <- "rdkit" } } else { - message("RDKit is not available via PythonInR") + message("RDKit is not available") } } @@ -158,13 +171,12 @@ chent <- R6Class("chent", } }, get_rdkit = function(template = NULL) { - if(!rdkit_available()) { - stop("RDKit is not available via PythonInR") + if(!rdkit_available) { + stop("RDKit is not available") } self$rdkit <- list() - PythonInR::pyImport("Descriptors", from = "rdkit.Chem") - PythonInR::pyExec(paste0("mol = Chem.MolFromSmiles('", self$smiles[1], "')")) - self$rdkit$mw <- PythonInR::pyExecg("mw = Descriptors.MolWt(mol)", "mw") + self$mol <- rdkit_module$Chem$MolFromSmiles(self$smiles[1]) + self$rdkit$mw <- rdkit_module$Chem$Descriptors$MolWt(self$mol) if (!is.null(self$mw)) { if (round(self$rdkit$mw, 1) != round(self$mw, 1)) { message("RDKit mw is ", self$rdkit$mw) @@ -173,20 +185,16 @@ chent <- R6Class("chent", } # Create an SVG representation - PythonInR::pyImport("Draw", from = "rdkit.Chem") - PythonInR::pyImport("rdMolDraw2D", from = "rdkit.Chem.Draw") - PythonInR::pyImport("rdDepictor", from = "rdkit.Chem") - PythonInR::pyExec("rdDepictor.Compute2DCoords(mol)") + rdkit_module$Chem$rdDepictor$Compute2DCoords(self$mol) if (!is.null(template)) { - PythonInR::pyImport("AllChem", from = "rdkit.Chem") - PythonInR::pyExec(paste0("template = Chem.MolFromSmiles('", template, "')")) - PythonInR::pyExec("AllChem.Compute2DCoords(template)") - PythonInR::pyExec("AllChem.GenerateDepictionMatching2DStructure(mol, template)") + rdkit_template <- rdkit_module$Chem$MolFromSmiles(template) + rdkit_module$Chem$rdDepictor$Compute2DCoords(template) + rdkit$Chem$AllChem$GenerateDepictionMatching2DStructure(self$mol, template) } - PythonInR::pyExec("d2d = rdMolDraw2D.MolDraw2DSVG(400,500)") - PythonInR::pyExec("d2d.DrawMolecule(mol)") - PythonInR::pyExec("d2d.FinishDrawing()") - self$svg <- PythonInR::pyGet("d2d.GetDrawingText()") + d2d <- rdkit_module$Chem$Draw$rdMolDraw2D$MolDraw2DSVG(400L, 400L) + d2d$DrawMolecule(self$mol) + d2d$FinishDrawing() + self$svg <- d2d$GetDrawingText() svgfile <- tempfile(fileext = ".svg") psfile <- tempfile(fileext = ".ps") writeLines(self$svg, svgfile) @@ -446,19 +454,14 @@ print.chent = function(x, ...) { draw_svg.chent = function(x, width = 300, height = 150, filename = paste0(names(x$identifier), ".svg"), subdir = "svg") { - if (!PythonInR::pyIsConnected()) { - PythonInR::pyConnect() - } - try_rdkit <- try(PythonInR::pyImport("Chem", from = "rdkit")) - if (inherits(try_rdkit, "try-error")) { - message("Could not import RDKit in Python session") + if (!rdkit_available) { + stop("RDkit is not available via reticulate") } else { if (!dir.exists(subdir)) dir.create(subdir) - PythonInR::pyExec(paste0("mol = Chem.MolFromSmiles('", x$smiles, "')")) - PythonInR::pyImport("Draw", from = "rdkit.Chem") - cmd <- paste0("Draw.MolToFile(mol, '", file.path(subdir, filename), - "', size = (", width, ", ", height, "))") - PythonInR::pyExec(cmd) + mol <- rdkit_module$Chem$MolFromSmiles(x$smiles) + + rdkit_module$Chem$Draw$MolToFile(mol, file.path(subdir, filename), + size = c(as.integer(width), as.integer(height))) } } @@ -482,19 +485,25 @@ plot.chent = function(x, ...) { #' @importFrom R6 R6Class #' @export #' @format An \code{\link{R6Class}} generator object -#' @field iso ISO common name according to ISO 1750 as retreived from www.alanwood.net/pesticides -#' @field alanwood List of information retreived from www.alanwood.net/pesticides +#' @field iso ISO common name according to ISO 1750 as retreived from pesticidecompendium.bcpc.org +#' @field bcpc List of information retrieved from pesticidecompendium.bcpc.org #' @keywords data +#' @examples +#' atr <- pai$new("atrazine") +#' print(atr) +#' if (!is.null(atr$Picture)) { +#' plot(atr) +#' } pai <- R6Class("pai", inherit = chent, public <- list( iso = NULL, - alanwood = NULL, + bcpc = NULL, initialize = function(iso, identifier = iso, smiles = NULL, smiles_source = 'user', inchikey = NULL, inchikey_source = 'user', - alanwood = TRUE, + bcpc = TRUE, pubchem = TRUE, pubchem_from = 'auto', rdkit = TRUE, template = NULL, chyaml = TRUE) @@ -505,28 +514,28 @@ pai <- R6Class("pai", attr(self$inchikey, "source") <- "user" } - if (!missing(iso) & alanwood) { - message("alanwood.net:") - aw_result = webchem::aw_query(identifier, from = "name") + if (!missing(iso) & bcpc) { + message("BCPC:") + bcpc_result = webchem::bcpc_query(identifier, from = "name") # Use first element of list, as we passed a query of length one - if (is.na(aw_result[[1]][1])) { - message("Common name ", identifier, " is not known at www.alanwood.net, trying PubChem") + if (is.na(bcpc_result[[1]][1])) { + message("Common name ", identifier, " is not known at the BCPC compendium, trying PubChem") } else { - self$alanwood = aw_result[[1]] - self$iso = self$alanwood$cname - attr(self$iso, "source") <- "alanwood" - attr(self$iso, "status") <- self$alanwood$status - aw_ik = self$alanwood$inchikey - if (length(aw_ik) == 1 && nchar(aw_ik) == 27 && !is.na(aw_ik)) { + self$bcpc = bcpc_result[[1]] + self$iso = self$bcpc$cname + attr(self$iso, "source") <- "bcpc" + attr(self$iso, "status") <- self$bcpc$status + bcpc_ik = self$bcpc$inchikey + if (length(bcpc_ik) == 1 && nchar(bcpc_ik) == 27 && !is.na(bcpc_ik)) { if (is.null(self$inchikey)) { - self$inchikey = self$alanwood$inchikey - attr(self$inchikey, "source") <- "alanwood" + self$inchikey = self$bcpc$inchikey + attr(self$inchikey, "source") <- "bcpc" } else { - if (aw_ik == self$inchikey) { - attr(self$inchikey, "source") = c(attr(self$inchikey, "source"), "alanwood") + if (bcpc_ik == self$inchikey) { + attr(self$inchikey, "source") = c(attr(self$inchikey, "source"), "bcpc") } else { - warning("InChIKey ", self$inchikey, " differs from ", aw_ik, " obtained from alanwood.net") + warning("InChIKey ", self$inchikey, " differs from ", bcpc_ik, " obtained from bcpc.org") } } } @@ -608,23 +617,4 @@ pp <- R6Class("pp", ) ) -rdkit_available <- function() -{ - if(requireNamespace("PythonInR", quietly = TRUE)) { - if (!PythonInR::pyIsConnected()) { - PythonInR::pyConnect() - } - sink(tempfile()) - try_rdkit <- try(PythonInR::pyImport("Chem", from = "rdkit"), - silent = TRUE) - sink() - if (inherits(try_rdkit, "try-error")) { - return(FALSE) - } else { - return(TRUE) - } - } else { - return(FALSE) - } -} # vim: set ts=2 sw=2 expandtab: diff --git a/R/zzz.R b/R/zzz.R new file mode 100644 index 0000000..19ca10d --- /dev/null +++ b/R/zzz.R @@ -0,0 +1,13 @@ +.onLoad = function(libname, pkgname) { + rdkit_available <- FALSE + if(requireNamespace("reticulate", quietly = TRUE)) { + rdkit_module <- try(reticulate::import("rdkit")) + if (!inherits(rdkit_module, "try-error")) { + rdkit_available <- TRUE + } + } + assign('rdkit_available', rdkit_available, envir = topenv()) + if (rdkit_available) { + assign('rdkit_module', rdkit_module, envir = topenv()) + } +} diff --git a/man/chent.Rd b/man/chent.Rd index a1a5fe8..d94452e 100644 --- a/man/chent.Rd +++ b/man/chent.Rd @@ -9,7 +9,20 @@ An \code{\link{R6Class}} generator object \description{ The class is initialised with an identifier. Chemical information is retrieved from the internet. Additionally, it can be generated using RDKit if RDKit and its -python bindings are installed and configured for use with PythonInR. +python bindings are installed. +} +\examples{ +oct <- chent$new("1-octanol", smiles = "CCCCCCCCO") +print(oct) +if (!is.null(oct$Picture)) { + plot(oct) +} + +caffeine <- chent$new("caffeine") +print(caffeine) +if (!is.null(caffeine$Picture)) { + plot(caffeine) +} } \keyword{data} \section{Public fields}{ @@ -25,8 +38,9 @@ python bindings are installed and configured for use with PythonInR. \item{\code{pubchem}}{List of information retreived from PubChem} -\item{\code{rdkit}}{List of information obtained with RDKit, if installed and -configured for use with PythonInR} +\item{\code{rdkit}}{List of information obtained with RDKit} + +\item{\code{mol}}{ object} \item{\code{svg}}{SVG code} diff --git a/man/pai.Rd b/man/pai.Rd index 6a243ab..c27a8f5 100644 --- a/man/pai.Rd +++ b/man/pai.Rd @@ -11,6 +11,13 @@ An \code{\link{R6Class}} generator object The class is initialised with an identifier which is generally an ISO common name. Additional chemical information is retrieved from the internet if available. } +\examples{ +atr <- pai$new("atrazine") +print(atr) +if (!is.null(atr$Picture)) { + plot(atr) +} +} \keyword{data} \section{Super class}{ \code{\link[chents:chent]{chents::chent}} -> \code{pai} @@ -18,9 +25,9 @@ Additional chemical information is retrieved from the internet if available. \section{Public fields}{ \if{html}{\out{
}} \describe{ -\item{\code{iso}}{ISO common name according to ISO 1750 as retreived from www.alanwood.net/pesticides} +\item{\code{iso}}{ISO common name according to ISO 1750 as retreived from pesticidecompendium.bcpc.org} -\item{\code{alanwood}}{List of information retreived from www.alanwood.net/pesticides} +\item{\code{bcpc}}{List of information retrieved from pesticidecompendium.bcpc.org} } \if{html}{\out{
}} } @@ -64,7 +71,7 @@ Additional chemical information is retrieved from the internet if available. smiles_source = "user", inchikey = NULL, inchikey_source = "user", - alanwood = TRUE, + bcpc = TRUE, pubchem = TRUE, pubchem_from = "auto", rdkit = TRUE, -- cgit v1.2.1