[Fix] Adjust Rules according to manual QA, Add missing Rule (#452)

Co-authored-by: Tim Lorsbach <tim@lorsba.ch>
Reviewed-on: enviPath/enviPy#452
This commit is contained in:
2026-08-13 17:37:38 +12:00
parent ada270aa3c
commit 2d3dca6a75
5 changed files with 140 additions and 17 deletions

View File

@ -0,0 +1,113 @@
# Generated by Django 6.0.3 on 2026-08-12 09:02
from django.conf import settings as s
from django.db import migrations
from envipy_additional_information import Likelihood, RuleLikelihood
NEW_RULE = {
"parent": "bt0005",
"name": "bt0005-3667",
"description": "vic-unsubstituted Aromatic > vic-Dihydroxyaromatic",
"smirks": "[#8:7]([H])-[#6:1]([H])-1-[#6:2]=[#6:3]-[#6:4]=[#6:5]-[#6:6]([H])-1-[#8:8]([H])>>[#8:7]([H])-[#6:1]=1-[#6:2]=[#6:3]-[#6:4]=[#6:5]-[#6:6]=1-[#8:8]([H])",
"scenario_name": "bt0005-3667 aerobic likelihood",
"scenario_aerobic_likelihood": RuleLikelihood(likelihood=Likelihood.LIKELY),
}
RULE_FIXES = {
"bt0005-4282": "[c:1]([H])1:[c:2]([H]):[#6,#7;a:3]:[c:4]:[c:5]:[c:6]1>>[c:1]([#8])1:[c:2]([#8]):[#6,#7;a:3]:[c:4]:[c:5]:[c:6]1",
"bt0014-4215": "[c:1]([H])1[c:8][#6,#7;a:7][c:6][c:5][c:4]1[#8;!$([OH]c:[#6,#7;a:7]([OH])):9]([H])>>[#8:9]([H])[c:4]1:[c:5]:[c:6]:[#6,#7;a:7]:[c:8]:[c:1]1[#8]([H])",
# "bt0063-3938": "[#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]([H:3])(=[#1,#6:7]).[#6;A:2]=O",
# CN1C=NC2=C1C(=O)N(C)C(=O)N2 not working anymore with bt0063-3938 if change above is applied
"bt0063-3938": "[#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",
"bt0068-3564": "[#7:4]!@-[#6:2](!@-[#7:1])=[O:5]>>[#7:4]-[#6:2](-[O+0H1])=[O:5].[#7H1:1]",
"bt0180-2844": "[H][C:2]([#6:5]([H])([H])([H]))([#1,#6:4])!@-[#6:1]([H])([H])-[#6:3](-[#8-:8])=[O:6]>>[#6:5]([H])([H])([H])\\[#6:2](-[#1,#6:4])=[#6H:1]\\[#6:3](-[#8-:8])=[O:6]",
"bt0181-1278": "[#8-:1]-[#6:2](=[O:11])-[#6:7]=[#6:8]-[#6:3](-[H])=[#6:5](-Cl)-[#6:6](-[#8-:10])=[O:9]>>[O+0H1:10]-[#6:6](=[O:9])-[#6:5]=[#6:3]-1-[O+0:1]-[#6:2](=[O:11])-[#6:7]=[#6:8]-1",
"bt0298-3335": "[#6:1][N+:2]#[C:3]>>[#6:1]-[#7H2:2]-[#6:3]=O",
"bt0322-3393": "[H:10]\\[#6:6](=[#6:9](/[#6:1]([H])([H])([H]))-[#6:11]-[#6:12]-[#6:13]=[#6:14])-[#6:5](-[#16:7])=[O:8]>>[H:10]\\[#6:6](-[#6:5](-[#16:7])=[O:8])=[#6:9](\\[#6:11]-[#6:12]-[#6:13]=[#6:14])-[#6:1]-[#6](-[#8-])=O",
"bt0343-2675": "[#8-]-[#6](=O)-[c:1]1[c:6][cH:7][c:8](-[#7H2,#8H1:9])[cH:10][c:11]1>>[#8H][c:1]1[c:6][c:7][c:8]([*:9])[c:10][c:11]1",
"bt0350-3319": "[#6:6][#7:3][#6;!R:2]=[#7;!R:1][#6:5]>>[#6:5][#7:1][#6:2]=O.[#6:6][#7:3]", # Trig before 5 -> all of them shouldn't
"bt0374-4081": "[cH:4]1[c:16][c:15][c:14][c:13][c:3]1[#7,#8:2][c:1]1[c:8][c:9][c:10][c:11][c:12]1>>[#7,#8:2]-[c:1]1[c:12][c:11][c:10][c:9][c:8]1[c:13]1[c:14][c:15][c:16][c:4](-[#8])[c:3]1-[#8]",
"bt0378-3188": "[#8-:7][c:1]1[c:6]([#7+]([#8-])=O)[c:5][c:4]([#7+:9]([#8-])=O)[c:3][c:2]1([#7+:8]([#8-])=O)>>[#8+0:7]=[#6:1]1-[#6:6]-[#6:5]-[#6:4]([#7+:9]([#8-])=O)-[#6:3]-[#6:2]1([#7+:8]([#8-])=O)",
"bt0379-3190": "[#9,#17,#35,#53]-[#6:1](-[H])-1-[#6:5]-,=[#6:6]-[#6:7]-,=[#6:8]-[#6:2](-[H])-1-[#9,#17,#35,#53]>>[#6:6]~1-[#6:7]~[#6:8]-[#6:2]=[#6:1]-[#6:5]~1",
"bt0393-3367": "[#6:5]-[#6:1](-[#7:2](-[H])(-[H]))=[S+:3]-[#8-:6]>>[#6:5]-[#6:1](=[#7H1:2])-[S+0:3](=[O])-[#8+0H1:6]",
}
def forward_func(apps, schema_editor):
ContentType = apps.get_model("contenttypes", "ContentType")
pkg_class = s.EPDB_PACKAGE_MODEL
if len(pkg_class.split(".")) != 2:
raise ValueError(
f"EPDB_PACKAGE_MODEL must be of the form 'app_label.model_name', got {pkg_class}"
)
app_label, model_name = pkg_class.split(".")
Package = apps.get_model(app_label, model_name)
SimpleAmbitRule = apps.get_model("epdb", "SimpleAmbitRule")
ParallelRule = apps.get_model("epdb", "ParallelRule")
Scenario = apps.get_model("epdb", "Scenario")
AdditionalInformation = apps.get_model("epdb", "AdditionalInformation")
simple_ambit_rule_ct = ContentType.objects.get_for_model(SimpleAmbitRule)
if Package.objects.filter(name="EAWAG-BBD").exists():
p = Package.objects.get(name="EAWAG-BBD")
if not SimpleAmbitRule.objects.filter(package=p, name=NEW_RULE["name"]).exists():
# Create Missing Rule
new_sr = SimpleAmbitRule()
new_sr.polymorphic_ctype = simple_ambit_rule_ct
new_sr.package = p
new_sr.name = NEW_RULE["name"]
new_sr.description = NEW_RULE["description"]
new_sr.smirks = NEW_RULE["smirks"]
new_sr.save()
new_sr.url = "{}/simple-ambit-rule/{}".format(new_sr.package.url, new_sr.uuid)
new_sr.save()
# Add likelihood
new_scen = Scenario()
new_scen.package = p
new_scen.name = NEW_RULE["scenario_name"]
new_scen.save()
new_scen.url = "{}/scenario/{}".format(new_scen.package.url, new_scen.uuid)
new_scen.save()
ai = NEW_RULE["scenario_aerobic_likelihood"]
new_add_inf = AdditionalInformation()
new_add_inf.package = p
new_add_inf.type = ai.__class__.__name__
new_add_inf.data = ai.model_dump(mode="json")
new_add_inf.scenario = new_scen
new_add_inf.save()
new_add_inf.url = "{}/additional-information/{}".format(
new_add_inf.scenario.url, new_add_inf.uuid
)
new_add_inf.save()
# Link Scenario
new_sr.scenarios.add(new_scen)
# Link to bt0005
pr = ParallelRule.objects.get(package=p, name="bt0005")
pr.simple_rules.add(new_sr)
# Update others
for rule_name, smirks in RULE_FIXES.items():
sr = SimpleAmbitRule.objects.get(package=p, name=rule_name)
sr.smirks = smirks
sr.save()
class Migration(migrations.Migration):
dependencies = [
("epdb", "0027_alter_compound_aliases_and_more"),
]
operations = [
migrations.RunPython(forward_func, reverse_code=migrations.RunPython.noop),
]