forked from enviPath/enviPy
Compare commits
15 Commits
fix/issue2
...
fix/xss
| Author | SHA1 | Date | |
|---|---|---|---|
| 35c342a3e3 | |||
| db9036ce72 | |||
| 01a20e438d | |||
| 654707e6b5 | |||
| c88b0ff3e7 | |||
| 4246460f91 | |||
| 44b646e58a | |||
| 2194b097ae | |||
| 4524b8fdf3 | |||
| c663eaf7bd | |||
| ec0fc8cdc1 | |||
| 61346c4097 | |||
| 43bce8a4e1 | |||
| 8d955d685c | |||
| 17744294cc |
@ -92,6 +92,8 @@ TEMPLATES = [
|
||||
},
|
||||
]
|
||||
|
||||
ALLOWED_HTML_TAGS = {'b', 'i', 'u', 'br', 'em', 'mark', 'p', 's', 'strong'}
|
||||
|
||||
WSGI_APPLICATION = "envipath.wsgi.application"
|
||||
|
||||
# Database
|
||||
|
||||
@ -4,6 +4,7 @@ import json
|
||||
from typing import Union, List, Optional, Set, Dict, Any
|
||||
from uuid import UUID
|
||||
|
||||
import nh3
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.db import transaction
|
||||
from django.conf import settings as s
|
||||
@ -185,6 +186,12 @@ class UserManager(object):
|
||||
def create_user(
|
||||
username, email, password, set_setting=True, add_to_group=True, *args, **kwargs
|
||||
):
|
||||
# Clean for potential XSS
|
||||
clean_username = nh3.clean(username).strip()
|
||||
clean_email = nh3.clean(email).strip()
|
||||
if clean_username != username or clean_email != email:
|
||||
# This will be caught by the try in view.py/register
|
||||
raise ValueError("Invalid username or password")
|
||||
# avoid circular import :S
|
||||
from .tasks import send_registration_mail
|
||||
|
||||
@ -262,8 +269,9 @@ class GroupManager(object):
|
||||
@staticmethod
|
||||
def create_group(current_user, name, description):
|
||||
g = Group()
|
||||
g.name = name
|
||||
g.description = description
|
||||
# Clean for potential XSS
|
||||
g.name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
g.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
g.owner = current_user
|
||||
g.save()
|
||||
|
||||
@ -518,8 +526,13 @@ class PackageManager(object):
|
||||
@transaction.atomic
|
||||
def create_package(current_user, name: str, description: str = None):
|
||||
p = Package()
|
||||
p.name = name
|
||||
p.description = description
|
||||
|
||||
# Clean for potential XSS
|
||||
p.name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
if description is not None and description.strip() != "":
|
||||
p.description = nh3.clean(description.strip(), tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
p.save()
|
||||
|
||||
up = UserPackagePermission()
|
||||
@ -1094,28 +1107,29 @@ class SettingManager(object):
|
||||
model: EPModel = None,
|
||||
model_threshold: float = None,
|
||||
):
|
||||
s = Setting()
|
||||
s.name = name
|
||||
s.description = description
|
||||
s.max_nodes = max_nodes
|
||||
s.max_depth = max_depth
|
||||
s.model = model
|
||||
s.model_threshold = model_threshold
|
||||
new_s = Setting()
|
||||
# Clean for potential XSS
|
||||
new_s.name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
new_s.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
new_s.max_nodes = max_nodes
|
||||
new_s.max_depth = max_depth
|
||||
new_s.model = model
|
||||
new_s.model_threshold = model_threshold
|
||||
|
||||
s.save()
|
||||
new_s.save()
|
||||
|
||||
if rule_packages is not None:
|
||||
for r in rule_packages:
|
||||
s.rule_packages.add(r)
|
||||
s.save()
|
||||
new_s.rule_packages.add(r)
|
||||
new_s.save()
|
||||
|
||||
usp = UserSettingPermission()
|
||||
usp.user = user
|
||||
usp.setting = s
|
||||
usp.setting = new_s
|
||||
usp.permission = Permission.ALL[0]
|
||||
usp.save()
|
||||
|
||||
return s
|
||||
return new_s
|
||||
|
||||
@staticmethod
|
||||
def get_default_setting(user: User):
|
||||
@ -1542,7 +1556,9 @@ class SPathway(object):
|
||||
if sub.app_domain_assessment is None:
|
||||
if self.prediction_setting.model:
|
||||
if self.prediction_setting.model.app_domain:
|
||||
app_domain_assessment = self.prediction_setting.model.app_domain.assess(sub.smiles)
|
||||
app_domain_assessment = self.prediction_setting.model.app_domain.assess(
|
||||
sub.smiles
|
||||
)
|
||||
|
||||
if self.persist is not None:
|
||||
n = self.snode_persist_lookup[sub]
|
||||
@ -1574,7 +1590,9 @@ class SPathway(object):
|
||||
app_domain_assessment = None
|
||||
if self.prediction_setting.model:
|
||||
if self.prediction_setting.model.app_domain:
|
||||
app_domain_assessment = (self.prediction_setting.model.app_domain.assess(c))
|
||||
app_domain_assessment = (
|
||||
self.prediction_setting.model.app_domain.assess(c)
|
||||
)
|
||||
|
||||
self.smiles_to_node[c] = SNode(
|
||||
c, sub.depth + 1, app_domain_assessment
|
||||
|
||||
190
epdb/models.py
190
epdb/models.py
@ -11,6 +11,7 @@ from typing import Union, List, Optional, Dict, Tuple, Set, Any
|
||||
from uuid import uuid4
|
||||
import math
|
||||
import joblib
|
||||
import nh3
|
||||
import numpy as np
|
||||
from django.conf import settings as s
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
@ -28,8 +29,14 @@ from sklearn.metrics import precision_score, recall_score, jaccard_score
|
||||
from sklearn.model_selection import ShuffleSplit
|
||||
|
||||
from utilities.chem import FormatConverter, ProductSet, PredictionResult, IndigoUtils
|
||||
from utilities.ml import RuleBasedDataset, ApplicabilityDomainPCA, EnsembleClassifierChain, RelativeReasoning, \
|
||||
EnviFormerDataset, Dataset
|
||||
from utilities.ml import (
|
||||
RuleBasedDataset,
|
||||
ApplicabilityDomainPCA,
|
||||
EnsembleClassifierChain,
|
||||
RelativeReasoning,
|
||||
EnviFormerDataset,
|
||||
Dataset,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -803,14 +810,16 @@ class Compound(EnviPathModel, AliasMixin, ScenarioMixin, ChemicalIdentifierMixin
|
||||
c = Compound()
|
||||
c.package = package
|
||||
|
||||
if name is None or name.strip() == "":
|
||||
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
|
||||
if description is not None and description.strip() != "":
|
||||
c.description = description.strip()
|
||||
c.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
c.save()
|
||||
|
||||
@ -982,11 +991,11 @@ class CompoundStructure(EnviPathModel, AliasMixin, ScenarioMixin, ChemicalIdenti
|
||||
raise ValueError("Unpersisted Compound! Persist compound first!")
|
||||
|
||||
cs = CompoundStructure()
|
||||
# Clean for potential XSS
|
||||
if name is not None:
|
||||
cs.name = name
|
||||
|
||||
cs.name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
if description is not None:
|
||||
cs.description = description
|
||||
cs.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
cs.smiles = smiles
|
||||
cs.compound = compound
|
||||
@ -1188,21 +1197,29 @@ class SimpleAmbitRule(SimpleRule):
|
||||
r = SimpleAmbitRule()
|
||||
r.package = package
|
||||
|
||||
if name is None or name.strip() == "":
|
||||
if name is not None:
|
||||
name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
if name is None or name == "":
|
||||
name = f"Rule {Rule.objects.filter(package=package).count() + 1}"
|
||||
|
||||
r.name = name
|
||||
|
||||
if description is not None and description.strip() != "":
|
||||
r.description = description
|
||||
r.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
r.smirks = smirks
|
||||
|
||||
if reactant_filter_smarts is not None and reactant_filter_smarts.strip() != "":
|
||||
r.reactant_filter_smarts = reactant_filter_smarts
|
||||
if not FormatConverter.is_valid_smarts(reactant_filter_smarts.strip()):
|
||||
raise ValueError(f'Reactant Filter SMARTS "{reactant_filter_smarts}" is invalid!')
|
||||
else:
|
||||
r.reactant_filter_smarts = reactant_filter_smarts.strip()
|
||||
|
||||
if product_filter_smarts is not None and product_filter_smarts.strip() != "":
|
||||
r.product_filter_smarts = product_filter_smarts
|
||||
if not FormatConverter.is_valid_smarts(product_filter_smarts.strip()):
|
||||
raise ValueError(f'Product Filter SMARTS "{product_filter_smarts}" is invalid!')
|
||||
else:
|
||||
r.product_filter_smarts = product_filter_smarts.strip()
|
||||
|
||||
r.save()
|
||||
return r
|
||||
@ -1403,12 +1420,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 = name
|
||||
|
||||
r.name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
if description is not None and name.strip() != "":
|
||||
r.description = description
|
||||
r.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
r.multi_step = multi_step
|
||||
|
||||
@ -1716,14 +1732,15 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin):
|
||||
):
|
||||
pw = Pathway()
|
||||
pw.package = package
|
||||
|
||||
if name is None or name.strip() == "":
|
||||
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 = description
|
||||
pw.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
pw.save()
|
||||
try:
|
||||
@ -2018,11 +2035,16 @@ class Edge(EnviPathModel, AliasMixin, ScenarioMixin):
|
||||
for node in end_nodes:
|
||||
e.end_nodes.add(node)
|
||||
|
||||
if name is None:
|
||||
# Clean for potential XSS
|
||||
# Cleaning technically not needed as it is also done in Reaction.create, including it here for consistency
|
||||
if name is not None:
|
||||
name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
if name is None or name == "":
|
||||
name = f"Reaction {pathway.package.reactions.count() + 1}"
|
||||
|
||||
if description is None:
|
||||
description = s.DEFAULT_VALUES["description"]
|
||||
description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
r = Reaction.create(
|
||||
pathway.package,
|
||||
@ -2344,7 +2366,9 @@ class PackageBasedModel(EPModel):
|
||||
eval_reactions = list(
|
||||
Reaction.objects.filter(package__in=self.eval_packages.all()).distinct()
|
||||
)
|
||||
ds = RuleBasedDataset.generate_dataset(eval_reactions, self.applicable_rules, educts_only=True)
|
||||
ds = RuleBasedDataset.generate_dataset(
|
||||
eval_reactions, self.applicable_rules, educts_only=True
|
||||
)
|
||||
if isinstance(self, RuleBasedRelativeReasoning):
|
||||
X = ds.X(exclude_id_col=False, na_replacement=None).to_numpy()
|
||||
y = ds.y(na_replacement=np.nan).to_numpy()
|
||||
@ -2542,14 +2566,15 @@ class RuleBasedRelativeReasoning(PackageBasedModel):
|
||||
):
|
||||
rbrr = RuleBasedRelativeReasoning()
|
||||
rbrr.package = package
|
||||
|
||||
if name is None or name.strip() == "":
|
||||
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"RuleBasedRelativeReasoning {RuleBasedRelativeReasoning.objects.filter(package=package).count() + 1}"
|
||||
|
||||
rbrr.name = name
|
||||
|
||||
if description is not None and description.strip() != "":
|
||||
rbrr.description = description
|
||||
rbrr.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
if threshold is None or (threshold <= 0 or 1 <= threshold):
|
||||
raise ValueError("Threshold must be a float between 0 and 1.")
|
||||
@ -2646,14 +2671,15 @@ class MLRelativeReasoning(PackageBasedModel):
|
||||
):
|
||||
mlrr = MLRelativeReasoning()
|
||||
mlrr.package = package
|
||||
|
||||
if name is None or name.strip() == "":
|
||||
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"MLRelativeReasoning {MLRelativeReasoning.objects.filter(package=package).count() + 1}"
|
||||
|
||||
mlrr.name = name
|
||||
|
||||
if description is not None and description.strip() != "":
|
||||
mlrr.description = description
|
||||
mlrr.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
if threshold is None or (threshold <= 0 or 1 <= threshold):
|
||||
raise ValueError("Threshold must be a float between 0 and 1.")
|
||||
@ -2807,7 +2833,9 @@ class ApplicabilityDomain(EnviPathModel):
|
||||
else:
|
||||
smiles.append(structures)
|
||||
|
||||
assessment_ds, assessment_prods = ds.classification_dataset(structures, self.model.applicable_rules)
|
||||
assessment_ds, assessment_prods = ds.classification_dataset(
|
||||
structures, self.model.applicable_rules
|
||||
)
|
||||
|
||||
# qualified_neighbours_per_rule is a nested dictionary structured as:
|
||||
# {
|
||||
@ -2823,12 +2851,16 @@ class ApplicabilityDomain(EnviPathModel):
|
||||
qualified_neighbours_per_rule: Dict = {}
|
||||
|
||||
import polars as pl
|
||||
|
||||
# Select only the triggered columns
|
||||
for i, row in enumerate(assessment_ds[:, assessment_ds.triggered()].iter_rows(named=True)):
|
||||
# Find the rules the structure triggers. For each rule, filter the training dataset to rows that also
|
||||
# trigger that rule.
|
||||
train_trig = {trig_uuid.split("_")[-1]: ds.filter(pl.col(trig_uuid).eq(1))
|
||||
for trig_uuid, value in row.items() if value == 1}
|
||||
train_trig = {
|
||||
trig_uuid.split("_")[-1]: ds.filter(pl.col(trig_uuid).eq(1))
|
||||
for trig_uuid, value in row.items()
|
||||
if value == 1
|
||||
}
|
||||
qualified_neighbours_per_rule[i] = train_trig
|
||||
rule_to_i = {str(r.uuid): i for i, r in enumerate(self.model.applicable_rules)}
|
||||
preds = self.model.combine_products_and_probs(
|
||||
@ -2848,18 +2880,28 @@ class ApplicabilityDomain(EnviPathModel):
|
||||
# loop through rule indices together with the collected neighbours indices from train dataset
|
||||
for rule_uuid, train_instances in qualified_neighbours_per_rule[i].items():
|
||||
# compute tanimoto distance for all neighbours and add to dataset
|
||||
dists = self._compute_distances(assessment_ds[i, assessment_ds.struct_features()].to_numpy()[0],
|
||||
train_instances[:, train_instances.struct_features()].to_numpy())
|
||||
dists = self._compute_distances(
|
||||
assessment_ds[i, assessment_ds.struct_features()].to_numpy()[0],
|
||||
train_instances[:, train_instances.struct_features()].to_numpy(),
|
||||
)
|
||||
train_instances = train_instances.with_columns(dist=pl.Series(dists))
|
||||
|
||||
# sort them in a descending way and take at most `self.num_neighbours`
|
||||
# TODO: Should this be descending? If we want the most similar then we want values close to zero (ascending)
|
||||
train_instances = train_instances.sort("dist", descending=True)[:self.num_neighbours]
|
||||
train_instances = train_instances.sort("dist", descending=True)[
|
||||
: self.num_neighbours
|
||||
]
|
||||
# compute average distance
|
||||
rule_reliabilities[rule_uuid] = train_instances.select(pl.mean("dist")).fill_nan(0.0).item()
|
||||
rule_reliabilities[rule_uuid] = (
|
||||
train_instances.select(pl.mean("dist")).fill_nan(0.0).item()
|
||||
)
|
||||
# for local_compatibility we'll need the datasets for the indices having the highest similarity
|
||||
local_compatibilities[rule_uuid] = self._compute_compatibility(rule_uuid, train_instances)
|
||||
neighbours_per_rule[rule_uuid] = list(CompoundStructure.objects.filter(uuid__in=train_instances["structure_id"]))
|
||||
local_compatibilities[rule_uuid] = self._compute_compatibility(
|
||||
rule_uuid, train_instances
|
||||
)
|
||||
neighbours_per_rule[rule_uuid] = list(
|
||||
CompoundStructure.objects.filter(uuid__in=train_instances["structure_id"])
|
||||
)
|
||||
neighbor_probs_per_rule[rule_uuid] = train_instances[f"prob_{rule_uuid}"].to_list()
|
||||
|
||||
ad_res = {
|
||||
@ -2933,8 +2975,11 @@ class ApplicabilityDomain(EnviPathModel):
|
||||
def _compute_compatibility(self, rule_idx: int, neighbours: "RuleBasedDataset"):
|
||||
accuracy = 0.0
|
||||
import polars as pl
|
||||
obs_pred = neighbours.select(obs=pl.col(f"obs_{rule_idx}").cast(pl.Boolean),
|
||||
pred=pl.col(f"prob_{rule_idx}") >= self.model.threshold)
|
||||
|
||||
obs_pred = neighbours.select(
|
||||
obs=pl.col(f"obs_{rule_idx}").cast(pl.Boolean),
|
||||
pred=pl.col(f"prob_{rule_idx}") >= self.model.threshold,
|
||||
)
|
||||
# Compute tp, tn, fp, fn using polars expressions
|
||||
tp = obs_pred.filter((pl.col("obs")) & (pl.col("pred"))).height
|
||||
tn = obs_pred.filter((~pl.col("obs")) & (~pl.col("pred"))).height
|
||||
@ -2961,14 +3006,15 @@ class EnviFormer(PackageBasedModel):
|
||||
):
|
||||
mod = EnviFormer()
|
||||
mod.package = package
|
||||
|
||||
if name is None or name.strip() == "":
|
||||
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"EnviFormer {EnviFormer.objects.filter(package=package).count() + 1}"
|
||||
|
||||
mod.name = name
|
||||
|
||||
if description is not None and description.strip() != "":
|
||||
mod.description = description
|
||||
mod.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
if threshold is None or (threshold <= 0 or 1 <= threshold):
|
||||
raise ValueError("Threshold must be a float between 0 and 1.")
|
||||
@ -3103,7 +3149,7 @@ class EnviFormer(PackageBasedModel):
|
||||
pred_dict = {}
|
||||
for k, pred in enumerate(predictions):
|
||||
pred_smiles, pred_proba = zip(*pred.items())
|
||||
reactant, true_product = test_ds[k, "educts"], test_ds[k, "products"]
|
||||
reactant, _ = test_ds[k, "educts"], test_ds[k, "products"]
|
||||
pred_dict.setdefault(reactant, {"predict": [], "scores": []})
|
||||
for smiles, proba in zip(pred_smiles, pred_proba):
|
||||
smiles = set(smiles.split("."))
|
||||
@ -3217,8 +3263,9 @@ class EnviFormer(PackageBasedModel):
|
||||
|
||||
# If there are eval packages perform single generation evaluation on them instead of random splits
|
||||
if self.eval_packages.count() > 0:
|
||||
ds = EnviFormerDataset.generate_dataset(Reaction.objects.filter(
|
||||
package__in=self.eval_packages.all()).distinct())
|
||||
ds = EnviFormerDataset.generate_dataset(
|
||||
Reaction.objects.filter(package__in=self.eval_packages.all()).distinct()
|
||||
)
|
||||
test_result = self.model.predict_batch(ds.X())
|
||||
single_gen_result = evaluate_sg(ds, test_result, self.threshold)
|
||||
self.eval_results = self.compute_averages([single_gen_result])
|
||||
@ -3236,7 +3283,9 @@ class EnviFormer(PackageBasedModel):
|
||||
train = ds[train_index]
|
||||
test = ds[test_index]
|
||||
start = datetime.now()
|
||||
model = fine_tune(train.X(), train.y(), s.MODEL_DIR, str(split_id), device=s.ENVIFORMER_DEVICE)
|
||||
model = fine_tune(
|
||||
train.X(), train.y(), s.MODEL_DIR, str(split_id), device=s.ENVIFORMER_DEVICE
|
||||
)
|
||||
end = datetime.now()
|
||||
logger.debug(
|
||||
f"EnviFormer finetuning took {(end - start).total_seconds():.2f} seconds"
|
||||
@ -3313,7 +3362,12 @@ class EnviFormer(PackageBasedModel):
|
||||
for pathway in train_pathways:
|
||||
for reaction in pathway.edges:
|
||||
reaction = reaction.edge_label
|
||||
if any([educt in test_educts for educt in reaction_to_educts[str(reaction.uuid)]]):
|
||||
if any(
|
||||
[
|
||||
educt in test_educts
|
||||
for educt in reaction_to_educts[str(reaction.uuid)]
|
||||
]
|
||||
):
|
||||
overlap += 1
|
||||
continue
|
||||
train_reactions.append(reaction)
|
||||
@ -3370,41 +3424,44 @@ class Scenario(EnviPathModel):
|
||||
scenario_type: str,
|
||||
additional_information: List["EnviPyModel"],
|
||||
):
|
||||
s = Scenario()
|
||||
s.package = package
|
||||
|
||||
if name is None or name.strip() == "":
|
||||
new_s = Scenario()
|
||||
new_s.package = package
|
||||
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"Scenario {Scenario.objects.filter(package=package).count() + 1}"
|
||||
|
||||
s.name = name
|
||||
new_s.name = name
|
||||
|
||||
if description is not None and description.strip() != "":
|
||||
s.description = description
|
||||
new_s.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
if scenario_date is not None and scenario_date.strip() != "":
|
||||
s.scenario_date = scenario_date
|
||||
new_s.scenario_date = nh3.clean(scenario_date).strip()
|
||||
|
||||
if scenario_type is not None and scenario_type.strip() != "":
|
||||
s.scenario_type = scenario_type
|
||||
new_s.scenario_type = scenario_type
|
||||
|
||||
add_inf = defaultdict(list)
|
||||
|
||||
for info in additional_information:
|
||||
cls_name = info.__class__.__name__
|
||||
ai_data = json.loads(info.model_dump_json())
|
||||
# Clean for potential XSS hidden in the additional information fields.
|
||||
ai_data = json.loads(nh3.clean(info.model_dump_json()).strip())
|
||||
ai_data["uuid"] = f"{uuid4()}"
|
||||
add_inf[cls_name].append(ai_data)
|
||||
|
||||
s.additional_information = add_inf
|
||||
new_s.additional_information = add_inf
|
||||
|
||||
s.save()
|
||||
new_s.save()
|
||||
|
||||
return s
|
||||
return new_s
|
||||
|
||||
@transaction.atomic
|
||||
def add_additional_information(self, data: "EnviPyModel"):
|
||||
cls_name = data.__class__.__name__
|
||||
ai_data = json.loads(data.model_dump_json())
|
||||
# Clean for potential XSS hidden in the additional information fields.
|
||||
ai_data = json.loads(nh3.clean(data.model_dump_json()).strip())
|
||||
ai_data["uuid"] = f"{uuid4()}"
|
||||
|
||||
if cls_name not in self.additional_information:
|
||||
@ -3439,7 +3496,8 @@ class Scenario(EnviPathModel):
|
||||
new_ais = defaultdict(list)
|
||||
for k, vals in data.items():
|
||||
for v in vals:
|
||||
ai_data = json.loads(v.model_dump_json())
|
||||
# Clean for potential XSS hidden in the additional information fields.
|
||||
ai_data = json.loads(nh3.clean(v.model_dump_json()).strip())
|
||||
if hasattr(v, "uuid"):
|
||||
ai_data["uuid"] = str(v.uuid)
|
||||
else:
|
||||
|
||||
115
epdb/views.py
115
epdb/views.py
@ -10,6 +10,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
|
||||
import nh3
|
||||
|
||||
from utilities.chem import FormatConverter, IndigoUtils
|
||||
from utilities.decorators import package_permission_required
|
||||
@ -85,7 +86,10 @@ def login(request):
|
||||
from django.contrib.auth import authenticate
|
||||
from django.contrib.auth import login
|
||||
|
||||
username = request.POST.get("username")
|
||||
username = request.POST.get("username").strip()
|
||||
if username != request.POST.get("username"):
|
||||
context["message"] = "Login failed!"
|
||||
return render(request, "static/login.html", context)
|
||||
password = request.POST.get("password")
|
||||
|
||||
# Get email for username and check if the account is active
|
||||
@ -670,7 +674,8 @@ def search(request):
|
||||
|
||||
if request.method == "GET":
|
||||
package_urls = request.GET.getlist("packages")
|
||||
searchterm = request.GET.get("search")
|
||||
searchterm = request.GET.get("search").strip()
|
||||
|
||||
mode = request.GET.get("mode")
|
||||
|
||||
# add HTTP_ACCEPT check to differentiate between index and ajax call
|
||||
@ -771,7 +776,6 @@ def package_models(request, package_uuid):
|
||||
|
||||
elif request.method == "POST":
|
||||
log_post_params(request)
|
||||
|
||||
name = request.POST.get("model-name")
|
||||
description = request.POST.get("model-description")
|
||||
|
||||
@ -936,8 +940,14 @@ def package_model(request, package_uuid, model_uuid):
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
else:
|
||||
name = request.POST.get("model-name", "").strip()
|
||||
description = request.POST.get("model-description", "").strip()
|
||||
# TODO: Move cleaning to property updater
|
||||
name = request.POST.get("model-name")
|
||||
if name is not None:
|
||||
name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
description = request.POST.get("model-description")
|
||||
if description is not None:
|
||||
description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
if any([name, description]):
|
||||
if name:
|
||||
@ -1039,8 +1049,16 @@ def package(request, package_uuid):
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
# TODO: Move cleaning to property updater
|
||||
new_package_name = request.POST.get("package-name")
|
||||
if new_package_name is not None:
|
||||
new_package_name = nh3.clean(new_package_name, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
new_package_description = request.POST.get("package-description")
|
||||
if new_package_description is not None:
|
||||
new_package_description = nh3.clean(
|
||||
new_package_description, tags=s.ALLOWED_HTML_TAGS
|
||||
).strip()
|
||||
|
||||
grantee_url = request.POST.get("grantee")
|
||||
read = request.POST.get("read") == "on"
|
||||
@ -1149,7 +1167,7 @@ def package_compounds(request, package_uuid):
|
||||
|
||||
elif request.method == "POST":
|
||||
compound_name = request.POST.get("compound-name")
|
||||
compound_smiles = request.POST.get("compound-smiles")
|
||||
compound_smiles = request.POST.get("compound-smiles").strip()
|
||||
compound_description = request.POST.get("compound-description")
|
||||
|
||||
c = Compound.create(current_package, compound_smiles, compound_name, compound_description)
|
||||
@ -1202,8 +1220,16 @@ def package_compound(request, package_uuid, compound_uuid):
|
||||
|
||||
return JsonResponse({"success": current_compound.url})
|
||||
|
||||
new_compound_name = request.POST.get("compound-name", "").strip()
|
||||
new_compound_description = request.POST.get("compound-description", "").strip()
|
||||
# TODO: Move cleaning to property updater
|
||||
new_compound_name = request.POST.get("compound-name")
|
||||
if new_compound_name is not None:
|
||||
new_compound_name = nh3.clean(new_compound_name, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
new_compound_description = request.POST.get("compound-description")
|
||||
if new_compound_description is not None:
|
||||
new_compound_description = nh3.clean(
|
||||
new_compound_description, tags=s.ALLOWED_HTML_TAGS
|
||||
).strip()
|
||||
|
||||
if new_compound_name:
|
||||
current_compound.name = new_compound_name
|
||||
@ -1268,7 +1294,7 @@ def package_compound_structures(request, package_uuid, compound_uuid):
|
||||
|
||||
elif request.method == "POST":
|
||||
structure_name = request.POST.get("structure-name")
|
||||
structure_smiles = request.POST.get("structure-smiles")
|
||||
structure_smiles = request.POST.get("structure-smiles").strip()
|
||||
structure_description = request.POST.get("structure-description")
|
||||
|
||||
try:
|
||||
@ -1339,8 +1365,16 @@ def package_compound_structure(request, package_uuid, compound_uuid, structure_u
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
new_structure_name = request.POST.get("compound-structure-name", "").strip()
|
||||
new_structure_description = request.POST.get("compound-structure-description", "").strip()
|
||||
# TODO: Move cleaning to property updater
|
||||
new_structure_name = request.POST.get("compound-structure-name")
|
||||
if new_structure_name is not None:
|
||||
new_structure_name = nh3.clean(new_structure_name, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
new_structure_description = request.POST.get("compound-structure-description")
|
||||
if new_structure_description is not None:
|
||||
new_structure_description = nh3.clean(
|
||||
new_structure_description, tags=s.ALLOWED_HTML_TAGS
|
||||
).strip()
|
||||
|
||||
if new_structure_name:
|
||||
current_structure.name = new_structure_name
|
||||
@ -1442,11 +1476,11 @@ def package_rules(request, package_uuid):
|
||||
|
||||
# Obtain parameters as required by rule type
|
||||
if rule_type == "SimpleAmbitRule":
|
||||
params["smirks"] = request.POST.get("rule-smirks")
|
||||
params["smirks"] = request.POST.get("rule-smirks").strip()
|
||||
params["reactant_filter_smarts"] = request.POST.get("rule-reactant-smarts")
|
||||
params["product_filter_smarts"] = request.POST.get("rule-product-smarts")
|
||||
elif rule_type == "SimpleRDKitRule":
|
||||
params["reaction_smarts"] = request.POST.get("rule-reaction-smarts")
|
||||
params["reaction_smarts"] = request.POST.get("rule-reaction-smarts").strip()
|
||||
elif rule_type == "ParallelRule":
|
||||
pass
|
||||
elif rule_type == "SequentialRule":
|
||||
@ -1547,8 +1581,14 @@ def package_rule(request, package_uuid, rule_uuid):
|
||||
|
||||
return JsonResponse({"success": current_rule.url})
|
||||
|
||||
rule_name = request.POST.get("rule-name", "").strip()
|
||||
rule_description = request.POST.get("rule-description", "").strip()
|
||||
# TODO: Move cleaning to property updater
|
||||
rule_name = request.POST.get("rule-name")
|
||||
if rule_name is not None:
|
||||
rule_name = nh3.clean(rule_name, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
rule_description = request.POST.get("rule-description")
|
||||
if rule_description is not None:
|
||||
rule_description = nh3.clean(rule_description, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
if rule_name:
|
||||
current_rule.name = rule_name
|
||||
@ -1637,8 +1677,8 @@ def package_reactions(request, package_uuid):
|
||||
elif request.method == "POST":
|
||||
reaction_name = request.POST.get("reaction-name")
|
||||
reaction_description = request.POST.get("reaction-description")
|
||||
reactions_smirks = request.POST.get("reaction-smirks")
|
||||
|
||||
reactions_smirks = request.POST.get("reaction-smirks").strip()
|
||||
educts = reactions_smirks.split(">>")[0].split(".")
|
||||
products = reactions_smirks.split(">>")[1].split(".")
|
||||
|
||||
@ -1699,8 +1739,16 @@ def package_reaction(request, package_uuid, reaction_uuid):
|
||||
|
||||
return JsonResponse({"success": current_reaction.url})
|
||||
|
||||
new_reaction_name = request.POST.get("reaction-name", "").strip()
|
||||
new_reaction_description = request.POST.get("reaction-description", "").strip()
|
||||
# TODO: Move cleaning to property updater
|
||||
new_reaction_name = request.POST.get("reaction-name")
|
||||
if new_reaction_name is not None:
|
||||
new_reaction_name = nh3.clean(new_reaction_name, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
new_reaction_description = request.POST.get("reaction-description")
|
||||
if new_reaction_description is not None:
|
||||
new_reaction_description = nh3.clean(
|
||||
new_reaction_description, tags=s.ALLOWED_HTML_TAGS
|
||||
).strip()
|
||||
|
||||
if new_reaction_name:
|
||||
current_reaction.name = new_reaction_name
|
||||
@ -1777,8 +1825,9 @@ def package_pathways(request, package_uuid):
|
||||
|
||||
name = request.POST.get("name")
|
||||
description = request.POST.get("description")
|
||||
pw_mode = request.POST.get("predict", "predict").strip()
|
||||
|
||||
smiles = request.POST.get("smiles", "").strip()
|
||||
pw_mode = request.POST.get("predict", "predict").strip()
|
||||
|
||||
if "smiles" in request.POST and smiles == "":
|
||||
return error(
|
||||
@ -1787,8 +1836,6 @@ def package_pathways(request, package_uuid):
|
||||
"Pathway prediction failed due to missing or empty SMILES",
|
||||
)
|
||||
|
||||
smiles = smiles.strip()
|
||||
|
||||
try:
|
||||
stand_smiles = FormatConverter.standardize(smiles)
|
||||
except ValueError:
|
||||
@ -1947,8 +1994,14 @@ def package_pathway(request, package_uuid, pathway_uuid):
|
||||
|
||||
return JsonResponse({"success": current_pathway.url})
|
||||
|
||||
# TODO: Move cleaning to property updater
|
||||
pathway_name = request.POST.get("pathway-name")
|
||||
if pathway_name is not None:
|
||||
pathway_name = nh3.clean(pathway_name, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
pathway_description = request.POST.get("pathway-description")
|
||||
if pathway_description is not None:
|
||||
pathway_description = nh3.clean(pathway_description, tags=s.ALLOWED_HTML_TAGS).strip()
|
||||
|
||||
if any([pathway_name, pathway_description]):
|
||||
if pathway_name is not None and pathway_name.strip() != "":
|
||||
@ -2036,8 +2089,8 @@ def package_pathway_nodes(request, package_uuid, pathway_uuid):
|
||||
elif request.method == "POST":
|
||||
node_name = request.POST.get("node-name")
|
||||
node_description = request.POST.get("node-description")
|
||||
node_smiles = request.POST.get("node-smiles")
|
||||
|
||||
node_smiles = request.POST.get("node-smiles").strip()
|
||||
current_pathway.add_node(node_smiles, name=node_name, description=node_description)
|
||||
|
||||
return redirect(current_pathway.url)
|
||||
@ -2199,9 +2252,9 @@ def package_pathway_edges(request, package_uuid, pathway_uuid):
|
||||
|
||||
elif request.method == "POST":
|
||||
log_post_params(request)
|
||||
|
||||
edge_name = request.POST.get("edge-name")
|
||||
edge_description = request.POST.get("edge-description")
|
||||
|
||||
edge_substrates = request.POST.getlist("edge-substrates")
|
||||
edge_products = request.POST.getlist("edge-products")
|
||||
|
||||
@ -2288,7 +2341,7 @@ 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_.name, "url": s_.url, "uuid": s_.uuid} for s_ in scens]
|
||||
return JsonResponse(res, safe=False)
|
||||
|
||||
context = get_base_context(request)
|
||||
@ -2336,21 +2389,21 @@ def package_scenarios(request, package_uuid):
|
||||
"name": "soil",
|
||||
"widgets": [
|
||||
HTMLGenerator.generate_html(ai, prefix=f"soil_{0}")
|
||||
for ai in [x for s in SOIL_ADDITIONAL_INFORMATION.values() for x in s]
|
||||
for ai in [x for sv in SOIL_ADDITIONAL_INFORMATION.values() for x in sv]
|
||||
],
|
||||
},
|
||||
"Sludge Data": {
|
||||
"name": "sludge",
|
||||
"widgets": [
|
||||
HTMLGenerator.generate_html(ai, prefix=f"sludge_{0}")
|
||||
for ai in [x for s in SLUDGE_ADDITIONAL_INFORMATION.values() for x in s]
|
||||
for ai in [x for sv in SLUDGE_ADDITIONAL_INFORMATION.values() for x in sv]
|
||||
],
|
||||
},
|
||||
"Water-Sediment System Data": {
|
||||
"name": "sediment",
|
||||
"widgets": [
|
||||
HTMLGenerator.generate_html(ai, prefix=f"sediment_{0}")
|
||||
for ai in [x for s in SEDIMENT_ADDITIONAL_INFORMATION.values() for x in s]
|
||||
for ai in [x for sv in SEDIMENT_ADDITIONAL_INFORMATION.values() for x in sv]
|
||||
],
|
||||
},
|
||||
}
|
||||
@ -2365,6 +2418,7 @@ def package_scenarios(request, package_uuid):
|
||||
|
||||
scenario_name = request.POST.get("scenario-name")
|
||||
scenario_description = request.POST.get("scenario-description")
|
||||
|
||||
scenario_date_year = request.POST.get("scenario-date-year")
|
||||
scenario_date_month = request.POST.get("scenario-date-month")
|
||||
scenario_date_day = request.POST.get("scenario-date-day")
|
||||
@ -2378,9 +2432,9 @@ def package_scenarios(request, package_uuid):
|
||||
scenario_type = request.POST.get("scenario-type")
|
||||
|
||||
additional_information = HTMLGenerator.build_models(request.POST.dict())
|
||||
additional_information = [x for s in additional_information.values() for x in s]
|
||||
additional_information = [x for sv in additional_information.values() for x in sv]
|
||||
|
||||
s = Scenario.create(
|
||||
new_scen = Scenario.create(
|
||||
current_package,
|
||||
name=scenario_name,
|
||||
description=scenario_description,
|
||||
@ -2389,7 +2443,7 @@ def package_scenarios(request, package_uuid):
|
||||
additional_information=additional_information,
|
||||
)
|
||||
|
||||
return redirect(s.url)
|
||||
return redirect(new_scen.url)
|
||||
else:
|
||||
return HttpResponseNotAllowed(
|
||||
[
|
||||
@ -2689,6 +2743,7 @@ def settings(request):
|
||||
|
||||
name = request.POST.get("prediction-setting-name")
|
||||
description = request.POST.get("prediction-setting-description")
|
||||
|
||||
new_default = request.POST.get("prediction-setting-new-default", "off") == "on"
|
||||
|
||||
max_nodes = min(
|
||||
|
||||
@ -27,6 +27,7 @@ dependencies = [
|
||||
"scikit-learn>=1.6.1",
|
||||
"sentry-sdk[django]>=2.32.0",
|
||||
"setuptools>=80.8.0",
|
||||
"nh3==0.3.2",
|
||||
"polars==1.35.1",
|
||||
]
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
{% extends "framework.html" %}
|
||||
{% load static %}
|
||||
{% load envipytags %}
|
||||
{% block content %}
|
||||
|
||||
<div class="panel-group" id="reviewListAccordion">
|
||||
|
||||
@ -192,7 +192,7 @@
|
||||
<div class="panel-body list-group-item" id="ReviewedContent">
|
||||
{% if object_type == 'package' %}
|
||||
{% for obj in reviewed_objects %}
|
||||
<a class="list-group-item" href="{{ obj.url }}">{{ obj.name }}
|
||||
<a class="list-group-item" href="{{ obj.url }}">{{ obj.name|safe }}
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"
|
||||
style="float:right" data-toggle="tooltip"
|
||||
data-placement="top" title="" data-original-title="Reviewed">
|
||||
@ -201,7 +201,7 @@
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% for obj in reviewed_objects|slice:":50" %}
|
||||
<a class="list-group-item" href="{{ obj.url }}">{{ obj.name }}{# <i>({{ obj.package.name }})</i> #}
|
||||
<a class="list-group-item" href="{{ obj.url }}">{{ obj.name|safe }}{# <i>({{ obj.package.name }})</i> #}
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"
|
||||
style="float:right" data-toggle="tooltip"
|
||||
data-placement="top" title="" data-original-title="Reviewed">
|
||||
@ -221,11 +221,11 @@
|
||||
<div class="panel-body list-group-item" id="UnreviewedContent">
|
||||
{% if object_type == 'package' %}
|
||||
{% for obj in unreviewed_objects %}
|
||||
<a class="list-group-item" href="{{ obj.url }}">{{ obj.name }}</a>
|
||||
<a class="list-group-item" href="{{ obj.url }}">{{ obj.name|safe }}</a>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% for obj in unreviewed_objects|slice:":50" %}
|
||||
<a class="list-group-item" href="{{ obj.url }}">{{ obj.name }}</a>
|
||||
<a class="list-group-item" href="{{ obj.url }}">{{ obj.name|safe }}</a>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
@ -236,9 +236,9 @@
|
||||
<ul class='list-group'>
|
||||
{% for obj in objects %}
|
||||
{% if object_type == 'user' %}
|
||||
<a class="list-group-item" href="{{ obj.url }}">{{ obj.username }}</a>
|
||||
<a class="list-group-item" href="{{ obj.url }}">{{ obj.username|safe }}</a>
|
||||
{% else %}
|
||||
<a class="list-group-item" href="{{ obj.url }}">{{ obj.name }}</a>
|
||||
<a class="list-group-item" href="{{ obj.url }}">{{ obj.name|safe }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
@ -26,12 +26,12 @@
|
||||
{% endif %}
|
||||
<h4 class="panel-title">
|
||||
<a id="{{ obj.id }}-link" data-toggle="collapse" data-parent="#migration-detail"
|
||||
href="#{{ obj.id }}">{{ obj.name }}</a>
|
||||
href="#{{ obj.id }}">{{ obj.name|safe }}</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="{{ obj.id }}" class="panel-collapse collapse {% if not obj.status %}in{% endif %}">
|
||||
<div class="panel-body list-group-item">
|
||||
<a class="list-group-item" href="{{ obj.detail_url }}">{{ obj.name }} Migration Detail Page</a>
|
||||
<a class="list-group-item" href="{{ obj.detail_url }}">{{ obj.name|safe }} Migration Detail Page</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
{% endif %}
|
||||
<h4 class="panel-title">
|
||||
<a id="{{ obj.id }}-link" data-toggle="collapse" data-parent="#migration-detail"
|
||||
href="#{{ obj.id }}">{{ obj.name }}</a>
|
||||
href="#{{ obj.id }}">{{ obj.name|safe }}</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="{{ obj.id }}" class="panel-collapse collapse {% if not obj.status %}in{% endif %}">
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
<div class="modal fade" tabindex="-1" id="new_model_modal" role="dialog" aria-labelledby="new_model_modal"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
@ -47,14 +48,14 @@
|
||||
<option disabled>Reviewed Packages</option>
|
||||
{% for obj in meta.readable_packages %}
|
||||
{% if obj.reviewed %}
|
||||
<option value="{{ obj.url }}">{{ obj.name }}</option>
|
||||
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
<option disabled>Unreviewed Packages</option>
|
||||
{% for obj in meta.readable_packages %}
|
||||
{% if not obj.reviewed %}
|
||||
<option value="{{ obj.url }}">{{ obj.name }}</option>
|
||||
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
@ -68,14 +69,14 @@
|
||||
<option disabled>Reviewed Packages</option>
|
||||
{% for obj in meta.readable_packages %}
|
||||
{% if obj.reviewed %}
|
||||
<option value="{{ obj.url }}">{{ obj.name }}</option>
|
||||
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
<option disabled>Unreviewed Packages</option>
|
||||
{% for obj in meta.readable_packages %}
|
||||
{% if not obj.reviewed %}
|
||||
<option value="{{ obj.url }}">{{ obj.name }}</option>
|
||||
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
{% load static %}
|
||||
<div class="modal fade" tabindex="-1" id="new_pathway_modal" role="dialog" aria-labelledby="new_pathway_modal"
|
||||
aria-hidden="true" style="overflow-y: auto;">
|
||||
@ -111,7 +112,7 @@
|
||||
|
||||
<select id="settingSelect" name="settingSelect" class="form-control">
|
||||
{% for setting in available_settings %}
|
||||
<option value="{{ setting.id }}">{{ setting.name }}</option>
|
||||
<option value="{{ setting.id }}">{{ setting.name|safe }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<p></p>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
{% load static %}
|
||||
|
||||
<div id="new_prediction_setting_modal" class="modal" tabindex="-1">
|
||||
@ -40,14 +41,14 @@
|
||||
<option disabled>Reviewed Packages</option>
|
||||
{% for obj in meta.readable_packages %}
|
||||
{% if obj.reviewed %}
|
||||
<option value="{{ obj.url }}">{{ obj.name }}</option>
|
||||
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
<option disabled>Unreviewed Packages</option>
|
||||
{% for obj in meta.readable_packages %}
|
||||
{% if not obj.reviewed %}
|
||||
<option value="{{ obj.url }}">{{ obj.name }}</option>
|
||||
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
@ -57,7 +58,7 @@
|
||||
<select id="model-based-prediction-setting-model" name="model-based-prediction-setting-model" class="form-control" data-width='100%'>
|
||||
<option disabled selected>Select the model</option>
|
||||
{% for m in models %}
|
||||
<option value="{{ m.url }}">{{ m.name }}</option>
|
||||
<option value="{{ m.url }}">{{ m.name|safe }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label for="model-based-prediction-setting-threshold">Threshold</label>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
{% load static %}
|
||||
<div class="modal fade bs-modal-lg" id="add_pathway_edge_modal" tabindex="-1" aria-labelledby="add_pathway_edge_modal"
|
||||
aria-modal="true"
|
||||
@ -36,7 +37,7 @@
|
||||
<select id="add_pathway_edge_substrates" name="edge-substrates"
|
||||
data-actions-box='true' class="form-control" multiple data-width='100%'>
|
||||
{% for n in pathway.nodes %}
|
||||
<option data-smiles="{{ n.default_node_label.smiles }}" value="{{ n.url }}">{{ n.default_node_label.name }}</option>
|
||||
<option data-smiles="{{ n.default_node_label.smiles }}" value="{{ n.url }}">{{ n.default_node_label.name|safe }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
@ -47,7 +48,7 @@
|
||||
<select id="add_pathway_edge_products" name="edge-products"
|
||||
data-actions-box='true' class="form-control" multiple data-width='100%'>
|
||||
{% for n in pathway.nodes %}
|
||||
<option data-smiles="{{ n.default_node_label.smiles }}" value="{{ n.url }}">{{ n.default_node_label.name }}</option>
|
||||
<option data-smiles="{{ n.default_node_label.smiles }}" value="{{ n.url }}">{{ n.default_node_label.name|safe }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
{% load static %}
|
||||
<!-- Delete Edge -->
|
||||
<div id="delete_pathway_edge_modal" class="modal" tabindex="-1">
|
||||
@ -19,7 +20,7 @@
|
||||
data-actions-box='true' class="form-control" data-width='100%'>
|
||||
<option value="" disabled selected>Select Reaction to delete</option>
|
||||
{% for e in pathway.edges %}
|
||||
<option value="{{ e.url }}">{{ e.edge_label.name }}</option>
|
||||
<option value="{{ e.url }}">{{ e.edge_label.name|safe }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="hidden" id="hidden" name="hidden" value="delete"/>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
{% load static %}
|
||||
|
||||
<!-- Delete Node -->
|
||||
<div id="delete_pathway_node_modal" class="modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
@ -19,7 +20,7 @@
|
||||
data-actions-box='true' class="form-control" data-width='100%'>
|
||||
<option value="" disabled selected>Select Compound to delete</option>
|
||||
{% for n in pathway.nodes %}
|
||||
<option value="{{ n.url }}">{{ n.default_node_label.name }}</option>
|
||||
<option value="{{ n.url }}">{{ n.default_node_label.name|safe }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="hidden" id="hidden" name="hidden" value="delete"/>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
{% load static %}
|
||||
<!-- Edit Compound -->
|
||||
<div id="edit_compound_modal" class="modal" tabindex="-1">
|
||||
@ -15,12 +16,12 @@
|
||||
{% csrf_token %}
|
||||
<p>
|
||||
<label for="compound-name">Name</label>
|
||||
<input id="compound-name" class="form-control" name="compound-name" value="{{ compound.name}}">
|
||||
<input id="compound-name" class="form-control" name="compound-name" value="{{ compound.name|safe}}">
|
||||
</p>
|
||||
<p>
|
||||
<label for="compound-description">Description</label>
|
||||
<input id="compound-description" type="text" class="form-control"
|
||||
value="{{ compound.description }}"
|
||||
value="{{ compound.description|safe }}"
|
||||
name="compound-description">
|
||||
</p>
|
||||
</form>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
{% load static %}
|
||||
<!-- Edit Compound -->
|
||||
<div id="edit_compound_structure_modal" class="modal" tabindex="-1">
|
||||
@ -15,12 +16,12 @@
|
||||
{% csrf_token %}
|
||||
<p>
|
||||
<label for="compound-structure-name">Name</label>
|
||||
<input id="compound-structure-name" class="form-control" name="compound-structure-name" value="{{ compound_structure.name }}">
|
||||
<input id="compound-structure-name" class="form-control" name="compound-structure-name" value="{{ compound_structure.name|safe }}">
|
||||
</p>
|
||||
<p>
|
||||
<label for="compound-structure-description">Description</label>
|
||||
<input id="compound-structure-description" type="text" class="form-control"
|
||||
value="{{ compound_structure.description }}" name="compound-structure-description">
|
||||
value="{{ compound_structure.description|safe }}" name="compound-structure-description">
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
{% load static %}
|
||||
<!-- Edit Package Permission -->
|
||||
<div id="edit_group_member_modal" class="modal" tabindex="-1">
|
||||
@ -39,7 +40,7 @@
|
||||
{% endfor %}
|
||||
<option disabled>Groups</option>
|
||||
{% for g in groups %}
|
||||
<option value="{{ g.url }}">{{ g.name }}</option>
|
||||
<option value="{{ g.url }}">{{ g.name|safe }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="hidden" name="action" value="add">
|
||||
@ -81,7 +82,7 @@
|
||||
accept-charset="UTF-8" action="" data-remote="true" method="post">
|
||||
{% csrf_token %}
|
||||
<div class="col-xs-8">
|
||||
{{ g.name }}
|
||||
{{ g.name|safe }}
|
||||
<input type="hidden" name="member" value="{{ g.url }}"/>
|
||||
<input type="hidden" name="action" value="remove">
|
||||
</div>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
{% load static %}
|
||||
<!-- Edit Model -->
|
||||
<div id="edit_model_modal" class="modal" tabindex="-1">
|
||||
@ -16,12 +17,12 @@
|
||||
<p>
|
||||
<label for="model-name">Name</label>
|
||||
<input id="model-name" type="text" class="form-control" name="model-name"
|
||||
value="{{ model.name }}">
|
||||
value="{{ model.name|safe }}">
|
||||
</p>
|
||||
<p>
|
||||
<label for="model-description">Description</label>
|
||||
<input id="model-description" type="text" class="form-control" name="model-description"
|
||||
value="{{ model.description }}">
|
||||
value="{{ model.description|safe }}">
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
{% load static %}
|
||||
<!-- Edit Node -->
|
||||
<div id="edit_node_modal" class="modal" tabindex="-1">
|
||||
@ -15,12 +16,12 @@
|
||||
{% csrf_token %}
|
||||
<p>
|
||||
<label for="node-name">Name</label>
|
||||
<input id="node-name" class="form-control" name="node-name" value="{{ node.name}}">
|
||||
<input id="node-name" class="form-control" name="node-name" value="{{ node.name|safe}}">
|
||||
</p>
|
||||
<p>
|
||||
<label for="node-description">Description</label>
|
||||
<input id="node-description" type="text" class="form-control"
|
||||
value="{{ node.description }}"
|
||||
value="{{ node.description|safe }}"
|
||||
name="node-description">
|
||||
</p>
|
||||
</form>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
{% load static %}
|
||||
<!-- Edit Package -->
|
||||
<div id="edit_package_modal" class="modal" tabindex="-1">
|
||||
@ -15,12 +16,12 @@
|
||||
{% csrf_token %}
|
||||
<p>
|
||||
<label for="package-name">Name</label>
|
||||
<input id="package-name" class="form-control" name="package-name" value="{{ package.name}}">
|
||||
<input id="package-name" class="form-control" name="package-name" value="{{ package.name|safe}}">
|
||||
</p>
|
||||
<p>
|
||||
<label for="package-description">Description</label>
|
||||
<input id="package-description" type="text" class="form-control"
|
||||
value="{{ package.description }}"
|
||||
value="{{ package.description|safe }}"
|
||||
name="package-description">
|
||||
</p>
|
||||
</form>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
{% load static %}
|
||||
<!-- Edit Package Permission -->
|
||||
<div id="edit_package_permissions_modal" class="modal" tabindex="-1">
|
||||
@ -46,7 +47,7 @@
|
||||
{% endfor %}
|
||||
<option disabled>Groups</option>
|
||||
{% for g in groups %}
|
||||
<option value="{{ g.url }}">{{ g.name }}</option>
|
||||
<option value="{{ g.url }}">{{ g.name|safe }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
@ -100,7 +101,7 @@
|
||||
accept-charset="UTF-8" action="" data-remote="true" method="post">
|
||||
{% csrf_token %}
|
||||
<div class="col-xs-4">
|
||||
{{ gp.group.name }}
|
||||
{{ gp.group.name|safe }}
|
||||
<input type="hidden" name="grantee" value="{{ gp.group.url }}"/>
|
||||
</div>
|
||||
<div class="col-xs-2">
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
{% load static %}
|
||||
<!-- Edit Pathway -->
|
||||
<div id="edit_pathway_modal" class="modal" tabindex="-1">
|
||||
@ -15,12 +16,12 @@
|
||||
{% csrf_token %}
|
||||
<p>
|
||||
<label for="pathway-name">Name</label>
|
||||
<input id="pathway-name" class="form-control" name="pathway-name" value="{{ pathway.name }}">
|
||||
<input id="pathway-name" class="form-control" name="pathway-name" value="{{ pathway.name|safe }}">
|
||||
</p>
|
||||
<p>
|
||||
<label for="pathway-description">Description</label>
|
||||
<textarea id="pathway-description" type="text" class="form-control" name="pathway-description"
|
||||
rows="10">{{ pathway.description }}</textarea>
|
||||
rows="10">{{ pathway.description|safe }}</textarea>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
<td colspan="2">
|
||||
<select id="model" name="model" class="form-control" data-width='100%'>
|
||||
{% for m in models %}
|
||||
<option value="{{ m.id }}" {% if user.prediction_settings.model.url == m.url %}selected{% endif %}>{{ m.name }}</option>
|
||||
<option value="{{ m.id }}" {% if user.prediction_settings.model.url == m.url %}selected{% endif %}>{{ m.name|safe }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
{% load static %}
|
||||
<!-- Edit Reaction -->
|
||||
<div id="edit_reaction_modal" class="modal" tabindex="-1">
|
||||
@ -14,12 +15,12 @@
|
||||
{% csrf_token %}
|
||||
<p>
|
||||
<label for="reaction-name">Name</label>
|
||||
<input id="reaction-name" class="form-control" name="reaction-name" value="{{ reaction.name }}">
|
||||
<input id="reaction-name" class="form-control" name="reaction-name" value="{{ reaction.name|safe }}">
|
||||
</p>
|
||||
<p>
|
||||
<label for="reaction-description">Description</label>
|
||||
<input id="reaction-description" type="text" class="form-control"
|
||||
value="{{ reaction.description }}" name="reaction-description">
|
||||
value="{{ reaction.description|safe }}" name="reaction-description">
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
{% load static %}
|
||||
<!-- Edit Rule -->
|
||||
<div id="edit_rule_modal" class="modal" tabindex="-1">
|
||||
@ -14,12 +15,12 @@
|
||||
{% csrf_token %}
|
||||
<p>
|
||||
<label for="rule-name">Name</label>
|
||||
<input id="rule-name" class="form-control" name="rule-name" value="{{ rule.name }}">
|
||||
<input id="rule-name" class="form-control" name="rule-name" value="{{ rule.name|safe }}">
|
||||
</p>
|
||||
<p>
|
||||
<label for="rule-description">Description</label>
|
||||
<input id="rule-description" type="text" class="form-control"
|
||||
value="{{ rule.description }}" name="rule-description">
|
||||
value="{{ rule.description|safe }}" name="rule-description">
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
{% load static %}
|
||||
<!-- Edit User -->
|
||||
<div id="edit_user_modal" class="modal" tabindex="-1">
|
||||
@ -18,7 +19,7 @@
|
||||
<select id="default-package" name="default-package" class="form-control" data-width='100%'>
|
||||
<option disabled>Select a Package</option>
|
||||
{% for p in meta.writeable_packages %}
|
||||
<option value="{{ p.url }}" {% if p.id == meta.user.default_package.id %}selected{% endif %}>{{ p.name }}</option>
|
||||
<option value="{{ p.url }}" {% if p.id == meta.user.default_package.id %}selected{% endif %}>{{ p.name|safe }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</p>
|
||||
@ -27,7 +28,7 @@
|
||||
<select id="default-group" name="default-group" class="form-control" data-width='100%'>
|
||||
<option disabled>Select a Group</option>
|
||||
{% for g in meta.available_groups %}
|
||||
<option value="{{ g.url }}" {% if g.id == meta.user.default_group.id %}selected{% endif %}>{{ g.name }}</option>
|
||||
<option value="{{ g.url }}" {% if g.id == meta.user.default_group.id %}selected{% endif %}>{{ g.name|safe }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</p>
|
||||
@ -36,7 +37,7 @@
|
||||
<select id="default-prediction-setting" name="default-prediction-setting" class="form-control" data-width='100%'>
|
||||
<option disabled>Select a Setting</option>
|
||||
{% for s in meta.available_settings %}
|
||||
<option value="{{ s.url }}" {% if s.id == meta.user.default_setting.id %}selected{% endif %}>{{ s.name }}</option>
|
||||
<option value="{{ s.url }}" {% if s.id == meta.user.default_setting.id %}selected{% endif %}>{{ s.name|safe }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</p>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
<div class="modal fade" tabindex="-1" id="evaluate_model_modal" role="dialog" aria-labelledby="evaluate_model_modal"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
@ -24,14 +25,14 @@
|
||||
<option disabled>Reviewed Packages</option>
|
||||
{% for obj in meta.readable_packages %}
|
||||
{% if obj.reviewed %}
|
||||
<option value="{{ obj.url }}">{{ obj.name }}</option>
|
||||
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
<option disabled>Unreviewed Packages</option>
|
||||
{% for obj in meta.readable_packages %}
|
||||
{% if not obj.reviewed %}
|
||||
<option value="{{ obj.url }}">{{ obj.name }}</option>
|
||||
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
{% load static %}
|
||||
<!-- Copy Object -->
|
||||
<div id="generic_copy_object_modal" class="modal" tabindex="-1">
|
||||
@ -18,7 +19,7 @@
|
||||
data-width='100%'>
|
||||
<option disabled selected>Select Target Package</option>
|
||||
{% for p in meta.writeable_packages %}
|
||||
<option value="{{ p.url }}">{{ p.name }}</option>`
|
||||
<option value="{{ p.url }}">{{ p.name|safe }}</option>`
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="hidden" name="hidden" value="copy">
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
{% load static %}
|
||||
|
||||
<style>
|
||||
@ -55,7 +56,7 @@
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">Set Aliases for {{ current_object.name }}</h4>
|
||||
<h4 class="modal-title">Set Aliases for {{ current_object.name|safe }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="set_aliases_modal_form" accept-charset="UTF-8" action="{{ current_object.url }}"
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
{% load static %}
|
||||
<!-- Delete Object -->
|
||||
<div id="generic_set_external_reference_modal" class="modal" tabindex="-1">
|
||||
@ -23,7 +24,7 @@
|
||||
{% if entity == object_type %}
|
||||
{% for db in databases %}
|
||||
<option id="db-select-{{ db.database.pk }}" data-input-placeholder="{{ db.placeholder }}"
|
||||
value="{{ db.database.id }}">{{ db.database.name }}</option>`
|
||||
value="{{ db.database.id }}">{{ db.database.name|safe }}</option>`
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
{% load static %}
|
||||
<div class="modal fade bs-modal-lg" id="set_scenario_modal" tabindex="-1" aria-labelledby="set_scenario_modal"
|
||||
aria-modal="true" role="dialog">
|
||||
@ -7,7 +8,7 @@
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">Set Scenarios for {{ current_object.name }}</h4>
|
||||
<h4 class="modal-title">Set Scenarios for {{ current_object.name|safe }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="loading_scenario_div" class="text-center"></div>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
<div class="modal fade"
|
||||
tabindex="-1"
|
||||
id="manage_api_token_modal"
|
||||
@ -41,7 +42,7 @@
|
||||
<div class="input-group">
|
||||
<input type="hidden" name="hidden" value="delete">
|
||||
<input type="hidden" name="token-id" value="{{ t.pk }}">
|
||||
<input type="text" class="form-control" value="{{ t.name }}" disabled>
|
||||
<input type="text" class="form-control" value="{{ t.name|safe }}" disabled>
|
||||
<span class="input-group-btn">
|
||||
<button type="submit" class="btn btn-danger">Delete</button>
|
||||
</span>
|
||||
|
||||
@ -56,7 +56,7 @@
|
||||
<option disabled>Select a Setting</option>
|
||||
{% for s in meta.available_settings %}
|
||||
<option value="{{ s.url }}"{% if s.id == meta.user.default_setting.id %}selected{% endif %}>
|
||||
{{ s.name }}{% if s.id == meta.user.default_setting.id %} <i>(User default)</i>{% endif %}
|
||||
{{ s.name|safe }}{% if s.id == meta.user.default_setting.id %} <i>(User default)</i>{% endif %}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
<div class="panel-group" id="rule-detail">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||
{{ rule.name }}
|
||||
{{ rule.name|safe }}
|
||||
<div id="actionsButton"
|
||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
|
||||
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||
@ -60,7 +60,7 @@
|
||||
<div id="rule-reaction-patterns" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for r in rule.srs %}
|
||||
<a class="list-group-item" href="{{ r.url }}">{{ r.name }}</a>
|
||||
<a class="list-group-item" href="{{ r.url }}">{{ r.name|safe }}</a>
|
||||
<div align="center">
|
||||
<p>
|
||||
{{r.as_svg|safe}}
|
||||
@ -81,7 +81,7 @@
|
||||
<div id="rule-scenario" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for s in rule.scenarios.all %}
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
<div class="panel-group" id="compound-detail">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||
{{ compound.name }}
|
||||
{{ compound.name|safe }}
|
||||
<div id="actionsButton"
|
||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
|
||||
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||
@ -64,7 +64,7 @@
|
||||
</div>
|
||||
<div id="compound-desc" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{{ compound.description }}
|
||||
{{ compound.description|safe }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -133,7 +133,7 @@
|
||||
<div id="compound-reaction" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for r in compound.related_reactions %}
|
||||
<a class="list-group-item" href="{{ r.url }}">{{ r.name }} <i>({{ r.package.name }})</i></a>
|
||||
<a class="list-group-item" href="{{ r.url }}">{{ r.name|safe }} <i>({{ r.package.name|safe }})</i></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
@ -150,7 +150,7 @@
|
||||
<div id="compound-pathway" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for r in compound.related_pathways %}
|
||||
<a class="list-group-item" href="{{ r.url }}">{{ r.name }} <i>({{ r.package.name }})</i></a>
|
||||
<a class="list-group-item" href="{{ r.url }}">{{ r.name|safe }} <i>({{ r.package.name|safe }})</i></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
@ -167,7 +167,7 @@
|
||||
<div id="compound-scenario" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for s in compound.scenarios.all %}
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
<div class="panel-group" id="compound-structure-detail">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||
{{ compound_structure.name }}
|
||||
{{ compound_structure.name|safe }}
|
||||
<div id="actionsButton"
|
||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
|
||||
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||
@ -28,7 +28,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p> {{ compound_structure.description }} </p>
|
||||
<p> {{ compound_structure.description|safe }} </p>
|
||||
</div>
|
||||
|
||||
<!-- Image -->
|
||||
@ -86,7 +86,7 @@
|
||||
<div id="compound-structure-scenario" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for s in compound_structure.scenarios.all %}
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
<div class="panel-group" id="edge-detail">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||
{{ edge.edge_label.name }}
|
||||
{{ edge.edge_label.name|safe }}
|
||||
<div id="actionsButton"
|
||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
|
||||
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||
@ -36,7 +36,7 @@
|
||||
</div>
|
||||
<div id="edge-desc" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{{ edge.description }}
|
||||
{{ edge.description|safe }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -82,12 +82,12 @@
|
||||
<div id="edge-description-smiles" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for educt in edge.start_nodes.all %}
|
||||
<a class="btn btn-default" href="{{ educt.url }}">{{ educt.name }}</a>
|
||||
<a class="btn btn-default" href="{{ educt.url }}">{{ educt.name|safe }}</a>
|
||||
{% endfor %}
|
||||
<span class="glyphicon glyphicon-arrow-right" style="margin-left:5em;margin-right:5em;"
|
||||
aria-hidden="true"></span>
|
||||
{% for product in edge.end_nodes.all %}
|
||||
<a class="btn btn-default" href="{{ product.url }}">{{ product.name }}</a>
|
||||
<a class="btn btn-default" href="{{ product.url }}">{{ product.name|safe }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
@ -116,7 +116,7 @@
|
||||
<div id="edge-rules" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for r in edge.edge_label.rules.all %}
|
||||
<a class="list-group-item" href="{{ r.url }}">{{ r.name }}</a>
|
||||
<a class="list-group-item" href="{{ r.url }}">{{ r.name|safe }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
@ -132,7 +132,7 @@
|
||||
<div id="edge-scenario" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for s in edge.scenarios.all %}
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
<div class="panel-group" id="package-detail">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||
{{ group.name }}
|
||||
{{ group.name|safe }}
|
||||
<div id="actionsButton"
|
||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
|
||||
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||
@ -26,7 +26,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p> {{ group.description }} </p>
|
||||
<p> {{ group.description|safe }} </p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -39,10 +39,10 @@
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
{% for um in group.user_member.all %}
|
||||
<a class="list-group-item" href="{{ um.url }}">{{ um.username }}</a>
|
||||
<a class="list-group-item" href="{{ um.url }}">{{ um.username|safe }}</a>
|
||||
{% endfor %}
|
||||
{% for gm in group.group_member.all %}
|
||||
<a class="list-group-item" href="{{ gm.url }}">{{ gm.name }}</a>
|
||||
<a class="list-group-item" href="{{ gm.url }}">{{ gm.name|safe }}</a>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
@ -56,7 +56,7 @@
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
{% for p in packages %}
|
||||
<a class="list-group-item" href="{{ p.url }}">{{ p.name }}</a>
|
||||
<a class="list-group-item" href="{{ p.url }}">{{ p.name|safe }}</a>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
{% extends "framework.html" %}
|
||||
{% load static %}
|
||||
{% load envipytags %}
|
||||
{% block content %}
|
||||
|
||||
{% block action_modals %}
|
||||
@ -18,7 +17,7 @@
|
||||
<div class="panel-group" id="model-detail">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||
{{ model.name }}
|
||||
{{ model.name|safe }}
|
||||
<div id="actionsButton"
|
||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
|
||||
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||
@ -33,7 +32,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p> {{ model.description }} </p>
|
||||
<p> {{ model.description|safe }} </p>
|
||||
</div>
|
||||
{% if model|classname == 'MLRelativeReasoning' or model|classname == 'RuleBasedRelativeReasoning'%}
|
||||
<!-- Rule Packages -->
|
||||
@ -46,7 +45,7 @@
|
||||
<div id="rule-package" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for p in model.rule_packages.all %}
|
||||
<a class="list-group-item" href="{{ p.url }}">{{ p.name }}</a>
|
||||
<a class="list-group-item" href="{{ p.url }}">{{ p.name|safe }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
@ -60,7 +59,7 @@
|
||||
<div id="reaction-package" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for p in model.data_packages.all %}
|
||||
<a class="list-group-item" href="{{ p.url }}">{{ p.name }}</a>
|
||||
<a class="list-group-item" href="{{ p.url }}">{{ p.name|safe }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
@ -75,7 +74,7 @@
|
||||
<div id="eval-package" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for p in model.eval_packages.all %}
|
||||
<a class="list-group-item" href="{{ p.url }}">{{ p.name }}</a>
|
||||
<a class="list-group-item" href="{{ p.url }}">{{ p.name|safe }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
<div class="panel-group" id="node-detail">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||
{{ node.name }}
|
||||
{{ node.name|safe }}
|
||||
<div id="actionsButton"
|
||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
|
||||
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||
@ -39,7 +39,7 @@
|
||||
</div>
|
||||
<div id="node-desc" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{{ node.description }}
|
||||
{{ node.description|safe }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -98,7 +98,7 @@
|
||||
<div id="node-scenario" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for s in node.scenarios.all %}
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
<div class="panel-group" id="package-detail">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||
{{ package.name }}
|
||||
{{ package.name|safe }}
|
||||
<div id="actionsButton"
|
||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
|
||||
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||
|
||||
@ -98,7 +98,7 @@
|
||||
<div class="panel-group" id="pwAccordion">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||
{{ pathway.name }}
|
||||
{{ pathway.name|safe }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||
@ -236,7 +236,7 @@
|
||||
<div id="pathway-scenario" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for s in pathway.scenarios.all %}
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
@ -266,7 +266,7 @@
|
||||
<td colspan="2">
|
||||
<li class="list-group-item">
|
||||
<a href="{{ pathway.setting.model.url }}">
|
||||
{{ pathway.setting.model.name }}
|
||||
{{ pathway.setting.model.name|safe }}
|
||||
</a>
|
||||
</li>
|
||||
</td>
|
||||
@ -299,7 +299,7 @@
|
||||
{% for p in pathway.setting.rule_packages.all %}
|
||||
<li class="list-group-item">
|
||||
<a href="{{ p.url }}">
|
||||
{{ p.name }}
|
||||
{{ p.name|safe }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
<div class="panel-group" id="reaction-detail">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||
{{ reaction.name }}
|
||||
{{ reaction.name|safe }}
|
||||
<div id="actionsButton"
|
||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
|
||||
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||
@ -38,7 +38,7 @@
|
||||
</div>
|
||||
<div id="reaction-desc" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{{ reaction.description }}
|
||||
{{ reaction.description|safe }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -84,12 +84,12 @@
|
||||
<div id="reaction-description-smiles" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for educt in reaction.educts.all %}
|
||||
<a class="btn btn-default" href="{{ educt.url }}">{{ educt.name }}</a>
|
||||
<a class="btn btn-default" href="{{ educt.url }}">{{ educt.name|safe }}</a>
|
||||
{% endfor %}
|
||||
<span class="glyphicon glyphicon-arrow-right" style="margin-left:5em;margin-right:5em;"
|
||||
aria-hidden="true"></span>
|
||||
{% for product in reaction.products.all %}
|
||||
<a class="btn btn-default" href="{{ product.url }}">{{ product.name }}</a>
|
||||
<a class="btn btn-default" href="{{ product.url }}">{{ product.name|safe }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
@ -118,7 +118,7 @@
|
||||
<div id="reaction-rules" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for r in reaction.rules.all %}
|
||||
<a class="list-group-item" href="{{ r.url }}">{{ r.name }}</a>
|
||||
<a class="list-group-item" href="{{ r.url }}">{{ r.name|safe }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
@ -152,7 +152,7 @@
|
||||
<div id="reaction-pathway" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for r in reaction.related_pathways %}
|
||||
<a class="list-group-item" href="{{ r.url }}">{{ r.name }}</a>
|
||||
<a class="list-group-item" href="{{ r.url }}">{{ r.name|safe }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
@ -168,7 +168,7 @@
|
||||
<div id="reaction-scenario" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for s in reaction.scenarios.all %}
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<div class="panel-group" id="scenario-detail">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||
{{ scenario.name }}
|
||||
{{ scenario.name|safe }}
|
||||
<div id="actionsButton"
|
||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
|
||||
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||
@ -30,7 +30,7 @@
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Description</div>
|
||||
<div class="panel-body">
|
||||
{{ scenario.description }}
|
||||
{{ scenario.description|safe }}
|
||||
<br>
|
||||
{{ scenario.scenario_type }}
|
||||
<br>
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
<div class="panel-group" id="rule-detail">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||
{{ rule.name }}
|
||||
{{ rule.name|safe }}
|
||||
<div id="actionsButton"
|
||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
|
||||
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||
@ -29,7 +29,7 @@
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
{{ rule.description }}
|
||||
{{ rule.description|safe }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -145,7 +145,7 @@
|
||||
<div id="rule-composite-rule" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for cr in rule.parallelrule_set.all %}
|
||||
<a class="list-group-item" href="{{ cr.url }}">{{ cr.name }}</a>
|
||||
<a class="list-group-item" href="{{ cr.url }}">{{ cr.name|safe }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
@ -162,7 +162,7 @@
|
||||
<div id="rule-scenario" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for s in rule.scenarios.all %}
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name }}</a>
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name|safe }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
@ -179,7 +179,7 @@
|
||||
<div id="rule-reaction" class="panel-collapse collapse">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for r in rule.related_reactions %}
|
||||
<a class="list-group-item" href="{{ r.url }}">{{ r.name }}</a>
|
||||
<a class="list-group-item" href="{{ r.url }}">{{ r.name|safe }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
@ -196,7 +196,7 @@
|
||||
<div id="rule-pathway" class="panel-collapse collapse">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for r in rule.related_pathways %}
|
||||
<a class="list-group-item" href="{{ r.url }}">{{ r.name }}</a>
|
||||
<a class="list-group-item" href="{{ r.url }}">{{ r.name|safe }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
<div id="default-package" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
<li class="list-group-item">
|
||||
<a href="{{ user.default_package.url }}"> {{ user.default_package.name }}</a>
|
||||
<a href="{{ user.default_package.url }}"> {{ user.default_package.name|safe }}</a>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
@ -58,7 +58,7 @@
|
||||
<div class="panel-body list-group-item">
|
||||
{% for g in meta.available_groups %}
|
||||
<li class="list-group-item">
|
||||
<a href="{{ g.url }}"> {{ g.name }}</a>
|
||||
<a href="{{ g.url }}"> {{ g.name|safe }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@ -90,7 +90,7 @@
|
||||
<td colspan="2">
|
||||
<li class="list-group-item">
|
||||
<a href="{{user.default_setting.model.url}}">
|
||||
{{ user.default_setting.model.name }}
|
||||
{{ user.default_setting.model.name|safe }}
|
||||
</a>
|
||||
</li>
|
||||
</td>
|
||||
@ -123,7 +123,7 @@
|
||||
{% for p in user.default_setting.rule_packages.all %}
|
||||
<li class="list-group-item">
|
||||
<a href="{{p.url}}">
|
||||
{{ p.name }}
|
||||
{{ p.name|safe }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<p></p>
|
||||
{{ pathway.name }}
|
||||
{{ pathway.name|safe }}
|
||||
<div id="viz">
|
||||
<svg width="2000" height="2000"> <!-- Sehr großes SVG für Zoom -->
|
||||
<defs>
|
||||
|
||||
@ -11,13 +11,13 @@
|
||||
<option disabled>Reviewed Packages</option>
|
||||
{% endif %}
|
||||
{% for obj in reviewed_objects %}
|
||||
<option value="{{ obj.url }}" selected>{{ obj.name }}</option>
|
||||
<option value="{{ obj.url }}" selected>{{ obj.name|safe }}</option>
|
||||
{% endfor %}
|
||||
{% if unreviewed_objects %}
|
||||
<option disabled>Unreviewed Packages</option>
|
||||
{% endif %}
|
||||
{% for obj in unreviewed_objects %}
|
||||
<option value="{{ obj.url }}">{{ obj.name }}</option>
|
||||
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@ -29,7 +29,7 @@ class RuleTest(TestCase):
|
||||
self.assertEqual(r.name, "bt0022-2833")
|
||||
self.assertEqual(
|
||||
r.description,
|
||||
"Dihalomethyl derivative + Halomethyl derivative > 1-Halo-1-methylalcohol derivative + 1-Methylalcohol derivative",
|
||||
"Dihalomethyl derivative + Halomethyl derivative > 1-Halo-1-methylalcohol derivative + 1-Methylalcohol derivative",
|
||||
)
|
||||
|
||||
def test_smirks_are_trimmed(self):
|
||||
|
||||
@ -255,6 +255,30 @@ class FormatConverter(object):
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def is_valid_smarts(smarts: str) -> bool:
|
||||
"""
|
||||
Checks whether a given string is a valid SMARTS pattern.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
smarts : str
|
||||
The SMARTS string to validate.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
True if the SMARTS string is valid, False otherwise.
|
||||
"""
|
||||
if not isinstance(smarts, str) or not smarts.strip():
|
||||
return False
|
||||
|
||||
try:
|
||||
mol = Chem.MolFromSmarts(smarts)
|
||||
return mol is not None
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def apply(
|
||||
smiles: str,
|
||||
|
||||
41
uv.lock
generated
41
uv.lock
generated
@ -571,6 +571,7 @@ dependencies = [
|
||||
{ name = "epam-indigo" },
|
||||
{ name = "gunicorn" },
|
||||
{ name = "networkx" },
|
||||
{ name = "nh3" },
|
||||
{ name = "polars" },
|
||||
{ name = "psycopg2-binary" },
|
||||
{ name = "python-dotenv" },
|
||||
@ -613,6 +614,7 @@ requires-dist = [
|
||||
{ name = "gunicorn", specifier = ">=23.0.0" },
|
||||
{ name = "msal", marker = "extra == 'ms-login'", specifier = ">=1.33.0" },
|
||||
{ name = "networkx", specifier = ">=3.4.2" },
|
||||
{ name = "nh3", specifier = "==0.3.2" },
|
||||
{ name = "poethepoet", marker = "extra == 'dev'", specifier = ">=0.37.0" },
|
||||
{ name = "polars", specifier = "==1.35.1" },
|
||||
{ name = "pre-commit", marker = "extra == 'dev'", specifier = ">=4.3.0" },
|
||||
@ -1147,6 +1149,39 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406, upload-time = "2025-05-29T11:35:04.961Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nh3"
|
||||
version = "0.3.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ca/a5/34c26015d3a434409f4d2a1cd8821a06c05238703f49283ffeb937bef093/nh3-0.3.2.tar.gz", hash = "sha256:f394759a06df8b685a4ebfb1874fb67a9cbfd58c64fc5ed587a663c0e63ec376", size = 19288, upload-time = "2025-10-30T11:17:45.948Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/01/a1eda067c0ba823e5e2bb033864ae4854549e49fb6f3407d2da949106bfb/nh3-0.3.2-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d18957a90806d943d141cc5e4a0fefa1d77cf0d7a156878bf9a66eed52c9cc7d", size = 1419839, upload-time = "2025-10-30T11:17:09.956Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/57/07826ff65d59e7e9cc789ef1dc405f660cabd7458a1864ab58aefa17411b/nh3-0.3.2-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45c953e57028c31d473d6b648552d9cab1efe20a42ad139d78e11d8f42a36130", size = 791183, upload-time = "2025-10-30T11:17:11.99Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/2f/e8a86f861ad83f3bb5455f596d5c802e34fcdb8c53a489083a70fd301333/nh3-0.3.2-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c9850041b77a9147d6bbd6dbbf13eeec7009eb60b44e83f07fcb2910075bf9b", size = 829127, upload-time = "2025-10-30T11:17:13.192Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/97/77aef4daf0479754e8e90c7f8f48f3b7b8725a3b8c0df45f2258017a6895/nh3-0.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:403c11563e50b915d0efdb622866d1d9e4506bce590ef7da57789bf71dd148b5", size = 997131, upload-time = "2025-10-30T11:17:14.677Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/ee/fd8140e4df9d52143e89951dd0d797f5546004c6043285289fbbe3112293/nh3-0.3.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:0dca4365db62b2d71ff1620ee4f800c4729849906c5dd504ee1a7b2389558e31", size = 1068783, upload-time = "2025-10-30T11:17:15.861Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/64/bdd9631779e2d588b08391f7555828f352e7f6427889daf2fa424bfc90c9/nh3-0.3.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0fe7ee035dd7b2290715baf29cb27167dddd2ff70ea7d052c958dbd80d323c99", size = 994732, upload-time = "2025-10-30T11:17:17.155Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/66/90190033654f1f28ca98e3d76b8be1194505583f9426b0dcde782a3970a2/nh3-0.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a40202fd58e49129764f025bbaae77028e420f1d5b3c8e6f6fd3a6490d513868", size = 975997, upload-time = "2025-10-30T11:17:18.77Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/30/ebf8e2e8d71fdb5a5d5d8836207177aed1682df819cbde7f42f16898946c/nh3-0.3.2-cp314-cp314t-win32.whl", hash = "sha256:1f9ba555a797dbdcd844b89523f29cdc90973d8bd2e836ea6b962cf567cadd93", size = 583364, upload-time = "2025-10-30T11:17:20.286Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/ae/95c52b5a75da429f11ca8902c2128f64daafdc77758d370e4cc310ecda55/nh3-0.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:dce4248edc427c9b79261f3e6e2b3ecbdd9b88c267012168b4a7b3fc6fd41d13", size = 589982, upload-time = "2025-10-30T11:17:21.384Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/bd/c7d862a4381b95f2469704de32c0ad419def0f4a84b7a138a79532238114/nh3-0.3.2-cp314-cp314t-win_arm64.whl", hash = "sha256:019ecbd007536b67fdf76fab411b648fb64e2257ca3262ec80c3425c24028c80", size = 577126, upload-time = "2025-10-30T11:17:22.755Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/3e/f5a5cc2885c24be13e9b937441bd16a012ac34a657fe05e58927e8af8b7a/nh3-0.3.2-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7064ccf5ace75825bd7bf57859daaaf16ed28660c1c6b306b649a9eda4b54b1e", size = 1431980, upload-time = "2025-10-30T11:17:25.457Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/f7/529a99324d7ef055de88b690858f4189379708abae92ace799365a797b7f/nh3-0.3.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8745454cdd28bbbc90861b80a0111a195b0e3961b9fa2e672be89eb199fa5d8", size = 820805, upload-time = "2025-10-30T11:17:26.98Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/62/19b7c50ccd1fa7d0764822d2cea8f2a320f2fd77474c7a1805cb22cf69b0/nh3-0.3.2-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72d67c25a84579f4a432c065e8b4274e53b7cf1df8f792cf846abfe2c3090866", size = 803527, upload-time = "2025-10-30T11:17:28.284Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/ca/f022273bab5440abff6302731a49410c5ef66b1a9502ba3fbb2df998d9ff/nh3-0.3.2-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:13398e676a14d6233f372c75f52d5ae74f98210172991f7a3142a736bd92b131", size = 1051674, upload-time = "2025-10-30T11:17:29.909Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/f7/5728e3b32a11daf5bd21cf71d91c463f74305938bc3eb9e0ac1ce141646e/nh3-0.3.2-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03d617e5c8aa7331bd2659c654e021caf9bba704b109e7b2b28b039a00949fe5", size = 1004737, upload-time = "2025-10-30T11:17:31.205Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/7f/f17e0dba0a99cee29e6cee6d4d52340ef9cb1f8a06946d3a01eb7ec2fb01/nh3-0.3.2-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f55c4d2d5a207e74eefe4d828067bbb01300e06e2a7436142f915c5928de07", size = 911745, upload-time = "2025-10-30T11:17:32.945Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/0f/c76bf3dba22c73c38e9b1113b017cf163f7696f50e003404ec5ecdb1e8a6/nh3-0.3.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bb18403f02b655a1bbe4e3a4696c2ae1d6ae8f5991f7cacb684b1ae27e6c9f7", size = 797184, upload-time = "2025-10-30T11:17:34.226Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/a1/73d8250f888fb0ddf1b119b139c382f8903d8bb0c5bd1f64afc7e38dad1d/nh3-0.3.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6d66f41672eb4060cf87c037f760bdbc6847852ca9ef8e9c5a5da18f090abf87", size = 838556, upload-time = "2025-10-30T11:17:35.875Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/09/deb57f1fb656a7a5192497f4a287b0ade5a2ff6b5d5de4736d13ef6d2c1f/nh3-0.3.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f97f8b25cb2681d25e2338148159447e4d689aafdccfcf19e61ff7db3905768a", size = 1006695, upload-time = "2025-10-30T11:17:37.071Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/61/8f4d41c4ccdac30e4b1a4fa7be4b0f9914d8314a5058472f84c8e101a418/nh3-0.3.2-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:2ab70e8c6c7d2ce953d2a58102eefa90c2d0a5ed7aa40c7e29a487bc5e613131", size = 1075471, upload-time = "2025-10-30T11:17:38.225Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/c6/966aec0cb4705e69f6c3580422c239205d5d4d0e50fac380b21e87b6cf1b/nh3-0.3.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:1710f3901cd6440ca92494ba2eb6dc260f829fa8d9196b659fa10de825610ce0", size = 1002439, upload-time = "2025-10-30T11:17:39.553Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/c8/97a2d5f7a314cce2c5c49f30c6f161b7f3617960ade4bfc2fd1ee092cb20/nh3-0.3.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:91e9b001101fb4500a2aafe3e7c92928d85242d38bf5ac0aba0b7480da0a4cd6", size = 987439, upload-time = "2025-10-30T11:17:40.81Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/95/2d6fc6461687d7a171f087995247dec33e8749a562bfadd85fb5dbf37a11/nh3-0.3.2-cp38-abi3-win32.whl", hash = "sha256:169db03df90da63286e0560ea0efa9b6f3b59844a9735514a1d47e6bb2c8c61b", size = 589826, upload-time = "2025-10-30T11:17:42.239Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/9a/1a1c154f10a575d20dd634e5697805e589bbdb7673a0ad00e8da90044ba7/nh3-0.3.2-cp38-abi3-win_amd64.whl", hash = "sha256:562da3dca7a17f9077593214a9781a94b8d76de4f158f8c895e62f09573945fe", size = 596406, upload-time = "2025-10-30T11:17:43.773Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/7e/a96255f63b7aef032cbee8fc4d6e37def72e3aaedc1f72759235e8f13cb1/nh3-0.3.2-cp38-abi3-win_arm64.whl", hash = "sha256:cf5964d54edd405e68583114a7cba929468bcd7db5e676ae38ee954de1cfc104", size = 584162, upload-time = "2025-10-30T11:17:44.96Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodeenv"
|
||||
version = "1.9.1"
|
||||
@ -1854,11 +1889,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "redis"
|
||||
version = "7.0.1"
|
||||
version = "6.4.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/57/8f/f125feec0b958e8d22c8f0b492b30b1991d9499a4315dfde466cf4289edc/redis-7.0.1.tar.gz", hash = "sha256:c949df947dca995dc68fdf5a7863950bf6df24f8d6022394585acc98e81624f1", size = 4755322, upload-time = "2025-10-27T14:34:00.33Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0d/d6/e8b92798a5bd67d659d51a18170e91c16ac3b59738d91894651ee255ed49/redis-6.4.0.tar.gz", hash = "sha256:b01bc7282b8444e28ec36b261df5375183bb47a07eb9c603f284e89cbc5ef010", size = 4647399, upload-time = "2025-08-07T08:10:11.441Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/97/9f22a33c475cda519f20aba6babb340fb2f2254a02fb947816960d1e669a/redis-7.0.1-py3-none-any.whl", hash = "sha256:4977af3c7d67f8f0eb8b6fec0dafc9605db9343142f634041fb0235f67c0588a", size = 339938, upload-time = "2025-10-27T14:33:58.553Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/02/89e2ed7e85db6c93dfa9e8f691c5087df4e3551ab39081a4d7c6d1f90e05/redis-6.4.0-py3-none-any.whl", hash = "sha256:f0544fa9604264e9464cdf4814e7d4830f74b165d52f2a330a760a88dd248b7f", size = 279847, upload-time = "2025-08-07T08:10:09.84Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in New Issue
Block a user