forked from enviPath/enviPy
Compare commits
12 Commits
ce1d709715
...
develop-ba
| Author | SHA1 | Date | |
|---|---|---|---|
| 57f5ce089c | |||
| 28e5dbbcf5 | |||
| 8620f98082 | |||
| 4ee37b5c71 | |||
| c65be30b6b | |||
| b5e3c1957b | |||
| 3bb9220b22 | |||
| d06ed33196 | |||
| ea672ec91a | |||
| dfc1309fd3 | |||
| 6e2dbccd6c | |||
| b77fbc9b90 |
@ -4,7 +4,9 @@
|
|||||||
<input type="checkbox" checked />
|
<input type="checkbox" checked />
|
||||||
<div class="collapse-title text-xl font-medium">Link to PES</div>
|
<div class="collapse-title text-xl font-medium">Link to PES</div>
|
||||||
<div class="collapse-content">
|
<div class="collapse-content">
|
||||||
<a href="{{ compound_structure.pes_link }}" class="hover:bg-base-200">{{ compound_structure.pes_link }}</a>
|
<p>
|
||||||
|
<a href="{{ compound_structure.pes_link }}" class="hover:bg-base-200">{{ compound_structure.pes_link }}</a>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,9 @@
|
|||||||
<input type="checkbox" checked />
|
<input type="checkbox" checked />
|
||||||
<div class="collapse-title text-xl font-medium">Link to PES</div>
|
<div class="collapse-title text-xl font-medium">Link to PES</div>
|
||||||
<div class="collapse-content">
|
<div class="collapse-content">
|
||||||
<a href="{{ compound.default_structure.pes_link }}" class="hover:bg-base-200">{{ compound.default_structure.pes_link }}</a>
|
<p>
|
||||||
|
<a href="{{ compound.default_structure.pes_link }}" class="hover:bg-base-200">{{ compound.default_structure.pes_link }}</a>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,9 @@
|
|||||||
<input type="checkbox" checked />
|
<input type="checkbox" checked />
|
||||||
<div class="collapse-title text-xl font-medium">Link to PES</div>
|
<div class="collapse-title text-xl font-medium">Link to PES</div>
|
||||||
<div class="collapse-content">
|
<div class="collapse-content">
|
||||||
<a href="{{ node.default_node_label.pes_link }}" class="hover:bg-base-200">{{ node.default_node_label.pes_link }}</a>
|
<p>
|
||||||
|
<a href="{{ node.default_node_label.pes_link }}" class="hover:bg-base-200">{{ node.default_node_label.pes_link }}</a>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -633,9 +633,14 @@ class CompoundSchema(Schema):
|
|||||||
reviewStatus: str = Field(False, alias="review_status")
|
reviewStatus: str = Field(False, alias="review_status")
|
||||||
scenarios: List["SimpleScenario"] = Field([], alias="scenarios")
|
scenarios: List["SimpleScenario"] = Field([], alias="scenarios")
|
||||||
structures: List["CompoundStructureSchema"] = []
|
structures: List["CompoundStructureSchema"] = []
|
||||||
|
pesLink: str | None = Field(None, alias="pes_link")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def resolve_review_status(obj: CompoundStructure):
|
def resolve_pes_link(obj: Compound):
|
||||||
|
return getattr(obj.default_structure, "pes_link", None)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_review_status(obj: Compound):
|
||||||
return "reviewed" if obj.package.reviewed else "unreviewed"
|
return "reviewed" if obj.package.reviewed else "unreviewed"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -709,6 +714,7 @@ class CompoundStructureSchema(Schema):
|
|||||||
reviewStatus: str = Field(None, alias="review_status")
|
reviewStatus: str = Field(None, alias="review_status")
|
||||||
scenarios: List["SimpleScenario"] = Field([], alias="scenarios")
|
scenarios: List["SimpleScenario"] = Field([], alias="scenarios")
|
||||||
smiles: str = Field(None, alias="smiles")
|
smiles: str = Field(None, alias="smiles")
|
||||||
|
pesLink: str | None = Field(None, alias="pes_link")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def resolve_review_status(obj: CompoundStructure):
|
def resolve_review_status(obj: CompoundStructure):
|
||||||
@ -1695,6 +1701,7 @@ class PathwayNode(Schema):
|
|||||||
proposed: List[Dict[str, str]] = Field([], alias="proposed_intermediate")
|
proposed: List[Dict[str, str]] = Field([], alias="proposed_intermediate")
|
||||||
smiles: str = Field(None, alias="smiles")
|
smiles: str = Field(None, alias="smiles")
|
||||||
pseudo: bool = Field(False, alias="pseudo")
|
pseudo: bool = Field(False, alias="pseudo")
|
||||||
|
pesLink: str | None = Field(None, alias="pes_link")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def resolve_atom_count(obj: Node):
|
def resolve_atom_count(obj: Node):
|
||||||
@ -1707,24 +1714,10 @@ class PathwayNode(Schema):
|
|||||||
# TODO
|
# TODO
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def resolve_engineered_intermediate(obj: Node):
|
|
||||||
# TODO
|
|
||||||
return False
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def resolve_image(obj: Node):
|
|
||||||
return f"{obj.default_node_label.url}?image=svg"
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def resolve_image_size(obj: Node):
|
def resolve_image_size(obj: Node):
|
||||||
return 400
|
return 400
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def resolve_proposed_intermediate(obj: Node):
|
|
||||||
# TODO
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
class PathwaySchema(Schema):
|
class PathwaySchema(Schema):
|
||||||
aliases: List[str] = Field([], alias="aliases")
|
aliases: List[str] = Field([], alias="aliases")
|
||||||
@ -2392,3 +2385,31 @@ def get_setting(request, setting_uuid):
|
|||||||
return 403, {
|
return 403, {
|
||||||
"message": f"Getting Setting with id {setting_uuid} failed due to insufficient rights!"
|
"message": f"Getting Setting with id {setting_uuid} failed due to insufficient rights!"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
########
|
||||||
|
# Util #
|
||||||
|
########
|
||||||
|
class NonPersistent(Schema):
|
||||||
|
smiles: str
|
||||||
|
setting_url: str = Field(..., alias="settingUri")
|
||||||
|
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
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!"
|
||||||
|
}
|
||||||
|
|||||||
@ -1896,6 +1896,12 @@ class SPathway(object):
|
|||||||
"to": to_indices,
|
"to": to_indices,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if edge.rule:
|
||||||
|
e["rule"] = edge.rule.simple_json()
|
||||||
|
|
||||||
|
if edge.probability:
|
||||||
|
e["probability"] = edge.probability
|
||||||
|
|
||||||
edges.append(e)
|
edges.append(e)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -2379,6 +2379,7 @@ class Node(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMixin)
|
|||||||
},
|
},
|
||||||
"predicted_properties": predicted_properties,
|
"predicted_properties": predicted_properties,
|
||||||
"is_engineered_intermediate": self.kv.get("is_engineered_intermediate", False),
|
"is_engineered_intermediate": self.kv.get("is_engineered_intermediate", False),
|
||||||
|
"proposed": self.is_proposed_intermediate(),
|
||||||
"timeseries": self.get_timeseries_data(),
|
"timeseries": self.get_timeseries_data(),
|
||||||
**structure_data,
|
**structure_data,
|
||||||
}
|
}
|
||||||
@ -2452,6 +2453,26 @@ class Node(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMixin)
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def is_proposed_intermediate(self):
|
||||||
|
collected = defaultdict(dict)
|
||||||
|
for ai in self.additional_information.filter(
|
||||||
|
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
|
||||||
|
|
||||||
|
if ai.type == "ProposedIntermediate":
|
||||||
|
collected[str(ai.scenario.uuid)]["proposed"] = True
|
||||||
|
|
||||||
|
if ai.type == "Confidence":
|
||||||
|
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
|
||||||
|
|
||||||
|
return list(collected.values())
|
||||||
|
|
||||||
def simple_json(self, include_description=False):
|
def simple_json(self, include_description=False):
|
||||||
res = super().simple_json()
|
res = super().simple_json()
|
||||||
name = res.get("name", None)
|
name = res.get("name", None)
|
||||||
|
|||||||
@ -2482,7 +2482,22 @@ def package_pathway_node(request, package_uuid, pathway_uuid, node_uuid):
|
|||||||
|
|
||||||
return JsonResponse({"success": current_node.url})
|
return JsonResponse({"success": current_node.url})
|
||||||
|
|
||||||
return HttpResponseBadRequest()
|
new_node_name = request.POST.get("node-name")
|
||||||
|
new_node_description = request.POST.get("node-description")
|
||||||
|
|
||||||
|
if any([new_node_name, new_node_description]):
|
||||||
|
if new_node_name is not None and new_node_name.strip() != "":
|
||||||
|
new_node_name = nh3.clean(new_node_name.strip(), tags=s.ALLOWED_HTML_TAGS).strip()
|
||||||
|
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()
|
||||||
|
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:
|
else:
|
||||||
return HttpResponseNotAllowed(["GET", "POST"])
|
return HttpResponseNotAllowed(["GET", "POST"])
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,125 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
document.addEventListener('alpine:init', () => {
|
document.addEventListener('alpine:init', () => {
|
||||||
|
|
||||||
|
const basePagination = (
|
||||||
|
items,
|
||||||
|
currentPage,
|
||||||
|
totalPages,
|
||||||
|
totalItems,
|
||||||
|
perPage,
|
||||||
|
isReviewed,
|
||||||
|
instanceId
|
||||||
|
) => ({
|
||||||
|
items,
|
||||||
|
currentPage,
|
||||||
|
totalPages,
|
||||||
|
totalItems,
|
||||||
|
perPage,
|
||||||
|
isReviewed,
|
||||||
|
instanceId,
|
||||||
|
|
||||||
|
get paginatedItems() {
|
||||||
|
return this.items;
|
||||||
|
},
|
||||||
|
|
||||||
|
get showingStart() {
|
||||||
|
if (this.totalItems === 0) return 0;
|
||||||
|
return (this.currentPage - 1) * this.perPage + 1;
|
||||||
|
},
|
||||||
|
|
||||||
|
get showingEnd() {
|
||||||
|
if (this.totalItems === 0) return 0;
|
||||||
|
return Math.min((this.currentPage - 1) * this.perPage + this.items.length, this.totalItems);
|
||||||
|
},
|
||||||
|
|
||||||
|
nextPage() {
|
||||||
|
if (this.currentPage < this.totalPages) {
|
||||||
|
this.fetchPage(this.currentPage + 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
prevPage() {
|
||||||
|
if (this.currentPage > 1) {
|
||||||
|
this.fetchPage(this.currentPage - 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
goToPage(page) {
|
||||||
|
if (page >= 1 && page <= this.totalPages) {
|
||||||
|
this.fetchPage(page);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get pageNumbers() {
|
||||||
|
const pages = [];
|
||||||
|
const total = this.totalPages;
|
||||||
|
const current = this.currentPage;
|
||||||
|
|
||||||
|
if (total === 0) {
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total <= 7) {
|
||||||
|
for (let i = 1; i <= total; i++) {
|
||||||
|
pages.push({ page: i, isEllipsis: false, key: `${this.instanceId}-page-${i}` });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pages.push({ page: 1, isEllipsis: false, key: `${this.instanceId}-page-1` });
|
||||||
|
|
||||||
|
let rangeStart;
|
||||||
|
let rangeEnd;
|
||||||
|
|
||||||
|
if (current <= 4) {
|
||||||
|
rangeStart = 2;
|
||||||
|
rangeEnd = 5;
|
||||||
|
} else if (current >= total - 3) {
|
||||||
|
rangeStart = total - 4;
|
||||||
|
rangeEnd = total - 1;
|
||||||
|
} else {
|
||||||
|
rangeStart = current - 1;
|
||||||
|
rangeEnd = current + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rangeStart > 2) {
|
||||||
|
pages.push({ page: '...', isEllipsis: true, key: `${this.instanceId}-ellipsis-start` });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = rangeStart; i <= rangeEnd; i++) {
|
||||||
|
pages.push({ page: i, isEllipsis: false, key: `${this.instanceId}-page-${i}` });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rangeEnd < total - 1) {
|
||||||
|
pages.push({ page: '...', isEllipsis: true, key: `${this.instanceId}-ellipsis-end` });
|
||||||
|
}
|
||||||
|
|
||||||
|
pages.push({ page: total, isEllipsis: false, key: `${this.instanceId}-page-${total}` });
|
||||||
|
}
|
||||||
|
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Alpine.data('paginatedList',
|
||||||
|
(items, options = {}) => ({
|
||||||
|
...basePagination(items,1, 0, 0, options.perPage || 50, options.isReviewed || false, options.instanceId || Math.random().toString(36).substring(2, 9),),
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.fetchPage(1);
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetchPage(page) {
|
||||||
|
this.totalItems = this.items.length;
|
||||||
|
this.totalPages = Math.ceil(this.items.length / this.perPage);
|
||||||
|
this.currentPage = page
|
||||||
|
|
||||||
|
const start = page * this.perPage - this.perPage;
|
||||||
|
const end = page * this.perPage;
|
||||||
|
return this.items.slice(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
Alpine.data('remotePaginatedList', (options = {}) => ({
|
Alpine.data('remotePaginatedList', (options = {}) => ({
|
||||||
items: [],
|
items: [],
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
|
|||||||
@ -67,6 +67,9 @@ function draw(pathway, elem) {
|
|||||||
const horizontalSpacing = 75; // horizontal space between nodes
|
const horizontalSpacing = 75; // horizontal space between nodes
|
||||||
const depthMap = new Map();
|
const depthMap = new Map();
|
||||||
|
|
||||||
|
// Avoid leaving unconnected Nodes leaving the viewport
|
||||||
|
nodes.forEach(node => {if (node.depth < 0) node.depth = 0;});
|
||||||
|
|
||||||
// Sort nodes by depth first to minimize crossings
|
// Sort nodes by depth first to minimize crossings
|
||||||
const sortedNodes = [...nodes].sort((a, b) => a.depth - b.depth);
|
const sortedNodes = [...nodes].sort((a, b) => a.depth - b.depth);
|
||||||
|
|
||||||
@ -154,6 +157,10 @@ function draw(pathway, elem) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
node.attr("transform", d => `translate(${d.x},${d.y})`);
|
node.attr("transform", d => `translate(${d.x},${d.y})`);
|
||||||
|
|
||||||
|
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) {
|
function dragstarted(event, d) {
|
||||||
@ -256,7 +263,7 @@ function draw(pathway, elem) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wait before showing popup (ms)
|
// Wait before showing popup (ms)
|
||||||
var popupWaitBeforeShow = 1000;
|
var popupWaitBeforeShow = 500;
|
||||||
|
|
||||||
// Custom popover element
|
// Custom popover element
|
||||||
let popoverTimeout = null;
|
let popoverTimeout = null;
|
||||||
@ -647,21 +654,31 @@ function draw(pathway, elem) {
|
|||||||
.on("tick", ticked);
|
.on("tick", ticked);
|
||||||
|
|
||||||
// Kanten zeichnen
|
// Kanten zeichnen
|
||||||
const link = zoomable.append("g")
|
const linkGroup = zoomable.append("g")
|
||||||
.selectAll("path")
|
.selectAll("g")
|
||||||
.data(links)
|
.data(links)
|
||||||
.enter().append("path")
|
.enter()
|
||||||
// Check if target is pseudo and draw marker only if not pseudo
|
.append("g")
|
||||||
.attr("class", d => d.target.pseudo ? "link_no_arrow" : "link")
|
.attr("class", "link-group");
|
||||||
.attr("marker-end", d => {
|
|
||||||
if (d.target.pseudo) return '';
|
const link = linkGroup
|
||||||
if (d.source.id === d.target.id) return 'url(#curve-arrow)'; // Use curve arrow for self-loops
|
.append("path")
|
||||||
return d.multi_step ? 'url(#doublearrow)' : 'url(#arrow)';
|
.attr("class", d => d.target.pseudo ? "link_no_arrow" : "link")
|
||||||
})
|
.attr("marker-end", d => d.target.pseudo ? "" : "url(#arrow)")
|
||||||
.attr("fill", "none")
|
.attr("fill", "none")
|
||||||
.on("click", function(event, d) {
|
|
||||||
|
// Check if target is pseudo and draw marker only if not pseudo
|
||||||
|
.attr("class", d => d.target.pseudo ? "link_no_arrow" : "link")
|
||||||
|
.attr("marker-end", d => {
|
||||||
|
if (d.target.pseudo) return '';
|
||||||
|
if (d.source.id === d.target.id) return 'url(#curve-arrow)'; // Use curve arrow for self-loops
|
||||||
|
return d.multi_step ? 'url(#doublearrow)' : 'url(#arrow)';
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.attr("fill", "none")
|
||||||
|
.on("click", function(event, d) {
|
||||||
const wasHighlighted = d3.select(this).classed("highlighted");
|
const wasHighlighted = d3.select(this).classed("highlighted");
|
||||||
d3.selectAll("path").classed("highlighted", false);
|
d3.selectAll("path").classed("highlighted", false);
|
||||||
if (!wasHighlighted) {
|
if (!wasHighlighted) {
|
||||||
const toHighlight = [];
|
const toHighlight = [];
|
||||||
toHighlight.push(d.el);
|
toHighlight.push(d.el);
|
||||||
@ -688,6 +705,18 @@ function draw(pathway, elem) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const linkText = linkGroup
|
||||||
|
.append("text")
|
||||||
|
.attr("class", "link-label")
|
||||||
|
.attr("text-anchor", "middle")
|
||||||
|
.attr("dy", -6)
|
||||||
|
.style("font-size", "4px")
|
||||||
|
.style("pointer-events", "none")
|
||||||
|
.style("display", "none")
|
||||||
|
.text(d => d.name)
|
||||||
|
.attr("x", d => (d.source.x + d.target.x) / 2)
|
||||||
|
.attr("y", d => (d.source.y + d.target.y) / 2);
|
||||||
|
|
||||||
// add element to links array
|
// add element to links array
|
||||||
link.each(function (d) {
|
link.each(function (d) {
|
||||||
d.el = this; // attach the DOM element to the data object
|
d.el = this; // attach the DOM element to the data object
|
||||||
|
|||||||
@ -55,6 +55,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Description -->
|
||||||
|
{% if node.description and node.description != "no description" %}
|
||||||
|
<div class="collapse-arrow bg-base-200 collapse">
|
||||||
|
<input type="checkbox" checked />
|
||||||
|
<div class="collapse-title text-xl font-medium">Description</div>
|
||||||
|
<div class="collapse-content">{{ node.description }}</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% epdb_slot_templates "epdb.objects.node.viz" as viz_templates %}
|
{% epdb_slot_templates "epdb.objects.node.viz" as viz_templates %}
|
||||||
|
|
||||||
{% for tpl in viz_templates %}
|
{% for tpl in viz_templates %}
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
.link {
|
.link {
|
||||||
stroke: #999;
|
stroke: #999;
|
||||||
stroke-opacity: 0.6;
|
stroke-opacity: 0.6;
|
||||||
|
stroke-width: 1.5px;
|
||||||
/* marker-end: url(#arrow); */
|
/* marker-end: url(#arrow); */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,6 +178,52 @@
|
|||||||
tabindex="0"
|
tabindex="0"
|
||||||
class="dropdown-content menu bg-base-100 rounded-box z-50 w-60 p-2"
|
class="dropdown-content menu bg-base-100 rounded-box z-50 w-60 p-2"
|
||||||
>
|
>
|
||||||
|
<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>
|
||||||
|
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>
|
||||||
|
Reaction Names
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
{% if pathway.setting.model.app_domain %}
|
{% if pathway.setting.model.app_domain %}
|
||||||
<li>
|
<li>
|
||||||
<a id="app-domain-toggle-button" class="cursor-pointer">
|
<a id="app-domain-toggle-button" class="cursor-pointer">
|
||||||
@ -497,6 +544,10 @@
|
|||||||
{{ pathway.d3_json|json_script:"pathway" }}
|
{{ pathway.d3_json|json_script:"pathway" }}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
// Global switch for compound names view
|
||||||
|
var compoundNamesViewEnabled = false;
|
||||||
|
// Gloabl switch for reaction names view
|
||||||
|
var reactionNamesViewEnabled = false;
|
||||||
// Global switch for app domain view
|
// Global switch for app domain view
|
||||||
var appDomainViewEnabled = false;
|
var appDomainViewEnabled = false;
|
||||||
// Global switch for timeseries view
|
// Global switch for timeseries view
|
||||||
@ -532,6 +583,67 @@
|
|||||||
descContent.innerHTML = newDesc;
|
descContent.innerHTML = newDesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.name)
|
||||||
|
.attr("text-anchor", "middle")
|
||||||
|
.attr("y", -20)
|
||||||
|
.style("font-size", "4px");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} 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"/>';
|
||||||
|
|
||||||
|
nodes.forEach((x) => {
|
||||||
|
d3.select(x.el)
|
||||||
|
.select("text")
|
||||||
|
.remove()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
} 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");
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// App domain toggle
|
// App domain toggle
|
||||||
const appDomainBtn = document.getElementById("app-domain-toggle-button");
|
const appDomainBtn = document.getElementById("app-domain-toggle-button");
|
||||||
if (appDomainBtn) {
|
if (appDomainBtn) {
|
||||||
|
|||||||
@ -24,6 +24,76 @@
|
|||||||
<td>Setting Name</td>
|
<td>Setting Name</td>
|
||||||
<td>{{ setting_to_render.name }}</td>
|
<td>{{ setting_to_render.name }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Setting URL</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ setting_to_render.url }}" class="link link-primary">{{ setting_to_render.url }}</a>
|
||||||
|
<div
|
||||||
|
x-data="{
|
||||||
|
value: '{{ setting_to_render.url }}',
|
||||||
|
copied: false,
|
||||||
|
|
||||||
|
async copy() {
|
||||||
|
await navigator.clipboard.writeText(this.value)
|
||||||
|
this.copied = true
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.copied = false
|
||||||
|
}, 1500)
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
class="join"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-ghost"
|
||||||
|
:data-tip="copied ? 'Copied!' : 'Copy'"
|
||||||
|
@click="copy"
|
||||||
|
aria-label="Copy to clipboard"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
x-show="!copied"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-4 w-4"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2"
|
||||||
|
/>
|
||||||
|
<rect
|
||||||
|
width="12"
|
||||||
|
height="12"
|
||||||
|
x="8"
|
||||||
|
y="8"
|
||||||
|
rx="2"
|
||||||
|
ry="2"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<svg
|
||||||
|
x-show="copied"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-4 w-4"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M5 13l4 4L19 7"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
{% if setting_to_render.description %}
|
{% if setting_to_render.description %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>Setting Description</td>
|
<td>Setting Description</td>
|
||||||
|
|||||||
@ -36,7 +36,7 @@ from epdb.models import (
|
|||||||
Setting,
|
Setting,
|
||||||
SimpleAmbitRule,
|
SimpleAmbitRule,
|
||||||
SimpleRDKitRule,
|
SimpleRDKitRule,
|
||||||
SimpleRule,
|
SimpleRule, Group,
|
||||||
)
|
)
|
||||||
from utilities.chem import FormatConverter
|
from utilities.chem import FormatConverter
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ logger = logging.getLogger(__name__)
|
|||||||
Package = s.GET_PACKAGE_MODEL()
|
Package = s.GET_PACKAGE_MODEL()
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from epdb.logic import SPathway
|
from epdb.logic import SPathway, GroupManager
|
||||||
|
|
||||||
|
|
||||||
class LicenseExportSchema(Schema):
|
class LicenseExportSchema(Schema):
|
||||||
@ -123,8 +123,8 @@ class PESCompoundExportSchema(RefCompoundExportSchema):
|
|||||||
description: str
|
description: str
|
||||||
aliases: List[str]
|
aliases: List[str]
|
||||||
default_structure: RefCompoundStructureExportSchema
|
default_structure: RefCompoundStructureExportSchema
|
||||||
structures: List["CompoundStructureExportSchema"]
|
|
||||||
structures: List["PESCompoundStructureExportSchema"]
|
structures: List["PESCompoundStructureExportSchema"]
|
||||||
|
scenarios: List[RefScenarioExportSchema]
|
||||||
|
|
||||||
|
|
||||||
class PESCompoundStructureExportSchema(RefCompoundStructureExportSchema):
|
class PESCompoundStructureExportSchema(RefCompoundStructureExportSchema):
|
||||||
@ -277,7 +277,7 @@ class PackageExportSchema(Schema):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def resolve_classification_level(obj):
|
def resolve_classification_level(obj):
|
||||||
if isinstance(obj, str):
|
if isinstance(obj, dict):
|
||||||
return obj["classification_level"]
|
return obj["classification_level"]
|
||||||
return obj.Classification(obj.classification_level).name
|
return obj.Classification(obj.classification_level).name
|
||||||
|
|
||||||
@ -602,15 +602,20 @@ class PackageImporter:
|
|||||||
package_license = License.objects.get(link=parsed.license.link) if parsed.license else None
|
package_license = License.objects.get(link=parsed.license.link) if parsed.license else None
|
||||||
package_classification_level = Package.Classification[parsed.classification_level.upper()]
|
package_classification_level = Package.Classification[parsed.classification_level.upper()]
|
||||||
|
|
||||||
package = Package.objects.create(
|
print(parsed.classification_level, "->", package_classification_level)
|
||||||
uuid=package_uuid,
|
|
||||||
name=f"{parsed.name} - Imported at {datetime.now()}",
|
package = Package()
|
||||||
description=parsed.description,
|
package.uuid = package_uuid
|
||||||
reviewed=False, # Everything will be imported as non reviewed
|
package.name = f"{parsed.name} - Imported at {datetime.now()}"
|
||||||
license=package_license,
|
package.description = parsed.description
|
||||||
classification_level=package_classification_level,
|
package.reviewed = False
|
||||||
data_pool=parsed.data_pool,
|
package.license = package_license
|
||||||
)
|
package.classification_level = package_classification_level
|
||||||
|
|
||||||
|
if package_classification_level == Package.Classification.SECRET:
|
||||||
|
package.data_pool = Group.objects.get(uuid=parsed.data_pool.uuid)
|
||||||
|
|
||||||
|
package.save()
|
||||||
|
|
||||||
# Import Elements
|
# Import Elements
|
||||||
self._import_compounds(package, parsed.compounds)
|
self._import_compounds(package, parsed.compounds)
|
||||||
|
|||||||
Reference in New Issue
Block a user