import json import logging import os.path from django.conf import settings as s from django.http import HttpResponseNotAllowed from django.shortcuts import render from rdkit import Chem from rdkit.Chem.MolStandardize import rdMolStandardize from epdb.models import CompoundStructure, Rule, SimpleAmbitRule from epdb.views import get_base_context from utilities.chem import FormatConverter Package = s.GET_PACKAGE_MODEL() logger = logging.getLogger(__name__) def normalize_smiles(smiles): m1 = Chem.MolFromSmiles(smiles) if m1 is None: print("Couldnt read smi: ", smiles) return smiles Chem.RemoveStereochemistry(m1) # Normalizer takes care of charge/tautomer/resonance standardization normalizer = rdMolStandardize.Normalizer() return Chem.MolToSmiles(normalizer.normalize(m1), canonical=True) def run_both_engines(SMILES, SMIRKS): from envipy_ambit import apply ambit_res = apply(SMIRKS, SMILES) # ambit_res, ambit_errors = FormatConverter.sanitize_smiles([str(s) for s in ambit_res]) ambit_res = list( set( [ normalize_smiles(str(x)) for x in FormatConverter.sanitize_smiles([str(s) for s in ambit_res])[0] ] ) ) products = FormatConverter.apply(SMILES, SMIRKS) all_rdkit_prods = [] for ps in products: for p in ps: all_rdkit_prods.append(p) all_rdkit_prods = list(set(all_rdkit_prods)) # all_rdkit_res, rdkit_errors = FormatConverter.sanitize_smiles(all_rdkit_prods) all_rdkit_res = list( set( [ normalize_smiles(str(x)) for x in FormatConverter.sanitize_smiles([str(s) for s in all_rdkit_prods])[0] ] ) ) # return ambit_res, ambit_errors, all_rdkit_res, rdkit_errors return ambit_res, 0, all_rdkit_res, 0 def migration(request): if request.method == "GET": context = get_base_context(request) if ( os.path.exists(s.BASE_DIR / "fixtures" / "migration_status_per_rule.json") and request.GET.get("force") is None ): migration_status = json.load( open(s.BASE_DIR / "fixtures" / "migration_status_per_rule.json") ) else: BBD = Package.objects.get( url="http://localhost:8000/package/32de3cf4-e3e6-4168-956e-32fa5ddb0ce1" ) ALL_SMILES = [ cs.smiles for cs in CompoundStructure.objects.filter(compound__package=BBD) ] RULES = SimpleAmbitRule.objects.filter(package=BBD) results = list() num_rules = len(RULES) success = 0 error = 0 total = 0 for i, r in enumerate(RULES): logger.debug(f"\r{i + 1:03d}/{num_rules}") res = True for smiles in ALL_SMILES: try: ambit_res, _, rdkit_res, _ = run_both_engines(smiles, r.smirks) res &= set(ambit_res) == set(rdkit_res) except Exception as e: logger.error(e) results.append( { "name": r.name, "detail_url": s.SERVER_URL + "/migration/" + r.url.replace("https://envipath.org/", "").replace( "http://localhost:8000/", "" ), "id": str(r.uuid), "url": r.url, "status": res, } ) if res: success += 1 else: error += 1 total += 1 results = sorted(results, key=lambda x: (x["status"], x["name"])) migration_status = { "results": results, "success": success, "error": error, "total": total, } json.dump( migration_status, open(s.BASE_DIR / "fixtures" / "migration_status_per_rule.json", "w"), ) for r in migration_status["results"]: r["detail_url"] = r["detail_url"].replace("http://localhost:8000", s.SERVER_URL) context.update(**migration_status) return render(request, "migration.html", context) def migration_detail(request, package_uuid, rule_uuid): if request.method == "GET": context = get_base_context(request) BBD = Package.objects.get(name="EAWAG-BBD") STRUCTURES = CompoundStructure.objects.filter(compound__package=BBD) rule = Rule.objects.get(package=BBD, uuid=rule_uuid) bt_rule_name = rule.name smirks = rule.smirks res = True results = [] all_prods = set() for structure in STRUCTURES: ambit_smiles, ambit_errors, rdkit_smiles, rdkit_errors = run_both_engines( structure.smiles, smirks ) for x in ambit_smiles: all_prods.add(x) # TODO mode "intersection" # partial_res = (len(set(ambit_smiles).intersection(set(rdkit_smiles))) > 0) or (len(ambit_smiles) == 0) # FAILED (failures=18) # TODO mode = "full ambit" # partial_res = len(set(ambit_smiles).intersection(set(rdkit_smiles))) == len(set(ambit_smiles)) # FAILED (failures=34) # TODO mode = "equality" partial_res = set(ambit_smiles) == set(rdkit_smiles) # FAILED (failures=30) if len(ambit_smiles) or len(rdkit_smiles): temp = { "url": structure.url, "id": str(structure.uuid), "name": structure.name, "initial_smiles": structure.smiles, "ambit_smiles": sorted(list(ambit_smiles)), "rdkit_smiles": sorted(list(rdkit_smiles)), "status": set(ambit_smiles) == set(rdkit_smiles), } detail = f""" BT: {bt_rule_name} SMIRKS: {smirks} Compound: {structure.smiles} Compound URL: {structure.url} Num ambit: {len(set(ambit_smiles))} Num rdkit: {len(set(rdkit_smiles))} Num Intersection A: {len(set(ambit_smiles).intersection(set(rdkit_smiles)))} Num Intersection B: {len(set(rdkit_smiles).intersection(set(ambit_smiles)))} Difference A: {set(ambit_smiles).difference(set(rdkit_smiles))} Difference B: {set(rdkit_smiles).difference(set(ambit_smiles))} ambit products: {ambit_smiles} rdkit products: {rdkit_smiles} ambit_errors: {ambit_errors} rdkit_errors: {rdkit_errors} """ temp["detail"] = "\n".join([x.strip() for x in detail.split("\n")]) results.append(temp) res &= partial_res results = sorted(results, key=lambda x: x["status"]) context["results"] = results context["res"] = res context["bt_rule_name"] = bt_rule_name return render(request, "migration_detail.html", context) def compare(request): context = get_base_context(request) if request.method == "GET": context["smirks"] = ( "[#1,#6:6][#7;X3;!$(NC1CC1)!$([N][C]=O)!$([!#8]CNC=O):1]([#1,#6:7])[#6;A;X4:2][H:3]>>[#1,#6:6][#7;X3:1]([#1,#6:7])[H:3].[#6;A:2]=O" ) context["smiles"] = "C(CC(=O)N[C@@H](CS[Se-])C(=O)NCC(=O)[O-])[C@@H](C(=O)[O-])N" return render(request, "compare.html", context) elif request.method == "POST": smiles = request.POST.get("smiles") smirks = request.POST.get("smirks") from envipy_ambit import apply ambit_res = apply(smirks, smiles) ambit_res, _ = FormatConverter.sanitize_smiles([str(x) for x in ambit_res]) products = FormatConverter.apply(smiles, smirks) all_rdkit_prods = [] for ps in products: for p in ps: all_rdkit_prods.append(p) all_rdkit_prods = list(set(all_rdkit_prods)) rdkit_res, _ = FormatConverter.sanitize_smiles(all_rdkit_prods) context["result"] = True context["ambit_res"] = sorted(set(ambit_res)) context["rdkit_res"] = sorted(set(rdkit_res)) context["diff"] = sorted(set(ambit_res).difference(set(rdkit_res))) context["smirks"] = smirks context["smiles"] = smiles r = SimpleAmbitRule.objects.filter(smirks=smirks) if r.exists(): context["rule"] = r.first() return render(request, "compare.html", context) else: return HttpResponseNotAllowed(["GET", "POST"])