forked from enviPath/enviPy
Compare commits
1 Commits
57f5ce089c
...
d51f842203
| Author | SHA1 | Date | |
|---|---|---|---|
| d51f842203 |
@ -1698,7 +1698,7 @@ class PathwayNode(Schema):
|
||||
image: str = Field(None, alias="image")
|
||||
imageSize: int = Field(None, alias="image_size")
|
||||
name: str = Field(None, alias="name")
|
||||
proposed: List[Dict[str, str]] = Field([], alias="proposed_intermediate")
|
||||
proposed: List[Dict[str, str]] = []
|
||||
smiles: str = Field(None, alias="smiles")
|
||||
pseudo: bool = Field(False, alias="pseudo")
|
||||
pesLink: str | None = Field(None, alias="pes_link")
|
||||
|
||||
@ -904,6 +904,7 @@ class Compound(
|
||||
# Check if we can find the standardized one
|
||||
if qs.exists():
|
||||
# TODO should we add a structure?
|
||||
# ->
|
||||
found_structure = qs.first()
|
||||
found_compound = found_structure.compound
|
||||
|
||||
@ -1994,6 +1995,7 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMix
|
||||
# add links start -> pseudo
|
||||
new_link = {
|
||||
"name": link["name"],
|
||||
"plain_name": link["plain_name"],
|
||||
"id": link["id"],
|
||||
"url": link["url"],
|
||||
"image": link["image"],
|
||||
@ -2003,6 +2005,7 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMix
|
||||
"source": node_url_to_idx[link["start_node_urls"][0]],
|
||||
"target": pseudo_idx,
|
||||
"app_domain": link.get("app_domain", None),
|
||||
"to_pseudo": True,
|
||||
}
|
||||
adjusted_links.append(new_link)
|
||||
|
||||
@ -2010,6 +2013,7 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMix
|
||||
for target in link["end_node_urls"]:
|
||||
new_link = {
|
||||
"name": link["name"],
|
||||
"plain_name": link["plain_name"],
|
||||
"id": link["id"],
|
||||
"url": link["url"],
|
||||
"image": link["image"],
|
||||
@ -2020,6 +2024,7 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMix
|
||||
"target": node_url_to_idx[target],
|
||||
"app_domain": link.get("app_domain", None),
|
||||
"multi_step": link["multi_step"],
|
||||
"from_pseudo": True,
|
||||
}
|
||||
adjusted_links.append(new_link)
|
||||
|
||||
@ -2329,17 +2334,19 @@ class Node(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMixin)
|
||||
def _url(self):
|
||||
return "{}/node/{}".format(self.pathway.url, self.uuid)
|
||||
|
||||
def get_name(self):
|
||||
def get_name(self, include_suffix=True):
|
||||
non_generic_name = True
|
||||
|
||||
if self.name == "no name":
|
||||
non_generic_name = False
|
||||
|
||||
return (
|
||||
self.name
|
||||
if non_generic_name
|
||||
else f"{self.default_node_label.name} (taken from underlying structure)"
|
||||
)
|
||||
if non_generic_name:
|
||||
return self.name
|
||||
else:
|
||||
if include_suffix:
|
||||
f"{self.default_node_label.name} (taken from underlying structure)"
|
||||
else:
|
||||
return self.default_node_label.name
|
||||
|
||||
def d3_json(self):
|
||||
app_domain_data = self.get_app_domain_assessment_data()
|
||||
@ -2369,6 +2376,7 @@ class Node(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMixin)
|
||||
),
|
||||
"image_type": "svg",
|
||||
"name": self.get_name(),
|
||||
"plain_name": self.get_name(include_suffix=False),
|
||||
"smiles": self.default_node_label.smiles,
|
||||
"scenarios": [{"name": s.get_name(), "url": s.url} for s in self.scenarios.all()],
|
||||
"app_domain": {
|
||||
@ -2502,6 +2510,7 @@ class Edge(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMixin)
|
||||
def d3_json(self):
|
||||
edge_json = {
|
||||
"name": self.get_name(),
|
||||
"plain_name": self.get_name(include_suffix=False),
|
||||
"id": self.url,
|
||||
"url": self.url,
|
||||
"image": self.url + "?image=svg",
|
||||
@ -2616,17 +2625,19 @@ class Edge(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMixin)
|
||||
|
||||
return res
|
||||
|
||||
def get_name(self):
|
||||
def get_name(self, include_suffix=True):
|
||||
non_generic_name = True
|
||||
|
||||
if self.name == "no name":
|
||||
non_generic_name = False
|
||||
|
||||
return (
|
||||
self.name
|
||||
if non_generic_name
|
||||
else f"{self.edge_label.name} (taken from underlying reaction)"
|
||||
)
|
||||
if non_generic_name:
|
||||
return self.name
|
||||
else:
|
||||
if include_suffix:
|
||||
return f"{self.edge_label.name} (taken from underlying reaction)"
|
||||
else:
|
||||
return self.edge_label.name
|
||||
|
||||
|
||||
class EPModel(PolymorphicModel, EnviPathModel, AdditionalInformationMixin):
|
||||
|
||||
@ -2121,12 +2121,14 @@ def package_pathways(request, package_uuid):
|
||||
else:
|
||||
prediction_setting = current_user.prediction_settings()
|
||||
|
||||
is_predict_mode = pw_mode in {"predict", "incremental"}
|
||||
|
||||
pw = Pathway.create(
|
||||
current_package,
|
||||
stand_smiles,
|
||||
stand_smiles if is_predict_mode else smiles,
|
||||
name=name,
|
||||
description=description,
|
||||
predicted=pw_mode in {"predict", "incremental"},
|
||||
predicted=is_predict_mode,
|
||||
)
|
||||
|
||||
# set mode
|
||||
|
||||
@ -495,7 +495,7 @@ function draw(pathway, elem) {
|
||||
if (n.stereo_removed) {
|
||||
popupContent += "<span class='alert alert-warning alert-soft'>Removed stereochemistry for prediction</span>";
|
||||
}
|
||||
popupContent += "<a href='" + n.url + "'>" + n.name + "</a><br>";
|
||||
popupContent += "<a href='" + n.url + "'>" + n.plain_name + "</a><br>";
|
||||
popupContent += "Depth " + n.depth + "<br>"
|
||||
|
||||
if (appDomainViewEnabled) {
|
||||
@ -535,6 +535,22 @@ function draw(pathway, elem) {
|
||||
popupContent += '<b>Half-lives and related scenarios:</b><br>'
|
||||
for (var s of n.scenarios) {
|
||||
popupContent += "<a href='" + s.url + "'>" + s.name + "</a><br>";
|
||||
for (var prop in n.proposed) {
|
||||
if (n.proposed[prop].scenarioId === s.url) {
|
||||
if("proposed" in n.proposed[prop]){
|
||||
popupContent += "This compound is a proposed intermediate" + "<br>";
|
||||
}
|
||||
if("Confidence" in n.proposed[prop]){
|
||||
popupContent += "Confidence: " + n.proposed[prop]["Confidence"] + "<br>";
|
||||
|
||||
}
|
||||
if("Transformation product importance" in n.proposed[prop]){
|
||||
popupContent += "Transformation product importance: " + n.proposed[prop]["Transformation product importance"] + "<br>";
|
||||
}
|
||||
}
|
||||
}
|
||||
popupContent += "<br>";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -547,7 +563,7 @@ function draw(pathway, elem) {
|
||||
}
|
||||
|
||||
function edge_popup(e) {
|
||||
popupContent = "<a href='" + e.url + "'>" + e.name + "</a><br><br>";
|
||||
popupContent = "<a href='" + e.url + "'>" + e.plain_name + "</a><br><br>";
|
||||
|
||||
if (e.reaction.rules) {
|
||||
for (var rule of e.reaction.rules) {
|
||||
@ -598,7 +614,7 @@ function draw(pathway, elem) {
|
||||
|
||||
// Add background rectangle FIRST to enable pan/zoom on empty space
|
||||
// This must be inserted before zoomable group so it's behind everything
|
||||
svg.insert("rect", "#zoomable")
|
||||
const rect = svg.insert("rect", "#zoomable")
|
||||
.attr("x", 0)
|
||||
.attr("y", 0)
|
||||
.attr("width", width)
|
||||
@ -607,6 +623,31 @@ function draw(pathway, elem) {
|
||||
.attr("pointer-events", "all")
|
||||
.style("cursor", "grab");
|
||||
|
||||
// Click on empty SVG space (not on a node or link)
|
||||
rect.on("click", function(event) {
|
||||
// Only treat it as a background click if the direct target is the rect itself
|
||||
// (clicks on nodes/links bubble up but originate from different elements)
|
||||
const target = event.target;
|
||||
const isNode = target.closest && (
|
||||
target.closest("circle") !== null ||
|
||||
target.closest("image") !== null ||
|
||||
target.closest(".node") !== null
|
||||
);
|
||||
const isLink = target.closest && (
|
||||
target.closest("path.link") !== null ||
|
||||
target.closest("path.link_no_arrow") !== null
|
||||
);
|
||||
|
||||
if (!isNode && !isLink) {
|
||||
// console.log("Clicked outside a node or link");
|
||||
// Clear all highlights
|
||||
d3.selectAll("circle").classed("highlighted", false);
|
||||
d3.selectAll("path").classed("highlighted", false);
|
||||
d3.selectAll("path").classed("inedge", false);
|
||||
d3.selectAll("path").classed("outedge", false);
|
||||
}
|
||||
});
|
||||
|
||||
// Zoom Funktion aktivieren
|
||||
const zoom = d3.zoom()
|
||||
.scaleExtent([0.5, 5])
|
||||
@ -705,6 +746,20 @@ function draw(pathway, elem) {
|
||||
}
|
||||
});
|
||||
|
||||
function reaction_name(d) {
|
||||
const to_pseudo = d?.to_pseudo || false;
|
||||
|
||||
if (to_pseudo) {
|
||||
return "";
|
||||
}
|
||||
|
||||
var suffix = "";
|
||||
if ("reaction_probability" in d) {
|
||||
suffix = " (p= " + d["reaction_probability"].toFixed(2) + ")"
|
||||
}
|
||||
return d.plain_name + suffix;
|
||||
}
|
||||
|
||||
const linkText = linkGroup
|
||||
.append("text")
|
||||
.attr("class", "link-label")
|
||||
@ -713,7 +768,7 @@ function draw(pathway, elem) {
|
||||
.style("font-size", "4px")
|
||||
.style("pointer-events", "none")
|
||||
.style("display", "none")
|
||||
.text(d => d.name)
|
||||
.text(d => reaction_name(d))
|
||||
.attr("x", d => (d.source.x + d.target.x) / 2)
|
||||
.attr("y", d => (d.source.y + d.target.y) / 2);
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
.link_no_arrow {
|
||||
stroke: #999;
|
||||
stroke-opacity: 0.6;
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
|
||||
.node image {
|
||||
|
||||
Reference in New Issue
Block a user