Skip to content

Commit

Permalink
Merge pull request #200 from Steinbeck-Lab/dev-kohulan
Browse files Browse the repository at this point in the history
feat: add depict router #194 and #199
  • Loading branch information
CS76 authored Jun 12, 2023
2 parents ff23bda + f2fac03 commit 76b4d39
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 89 deletions.
5 changes: 3 additions & 2 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from fastapi.responses import RedirectResponse
from fastapi_versioning import VersionedFastAPI

from .routers import chem, converters, decimer
from .routers import chem, converters, depict, ocsr
from fastapi.middleware.cors import CORSMiddleware

from prometheus_fastapi_instrumentator import Instrumentator
Expand Down Expand Up @@ -34,7 +34,8 @@

app.include_router(chem.router)
app.include_router(converters.router)
app.include_router(decimer.router)
app.include_router(depict.router)
app.include_router(ocsr.router)

app = VersionedFastAPI(
app,
Expand Down
File renamed without changes.
17 changes: 17 additions & 0 deletions app/modules/toolkits/rdkitmodules.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,3 +212,20 @@ def get2Dmol(smiles: str):
return molfile
else:
return "Error reading SMILES string, check again."


def getRDKitCXSMILES(smiles: str):
"""This function takes an input as a SMILES string and
returns a CXSMILES with coordinates.
Args (str): SMILES string.
Returns (str): CXSMILES with coordinates.
"""
if any(char.isspace() for char in smiles):
smiles = smiles.replace(" ", "+")
mol = Chem.MolFromSmiles(smiles)

if mol:
AllChem.Compute2DCoords(mol)
return Chem.MolToCXSmiles(mol)
else:
return "Error reading SMILES string, check again."
80 changes: 13 additions & 67 deletions app/routers/chem.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
from fastapi import Body, Request, APIRouter
from fastapi import Body, APIRouter
from typing import Optional
from typing_extensions import Annotated
from rdkit import Chem
from rdkit.Chem.EnumerateStereoisomers import (
EnumerateStereoisomers,
)
from chembl_structure_pipeline import standardizer, checker
from fastapi.responses import Response, HTMLResponse, JSONResponse
from fastapi.responses import Response, JSONResponse
from app.modules.npscorer import getNPScore
from app.modules.classyfire import classify, result
from app.modules.toolkits.cdkmodules import (
getTanimotoSimilarityCDK,
getCDKHOSECodes,
)
from app.modules.depict import getRDKitDepiction, getCDKDepiction
from app.modules.toolkits.rdkitmodules import (
get3Dconformers,
getTanimotoSimilarityRDKit,
getRDKitHOSECodes,
)
Expand Down Expand Up @@ -148,24 +146,6 @@ async def ClassyFire_result(id: str):
return data


@router.get("/rdkit3d")
async def RDKit3D_Mol(smiles: str):
"""
Generate 3D Coordinates using RDKit and return the mol block.
- **SMILES**: required (query)
"""
if smiles:
mol = Chem.MolFromSmiles(smiles)
if mol:
return Response(
content=get3Dconformers(smiles).replace("$$$$\n", ""),
media_type="text/plain",
)
else:
return "Error reading SMILES string, check again."


@router.get("/tanimoto")
async def Tanimoto_Similarity(smiles: str, toolkit: Optional[str] = "cdk"):
"""
Expand Down Expand Up @@ -194,38 +174,6 @@ async def Tanimoto_Similarity(smiles: str, toolkit: Optional[str] = "cdk"):
return 'Please give a SMILES pair with "," separated. (Example: api.naturalproducts.net/chem/tanimoto?smiles=CN1C=NC2=C1C(=O)N(C(=O)N2C)C,CN1C=NC2=C1C(=O)NC(=O)N2C)'


@router.get("/depict")
async def Depict2D_molecule(
smiles: str,
generator: Optional[str] = "cdksdg",
width: Optional[int] = 512,
height: Optional[int] = 512,
rotate: Optional[int] = 0,
CIP: Optional[bool] = False,
unicolor: Optional[bool] = False,
):
"""
Generate 2D Depictions using CDK or RDKit using given parameters.
- **SMILES**: required (query)
- **generator**: optional (defaults: cdk)
- **width**: optional (defaults: 512)
- **height**: optional (defaults: 512)
- **rotate**: optional (defaults: 0)
"""
if generator:
if generator == "cdksdg":
return Response(
content=getCDKDepiction(smiles, [width, height], rotate, CIP, unicolor),
media_type="image/svg+xml",
)
else:
return Response(
content=getRDKitDepiction(smiles, [width, height], rotate),
media_type="image/svg+xml",
)


@router.get("/checkerrors")
async def Check_Errors(smiles: str, fix: Optional[bool] = False):
"""
Expand Down Expand Up @@ -272,27 +220,25 @@ async def Check_Errors(smiles: str, fix: Optional[bool] = False):
return "Error reading SMILES string, check again."


@router.get("/depict3D", response_class=HTMLResponse)
async def Depict3D_Molecule(
request: Request,
@router.get("/hosecode")
async def HOSE_Codes(
smiles: str,
spheres: int,
toolkit: Optional[str] = "cdk",
ringsize: Optional[bool] = False,
):
"""
Generate 3D Depictions using RDKit.
Generates HOSE Codes using CDK/RDKit.
- **SMILES**: required (query)
- **spheres**: required (query)
- **toolkit**: Optional (default:CDK)
- **ringsize**: Optional (default:False)
"""
if smiles:
content = {"request": request, "molecule": get3Dconformers(smiles)}
return templates.TemplateResponse("mol.html", content)


@router.get("/hosecode")
async def HOSE_Codes(framework: str, smiles: str, spheres: int, ringsize: bool = False):
if smiles:
if framework == "cdk":
if toolkit == "cdk":
return await getCDKHOSECodes(smiles, spheres, ringsize)
elif framework == "rdkit":
elif toolkit == "rdkit":
return await getRDKitHOSECodes(smiles, spheres)
else:
return "Error reading SMILES string, check again."
Expand Down
40 changes: 24 additions & 16 deletions app/routers/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
getCanonSMILES,
getInChI,
)
from app.modules.toolkits.rdkitmodules import get3Dconformers, get2Dmol
from app.modules.toolkits.rdkitmodules import (
get3Dconformers,
get2Dmol,
getRDKitCXSMILES,
)
from app.modules.toolkits.openbabelmodules import (
getOBMol,
getOBCanonicalSMILES,
Expand Down Expand Up @@ -124,12 +128,12 @@ async def SMILES_to_InChI(smiles: str, generator: Optional[str] = "cdk"):
if generator:
if generator == "cdk":
return str(getInChI(smiles))
elif generator == "rdkit":
mol = Chem.MolFromSmiles(smiles)
if mol:
return Chem.inchi.MolToInchi(mol)
elif generator == "openbabel":
return getOBInChI(smiles)
elif generator == "rdkit":
mol = Chem.MolFromSmiles(smiles)
if mol:
return Chem.inchi.MolToInchi(mol)
elif generator == "openbabel":
return getOBInChI(smiles)
else:
return "Error reading SMILES string check again."
else:
Expand All @@ -149,20 +153,20 @@ async def SMILES_to_InChIKey(smiles: str, generator: Optional[str] = "cdk"):
if generator:
if generator == "cdk":
return str(getInChI(smiles, InChIKey=True))
elif generator == "rdkit":
mol = Chem.MolFromSmiles(smiles)
if mol:
return Chem.inchi.MolToInchiKey(mol)
elif generator == "openbabel":
return getOBInChI(smiles, InChIKey=True)
elif generator == "rdkit":
mol = Chem.MolFromSmiles(smiles)
if mol:
return Chem.inchi.MolToInchiKey(mol)
elif generator == "openbabel":
return getOBInChI(smiles, InChIKey=True)
else:
return "Error reading SMILES string check again."
else:
return "Error reading SMILES string check again."


@router.get("/cxsmiles")
async def SMILES_to_CXSMILES(smiles: str):
async def SMILES_to_CXSMILES(smiles: str, generator: Optional[str] = "cdk"):
"""
Convert SMILES to CXSMILES:
Expand All @@ -171,8 +175,12 @@ async def SMILES_to_CXSMILES(smiles: str):
if any(char.isspace() for char in smiles):
smiles = smiles.replace(" ", "+")
if smiles:
cxsmiles = getCXSMILES(smiles)
return cxsmiles
if generator:
if generator == "cdk":
cxsmiles = getCXSMILES(smiles)
return cxsmiles
else:
return getRDKitCXSMILES(smiles)
else:
return "Error reading SMILES string check again."

Expand Down
67 changes: 67 additions & 0 deletions app/routers/depict.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from fastapi import Request, APIRouter
from typing import Optional
from fastapi.responses import Response, HTMLResponse
from app.modules.depiction import getRDKitDepiction, getCDKDepiction
from app.modules.toolkits.rdkitmodules import get3Dconformers
from fastapi.templating import Jinja2Templates

templates = Jinja2Templates(directory="app/templates")

router = APIRouter(
prefix="/depict",
tags=["depict"],
dependencies=[],
responses={404: {"description": "Not found"}},
)


@router.get("/")
async def depict_index():
return {"module": "depict", "message": "Successful", "status": 200}


@router.get("/2D")
async def Depict2D_molecule(
smiles: str,
generator: Optional[str] = "cdksdg",
width: Optional[int] = 512,
height: Optional[int] = 512,
rotate: Optional[int] = 0,
CIP: Optional[bool] = False,
unicolor: Optional[bool] = False,
):
"""
Generate 2D Depictions using CDK or RDKit using given parameters.
- **SMILES**: required (query)
- **generator**: optional (defaults: cdk)
- **width**: optional (defaults: 512)
- **height**: optional (defaults: 512)
- **rotate**: optional (defaults: 0)
"""
if generator:
if generator == "cdksdg":
return Response(
content=getCDKDepiction(smiles, [width, height], rotate, CIP, unicolor),
media_type="image/svg+xml",
)
else:
return Response(
content=getRDKitDepiction(smiles, [width, height], rotate),
media_type="image/svg+xml",
)


@router.get("/3D", response_class=HTMLResponse)
async def Depict3D_Molecule(
request: Request,
smiles: str,
):
"""
Generate 3D Depictions using RDKit.
- **SMILES**: required (query)
"""
if smiles:
content = {"request": request, "molecule": get3Dconformers(smiles)}
return templates.TemplateResponse("mol.html", content)
8 changes: 4 additions & 4 deletions app/routers/decimer.py → app/routers/ocsr.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@
from app.modules.decimermodules import getPredictedSegments

router = APIRouter(
prefix="/decimer",
tags=["decimer"],
prefix="/ocsr",
tags=["ocsr"],
dependencies=[],
responses={404: {"description": "Not found"}},
)


@router.get("/")
async def DECIMER_Index():
return {"module": "decimer", "message": "Successful", "status": 200}
async def ocsr_index():
return {"module": "ocsr", "message": "Successful", "status": 200}


@router.post("/process")
Expand Down

0 comments on commit 76b4d39

Please sign in to comment.