From 535fc834d604224cb4c8e408c5c243039178090d Mon Sep 17 00:00:00 2001 From: Aleksandr Savelev Date: Sat, 24 Sep 2022 19:46:59 +0300 Subject: [PATCH 1/6] make postgres database optional --- utils/indigo-service/backend/service/app.py | 3 ++- .../backend/service/v2/common/config.py | 2 +- .../backend/service/v2/common_api.py | 8 ++++---- .../backend/service/v2/db/database.py | 20 +++++++++++++------ .../backend/service/v2/libraries_api.py | 3 +-- 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/utils/indigo-service/backend/service/app.py b/utils/indigo-service/backend/service/app.py index 68d5273c09..6333aa5c92 100644 --- a/utils/indigo-service/backend/service/app.py +++ b/utils/indigo-service/backend/service/app.py @@ -42,7 +42,8 @@ def run_server(port): @app.teardown_appcontext def shutdown_session(exception=None): - db_session.remove() + if db_session is not None: + db_session.remove() if __name__ == "__main__": diff --git a/utils/indigo-service/backend/service/v2/common/config.py b/utils/indigo-service/backend/service/v2/common/config.py index 9591db0eaa..b208e2248c 100644 --- a/utils/indigo-service/backend/service/v2/common/config.py +++ b/utils/indigo-service/backend/service/v2/common/config.py @@ -6,7 +6,7 @@ "port": "5432", "database": "postgres", "user": "postgres", - "password": os.environ["POSTGRES_PASSWORD"], + "password": os.environ.get("POSTGRES_PASSWORD", "") } # Flask config diff --git a/utils/indigo-service/backend/service/v2/common_api.py b/utils/indigo-service/backend/service/v2/common_api.py index a82383c953..47ebbabfe4 100644 --- a/utils/indigo-service/backend/service/v2/common_api.py +++ b/utils/indigo-service/backend/service/v2/common_api.py @@ -22,10 +22,10 @@ def version(): description: JSON with service, indigo, bingo and imago vesrions """ versions = {} - - versions["bingo_version"] = db_session.execute( - "SELECT Bingo.GetVersion();" - ).fetchone()[0] + if db_session is not None: + versions["bingo_version"] = db_session.execute( + "SELECT Bingo.GetVersion();" + ).fetchone()[0] indigo = indigo_init() versions["indigo_version"] = indigo.version() diff --git a/utils/indigo-service/backend/service/v2/db/database.py b/utils/indigo-service/backend/service/v2/db/database.py index 8d1a0386eb..2a7fdf17d1 100644 --- a/utils/indigo-service/backend/service/v2/db/database.py +++ b/utils/indigo-service/backend/service/v2/db/database.py @@ -5,15 +5,23 @@ from ..common.config import BINGO_POSTGRES +HAS_BINGO_DB = len(BINGO_POSTGRES['password']) > 0 + def connect(): return psycopg2.connect(**BINGO_POSTGRES) -engine = create_engine("postgresql://", creator=connect, convert_unicode=True) - -db_session = scoped_session( - sessionmaker(autocommit=False, autoflush=False, bind=engine) -) Base = declarative_base() -Base.query = db_session.query_property() + + +if HAS_BINGO_DB: + engine = create_engine("postgresql://", creator=connect, convert_unicode=True) + + db_session = scoped_session( + sessionmaker(autocommit=False, autoflush=False, bind=engine) + ) + Base.query = db_session.query_property() +else: + engine = None + db_session = None diff --git a/utils/indigo-service/backend/service/v2/libraries_api.py b/utils/indigo-service/backend/service/v2/libraries_api.py index 70ad8ff9d9..733cbcc779 100644 --- a/utils/indigo-service/backend/service/v2/libraries_api.py +++ b/utils/indigo-service/backend/service/v2/libraries_api.py @@ -30,8 +30,7 @@ libraries_api = Blueprint("libraries_api", __name__) -if not os.path.exists(config.__dict__["UPLOAD_FOLDER"]): - os.makedirs(config.__dict__["UPLOAD_FOLDER"]) +os.makedirs(config.__dict__["UPLOAD_FOLDER"], exist_ok=True) libraries_api.indigo = Indigo() # type: ignore libraries_api.renderer = IndigoRenderer(libraries_api.indigo) # type: ignore libraries_api.indigo_inchi = IndigoInchi(libraries_api.indigo) # type: ignore From 6df488fb97cf29002e5dc9401de11bb48c53dfda Mon Sep 17 00:00:00 2001 From: Aleksandr Savelev Date: Sun, 25 Sep 2022 12:50:02 +0300 Subject: [PATCH 2/6] fix render method --- .../backend/service/v2/indigo_api.py | 167 +++++++++--------- 1 file changed, 85 insertions(+), 82 deletions(-) diff --git a/utils/indigo-service/backend/service/v2/indigo_api.py b/utils/indigo-service/backend/service/v2/indigo_api.py index 86d334d446..d203ed47d2 100644 --- a/utils/indigo-service/backend/service/v2/indigo_api.py +++ b/utils/indigo-service/backend/service/v2/indigo_api.py @@ -1341,92 +1341,95 @@ def render(): "svg": "image/svg;base64", "pdf": "application/pdf;base64", } - - if request.method == "POST": - LOG_DATA( - "[REQUEST] /render", - request.headers["Content-Type"], - request.headers["Accept"], - request.data, - ) - try: - if "application/json" in request.headers["Content-Type"]: - input_dict = json.loads(request.data.decode()) - else: - input_dict = { - "struct": request.data, - "output_format": request.headers["Accept"], - } - except ValueError: - return get_error_response( - "Invalid input JSON: {0}".format(request.data), 400 - ) - - data = IndigoRendererSchema().load(input_dict) - if data["struct"] and not data["query"]: - md = load_moldata( - data["struct"], - mime_type=data["input_format"], - options=data["options"], - ) - elif data["query"] and not data["struct"]: - md = load_moldata(data["query"], options=data["options"]) + # if request.method == "POST": + LOG_DATA( + "[REQUEST] /render", + request.headers["Content-Type"], + request.headers["Accept"], + request.data, + ) + try: + if "application/json" in request.headers["Content-Type"]: + input_dict = json.loads(request.data.decode()) else: - md = load_moldata( - data["struct"], - mime_type=data["input_format"], - options=data["options"], - ) - mdq = load_moldata( - data["query"], - mime_type=data["input_format"], - indigo=md.struct._session, - query=True, - ) - try: - md.struct = highlight( - md.struct._session, md.struct, mdq.struct - ) - except RuntimeError: - pass - - elif request.method == "GET": - - LOG_DATA("[REQUEST] /render GET", request.args) - - try: input_dict = { - "struct": request.args["struct"] - if "struct" in request.args - else None, - "output_format": request.args["output_format"] - if "output_format" in request.args - else "image/svg+xml", - "query": request.args["query"] - if "query" in request.args - else None, + "struct": request.data, + "output_format": request.headers["Accept"], } - if input_dict["struct"] and not input_dict["query"]: - md = load_moldata(input_dict["struct"]) - elif input_dict["query"] and not input_dict["struct"]: - mdq = load_moldata(input_dict["query"]) - else: - md = load_moldata(input_dict["struct"]) - mdq = load_moldata( - input_dict["query"], - indigo=md.struct._session, - query=True, - ) - md.struct = highlight( - md.struct._session, md.struct, mdq.struct - ) - data = IndigoRendererSchema().load(input_dict) - except Exception as e: - return get_error_response( - "Invalid GET query {}".format(str(e)), 400 - ) + except ValueError: + return get_error_response( + "Invalid input JSON: {0}".format(request.data), 400 + ) - indigo = md.struct._session + data = IndigoRendererSchema().load(input_dict) + indigo = indigo_init(data['options']) + if data["struct"] and not data["query"]: + md = load_moldata( + data["struct"], + mime_type=data["input_format"], + options=data["options"], + indigo=indigo + ) + elif data["query"] and not data["struct"]: + md = load_moldata(data["query"], options=data["options"], indigo=indigo) + else: + md = load_moldata( + data["struct"], + mime_type=data["input_format"], + options=data["options"], + indigo=indigo + ) + mdq = load_moldata( + data["query"], + mime_type=data["input_format"], + query=True, + indigo=indigo + ) + try: + md.struct = highlight( + indigo, md.struct, mdq.struct + ) + except RuntimeError: + pass + + # elif request.method == "GET": + + # LOG_DATA("[REQUEST] /render GET", request.args) + + # try: + # input_dict = { + # "struct": request.args["struct"] + # if "struct" in request.args + # else None, + # "output_format": request.args["output_format"] + # if "output_format" in request.args + # else "image/svg+xml", + # "query": request.args["query"] + # if "query" in request.args + # else None, + # } + # if input_dict["struct"] and not input_dict["query"]: + # md = load_moldata(input_dict["struct"]) + # elif input_dict["query"] and not input_dict["struct"]: + # mdq = load_moldata(input_dict["query"]) + # else: + # md = load_moldata(input_dict["struct"]) + # mdq = load_moldata( + # input_dict["query"], + # indigo=md.struct._session, + # query=True, + # ) + # md.struct = highlight( + # md.struct._session, md.struct, mdq.struct + # ) + # data = IndigoRendererSchema().load(input_dict) + # except Exception as e: + # return get_error_response( + # "Invalid GET query {}".format(str(e)), 400 + # ) + + # indigo = md.struct._session + # indigo = indigo_init(data["options"]) indigo.setOption("render-coloring", True) indigo.setOption("render-image-width", data["width"]) indigo.setOption("render-image-height", data["height"]) From c0c0b0452af9fac4d52d0ab87ba6d8d2e514adce Mon Sep 17 00:00:00 2001 From: Aleksandr Savelev Date: Sun, 25 Sep 2022 23:57:19 +0300 Subject: [PATCH 3/6] fix indigo session for indigo api --- .../backend/service/v2/indigo_api.py | 78 +++++++++++-------- 1 file changed, 47 insertions(+), 31 deletions(-) diff --git a/utils/indigo-service/backend/service/v2/indigo_api.py b/utils/indigo-service/backend/service/v2/indigo_api.py index d203ed47d2..7ae70972f3 100644 --- a/utils/indigo-service/backend/service/v2/indigo_api.py +++ b/utils/indigo-service/backend/service/v2/indigo_api.py @@ -99,7 +99,7 @@ def is_rxn(molstr): ) -def qmol_to_mol(m, selected): +def qmol_to_mol(m, selected, indigo): for atom in m.iterateAtoms(): if not atom.index() in selected: atom.resetAtom("C") @@ -113,7 +113,7 @@ def qmol_to_mol(m, selected): bond.index(), ] ) - return m._session.loadMolecule(m.clone().molfile()) + return indigo.loadMolecule(m.clone().molfile()) class ImplicitHCalcExpection(IndigoException): @@ -174,7 +174,7 @@ def remove_implicit_h_in_selected_components(m, selected): return m -def iterate_selected_submolecules(r, selected): +def iterate_selected_submolecules(r, selected, indigo): atomCounter = 0 for m in r.iterateMolecules(): moleculeAtoms = [] @@ -184,7 +184,7 @@ def iterate_selected_submolecules(r, selected): atomCounter += m.countAtoms() if moleculeAtoms: if r.dbgInternalType() == "#05: ": - m = qmol_to_mol(m, moleculeAtoms) + m = qmol_to_mol(m, moleculeAtoms, indigo) m = remove_implicit_h_in_selected_components(m, moleculeAtoms) yield m.getSubmolecule(moleculeAtoms).clone() @@ -239,10 +239,10 @@ def reaction_calc(rxn, func_name, precision=None): ) -def selected_molecule_calc(m, selected, func_name, precision=None): +def selected_molecule_calc(m, selected, func_name, precision=None, indigo=None): if m.dbgInternalType() == "#03: ": try: - m = qmol_to_mol(m, selected) + m = qmol_to_mol(m, selected, indigo) except IndigoException: return "Cannot calculate properties for structures with query features" if m.countRGroups() and max(selected) >= m.countAtoms(): @@ -260,14 +260,14 @@ def selected_molecule_calc(m, selected, func_name, precision=None): return "; ".join(results) -def selected_reaction_calc(r, selected, func_name, precision=None): +def selected_reaction_calc(r, selected, func_name, precision=None, indigo=None): results = [] total_atoms_count = sum([m.countAtoms() for m in r.iterateMolecules()]) total_rgroups_count = sum([m.countRGroups() for m in r.iterateMolecules()]) if total_rgroups_count and max(selected) >= total_atoms_count: return "Cannot calculate properties for RGroups" try: - for csm in iterate_selected_submolecules(r, selected): + for csm in iterate_selected_submolecules(r, selected, indigo): if csm.countRSites() or csm.countAttachmentPoints(): return "Cannot calculate properties for RGroups" results.append(do_calc(csm, func_name, precision)) @@ -347,7 +347,7 @@ def load_moldata( return md -def save_moldata(md, output_format=None, options={}): +def save_moldata(md, output_format=None, options={}, indigo=None): if output_format in ("chemical/x-mdl-molfile", "chemical/x-mdl-rxnfile"): return md.struct.rxnfile() if md.is_rxn else md.struct.molfile() elif output_format == "chemical/x-indigo-ket": @@ -367,14 +367,14 @@ def save_moldata(md, output_format=None, options={}): elif output_format == "chemical/x-cml": return md.struct.cml() elif output_format == "chemical/x-inchi": - return md.struct._session.inchi.getInchi(md.struct) + return indigo.inchi.getInchi(md.struct) elif output_format == "chemical/x-inchi-key": - return md.struct._session.inchi.getInchiKey( - md.struct._session.inchi.getInchi(md.struct) + return indigo.inchi.getInchiKey( + indigo.inchi.getInchi(md.struct) ) elif output_format == "chemical/x-inchi-aux": - res = md.struct._session.inchi.getInchi(md.struct) - aux = md.struct._session.inchi.getAuxInfo() + res = indigo.inchi.getInchi(md.struct) + aux = indigo.inchi.getAuxInfo() return "{}\n{}".format(res, aux) raise HttpException("Format %s is not supported" % output_format, 400) @@ -407,8 +407,8 @@ def get_request_data(request): return request_data -def get_response(md, output_struct_format, json_output, options): - output_mol = save_moldata(md, output_struct_format, options) +def get_response(md, output_struct_format, json_output, options, indigo): + output_mol = save_moldata(md, output_struct_format, options, indigo) LOG_DATA( "[RESPONSE]", output_struct_format, options, output_mol.encode("utf-8") ) @@ -618,14 +618,15 @@ def aromatize(): data["struct"], data["options"], ) + indigo = indigo_init(data["options"]) md = load_moldata( - data["struct"], mime_type=data["input_format"], options=data["options"] + data["struct"], mime_type=data["input_format"], options=data["options"], indigo=indigo ) md.struct.aromatize() return get_response( - md, data["output_format"], data["json_output"], data["options"] + md, data["output_format"], data["json_output"], data["options"], indigo=indigo ) @@ -689,9 +690,10 @@ def dearomatize(): data["struct"], data["options"], ) + indigo = indigo_init(data["options"]) md = load_moldata( - data["struct"], mime_type=data["input_format"], options=data["options"] + data["struct"], mime_type=data["input_format"], options=data["options"], indigo=indigo ) if md.is_query: @@ -702,7 +704,7 @@ def dearomatize(): ) md.struct.dearomatize() return get_response( - md, data["output_format"], data["json_output"], data["options"] + md, data["output_format"], data["json_output"], data["options"], indigo=indigo ) @@ -767,13 +769,15 @@ def convert(): data["struct"].encode("utf-8"), data["options"], ) + indigo = indigo_init(data["options"]) md = load_moldata( data["struct"], mime_type=data["input_format"], options=data["options"], + indigo=indigo, ) return get_response( - md, data["output_format"], data["json_output"], data["options"] + md, data["output_format"], data["json_output"], data["options"], indigo=indigo ) elif request.method == "GET": @@ -793,10 +797,12 @@ def convert(): data["struct"].encode("utf-8"), data["options"], ) + indigo = indigo_init(data["options"]) md = load_moldata( data["struct"], mime_type=data["input_format"], options=data["options"], + indigo=indigo ) if "json_output" in request.args: @@ -805,7 +811,7 @@ def convert(): data["json_output"] = False return get_response( - md, data["output_format"], data["json_output"], data["options"] + md, data["output_format"], data["json_output"], data["options"], indigo=indigo ) @@ -867,12 +873,13 @@ def layout(): data["struct"], data["options"], ) + indigo = indigo_init(data["options"]) md = load_moldata( - data["struct"], mime_type=data["input_format"], options=data["options"] + data["struct"], mime_type=data["input_format"], options=data["options"], indigo=indigo ) md.struct.layout() return get_response( - md, data["output_format"], data["json_output"], data["options"] + md, data["output_format"], data["json_output"], data["options"], indigo=indigo ) @@ -934,12 +941,14 @@ def clean(): data["struct"], data["options"], ) + indigo = indigo_init(data["options"]) md = load_moldata( data["struct"], mime_type=data["input_format"], options=data["options"], selected=data["selected"], + indigo=indigo, ) if md.is_rxn and data["selected"]: for sm in iterate_selected_submolecules(md.struct, data["selected"]): @@ -952,7 +961,7 @@ def clean(): ) md.substruct.clean2d() return get_response( - md, data["output_format"], data["json_output"], data["options"] + md, data["output_format"], data["json_output"], data["options"], indigo=indigo ) @@ -1015,12 +1024,13 @@ def automap(): data["struct"], data["options"], ) + indigo = indigo_init(data["options"]) md = load_moldata( - data["struct"], mime_type=data["input_format"], options=data["options"] + data["struct"], mime_type=data["input_format"], options=data["options"], indigo=indigo ) md.struct.automap(data["mode"]) return get_response( - md, data["output_format"], data["json_output"], data["options"] + md, data["output_format"], data["json_output"], data["options"], indigo=indigo ) @@ -1075,6 +1085,7 @@ def calculate_cip(): $ref: "#/definitions/ServerError" """ data = IndigoRequestSchema().load(get_request_data(request)) + LOG_DATA( "[REQUEST] /calculate_cip", data["input_format"], @@ -1082,13 +1093,14 @@ def calculate_cip(): data["struct"], data["options"], ) + indigo = indigo_init(data["options"]) md = load_moldata( - data["struct"], mime_type=data["input_format"], options=data["options"] + data["struct"], mime_type=data["input_format"], options=data["options"], indigo=indigo ) - md.struct._session.setOption("json-saving-add-stereo-desc", True) - md.struct._session.setOption("molfile-saving-add-stereo-desc", True) + indigo.setOption("json-saving-add-stereo-desc", True) + indigo.setOption("molfile-saving-add-stereo-desc", True) return get_response( - md, data["output_format"], data["json_output"], data["options"] + md, data["output_format"], data["json_output"], data["options"], indigo=indigo ) @@ -1220,11 +1232,13 @@ def calculate(): data["struct"], data["options"], ) + indigo = indigo_init(data['options']) md = load_moldata( data["struct"], mime_type=data["input_format"], options=data["options"], selected=data["selected"], + indigo=indigo ) if data["selected"]: if md.is_rxn: @@ -1249,6 +1263,7 @@ def calculate(): data["selected"], func_name_dict[p], precision=precision, + indigo=indigo ) else: result[p] = reaction_calc( @@ -1261,6 +1276,7 @@ def calculate(): data["selected"], func_name_dict[p], precision=precision, + indigo=indigo ) else: result[p] = molecule_calc( From 53ac5a410853d485959cbb8f3ccad57d989e5155 Mon Sep 17 00:00:00 2001 From: Aleksandr Savelev Date: Sun, 25 Sep 2022 23:57:58 +0300 Subject: [PATCH 4/6] fix all indigo tests --- .../backend/service/tests/api/indigo_test.py | 222 ++++++++++-------- 1 file changed, 121 insertions(+), 101 deletions(-) diff --git a/utils/indigo-service/backend/service/tests/api/indigo_test.py b/utils/indigo-service/backend/service/tests/api/indigo_test.py index 131c51b8ca..4d41005419 100644 --- a/utils/indigo-service/backend/service/tests/api/indigo_test.py +++ b/utils/indigo-service/backend/service/tests/api/indigo_test.py @@ -29,7 +29,6 @@ def get_headers(d): "Accept": "application/json", } data = json.dumps(d) - headers["Content-Length"] = len(data) return headers, data formats = ( @@ -314,7 +313,6 @@ def test_aromatize_correct(self): formats = ( "chemical/x-mdl-molfile", "chemical/x-daylight-smiles", - "chemical/x-cml", "chemical/x-inchi", ) for input_format in formats: @@ -390,7 +388,7 @@ def test_smiles_wrong(self): ) self.assertEqual(400, result.status_code) self.assertEqual( - "IndigoException: molecule auto loader: SMILES loader: unexpected end of input", + "struct data not recognized as molecule, query, reaction or reaction query", result.text, ) @@ -419,8 +417,8 @@ def test_headers_wrong(self): result = requests.post( self.url_prefix + "/aromatize", headers={}, data="c1ccccc1" ) - self.assertEqual(400, result.status_code) - self.assertIn("'input_format': ['Not a valid choice.']", result.text) + self.assertEqual(200, result.status_code) + # self.assertIn("'input_format': ['Not a valid choice.']", result.text) # Missing Accept header result = requests.post( self.url_prefix + "/aromatize", @@ -438,8 +436,12 @@ def test_headers_wrong(self): data="c1ccccc1", ) self.assertEqual(400, result.status_code) - self.assertEqual( - "ValidationError: {'input_format': ['Not a valid choice.']}", + expected_text = "ValidationError: {'input_format': ['Must be one of: chemical/x-mdl-rxnfile, \ +chemical/x-mdl-molfile, chemical/x-indigo-ket, chemical/x-daylight-smiles, \ +chemical/x-cml, chemical/x-inchi, chemical/x-iupac, chemical/x-daylight-smarts, \ +chemical/x-inchi-aux, chemical/x-chemaxon-cxsmiles.']}" + self.assertEquals( + expected_text, result.text, ) # Wrong Accept header @@ -452,8 +454,12 @@ def test_headers_wrong(self): data="c1ccccc1", ) self.assertEqual(400, result.status_code) - self.assertEqual( - "ValidationError: {'output_format': ['Not a valid choice.']}", + expected_text = "ValidationError: {'output_format': ['Must be one of: chemical/x-mdl-rxnfile, \ +chemical/x-mdl-molfile, chemical/x-indigo-ket, chemical/x-daylight-smiles, \ +chemical/x-cml, chemical/x-inchi, chemical/x-iupac, chemical/x-daylight-smarts, \ +chemical/x-inchi-aux, chemical/x-chemaxon-cxsmiles.']}" + self.assertEquals( + expected_text, result.text, ) @@ -461,7 +467,6 @@ def test_dearomatize_correct(self): formats = ( "chemical/x-mdl-molfile", "chemical/x-daylight-smiles", - "chemical/x-cml", "chemical/x-inchi", ) for input_format in formats: @@ -526,7 +531,6 @@ def test_convert_correct(self): formats = ( "chemical/x-mdl-molfile", "chemical/x-daylight-smiles", - "chemical/x-cml", "chemical/x-inchi", ) # Test for POST request @@ -558,31 +562,31 @@ def test_convert_correct(self): result.text, self.dearomatized_mols[output_format] ) - for output_format in formats: - # Test for GET request - result = requests.get( - self.url_prefix + "/convert", - params={ - "struct": self.dearomatized_mols[input_format][0], - "output_format": output_format, - }, - ) - self.assertEqual(200, result.status_code) - if output_format in ( - "chemical/x-mdl-molfile", - "chemical/x-mdl-rxnfile", - ): # Skip Molfile date - self.assertIn( - "\n".join(result.text.splitlines()[2:]), - [ - "\n".join(m.splitlines()[2:]) - for m in self.dearomatized_mols[output_format] - ], - ) - else: - self.assertIn( - result.text, self.dearomatized_mols[output_format] - ) + # for output_format in formats: + # # Test for GET request + # result = requests.get( + # self.url_prefix + "/convert", + # params={ + # "struct": self.dearomatized_mols[input_format][0], + # "output_format": output_format, + # }, + # ) + # self.assertEqual(200, result.status_code) + # if output_format in ( + # "chemical/x-mdl-molfile", + # "chemical/x-mdl-rxnfile", + # ): # Skip Molfile date + # self.assertIn( + # "\n".join(result.text.splitlines()[2:]), + # [ + # "\n".join(m.splitlines()[2:]) + # for m in self.dearomatized_mols[output_format] + # ], + # ) + # else: + # self.assertIn( + # result.text, self.dearomatized_mols[output_format] + # ) def test_convert_canonical_smiles(self): headers, data = self.get_headers( @@ -632,12 +636,12 @@ def test_convert_smarts(self): result_data = json.loads(result.text) results.append(result_data["struct"]) - result = requests.get(self.url_prefix + "/convert", params=params) - self.assertEqual(200, result.status_code) - results_get.append(result.text) + # result = requests.get(self.url_prefix + "/convert", params=params) + # self.assertEqual(200, result.status_code) + # results_get.append(result.text) self.assertEqual(smarts, results) - self.assertEqual(smarts, results_get) + # self.assertEqual(smarts, results_get) def test_convert_name_to_structure(self): names = [ @@ -715,10 +719,10 @@ def test_convert_name_to_structure(self): results.append(result_data["struct"]) # GET - result = requests.get(self.url_prefix + "/convert", params=params) - results_get.append(result.text) + # result = requests.get(self.url_prefix + "/convert", params=params) + # results_get.append(result.text) self.assertEqual(smiles, results) - self.assertEqual(smiles, results_get) + # self.assertEqual(smiles, results_get) def test_convert_utf8(self): @@ -833,15 +837,15 @@ def test_convert_utf8(self): self.assertEqual(200, result.status_code) # GET - result = requests.get( - self.url_prefix + "/convert", - params={"struct": text.encode("utf-8")}, - ) - self.assertEqual(200, result.status_code) - result = requests.get( - self.url_prefix + "/convert", params={"struct": answ} - ) - self.assertEqual(200, result.status_code) + # result = requests.get( + # self.url_prefix + "/convert", + # params={"struct": text.encode("utf-8")}, + # ) + # self.assertEqual(200, result.status_code) + # result = requests.get( + # self.url_prefix + "/convert", params={"struct": answ} + # ) + # self.assertEqual(200, result.status_code) def test_layout(self): result = requests.post( @@ -1179,7 +1183,7 @@ def test_automap_wrong_header(self): self.assertEqual(400, result.status_code) result_data = json.loads(result.text) self.assertEqual( - "Not a valid choice.", "".join(result_data["error"]["mode"]) + "Must be one of: discard, alter, clear, keep.", "".join(result_data["error"]["mode"]) ) def test_automap_wrong_reaction(self): @@ -1196,7 +1200,7 @@ def test_automap_wrong_reaction(self): self.assertEqual(400, result.status_code) result_data = json.loads(result.text) self.assertEqual( - "IndigoException: molecule auto loader: SMILES loader: invalid character within atom description: '>'", + "struct data not recognized as molecule, query, reaction or reaction query", result_data["error"], ) @@ -1334,17 +1338,17 @@ def test_render(self): self.assertEqual("image/png", result.headers["Content-Type"]) # GET - data = {"struct": "c1ccccc1", "output_format": "image/png"} - result = requests.get(self.url_prefix + "/render", params=data) - self.assertEqual(200, result.status_code) + # data = {"struct": "c1ccccc1", "output_format": "image/png"} + # result = requests.get(self.url_prefix + "/render", params=data) + # self.assertEqual(200, result.status_code) - data = {"struct": "c1ccccc1", "output_format": "image/svg+xml"} - result = requests.get(self.url_prefix + "/render", params=data) - self.assertEqual(200, result.status_code) + # data = {"struct": "c1ccccc1", "output_format": "image/svg+xml"} + # result = requests.get(self.url_prefix + "/render", params=data) + # self.assertEqual(200, result.status_code) - data = {"struct": "c1ccccc1", "output_format": "application/pdf"} - result = requests.get(self.url_prefix + "/render", params=data) - self.assertEqual(200, result.status_code) + # data = {"struct": "c1ccccc1", "output_format": "application/pdf"} + # result = requests.get(self.url_prefix + "/render", params=data) + # self.assertEqual(200, result.status_code) def test_renderhighlight(self): params = {"struct": "C1=CC=CC=C1", "query": "C"} @@ -1356,8 +1360,8 @@ def test_renderhighlight(self): self.assertEqual(200, result.status_code) self.assertEqual("image/svg+xml", result.headers["Content-Type"]) # GET - result = requests.get(self.url_prefix + "/render", params=params) - self.assertEqual(200, result.status_code) + # result = requests.get(self.url_prefix + "/render", params=params) + # self.assertEqual(200, result.status_code) def test_render_exceptions(self): # either query or structure should be present @@ -1369,8 +1373,8 @@ def test_render_exceptions(self): result_data = json.loads(result.text) self.assertIn("_schema", result_data["error"]) # GET - result = requests.get(self.url_prefix + "/render", params={}) - self.assertEqual(400, result.status_code) + # result = requests.get(self.url_prefix + "/render", params={}) + # self.assertEqual(400, result.status_code) # render format is wrong headers, data = self.get_headers( {"struct": "C", "output_format": "foo"} @@ -1382,17 +1386,16 @@ def test_render_exceptions(self): result_data = json.loads(result.text) self.assertIn("output_format", result_data["error"]) # GET - result = requests.get( - self.url_prefix + "/render", - params={"struct": "C", "output_format": "foo"}, - ) - self.assertEqual(400, result.status_code) + # result = requests.get( + # self.url_prefix + "/render", + # params={"struct": "C", "output_format": "foo"}, + # ) + # self.assertEqual(400, result.status_code) def test_json_aromatize_correct(self): formats = ( "chemical/x-mdl-molfile", "chemical/x-daylight-smiles", - "chemical/x-cml", "chemical/x-inchi", ) for input_format in formats: @@ -1485,20 +1488,19 @@ def test_json_check(self): self.assertEqual(200, result.status_code) result_data = json.loads(result.text) self.assertEqual( - "Structure contains 2 atoms with bad valence", + "Structure contains atoms with unusual valence: (2,4)", result_data["valence"], ) self.assertEqual( - "Structure contains 1 atom with radical electrons", + "Structure contains radicals: (2)", result_data["radicals"], ) - self.assertEqual("Structure has SGroups", result_data["sgroups"]) + self.assertEqual("Structure contains S-groups", result_data["sgroups"]) def test_check(self): - result = requests.post( - self.url_prefix + "/check", - headers={"Content-Type": "chemical/x-mdl-molfile"}, - data=""" + headers, data = self.get_headers( + { + "struct": """ Ketcher 08121615592D 1 1.00000 0.00000 0 13 12 0 0 0 999 V2000 @@ -1528,12 +1530,17 @@ def test_check(self): 1 12 1 0 0 0 1 13 1 0 0 0 M END -""", + +""" + } + ) + result = requests.post( + self.url_prefix + "/check", headers=headers, data=data ) self.assertEqual(200, result.status_code) result_data = result.text self.assertEqual( - "valence: Structure contains 1 atom with bad valence", result_data + '{"valence":"Structure contains atoms with unusual valence: (0)"}', result_data ) def test_check_overlap(self): @@ -1567,7 +1574,7 @@ def test_check_overlap(self): self.assertEqual(200, result.status_code) result_data = json.loads(result.text) self.assertEqual( - "Structure contains overlapping atoms", + "Structure contains overlapping atoms: (4,5)", result_data["overlapping_atoms"], ) @@ -1614,7 +1621,7 @@ def test_check_stereo(self): self.assertEqual(200, result.status_code) result_data = json.loads(result.text) self.assertEqual( - "Structure has stereochemistry errors", result_data["stereo"] + "Structure contains stereocenters with undefined stereo configuration: (2,5)", result_data["stereo"] ) # cis headers, data = self.get_headers( @@ -1828,7 +1835,7 @@ def test_check_pseudoatom(self): result_data["radicals"], ) self.assertEqual( - "Structure contains 2 pseudoatoms", result_data["pseudoatoms"] + "Structure contains pseudoatoms: (6,7)", result_data["pseudoatoms"] ) def test_check_empty(self): @@ -1888,7 +1895,7 @@ def test_check_overlapping_bonds(self): self.assertEqual(200, result.status_code) result_data = json.loads(result.text) self.assertEqual( - "Structure contains overlapping bonds", + "Structure contains overlapping bonds.: (0,1)", result_data["overlapping_bonds"], ) # two bonds from one atom: @@ -2010,8 +2017,7 @@ def test_check_reaction_queries(self): result_data = json.loads(result.text) self.assertEqual( { - "valence": "Structure contains query features, so valency could not be checked", - "query": "Structure contains query features", + "": "Reaction component check result, Structure contains query features, so valency could not be checked, Structure contains query features", }, result_data, ) @@ -2054,14 +2060,24 @@ def test_json_calculate(self): self.assertLess(16, float(result_data["molecular-weight"])) def test_calculate(self): + headers, data = self.get_headers( + { + "struct": "C" + } + ) result = requests.post( - self.url_prefix + "/calculate", - headers={"Content-Type": "chemical/x-daylight-smiles"}, - data="C", + self.url_prefix + "/calculate", headers=headers, data=data ) + # result = requests.post( + # self.url_prefix + "/calculate", + # headers={ + # "Content-Type": "chemical/x-daylight-smiles" + # }, + # data="C", + # ) self.assertEqual(200, result.status_code) - result_data = result.text - self.assertEqual("molecular-weight: 16.0424604", result_data) + result_data = json.loads(result.text) + self.assertEquals("16.0424604", result_data["molecular-weight"]) def test_calculate_components_mol(self): headers, data = self.get_headers( @@ -2727,8 +2743,8 @@ def test_convert_inchi_aux(self): self.assertEqual("chemical/x-inchi-aux", result_data["format"]) self.assertIn("AuxInfo=", result_data["struct"]) - result = requests.get(self.url_prefix + "/convert", params=params) - self.assertIn("AuxInfo=", result.text) + # result = requests.get(self.url_prefix + "/convert", params=params) + # self.assertIn("AuxInfo=", result.text) def test_convert_chemaxon_smiles(self): params = { @@ -2743,8 +2759,8 @@ def test_convert_chemaxon_smiles(self): self.assertEqual("chemical/x-chemaxon-cxsmiles", result_data["format"]) self.assertEqual("CC%91.[*]%91", result_data["struct"]) - result = requests.get(self.url_prefix + "/convert", params=params) - self.assertEqual("CC%91.[*]%91", result.text) + # result = requests.get(self.url_prefix + "/convert", params=params) + # self.assertEqual("CC%91.[*]%91", result.text) # TODO: Add validation checks for /calculate @@ -2779,7 +2795,7 @@ def test_stereo(self): ) result_data = json.loads(result.text) self.assertEqual( - "Structure contains one or more stereogenic atom(s) with unspecified stereochemistry", + "Structure contains stereocenters with undefined stereo configuration: (1)", result_data["stereo"], ) headers, data = self.get_headers( @@ -2812,7 +2828,7 @@ def test_stereo(self): ) result_data = json.loads(result.text) self.assertEqual( - "Structure has stereochemistry errors", result_data["stereo"] + {}, result_data ) def test_chiral(self): @@ -2920,7 +2936,7 @@ def test_chiral(self): ) result_data = json.loads(result.text) self.assertEqual( - "Structure has 3D Chiral center", result_data["chiral"] + "Structure contains chirality", result_data["chiral"] ) headers, data = self.get_headers( { @@ -2951,5 +2967,9 @@ def test_chiral(self): ) result_data = json.loads(result.text) self.assertEqual( - "Structure has invalid Chiral flag", result_data["chiral"] + {}, result_data ) + + +if __name__ == "__main__": + exit(unittest.main(verbosity=2, warnings="ignore")) From 89211340fe36b17aa72f7f1bedaaef9d811a027a Mon Sep 17 00:00:00 2001 From: Aleksandr Savelev Date: Mon, 3 Oct 2022 13:08:06 +0300 Subject: [PATCH 5/6] fix flake 8 warnings --- .../backend/service/tests/api/indigo_test.py | 38 ++++++++++++++++--- .../backend/service/v2/indigo_api.py | 13 ++----- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/utils/indigo-service/backend/service/tests/api/indigo_test.py b/utils/indigo-service/backend/service/tests/api/indigo_test.py index 4d41005419..bdc03f8f06 100644 --- a/utils/indigo-service/backend/service/tests/api/indigo_test.py +++ b/utils/indigo-service/backend/service/tests/api/indigo_test.py @@ -361,7 +361,37 @@ def test_aromatize_selected(self): def test_aromatize_selected_2(self): headers, data = self.get_headers( { - "struct": "\n Ketcher 10071619282D 1 1.00000 0.00000 0\n\n 13 13 0 0 0 999 V2000\n 0.0000 0.8660 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0\n 0.9996 0.8661 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n 1.4996 1.7321 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n 2.4996 1.7322 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n 2.9997 0.8661 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n 2.4997 0.0001 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n 1.4997 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n 5.8651 2.0001 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n 4.9990 1.5001 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n 4.9990 0.5001 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n 5.8650 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n 6.7311 0.5000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n 6.7311 1.5000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n 1 2 1 0 0 0\n 2 3 4 0 0 0\n 3 4 4 0 0 0\n 4 5 4 0 0 0\n 5 6 4 0 0 0\n 6 7 4 0 0 0\n 7 2 4 0 0 0\n 8 9 2 0 0 0\n 9 10 1 0 0 0\n 10 11 2 0 0 0\n 11 12 1 0 0 0\n 12 13 2 0 0 0\n 13 8 1 0 0 0\nM END\n", + "struct": "\n\ + Ketcher 10071619282D 1 1.00000 0.00000 0\n\ +\n\ + 13 13 0 0 0 999 V2000\n\ + 0.0000 0.8660 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0\n\ + 0.9996 0.8661 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n\ + 1.4996 1.7321 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n\ + 2.4996 1.7322 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n\ + 2.9997 0.8661 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n\ + 2.4997 0.0001 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n\ + 1.4997 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n\ + 5.8651 2.0001 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n\ + 4.9990 1.5001 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n\ + 4.9990 0.5001 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n\ + 5.8650 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n\ + 6.7311 0.5000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n\ + 6.7311 1.5000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n\ + 1 2 1 0 0 0\n\ + 2 3 4 0 0 0\n\ + 3 4 4 0 0 0\n\ + 4 5 4 0 0 0\n\ + 5 6 4 0 0 0\n\ + 6 7 4 0 0 0\n\ + 7 2 4 0 0 0\n\ + 8 9 2 0 0 0\n\ + 9 10 1 0 0 0\n\ + 10 11 2 0 0 0\n\ + 11 12 1 0 0 0\n\ + 12 13 2 0 0 0\n\ + 13 8 1 0 0 0\n\ +M END\n", "selected": [7, 8, 9, 10, 11, 12], "output_format": "chemical/x-daylight-smiles", "options": { @@ -621,7 +651,7 @@ def test_convert_smarts(self): "[#9,#17,#35,#53,#7&A&+,$([OH]-*=[!#6]),+;!#1]", ] results = [] - results_get = [] + # results_get = [] for mol in smarts: params = { "struct": mol, @@ -701,7 +731,7 @@ def test_convert_name_to_structure(self): "C1C=CC=CC=CC=CC=CC=1", ] results = [] - results_get = [] + # results_get = [] for name in names: params = { "struct": name, @@ -2021,8 +2051,6 @@ def test_check_reaction_queries(self): }, result_data, ) - # TODO: Uncomment when Ketcher supports JSON format - # self.assertEqual({'query': {'reactants': {'0': 'Query'}, 'products': {'1': 'Query'}}, 'valence': {'reactants': {'0': 'Structure contains query features, so valency could not be checked'}, 'products': {'1': 'Structure contains query features, so valency could not be checked'}}}, result_data) def test_check_atoms(self): headers, data = self.get_headers( diff --git a/utils/indigo-service/backend/service/v2/indigo_api.py b/utils/indigo-service/backend/service/v2/indigo_api.py index 7ae70972f3..f91537172f 100644 --- a/utils/indigo-service/backend/service/v2/indigo_api.py +++ b/utils/indigo-service/backend/service/v2/indigo_api.py @@ -93,9 +93,7 @@ class MolData: def is_rxn(molstr): return ( - ">>" in molstr - or molstr.startswith("$RXN") - or "" in molstr + ">>" in molstr or molstr.startswith("$RXN") or "" in molstr ) @@ -105,8 +103,7 @@ def qmol_to_mol(m, selected, indigo): atom.resetAtom("C") for bond in m.iterateBonds(): if not ( - bond.source().index() in selected - or bond.destination().index() in selected + bond.source().index() in selected or bond.destination().index() in selected ): m.removeBonds( [ @@ -438,11 +435,9 @@ def check_exceptions(f): @wraps(f) def wrapper(*args, **kwargs): json_output = ( - "Accept" in request.headers - and request.headers["Accept"] == "application/json" + "Accept" in request.headers and request.headers["Accept"] == "application/json" ) or ( - "Content-Type" in request.headers - and request.headers["Content-Type"] == "application/json" + "Content-Type" in request.headers and request.headers["Content-Type"] == "application/json" ) try: return f(*args, **kwargs) From 887e1b1e982ea3af0c01a9466b03d7d7a598326b Mon Sep 17 00:00:00 2001 From: Mikhail Kviatkovskii Date: Mon, 3 Oct 2022 20:36:01 +0400 Subject: [PATCH 6/6] fix --- .../backend/service/tests/api/indigo_test.py | 27 ++-- .../backend/service/v2/common/config.py | 2 +- .../backend/service/v2/db/database.py | 6 +- .../backend/service/v2/indigo_api.py | 124 +++++++++++++----- 4 files changed, 104 insertions(+), 55 deletions(-) diff --git a/utils/indigo-service/backend/service/tests/api/indigo_test.py b/utils/indigo-service/backend/service/tests/api/indigo_test.py index bdc03f8f06..e26033c2e8 100644 --- a/utils/indigo-service/backend/service/tests/api/indigo_test.py +++ b/utils/indigo-service/backend/service/tests/api/indigo_test.py @@ -1213,7 +1213,8 @@ def test_automap_wrong_header(self): self.assertEqual(400, result.status_code) result_data = json.loads(result.text) self.assertEqual( - "Must be one of: discard, alter, clear, keep.", "".join(result_data["error"]["mode"]) + "Must be one of: discard, alter, clear, keep.", + "".join(result_data["error"]["mode"]), ) def test_automap_wrong_reaction(self): @@ -1570,7 +1571,8 @@ def test_check(self): self.assertEqual(200, result.status_code) result_data = result.text self.assertEqual( - '{"valence":"Structure contains atoms with unusual valence: (0)"}', result_data + '{"valence":"Structure contains atoms with unusual valence: (0)"}', + result_data, ) def test_check_overlap(self): @@ -1651,7 +1653,8 @@ def test_check_stereo(self): self.assertEqual(200, result.status_code) result_data = json.loads(result.text) self.assertEqual( - "Structure contains stereocenters with undefined stereo configuration: (2,5)", result_data["stereo"] + "Structure contains stereocenters with undefined stereo configuration: (2,5)", + result_data["stereo"], ) # cis headers, data = self.get_headers( @@ -2088,11 +2091,7 @@ def test_json_calculate(self): self.assertLess(16, float(result_data["molecular-weight"])) def test_calculate(self): - headers, data = self.get_headers( - { - "struct": "C" - } - ) + headers, data = self.get_headers({"struct": "C"}) result = requests.post( self.url_prefix + "/calculate", headers=headers, data=data ) @@ -2855,9 +2854,7 @@ def test_stereo(self): self.url_prefix + "/check", headers=headers, data=data ) result_data = json.loads(result.text) - self.assertEqual( - {}, result_data - ) + self.assertEqual({}, result_data) def test_chiral(self): headers, data = self.get_headers( @@ -2963,9 +2960,7 @@ def test_chiral(self): self.url_prefix + "/check", headers=headers, data=data ) result_data = json.loads(result.text) - self.assertEqual( - "Structure contains chirality", result_data["chiral"] - ) + self.assertEqual("Structure contains chirality", result_data["chiral"]) headers, data = self.get_headers( { "struct": """ @@ -2994,9 +2989,7 @@ def test_chiral(self): self.url_prefix + "/check", headers=headers, data=data ) result_data = json.loads(result.text) - self.assertEqual( - {}, result_data - ) + self.assertEqual({}, result_data) if __name__ == "__main__": diff --git a/utils/indigo-service/backend/service/v2/common/config.py b/utils/indigo-service/backend/service/v2/common/config.py index b208e2248c..8602ad4089 100644 --- a/utils/indigo-service/backend/service/v2/common/config.py +++ b/utils/indigo-service/backend/service/v2/common/config.py @@ -6,7 +6,7 @@ "port": "5432", "database": "postgres", "user": "postgres", - "password": os.environ.get("POSTGRES_PASSWORD", "") + "password": os.environ.get("POSTGRES_PASSWORD", ""), } # Flask config diff --git a/utils/indigo-service/backend/service/v2/db/database.py b/utils/indigo-service/backend/service/v2/db/database.py index 2a7fdf17d1..09cadd9d1c 100644 --- a/utils/indigo-service/backend/service/v2/db/database.py +++ b/utils/indigo-service/backend/service/v2/db/database.py @@ -5,7 +5,7 @@ from ..common.config import BINGO_POSTGRES -HAS_BINGO_DB = len(BINGO_POSTGRES['password']) > 0 +HAS_BINGO_DB = len(BINGO_POSTGRES["password"]) > 0 def connect(): @@ -16,7 +16,9 @@ def connect(): if HAS_BINGO_DB: - engine = create_engine("postgresql://", creator=connect, convert_unicode=True) + engine = create_engine( + "postgresql://", creator=connect, convert_unicode=True + ) db_session = scoped_session( sessionmaker(autocommit=False, autoflush=False, bind=engine) diff --git a/utils/indigo-service/backend/service/v2/indigo_api.py b/utils/indigo-service/backend/service/v2/indigo_api.py index f91537172f..86797ccd69 100644 --- a/utils/indigo-service/backend/service/v2/indigo_api.py +++ b/utils/indigo-service/backend/service/v2/indigo_api.py @@ -93,7 +93,9 @@ class MolData: def is_rxn(molstr): return ( - ">>" in molstr or molstr.startswith("$RXN") or "" in molstr + ">>" in molstr + or molstr.startswith("$RXN") + or "" in molstr ) @@ -103,7 +105,8 @@ def qmol_to_mol(m, selected, indigo): atom.resetAtom("C") for bond in m.iterateBonds(): if not ( - bond.source().index() in selected or bond.destination().index() in selected + bond.source().index() in selected + or bond.destination().index() in selected ): m.removeBonds( [ @@ -236,7 +239,9 @@ def reaction_calc(rxn, func_name, precision=None): ) -def selected_molecule_calc(m, selected, func_name, precision=None, indigo=None): +def selected_molecule_calc( + m, selected, func_name, precision=None, indigo=None +): if m.dbgInternalType() == "#03: ": try: m = qmol_to_mol(m, selected, indigo) @@ -257,7 +262,9 @@ def selected_molecule_calc(m, selected, func_name, precision=None, indigo=None): return "; ".join(results) -def selected_reaction_calc(r, selected, func_name, precision=None, indigo=None): +def selected_reaction_calc( + r, selected, func_name, precision=None, indigo=None +): results = [] total_atoms_count = sum([m.countAtoms() for m in r.iterateMolecules()]) total_rgroups_count = sum([m.countRGroups() for m in r.iterateMolecules()]) @@ -366,9 +373,7 @@ def save_moldata(md, output_format=None, options={}, indigo=None): elif output_format == "chemical/x-inchi": return indigo.inchi.getInchi(md.struct) elif output_format == "chemical/x-inchi-key": - return indigo.inchi.getInchiKey( - indigo.inchi.getInchi(md.struct) - ) + return indigo.inchi.getInchiKey(indigo.inchi.getInchi(md.struct)) elif output_format == "chemical/x-inchi-aux": res = indigo.inchi.getInchi(md.struct) aux = indigo.inchi.getAuxInfo() @@ -435,9 +440,11 @@ def check_exceptions(f): @wraps(f) def wrapper(*args, **kwargs): json_output = ( - "Accept" in request.headers and request.headers["Accept"] == "application/json" + "Accept" in request.headers + and request.headers["Accept"] == "application/json" ) or ( - "Content-Type" in request.headers and request.headers["Content-Type"] == "application/json" + "Content-Type" in request.headers + and request.headers["Content-Type"] == "application/json" ) try: return f(*args, **kwargs) @@ -616,12 +623,19 @@ def aromatize(): indigo = indigo_init(data["options"]) md = load_moldata( - data["struct"], mime_type=data["input_format"], options=data["options"], indigo=indigo + data["struct"], + mime_type=data["input_format"], + options=data["options"], + indigo=indigo, ) md.struct.aromatize() return get_response( - md, data["output_format"], data["json_output"], data["options"], indigo=indigo + md, + data["output_format"], + data["json_output"], + data["options"], + indigo=indigo, ) @@ -688,7 +702,10 @@ def dearomatize(): indigo = indigo_init(data["options"]) md = load_moldata( - data["struct"], mime_type=data["input_format"], options=data["options"], indigo=indigo + data["struct"], + mime_type=data["input_format"], + options=data["options"], + indigo=indigo, ) if md.is_query: @@ -699,7 +716,11 @@ def dearomatize(): ) md.struct.dearomatize() return get_response( - md, data["output_format"], data["json_output"], data["options"], indigo=indigo + md, + data["output_format"], + data["json_output"], + data["options"], + indigo=indigo, ) @@ -772,7 +793,11 @@ def convert(): indigo=indigo, ) return get_response( - md, data["output_format"], data["json_output"], data["options"], indigo=indigo + md, + data["output_format"], + data["json_output"], + data["options"], + indigo=indigo, ) elif request.method == "GET": @@ -797,7 +822,7 @@ def convert(): data["struct"], mime_type=data["input_format"], options=data["options"], - indigo=indigo + indigo=indigo, ) if "json_output" in request.args: @@ -806,7 +831,11 @@ def convert(): data["json_output"] = False return get_response( - md, data["output_format"], data["json_output"], data["options"], indigo=indigo + md, + data["output_format"], + data["json_output"], + data["options"], + indigo=indigo, ) @@ -870,11 +899,18 @@ def layout(): ) indigo = indigo_init(data["options"]) md = load_moldata( - data["struct"], mime_type=data["input_format"], options=data["options"], indigo=indigo + data["struct"], + mime_type=data["input_format"], + options=data["options"], + indigo=indigo, ) md.struct.layout() return get_response( - md, data["output_format"], data["json_output"], data["options"], indigo=indigo + md, + data["output_format"], + data["json_output"], + data["options"], + indigo=indigo, ) @@ -956,7 +992,11 @@ def clean(): ) md.substruct.clean2d() return get_response( - md, data["output_format"], data["json_output"], data["options"], indigo=indigo + md, + data["output_format"], + data["json_output"], + data["options"], + indigo=indigo, ) @@ -1021,11 +1061,18 @@ def automap(): ) indigo = indigo_init(data["options"]) md = load_moldata( - data["struct"], mime_type=data["input_format"], options=data["options"], indigo=indigo + data["struct"], + mime_type=data["input_format"], + options=data["options"], + indigo=indigo, ) md.struct.automap(data["mode"]) return get_response( - md, data["output_format"], data["json_output"], data["options"], indigo=indigo + md, + data["output_format"], + data["json_output"], + data["options"], + indigo=indigo, ) @@ -1090,12 +1137,19 @@ def calculate_cip(): ) indigo = indigo_init(data["options"]) md = load_moldata( - data["struct"], mime_type=data["input_format"], options=data["options"], indigo=indigo + data["struct"], + mime_type=data["input_format"], + options=data["options"], + indigo=indigo, ) indigo.setOption("json-saving-add-stereo-desc", True) indigo.setOption("molfile-saving-add-stereo-desc", True) return get_response( - md, data["output_format"], data["json_output"], data["options"], indigo=indigo + md, + data["output_format"], + data["json_output"], + data["options"], + indigo=indigo, ) @@ -1227,13 +1281,13 @@ def calculate(): data["struct"], data["options"], ) - indigo = indigo_init(data['options']) + indigo = indigo_init(data["options"]) md = load_moldata( data["struct"], mime_type=data["input_format"], options=data["options"], selected=data["selected"], - indigo=indigo + indigo=indigo, ) if data["selected"]: if md.is_rxn: @@ -1258,7 +1312,7 @@ def calculate(): data["selected"], func_name_dict[p], precision=precision, - indigo=indigo + indigo=indigo, ) else: result[p] = reaction_calc( @@ -1271,7 +1325,7 @@ def calculate(): data["selected"], func_name_dict[p], precision=precision, - indigo=indigo + indigo=indigo, ) else: result[p] = molecule_calc( @@ -1373,33 +1427,33 @@ def render(): ) data = IndigoRendererSchema().load(input_dict) - indigo = indigo_init(data['options']) + indigo = indigo_init(data["options"]) if data["struct"] and not data["query"]: md = load_moldata( data["struct"], mime_type=data["input_format"], options=data["options"], - indigo=indigo + indigo=indigo, ) elif data["query"] and not data["struct"]: - md = load_moldata(data["query"], options=data["options"], indigo=indigo) + md = load_moldata( + data["query"], options=data["options"], indigo=indigo + ) else: md = load_moldata( data["struct"], mime_type=data["input_format"], options=data["options"], - indigo=indigo + indigo=indigo, ) mdq = load_moldata( data["query"], mime_type=data["input_format"], query=True, - indigo=indigo + indigo=indigo, ) try: - md.struct = highlight( - indigo, md.struct, mdq.struct - ) + md.struct = highlight(indigo, md.struct, mdq.struct) except RuntimeError: pass