From c3433f94e2e2ee570de59d8713a5d8646ae9dccf Mon Sep 17 00:00:00 2001 From: NishaSharma14 Date: Fri, 24 Mar 2023 10:31:17 +0100 Subject: [PATCH 1/5] fix: docker build command in prod --- .github/workflows/prod-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prod-build.yml b/.github/workflows/prod-build.yml index 2e8a5b8..e7d59c6 100644 --- a/.github/workflows/prod-build.yml +++ b/.github/workflows/prod-build.yml @@ -77,7 +77,7 @@ jobs: - name: Build docker image run: |- docker build --build-arg RELEASE_VERSION=${{ steps.fetch-latest-release.outputs.tag_name }} --tag "europe-west3-docker.pkg.dev/$PROJECT_ID/$REPOSITORY_NAME/$APP_IMAGE:latest" . - . + # Push the Docker image to Google Container Registry - name: Publish image to Google Artifact Registry run: |- From d4679cc5b4fd308db53735d725b5baf1e762da3e Mon Sep 17 00:00:00 2001 From: NishaSharma14 Date: Fri, 24 Mar 2023 10:45:51 +0100 Subject: [PATCH 2/5] fix: move test job before creating a release. add decsription to workflows. fixes #88 --- .github/workflows/dev-build.yml | 11 ++++++++ .github/workflows/prod-build.yml | 41 +++++++--------------------- .github/workflows/release-please.yml | 40 +++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 31 deletions(-) diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml index b60a852..67c8fb2 100644 --- a/.github/workflows/dev-build.yml +++ b/.github/workflows/dev-build.yml @@ -1,3 +1,14 @@ + +# This worklflow will perform following actions when the code is pushed to development branch: +# - Test linting with pylint. +# - Fetch Latest release. +# - Build the latest docker image in development which needs test to pass first. +# - Push the docker image to Github Artifact Registry-Dev. +# +# Maintainers: +# - name: Nisha Sharma +# - email: nisha.sharma@uni-jena.de + name : Dev Build, Test and Publish on: diff --git a/.github/workflows/prod-build.yml b/.github/workflows/prod-build.yml index e7d59c6..197746a 100644 --- a/.github/workflows/prod-build.yml +++ b/.github/workflows/prod-build.yml @@ -1,3 +1,13 @@ + +# This worklflow will perform following actions when a release is published: +# - Fetch Latest release. +# - Build the latest docker image in production. +# - Push the docker image to Github Artifact Registry-Prod. +# +# Maintainers: +# - name: Nisha Sharma +# - email: nisha.sharma@uni-jena.de + name : Prod Build, Test and Publish on: @@ -11,40 +21,9 @@ env: APP_IMAGE: chem-py-microservice jobs: - test: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.10"] - steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip3 install --upgrade setuptools pip - pip3 install --no-cache-dir -r requirements.txt - python3 -m pip uninstall -y imantics - pip3 install imantics==0.1.12 - pip3 install --no-deps decimer-segmentation - pip3 install --no-deps decimer>=2.2.0 - pip3 install --no-deps STOUT-pypi>=2.0.5 - pip install flake8 pytest - - name: Analysing the code with pylint - run: | - flake8 --ignore E501,W503 $(git ls-files '*.py') - - name: Run test - run: | - pytest -p no:warnings - setup-build-publish-prod: name: Build & publish to prod registry - # if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest - needs: test steps: - name: Checkout uses: actions/checkout@v2 diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 8307264..02de48a 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -1,3 +1,13 @@ + +# This worklflow will perform following actions when the code is pushed to main branch. +# - Test linting with pylint. +# - Test the code with pytest. +# - Trigger release-please action to create release which needs test to pass first. +# +# Maintainers: +# - name: Nisha Sharma +# - email: nisha.sharma@uni-jena.de + name: release-please-action on: @@ -6,8 +16,38 @@ on: - main jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.10"] + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip3 install --upgrade setuptools pip + pip3 install --no-cache-dir -r requirements.txt + python3 -m pip uninstall -y imantics + pip3 install imantics==0.1.12 + pip3 install --no-deps decimer-segmentation + pip3 install --no-deps decimer>=2.2.0 + pip3 install --no-deps STOUT-pypi>=2.0.5 + pip install flake8 pytest + - name: Analysing the code with pylint + run: | + flake8 --ignore E501,W503 $(git ls-files '*.py') + - name: Run test + run: | + pytest -p no:warnings + release-please: runs-on: ubuntu-latest + needs: test steps: - uses: google-github-actions/release-please-action@v3 with: From 0f762ae355b54d93228f4e052b7cdd2063a8dfba Mon Sep 17 00:00:00 2001 From: Kohulan Date: Fri, 24 Mar 2023 11:42:41 +0100 Subject: [PATCH 3/5] feat: add CDK aromatic ring calculator #84 --- app/modules/cdkmodules.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/app/modules/cdkmodules.py b/app/modules/cdkmodules.py index f0d9eec..f0ab4ee 100644 --- a/app/modules/cdkmodules.py +++ b/app/modules/cdkmodules.py @@ -112,6 +112,33 @@ def getCDKSDGMol(smiles: str): return mol_str +def getAromaticRingCount(mol): + """This function is adapted from CDK to + calculate the number of Aromatic Rings + present in a given molecule. + Args (mol): CDK mol object as input. + Returns (int): Number if aromatic rings present + """ + Cycles = JClass(cdk_base + ".graph.Cycles") + ElectronDonation = JClass(cdk_base + ".aromaticity.ElectronDonation") + + Aromaticity = JClass(cdk_base + ".aromaticity.Aromaticity")( + ElectronDonation.daylight(), Cycles.cdkAromaticSet() + ) + Aromaticity.apply(mol) + MCBRings = Cycles.mcb(mol).toRingSet() + NumberOfAromaticRings = 0 + for RingContainer in MCBRings.atomContainers(): + AreAllRingBondsAromatic = True + for Bond in RingContainer.bonds(): + if not Bond.isAromatic(): + AreAllRingBondsAromatic = False + break + if AreAllRingBondsAromatic: + NumberOfAromaticRings += 1 + return NumberOfAromaticRings + + def getCDKDescriptors(smiles: str): """Take an input SMILES and generate a selected set of molecular descriptors generated using CDK as a list. @@ -172,7 +199,7 @@ def getCDKDescriptors(smiles: str): .calculate(Mol) .getValue() ) - AromaticRings = None + AromaticRings = getAromaticRingCount(Mol) QEDWeighted = None FormalCharge = JClass( cdk_base + ".tools.manipulator.AtomContainerManipulator" @@ -198,7 +225,7 @@ def getCDKDescriptors(smiles: str): str(HBondAcceptorCountDescriptor), str(HBondDonorCountDescriptor), str(RuleOfFiveDescriptor), - str(AromaticRings), + AromaticRings, str(QEDWeighted), FormalCharge, "{:.2f}".format(float(str(FractionalCSP3Descriptor))), From 47c9908c23575ab105f603fdb46b0dfa9bc70886 Mon Sep 17 00:00:00 2001 From: Venkata Chandra Sekhar Nainala Date: Fri, 24 Mar 2023 12:43:56 +0100 Subject: [PATCH 4/5] fix: #81 POST requests now parse body using fastapi interface and swagger UI --- app/routers/chem.py | 7 +++---- app/routers/decimer.py | 32 ++++++++++++++------------------ 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/app/routers/chem.py b/app/routers/chem.py index 121e473..1563512 100644 --- a/app/routers/chem.py +++ b/app/routers/chem.py @@ -1,5 +1,6 @@ -from fastapi import Request, APIRouter +from fastapi import Body, Request, APIRouter from typing import Optional +from typing_extensions import Annotated from rdkit import Chem from rdkit.Chem.EnumerateStereoisomers import ( EnumerateStereoisomers, @@ -49,14 +50,12 @@ async def SMILES_stereoisomers(smiles: str): @router.post("/standardize") -async def standardize_mol(request: Request): +async def standardize_mol(mol: Annotated[str, Body(embed=True)]): """ Standardize molblock using the ChEMBL curation pipeline routine: - **mol**: required """ - body = await request.json() - mol = body["mol"] if mol: standardized_mol = standardizer.standardize_molblock(mol) rdkit_mol = Chem.MolFromMolBlock(standardized_mol) diff --git a/app/routers/decimer.py b/app/routers/decimer.py index 366da45..67c7d98 100644 --- a/app/routers/decimer.py +++ b/app/routers/decimer.py @@ -4,7 +4,8 @@ from fastapi.responses import JSONResponse from urllib.request import urlopen from urllib.parse import urlsplit -from fastapi import Request, APIRouter +from fastapi import Body, APIRouter +from typing_extensions import Annotated from app.modules.decimermodules import getPredictedSegments router = APIRouter( @@ -21,25 +22,20 @@ async def decimer_index(): @router.post("/process") -async def extract_chemicalinfo(request: Request): - body = await request.json() - image_path = body["path"] - reference = body["reference"] - split = urlsplit(image_path) +async def extract_chemicalinfo(path: Annotated[str, Body(embed=True)], reference: Annotated[str, Body(embed=True)], img: Annotated[str, Body(embed=True)]): + split = urlsplit(path) filename = "/tmp/" + split.path.split("/")[-1] - if "img" in body: - imgDataURI = body["img"] - if imgDataURI: - response = urlopen(imgDataURI) - with open(filename, "wb") as f: - f.write(response.file.read()) - smiles = getPredictedSegments(filename) - os.remove(filename) - return JSONResponse( - content={"reference": reference, "smiles": smiles.split(".")} - ) + if img: + response = urlopen(img) + with open(filename, "wb") as f: + f.write(response.file.read()) + smiles = getPredictedSegments(filename) + os.remove(filename) + return JSONResponse( + content={"reference": reference, "smiles": smiles.split(".")} + ) else: - response = requests.get(image_path) + response = requests.get(path) if response.status_code == 200: with open(filename, "wb") as f: f.write(response.content) From e086b19f478ec7a64f57bcf15b57bbddeb46b736 Mon Sep 17 00:00:00 2001 From: Venkata Chandra Sekhar Nainala Date: Fri, 24 Mar 2023 12:44:22 +0100 Subject: [PATCH 5/5] fix: updated default release version from "pre-release" to "latest" --- app/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/main.py b/app/main.py index 7be498e..4ca99d9 100644 --- a/app/main.py +++ b/app/main.py @@ -35,7 +35,7 @@ def custom_openapi(): return app.openapi_schema openapi_schema = get_openapi( title="Cheminf Micro Services", - version=os.getenv("RELEASE_VERSION", "pre-release"), + version=os.getenv("RELEASE_VERSION", "latest"), description="This set of essential and valuable microservices is designed to be accessed via API calls to support cheminformatics. Generally, it is designed to work with SMILES-based inputs and could be used to translate between different machine-readable representations, get Natural Product (NP) likeliness scores, visualize chemical structures, and generate descriptors. In addition, the microservices also host an instance of STOUT and another instance of DECIMER (two deep learning models for IUPAC name generation and optical chemical structure recognition, respectively).", routes=app.routes, )