11from typing import Annotated
22from psycopg2 .errors import UniqueViolation
33from app .modules .cdkmodules import getCDKHOSECodes
4- from fastapi import APIRouter , HTTPException , status , Query , Body
4+ from fastapi import APIRouter , HTTPException , status , Query
55from app .modules .rdkitmodules import getRDKitHOSECodes
66from app .schemas import HealthCheck
7- from app .schemas .alatis import AlatisModel
8- import requests
97
108router = APIRouter (
119 prefix = "/chem" ,
@@ -41,23 +39,91 @@ def get_health() -> HealthCheck:
4139@router .get (
4240 "/hosecode" ,
4341 tags = ["chem" ],
44- summary = "Generates HOSE codes of molecule" ,
42+ summary = "Generate HOSE codes for a molecule" ,
43+ description = (
44+ "Generate **Hierarchically Ordered Spherical Environment (HOSE)** codes "
45+ "for every atom in the given molecule. HOSE codes encode the local chemical "
46+ "environment around each atom up to a configurable number of spheres.\n \n "
47+ "Supports two cheminformatics frameworks:\n "
48+ "- **CDK** (Chemistry Development Kit) — default, supports stereo\n "
49+ "- **RDKit** — alternative implementation"
50+ ),
4551 response_model = list [str ],
46- response_description = "Returns an array of hose codes generated " ,
52+ response_description = "Array of HOSE code strings, one per atom in the molecule " ,
4753 status_code = status .HTTP_200_OK ,
54+ responses = {
55+ 200 : {
56+ "description" : "Successfully generated HOSE codes" ,
57+ "content" : {
58+ "application/json" : {
59+ "example" : [
60+ "C(CC,CC,&)" ,
61+ "C(CC,C&,&)" ,
62+ "C(CC,CC,&)" ,
63+ "C(CCC,CC&,&)" ,
64+ "C(CC,CC,CC)" ,
65+ "C(CC,CC,CC)" ,
66+ ]
67+ }
68+ },
69+ },
70+ 409 : {"description" : "Molecule already exists (unique constraint violation)" },
71+ 422 : {"description" : "Error parsing the molecular structure" },
72+ },
4873)
4974async def HOSE_Codes (
50- smiles : Annotated [str , Query (examples = ["CCCC1CC1" ])],
51- framework : Annotated [str , Query (enum = ["cdk" , "rdkit" ])] = "cdk" ,
52- spheres : Annotated [int , Query ()] = 3 ,
53- usestereo : Annotated [bool , Query ()] = False ,
75+ smiles : Annotated [
76+ str ,
77+ Query (
78+ description = "SMILES string representing the molecular structure" ,
79+ example = "CCCC1CC1" ,
80+ examples = [
81+ "CCCC1CC1" ,
82+ "c1ccccc1" ,
83+ "CC(=O)O" ,
84+ "CCO" ,
85+ "C1CCCCC1" ,
86+ "CC(=O)Oc1ccccc1C(=O)O" ,
87+ ],
88+ ),
89+ ],
90+ framework : Annotated [
91+ str ,
92+ Query (
93+ enum = ["cdk" , "rdkit" ],
94+ description = "Cheminformatics framework to use for HOSE code generation" ,
95+ ),
96+ ] = "cdk" ,
97+ spheres : Annotated [
98+ int ,
99+ Query (
100+ description = "Number of spheres (bond distance) to consider around each atom" ,
101+ ge = 1 ,
102+ le = 10 ,
103+ ),
104+ ] = 3 ,
105+ usestereo : Annotated [
106+ bool ,
107+ Query (
108+ description = "Whether to include stereochemistry information in HOSE codes (CDK only)" ,
109+ ),
110+ ] = False ,
54111) -> list [str ]:
55112 """
56- ## Generates HOSE codes for a given molecule
57- Endpoint to generate HOSE codes based on each atom in the given molecule.
113+ ## Generate HOSE codes for a given molecule
58114
59- Returns:
60- HOSE Codes: An array of hose codes generated
115+ Generates HOSE (Hierarchically Ordered Spherical Environment) codes based on
116+ each atom in the given molecule. These codes are widely used in NMR chemical
117+ shift prediction.
118+
119+ ### Parameters
120+ - **smiles**: A valid SMILES string (e.g. `CCCC1CC1`)
121+ - **framework**: Choose `cdk` (default) or `rdkit`
122+ - **spheres**: Number of bond spheres to encode (default: 3)
123+ - **usestereo**: Include stereochemistry in codes (CDK only, default: false)
124+
125+ ### Returns
126+ An array of HOSE code strings, one for each atom in the molecule.
61127 """
62128 try :
63129 if framework == "cdk" :
@@ -78,32 +144,3 @@ async def HOSE_Codes(
78144 detail = "Error parsing the structure " + e .message ,
79145 headers = {"X-Error" : "RDKit molecule input parse error" },
80146 )
81-
82-
83- @router .post (
84- "/label-atoms" ,
85- tags = ["chem" ],
86- summary = "Label atoms using ALATIS naming system" ,
87- response_model = AlatisModel ,
88- response_description = "" ,
89- status_code = status .HTTP_200_OK ,
90- )
91- async def label_atoms (data : Annotated [str , Body (embed = False , media_type = "text/plain" )]):
92- """
93- ## Generates atom labels for a given molecule
94-
95- Returns:
96- JSON with various representations
97- """
98- try :
99- url = "http://alatis.nmrfam.wisc.edu/upload"
100- payload = {"input_text" : data , "format" : "format_" , "response_type" : "json" }
101- response = requests .request ("POST" , url , data = payload )
102- response .raise_for_status () # Raise an error for bad status codes
103- return response .json ()
104- except Exception as e :
105- raise HTTPException (
106- status_code = 422 ,
107- detail = f"Error parsing the structure: { str (e )} " ,
108- headers = {"X-Error" : "RDKit molecule input parse error" },
109- )
0 commit comments