Files
enviPy-bayer/epdb/management/commands/reaction_rule_mapping.py

98 lines
3.5 KiB
Python

import logging
from django.core.management.base import BaseCommand
from django.db import transaction
from uuid import uuid4
from epdb.models import Package, ReactionExplanation
from utilities.chem import FormatConverter
from django.utils import timezone
logger = logging.getLogger(__name__)
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument(
"--rule-package",
action="append",
default=["32de3cf4-e3e6-4168-956e-32fa5ddb0ce1"],
type=str,
help="UUID to process. Can be specified multiple times.",
)
parser.add_argument(
"--reaction-package",
action="append",
default=[
"32de3cf4-e3e6-4168-956e-32fa5ddb0ce1", # BBD
"f05e38d8-e9b4-4c3e-b0d8-9ab29966eccf", # Sediment
"521c547a-fd2a-491c-ad5b-7eaa1577fb65", # Sludge
"5882df9c-dae1-4d80-a40e-db4724271456", # Soil
"87a49584-d937-482c-9c33-25928dcb02a8", # PFAS
],
type=str,
help="UUID to process. Can be specified multiple times.",
)
parser.add_argument(
"--dry-run",
default=False,
action="store_true",
help="Perform dry run",
)
@transaction.atomic
def handle(self, *args, **options):
RUN_UUID = uuid4()
RUN_START = timezone.now()
rule_packages = Package.objects.filter(uuid__in=options["rule_package"])
reaction_packages = Package.objects.filter(uuid__in=options["reaction_package"])
rules = []
for rule_package in rule_packages:
rules.extend(rule_package.get_applicable_rules())
reactions = []
for reaction_package in reaction_packages:
reactions.extend(reaction_package.reactions)
logger.debug(f"Collected {len(rules)} rules and {len(reactions)} reactions.")
for i, reaction in enumerate(reactions):
logger.debug(f"Reaction {i} / {len(reactions)}")
for j, rule in enumerate(rules):
reactants, products = reaction.smirks().split(">>")
if len(reactants.split(".")) > 1:
logger.debug(f"Skipping reaction {reaction.uuid} as it has multiple reactants.")
break
products = products.split(".")
# Run reaction with rule
rule_products = rule.apply(reactants)
# Check if products match (in both directions if extras are not allowed)
for product_set in rule_products:
covered, exact = FormatConverter.smiles_covered_by(
products,
product_set.product_set,
standardize=True,
canonicalize_tautomers=True,
return_exact_match=True,
)
if covered and not options["dry-run"]:
logger.debug(f"Reaction {reaction.uuid} explained by rule {rule.uuid}")
re = ReactionExplanation()
re.run_uuid = RUN_UUID
re.run_start = RUN_START
re.reaction = reaction
re.rule = rule
re.exact = exact
re.save()
# Its explained, if there are more sets skip them
break