forked from enviPath/enviPy
Compare commits
1 Commits
421d33dddc
...
1d2bb85c90
| Author | SHA1 | Date | |
|---|---|---|---|
| 1d2bb85c90 |
@ -8,3 +8,4 @@ class InvalidMolfileException(Exception):
|
||||
|
||||
class PackageImportException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
@ -2412,12 +2412,17 @@ class NonPersistent(Schema):
|
||||
@router.post("/util", response={200: Any, 403: Error})
|
||||
def predict(request, np: Form[NonPersistent]):
|
||||
try:
|
||||
print(request.user)
|
||||
print(np.setting_url)
|
||||
setting = SettingManager.get_setting_by_url(request.user, np.setting_url)
|
||||
|
||||
from epdb.logic import SPathway
|
||||
|
||||
setting = SettingManager.get_setting_by_url(request.user, np.setting_url)
|
||||
spw = SPathway(prediction_setting=setting, root_nodes=[np.smiles])
|
||||
spw.predict()
|
||||
|
||||
return spw.to_json()
|
||||
|
||||
except ValueError:
|
||||
return 403, {
|
||||
"message": f"Getting Setting with id {np.setting_url} failed due to insufficient rights!"
|
||||
|
||||
@ -1834,7 +1834,7 @@ class Reaction(
|
||||
r = Reaction()
|
||||
r.package = package
|
||||
|
||||
if name is not None:
|
||||
if r is not None:
|
||||
r.name = name
|
||||
|
||||
if description is not None and description.strip() != "":
|
||||
@ -2414,14 +2414,14 @@ class Node(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMixin)
|
||||
def get_name(self, include_suffix=True):
|
||||
non_generic_name = True
|
||||
|
||||
if self.name is None or self.name == "no name":
|
||||
if self.name == "no name":
|
||||
non_generic_name = False
|
||||
|
||||
if non_generic_name:
|
||||
return self.name
|
||||
else:
|
||||
if include_suffix:
|
||||
return f"{self.default_node_label.name} (taken from underlying structure)"
|
||||
f"{self.default_node_label.name} (taken from underlying structure)"
|
||||
else:
|
||||
return self.default_node_label.name
|
||||
|
||||
@ -2556,8 +2556,8 @@ class Node(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMixin)
|
||||
def is_proposed_intermediate(self):
|
||||
collected = defaultdict(dict)
|
||||
for ai in self.additional_information.filter(
|
||||
type__in=["ProposedIntermediate", "TransformationProductImportance", "Confidence"],
|
||||
scenario__isnull=False,
|
||||
type__in=["ProposedIntermediate", "TransformationProductImportance", "Confidence"]
|
||||
, scenario__isnull=False
|
||||
):
|
||||
collected[str(ai.scenario.uuid)]["scenarioId"] = ai.scenario.url
|
||||
collected[str(ai.scenario.uuid)]["scenarioName"] = ai.scenario.name
|
||||
@ -2569,9 +2569,7 @@ class Node(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMixin)
|
||||
collected[str(ai.scenario.uuid)]["Confidence"] = ai.get().level
|
||||
|
||||
if ai.type == "TransformationProductImportance":
|
||||
collected[str(ai.scenario.uuid)]["Transformation product importance"] = (
|
||||
ai.get().importance.value
|
||||
)
|
||||
collected[str(ai.scenario.uuid)]["Transformation product importance"] = ai.get().importance.value
|
||||
|
||||
return list(collected.values())
|
||||
|
||||
|
||||
@ -2506,17 +2506,13 @@ def package_pathway_node(request, package_uuid, pathway_uuid, node_uuid):
|
||||
current_node.name = new_node_name
|
||||
|
||||
if new_node_description is not None and new_node_description.strip() != "":
|
||||
new_node_description = nh3.clean(
|
||||
new_node_description.strip(), tags=s.ALLOWED_HTML_TAGS
|
||||
).strip()
|
||||
new_node_description = nh3.clean(new_node_description.strip(), tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
current_node.description = new_node_description
|
||||
|
||||
current_node.save()
|
||||
|
||||
return redirect(current_node.url)
|
||||
|
||||
return error(request, "Node update failed!", "No changes were made to the node")
|
||||
|
||||
else:
|
||||
return HttpResponseNotAllowed(["GET", "POST"])
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
1
fixtures/Fixture_Package.json
Normal file
1
fixtures/Fixture_Package.json
Normal file
File diff suppressed because one or more lines are too long
@ -122,8 +122,7 @@ document.addEventListener('alpine:init', () => {
|
||||
return this.items.slice(start, end);
|
||||
}
|
||||
|
||||
})
|
||||
);
|
||||
}));
|
||||
|
||||
Alpine.data('remotePaginatedList', (options = {}) => ({
|
||||
items: [],
|
||||
|
||||
@ -161,7 +161,6 @@ function draw(pathway, elem) {
|
||||
linkText
|
||||
.attr("x", d => (d.source.x + d.target.x) / 2)
|
||||
.attr("y", d => (d.source.y + d.target.y) / 2);
|
||||
|
||||
}
|
||||
|
||||
function dragstarted(event, d) {
|
||||
@ -543,6 +542,7 @@ function draw(pathway, elem) {
|
||||
}
|
||||
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>";
|
||||
@ -623,7 +623,7 @@ function draw(pathway, elem) {
|
||||
.attr("pointer-events", "all")
|
||||
.style("cursor", "grab");
|
||||
|
||||
// Click on empty SVG space (not on a node or link)
|
||||
// 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)
|
||||
@ -637,6 +637,7 @@ function draw(pathway, elem) {
|
||||
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
|
||||
@ -693,7 +694,7 @@ function draw(pathway, elem) {
|
||||
.force("center", d3.forceCenter(width / 2, height / 4))
|
||||
.on("tick", ticked);
|
||||
|
||||
// Draw Edges
|
||||
// Kanten zeichnen
|
||||
const linkGroup = zoomable.append("g")
|
||||
.selectAll("g")
|
||||
.data(links)
|
||||
@ -753,7 +754,7 @@ function draw(pathway, elem) {
|
||||
}
|
||||
|
||||
var suffix = "";
|
||||
if ("reaction_probability" in d && d["reaction_probability"] !== null) {
|
||||
if ("reaction_probability" in d) {
|
||||
suffix = " (p= " + d["reaction_probability"].toFixed(2) + ")"
|
||||
}
|
||||
return d.plain_name + suffix;
|
||||
@ -778,7 +779,7 @@ function draw(pathway, elem) {
|
||||
|
||||
pop_add(link, "Reaction", edge_popup);
|
||||
|
||||
// Draw Nodes
|
||||
// Knoten zeichnen
|
||||
const node = zoomable.append("g")
|
||||
.selectAll("g")
|
||||
.data(nodes)
|
||||
|
||||
@ -63,6 +63,7 @@
|
||||
<div class="collapse-content">{{ node.description }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% epdb_slot_templates "epdb.objects.node.viz" as viz_templates %}
|
||||
|
||||
{% for tpl in viz_templates %}
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
stroke: #999;
|
||||
stroke-opacity: 0.6;
|
||||
stroke-width: 1.5px;
|
||||
/* marker-end: url(#arrow); */
|
||||
}
|
||||
|
||||
.link_no_arrow {
|
||||
@ -181,46 +182,46 @@
|
||||
<li>
|
||||
<a id="compound-names-toggle-button" class="cursor-pointer">
|
||||
<svg
|
||||
id="compound-names-icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-eye"
|
||||
>
|
||||
<path
|
||||
d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"
|
||||
/>
|
||||
<circle cx="12" cy="12" r="3" />
|
||||
</svg>
|
||||
id="compound-names-icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-eye"
|
||||
>
|
||||
<path
|
||||
d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"
|
||||
/>
|
||||
<circle cx="12" cy="12" r="3" />
|
||||
</svg>
|
||||
Compound Names
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="reaction-names-toggle-button" class="cursor-pointer">
|
||||
<svg
|
||||
id="reaction-names-icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-eye"
|
||||
>
|
||||
<path
|
||||
d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"
|
||||
/>
|
||||
<circle cx="12" cy="12" r="3" />
|
||||
</svg>
|
||||
id="reaction-names-icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-eye"
|
||||
>
|
||||
<path
|
||||
d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"
|
||||
/>
|
||||
<circle cx="12" cy="12" r="3" />
|
||||
</svg>
|
||||
Reaction Names
|
||||
</a>
|
||||
</li>
|
||||
@ -583,22 +584,22 @@
|
||||
descContent.innerHTML = newDesc;
|
||||
}
|
||||
|
||||
const compoundNamesBtn = document.getElementById(
|
||||
"compound-names-toggle-button",
|
||||
);
|
||||
const compoundNamesBtn = document.getElementById("compound-names-toggle-button");
|
||||
if (compoundNamesBtn) {
|
||||
compoundNamesBtn.addEventListener("click", function () {
|
||||
compoundNamesViewEnabled = !compoundNamesViewEnabled;
|
||||
const icon = document.getElementById("compound-names-icon");
|
||||
|
||||
if (compoundNamesViewEnabled) {
|
||||
// Change to eye-off icon
|
||||
icon.innerHTML =
|
||||
'<path d="M9.88 9.88a3 3 0 1 0 4.24 4.24"/><path d="M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68"/><path d="M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61"/><line x1="2" x2="22" y1="2" y2="22"/>';
|
||||
|
||||
nodes.forEach((x) => {
|
||||
if (x.name) {
|
||||
d3.select(x.el)
|
||||
.append("text")
|
||||
.text((d) => d.plain_name)
|
||||
.text(d => d.name)
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("y", -20)
|
||||
.style("font-size", "4px");
|
||||
@ -608,29 +609,38 @@
|
||||
// Change back to eye icon
|
||||
icon.innerHTML =
|
||||
'<path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/>';
|
||||
|
||||
nodes.forEach((x) => {
|
||||
d3.select(x.el).select("text").remove();
|
||||
});
|
||||
d3.select(x.el)
|
||||
.select("text")
|
||||
.remove()
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
const reactionNamesBtn = document.getElementById(
|
||||
"reaction-names-toggle-button",
|
||||
);
|
||||
|
||||
const reactionNamesBtn = document.getElementById("reaction-names-toggle-button");
|
||||
if (reactionNamesBtn) {
|
||||
reactionNamesBtn.addEventListener("click", function () {
|
||||
reactionNamesViewEnabled = !reactionNamesViewEnabled;
|
||||
const icon = document.getElementById("reaction-names-icon");
|
||||
|
||||
if (reactionNamesViewEnabled) {
|
||||
// Change to eye-off icon
|
||||
icon.innerHTML =
|
||||
'<path d="M9.88 9.88a3 3 0 1 0 4.24 4.24"/><path d="M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68"/><path d="M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61"/><line x1="2" x2="22" y1="2" y2="22"/>';
|
||||
d3.selectAll(".link-label").style("display", null);
|
||||
|
||||
d3.selectAll(".link-label")
|
||||
.style("display", null);
|
||||
|
||||
} else {
|
||||
// Change back to eye icon
|
||||
icon.innerHTML =
|
||||
'<path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/>';
|
||||
d3.selectAll(".link-label").style("display", "none");
|
||||
|
||||
d3.selectAll(".link-label")
|
||||
.style("display", "none");
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -72,10 +72,8 @@ class PackageViewTest(TestCase):
|
||||
|
||||
def test_import_package(self):
|
||||
file = SimpleUploadedFile(
|
||||
"EAWAG-BBD_32de3cf4-e3e6-4168-956e-32fa5ddb0ce1.json",
|
||||
open(
|
||||
s.FIXTURE_DIRS[0] / "EAWAG-BBD_32de3cf4-e3e6-4168-956e-32fa5ddb0ce1.json", "rb"
|
||||
).read(),
|
||||
"Fixture_Package.json",
|
||||
open(s.FIXTURE_DIRS[0] / "Fixture_Package.json", "rb").read(),
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
@ -88,11 +86,10 @@ class PackageViewTest(TestCase):
|
||||
|
||||
p = Package.objects.get(url=package_url)
|
||||
|
||||
self.assertEqual(p.pathways.count(), 219)
|
||||
self.assertEqual(p.rules.count(), 498)
|
||||
self.assertEqual(p.compounds.count(), 1399)
|
||||
self.assertEqual(p.reactions.count(), 1480)
|
||||
self.assertEqual(p.scenarios.count(), 1914)
|
||||
self.assertEqual(p.pathways.count(), 22)
|
||||
self.assertEqual(p.rules.count(), 45)
|
||||
self.assertEqual(p.compounds.count(), 223)
|
||||
self.assertEqual(p.reactions.count(), 212)
|
||||
|
||||
upp = UserPackagePermission.objects.get(package=p, user=self.user1)
|
||||
self.assertEqual(upp.permission, Permission.ALL[0])
|
||||
|
||||
@ -9,6 +9,7 @@ from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional, TYPE_CHECKING
|
||||
|
||||
from django.conf import settings as s
|
||||
from django.db import transaction
|
||||
from ninja import Schema
|
||||
from pydantic import HttpUrl, ValidationError
|
||||
|
||||
@ -19,15 +20,23 @@ from epdb.models import (
|
||||
Compound,
|
||||
CompoundStructure,
|
||||
Edge,
|
||||
EnviFormer,
|
||||
EPModel,
|
||||
ExternalDatabase,
|
||||
ExternalIdentifier,
|
||||
License,
|
||||
MLRelativeReasoning,
|
||||
Node,
|
||||
ParallelRule,
|
||||
Pathway,
|
||||
Reaction,
|
||||
Rule,
|
||||
RuleBasedRelativeReasoning,
|
||||
Scenario,
|
||||
Setting,
|
||||
SimpleAmbitRule,
|
||||
SimpleRDKitRule,
|
||||
SimpleRule, Group,
|
||||
)
|
||||
from utilities.chem import FormatConverter
|
||||
|
||||
@ -344,6 +353,7 @@ class PackageExporter:
|
||||
|
||||
|
||||
class PackageImporter:
|
||||
|
||||
def __init__(self, package: Dict[str, Any], preserve_uuids: bool = False):
|
||||
self.preserve_uuids = preserve_uuids
|
||||
self._raw_package = package
|
||||
@ -402,7 +412,11 @@ class PackageImporter:
|
||||
new_c.default_structure = self._cache[c.default_structure.uuid]
|
||||
new_c.save()
|
||||
|
||||
def _import_simple_rules(self, package: Package, rules: List[RuleExportSchema]):
|
||||
def _import_simple_rules(
|
||||
self,
|
||||
package: Package,
|
||||
rules: List[RuleExportSchema]
|
||||
):
|
||||
for r in rules:
|
||||
new_r = SimpleAmbitRule()
|
||||
new_r.uuid = str(uuid.uuid4()) if not self.preserve_uuids else r.uuid
|
||||
@ -417,7 +431,11 @@ class PackageImporter:
|
||||
|
||||
self._cache[r.uuid] = new_r
|
||||
|
||||
def _import_composite_rules(self, package: Package, rules: List[ParallelRuleExportSchema]):
|
||||
def _import_composite_rules(
|
||||
self,
|
||||
package: Package,
|
||||
rules: List[ParallelRuleExportSchema]
|
||||
):
|
||||
for r in rules:
|
||||
new_r = ParallelRule()
|
||||
new_r.uuid = str(uuid.uuid4()) if not self.preserve_uuids else r.uuid
|
||||
@ -433,9 +451,9 @@ class PackageImporter:
|
||||
self._cache[r.uuid] = new_r
|
||||
|
||||
def _import_reactions(
|
||||
self,
|
||||
package: Package,
|
||||
reactions: List[ReactionExportSchema],
|
||||
self,
|
||||
package: Package,
|
||||
reactions: List[ReactionExportSchema],
|
||||
):
|
||||
for r in reactions:
|
||||
new_r = Reaction()
|
||||
@ -459,7 +477,11 @@ class PackageImporter:
|
||||
|
||||
self._cache[r.uuid] = new_r
|
||||
|
||||
def _import_pathways(self, package: Package, pathways: List[PathwayExportSchema]):
|
||||
def _import_pathways(
|
||||
self,
|
||||
package: Package,
|
||||
pathways: List[PathwayExportSchema]
|
||||
):
|
||||
for pw in pathways:
|
||||
new_pw = Pathway()
|
||||
new_pw.uuid = str(uuid.uuid4()) if not self.preserve_uuids else pw.uuid
|
||||
@ -507,21 +529,27 @@ class PackageImporter:
|
||||
|
||||
self._cache[e.uuid] = new_e
|
||||
|
||||
def _import_scenarios(self, package: Package, scenarios: List[ScenarioExportSchema]):
|
||||
for scen in scenarios:
|
||||
def _import_scenarios(
|
||||
self,
|
||||
package: Package,
|
||||
scenarios: List[ScenarioExportSchema]
|
||||
):
|
||||
for s in scenarios:
|
||||
new_s = Scenario()
|
||||
new_s.uuid = str(uuid.uuid4()) if not self.preserve_uuids else scen.uuid
|
||||
new_s.uuid = str(uuid.uuid4()) if not self.preserve_uuids else s.uuid
|
||||
new_s.package = package
|
||||
new_s.name = scen.name
|
||||
new_s.description = scen.description
|
||||
new_s.scenario_date = scen.scenario_date
|
||||
new_s.scenario_type = scen.scenario_type
|
||||
new_s.name = s.name
|
||||
new_s.description = s.description
|
||||
new_s.scenario_date = s.scenario_date
|
||||
new_s.scenario_type = s.scenario_type
|
||||
new_s.save()
|
||||
|
||||
self._cache[scen.uuid] = new_s
|
||||
self._cache[s.uuid] = new_s
|
||||
|
||||
def _import_additional_information(
|
||||
self, package: Package, additional_information: List[AdditionalInformationExportSchema]
|
||||
self,
|
||||
package: Package,
|
||||
additional_information: List[AdditionalInformationExportSchema]
|
||||
):
|
||||
for ai in additional_information:
|
||||
new_ai = AdditionalInformation()
|
||||
@ -554,11 +582,12 @@ class PackageImporter:
|
||||
for coll in collections:
|
||||
for elem in coll:
|
||||
elem_obj = self._cache[elem.uuid]
|
||||
for scen in elem.scenarios:
|
||||
elem_obj.scenarios.add(self._cache[scen.uuid])
|
||||
for s in elem.scenarios:
|
||||
elem_obj.scenarios.add(self._cache[s.uuid])
|
||||
|
||||
|
||||
def _import_package_from_json(
|
||||
self,
|
||||
self,
|
||||
) -> Package:
|
||||
try:
|
||||
parsed = PackageExportSchema.model_validate(self._raw_package)
|
||||
@ -573,6 +602,8 @@ class PackageImporter:
|
||||
package_license = License.objects.get(link=parsed.license.link) if parsed.license else None
|
||||
package_classification_level = Package.Classification[parsed.classification_level.upper()]
|
||||
|
||||
print(parsed.classification_level, "->", package_classification_level)
|
||||
|
||||
package = Package()
|
||||
package.uuid = package_uuid
|
||||
package.name = f"{parsed.name} - Imported at {datetime.now()}"
|
||||
|
||||
Reference in New Issue
Block a user