20 Commits

Author SHA1 Message Date
57f5ce089c Make links thicker, reduce timeout trigger time
Some checks failed
CI / test (pull_request) Failing after 20s
API CI / api-tests (pull_request) Failing after 38s
2026-07-09 20:39:48 +02:00
28e5dbbcf5 Unconnected Nodes 2026-07-09 20:30:27 +02:00
8620f98082 Implemented Compound Names / Reaction Names View Option 2026-07-09 20:18:28 +02:00
4ee37b5c71 provide setting link and copy button 2026-07-09 13:55:10 +02:00
c65be30b6b Fix alter name/desc for node, make /node /edge funcitonal 2026-07-09 13:33:53 +02:00
b5e3c1957b underline pes link in templates 2026-07-08 20:24:53 +02:00
3bb9220b22 Fix Classification import, Fix PES Deserialization 2026-07-08 20:20:36 +02:00
d06ed33196 removed debug 2026-07-08 10:46:45 +02:00
ea672ec91a PES Fields in API Output 2026-07-08 10:46:27 +02:00
dfc1309fd3 Added pesLink field to PW Api output 2026-07-08 10:39:01 +02:00
6e2dbccd6c Harmonised proposed field in Json output 2026-07-08 10:32:05 +02:00
b77fbc9b90 implemented non persistent 2026-07-06 15:44:23 +02:00
ce1d709715 highlight Description links
Some checks failed
CI / test (pull_request) Failing after 18s
API CI / api-tests (pull_request) Failing after 36s
2026-07-06 15:17:38 +02:00
b0004ea66e Package Export/Import cycle 2026-07-06 15:17:16 +02:00
2fbffb4b3b Fixed molfile assignment, adjusted Export 2026-07-03 09:40:47 +02:00
365af14448 Fix PES Link HTML for other entities 2026-07-03 09:29:53 +02:00
8017070de8 Fixed error return, removed unused options 2026-07-02 22:57:30 +02:00
a0ac79a897 Return proper http response instead of error
Some checks failed
API CI / api-tests (pull_request) Failing after 18s
CI / test (pull_request) Failing after 33s
2026-07-02 21:37:19 +02:00
caeec2ab79 Make PES Link clickable
Some checks failed
API CI / api-tests (pull_request) Failing after 17s
CI / test (pull_request) Failing after 37s
2026-07-02 21:25:57 +02:00
3525621281 adjusted migration
Initial bayer app

Show Pack Classification

Adjusted docker compose to bayer specifics

Adjusted Dockerfile for Bayer

Adding secret flags to group, add secret pools to packages

Adjusted View for Package creation

Prep configs, added Package Create Modal

wip

More on PES

wip

wip

Wip

minor

PW interactions

API PES

wip

Make Select Widget reflect required

make required generallay available

Update UI if pathway mode is set to build

Added ais

circle adjustments

Initial Zoom, fix AD Creation

wip

auth log, bb4g fix

missing import

Added viz hint if PES is part of reaction

Add Edge check for pes

flip boolean

...

pes

Added extra

...

In / Out Edges Viz, Submitting Button Text

...
2026-07-02 21:25:57 +02:00
5 changed files with 19 additions and 88 deletions

View File

@ -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]] = []
proposed: List[Dict[str, str]] = Field([], alias="proposed_intermediate")
smiles: str = Field(None, alias="smiles")
pseudo: bool = Field(False, alias="pseudo")
pesLink: str | None = Field(None, alias="pes_link")

View File

@ -904,7 +904,6 @@ 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
@ -1995,7 +1994,6 @@ 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"],
@ -2005,7 +2003,6 @@ 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)
@ -2013,7 +2010,6 @@ 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"],
@ -2024,7 +2020,6 @@ 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)
@ -2334,19 +2329,17 @@ class Node(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMixin)
def _url(self):
return "{}/node/{}".format(self.pathway.url, self.uuid)
def get_name(self, include_suffix=True):
def get_name(self):
non_generic_name = True
if self.name == "no name":
non_generic_name = False
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
return (
self.name
if non_generic_name
else f"{self.default_node_label.name} (taken from underlying structure)"
)
def d3_json(self):
app_domain_data = self.get_app_domain_assessment_data()
@ -2376,7 +2369,6 @@ 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": {
@ -2510,7 +2502,6 @@ 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",
@ -2625,19 +2616,17 @@ class Edge(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMixin)
return res
def get_name(self, include_suffix=True):
def get_name(self):
non_generic_name = True
if self.name == "no name":
non_generic_name = False
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
return (
self.name
if non_generic_name
else f"{self.edge_label.name} (taken from underlying reaction)"
)
class EPModel(PolymorphicModel, EnviPathModel, AdditionalInformationMixin):

View File

@ -2121,14 +2121,12 @@ 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 if is_predict_mode else smiles,
stand_smiles,
name=name,
description=description,
predicted=is_predict_mode,
predicted=pw_mode in {"predict", "incremental"},
)
# set mode

View File

@ -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.plain_name + "</a><br>";
popupContent += "<a href='" + n.url + "'>" + n.name + "</a><br>";
popupContent += "Depth " + n.depth + "<br>"
if (appDomainViewEnabled) {
@ -535,22 +535,6 @@ 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>";
}
}
@ -563,7 +547,7 @@ function draw(pathway, elem) {
}
function edge_popup(e) {
popupContent = "<a href='" + e.url + "'>" + e.plain_name + "</a><br><br>";
popupContent = "<a href='" + e.url + "'>" + e.name + "</a><br><br>";
if (e.reaction.rules) {
for (var rule of e.reaction.rules) {
@ -614,7 +598,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
const rect = svg.insert("rect", "#zoomable")
svg.insert("rect", "#zoomable")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
@ -623,31 +607,6 @@ 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])
@ -746,20 +705,6 @@ 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")
@ -768,7 +713,7 @@ function draw(pathway, elem) {
.style("font-size", "4px")
.style("pointer-events", "none")
.style("display", "none")
.text(d => reaction_name(d))
.text(d => d.name)
.attr("x", d => (d.source.x + d.target.x) / 2)
.attr("y", d => (d.source.y + d.target.y) / 2);

View File

@ -28,7 +28,6 @@
.link_no_arrow {
stroke: #999;
stroke-opacity: 0.6;
stroke-width: 1.5px;
}
.node image {