forked from enviPath/enviPy
Compare commits
4
Commits
feat/bb4g
...
a6b901aa47
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6b901aa47 | ||
|
|
423ddf16d1 | ||
|
|
a7b2e60660 | ||
|
|
ae3a164e74 |
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 6.0.3 on 2026-11-09 21:43
|
||||
|
||||
import django.utils.timezone
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('bayer', '0006_remove_newsentry_url'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='newsentry',
|
||||
name='publication_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Date'),
|
||||
),
|
||||
]
|
||||
@@ -29,6 +29,8 @@ class SamplingAlgorithm(enum.Enum):
|
||||
class BB4GConfig(EnviPyModel):
|
||||
sampling_algorithm: SamplingAlgorithm = SamplingAlgorithm.EXACT
|
||||
cutoff: int = -5
|
||||
model: str = "rat"
|
||||
mode: str | None = None
|
||||
|
||||
class UI:
|
||||
title = "BB4G Configuration"
|
||||
@@ -44,6 +46,18 @@ class BB4GConfig(EnviPyModel):
|
||||
order=2,
|
||||
placeholder="If unset defaults to -5"
|
||||
)
|
||||
model = UIConfig(
|
||||
widget=WidgetType.TEXT,
|
||||
label="BB4G Model",
|
||||
order=3,
|
||||
placeholder="If unset defaults to rat"
|
||||
)
|
||||
mode = UIConfig(
|
||||
widget=WidgetType.TEXT,
|
||||
label="BB4G Mode",
|
||||
order=4,
|
||||
placeholder="If unset defaults to None"
|
||||
)
|
||||
|
||||
|
||||
# Once stable these will be exposed by enviPy-plugins lib
|
||||
@@ -167,6 +181,8 @@ class BB4G(Classifier):
|
||||
"smiles": smi,
|
||||
"sampling_alg": self.config.sampling_algorithm.value,
|
||||
"cutoff": self.config.cutoff,
|
||||
"model": self.config.model,
|
||||
"mode": self.config.mode,
|
||||
}
|
||||
|
||||
retries = 0
|
||||
|
||||
@@ -378,6 +378,10 @@ class PackageManager(object):
|
||||
# EDIT START
|
||||
|
||||
if package.classification_level == Package.Classification.SECRET:
|
||||
if user.principal_type == user.PrincipalType.APP:
|
||||
if not user.app_details.secret_allowed or package.data_pool != user.app_details.data_pool:
|
||||
return False
|
||||
|
||||
if package.data_pool not in groups:
|
||||
return False
|
||||
|
||||
|
||||
+102
-15
@@ -2071,7 +2071,10 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMix
|
||||
def empty_due_to_threshold(self):
|
||||
return self.kv.get("empty_due_to_threshold", False)
|
||||
|
||||
def d3_json(self):
|
||||
def merged_d3_json(self):
|
||||
return self.d3_json(merged=True)
|
||||
|
||||
def d3_json(self, merged=False):
|
||||
# Ideally it would be something like this but
|
||||
# to reduce crossing in edges do a DFS
|
||||
# nodes = [n.d3_json() for n in self.nodes]
|
||||
@@ -2079,15 +2082,68 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMix
|
||||
nodes = []
|
||||
processed = set()
|
||||
|
||||
structure_to_combined_nodes = dict()
|
||||
node_to_combined_node = dict()
|
||||
queue = list()
|
||||
for n in self.root_nodes:
|
||||
queue.append(n)
|
||||
|
||||
# Add unconnected nodes
|
||||
for n in self.nodes.order_by("url"):
|
||||
if len(n.out_edges.all()) == 0:
|
||||
if n not in queue:
|
||||
queue.append(n)
|
||||
class CombinedNode(object):
|
||||
|
||||
def __init__(self, representative: "Node"):
|
||||
self.representative = representative
|
||||
self.nodes = set([self.representative])
|
||||
|
||||
def ids(self):
|
||||
return [n.id for n in self.nodes]
|
||||
|
||||
def d3_json(self):
|
||||
d3_json = self.representative.d3_json()
|
||||
compound = self.representative.default_node_label.compound
|
||||
d3_json["name"] = compound.get_name()
|
||||
d3_json["smiles"] = compound.default_structure.smiles
|
||||
d3_json["node_label_id"] = compound.default_structure.url
|
||||
d3_json["image"] = f"{compound.default_structure.url}?image=svg",
|
||||
d3_json["image_svg"] = IndigoUtils.mol_to_svg(
|
||||
compound.default_structure.molfile
|
||||
if compound.default_structure.molfile is not None
|
||||
and compound.default_structure.molfile.strip()
|
||||
else compound.default_structure.smiles,
|
||||
width=40,
|
||||
height=40,
|
||||
),
|
||||
|
||||
return d3_json
|
||||
|
||||
def __repr__(self):
|
||||
return f"CombinedNode({self.nodes})"
|
||||
|
||||
if merged:
|
||||
for n in self.nodes:
|
||||
def_struct = n.default_node_label.compound.default_structure
|
||||
if def_struct in structure_to_combined_nodes:
|
||||
structure_to_combined_nodes[def_struct].nodes.add(n)
|
||||
else:
|
||||
cn = CombinedNode(n)
|
||||
structure_to_combined_nodes[def_struct] = cn
|
||||
|
||||
# for n in self.nodes.order_by("url"):
|
||||
# if len(n.out_edges.all()) == 0:
|
||||
# if n not in queue:
|
||||
# queue.append(n)
|
||||
|
||||
for cn in structure_to_combined_nodes.values():
|
||||
for n in cn.nodes:
|
||||
node_to_combined_node[n] = cn
|
||||
|
||||
queue = list(structure_to_combined_nodes.values())
|
||||
else:
|
||||
for n in self.nodes:
|
||||
queue.append(n)
|
||||
|
||||
# Add unconnected nodes
|
||||
for n in self.nodes.order_by("url"):
|
||||
if len(n.out_edges.all()) == 0:
|
||||
if n not in queue:
|
||||
queue.append(n)
|
||||
|
||||
for i in queue:
|
||||
processed.add(i)
|
||||
@@ -2097,25 +2153,56 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin, AdditionalInformationMix
|
||||
processed.add(current)
|
||||
nodes.append(current.d3_json())
|
||||
|
||||
for e in self.edges.filter(start_nodes=current).order_by("url").distinct():
|
||||
if merged:
|
||||
q = Q(start_nodes__in=current.ids())
|
||||
else:
|
||||
q = Q(start_nodes=current)
|
||||
|
||||
for e in self.edges.filter(q).order_by("url").distinct():
|
||||
for prod in e.end_nodes.all().order_by("url"):
|
||||
if prod not in queue and prod not in processed:
|
||||
queue.append(prod)
|
||||
|
||||
if merged:
|
||||
def_struct = n.default_node_label.compound.default_structure
|
||||
if def_struct in structure_to_combined_nodes:
|
||||
structure_to_combined_nodes[def_struct].nodes.add(n)
|
||||
else:
|
||||
cn = CombinedNode(n)
|
||||
structure_to_combined_nodes[def_struct] = cn
|
||||
if cn not in queue and cn not in processed:
|
||||
queue.append(cn)
|
||||
else:
|
||||
if prod not in queue and prod not in processed:
|
||||
queue.append(prod)
|
||||
|
||||
# We shouldn't lose or make up nodes...
|
||||
if len(nodes) != len(self.nodes):
|
||||
logger.debug(
|
||||
f"{self.get_name()}: Num Nodes {len(nodes)} vs. DB Nodes {len(self.nodes)}"
|
||||
)
|
||||
if not merged:
|
||||
if len(nodes) != len(self.nodes):
|
||||
logger.debug(
|
||||
f"{self.get_name()}: Num Nodes {len(nodes)} vs. DB Nodes {len(self.nodes)}"
|
||||
)
|
||||
|
||||
links = [e.d3_json() for e in self.edges]
|
||||
|
||||
# D3 links Nodes based on indices in nodes array
|
||||
combined_node_depth_to_idx = dict()
|
||||
node_url_to_idx = dict()
|
||||
for i, n in enumerate(nodes):
|
||||
n["id"] = i
|
||||
node_url_to_idx[n["url"]] = i
|
||||
|
||||
# Add potential missing mappings
|
||||
if merged:
|
||||
cn_idx_mapping = dict()
|
||||
for n in nodes:
|
||||
for cn in processed:
|
||||
for sub_n in cn.nodes:
|
||||
if n["url"] == sub_n.url:
|
||||
cn_idx_mapping[cn] = n["id"]
|
||||
|
||||
for n in processed:
|
||||
for sub_n in n.nodes:
|
||||
node_url_to_idx[sub_n.url] = cn_idx_mapping[n]
|
||||
|
||||
adjusted_links = []
|
||||
for link in links:
|
||||
# Check if we'll need pseudo nodes
|
||||
|
||||
@@ -2313,6 +2313,7 @@ def package_pathway(request, package_uuid, pathway_uuid):
|
||||
|
||||
context["pathway"] = current_pathway
|
||||
context["current_object"] = current_pathway
|
||||
context["merged"] = request.GET.get("merged", False) == "true"
|
||||
|
||||
context["breadcrumbs"] = [
|
||||
{"Home": s.SERVER_URL},
|
||||
|
||||
@@ -266,7 +266,7 @@
|
||||
|
||||
<div id="services-container" class="flex justify-center gap-4">
|
||||
{% for ne in news_entries %}
|
||||
<div class="card-body flex flex-col h-full justify-between p-5">
|
||||
<div class="card-body flex flex-col h-full justify-between p-5 w-96"> <!-- Fixed width applied via w-96 -->
|
||||
<div>
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<div class="bg-primary/10 text-primary rounded-lg p-1.5">
|
||||
@@ -296,6 +296,7 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mt-6 text-right">
|
||||
<a
|
||||
href="https://community.envipath.org/c/announcements/10"
|
||||
|
||||
@@ -544,8 +544,11 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if merged %}
|
||||
{{ pathway.merged_d3_json|json_script:"pathway" }}
|
||||
{% else %}
|
||||
{{ pathway.d3_json|json_script:"pathway" }}
|
||||
|
||||
{% endif %}
|
||||
<script>
|
||||
// Global switch for compound names view
|
||||
var compoundNamesViewEnabled = false;
|
||||
|
||||
Reference in New Issue
Block a user