from uuid import UUID from epdb.logic import PackageManager from epdb.models import Pathway from epapi.v1.errors import EPAPINotFoundError, EPAPIPermissionDeniedError from .dto import ( PathwayCompoundDTO, PathwayEdgeDTO, PathwayExportDTO, PathwayModelInfoDTO, PathwayNodeDTO, PathwayScenarioDTO, ) def get_pathway_for_iuclid_export(user, pathway_uuid: UUID) -> PathwayExportDTO: """Return pathway data projected into DTOs for the IUCLID export consumer.""" try: pathway = ( Pathway.objects.select_related("package", "setting", "setting__model") .prefetch_related( "node_set__default_node_label__compound__external_identifiers__database", "node_set__scenarios", "edge_set__start_nodes__default_node_label__compound", "edge_set__end_nodes__default_node_label__compound", ) .get(uuid=pathway_uuid) ) except Pathway.DoesNotExist: raise EPAPINotFoundError(f"Pathway with UUID {pathway_uuid} not found") if not user or user.is_anonymous or not PackageManager.readable(user, pathway.package): raise EPAPIPermissionDeniedError("Insufficient permissions to access this pathway.") nodes: list[PathwayNodeDTO] = [] edges: list[PathwayEdgeDTO] = [] compounds_by_pk: dict[int, PathwayCompoundDTO] = {} root_compound_pks: list[int] = [] for node in pathway.node_set.all().order_by("depth", "pk"): cs = node.default_node_label if cs is None: continue compound = cs.compound cas_number = None ec_number = None for ext_id in compound.external_identifiers.all(): db_name = ext_id.database.name if ext_id.database else None if db_name == "CAS" and cas_number is None: cas_number = ext_id.identifier_value elif db_name == "EC" and ec_number is None: ec_number = ext_id.identifier_value ai_for_node = [] scenario_entries: list[PathwayScenarioDTO] = [] for scenario in sorted(node.scenarios.all(), key=lambda item: item.pk): ai_for_scenario = list(scenario.get_additional_information(direct_only=True)) ai_for_node.extend(ai_for_scenario) scenario_entries.append( PathwayScenarioDTO( scenario_uuid=scenario.uuid, name=scenario.name, additional_info=ai_for_scenario, ) ) nodes.append( PathwayNodeDTO( node_uuid=node.uuid, compound_pk=compound.pk, name=compound.name, depth=node.depth, smiles=cs.smiles, cas_number=cas_number, ec_number=ec_number, additional_info=ai_for_node, scenarios=scenario_entries, ) ) if node.depth == 0 and compound.pk not in root_compound_pks: root_compound_pks.append(compound.pk) if compound.pk not in compounds_by_pk: compounds_by_pk[compound.pk] = PathwayCompoundDTO( pk=compound.pk, name=compound.name, smiles=cs.smiles, cas_number=cas_number, ec_number=ec_number, ) for edge in pathway.edge_set.all(): start_compounds = { n.default_node_label.compound.pk for n in edge.start_nodes.all() if n.default_node_label is not None } end_compounds = { n.default_node_label.compound.pk for n in edge.end_nodes.all() if n.default_node_label is not None } probability = None if edge.kv and edge.kv.get("probability") is not None: try: probability = float(edge.kv.get("probability")) except (TypeError, ValueError): probability = None edges.append( PathwayEdgeDTO( edge_uuid=edge.uuid, start_compound_pks=sorted(start_compounds), end_compound_pks=sorted(end_compounds), probability=probability, ) ) model_info = None if pathway.setting and pathway.setting.model: model = pathway.setting.model model_info = PathwayModelInfoDTO( model_name=model.get_name(), model_uuid=model.uuid, software_name="enviPath", software_version=None, ) return PathwayExportDTO( pathway_uuid=pathway.uuid, pathway_name=pathway.get_name(), compounds=list(compounds_by_pk.values()), nodes=nodes, edges=edges, root_compound_pks=root_compound_pks, model_info=model_info, )