forked from enviPath/enviPy
[Fix] UI bugs, Registrations Mail, BTRules Popup, Legacy API fixes (#309)
Co-authored-by: Tim Lorsbach <tim@lorsba.ch> Reviewed-on: enviPath/enviPy#309
This commit is contained in:
@ -48,11 +48,6 @@ runs:
|
||||
shell: bash
|
||||
run: |
|
||||
uv run python scripts/pnpm_wrapper.py install
|
||||
cat << 'EOF' > pnpm-workspace.yaml
|
||||
onlyBuiltDependencies:
|
||||
- '@parcel/watcher'
|
||||
- '@tailwindcss/oxide'
|
||||
EOF
|
||||
uv run python scripts/pnpm_wrapper.py run build
|
||||
|
||||
- name: Wait for Postgres
|
||||
|
||||
@ -212,7 +212,7 @@ def get_user(request, user_uuid):
|
||||
|
||||
|
||||
class GroupMember(Schema):
|
||||
id: str = Field(None, alias="url")
|
||||
id: str
|
||||
identifier: str
|
||||
name: str
|
||||
|
||||
@ -228,7 +228,7 @@ class GroupSchema(Schema):
|
||||
members: List[GroupMember] = Field([], alias="members")
|
||||
name: str = Field(None, alias="name")
|
||||
ownerid: str = Field(None, alias="owner.url")
|
||||
ownername: str = Field(None, alias="owner.name")
|
||||
ownername: str = Field(None, alias="owner.get_name")
|
||||
packages: List["SimplePackage"] = Field([], alias="packages")
|
||||
readers: List[GroupMember] = Field([], alias="readers")
|
||||
writers: List[GroupMember] = Field([], alias="writers")
|
||||
@ -237,10 +237,10 @@ class GroupSchema(Schema):
|
||||
def resolve_members(obj: Group):
|
||||
res = []
|
||||
for member in obj.user_member.all():
|
||||
res.append(GroupMember(id=member.url, identifier="usermember", name=member.username))
|
||||
res.append(GroupMember(id=member.url, identifier="usermember", name=member.get_name()))
|
||||
|
||||
for member in obj.group_member.all():
|
||||
res.append(GroupMember(id=member.url, identifier="groupmember", name=member.name))
|
||||
res.append(GroupMember(id=member.url, identifier="groupmember", name=member.get_name()))
|
||||
|
||||
return res
|
||||
|
||||
@ -374,7 +374,7 @@ class PackageSchema(Schema):
|
||||
).values_list("user", flat=True)
|
||||
).distinct()
|
||||
|
||||
return [{u.id: u.name} for u in users]
|
||||
return [{u.id: u.get_name()} for u in users]
|
||||
|
||||
@staticmethod
|
||||
def resolve_writers(obj: Package):
|
||||
@ -384,7 +384,7 @@ class PackageSchema(Schema):
|
||||
).values_list("user", flat=True)
|
||||
).distinct()
|
||||
|
||||
return [{u.id: u.name} for u in users]
|
||||
return [{u.id: u.get_name()} for u in users]
|
||||
|
||||
@staticmethod
|
||||
def resolve_review_comment(obj):
|
||||
@ -966,7 +966,12 @@ def create_package_simple_rule(
|
||||
raise ValueError("Not yet implemented!")
|
||||
else:
|
||||
sr = SimpleAmbitRule.create(
|
||||
p, r.name, r.description, r.smirks, r.reactantFilterSmarts, r.productFilterSmarts
|
||||
p,
|
||||
r.name,
|
||||
r.description,
|
||||
r.smirks,
|
||||
r.reactantFilterSmarts,
|
||||
r.productFilterSmarts,
|
||||
)
|
||||
|
||||
return redirect(sr.url)
|
||||
@ -1119,7 +1124,7 @@ class ReactionSchema(Schema):
|
||||
name: str = Field(None, alias="name")
|
||||
pathways: List["SimplePathway"] = Field([], alias="related_pathways")
|
||||
products: List["ReactionCompoundStructure"] = Field([], alias="products")
|
||||
references: List[Dict[str, List[str]]] = Field([], alias="references")
|
||||
references: Dict[str, List[str]] = Field({}, alias="references")
|
||||
reviewStatus: str = Field(None, alias="review_status")
|
||||
scenarios: List["SimpleScenario"] = Field([], alias="scenarios")
|
||||
smirks: str = Field("", alias="smirks")
|
||||
@ -1135,8 +1140,12 @@ class ReactionSchema(Schema):
|
||||
|
||||
@staticmethod
|
||||
def resolve_references(obj: Reaction):
|
||||
# TODO
|
||||
return []
|
||||
rhea_refs = []
|
||||
for rhea in obj.get_rhea_identifiers():
|
||||
rhea_refs.append(f"{rhea.identifier_value}")
|
||||
|
||||
# TODO UniProt
|
||||
return {"rheaReferences": rhea_refs, "uniprotCount": []}
|
||||
|
||||
@staticmethod
|
||||
def resolve_medline_references(obj: Reaction):
|
||||
@ -1715,7 +1724,7 @@ class EdgeSchema(Schema):
|
||||
id: str = Field(None, alias="url")
|
||||
identifier: str = "edge"
|
||||
name: str = Field(None, alias="name")
|
||||
reactionName: str = Field(None, alias="edge_label.name")
|
||||
reactionName: str = Field(None, alias="edge_label.get_name")
|
||||
reactionURI: str = Field(None, alias="edge_label.url")
|
||||
reviewStatus: str = Field(None, alias="review_status")
|
||||
scenarios: List["SimpleScenario"] = Field([], alias="scenarios")
|
||||
@ -1764,7 +1773,7 @@ class CreateEdge(Schema):
|
||||
|
||||
|
||||
@router.post(
|
||||
"/package/{uuid:package_uuid}/üathway/{uuid:pathway_uuid}/edge",
|
||||
"/package/{uuid:package_uuid}/pathway/{uuid:pathway_uuid}/edge",
|
||||
response={200: str | Any, 403: Error},
|
||||
)
|
||||
def add_pathway_edge(request, package_uuid, pathway_uuid, e: Form[CreateEdge]):
|
||||
@ -1783,10 +1792,26 @@ def add_pathway_edge(request, package_uuid, pathway_uuid, e: Form[CreateEdge]):
|
||||
|
||||
if e.edgeAsSmirks:
|
||||
for ed in e.edgeAsSmirks.split(">>")[0].split("\\."):
|
||||
educts.append(Node.objects.get(pathway=pw, default_node_label__smiles=ed))
|
||||
stand_ed = FormatConverter.standardize(ed, remove_stereo=True)
|
||||
educts.append(
|
||||
Node.objects.get(
|
||||
pathway=pw,
|
||||
default_node_label=CompoundStructure.objects.get(
|
||||
compound__package=p, smiles=stand_ed
|
||||
).compound.default_structure,
|
||||
)
|
||||
)
|
||||
|
||||
for pr in e.edgeAsSmirks.split(">>")[1].split("\\."):
|
||||
products.append(Node.objects.get(pathway=pw, default_node_label__smiles=pr))
|
||||
stand_pr = FormatConverter.standardize(pr, remove_stereo=True)
|
||||
products.append(
|
||||
Node.objects.get(
|
||||
pathway=pw,
|
||||
default_node_label=CompoundStructure.objects.get(
|
||||
compound__package=p, smiles=stand_pr
|
||||
).compound.default_structure,
|
||||
)
|
||||
)
|
||||
else:
|
||||
for ed in e.educts.split(","):
|
||||
educts.append(Node.objects.get(pathway=pw, url=ed.strip()))
|
||||
@ -1799,7 +1824,7 @@ def add_pathway_edge(request, package_uuid, pathway_uuid, e: Form[CreateEdge]):
|
||||
start_nodes=educts,
|
||||
end_nodes=products,
|
||||
rule=None,
|
||||
name=e.name,
|
||||
name=None,
|
||||
description=e.edgeReason,
|
||||
)
|
||||
|
||||
@ -1936,7 +1961,7 @@ def get_model(request, package_uuid, model_uuid, c: Query[Classify]):
|
||||
if pr.rule:
|
||||
res["id"] = pr.rule.url
|
||||
res["identifier"] = pr.rule.get_rule_identifier()
|
||||
res["name"] = pr.rule.name
|
||||
res["name"] = pr.rule.get_name()
|
||||
res["reviewStatus"] = (
|
||||
"reviewed" if pr.rule.package.reviewed else "unreviewed"
|
||||
)
|
||||
|
||||
@ -8,7 +8,6 @@ from epdb.logic import UserManager, GroupManager, PackageManager, SettingManager
|
||||
from epdb.models import (
|
||||
UserSettingPermission,
|
||||
MLRelativeReasoning,
|
||||
EnviFormer,
|
||||
Permission,
|
||||
User,
|
||||
ExternalDatabase,
|
||||
@ -231,7 +230,6 @@ class Command(BaseCommand):
|
||||
package=pack,
|
||||
rule_packages=[mapping["EAWAG-BBD"]],
|
||||
data_packages=[mapping["EAWAG-BBD"]],
|
||||
eval_packages=[],
|
||||
threshold=0.5,
|
||||
name="ECC - BBD - T0.5",
|
||||
description="ML Relative Reasoning",
|
||||
@ -239,7 +237,3 @@ class Command(BaseCommand):
|
||||
|
||||
ml_model.build_dataset()
|
||||
ml_model.build_model()
|
||||
|
||||
# If available, create EnviFormerModel
|
||||
if s.ENVIFORMER_PRESENT:
|
||||
EnviFormer.create(pack, "EnviFormer - T0.5", "EnviFormer Model with Threshold 0.5", 0.5)
|
||||
|
||||
@ -47,7 +47,7 @@ class Command(BaseCommand):
|
||||
"description": model.description,
|
||||
"kv": model.kv,
|
||||
"data_packages_uuids": [str(p.uuid) for p in model.data_packages.all()],
|
||||
"eval_packages_uuids": [str(p.uuid) for p in model.data_packages.all()],
|
||||
"eval_packages_uuids": [str(p.uuid) for p in model.eval_packages.all()],
|
||||
"threshold": model.threshold,
|
||||
"eval_results": model.eval_results,
|
||||
"multigen_eval": model.multigen_eval,
|
||||
|
||||
144
epdb/models.py
144
epdb/models.py
@ -77,6 +77,9 @@ class User(AbstractUser):
|
||||
USERNAME_FIELD = "email"
|
||||
REQUIRED_FIELDS = ["username"]
|
||||
|
||||
def get_name(self):
|
||||
return self.username
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.url:
|
||||
self.url = self._url()
|
||||
@ -208,7 +211,10 @@ class Group(TimeStampedModel):
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} (pk={self.pk})"
|
||||
return f"{self.get_name()} (pk={self.pk})"
|
||||
|
||||
def get_name(self):
|
||||
return self.name
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.url:
|
||||
@ -596,7 +602,7 @@ class EnviPathModel(TimeStampedModel):
|
||||
res = {
|
||||
"url": self.url,
|
||||
"uuid": str(self.uuid),
|
||||
"name": self.name,
|
||||
"name": self.get_name(),
|
||||
}
|
||||
|
||||
if include_description:
|
||||
@ -609,11 +615,14 @@ class EnviPathModel(TimeStampedModel):
|
||||
return self.kv.get(k, default)
|
||||
return default
|
||||
|
||||
def get_name(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} (pk={self.pk})"
|
||||
return f"{self.get_name()} (pk={self.pk})"
|
||||
|
||||
|
||||
class AliasMixin(models.Model):
|
||||
@ -624,7 +633,7 @@ class AliasMixin(models.Model):
|
||||
@transaction.atomic
|
||||
def add_alias(self, new_alias, set_as_default=False):
|
||||
if set_as_default:
|
||||
self.aliases.append(self.name)
|
||||
self.aliases.append(self.get_name())
|
||||
self.name = new_alias
|
||||
|
||||
if new_alias in self.aliases:
|
||||
@ -765,15 +774,17 @@ class Compound(EnviPathModel, AliasMixin, ScenarioMixin, ChemicalIdentifierMixin
|
||||
logger.debug(
|
||||
f"#Structures: {num_structs} - #Standardized SMILES: {len(stand_smiles)}"
|
||||
)
|
||||
logger.debug(f"Couldn't infer normalized structure for {self.name} - {self.url}")
|
||||
logger.debug(
|
||||
f"Couldn't infer normalized structure for {self.get_name()} - {self.url}"
|
||||
)
|
||||
raise ValueError(
|
||||
f"Couldn't find nor infer normalized structure for {self.name} ({self.url})"
|
||||
f"Couldn't find nor infer normalized structure for {self.get_name()} ({self.url})"
|
||||
)
|
||||
else:
|
||||
cs = CompoundStructure.create(
|
||||
self,
|
||||
stand_smiles.pop(),
|
||||
name="Normalized structure of {}".format(self.name),
|
||||
name="Normalized structure of {}".format(self.get_name()),
|
||||
description="{} (in its normalized form)".format(self.description),
|
||||
normalized_structure=True,
|
||||
)
|
||||
@ -848,8 +859,10 @@ class Compound(EnviPathModel, AliasMixin, ScenarioMixin, ChemicalIdentifierMixin
|
||||
if name is not None:
|
||||
# Clean for potential XSS
|
||||
name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
if name is None or name == "":
|
||||
name = f"Compound {Compound.objects.filter(package=package).count() + 1}"
|
||||
|
||||
c.name = name
|
||||
|
||||
# We have a default here only set the value if it carries some payload
|
||||
@ -978,7 +991,7 @@ class Compound(EnviPathModel, AliasMixin, ScenarioMixin, ChemicalIdentifierMixin
|
||||
cs = CompoundStructure.create(
|
||||
existing_normalized_compound,
|
||||
structure.smiles,
|
||||
name=structure.name,
|
||||
name=structure.get_name(),
|
||||
description=structure.description,
|
||||
normalized_structure=structure.normalized_structure,
|
||||
)
|
||||
@ -989,13 +1002,13 @@ class Compound(EnviPathModel, AliasMixin, ScenarioMixin, ChemicalIdentifierMixin
|
||||
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Found a CompoundStructure for {default_structure_smiles} but not for {normalized_structure_smiles} in target package {target.name}"
|
||||
f"Found a CompoundStructure for {default_structure_smiles} but not for {normalized_structure_smiles} in target package {target.get_name()}"
|
||||
)
|
||||
else:
|
||||
# Here we can safely use Compound.objects.create as we won't end up in a duplicate
|
||||
new_compound = Compound.objects.create(
|
||||
package=target,
|
||||
name=self.name,
|
||||
name=self.get_name(),
|
||||
description=self.description,
|
||||
kv=self.kv.copy() if self.kv else {},
|
||||
)
|
||||
@ -1011,7 +1024,7 @@ class Compound(EnviPathModel, AliasMixin, ScenarioMixin, ChemicalIdentifierMixin
|
||||
canonical_smiles=structure.canonical_smiles,
|
||||
inchikey=structure.inchikey,
|
||||
normalized_structure=structure.normalized_structure,
|
||||
name=structure.name,
|
||||
name=structure.get_name(),
|
||||
description=structure.description,
|
||||
kv=structure.kv.copy() if structure.kv else {},
|
||||
)
|
||||
@ -1050,11 +1063,8 @@ class Compound(EnviPathModel, AliasMixin, ScenarioMixin, ChemicalIdentifierMixin
|
||||
def half_lifes(self):
|
||||
hls: Dict[Scenario, List[HalfLife]] = defaultdict(list)
|
||||
|
||||
for n in self.related_nodes:
|
||||
for scen in n.scenarios.all().order_by("name"):
|
||||
for ai in scen.get_additional_information():
|
||||
if isinstance(ai, HalfLife):
|
||||
hls[scen].append(ai)
|
||||
for cs in self.structures:
|
||||
hls.update(cs.half_lifes())
|
||||
|
||||
return dict(hls)
|
||||
|
||||
@ -1104,6 +1114,7 @@ class CompoundStructure(EnviPathModel, AliasMixin, ScenarioMixin, ChemicalIdenti
|
||||
# Clean for potential XSS
|
||||
if name is not None:
|
||||
cs.name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
if description is not None:
|
||||
cs.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
@ -1147,6 +1158,21 @@ class CompoundStructure(EnviPathModel, AliasMixin, ScenarioMixin, ChemicalIdenti
|
||||
def is_default_structure(self):
|
||||
return self.compound.default_structure == self
|
||||
|
||||
@property
|
||||
def related_nodes(self):
|
||||
return Node.objects.filter(node_labels__in=[self], pathway__package=self.compound.package)
|
||||
|
||||
def half_lifes(self):
|
||||
hls: Dict[Scenario, List[HalfLife]] = defaultdict(list)
|
||||
|
||||
for n in self.related_nodes:
|
||||
for scen in n.scenarios.all().order_by("name"):
|
||||
for ai in scen.get_additional_information():
|
||||
if isinstance(ai, HalfLife):
|
||||
hls[scen].append(ai)
|
||||
|
||||
return dict(hls)
|
||||
|
||||
|
||||
class EnzymeLink(EnviPathModel, KEGGIdentifierMixin):
|
||||
rule = models.ForeignKey("Rule", on_delete=models.CASCADE, db_index=True)
|
||||
@ -1218,7 +1244,7 @@ class Rule(PolymorphicModel, EnviPathModel, AliasMixin, ScenarioMixin):
|
||||
if rule_type == SimpleAmbitRule:
|
||||
new_rule = SimpleAmbitRule.create(
|
||||
package=target,
|
||||
name=self.name,
|
||||
name=self.get_name(),
|
||||
description=self.description,
|
||||
smirks=self.smirks,
|
||||
reactant_filter_smarts=self.reactant_filter_smarts,
|
||||
@ -1232,7 +1258,7 @@ class Rule(PolymorphicModel, EnviPathModel, AliasMixin, ScenarioMixin):
|
||||
elif rule_type == SimpleRDKitRule:
|
||||
new_rule = SimpleRDKitRule.create(
|
||||
package=target,
|
||||
name=self.name,
|
||||
name=self.get_name(),
|
||||
description=self.description,
|
||||
reaction_smarts=self.reaction_smarts,
|
||||
)
|
||||
@ -1250,7 +1276,7 @@ class Rule(PolymorphicModel, EnviPathModel, AliasMixin, ScenarioMixin):
|
||||
new_rule = ParallelRule.create(
|
||||
package=target,
|
||||
simple_rules=new_srs,
|
||||
name=self.name,
|
||||
name=self.get_name(),
|
||||
description=self.description,
|
||||
)
|
||||
|
||||
@ -1624,9 +1650,11 @@ class Reaction(EnviPathModel, AliasMixin, ScenarioMixin, ReactionIdentifierMixin
|
||||
|
||||
r = Reaction()
|
||||
r.package = package
|
||||
|
||||
# Clean for potential XSS
|
||||
if name is not None and name.strip() != "":
|
||||
r.name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
if description is not None and name.strip() != "":
|
||||
r.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
@ -1674,7 +1702,7 @@ class Reaction(EnviPathModel, AliasMixin, ScenarioMixin, ReactionIdentifierMixin
|
||||
|
||||
new_reaction = Reaction.create(
|
||||
package=target,
|
||||
name=self.name,
|
||||
name=self.get_name(),
|
||||
description=self.description,
|
||||
educts=copied_reaction_educts,
|
||||
products=copied_reaction_products,
|
||||
@ -1830,7 +1858,9 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin):
|
||||
|
||||
# We shouldn't lose or make up nodes...
|
||||
if len(nodes) != len(self.nodes):
|
||||
logger.debug(f"{self.name}: Num Nodes {len(nodes)} vs. DB 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]
|
||||
|
||||
@ -1898,7 +1928,7 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin):
|
||||
"isIncremental": self.kv.get("mode") == "incremental",
|
||||
"isPredicted": self.kv.get("mode") == "predicted",
|
||||
"lastModified": self.modified.strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"pathwayName": self.name,
|
||||
"pathwayName": self.get_name(),
|
||||
"reviewStatus": "reviewed" if self.package.reviewed else "unreviewed",
|
||||
"scenarios": [],
|
||||
"upToDate": True,
|
||||
@ -1941,14 +1971,14 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin):
|
||||
if include_pathway_url:
|
||||
row.append(n.pathway.url)
|
||||
|
||||
row += [cs.smiles, cs.name, n.depth]
|
||||
row += [cs.smiles, cs.get_name(), n.depth]
|
||||
|
||||
edges = self.edges.filter(end_nodes__in=[n])
|
||||
if len(edges):
|
||||
for e in edges:
|
||||
_row = row.copy()
|
||||
_row.append(e.kv.get("probability"))
|
||||
_row.append(",".join([r.name for r in e.edge_label.rules.all()]))
|
||||
_row.append(",".join([r.get_name() for r in e.edge_label.rules.all()]))
|
||||
_row.append(",".join([r.url for r in e.edge_label.rules.all()]))
|
||||
_row.append(e.start_nodes.all()[0].default_node_label.smiles)
|
||||
rows.append(_row)
|
||||
@ -1979,12 +2009,14 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin):
|
||||
if name is not None:
|
||||
# Clean for potential XSS
|
||||
name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
if name is None or name == "":
|
||||
name = f"Pathway {Pathway.objects.filter(package=package).count() + 1}"
|
||||
|
||||
pw.name = name
|
||||
if description is not None and description.strip() != "":
|
||||
pw.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
pw.predicted = predicted
|
||||
|
||||
pw.save()
|
||||
@ -2009,7 +2041,7 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin):
|
||||
# deduplicated
|
||||
new_pathway = Pathway.objects.create(
|
||||
package=target,
|
||||
name=self.name,
|
||||
name=self.get_name(),
|
||||
description=self.description,
|
||||
setting=self.setting, # TODO copy settings?
|
||||
kv=self.kv.copy() if self.kv else {},
|
||||
@ -2039,7 +2071,7 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin):
|
||||
pathway=new_pathway,
|
||||
default_node_label=copied_structure,
|
||||
depth=node.depth,
|
||||
name=node.name,
|
||||
name=node.get_name(),
|
||||
description=node.description,
|
||||
kv=node.kv.copy() if node.kv else {},
|
||||
)
|
||||
@ -2063,7 +2095,7 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin):
|
||||
new_edge = Edge.objects.create(
|
||||
pathway=new_pathway,
|
||||
edge_label=copied_reaction,
|
||||
name=edge.name,
|
||||
name=edge.get_name(),
|
||||
description=edge.description,
|
||||
kv=edge.kv.copy() if edge.kv else {},
|
||||
)
|
||||
@ -2123,6 +2155,18 @@ class Node(EnviPathModel, AliasMixin, ScenarioMixin):
|
||||
def _url(self):
|
||||
return "{}/node/{}".format(self.pathway.url, self.uuid)
|
||||
|
||||
def get_name(self):
|
||||
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)"
|
||||
)
|
||||
|
||||
def d3_json(self):
|
||||
app_domain_data = self.get_app_domain_assessment_data()
|
||||
|
||||
@ -2135,9 +2179,9 @@ class Node(EnviPathModel, AliasMixin, ScenarioMixin):
|
||||
"image_svg": IndigoUtils.mol_to_svg(
|
||||
self.default_node_label.smiles, width=40, height=40
|
||||
),
|
||||
"name": self.default_node_label.name,
|
||||
"name": self.get_name(),
|
||||
"smiles": self.default_node_label.smiles,
|
||||
"scenarios": [{"name": s.name, "url": s.url} for s in self.scenarios.all()],
|
||||
"scenarios": [{"name": s.get_name(), "url": s.url} for s in self.scenarios.all()],
|
||||
"app_domain": {
|
||||
"inside_app_domain": app_domain_data["assessment"]["inside_app_domain"]
|
||||
if app_domain_data
|
||||
@ -2205,7 +2249,7 @@ class Node(EnviPathModel, AliasMixin, ScenarioMixin):
|
||||
res = super().simple_json()
|
||||
name = res.get("name", None)
|
||||
if name == "no name":
|
||||
res["name"] = self.default_node_label.name
|
||||
res["name"] = self.default_node_label.get_name()
|
||||
|
||||
return res
|
||||
|
||||
@ -2229,18 +2273,24 @@ class Edge(EnviPathModel, AliasMixin, ScenarioMixin):
|
||||
|
||||
def d3_json(self):
|
||||
edge_json = {
|
||||
"name": self.name,
|
||||
"name": self.get_name(),
|
||||
"id": self.url,
|
||||
"url": self.url,
|
||||
"image": self.url + "?image=svg",
|
||||
"reaction": {"name": self.edge_label.name, "url": self.edge_label.url}
|
||||
"reaction": {
|
||||
"name": self.edge_label.get_name(),
|
||||
"url": self.edge_label.url,
|
||||
"rules": [
|
||||
{"name": r.get_name(), "url": r.url} for r in self.edge_label.rules.all()
|
||||
],
|
||||
}
|
||||
if self.edge_label
|
||||
else None,
|
||||
"multi_step": self.edge_label.multi_step if self.edge_label else False,
|
||||
"reaction_probability": self.kv.get("probability"),
|
||||
"start_node_urls": [x.url for x in self.start_nodes.all()],
|
||||
"end_node_urls": [x.url for x in self.end_nodes.all()],
|
||||
"scenarios": [{"name": s.name, "url": s.url} for s in self.scenarios.all()],
|
||||
"scenarios": [{"name": s.get_name(), "url": s.url} for s in self.scenarios.all()],
|
||||
}
|
||||
|
||||
for n in self.start_nodes.all():
|
||||
@ -2329,10 +2379,22 @@ class Edge(EnviPathModel, AliasMixin, ScenarioMixin):
|
||||
res = super().simple_json()
|
||||
name = res.get("name", None)
|
||||
if name == "no name":
|
||||
res["name"] = self.edge_label.name
|
||||
res["name"] = self.edge_label.get_name()
|
||||
|
||||
return res
|
||||
|
||||
def get_name(self):
|
||||
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)"
|
||||
)
|
||||
|
||||
|
||||
class EPModel(PolymorphicModel, EnviPathModel):
|
||||
package = models.ForeignKey(
|
||||
@ -2613,7 +2675,7 @@ class PackageBasedModel(EPModel):
|
||||
root_compounds.append(pw.root_nodes[0].default_node_label)
|
||||
else:
|
||||
logger.info(
|
||||
f"Skipping MG Eval of Pathway {pw.name} ({pw.uuid}) as it has no root compounds!"
|
||||
f"Skipping MG Eval of Pathway {pw.get_name()} ({pw.uuid}) as it has no root compounds!"
|
||||
)
|
||||
|
||||
# As we need a Model Instance in our setting, get a fresh copy from db, overwrite the serialized mode and
|
||||
@ -2739,7 +2801,7 @@ class PackageBasedModel(EPModel):
|
||||
pathways.append(pathway)
|
||||
else:
|
||||
logging.warning(
|
||||
f"No root compound in pathway {pathway.name}, excluding from multigen evaluation"
|
||||
f"No root compound in pathway {pathway.get_name()}, excluding from multigen evaluation"
|
||||
)
|
||||
|
||||
# build lookup reaction -> {uuid1, uuid2} for overlap check
|
||||
@ -3071,7 +3133,7 @@ class ApplicabilityDomain(EnviPathModel):
|
||||
ad = ApplicabilityDomain()
|
||||
ad.model = mlrr
|
||||
# ad.uuid = mlrr.uuid
|
||||
ad.name = f"AD for {mlrr.name}"
|
||||
ad.name = f"AD for {mlrr.get_name()}"
|
||||
ad.num_neighbours = num_neighbours
|
||||
ad.reliability_threshold = reliability_threshold
|
||||
ad.local_compatibilty_threshold = local_compatibility_threshold
|
||||
@ -3355,7 +3417,7 @@ class EnviFormer(PackageBasedModel):
|
||||
)
|
||||
for smiles in smiles_list
|
||||
]
|
||||
logger.info(f"Submitting {canon_smiles} to {self.name}")
|
||||
logger.info(f"Submitting {canon_smiles} to {self.get_name()}")
|
||||
start = datetime.now()
|
||||
products_list = self.model.predict_batch(canon_smiles)
|
||||
end = datetime.now()
|
||||
@ -3512,7 +3574,7 @@ class EnviFormer(PackageBasedModel):
|
||||
root_node = p.root_nodes
|
||||
if len(root_node) > 1:
|
||||
logging.warning(
|
||||
f"Pathway {p.name} has more than one root compound, only {root_node[0]} will be used"
|
||||
f"Pathway {p.get_name()} has more than one root compound, only {root_node[0]} will be used"
|
||||
)
|
||||
root_node = ".".join(
|
||||
[
|
||||
@ -3632,7 +3694,7 @@ class EnviFormer(PackageBasedModel):
|
||||
pathways.append(pathway)
|
||||
else:
|
||||
logging.warning(
|
||||
f"No root compound in pathway {pathway.name}, excluding from multigen evaluation"
|
||||
f"No root compound in pathway {pathway.get_name()}, excluding from multigen evaluation"
|
||||
)
|
||||
|
||||
# build lookup reaction -> {uuid1, uuid2} for overlap check
|
||||
@ -4038,6 +4100,6 @@ class JobLog(TimeStampedModel):
|
||||
return self.task_result
|
||||
|
||||
def is_result_downloadable(self):
|
||||
downloadable = ["batch_predict"]
|
||||
downloadable = ["batch_predict", "identify_missing_rules"]
|
||||
|
||||
return self.job_name in downloadable
|
||||
|
||||
@ -7,6 +7,7 @@ from uuid import uuid4
|
||||
from celery import shared_task
|
||||
from celery.utils.functional import LRUCache
|
||||
from django.conf import settings as s
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.utils import timezone
|
||||
|
||||
from epdb.logic import SPathway
|
||||
@ -73,7 +74,31 @@ def predict_simple(model_pk: int, smiles: str):
|
||||
|
||||
@shared_task(queue="background")
|
||||
def send_registration_mail(user_pk: int):
|
||||
pass
|
||||
u = User.objects.get(id=user_pk)
|
||||
|
||||
tpl = """Welcome {username}!,
|
||||
|
||||
Thank you for your interest in enviPath.
|
||||
|
||||
The public system is intended for non-commercial use only.
|
||||
We will review your account details and usually activate your account within 24 hours.
|
||||
Once activated, you will be notified by email.
|
||||
|
||||
If we have any questions, we will contact you at this email address.
|
||||
|
||||
Best regards,
|
||||
|
||||
enviPath team"""
|
||||
|
||||
msg = EmailMultiAlternatives(
|
||||
"Your enviPath account",
|
||||
tpl.format(username=u.username),
|
||||
"admin@envipath.org",
|
||||
[u.email],
|
||||
bcc=["admin@envipath.org"],
|
||||
)
|
||||
|
||||
msg.send(fail_silently=False)
|
||||
|
||||
|
||||
@shared_task(bind=True, queue="model")
|
||||
|
||||
142
epdb/views.py
142
epdb/views.py
@ -1,7 +1,7 @@
|
||||
import json
|
||||
import logging
|
||||
from typing import Any, Dict, List
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List
|
||||
|
||||
import nh3
|
||||
from django.conf import settings as s
|
||||
@ -13,6 +13,7 @@ from django.urls import reverse
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from envipy_additional_information import NAME_MAPPING
|
||||
from oauth2_provider.decorators import protected_resource
|
||||
from sentry_sdk import capture_exception
|
||||
|
||||
from utilities.chem import FormatConverter, IndigoUtils
|
||||
from utilities.decorators import package_permission_required
|
||||
@ -34,6 +35,7 @@ from .models import (
|
||||
EnviFormer,
|
||||
EnzymeLink,
|
||||
EPModel,
|
||||
ExpansionSchemeChoice,
|
||||
ExternalDatabase,
|
||||
ExternalIdentifier,
|
||||
Group,
|
||||
@ -51,7 +53,6 @@ from .models import (
|
||||
SimpleAmbitRule,
|
||||
User,
|
||||
UserPackagePermission,
|
||||
ExpansionSchemeChoice,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -238,6 +239,15 @@ def register(request):
|
||||
try:
|
||||
u = UserManager.create_user(username, email, password)
|
||||
logger.info(f"Created user {u.username} ({u.pk})")
|
||||
|
||||
try:
|
||||
from .tasks import send_registration_mail
|
||||
|
||||
send_registration_mail.delay(u.pk)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to send registration mail to {u.email}: {e}")
|
||||
capture_exception(e)
|
||||
|
||||
except Exception:
|
||||
context["message"] = "Registration failed! Couldn't create User Account."
|
||||
return render(request, "static/login.html", context)
|
||||
@ -339,7 +349,7 @@ def breadcrumbs(
|
||||
{"Package": s.SERVER_URL + "/package"},
|
||||
]
|
||||
if first_level_object is not None:
|
||||
bread.append({first_level_object.name: first_level_object.url})
|
||||
bread.append({first_level_object.get_name(): first_level_object.url})
|
||||
|
||||
if second_level_namespace is not None:
|
||||
bread.append(
|
||||
@ -350,7 +360,7 @@ def breadcrumbs(
|
||||
)
|
||||
|
||||
if second_level_object is not None:
|
||||
bread.append({second_level_object.name: second_level_object.url})
|
||||
bread.append({second_level_object.get_name(): second_level_object.url})
|
||||
|
||||
if third_level_namespace is not None:
|
||||
bread.append(
|
||||
@ -361,7 +371,7 @@ def breadcrumbs(
|
||||
)
|
||||
|
||||
if third_level_object is not None:
|
||||
bread.append({third_level_object.name: third_level_object.url})
|
||||
bread.append({third_level_object.get_name(): third_level_object.url})
|
||||
|
||||
return bread
|
||||
|
||||
@ -462,7 +472,7 @@ def package_predict_pathway(request, package_uuid):
|
||||
current_package = PackageManager.get_package_by_id(current_user, package_uuid)
|
||||
|
||||
context = get_base_context(request)
|
||||
context["title"] = f"enviPath - {current_package.name} - Predict Pathway"
|
||||
context["title"] = f"enviPath - {current_package.get_name()} - Predict Pathway"
|
||||
context["meta"]["current_package"] = current_package
|
||||
|
||||
return render(request, "predict_pathway.html", context)
|
||||
@ -475,6 +485,10 @@ def packages(request):
|
||||
context = get_base_context(request)
|
||||
context["title"] = "enviPath - Packages"
|
||||
context["meta"]["current_package"] = context["meta"]["user"].default_package
|
||||
context["breadcrumbs"] = [
|
||||
{"Home": s.SERVER_URL},
|
||||
{"Package": s.SERVER_URL + "/package"},
|
||||
]
|
||||
|
||||
# Context for paginated template
|
||||
context["entity_type"] = "package"
|
||||
@ -529,6 +543,10 @@ def compounds(request):
|
||||
context = get_base_context(request)
|
||||
context["title"] = "enviPath - Compounds"
|
||||
context["meta"]["current_package"] = context["meta"]["user"].default_package
|
||||
context["breadcrumbs"] = [
|
||||
{"Home": s.SERVER_URL},
|
||||
{"Compound": s.SERVER_URL + "/compound"},
|
||||
]
|
||||
|
||||
# Context for paginated template
|
||||
context["entity_type"] = "compound"
|
||||
@ -759,7 +777,7 @@ def package_models(request, package_uuid):
|
||||
|
||||
if request.method == "GET":
|
||||
context = get_base_context(request)
|
||||
context["title"] = f"enviPath - {current_package.name} - Models"
|
||||
context["title"] = f"enviPath - {current_package.get_name()} - Models"
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
context["object_type"] = "model"
|
||||
@ -781,7 +799,7 @@ def package_models(request, package_uuid):
|
||||
return JsonResponse(
|
||||
{
|
||||
"objects": [
|
||||
{"name": pw.name, "url": pw.url, "reviewed": current_package.reviewed}
|
||||
{"name": pw.get_name(), "url": pw.url, "reviewed": current_package.reviewed}
|
||||
for pw in (
|
||||
reviewed_model_qs if current_package.reviewed else unreviewed_model_qs
|
||||
)
|
||||
@ -931,7 +949,7 @@ def package_model(request, package_uuid, model_uuid):
|
||||
return JsonResponse(app_domain_assessment, safe=False)
|
||||
|
||||
context = get_base_context(request)
|
||||
context["title"] = f"enviPath - {current_package.name} - {current_model.name}"
|
||||
context["title"] = f"enviPath - {current_package.get_name()} - {current_model.get_name()}"
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
context["object_type"] = "model"
|
||||
@ -1009,7 +1027,7 @@ def package(request, package_uuid):
|
||||
|
||||
if request.method == "GET":
|
||||
if request.GET.get("export", False) == "true":
|
||||
filename = f"{current_package.name.replace(' ', '_')}_{current_package.uuid}.json"
|
||||
filename = f"{current_package.get_name().replace(' ', '_')}_{current_package.uuid}.json"
|
||||
pack_json = PackageManager.export_package(
|
||||
current_package, include_models=False, include_external_identifiers=False
|
||||
)
|
||||
@ -1019,7 +1037,7 @@ def package(request, package_uuid):
|
||||
return response
|
||||
|
||||
context = get_base_context(request)
|
||||
context["title"] = f"enviPath - {current_package.name}"
|
||||
context["title"] = f"enviPath - {current_package.get_name()}"
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
context["object_type"] = "package"
|
||||
@ -1056,7 +1074,7 @@ def package(request, package_uuid):
|
||||
if current_user.default_package == current_package:
|
||||
return error(
|
||||
request,
|
||||
f'Package "{current_package.name}" is the default and cannot be deleted!',
|
||||
f'Package "{current_package.get_name()}" is the default and cannot be deleted!',
|
||||
"You cannot delete the default package. If you want to delete this package you have to set another default package first.",
|
||||
)
|
||||
|
||||
@ -1154,7 +1172,7 @@ def package_compounds(request, package_uuid):
|
||||
|
||||
if request.method == "GET":
|
||||
context = get_base_context(request)
|
||||
context["title"] = f"enviPath - {current_package.name} - Compounds"
|
||||
context["title"] = f"enviPath - {current_package.get_name()} - Compounds"
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
context["object_type"] = "compound"
|
||||
@ -1179,7 +1197,7 @@ def package_compounds(request, package_uuid):
|
||||
return JsonResponse(
|
||||
{
|
||||
"objects": [
|
||||
{"name": pw.name, "url": pw.url, "reviewed": current_package.reviewed}
|
||||
{"name": pw.get_name(), "url": pw.url, "reviewed": current_package.reviewed}
|
||||
for pw in (
|
||||
reviewed_compound_qs
|
||||
if current_package.reviewed
|
||||
@ -1216,7 +1234,9 @@ def package_compound(request, package_uuid, compound_uuid):
|
||||
|
||||
if request.method == "GET":
|
||||
context = get_base_context(request)
|
||||
context["title"] = f"enviPath - {current_package.name} - {current_compound.name}"
|
||||
context["title"] = (
|
||||
f"enviPath - {current_package.get_name()} - {current_compound.get_name()}"
|
||||
)
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
context["object_type"] = "compound"
|
||||
@ -1300,7 +1320,7 @@ def package_compound_structures(request, package_uuid, compound_uuid):
|
||||
if request.method == "GET":
|
||||
context = get_base_context(request)
|
||||
context["title"] = (
|
||||
f"enviPath - {current_package.name} - {current_compound.name} - Structures"
|
||||
f"enviPath - {current_package.get_name()} - {current_compound.get_name()} - Structures"
|
||||
)
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
@ -1309,7 +1329,7 @@ def package_compound_structures(request, package_uuid, compound_uuid):
|
||||
current_package, "compound", current_compound, "structure"
|
||||
)
|
||||
context["entity_type"] = "structure"
|
||||
context["page_title"] = f"{current_compound.name} - Structures"
|
||||
context["page_title"] = f"{current_compound.get_name()} - Structures"
|
||||
context["api_endpoint"] = (
|
||||
f"/api/v1/package/{current_package.uuid}/compound/{current_compound.uuid}/structure/"
|
||||
)
|
||||
@ -1362,7 +1382,7 @@ def package_compound_structure(request, package_uuid, compound_uuid, structure_u
|
||||
|
||||
context = get_base_context(request)
|
||||
context["title"] = (
|
||||
f"enviPath - {current_package.name} - {current_compound.name} - {current_structure.name}"
|
||||
f"enviPath - {current_package.get_name()} - {current_compound.get_name()} - {current_structure.get_name()}"
|
||||
)
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
@ -1468,7 +1488,7 @@ def package_rules(request, package_uuid):
|
||||
|
||||
if request.method == "GET":
|
||||
context = get_base_context(request)
|
||||
context["title"] = f"enviPath - {current_package.name} - Rules"
|
||||
context["title"] = f"enviPath - {current_package.get_name()} - Rules"
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
context["object_type"] = "rule"
|
||||
@ -1490,7 +1510,7 @@ def package_rules(request, package_uuid):
|
||||
return JsonResponse(
|
||||
{
|
||||
"objects": [
|
||||
{"name": pw.name, "url": pw.url, "reviewed": current_package.reviewed}
|
||||
{"name": pw.get_name(), "url": pw.url, "reviewed": current_package.reviewed}
|
||||
for pw in (
|
||||
reviewed_rule_qs if current_package.reviewed else unreviewed_rule_qs
|
||||
)
|
||||
@ -1580,7 +1600,7 @@ def package_rule(request, package_uuid, rule_uuid):
|
||||
content_type="image/svg+xml",
|
||||
)
|
||||
|
||||
context["title"] = f"enviPath - {current_package.name} - {current_rule.name}"
|
||||
context["title"] = f"enviPath - {current_package.get_name()} - {current_rule.get_name()}"
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
context["object_type"] = "rule"
|
||||
@ -1653,7 +1673,7 @@ def package_rule_enzymelink(request, package_uuid, rule_uuid, enzymelink_uuid):
|
||||
if request.method == "GET":
|
||||
context = get_base_context(request)
|
||||
|
||||
context["title"] = f"enviPath - {current_package.name} - {current_rule.name}"
|
||||
context["title"] = f"enviPath - {current_package.get_name()} - {current_rule.get_name()}"
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
context["object_type"] = "enzyme"
|
||||
@ -1676,7 +1696,7 @@ def package_reactions(request, package_uuid):
|
||||
|
||||
if request.method == "GET":
|
||||
context = get_base_context(request)
|
||||
context["title"] = f"enviPath - {current_package.name} - Reactions"
|
||||
context["title"] = f"enviPath - {current_package.get_name()} - Reactions"
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
context["object_type"] = "reaction"
|
||||
@ -1700,7 +1720,7 @@ def package_reactions(request, package_uuid):
|
||||
return JsonResponse(
|
||||
{
|
||||
"objects": [
|
||||
{"name": pw.name, "url": pw.url, "reviewed": current_package.reviewed}
|
||||
{"name": pw.get_name(), "url": pw.url, "reviewed": current_package.reviewed}
|
||||
for pw in (
|
||||
reviewed_reaction_qs
|
||||
if current_package.reviewed
|
||||
@ -1741,7 +1761,9 @@ def package_reaction(request, package_uuid, reaction_uuid):
|
||||
|
||||
if request.method == "GET":
|
||||
context = get_base_context(request)
|
||||
context["title"] = f"enviPath - {current_package.name} - {current_reaction.name}"
|
||||
context["title"] = (
|
||||
f"enviPath - {current_package.get_name()} - {current_reaction.get_name()}"
|
||||
)
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
context["object_type"] = "reaction"
|
||||
@ -1824,7 +1846,7 @@ def package_pathways(request, package_uuid):
|
||||
|
||||
if request.method == "GET":
|
||||
context = get_base_context(request)
|
||||
context["title"] = f"enviPath - {current_package.name} - Pathways"
|
||||
context["title"] = f"enviPath - {current_package.get_name()} - Pathways"
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
context["object_type"] = "pathway"
|
||||
@ -1846,7 +1868,7 @@ def package_pathways(request, package_uuid):
|
||||
return JsonResponse(
|
||||
{
|
||||
"objects": [
|
||||
{"name": pw.name, "url": pw.url, "reviewed": current_package.reviewed}
|
||||
{"name": pw.get_name(), "url": pw.url, "reviewed": current_package.reviewed}
|
||||
for pw in (
|
||||
reviewed_pathway_qs
|
||||
if current_package.reviewed
|
||||
@ -1953,7 +1975,7 @@ def package_pathway(request, package_uuid, pathway_uuid):
|
||||
)
|
||||
|
||||
if request.GET.get("download", False) == "true":
|
||||
filename = f"{current_pathway.name.replace(' ', '_')}_{current_pathway.uuid}.csv"
|
||||
filename = f"{current_pathway.get_name().replace(' ', '_')}_{current_pathway.uuid}.csv"
|
||||
csv_pw = current_pathway.to_csv()
|
||||
response = HttpResponse(csv_pw, content_type="text/csv")
|
||||
response["Content-Disposition"] = f'attachment; filename="{filename}"'
|
||||
@ -1973,7 +1995,7 @@ def package_pathway(request, package_uuid, pathway_uuid):
|
||||
current_user, identify_missing_rules, [current_pathway.pk], rule_package.pk
|
||||
)
|
||||
|
||||
filename = f"{current_pathway.name.replace(' ', '_')}_{current_pathway.uuid}.csv"
|
||||
filename = f"{current_pathway.get_name().replace(' ', '_')}_{current_pathway.uuid}.csv"
|
||||
response = HttpResponse(res, content_type="text/csv")
|
||||
response["Content-Disposition"] = f'attachment; filename="{filename}"'
|
||||
|
||||
@ -1996,7 +2018,7 @@ def package_pathway(request, package_uuid, pathway_uuid):
|
||||
).get(uuid=pathway_uuid)
|
||||
|
||||
context = get_base_context(request)
|
||||
context["title"] = f"enviPath - {current_package.name} - {current_pathway.name}"
|
||||
context["title"] = f"enviPath - {current_package.get_name()} - {current_pathway.get_name()}"
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
context["object_type"] = "pathway"
|
||||
@ -2008,9 +2030,9 @@ def package_pathway(request, package_uuid, pathway_uuid):
|
||||
context["breadcrumbs"] = [
|
||||
{"Home": s.SERVER_URL},
|
||||
{"Package": s.SERVER_URL + "/package"},
|
||||
{current_package.name: current_package.url},
|
||||
{current_package.get_name(): current_package.url},
|
||||
{"Pathway": current_package.url + "/pathway"},
|
||||
{current_pathway.name: current_pathway.url},
|
||||
{current_pathway.get_name(): current_pathway.url},
|
||||
]
|
||||
|
||||
return render(request, "objects/pathway.html", context)
|
||||
@ -2097,16 +2119,18 @@ def package_pathway_nodes(request, package_uuid, pathway_uuid):
|
||||
|
||||
if request.method == "GET":
|
||||
context = get_base_context(request)
|
||||
context["title"] = f"enviPath - {current_package.name} - {current_pathway.name} - Nodes"
|
||||
context["title"] = (
|
||||
f"enviPath - {current_package.get_name()} - {current_pathway.get_name()} - Nodes"
|
||||
)
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
context["object_type"] = "node"
|
||||
context["breadcrumbs"] = [
|
||||
{"Home": s.SERVER_URL},
|
||||
{"Package": s.SERVER_URL + "/package"},
|
||||
{current_package.name: current_package.url},
|
||||
{current_package.get_name(): current_package.url},
|
||||
{"Pathway": current_package.url + "/pathway"},
|
||||
{current_pathway.name: current_pathway.url},
|
||||
{current_pathway.get_name(): current_pathway.url},
|
||||
{"Node": current_pathway.url + "/node"},
|
||||
]
|
||||
|
||||
@ -2122,7 +2146,7 @@ def package_pathway_nodes(request, package_uuid, pathway_uuid):
|
||||
return JsonResponse(
|
||||
{
|
||||
"objects": [
|
||||
{"name": pw.name, "url": pw.url, "reviewed": current_package.reviewed}
|
||||
{"name": pw.get_name(), "url": pw.url, "reviewed": current_package.reviewed}
|
||||
for pw in (
|
||||
reviewed_node_qs if current_package.reviewed else unreviewed_node_qs
|
||||
)
|
||||
@ -2196,7 +2220,7 @@ def package_pathway_node(request, package_uuid, pathway_uuid, node_uuid):
|
||||
return HttpResponse(svg_data, content_type="image/svg+xml")
|
||||
|
||||
context = get_base_context(request)
|
||||
context["title"] = f"enviPath - {current_package.name} - {current_pathway.name}"
|
||||
context["title"] = f"enviPath - {current_package.get_name()} - {current_pathway.get_name()}"
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
context["object_type"] = "pathway"
|
||||
@ -2204,11 +2228,11 @@ def package_pathway_node(request, package_uuid, pathway_uuid, node_uuid):
|
||||
context["breadcrumbs"] = [
|
||||
{"Home": s.SERVER_URL},
|
||||
{"Package": s.SERVER_URL + "/package"},
|
||||
{current_package.name: current_package.url},
|
||||
{current_package.get_name(): current_package.url},
|
||||
{"Pathway": current_package.url + "/pathway"},
|
||||
{current_pathway.name: current_pathway.url},
|
||||
{current_pathway.get_name(): current_pathway.url},
|
||||
{"Node": current_pathway.url + "/node"},
|
||||
{current_node.name: current_node.url},
|
||||
{current_node.get_name(): current_node.url},
|
||||
]
|
||||
|
||||
context["node"] = current_node
|
||||
@ -2261,16 +2285,18 @@ def package_pathway_edges(request, package_uuid, pathway_uuid):
|
||||
|
||||
if request.method == "GET":
|
||||
context = get_base_context(request)
|
||||
context["title"] = f"enviPath - {current_package.name} - {current_pathway.name} - Edges"
|
||||
context["title"] = (
|
||||
f"enviPath - {current_package.get_name()} - {current_pathway.get_name()} - Edges"
|
||||
)
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
context["object_type"] = "edge"
|
||||
context["breadcrumbs"] = [
|
||||
{"Home": s.SERVER_URL},
|
||||
{"Package": s.SERVER_URL + "/package"},
|
||||
{current_package.name: current_package.url},
|
||||
{current_package.get_name(): current_package.url},
|
||||
{"Pathway": current_package.url + "/pathway"},
|
||||
{current_pathway.name: current_pathway.url},
|
||||
{current_pathway.get_name(): current_pathway.url},
|
||||
{"Edge": current_pathway.url + "/edge"},
|
||||
]
|
||||
|
||||
@ -2286,7 +2312,7 @@ def package_pathway_edges(request, package_uuid, pathway_uuid):
|
||||
return JsonResponse(
|
||||
{
|
||||
"objects": [
|
||||
{"name": pw.name, "url": pw.url, "reviewed": current_package.reviewed}
|
||||
{"name": pw.get_name(), "url": pw.url, "reviewed": current_package.reviewed}
|
||||
for pw in (
|
||||
reviewed_edge_qs if current_package.reviewed else unreviewed_edge_qs
|
||||
)
|
||||
@ -2338,7 +2364,7 @@ def package_pathway_edge(request, package_uuid, pathway_uuid, edge_uuid):
|
||||
|
||||
context = get_base_context(request)
|
||||
context["title"] = (
|
||||
f"enviPath - {current_package.name} - {current_pathway.name} - {current_edge.edge_label.name}"
|
||||
f"enviPath - {current_package.get_name()} - {current_pathway.get_name()} - {current_edge.edge_label.get_name()}"
|
||||
)
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
@ -2391,11 +2417,11 @@ def package_scenarios(request, package_uuid):
|
||||
"all", False
|
||||
):
|
||||
scens = Scenario.objects.filter(package=current_package).order_by("name")
|
||||
res = [{"name": s_.name, "url": s_.url, "uuid": s_.uuid} for s_ in scens]
|
||||
res = [{"name": s_.get_name(), "url": s_.url, "uuid": s_.uuid} for s_ in scens]
|
||||
return JsonResponse(res, safe=False)
|
||||
|
||||
context = get_base_context(request)
|
||||
context["title"] = f"enviPath - {current_package.name} - Scenarios"
|
||||
context["title"] = f"enviPath - {current_package.get_name()} - Scenarios"
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
context["object_type"] = "scenario"
|
||||
@ -2419,7 +2445,7 @@ def package_scenarios(request, package_uuid):
|
||||
return JsonResponse(
|
||||
{
|
||||
"objects": [
|
||||
{"name": pw.name, "url": pw.url, "reviewed": current_package.reviewed}
|
||||
{"name": pw.get_name(), "url": pw.url, "reviewed": current_package.reviewed}
|
||||
for pw in (
|
||||
reviewed_scenario_qs
|
||||
if current_package.reviewed
|
||||
@ -2511,7 +2537,9 @@ def package_scenario(request, package_uuid, scenario_uuid):
|
||||
|
||||
if request.method == "GET":
|
||||
context = get_base_context(request)
|
||||
context["title"] = f"enviPath - {current_package.name} - {current_scenario.name}"
|
||||
context["title"] = (
|
||||
f"enviPath - {current_package.get_name()} - {current_scenario.get_name()}"
|
||||
)
|
||||
|
||||
context["meta"]["current_package"] = current_package
|
||||
context["object_type"] = "scenario"
|
||||
@ -2748,13 +2776,13 @@ def group(request, group_uuid):
|
||||
|
||||
if request.method == "GET":
|
||||
context = get_base_context(request)
|
||||
context["title"] = f"enviPath - {current_group.name}"
|
||||
context["title"] = f"enviPath - {current_group.get_name()}"
|
||||
|
||||
context["object_type"] = "group"
|
||||
context["breadcrumbs"] = [
|
||||
{"Home": s.SERVER_URL},
|
||||
{"Group": s.SERVER_URL + "/group"},
|
||||
{current_group.name: current_group.url},
|
||||
{current_group.get_name(): current_group.url},
|
||||
]
|
||||
|
||||
context["group"] = current_group
|
||||
@ -2909,13 +2937,13 @@ def setting(request, setting_uuid):
|
||||
|
||||
if request.method == "GET":
|
||||
context = get_base_context(request)
|
||||
context["title"] = f"enviPath - {current_setting.name}"
|
||||
context["title"] = f"enviPath - {current_setting.get_name()}"
|
||||
|
||||
context["object_type"] = "setting"
|
||||
context["breadcrumbs"] = [
|
||||
{"Home": s.SERVER_URL},
|
||||
{"Setting": s.SERVER_URL + "/setting"},
|
||||
{f"{current_setting.name}": current_setting.url},
|
||||
{f"{current_setting.get_name()}": current_setting.url},
|
||||
]
|
||||
|
||||
context["setting"] = current_setting
|
||||
@ -2964,8 +2992,8 @@ def jobs(request):
|
||||
|
||||
target_package = PackageManager.create_package(
|
||||
current_user,
|
||||
f"Autogenerated Package for Pathway Engineering of {pathway_to_engineer.name}",
|
||||
f"This Package was generated automatically for the engineering Task of {pathway_to_engineer.name}.",
|
||||
f"Autogenerated Package for Pathway Engineering of {pathway_to_engineer.get_name()}",
|
||||
f"This Package was generated automatically for the engineering Task of {pathway_to_engineer.get_name()}.",
|
||||
)
|
||||
|
||||
from .tasks import dispatch, engineer_pathways
|
||||
@ -3019,7 +3047,7 @@ def jobs(request):
|
||||
"This Package was generated automatically for the batch prediction task.",
|
||||
)
|
||||
|
||||
from .tasks import dispatch, batch_predict
|
||||
from .tasks import batch_predict, dispatch
|
||||
|
||||
res = dispatch(
|
||||
current_user,
|
||||
@ -3057,6 +3085,8 @@ def job(request, job_uuid):
|
||||
|
||||
if job.job_name == "batch_predict":
|
||||
filename = f"{job.job_name.replace(' ', '_')}_{job.task_id}.csv"
|
||||
elif job.job_name == "identify_missing_rules":
|
||||
filename = f"{job.job_name.replace(' ', '_')}_{job.task_id}.csv"
|
||||
else:
|
||||
raise BadRequest("Result is not downloadable!")
|
||||
|
||||
|
||||
3
pnpm-workspace.yaml
Normal file
3
pnpm-workspace.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
onlyBuiltDependencies:
|
||||
- '@parcel/watcher'
|
||||
- '@tailwindcss/oxide'
|
||||
@ -393,7 +393,14 @@ function draw(pathway, elem) {
|
||||
}
|
||||
|
||||
function edge_popup(e) {
|
||||
popupContent = "<a href='" + e.url + "'>" + e.name + "</a><br>";
|
||||
popupContent = "<a href='" + e.url + "'>" + e.name + "</a><br><br>";
|
||||
|
||||
if (e.reaction.rules) {
|
||||
console.log(e.reaction.rules);
|
||||
for (var rule of e.reaction.rules) {
|
||||
popupContent += "Rule <a href='" + rule.url + "'>" + rule.name + "</a><br>";
|
||||
}
|
||||
}
|
||||
|
||||
if (e.app_domain) {
|
||||
adcontent = "<p>";
|
||||
|
||||
@ -176,8 +176,8 @@
|
||||
href="#"
|
||||
class="example-link hover:text-primary cursor-pointer"
|
||||
title="load example"
|
||||
@click.prevent="loadExample('CC(C)CC1=CC=C(C=C1)C(C)C(=O)O', $el)"
|
||||
>Ibuprofen</a
|
||||
@click.prevent="loadExample('COC(=O)[C@H](CC1=CC=CC=C1)NC(=O)[C@H](CC(=O)O)N', $el)"
|
||||
>Aspartame</a
|
||||
>
|
||||
</div>
|
||||
<a
|
||||
|
||||
@ -85,6 +85,55 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if compound_structure.half_lifes %}
|
||||
<div class="collapse-arrow bg-base-200 collapse">
|
||||
<input type="checkbox" />
|
||||
<div class="collapse-title text-xl font-medium">Half-lives</div>
|
||||
<div class="collapse-content">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table-zebra table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Scenario</th>
|
||||
<th>Values</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for scenario, half_lifes in compound_structure.half_lifes.items %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ scenario.url }}" class="hover:bg-base-200"
|
||||
>{{ scenario.name }}
|
||||
<i>({{ scenario.package.name }})</i></a
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<table class="table-zebra table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Scenario Type</td>
|
||||
<td>{{ scenario.scenario_type }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Half-life (days)</td>
|
||||
<td>{{ half_lifes.0.dt50 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Model</td>
|
||||
<td>{{ half_lifes.0.model }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if compound_structure.scenarios.all %}
|
||||
<!-- Scenarios -->
|
||||
<div class="collapse-arrow bg-base-200 collapse">
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
<div class="card bg-base-100">
|
||||
<div class="card-body">
|
||||
<div class="flex items-center justify-between">
|
||||
<h2 class="card-title text-2xl">{{ edge.edge_label.name }}</h2>
|
||||
<h2 class="card-title text-2xl">{{ edge.get_name }}</h2>
|
||||
<div id="actionsButton" class="dropdown dropdown-end hidden">
|
||||
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
|
||||
<svg
|
||||
@ -45,6 +45,11 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-2">
|
||||
The underlying reaction can be found
|
||||
<a href="{{ edge.edge_label.url }}" class="link link-primary">here</a
|
||||
>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -103,7 +103,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% elif job.job_name == 'batch_predict' %}
|
||||
{% elif job.job_name == 'batch_predict' or job.job_name == 'identify_missing_rules' %}
|
||||
<div
|
||||
id="table-container"
|
||||
class="overflow-x-auto overflow-y-auto max-h-96 border rounded-lg"
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
<div class="card bg-base-100">
|
||||
<div class="card-body">
|
||||
<div class="flex items-center justify-between">
|
||||
<h2 class="card-title text-2xl">{{ node.name }}</h2>
|
||||
<h2 class="card-title text-2xl">{{ node.get_name }}</h2>
|
||||
<div id="actionsButton" class="dropdown dropdown-end hidden">
|
||||
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
|
||||
<svg
|
||||
@ -54,28 +54,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
{% if node.aliases %}
|
||||
<!-- Aliases -->
|
||||
<div class="collapse-arrow bg-base-200 collapse">
|
||||
<input type="checkbox" checked />
|
||||
<div class="collapse-title text-xl font-medium">Aliases</div>
|
||||
<div class="collapse-content">
|
||||
<ul class="menu bg-base-100 rounded-box">
|
||||
{% for alias in node.aliases %}
|
||||
<li><a class="hover:bg-base-200">{{ alias }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Image Representation -->
|
||||
<div class="collapse-arrow bg-base-200 collapse">
|
||||
<input type="checkbox" checked />
|
||||
@ -96,6 +74,70 @@
|
||||
<div class="collapse-content">{{ node.default_node_label.smiles }}</div>
|
||||
</div>
|
||||
|
||||
{% if node.default_node_label.aliases %}
|
||||
<!-- Aliases -->
|
||||
<div class="collapse-arrow bg-base-200 collapse">
|
||||
<input type="checkbox" checked />
|
||||
<div class="collapse-title text-xl font-medium">Aliases</div>
|
||||
<div class="collapse-content">
|
||||
<ul class="menu bg-base-100 rounded-box">
|
||||
{% for alias in node.default_node_label.aliases %}
|
||||
<li><a class="hover:bg-base-200">{{ alias }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if node.default_node_label.half_lifes %}
|
||||
<div class="collapse-arrow bg-base-200 collapse">
|
||||
<input type="checkbox" />
|
||||
<div class="collapse-title text-xl font-medium">Half-lives</div>
|
||||
<div class="collapse-content">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table-zebra table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Scenario</th>
|
||||
<th>Values</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for scenario, half_lifes in node.default_node_label.half_lifes.items %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ scenario.url }}" class="hover:bg-base-200"
|
||||
>{{ scenario.name }}
|
||||
<i>({{ scenario.package.name }})</i></a
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<table class="table-zebra table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Scenario Type</td>
|
||||
<td>{{ scenario.scenario_type }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Half-life (days)</td>
|
||||
<td>{{ half_lifes.0.dt50 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Model</td>
|
||||
<td>{{ half_lifes.0.model }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if node.scenarios.all %}
|
||||
<!-- Scenarios -->
|
||||
<div class="collapse-arrow bg-base-200 collapse">
|
||||
|
||||
Reference in New Issue
Block a user