forked from enviPath/enviPy
Basic System (#31)
Co-authored-by: Tim Lorsbach <tim@lorsba.ch> Reviewed-on: enviPath/enviPy#31
This commit is contained in:
@ -15,6 +15,7 @@ from pathlib import Path
|
|||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from envipy_plugins import Classifier, Property, Descriptor
|
from envipy_plugins import Classifier, Property, Descriptor
|
||||||
from sklearn.ensemble import RandomForestClassifier
|
from sklearn.ensemble import RandomForestClassifier
|
||||||
|
from sklearn.tree import DecisionTreeClassifier
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
@ -256,14 +257,29 @@ CELERY_RESULT_BACKEND = 'redis://localhost:6379/1'
|
|||||||
CELERY_ACCEPT_CONTENT = ['json']
|
CELERY_ACCEPT_CONTENT = ['json']
|
||||||
CELERY_TASK_SERIALIZER = 'json'
|
CELERY_TASK_SERIALIZER = 'json'
|
||||||
|
|
||||||
DEFAULT_MODELS_PARAMS = {
|
DEFAULT_RF_MODEL_PARAMS = {
|
||||||
'base_clf': RandomForestClassifier(n_estimators=100,
|
'base_clf': RandomForestClassifier(
|
||||||
max_features='log2',
|
n_estimators=100,
|
||||||
random_state=42,
|
max_features='log2',
|
||||||
criterion='entropy',
|
random_state=42,
|
||||||
ccp_alpha=0.0,
|
criterion='entropy',
|
||||||
max_depth=3,
|
ccp_alpha=0.0,
|
||||||
min_samples_leaf=1),
|
max_depth=3,
|
||||||
|
min_samples_leaf=1
|
||||||
|
),
|
||||||
|
'num_chains': 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFAULT_DT_MODEL_PARAMS = {
|
||||||
|
'base_clf': DecisionTreeClassifier(
|
||||||
|
criterion='entropy',
|
||||||
|
max_depth=3,
|
||||||
|
min_samples_split=5,
|
||||||
|
min_samples_leaf=5,
|
||||||
|
max_features='sqrt',
|
||||||
|
class_weight='balanced',
|
||||||
|
random_state=42
|
||||||
|
),
|
||||||
'num_chains': 10,
|
'num_chains': 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
249
epdb/logic.py
249
epdb/logic.py
@ -7,13 +7,21 @@ from django.db import transaction
|
|||||||
from django.conf import settings as s
|
from django.conf import settings as s
|
||||||
|
|
||||||
from epdb.models import User, Package, UserPackagePermission, GroupPackagePermission, Permission, Group, Setting, \
|
from epdb.models import User, Package, UserPackagePermission, GroupPackagePermission, Permission, Group, Setting, \
|
||||||
EPModel, UserSettingPermission, Rule, Pathway, Node, Edge
|
EPModel, UserSettingPermission, Rule, Pathway, Node, Edge, Compound, Reaction, CompoundStructure
|
||||||
|
from utilities.chem import FormatConverter
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class UserManager(object):
|
class UserManager(object):
|
||||||
|
user_pattern = re.compile(r".*/user/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_user(username, email, password, *args, **kwargs):
|
def is_user_url(url: str):
|
||||||
|
return bool(re.findall(UserManager.user_pattern, url))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_user(username, email, password, set_setting=True, add_to_group=True, *args, **kwargs):
|
||||||
# avoid circular import :S
|
# avoid circular import :S
|
||||||
from .tasks import send_registration_mail
|
from .tasks import send_registration_mail
|
||||||
|
|
||||||
@ -34,6 +42,17 @@ class UserManager(object):
|
|||||||
# send email for verification
|
# send email for verification
|
||||||
send_registration_mail.delay(u.pk)
|
send_registration_mail.delay(u.pk)
|
||||||
|
|
||||||
|
if set_setting:
|
||||||
|
u.default_setting = Setting.objects.get(global_default=True)
|
||||||
|
u.save()
|
||||||
|
|
||||||
|
if add_to_group:
|
||||||
|
g = Group.objects.get(public=True, name='enviPath Users')
|
||||||
|
g.user_member.add(u)
|
||||||
|
g.save()
|
||||||
|
u.default_group = g
|
||||||
|
u.save()
|
||||||
|
|
||||||
return u
|
return u
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -54,7 +73,16 @@ class UserManager(object):
|
|||||||
uuid = user_url.strip().split('/')[-1]
|
uuid = user_url.strip().split('/')[-1]
|
||||||
return get_user_model().objects.get(uuid=uuid)
|
return get_user_model().objects.get(uuid=uuid)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def writable(current_user, user):
|
||||||
|
return (current_user == user) or user.is_superuser
|
||||||
|
|
||||||
class GroupManager(object):
|
class GroupManager(object):
|
||||||
|
group_pattern = re.compile(r".*/group/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_group_url(url: str):
|
||||||
|
return bool(re.findall(GroupManager.group_pattern, url))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_group(current_user, name, description):
|
def create_group(current_user, name, description):
|
||||||
@ -110,9 +138,17 @@ class GroupManager(object):
|
|||||||
|
|
||||||
group.save()
|
group.save()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def writable(user, group):
|
||||||
|
return (user == group.owner) or user.is_superuser
|
||||||
|
|
||||||
|
|
||||||
class PackageManager(object):
|
class PackageManager(object):
|
||||||
package_pattern = re.compile(r".*/package/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")
|
package_pattern = re.compile(r".*/package/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_package_url(url: str):
|
||||||
|
return bool(re.findall(PackageManager.package_pattern, url))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_reviewed_packages():
|
def get_reviewed_packages():
|
||||||
@ -120,7 +156,6 @@ class PackageManager(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def readable(user, package):
|
def readable(user, package):
|
||||||
# TODO Owner!
|
|
||||||
if UserPackagePermission.objects.filter(package=package, user=user).exists() or \
|
if UserPackagePermission.objects.filter(package=package, user=user).exists() or \
|
||||||
GroupPackagePermission.objects.filter(package=package, group__in=GroupManager.get_groups(user)) or \
|
GroupPackagePermission.objects.filter(package=package, group__in=GroupManager.get_groups(user)) or \
|
||||||
package.reviewed is True or \
|
package.reviewed is True or \
|
||||||
@ -131,14 +166,21 @@ class PackageManager(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def writable(user, package):
|
def writable(user, package):
|
||||||
# TODO Owner!
|
if UserPackagePermission.objects.filter(package=package, user=user, permission=Permission.WRITE[0]).exists() or \
|
||||||
if UserPackagePermission.objects.filter(package=package, user=user, permission=Permission.WRITE).exists() or \
|
GroupPackagePermission.objects.filter(package=package, group__in=GroupManager.get_groups(user), permission=Permission.WRITE[0]).exists() or \
|
||||||
GroupPackagePermission.objects.filter(package=package, group__in=GroupManager.get_groups(user),
|
UserPackagePermission.objects.filter(package=package, user=user, permission=Permission.ALL[0]).exists() or \
|
||||||
permission=Permission.WRITE) or \
|
|
||||||
user.is_superuser:
|
user.is_superuser:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_package_lp(package_url):
|
||||||
|
match = re.findall(PackageManager.package_pattern, package_url)
|
||||||
|
if match:
|
||||||
|
package_id = match[0].split('/')[-1]
|
||||||
|
return Package.objects.get(uuid=package_id)
|
||||||
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_package_by_url(user, package_url):
|
def get_package_by_url(user, package_url):
|
||||||
match = re.findall(PackageManager.package_pattern, package_url)
|
match = re.findall(PackageManager.package_pattern, package_url)
|
||||||
@ -229,8 +271,12 @@ class PackageManager(object):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def update_permissions(caller: User, package: Package, grantee: Union[User, Group], new_perm: Optional[str]):
|
def update_permissions(caller: User, package: Package, grantee: Union[User, Group], new_perm: Optional[str]):
|
||||||
if not PackageManager.writable(caller, package):
|
caller_perm = None
|
||||||
raise ValueError(f"User {caller} is not allowed to modify permissions on {package}")
|
if not caller.is_superuser:
|
||||||
|
caller_perm = UserPackagePermission.objects.get(user=caller, package=package).permission
|
||||||
|
|
||||||
|
if caller_perm != Permission.ALL[0] and not caller.is_superuser:
|
||||||
|
raise ValueError(f"Only owner are allowed to modify permissions")
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'package': package,
|
'package': package,
|
||||||
@ -629,8 +675,6 @@ class PackageManager(object):
|
|||||||
|
|
||||||
return pack
|
return pack
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SettingManager(object):
|
class SettingManager(object):
|
||||||
setting_pattern = re.compile(r".*/setting/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")
|
setting_pattern = re.compile(r".*/setting/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")
|
||||||
|
|
||||||
@ -648,7 +692,8 @@ class SettingManager(object):
|
|||||||
def get_setting_by_id(user, setting_id):
|
def get_setting_by_id(user, setting_id):
|
||||||
s = Setting.objects.get(uuid=setting_id)
|
s = Setting.objects.get(uuid=setting_id)
|
||||||
|
|
||||||
if s.global_default or s.public or s.owner == user or user.is_superuser:
|
if s.global_default or s.public or user.is_superuser or \
|
||||||
|
UserSettingPermission.objects.filter(user=user, setting=s).exists():
|
||||||
return s
|
return s
|
||||||
|
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
@ -697,6 +742,116 @@ class SettingManager(object):
|
|||||||
def set_default_setting(user: User, setting: Setting):
|
def set_default_setting(user: User, setting: Setting):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class SearchManager(object):
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def search(packages: Union[Package, List[Package]], searchterm: str, mode: str):
|
||||||
|
match mode:
|
||||||
|
case 'text':
|
||||||
|
return SearchManager._search_text(packages, searchterm)
|
||||||
|
case 'default':
|
||||||
|
return SearchManager._search_default_smiles(packages, searchterm)
|
||||||
|
case 'exact':
|
||||||
|
return SearchManager._search_exact_smiles(packages, searchterm)
|
||||||
|
case 'canonical':
|
||||||
|
return SearchManager._search_canonical_smiles(packages, searchterm)
|
||||||
|
case 'inchikey':
|
||||||
|
return SearchManager._search_inchikey(packages, searchterm)
|
||||||
|
case _:
|
||||||
|
raise ValueError(f"Unknown search mode {mode}!")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _search_inchikey(packages: Union[Package, List[Package]], searchterm: str):
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
search_cond = Q(inchikey=searchterm)
|
||||||
|
compound_qs = Compound.objects.filter(Q(package__in=packages) & Q(compoundstructure__inchikey=searchterm)).distinct()
|
||||||
|
compound_structure_qs = CompoundStructure.objects.filter(Q(compound__package__in=packages) & search_cond)
|
||||||
|
reactions_qs = Reaction.objects.filter(Q(package__in=packages) & (Q(educts__inchikey=searchterm) | Q(products__inchikey=searchterm))).distinct()
|
||||||
|
pathway_qs = Pathway.objects.filter(Q(package__in=packages) & (Q(edge__edge_label__educts__inchikey=searchterm) | Q(edge__edge_label__products__inchikey=searchterm))).distinct()
|
||||||
|
|
||||||
|
return {
|
||||||
|
'Compounds': [{'name': c.name, 'description': c.description, 'url': c.url} for c in compound_qs],
|
||||||
|
'Compound Structures': [{'name': c.name, 'description': c.description, 'url': c.url} for c in compound_structure_qs],
|
||||||
|
'Reactions': [{'name': r.name, 'description': r.description, 'url': r.url} for r in reactions_qs],
|
||||||
|
'Pathways': [{'name': p.name, 'description': p.description, 'url': p.url} for p in pathway_qs],
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _search_exact_smiles(packages: Union[Package, List[Package]], searchterm: str):
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
search_cond = Q(smiles=searchterm)
|
||||||
|
compound_qs = Compound.objects.filter(Q(package__in=packages) & Q(compoundstructure__smiles=searchterm)).distinct()
|
||||||
|
compound_structure_qs = CompoundStructure.objects.filter(Q(compound__package__in=packages) & search_cond)
|
||||||
|
reactions_qs = Reaction.objects.filter(Q(package__in=packages) & (Q(educts__smiles=searchterm) | Q(products__smiles=searchterm))).distinct()
|
||||||
|
pathway_qs = Pathway.objects.filter(Q(package__in=packages) & (Q(edge__edge_label__educts__smiles=searchterm) | Q(edge__edge_label__products__smiles=searchterm))).distinct()
|
||||||
|
|
||||||
|
return {
|
||||||
|
'Compounds': [{'name': c.name, 'description': c.description, 'url': c.url} for c in compound_qs],
|
||||||
|
'Compound Structures': [{'name': c.name, 'description': c.description, 'url': c.url} for c in compound_structure_qs],
|
||||||
|
'Reactions': [{'name': r.name, 'description': r.description, 'url': r.url} for r in reactions_qs],
|
||||||
|
'Pathways': [{'name': p.name, 'description': p.description, 'url': p.url} for p in pathway_qs],
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _search_default_smiles(packages: Union[Package, List[Package]], searchterm: str):
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
inchi_front = FormatConverter.InChIKey(searchterm)[:14]
|
||||||
|
|
||||||
|
search_cond = Q(inchikey__startswith=inchi_front)
|
||||||
|
compound_qs = Compound.objects.filter(Q(package__in=packages) & Q(compoundstructure__inchikey__startswith=inchi_front)).distinct()
|
||||||
|
compound_structure_qs = CompoundStructure.objects.filter(Q(compound__package__in=packages) & search_cond)
|
||||||
|
reactions_qs = Reaction.objects.filter(Q(package__in=packages) & (Q(educts__inchikey__startswith=inchi_front) | Q(products__inchikey__startswith=inchi_front))).distinct()
|
||||||
|
pathway_qs = Pathway.objects.filter(Q(package__in=packages) & (Q(edge__edge_label__educts__inchikey__startswith=inchi_front) | Q(edge__edge_label__products__inchikey__startswith=inchi_front))).distinct()
|
||||||
|
|
||||||
|
return {
|
||||||
|
'Compounds': [{'name': c.name, 'description': c.description, 'url': c.url} for c in compound_qs],
|
||||||
|
'Compound Structures': [{'name': c.name, 'description': c.description, 'url': c.url} for c in compound_structure_qs],
|
||||||
|
'Reactions': [{'name': r.name, 'description': r.description, 'url': r.url} for r in reactions_qs],
|
||||||
|
'Pathways': [{'name': p.name, 'description': p.description, 'url': p.url} for p in pathway_qs],
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _search_canonical_smiles(packages: Union[Package, List[Package]], searchterm: str):
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
search_cond = Q(canonical_smiles=searchterm)
|
||||||
|
compound_qs = Compound.objects.filter(Q(package__in=packages) & Q(compoundstructure__canonical_smiles=searchterm)).distinct()
|
||||||
|
compound_structure_qs = CompoundStructure.objects.filter(Q(compound__package__in=packages) & search_cond)
|
||||||
|
reactions_qs = Reaction.objects.filter(Q(package__in=packages) & (Q(educts__canonical_smiles=searchterm) | Q(products__canonical_smiles=searchterm))).distinct()
|
||||||
|
pathway_qs = Pathway.objects.filter(Q(package__in=packages) & (Q(edge__edge_label__educts__canonical_smiles=searchterm) | Q(edge__edge_label__products__canonical_smiles=searchterm))).distinct()
|
||||||
|
|
||||||
|
return {
|
||||||
|
'Compounds': [{'name': c.name, 'description': c.description, 'url': c.url} for c in compound_qs],
|
||||||
|
'Compound Structures': [{'name': c.name, 'description': c.description, 'url': c.url} for c in compound_structure_qs],
|
||||||
|
'Reactions': [{'name': r.name, 'description': r.description, 'url': r.url} for r in reactions_qs],
|
||||||
|
'Pathways': [{'name': p.name, 'description': p.description, 'url': p.url} for p in pathway_qs],
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _search_text(packages: Union[Package, List[Package]], searchterm: str):
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
search_cond = (Q(name__icontains=searchterm) | Q(description__icontains=searchterm))
|
||||||
|
cond = Q(package__in=packages) & search_cond
|
||||||
|
compound_qs = Compound.objects.filter(cond)
|
||||||
|
compound_structure_qs = CompoundStructure.objects.filter(Q(compound__package__in=packages) & search_cond)
|
||||||
|
rule_qs = Rule.objects.filter(cond)
|
||||||
|
reactions_qs = Reaction.objects.filter(cond)
|
||||||
|
pathway_qs = Pathway.objects.filter(cond)
|
||||||
|
|
||||||
|
res = {
|
||||||
|
'Compounds': [{'name': c.name, 'description': c.description, 'url': c.url} for c in compound_qs],
|
||||||
|
'Compound Structures': [{'name': c.name, 'description': c.description, 'url': c.url} for c in compound_structure_qs],
|
||||||
|
'Rules': [{'name': r.name, 'description': r.description, 'url': r.url} for r in rule_qs],
|
||||||
|
'Reactions': [{'name': r.name, 'description': r.description, 'url': r.url} for r in reactions_qs],
|
||||||
|
'Pathways': [{'name': p.name, 'description': p.description, 'url': p.url} for p in pathway_qs],
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
class SNode(object):
|
class SNode(object):
|
||||||
|
|
||||||
@ -719,7 +874,7 @@ class SNode(object):
|
|||||||
class SEdge(object):
|
class SEdge(object):
|
||||||
|
|
||||||
def __init__(self, educts: Union[SNode, List[SNode]], products: Union[SNode | List[SNode]],
|
def __init__(self, educts: Union[SNode, List[SNode]], products: Union[SNode | List[SNode]],
|
||||||
rule: Optional['Rule'] = None):
|
rule: Optional['Rule'] = None, probability: Optional[float] = None):
|
||||||
|
|
||||||
if not isinstance(educts, list):
|
if not isinstance(educts, list):
|
||||||
educts = [educts]
|
educts = [educts]
|
||||||
@ -727,6 +882,7 @@ class SEdge(object):
|
|||||||
self.educts = educts
|
self.educts = educts
|
||||||
self.products = products
|
self.products = products
|
||||||
self.rule = rule
|
self.rule = rule
|
||||||
|
self.probability = probability
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
full_hash = 0
|
full_hash = 0
|
||||||
@ -799,11 +955,45 @@ class SPathway(object):
|
|||||||
elif isinstance(n, SNode):
|
elif isinstance(n, SNode):
|
||||||
self.root_nodes.append(n)
|
self.root_nodes.append(n)
|
||||||
|
|
||||||
self.queue = list()
|
|
||||||
self.smiles_to_node: Dict[str, SNode] = dict(**{n.smiles: n for n in self.root_nodes})
|
self.smiles_to_node: Dict[str, SNode] = dict(**{n.smiles: n for n in self.root_nodes})
|
||||||
self.edges: Set['SEdge'] = set()
|
self.edges: Set['SEdge'] = set()
|
||||||
self.done = False
|
self.done = False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_pathway(pw: 'Pathway', persist: bool = True):
|
||||||
|
""" Initializes a SPathway with a state given by a Pathway """
|
||||||
|
spw = SPathway(root_nodes=pw.root_nodes, persist=pw if persist else None, prediction_setting=pw.setting)
|
||||||
|
# root_nodes are already added in __init__, add remaining nodes
|
||||||
|
for n in pw.nodes:
|
||||||
|
snode = SNode(n.default_node_label.smiles, n.depth)
|
||||||
|
if snode.smiles not in spw.smiles_to_node:
|
||||||
|
spw.smiles_to_node[snode.smiles] = snode
|
||||||
|
spw.snode_persist_lookup[snode] = n
|
||||||
|
|
||||||
|
for e in pw.edges:
|
||||||
|
sub = []
|
||||||
|
prod = []
|
||||||
|
|
||||||
|
for n in e.start_nodes.all():
|
||||||
|
sub.append(spw.smiles_to_node[n.default_node_label.smiles])
|
||||||
|
|
||||||
|
for n in e.end_nodes.all():
|
||||||
|
prod.append(spw.smiles_to_node[n.default_node_label.smiles])
|
||||||
|
|
||||||
|
rule = None
|
||||||
|
if e.edge_label.rules.all():
|
||||||
|
rule = e.edge_label.rules.all().first()
|
||||||
|
|
||||||
|
prob = None
|
||||||
|
if e.kv.get('probability'):
|
||||||
|
prob = float(e.kv['probability'])
|
||||||
|
|
||||||
|
sedge = SEdge(sub, prod, rule=rule, probability=prob)
|
||||||
|
spw.edges.add(sedge)
|
||||||
|
spw.sedge_persist_lookup[sedge] = e
|
||||||
|
|
||||||
|
return spw
|
||||||
|
|
||||||
def num_nodes(self):
|
def num_nodes(self):
|
||||||
return len(self.smiles_to_node.keys())
|
return len(self.smiles_to_node.keys())
|
||||||
|
|
||||||
@ -830,8 +1020,18 @@ class SPathway(object):
|
|||||||
|
|
||||||
return sorted(res, key=lambda x: hash(x))
|
return sorted(res, key=lambda x: hash(x))
|
||||||
|
|
||||||
def predict_step(self, from_depth: int = 0):
|
def predict_step(self, from_depth: int = None, from_node: 'Node' = None):
|
||||||
substrates = self._get_nodes_for_depth(from_depth)
|
substrates: List[SNode] = []
|
||||||
|
|
||||||
|
if from_depth is not None:
|
||||||
|
substrates = self._get_nodes_for_depth(from_depth)
|
||||||
|
elif from_node is not None:
|
||||||
|
for k,v in self.snode_persist_lookup.items():
|
||||||
|
if from_node == v:
|
||||||
|
substrates = [k]
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise ValueError("Neither from_depth nor from_node_url specified")
|
||||||
|
|
||||||
new_tp = False
|
new_tp = False
|
||||||
if substrates:
|
if substrates:
|
||||||
@ -849,13 +1049,19 @@ class SPathway(object):
|
|||||||
node = self.smiles_to_node[c]
|
node = self.smiles_to_node[c]
|
||||||
cand_nodes.append(node)
|
cand_nodes.append(node)
|
||||||
|
|
||||||
edge = SEdge(sub, cand_nodes, cand_set.rule)
|
edge = SEdge(sub, cand_nodes, rule=cand_set.rule, probability=cand_set.probability)
|
||||||
self.edges.add(edge)
|
self.edges.add(edge)
|
||||||
else:
|
|
||||||
|
# In case no substrates are found, we're done.
|
||||||
|
# For "predict from node" we're always done
|
||||||
|
if len(substrates) == 0 or from_node is not None:
|
||||||
self.done = True
|
self.done = True
|
||||||
|
|
||||||
|
# Check if we need to write back data to database
|
||||||
if new_tp and self.persist:
|
if new_tp and self.persist:
|
||||||
self._sync_to_pathway()
|
self._sync_to_pathway()
|
||||||
|
# call save to update internal modified field
|
||||||
|
self.persist.save()
|
||||||
|
|
||||||
def _sync_to_pathway(self):
|
def _sync_to_pathway(self):
|
||||||
logger.info("Updating Pathway with SPathway")
|
logger.info("Updating Pathway with SPathway")
|
||||||
@ -876,6 +1082,11 @@ class SPathway(object):
|
|||||||
product_nodes.append(self.snode_persist_lookup[snode])
|
product_nodes.append(self.snode_persist_lookup[snode])
|
||||||
|
|
||||||
e = Edge.create(self.persist, educt_nodes, product_nodes, sedge.rule)
|
e = Edge.create(self.persist, educt_nodes, product_nodes, sedge.rule)
|
||||||
|
|
||||||
|
if sedge.probability:
|
||||||
|
e.kv.update({'probability': sedge.probability})
|
||||||
|
e.save()
|
||||||
|
|
||||||
self.sedge_persist_lookup[sedge] = e
|
self.sedge_persist_lookup[sedge] = e
|
||||||
|
|
||||||
logger.info("Update done!")
|
logger.info("Update done!")
|
||||||
|
|||||||
@ -13,12 +13,14 @@ class Command(BaseCommand):
|
|||||||
def create_users(self):
|
def create_users(self):
|
||||||
|
|
||||||
if not User.objects.filter(email='anon@lorsba.ch').exists():
|
if not User.objects.filter(email='anon@lorsba.ch').exists():
|
||||||
anon = UserManager.create_user("anonymous", "anon@lorsba.ch", "SuperSafe", is_active=True)
|
anon = UserManager.create_user("anonymous", "anon@lorsba.ch", "SuperSafe", is_active=True,
|
||||||
|
add_to_group=False, set_setting=False)
|
||||||
else:
|
else:
|
||||||
anon = User.objects.get(email='anon@lorsba.ch')
|
anon = User.objects.get(email='anon@lorsba.ch')
|
||||||
|
|
||||||
if not User.objects.filter(email='admin@lorsba.ch').exists():
|
if not User.objects.filter(email='admin@lorsba.ch').exists():
|
||||||
admin = UserManager.create_user("admin", "admin@lorsba.ch", "SuperSafe", is_active=True)
|
admin = UserManager.create_user("admin", "admin@lorsba.ch", "SuperSafe", is_active=True, add_to_group=False,
|
||||||
|
set_setting=False)
|
||||||
admin.is_staff = True
|
admin.is_staff = True
|
||||||
admin.is_superuser = True
|
admin.is_superuser = True
|
||||||
admin.save()
|
admin.save()
|
||||||
@ -26,6 +28,9 @@ class Command(BaseCommand):
|
|||||||
admin = User.objects.get(email='admin@lorsba.ch')
|
admin = User.objects.get(email='admin@lorsba.ch')
|
||||||
|
|
||||||
g = GroupManager.create_group(admin, 'enviPath Users', 'All enviPath Users')
|
g = GroupManager.create_group(admin, 'enviPath Users', 'All enviPath Users')
|
||||||
|
g.public = True
|
||||||
|
g.save()
|
||||||
|
|
||||||
g.user_member.add(anon)
|
g.user_member.add(anon)
|
||||||
g.save()
|
g.save()
|
||||||
|
|
||||||
@ -36,7 +41,8 @@ class Command(BaseCommand):
|
|||||||
admin.save()
|
admin.save()
|
||||||
|
|
||||||
if not User.objects.filter(email='jebus@lorsba.ch').exists():
|
if not User.objects.filter(email='jebus@lorsba.ch').exists():
|
||||||
jebus = UserManager.create_user("jebus", "jebus@lorsba.ch", "SuperSafe", is_active=True)
|
jebus = UserManager.create_user("jebus", "jebus@lorsba.ch", "SuperSafe", is_active=True, add_to_group=False,
|
||||||
|
set_setting=False)
|
||||||
jebus.is_staff = True
|
jebus.is_staff = True
|
||||||
jebus.is_superuser = True
|
jebus.is_superuser = True
|
||||||
jebus.save()
|
jebus.save()
|
||||||
@ -94,6 +100,9 @@ class Command(BaseCommand):
|
|||||||
setting.make_global_default()
|
setting.make_global_default()
|
||||||
|
|
||||||
for u in [anon, jebus]:
|
for u in [anon, jebus]:
|
||||||
|
u.default_setting = setting
|
||||||
|
u.save()
|
||||||
|
|
||||||
usp = UserSettingPermission()
|
usp = UserSettingPermission()
|
||||||
usp.user = u
|
usp.user = u
|
||||||
usp.setting = setting
|
usp.setting = setting
|
||||||
@ -119,7 +128,7 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
X, y = ml_model.build_dataset()
|
X, y = ml_model.build_dataset()
|
||||||
ml_model.build_model(X, y)
|
ml_model.build_model(X, y)
|
||||||
ml_model.evaluate_model()
|
# ml_model.evaluate_model()
|
||||||
|
|
||||||
# If available create EnviFormerModel
|
# If available create EnviFormerModel
|
||||||
if s.ENVIFORMER_PRESENT:
|
if s.ENVIFORMER_PRESENT:
|
||||||
|
|||||||
303
epdb/models.py
303
epdb/models.py
@ -14,7 +14,7 @@ from django.contrib.auth.hashers import make_password, check_password
|
|||||||
from django.contrib.auth.models import AbstractUser
|
from django.contrib.auth.models import AbstractUser
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.contrib.postgres.fields import ArrayField
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.db.models import JSONField
|
from django.db.models import JSONField, Count, Q
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from model_utils.models import TimeStampedModel
|
from model_utils.models import TimeStampedModel
|
||||||
@ -43,8 +43,6 @@ class User(AbstractUser):
|
|||||||
on_delete=models.SET_NULL, related_name='default_group')
|
on_delete=models.SET_NULL, related_name='default_group')
|
||||||
default_setting = models.ForeignKey('epdb.Setting', on_delete=models.SET_NULL,
|
default_setting = models.ForeignKey('epdb.Setting', on_delete=models.SET_NULL,
|
||||||
verbose_name='The users default settings', null=True, blank=False)
|
verbose_name='The users default settings', null=True, blank=False)
|
||||||
# TODO remove
|
|
||||||
groups = models.ManyToManyField("Group", verbose_name='groups')
|
|
||||||
|
|
||||||
USERNAME_FIELD = "email"
|
USERNAME_FIELD = "email"
|
||||||
REQUIRED_FIELDS = ['username']
|
REQUIRED_FIELDS = ['username']
|
||||||
@ -87,6 +85,7 @@ class Group(TimeStampedModel):
|
|||||||
uuid = models.UUIDField(null=False, blank=False, verbose_name='UUID of this object', unique=True, default=uuid4)
|
uuid = models.UUIDField(null=False, blank=False, verbose_name='UUID of this object', unique=True, default=uuid4)
|
||||||
name = models.TextField(blank=False, null=False, verbose_name='Group name')
|
name = models.TextField(blank=False, null=False, verbose_name='Group name')
|
||||||
owner = models.ForeignKey("User", verbose_name='Group Owner', on_delete=models.CASCADE)
|
owner = models.ForeignKey("User", verbose_name='Group Owner', on_delete=models.CASCADE)
|
||||||
|
public = models.BooleanField(verbose_name='Public Group', default=False)
|
||||||
description = models.TextField(blank=False, null=False, verbose_name='Descriptions', default='no description')
|
description = models.TextField(blank=False, null=False, verbose_name='Descriptions', default='no description')
|
||||||
user_member = models.ManyToManyField("User", verbose_name='User members', related_name='users_in_group')
|
user_member = models.ManyToManyField("User", verbose_name='User members', related_name='users_in_group')
|
||||||
group_member = models.ManyToManyField("Group", verbose_name='Group member', related_name='groups_in_group')
|
group_member = models.ManyToManyField("Group", verbose_name='Group member', related_name='groups_in_group')
|
||||||
@ -314,7 +313,7 @@ class Compound(EnviPathModel, AliasMixin, ScenarioMixin):
|
|||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def create(package: Package, smiles: str, name: str = None, description: str = None, *args, **kwargs) -> 'Compound':
|
def create(package: Package, smiles: str, name: str = None, description: str = None, *args, **kwargs) -> 'Compound':
|
||||||
|
|
||||||
if smiles is None or smiles == '':
|
if smiles is None or smiles.strip() == '':
|
||||||
raise ValueError('SMILES is required')
|
raise ValueError('SMILES is required')
|
||||||
|
|
||||||
smiles = smiles.strip()
|
smiles = smiles.strip()
|
||||||
@ -338,12 +337,14 @@ class Compound(EnviPathModel, AliasMixin, ScenarioMixin):
|
|||||||
c = Compound()
|
c = Compound()
|
||||||
c.package = package
|
c.package = package
|
||||||
|
|
||||||
# For name and description we have defaults so only set them if they carry a real value
|
if name is None or name.strip() == '':
|
||||||
if name is not None and name != '':
|
name = f"Compound {Compound.objects.filter(package=package).count() + 1}"
|
||||||
c.name = name
|
|
||||||
|
|
||||||
if description is not None and description != '':
|
c.name = name
|
||||||
c.description = description
|
|
||||||
|
# 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.save()
|
c.save()
|
||||||
|
|
||||||
@ -403,25 +404,27 @@ class Compound(EnviPathModel, AliasMixin, ScenarioMixin):
|
|||||||
class CompoundStructure(EnviPathModel, AliasMixin, ScenarioMixin):
|
class CompoundStructure(EnviPathModel, AliasMixin, ScenarioMixin):
|
||||||
compound = models.ForeignKey('epdb.Compound', on_delete=models.CASCADE, db_index=True)
|
compound = models.ForeignKey('epdb.Compound', on_delete=models.CASCADE, db_index=True)
|
||||||
smiles = models.TextField(blank=False, null=False, verbose_name='SMILES')
|
smiles = models.TextField(blank=False, null=False, verbose_name='SMILES')
|
||||||
|
canonical_smiles = models.TextField(blank=False, null=False, verbose_name='Canonical SMILES')
|
||||||
|
inchikey = models.TextField(max_length=27, blank=False, null=False, verbose_name="InChIKey")
|
||||||
normalized_structure = models.BooleanField(null=False, blank=False, default=False)
|
normalized_structure = models.BooleanField(null=False, blank=False, default=False)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
# Compute these fields only on initial save call
|
||||||
|
if self.pk is None:
|
||||||
|
try:
|
||||||
|
# Generate canonical SMILES
|
||||||
|
self.canonical_smiles = FormatConverter.canonicalize(self.smiles)
|
||||||
|
# Generate InChIKey
|
||||||
|
self.inchikey = FormatConverter.InChIKey(self.smiles)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Could compute canonical SMILES/InChIKey from {self.smiles}, error: {e}")
|
||||||
|
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def url(self):
|
def url(self):
|
||||||
return '{}/structure/{}'.format(self.compound.url, self.uuid)
|
return '{}/structure/{}'.format(self.compound.url, self.uuid)
|
||||||
|
|
||||||
# @property
|
|
||||||
# def related_pathways(self):
|
|
||||||
# pathways = Node.objects.filter(node_labels__in=[self]).values_list('pathway', flat=True)
|
|
||||||
# return Pathway.objects.filter(package=self.compound.package, id__in=set(pathways)).order_by('name')
|
|
||||||
|
|
||||||
# @property
|
|
||||||
# def related_reactions(self):
|
|
||||||
# return (
|
|
||||||
# Reaction.objects.filter(package=self.compound.package, educts__in=[self])
|
|
||||||
# |
|
|
||||||
# Reaction.objects.filter(package=self.compound.package, products__in=[self])
|
|
||||||
# ).order_by('name')
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def create(compound: Compound, smiles: str, name: str = None, description: str = None, *args, **kwargs):
|
def create(compound: Compound, smiles: str, name: str = None, description: str = None, *args, **kwargs):
|
||||||
@ -448,19 +451,9 @@ class CompoundStructure(EnviPathModel, AliasMixin, ScenarioMixin):
|
|||||||
|
|
||||||
return cs
|
return cs
|
||||||
|
|
||||||
# TODO add find method
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def InChIKey(self):
|
def as_svg(self, width: int = 800, height: int = 400):
|
||||||
return FormatConverter.InChIKey(self.smiles)
|
return IndigoUtils.mol_to_svg(self.smiles, width=width, height=height)
|
||||||
|
|
||||||
@property
|
|
||||||
def canonical_smiles(self):
|
|
||||||
return FormatConverter.canonicalize(self.smiles)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def as_svg(self):
|
|
||||||
return IndigoUtils.mol_to_svg(self.smiles)
|
|
||||||
|
|
||||||
|
|
||||||
class Rule(PolymorphicModel, EnviPathModel, AliasMixin, ScenarioMixin):
|
class Rule(PolymorphicModel, EnviPathModel, AliasMixin, ScenarioMixin):
|
||||||
@ -492,19 +485,9 @@ class Rule(PolymorphicModel, EnviPathModel, AliasMixin, ScenarioMixin):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def create(package: Package, rule_type: str, name: str = None, description: str = None, *args, **kwargs):
|
def create(rule_type: str, *args, **kwargs):
|
||||||
r = Rule.cls_for_type(rule_type)()
|
cls = Rule.cls_for_type(rule_type)
|
||||||
r.package = package
|
return cls.create(*args, **kwargs)
|
||||||
r.name = name
|
|
||||||
r.description = description
|
|
||||||
|
|
||||||
# As we are setting params this way the "k" has to match the property name
|
|
||||||
for k, v in kwargs.items():
|
|
||||||
setattr(r, k, v)
|
|
||||||
|
|
||||||
r.save()
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# @property
|
# @property
|
||||||
@ -533,6 +516,54 @@ class SimpleAmbitRule(SimpleRule):
|
|||||||
reactant_filter_smarts = models.TextField(null=True, verbose_name='Reactant Filter SMARTS')
|
reactant_filter_smarts = models.TextField(null=True, verbose_name='Reactant Filter SMARTS')
|
||||||
product_filter_smarts = models.TextField(null=True, verbose_name='Product Filter SMARTS')
|
product_filter_smarts = models.TextField(null=True, verbose_name='Product Filter SMARTS')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@transaction.atomic
|
||||||
|
def create(package: Package, name: str = None, description: str = None, smirks: str = None,
|
||||||
|
reactant_filter_smarts: str = None, product_filter_smarts: str = None):
|
||||||
|
|
||||||
|
if smirks is None or smirks.strip() == '':
|
||||||
|
raise ValueError('SMIRKS is required!')
|
||||||
|
|
||||||
|
smirks = smirks.strip()
|
||||||
|
|
||||||
|
if not FormatConverter.is_valid_smirks(smirks):
|
||||||
|
raise ValueError(f'SMIRKS "{smirks}" is invalid!')
|
||||||
|
|
||||||
|
query = SimpleAmbitRule.objects.filter(package=package, smirks=smirks)
|
||||||
|
|
||||||
|
if reactant_filter_smarts is not None and reactant_filter_smarts.strip() != '':
|
||||||
|
query = query.filter(reactant_filter_smarts=reactant_filter_smarts)
|
||||||
|
|
||||||
|
if product_filter_smarts is not None and product_filter_smarts.strip() != '':
|
||||||
|
query = query.filter(product_filter_smarts=product_filter_smarts)
|
||||||
|
|
||||||
|
if query.exists():
|
||||||
|
if query.count() > 1:
|
||||||
|
logger.error(f'More than one rule matched this one! {query}')
|
||||||
|
return query.first()
|
||||||
|
|
||||||
|
r = SimpleAmbitRule()
|
||||||
|
r.package = package
|
||||||
|
|
||||||
|
if name is None or name.strip() == '':
|
||||||
|
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.smirks = smirks
|
||||||
|
|
||||||
|
if reactant_filter_smarts is not None and reactant_filter_smarts.strip() != '':
|
||||||
|
r.reactant_filter_smarts = reactant_filter_smarts
|
||||||
|
|
||||||
|
if product_filter_smarts is not None and product_filter_smarts.strip() != '':
|
||||||
|
r.product_filter_smarts = product_filter_smarts
|
||||||
|
|
||||||
|
r.save()
|
||||||
|
return r
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def url(self):
|
def url(self):
|
||||||
return '{}/simple-ambit-rule/{}'.format(self.package.url, self.uuid)
|
return '{}/simple-ambit-rule/{}'.format(self.package.url, self.uuid)
|
||||||
@ -642,7 +673,7 @@ class Reaction(EnviPathModel, AliasMixin, ScenarioMixin):
|
|||||||
def create(package: Package, name: str = None, description: str = None,
|
def create(package: Package, name: str = None, description: str = None,
|
||||||
educts: Union[List[str], List[CompoundStructure]] = None,
|
educts: Union[List[str], List[CompoundStructure]] = None,
|
||||||
products: Union[List[str], List[CompoundStructure]] = None,
|
products: Union[List[str], List[CompoundStructure]] = None,
|
||||||
rule: Rule = None, multi_step: bool = True):
|
rules: Union[Rule|List[Rule]] = None, multi_step: bool = True):
|
||||||
|
|
||||||
_educts = []
|
_educts = []
|
||||||
_products = []
|
_products = []
|
||||||
@ -662,18 +693,61 @@ class Reaction(EnviPathModel, AliasMixin, ScenarioMixin):
|
|||||||
_products += products
|
_products += products
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise ValueError("")
|
raise ValueError("Found mixed types for educts and/or products!")
|
||||||
|
|
||||||
|
if len(_educts) == 0 or len(_products) == 0:
|
||||||
|
raise ValueError("No educts or products specified!")
|
||||||
|
|
||||||
|
if rules is None:
|
||||||
|
rules = []
|
||||||
|
|
||||||
|
if isinstance(rules, Rule):
|
||||||
|
rules = [rules]
|
||||||
|
|
||||||
|
|
||||||
|
query = Reaction.objects.annotate(
|
||||||
|
educt_count=Count('educts', filter=Q(educts__in=_educts), distinct=True),
|
||||||
|
product_count=Count('products', filter=Q(products__in=_products), distinct=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
# The annotate/filter wont work if rules is an empty list
|
||||||
|
if rules:
|
||||||
|
query = query.annotate(
|
||||||
|
rule_count=Count('rules', filter=Q(rules__in=rules), distinct=True)
|
||||||
|
).filter(rule_count=len(rules))
|
||||||
|
else:
|
||||||
|
query = query.annotate(
|
||||||
|
rule_count=Count('rules', distinct=True)
|
||||||
|
).filter(rule_count=0)
|
||||||
|
|
||||||
|
existing_reaction_qs = query.filter(
|
||||||
|
educt_count=len(_educts),
|
||||||
|
product_count=len(_products),
|
||||||
|
multi_step=multi_step,
|
||||||
|
package=package
|
||||||
|
)
|
||||||
|
|
||||||
|
if existing_reaction_qs.exists():
|
||||||
|
if existing_reaction_qs.count() > 1:
|
||||||
|
logger.error(f'Found more than one reaction for given input! {existing_reaction_qs}')
|
||||||
|
return existing_reaction_qs.first()
|
||||||
|
|
||||||
r = Reaction()
|
r = Reaction()
|
||||||
r.package = package
|
r.package = package
|
||||||
r.name = name
|
|
||||||
r.description = description
|
if name is not None and name.strip() != '':
|
||||||
|
r.name = name
|
||||||
|
|
||||||
|
if description is not None and name.strip() != '':
|
||||||
|
r.description = description
|
||||||
|
|
||||||
r.multi_step = multi_step
|
r.multi_step = multi_step
|
||||||
|
|
||||||
r.save()
|
r.save()
|
||||||
|
|
||||||
if rule:
|
if rules:
|
||||||
r.rules.add(rule)
|
for rule in rules:
|
||||||
|
r.rules.add(rule)
|
||||||
|
|
||||||
for educt in _educts:
|
for educt in _educts:
|
||||||
r.educts.add(educt)
|
r.educts.add(educt)
|
||||||
@ -700,6 +774,7 @@ class Reaction(EnviPathModel, AliasMixin, ScenarioMixin):
|
|||||||
|
|
||||||
class Pathway(EnviPathModel, AliasMixin, ScenarioMixin):
|
class Pathway(EnviPathModel, AliasMixin, ScenarioMixin):
|
||||||
package = models.ForeignKey('epdb.Package', verbose_name='Package', on_delete=models.CASCADE, db_index=True)
|
package = models.ForeignKey('epdb.Package', verbose_name='Package', on_delete=models.CASCADE, db_index=True)
|
||||||
|
setting = models.ForeignKey('epdb.Setting', verbose_name='Setting', on_delete=models.CASCADE, null=True, blank=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def root_nodes(self):
|
def root_nodes(self):
|
||||||
@ -709,6 +784,12 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin):
|
|||||||
def nodes(self):
|
def nodes(self):
|
||||||
return Node.objects.filter(pathway=self)
|
return Node.objects.filter(pathway=self)
|
||||||
|
|
||||||
|
def get_node(self, node_url):
|
||||||
|
for n in self.nodes:
|
||||||
|
if n.url == node_url:
|
||||||
|
return n
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def edges(self):
|
def edges(self):
|
||||||
return Edge.objects.filter(pathway=self)
|
return Edge.objects.filter(pathway=self)
|
||||||
@ -717,6 +798,26 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin):
|
|||||||
def url(self):
|
def url(self):
|
||||||
return '{}/pathway/{}'.format(self.package.url, self.uuid)
|
return '{}/pathway/{}'.format(self.package.url, self.uuid)
|
||||||
|
|
||||||
|
# Mode
|
||||||
|
def is_built(self):
|
||||||
|
return self.kv.get('mode', 'build') == 'predicted'
|
||||||
|
|
||||||
|
def is_predicted(self):
|
||||||
|
return self.kv.get('mode', 'build') == 'predicted'
|
||||||
|
|
||||||
|
def is_predicted(self):
|
||||||
|
return self.kv.get('mode', 'build') == 'predicted'
|
||||||
|
|
||||||
|
# Status
|
||||||
|
def completed(self):
|
||||||
|
return self.kv.get('status', 'completed') == 'completed'
|
||||||
|
|
||||||
|
def running(self):
|
||||||
|
return self.kv.get('status', 'completed') == 'running'
|
||||||
|
|
||||||
|
def failed(self):
|
||||||
|
return self.kv.get('status', 'completed') == 'failed'
|
||||||
|
|
||||||
def d3_json(self):
|
def d3_json(self):
|
||||||
# Ideally it would be something like this but
|
# Ideally it would be something like this but
|
||||||
# to reduce crossing in edges do a DFS
|
# to reduce crossing in edges do a DFS
|
||||||
@ -770,7 +871,11 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin):
|
|||||||
new_link = {
|
new_link = {
|
||||||
'name': link['name'],
|
'name': link['name'],
|
||||||
'id': link['id'],
|
'id': link['id'],
|
||||||
|
'url': link['url'],
|
||||||
|
'image': link['image'],
|
||||||
'reaction': link['reaction'],
|
'reaction': link['reaction'],
|
||||||
|
'reaction_probability': link['reaction_probability'],
|
||||||
|
'scenarios': link['scenarios'],
|
||||||
'source': node_url_to_idx[link['start_node_urls'][0]],
|
'source': node_url_to_idx[link['start_node_urls'][0]],
|
||||||
'target': pseudo_idx
|
'target': pseudo_idx
|
||||||
}
|
}
|
||||||
@ -781,7 +886,11 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin):
|
|||||||
new_link = {
|
new_link = {
|
||||||
'name': link['name'],
|
'name': link['name'],
|
||||||
'id': link['id'],
|
'id': link['id'],
|
||||||
|
'url': link['url'],
|
||||||
|
'image': link['image'],
|
||||||
'reaction': link['reaction'],
|
'reaction': link['reaction'],
|
||||||
|
'reaction_probability': link['reaction_probability'],
|
||||||
|
'scenarios': link['scenarios'],
|
||||||
'source': pseudo_idx,
|
'source': pseudo_idx,
|
||||||
'target': node_url_to_idx[target]
|
'target': node_url_to_idx[target]
|
||||||
}
|
}
|
||||||
@ -797,9 +906,9 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin):
|
|||||||
"completed": "true",
|
"completed": "true",
|
||||||
"description": self.description,
|
"description": self.description,
|
||||||
"id": self.url,
|
"id": self.url,
|
||||||
"isIncremental": False,
|
"isIncremental": self.kv.get('mode') == 'incremental',
|
||||||
"isPredicted": False,
|
"isPredicted": self.kv.get('mode') == 'predicted',
|
||||||
"lastModified": 1447842835894,
|
"lastModified": self.modified.strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
"pathwayName": self.name,
|
"pathwayName": self.name,
|
||||||
"reviewStatus": "reviewed" if self.package.reviewed else 'unreviewed',
|
"reviewStatus": "reviewed" if self.package.reviewed else 'unreviewed',
|
||||||
"scenarios": [],
|
"scenarios": [],
|
||||||
@ -813,18 +922,38 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def create(package, name, description, smiles):
|
def create(package: 'Package', smiles: str, name: Optional[str] = None, description: Optional[str] = None):
|
||||||
pw = Pathway()
|
pw = Pathway()
|
||||||
pw.package = package
|
pw.package = package
|
||||||
pw.name = name
|
|
||||||
pw.description = description
|
|
||||||
pw.save()
|
|
||||||
|
|
||||||
# create root node
|
if name is None:
|
||||||
Node.create(pw, smiles, 0)
|
name = f"Pathway {Pathway.objects.filter(package=package).count() + 1}"
|
||||||
|
|
||||||
|
pw.name = name
|
||||||
|
|
||||||
|
if description is not None:
|
||||||
|
pw.description = description
|
||||||
|
|
||||||
|
pw.save()
|
||||||
|
try:
|
||||||
|
# create root node
|
||||||
|
Node.create(pw, smiles, 0)
|
||||||
|
except ValueError as e:
|
||||||
|
# Node creation failed, most likely due to an invalid smiles
|
||||||
|
# delete this pathway...
|
||||||
|
pw.delete()
|
||||||
|
raise e
|
||||||
|
|
||||||
return pw
|
return pw
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def add_node(self, smiles: str, name: Optional[str] = None, description: Optional[str] = None):
|
||||||
|
return Node.create(self, smiles, 0)
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def add_edge(self, start_nodes: List['Node'], end_nodes: List['Node'], rule: Optional['Rule'] = None,
|
||||||
|
name: Optional[str] = None, description: Optional[str] = None):
|
||||||
|
return Edge.create(self, start_nodes, end_nodes, rule, name=name, description=description)
|
||||||
|
|
||||||
class Node(EnviPathModel, AliasMixin, ScenarioMixin):
|
class Node(EnviPathModel, AliasMixin, ScenarioMixin):
|
||||||
pathway = models.ForeignKey('epdb.Pathway', verbose_name='belongs to', on_delete=models.CASCADE, db_index=True)
|
pathway = models.ForeignKey('epdb.Pathway', verbose_name='belongs to', on_delete=models.CASCADE, db_index=True)
|
||||||
@ -848,14 +977,14 @@ class Node(EnviPathModel, AliasMixin, ScenarioMixin):
|
|||||||
"imageSize": 490, # TODO
|
"imageSize": 490, # TODO
|
||||||
"name": self.default_node_label.name,
|
"name": self.default_node_label.name,
|
||||||
"smiles": self.default_node_label.smiles,
|
"smiles": self.default_node_label.smiles,
|
||||||
|
"scenarios": [{'name': s.name, 'url': s.url} for s in self.scenarios.all()],
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create(pathway, smiles, depth):
|
def create(pathway: 'Pathway', smiles: str, depth: int, name: Optional[str] = None, description: Optional[str] = None):
|
||||||
c = Compound.create(pathway.package, smiles)
|
c = Compound.create(pathway.package, smiles, name=name, description=description)
|
||||||
|
|
||||||
if Node.objects.filter(pathway=pathway, default_node_label=c.default_structure).exists():
|
if Node.objects.filter(pathway=pathway, default_node_label=c.default_structure).exists():
|
||||||
print("found node")
|
|
||||||
return Node.objects.get(pathway=pathway, default_node_label=c.default_structure)
|
return Node.objects.get(pathway=pathway, default_node_label=c.default_structure)
|
||||||
|
|
||||||
n = Node()
|
n = Node()
|
||||||
@ -886,34 +1015,21 @@ class Edge(PolymorphicModel, EnviPathModel, AliasMixin, ScenarioMixin):
|
|||||||
return '{}/edge/{}'.format(self.pathway.url, self.uuid)
|
return '{}/edge/{}'.format(self.pathway.url, self.uuid)
|
||||||
|
|
||||||
def d3_json(self):
|
def d3_json(self):
|
||||||
# {
|
|
||||||
# "ecNumbers": [
|
|
||||||
# {
|
|
||||||
# "ecName": "DDT 2,3-dioxygenase",
|
|
||||||
# "ecNumber": "1.14.12.-"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# "id": "https://envipath.org/package/32de3cf4-e3e6-4168-956e-32fa5ddb0ce1/pathway/3f58e4d4-1c63-4b30-bf31-7ae4b98899fe/edge/ff193e7b-f010-43d4-acb3-45f34d938824",
|
|
||||||
# "idreaction": "https://envipath.org/package/32de3cf4-e3e6-4168-956e-32fa5ddb0ce1/reaction/e11419cd-6b46-470b-8a06-a08d62281734",
|
|
||||||
# "multistep": "false",
|
|
||||||
# "name": "Eawag BBD reaction r0450",
|
|
||||||
# "pseudo": False,
|
|
||||||
# "scenarios": [],
|
|
||||||
# "source": 0,
|
|
||||||
# "target": 4
|
|
||||||
# }
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'name': self.name,
|
'name': self.name,
|
||||||
'id': self.url,
|
'id': self.url,
|
||||||
'reaction': self.edge_label.url if self.edge_label else None,
|
'url': self.url,
|
||||||
|
'image': self.url + '?image=svg',
|
||||||
|
'reaction': {'name': self.edge_label.name, 'url': self.edge_label.url } if self.edge_label else None,
|
||||||
|
'reaction_probability': self.kv.get('probability'),
|
||||||
# TODO
|
# TODO
|
||||||
'start_node_urls': [x.url for x in self.start_nodes.all()],
|
'start_node_urls': [x.url for x in self.start_nodes.all()],
|
||||||
'end_node_urls': [x.url for x in self.end_nodes.all()],
|
'end_node_urls': [x.url for x in self.end_nodes.all()],
|
||||||
|
"scenarios": [{'name': s.name, 'url': s.url} for s in self.scenarios.all()],
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create(pathway, start_nodes, end_nodes, rule: Optional[Rule] = None, name: Optional[str] = None,
|
def create(pathway, start_nodes: List[Node], end_nodes: List[Node], rule: Optional[Rule] = None, name: Optional[str] = None,
|
||||||
description: Optional[str] = None):
|
description: Optional[str] = None):
|
||||||
e = Edge()
|
e = Edge()
|
||||||
e.pathway = pathway
|
e.pathway = pathway
|
||||||
@ -934,13 +1050,17 @@ class Edge(PolymorphicModel, EnviPathModel, AliasMixin, ScenarioMixin):
|
|||||||
r = Reaction.create(pathway.package, name=name, description=description,
|
r = Reaction.create(pathway.package, name=name, description=description,
|
||||||
educts=[n.default_node_label for n in e.start_nodes.all()],
|
educts=[n.default_node_label for n in e.start_nodes.all()],
|
||||||
products=[n.default_node_label for n in e.end_nodes.all()],
|
products=[n.default_node_label for n in e.end_nodes.all()],
|
||||||
rule=rule, multi_step=False
|
rules=rule, multi_step=False
|
||||||
)
|
)
|
||||||
|
|
||||||
e.edge_label = r
|
e.edge_label = r
|
||||||
e.save()
|
e.save()
|
||||||
return e
|
return e
|
||||||
|
|
||||||
|
@property
|
||||||
|
def as_svg(self):
|
||||||
|
return self.edge_label.as_svg if self.edge_label else None
|
||||||
|
|
||||||
|
|
||||||
class EPModel(PolymorphicModel, EnviPathModel):
|
class EPModel(PolymorphicModel, EnviPathModel):
|
||||||
package = models.ForeignKey('epdb.Package', verbose_name='Package', on_delete=models.CASCADE, db_index=True)
|
package = models.ForeignKey('epdb.Package', verbose_name='Package', on_delete=models.CASCADE, db_index=True)
|
||||||
@ -976,6 +1096,9 @@ class MLRelativeReasoning(EPModel):
|
|||||||
|
|
||||||
eval_results = JSONField(null=True, blank=True, default=dict)
|
eval_results = JSONField(null=True, blank=True, default=dict)
|
||||||
|
|
||||||
|
def status(self):
|
||||||
|
return self.PROGRESS_STATUS_CHOICES[self.model_status]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def create(package, name, description, rule_packages, data_packages, eval_packages, threshold):
|
def create(package, name, description, rule_packages, data_packages, eval_packages, threshold):
|
||||||
@ -1201,7 +1324,7 @@ class MLRelativeReasoning(EPModel):
|
|||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
mod = SparseLabelECC(
|
mod = SparseLabelECC(
|
||||||
**s.DEFAULT_MODELS_PARAMS
|
**s.DEFAULT_DT_MODEL_PARAMS
|
||||||
)
|
)
|
||||||
|
|
||||||
mod.fit(X, y)
|
mod.fit(X, y)
|
||||||
@ -1247,7 +1370,7 @@ class MLRelativeReasoning(EPModel):
|
|||||||
y_train, y_test = y[train_index], y[test_index]
|
y_train, y_test = y[train_index], y[test_index]
|
||||||
|
|
||||||
model = SparseLabelECC(
|
model = SparseLabelECC(
|
||||||
**s.DEFAULT_MODELS_PARAMS
|
**s.DEFAULT_DT_MODEL_PARAMS
|
||||||
)
|
)
|
||||||
model.fit(X_train, y_train)
|
model.fit(X_train, y_train)
|
||||||
|
|
||||||
@ -1500,11 +1623,15 @@ class Setting(EnviPathModel):
|
|||||||
max_nodes = models.IntegerField(null=False, blank=False, verbose_name='Setting Max Number of Nodes', default=30)
|
max_nodes = models.IntegerField(null=False, blank=False, verbose_name='Setting Max Number of Nodes', default=30)
|
||||||
|
|
||||||
rule_packages = models.ManyToManyField("Package", verbose_name="Setting Rule Packages",
|
rule_packages = models.ManyToManyField("Package", verbose_name="Setting Rule Packages",
|
||||||
related_name="setting_rule_packages")
|
related_name="setting_rule_packages", blank=True)
|
||||||
model = models.ForeignKey('EPModel', verbose_name='Setting EPModel', on_delete=models.SET_NULL, null=True,
|
model = models.ForeignKey('EPModel', verbose_name='Setting EPModel', on_delete=models.SET_NULL, null=True,
|
||||||
blank=True)
|
blank=True)
|
||||||
model_threshold = models.FloatField(null=True, blank=True, verbose_name='Setting Model Threshold', default=0.25)
|
model_threshold = models.FloatField(null=True, blank=True, verbose_name='Setting Model Threshold', default=0.25)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url(self):
|
||||||
|
return '{}/setting/{}'.format(s.SERVER_URL, self.uuid)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def applicable_rules(self):
|
def applicable_rules(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from celery.signals import worker_process_init
|
from celery.signals import worker_process_init
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
from epdb.models import Pathway, Node, Edge, EPModel, Setting
|
from epdb.models import Pathway, Node, Edge, EPModel, Setting
|
||||||
@ -40,11 +42,40 @@ def evaluate_model(model_pk: int):
|
|||||||
|
|
||||||
|
|
||||||
@shared_task(queue='predict')
|
@shared_task(queue='predict')
|
||||||
def predict(pw_pk: int, pred_setting_pk: int):
|
def predict(pw_pk: int, pred_setting_pk: int, limit: Optional[int] = None, node_pk: Optional[int] = None) -> Pathway:
|
||||||
pw = Pathway.objects.get(id=pw_pk)
|
pw = Pathway.objects.get(id=pw_pk)
|
||||||
setting = Setting.objects.get(id=pred_setting_pk)
|
setting = Setting.objects.get(id=pred_setting_pk)
|
||||||
spw = SPathway(prediction_setting=setting, persist=pw)
|
|
||||||
level = 0
|
pw.kv.update(**{'status': 'running'})
|
||||||
while not spw.done:
|
pw.save()
|
||||||
spw.predict_step(from_depth=level)
|
|
||||||
level += 1
|
try:
|
||||||
|
# regular prediction
|
||||||
|
if limit is not None:
|
||||||
|
spw = SPathway(prediction_setting=setting, persist=pw)
|
||||||
|
level = 0
|
||||||
|
while not spw.done:
|
||||||
|
spw.predict_step(from_depth=level)
|
||||||
|
level += 1
|
||||||
|
|
||||||
|
# break in case we are in incremental model
|
||||||
|
if limit != -1:
|
||||||
|
if level >= limit:
|
||||||
|
break
|
||||||
|
|
||||||
|
elif node_pk is not None:
|
||||||
|
n = Node.objects.get(id=node_pk, pathway=pw)
|
||||||
|
spw = SPathway.from_pathway(pw)
|
||||||
|
spw.predict_step(from_node=n)
|
||||||
|
else:
|
||||||
|
raise ValueError("Neither limit nor node_pk given!")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
pw.kv.update({'status': 'failed'})
|
||||||
|
pw.save()
|
||||||
|
raise e
|
||||||
|
|
||||||
|
pw.kv.update(**{'status': 'completed'})
|
||||||
|
pw.save()
|
||||||
0
epdb/templatetags/__init__.py
Normal file
0
epdb/templatetags/__init__.py
Normal file
7
epdb/templatetags/envipytags.py
Normal file
7
epdb/templatetags/envipytags.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from django import template
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def classname(obj):
|
||||||
|
return obj.__class__.__name__
|
||||||
@ -13,6 +13,8 @@ urlpatterns = [
|
|||||||
# Home
|
# Home
|
||||||
re_path(r'^$', v.index, name='index'),
|
re_path(r'^$', v.index, name='index'),
|
||||||
|
|
||||||
|
# re_path(r'^login', v.login, name='login'),
|
||||||
|
|
||||||
# Top level urls
|
# Top level urls
|
||||||
re_path(r'^package$', v.packages, name='packages'),
|
re_path(r'^package$', v.packages, name='packages'),
|
||||||
re_path(r'^compound$', v.compounds, name='compounds'),
|
re_path(r'^compound$', v.compounds, name='compounds'),
|
||||||
@ -53,11 +55,11 @@ urlpatterns = [
|
|||||||
re_path(rf'^package/(?P<package_uuid>{UUID})/pathway$', v.package_pathways, name='package pathway list'),
|
re_path(rf'^package/(?P<package_uuid>{UUID})/pathway$', v.package_pathways, name='package pathway list'),
|
||||||
re_path(rf'^package/(?P<package_uuid>{UUID})/pathway/(?P<pathway_uuid>{UUID})$', v.package_pathway, name='package pathway detail'),
|
re_path(rf'^package/(?P<package_uuid>{UUID})/pathway/(?P<pathway_uuid>{UUID})$', v.package_pathway, name='package pathway detail'),
|
||||||
# Pathway Nodes
|
# Pathway Nodes
|
||||||
# re_path(rf'^package/(?P<package_uuid>{UUID})/pathway(?P<pathway_uuid>{UUID})/node$', v.package_pathway_nodes, name='package pathway node list'),
|
re_path(rf'^package/(?P<package_uuid>{UUID})/pathway/(?P<pathway_uuid>{UUID})/node$', v.package_pathway_nodes, name='package pathway node list'),
|
||||||
re_path(rf'^package/(?P<package_uuid>{UUID})/pathway/(?P<pathway_uuid>{UUID})/node/(?P<node_uuid>{UUID})$', v.package_pathway_node, name='package pathway node detail'),
|
re_path(rf'^package/(?P<package_uuid>{UUID})/pathway/(?P<pathway_uuid>{UUID})/node/(?P<node_uuid>{UUID})$', v.package_pathway_node, name='package pathway node detail'),
|
||||||
# Pathway Edges
|
# Pathway Edges
|
||||||
# re_path(rf'^package/(?P<package_uuid>{UUID})/pathway(?P<pathway_uuid>{UUID})/edge$', v.package_pathway_edges, name='package pathway edge list'),
|
re_path(rf'^package/(?P<package_uuid>{UUID})/pathway/(?P<pathway_uuid>{UUID})/edge$', v.package_pathway_edges, name='package pathway edge list'),
|
||||||
# re_path(rf'^package/(?P<package_uuid>{UUID})/pathway(?P<pathway_uuid>{UUID})/edge/(?P<edge_uuid>{UUID})$', v.package_pathway_edge, name='package pathway edge detail'),
|
re_path(rf'^package/(?P<package_uuid>{UUID})/pathway/(?P<pathway_uuid>{UUID})/edge/(?P<edge_uuid>{UUID})$', v.package_pathway_edge, name='package pathway edge detail'),
|
||||||
# Scenario
|
# Scenario
|
||||||
re_path(rf'^package/(?P<package_uuid>{UUID})/scenario$', v.package_scenarios, name='package scenario list'),
|
re_path(rf'^package/(?P<package_uuid>{UUID})/scenario$', v.package_scenarios, name='package scenario list'),
|
||||||
re_path(rf'^package/(?P<package_uuid>{UUID})/scenario/(?P<scenario_uuid>{UUID})$', v.package_scenario, name='package scenario detail'),
|
re_path(rf'^package/(?P<package_uuid>{UUID})/scenario/(?P<scenario_uuid>{UUID})$', v.package_scenario, name='package scenario detail'),
|
||||||
|
|||||||
450
epdb/views.py
450
epdb/views.py
@ -1,5 +1,6 @@
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
from functools import wraps
|
||||||
from typing import List, Dict, Any
|
from typing import List, Dict, Any
|
||||||
|
|
||||||
from django.conf import settings as s
|
from django.conf import settings as s
|
||||||
@ -7,29 +8,71 @@ from django.contrib.auth import get_user_model
|
|||||||
from django.http import JsonResponse, HttpResponse, HttpResponseNotAllowed, HttpResponseBadRequest
|
from django.http import JsonResponse, HttpResponse, HttpResponseNotAllowed, HttpResponseBadRequest
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
|
||||||
from utilities.chem import FormatConverter, IndigoUtils
|
from utilities.chem import FormatConverter, IndigoUtils
|
||||||
from .logic import GroupManager, PackageManager, UserManager, SettingManager
|
from .logic import GroupManager, PackageManager, UserManager, SettingManager, SearchManager
|
||||||
from .models import Package, GroupPackagePermission, Group, CompoundStructure, Compound, Reaction, Rule, Pathway, Node, \
|
from .models import Package, GroupPackagePermission, Group, CompoundStructure, Compound, Reaction, Rule, Pathway, Node, \
|
||||||
EPModel, EnviFormer, MLRelativeReasoning, RuleBaseRelativeReasoning, Scenario, SimpleAmbitRule, APIToken, \
|
EPModel, EnviFormer, MLRelativeReasoning, RuleBaseRelativeReasoning, Scenario, SimpleAmbitRule, APIToken, \
|
||||||
UserPackagePermission, Permission, License, User
|
UserPackagePermission, Permission, License, User, Edge
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def log_post_params(request):
|
def log_post_params(request):
|
||||||
for k, v in request.POST.items():
|
if s.DEBUG:
|
||||||
logger.debug(f"{k}\t{v}")
|
for k, v in request.POST.items():
|
||||||
|
logger.debug(f"{k}\t{v}")
|
||||||
|
|
||||||
|
|
||||||
|
def catch_exceptions(view_func):
|
||||||
|
@wraps(view_func)
|
||||||
|
def _wrapped_view(request, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
return view_func(request, *args, **kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
# Optionally return JSON or plain HttpResponse
|
||||||
|
if request.headers.get('Accept') == 'application/json':
|
||||||
|
return JsonResponse(
|
||||||
|
{'error': 'Internal server error. Please try again later.'},
|
||||||
|
status=500
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return render(request, 'errors/error.html', get_base_context(request))
|
||||||
|
return _wrapped_view
|
||||||
|
|
||||||
|
|
||||||
|
def editable(request, user):
|
||||||
|
url = request.build_absolute_uri(request.path)
|
||||||
|
if PackageManager.is_package_url(url):
|
||||||
|
_package = PackageManager.get_package_lp(request.build_absolute_uri())
|
||||||
|
return PackageManager.writable(user, _package)
|
||||||
|
elif GroupManager.is_group_url(url):
|
||||||
|
_group = GroupManager.get_group_lp(request.build_absolute_uri())
|
||||||
|
return GroupManager.writable(user, _group)
|
||||||
|
elif UserManager.is_user_url(url):
|
||||||
|
_user = UserManager.get_user_lp(request.build_absolute_uri())
|
||||||
|
return UserManager.writable(user, _user)
|
||||||
|
elif url in [s.SERVER_URL, f"{s.SERVER_URL}/", f"{s.SERVER_URL}/package", f"{s.SERVER_URL}/user",
|
||||||
|
f"{s.SERVER_URL}/group", f"{s.SERVER_URL}/search"]:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print(f"Unknown url: {url}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_base_context(request) -> Dict[str, Any]:
|
def get_base_context(request) -> Dict[str, Any]:
|
||||||
current_user = _anonymous_or_real(request)
|
current_user = _anonymous_or_real(request)
|
||||||
|
|
||||||
|
can_edit = editable(request, current_user)
|
||||||
|
|
||||||
ctx = {
|
ctx = {
|
||||||
'title': 'enviPath',
|
'title': 'enviPath',
|
||||||
'meta': {
|
'meta': {
|
||||||
'version': '0.0.1',
|
'version': '0.0.1',
|
||||||
'server_url': s.SERVER_URL,
|
'server_url': s.SERVER_URL,
|
||||||
'user': current_user,
|
'user': current_user,
|
||||||
|
'can_edit': can_edit,
|
||||||
'readable_packages': PackageManager.get_all_readable_packages(current_user, include_reviewed=True),
|
'readable_packages': PackageManager.get_all_readable_packages(current_user, include_reviewed=True),
|
||||||
'writeable_packages': PackageManager.get_all_writeable_packages(current_user),
|
'writeable_packages': PackageManager.get_all_writeable_packages(current_user),
|
||||||
'available_groups': GroupManager.get_groups(current_user),
|
'available_groups': GroupManager.get_groups(current_user),
|
||||||
@ -65,8 +108,9 @@ def breadcrumbs(first_level_object=None, second_level_namespace=None, second_lev
|
|||||||
return bread
|
return bread
|
||||||
|
|
||||||
|
|
||||||
|
# @catch_exceptions
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
current_user = _anonymous_or_real(request)
|
|
||||||
context = get_base_context(request)
|
context = get_base_context(request)
|
||||||
context['title'] = 'enviPath - Home'
|
context['title'] = 'enviPath - Home'
|
||||||
context['meta']['current_package'] = context['meta']['user'].default_package
|
context['meta']['current_package'] = context['meta']['user'].default_package
|
||||||
@ -77,6 +121,16 @@ def index(request):
|
|||||||
return render(request, 'index/index.html', context)
|
return render(request, 'index/index.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
# def login(request):
|
||||||
|
# current_user = _anonymous_or_real(request)
|
||||||
|
# if request.method == 'GET':
|
||||||
|
# context = get_base_context(request)
|
||||||
|
# context['title'] = 'enviPath'
|
||||||
|
# return render(request, 'login.html', context)
|
||||||
|
# else:
|
||||||
|
# return HttpResponseBadRequest()
|
||||||
|
#
|
||||||
|
# @login_required(login_url='/login')
|
||||||
def packages(request):
|
def packages(request):
|
||||||
current_user = _anonymous_or_real(request)
|
current_user = _anonymous_or_real(request)
|
||||||
|
|
||||||
@ -86,9 +140,10 @@ def packages(request):
|
|||||||
|
|
||||||
context['object_type'] = 'package'
|
context['object_type'] = 'package'
|
||||||
context['meta']['current_package'] = context['meta']['user'].default_package
|
context['meta']['current_package'] = context['meta']['user'].default_package
|
||||||
|
context['meta']['can_edit'] = True
|
||||||
|
|
||||||
reviewed_package_qs = Package.objects.filter(reviewed=True)
|
reviewed_package_qs = Package.objects.filter(reviewed=True).order_by('created')
|
||||||
unreviewed_package_qs = PackageManager.get_all_readable_packages(current_user)
|
unreviewed_package_qs = PackageManager.get_all_readable_packages(current_user).order_by('name')
|
||||||
|
|
||||||
context['reviewed_objects'] = reviewed_package_qs
|
context['reviewed_objects'] = reviewed_package_qs
|
||||||
context['unreviewed_objects'] = unreviewed_package_qs
|
context['unreviewed_objects'] = unreviewed_package_qs
|
||||||
@ -103,7 +158,6 @@ def packages(request):
|
|||||||
else:
|
else:
|
||||||
package_name = request.POST.get('package-name')
|
package_name = request.POST.get('package-name')
|
||||||
package_description = request.POST.get('package-description', s.DEFAULT_VALUES['description'])
|
package_description = request.POST.get('package-description', s.DEFAULT_VALUES['description'])
|
||||||
# group = GroupManager.get_group_by_url(request.user, request.POST.get('package-group'))
|
|
||||||
|
|
||||||
created_package = PackageManager.create_package(current_user, package_name, package_description)
|
created_package = PackageManager.create_package(current_user, package_name, package_description)
|
||||||
|
|
||||||
@ -342,7 +396,23 @@ def models(request):
|
|||||||
|
|
||||||
|
|
||||||
def search(request):
|
def search(request):
|
||||||
|
current_user = _anonymous_or_real(request)
|
||||||
|
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
|
package_urls = request.GET.getlist('packages')
|
||||||
|
searchterm = request.GET.get('search')
|
||||||
|
mode = request.GET.get('mode')
|
||||||
|
|
||||||
|
# add HTTP_ACCEPT check to differentiate between index and ajax call
|
||||||
|
if 'application/json' in request.META.get('HTTP_ACCEPT') and all([searchterm, mode]):
|
||||||
|
if package_urls:
|
||||||
|
packages = [PackageManager.get_package_by_url(current_user, p) for p in package_urls]
|
||||||
|
else:
|
||||||
|
packages = PackageManager.get_reviewed_packages()
|
||||||
|
|
||||||
|
search_result = SearchManager.search(packages, searchterm, mode)
|
||||||
|
return JsonResponse(search_result, safe=False)
|
||||||
|
|
||||||
context = get_base_context(request)
|
context = get_base_context(request)
|
||||||
context['title'] = 'enviPath - Search'
|
context['title'] = 'enviPath - Search'
|
||||||
|
|
||||||
@ -352,13 +422,21 @@ def search(request):
|
|||||||
{'Home': s.SERVER_URL},
|
{'Home': s.SERVER_URL},
|
||||||
{'Search': s.SERVER_URL + '/search'},
|
{'Search': s.SERVER_URL + '/search'},
|
||||||
]
|
]
|
||||||
# TODO perm
|
|
||||||
reviewed_package_qs = Package.objects.filter(reviewed=True)
|
reviewed_package_qs = PackageManager.get_reviewed_packages()
|
||||||
unreviewed_package_qs = Package.objects.filter(reviewed=False)
|
unreviewed_package_qs = PackageManager.get_all_readable_packages(current_user)
|
||||||
|
|
||||||
context['reviewed_objects'] = reviewed_package_qs
|
context['reviewed_objects'] = reviewed_package_qs
|
||||||
context['unreviewed_objects'] = unreviewed_package_qs
|
context['unreviewed_objects'] = unreviewed_package_qs
|
||||||
|
|
||||||
|
if all([searchterm, mode]):
|
||||||
|
if package_urls:
|
||||||
|
packages = [PackageManager.get_package_by_url(current_user, p) for p in package_urls]
|
||||||
|
else:
|
||||||
|
packages = PackageManager.get_reviewed_packages()
|
||||||
|
|
||||||
|
context['search_result'] = SearchManager.search(packages, searchterm, mode)
|
||||||
|
|
||||||
return render(request, 'search.html', context)
|
return render(request, 'search.html', context)
|
||||||
|
|
||||||
|
|
||||||
@ -487,7 +565,7 @@ def package_model(request, package_uuid, model_uuid):
|
|||||||
|
|
||||||
return render(request, 'objects/model.html', context)
|
return render(request, 'objects/model.html', context)
|
||||||
|
|
||||||
if request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
if hidden := request.POST.get('hidden', None):
|
if hidden := request.POST.get('hidden', None):
|
||||||
if hidden == 'delete-model':
|
if hidden == 'delete-model':
|
||||||
current_model.delete()
|
current_model.delete()
|
||||||
@ -496,21 +574,9 @@ def package_model(request, package_uuid, model_uuid):
|
|||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
else:
|
else:
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
#
|
|
||||||
# new_compound_name = request.POST.get('compound-name')
|
else:
|
||||||
# new_compound_description = request.POST.get('compound-description')
|
return HttpResponseBadRequest()
|
||||||
#
|
|
||||||
# if new_compound_name:
|
|
||||||
# current_compound.name = new_compound_name
|
|
||||||
#
|
|
||||||
# if new_compound_description:
|
|
||||||
# current_compound.description = new_compound_description
|
|
||||||
#
|
|
||||||
# if any([new_compound_name, new_compound_description]):
|
|
||||||
# current_compound.save()
|
|
||||||
# return redirect(current_compound.url)
|
|
||||||
# else:
|
|
||||||
# return HttpResponseBadRequest()
|
|
||||||
|
|
||||||
|
|
||||||
def package(request, package_uuid):
|
def package(request, package_uuid):
|
||||||
@ -803,8 +869,7 @@ def package_rules(request, package_uuid):
|
|||||||
|
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
|
|
||||||
for k, v in request.POST.items():
|
log_post_params(request)
|
||||||
print(k, v)
|
|
||||||
|
|
||||||
# Generic params
|
# Generic params
|
||||||
rule_name = request.POST.get('rule-name')
|
rule_name = request.POST.get('rule-name')
|
||||||
@ -817,8 +882,8 @@ def package_rules(request, package_uuid):
|
|||||||
# Obtain parameters as required by rule type
|
# Obtain parameters as required by rule type
|
||||||
if rule_type == 'SimpleAmbitRule':
|
if rule_type == 'SimpleAmbitRule':
|
||||||
params['smirks'] = request.POST.get('rule-smirks')
|
params['smirks'] = request.POST.get('rule-smirks')
|
||||||
params['reactant_smarts'] = request.POST.get('rule-reactant-smarts')
|
params['reactant_filter_smarts'] = request.POST.get('rule-reactant-smarts')
|
||||||
params['product_smarts'] = request.POST.get('rule-product-smarts')
|
params['product_filter_smarts'] = request.POST.get('rule-product-smarts')
|
||||||
elif rule_type == 'SimpleRDKitRule':
|
elif rule_type == 'SimpleRDKitRule':
|
||||||
params['reaction_smarts'] = request.POST.get('rule-reaction-smarts')
|
params['reaction_smarts'] = request.POST.get('rule-reaction-smarts')
|
||||||
elif rule_type == 'ParallelRule':
|
elif rule_type == 'ParallelRule':
|
||||||
@ -828,7 +893,7 @@ def package_rules(request, package_uuid):
|
|||||||
else:
|
else:
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
r = Rule.create(current_package, rule_type, name=rule_name, description=rule_description, **params)
|
r = Rule.create(rule_type=rule_type, package=current_package, name=rule_name, description=rule_description, **params)
|
||||||
return redirect(r.url)
|
return redirect(r.url)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -854,7 +919,7 @@ def package_rule(request, package_uuid, rule_uuid):
|
|||||||
else: # isinstance(current_rule, ParallelRule) or isinstance(current_rule, SequentialRule):
|
else: # isinstance(current_rule, ParallelRule) or isinstance(current_rule, SequentialRule):
|
||||||
return render(request, 'objects/composite_rule.html', context)
|
return render(request, 'objects/composite_rule.html', context)
|
||||||
|
|
||||||
if request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
if hidden := request.POST.get('hidden', None):
|
if hidden := request.POST.get('hidden', None):
|
||||||
if hidden == 'delete-rule':
|
if hidden == 'delete-rule':
|
||||||
current_rule.delete()
|
current_rule.delete()
|
||||||
@ -862,8 +927,23 @@ def package_rule(request, package_uuid, rule_uuid):
|
|||||||
else:
|
else:
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
# TODO update!
|
rule_name = request.POST.get('rule-name', '').strip()
|
||||||
|
rule_description = request.POST.get('rule-description', '').strip()
|
||||||
|
|
||||||
|
if rule_name:
|
||||||
|
current_rule.name = rule_name
|
||||||
|
|
||||||
|
if rule_description:
|
||||||
|
current_rule.description = rule_description
|
||||||
|
|
||||||
|
if any([rule_name, rule_description]):
|
||||||
|
current_rule.save()
|
||||||
|
return redirect(current_rule.url)
|
||||||
|
else:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
else:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
# https://envipath.org/package/<id>/reaction
|
# https://envipath.org/package/<id>/reaction
|
||||||
def package_reactions(request, package_uuid):
|
def package_reactions(request, package_uuid):
|
||||||
@ -912,6 +992,8 @@ def package_reactions(request, package_uuid):
|
|||||||
|
|
||||||
return redirect(r.url)
|
return redirect(r.url)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
# https://envipath.org/package/<id>/reaction/<id>
|
# https://envipath.org/package/<id>/reaction/<id>
|
||||||
def package_reaction(request, package_uuid, reaction_uuid):
|
def package_reaction(request, package_uuid, reaction_uuid):
|
||||||
@ -931,7 +1013,7 @@ def package_reaction(request, package_uuid, reaction_uuid):
|
|||||||
|
|
||||||
return render(request, 'objects/reaction.html', context)
|
return render(request, 'objects/reaction.html', context)
|
||||||
|
|
||||||
if request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
if hidden := request.POST.get('hidden', None):
|
if hidden := request.POST.get('hidden', None):
|
||||||
if hidden == 'delete-reaction':
|
if hidden == 'delete-reaction':
|
||||||
current_reaction.delete()
|
current_reaction.delete()
|
||||||
@ -954,6 +1036,8 @@ def package_reaction(request, package_uuid, reaction_uuid):
|
|||||||
else:
|
else:
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
else:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
# https://envipath.org/package/<id>/pathway
|
# https://envipath.org/package/<id>/pathway
|
||||||
def package_pathways(request, package_uuid):
|
def package_pathways(request, package_uuid):
|
||||||
@ -989,7 +1073,7 @@ def package_pathways(request, package_uuid):
|
|||||||
|
|
||||||
return render(request, 'collections/objects_list.html', context)
|
return render(request, 'collections/objects_list.html', context)
|
||||||
|
|
||||||
if request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
|
|
||||||
log_post_params(request)
|
log_post_params(request)
|
||||||
|
|
||||||
@ -1002,15 +1086,34 @@ def package_pathways(request, package_uuid):
|
|||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
stand_smiles = FormatConverter.standardize(smiles)
|
stand_smiles = FormatConverter.standardize(smiles)
|
||||||
pw = Pathway.create(current_package, name, description, stand_smiles)
|
|
||||||
|
|
||||||
if pw_mode != 'build':
|
if pw_mode not in ['predict', 'build', 'incremental']:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
pw = Pathway.create(current_package, name, description, stand_smiles)
|
||||||
|
# set mode
|
||||||
|
pw.kv.update({'mode': pw_mode})
|
||||||
|
pw.save()
|
||||||
|
|
||||||
|
if pw_mode == 'predict' or pw_mode == 'incremental':
|
||||||
|
# unlimited pred (will be handled by setting)
|
||||||
|
limit = -1
|
||||||
|
|
||||||
|
# For incremental predict first level and return
|
||||||
|
if pw_mode == 'incremental':
|
||||||
|
limit = 1
|
||||||
|
|
||||||
pred_setting = current_user.prediction_settings()
|
pred_setting = current_user.prediction_settings()
|
||||||
|
pw.setting = pred_setting
|
||||||
|
pw.save()
|
||||||
|
|
||||||
from .tasks import predict
|
from .tasks import predict
|
||||||
predict.delay(pw.pk, pred_setting.pk)
|
predict.delay(pw.pk, pred_setting.pk, limit=limit)
|
||||||
|
|
||||||
return redirect(pw.url)
|
return redirect(pw.url)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
# https://envipath.org/package/<id>/pathway/<id>
|
# https://envipath.org/package/<id>/pathway/<id>
|
||||||
def package_pathway(request, package_uuid, pathway_uuid):
|
def package_pathway(request, package_uuid, pathway_uuid):
|
||||||
@ -1043,7 +1146,7 @@ def package_pathway(request, package_uuid, pathway_uuid):
|
|||||||
return render(request, 'objects/pathway.html', context)
|
return render(request, 'objects/pathway.html', context)
|
||||||
# return render(request, 'pathway_playground2.html', context)
|
# return render(request, 'pathway_playground2.html', context)
|
||||||
|
|
||||||
if request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
if hidden := request.POST.get('hidden', None):
|
if hidden := request.POST.get('hidden', None):
|
||||||
if hidden == 'delete-pathway':
|
if hidden == 'delete-pathway':
|
||||||
current_pathway.delete()
|
current_pathway.delete()
|
||||||
@ -1051,30 +1154,92 @@ def package_pathway(request, package_uuid, pathway_uuid):
|
|||||||
else:
|
else:
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
pathway_name = request.POST.get('pathway-name')
|
||||||
|
pathway_description = request.POST.get('pathway-description')
|
||||||
|
|
||||||
#
|
if any([pathway_name, pathway_description]):
|
||||||
#
|
if pathway_name is not None and pathway_name.strip() != '':
|
||||||
#
|
pathway_name = pathway_name.strip()
|
||||||
# def package_relative_reasonings(request, package_id):
|
|
||||||
# if request.method == 'GET':
|
current_pathway.name = pathway_name
|
||||||
# pass
|
|
||||||
#
|
if pathway_description is not None and pathway_description.strip() != '':
|
||||||
#
|
pathway_description = pathway_description.strip()
|
||||||
# def package_relative_reasoning(request, package_id, relative_reasoning_id):
|
|
||||||
# current_user = _anonymous_or_real(request)
|
current_pathway.description = pathway_description
|
||||||
#
|
|
||||||
# if request.method == 'GET':
|
current_pathway.save()
|
||||||
# pass
|
return redirect(current_pathway.url)
|
||||||
# elif request.method == 'POST':
|
|
||||||
# pass
|
node_url = request.POST.get('node')
|
||||||
#
|
|
||||||
# #
|
if node_url:
|
||||||
# #
|
n = current_pathway.get_node(node_url)
|
||||||
# # # https://envipath.org/package/<id>/pathway/<id>/node
|
|
||||||
# # def package_pathway_nodes(request, package_id, pathway_id):
|
from .tasks import predict
|
||||||
# # pass
|
# Dont delay?
|
||||||
# #
|
predict(current_pathway.pk, current_pathway.setting.pk, node_pk=n.pk)
|
||||||
# #
|
return JsonResponse({'success': current_pathway.url})
|
||||||
|
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
else:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
# https://envipath.org/package/<id>/pathway/<id>/node
|
||||||
|
def package_pathway_nodes(request, package_uuid, pathway_uuid):
|
||||||
|
current_user = _anonymous_or_real(request)
|
||||||
|
current_package = PackageManager.get_package_by_id(current_user, package_uuid)
|
||||||
|
current_pathway = Pathway.objects.get(package=current_package, uuid=pathway_uuid)
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
context = get_base_context(request)
|
||||||
|
context['title'] = f'enviPath - {current_package.name} - {current_pathway.name} - Nodes'
|
||||||
|
|
||||||
|
context['meta']['current_package'] = current_package
|
||||||
|
context['object_type'] = 'node'
|
||||||
|
context['breadcrumbs'] = [
|
||||||
|
{'Home': s.SERVER_URL},
|
||||||
|
{'Package': s.SERVER_URL + '/package'},
|
||||||
|
{current_package.name: current_package.url},
|
||||||
|
{'Pathway': current_package.url + '/pathway'},
|
||||||
|
{current_pathway.name: current_pathway.url},
|
||||||
|
{'Node': current_pathway.url + '/node'},
|
||||||
|
]
|
||||||
|
|
||||||
|
reviewed_node_qs = Node.objects.none()
|
||||||
|
unreviewed_node_qs = Node.objects.none()
|
||||||
|
|
||||||
|
if current_package.reviewed:
|
||||||
|
reviewed_node_qs = Node.objects.filter(pathway=current_pathway).order_by('name')
|
||||||
|
else:
|
||||||
|
unreviewed_node_qs = Node.objects.filter(pathway=current_pathway).order_by('name')
|
||||||
|
|
||||||
|
if request.GET.get('all'):
|
||||||
|
return JsonResponse({
|
||||||
|
"objects": [
|
||||||
|
{"name": pw.name, "url": pw.url, "reviewed": current_package.reviewed}
|
||||||
|
for pw in (reviewed_node_qs if current_package.reviewed else unreviewed_node_qs)
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
context['reviewed_objects'] = reviewed_node_qs
|
||||||
|
context['unreviewed_objects'] = unreviewed_node_qs
|
||||||
|
|
||||||
|
return render(request, 'collections/objects_list.html', context)
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
current_pathway.add_node(node_smiles, name=node_name, description=node_description)
|
||||||
|
|
||||||
|
return redirect(current_pathway.url)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
|
||||||
# https://envipath.org/package/<id>/pathway/<id>/node/<id>
|
# https://envipath.org/package/<id>/pathway/<id>/node/<id>
|
||||||
@ -1091,19 +1256,129 @@ def package_pathway_node(request, package_uuid, pathway_uuid, node_uuid):
|
|||||||
svg_data = current_node.as_svg
|
svg_data = current_node.as_svg
|
||||||
return HttpResponse(svg_data, content_type="image/svg+xml")
|
return HttpResponse(svg_data, content_type="image/svg+xml")
|
||||||
|
|
||||||
|
context = get_base_context(request)
|
||||||
|
context['title'] = f'enviPath - {current_package.name} - {current_pathway.name}'
|
||||||
|
|
||||||
|
context['meta']['current_package'] = current_package
|
||||||
|
context['object_type'] = 'pathway'
|
||||||
|
|
||||||
|
context['breadcrumbs'] = [
|
||||||
|
{'Home': s.SERVER_URL},
|
||||||
|
{'Package': s.SERVER_URL + '/package'},
|
||||||
|
{current_package.name: current_package.url},
|
||||||
|
{'Pathway': current_package.url + '/pathway'},
|
||||||
|
{current_pathway.name: current_pathway.url},
|
||||||
|
{'Node': current_pathway.url + '/node'},
|
||||||
|
{current_node.name: current_node.url},
|
||||||
|
]
|
||||||
|
|
||||||
|
context['node'] = current_node
|
||||||
|
|
||||||
|
return render(request, 'objects/node.html', context)
|
||||||
|
|
||||||
|
elif request.method == 'POST':
|
||||||
|
if s.DEBUG:
|
||||||
|
for k, v in request.POST.items():
|
||||||
|
print(k, v)
|
||||||
|
|
||||||
|
if hidden := request.POST.get('hidden', None):
|
||||||
|
if hidden == 'delete-node':
|
||||||
|
current_node.delete()
|
||||||
|
return redirect(current_pathway.url)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
|
||||||
|
# https://envipath.org/package/<id>/pathway/<id>/edge
|
||||||
|
def package_pathway_edges(request, package_uuid, pathway_uuid):
|
||||||
|
current_user = _anonymous_or_real(request)
|
||||||
|
current_package = PackageManager.get_package_by_id(current_user, package_uuid)
|
||||||
|
current_pathway = Pathway.objects.get(package=current_package, uuid=pathway_uuid)
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
context = get_base_context(request)
|
||||||
|
context['title'] = f'enviPath - {current_package.name} - {current_pathway.name} - Edges'
|
||||||
|
|
||||||
|
context['meta']['current_package'] = current_package
|
||||||
|
context['object_type'] = 'edge'
|
||||||
|
context['breadcrumbs'] = [
|
||||||
|
{'Home': s.SERVER_URL},
|
||||||
|
{'Package': s.SERVER_URL + '/package'},
|
||||||
|
{current_package.name: current_package.url},
|
||||||
|
{'Pathway': current_package.url + '/pathway'},
|
||||||
|
{current_pathway.name: current_pathway.url},
|
||||||
|
{'Edge': current_pathway.url + '/edge'},
|
||||||
|
]
|
||||||
|
|
||||||
|
reviewed_edge_qs = Edge.objects.none()
|
||||||
|
unreviewed_edge_qs = Edge.objects.none()
|
||||||
|
|
||||||
|
if current_package.reviewed:
|
||||||
|
reviewed_edge_qs = Edge.objects.filter(pathway=current_pathway).order_by('name')
|
||||||
|
else:
|
||||||
|
unreviewed_edge_qs = Edge.objects.filter(pathway=current_pathway).order_by('name')
|
||||||
|
|
||||||
|
if request.GET.get('all'):
|
||||||
|
return JsonResponse({
|
||||||
|
"objects": [
|
||||||
|
{"name": pw.name, "url": pw.url, "reviewed": current_package.reviewed}
|
||||||
|
for pw in (reviewed_edge_qs if current_package.reviewed else unreviewed_edge_qs)
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
context['reviewed_objects'] = reviewed_edge_qs
|
||||||
|
context['unreviewed_objects'] = unreviewed_edge_qs
|
||||||
|
|
||||||
|
return render(request, 'collections/objects_list.html', context)
|
||||||
|
|
||||||
|
elif request.method == 'POST':
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
substrate_nodes = [current_pathway.get_node(url) for url in edge_substrates]
|
||||||
|
product_nodes = [current_pathway.get_node(url) for url in edge_products]
|
||||||
|
|
||||||
|
# TODO in the future consider Rules here?
|
||||||
|
current_pathway.add_edge(substrate_nodes, product_nodes, name=edge_name, description=edge_description)
|
||||||
|
|
||||||
|
return redirect(current_pathway.url)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
|
||||||
|
# https://envipath.org/package/<id>/pathway/<id>/edge/<id>
|
||||||
|
def package_pathway_edge(request, package_uuid, pathway_uuid, edge_uuid):
|
||||||
|
current_user = _anonymous_or_real(request)
|
||||||
|
current_package = PackageManager.get_package_by_id(current_user, package_uuid)
|
||||||
|
current_pathway = Pathway.objects.get(package=current_package, uuid=pathway_uuid)
|
||||||
|
current_edge = Edge.objects.get(pathway=current_pathway, uuid=edge_uuid)
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
is_image_request = request.GET.get('image')
|
||||||
|
if is_image_request:
|
||||||
|
if is_image_request == 'svg':
|
||||||
|
svg_data = current_edge.as_svg
|
||||||
|
return HttpResponse(svg_data, content_type="image/svg+xml")
|
||||||
|
|
||||||
|
elif request.method == 'POST':
|
||||||
|
if s.DEBUG:
|
||||||
|
for k, v in request.POST.items():
|
||||||
|
print(k, v)
|
||||||
|
|
||||||
|
if hidden := request.POST.get('hidden', None):
|
||||||
|
if hidden == 'delete-edge':
|
||||||
|
current_edge.delete()
|
||||||
|
return redirect(current_pathway.url)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# # # https://envipath.org/package/<id>/pathway/<id>/edge
|
|
||||||
# # def package_pathway_edges(request, package_id, pathway_id):
|
|
||||||
# # pass
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# # # https://envipath.org/package/<id>/pathway/<id>/edge/<id>
|
|
||||||
# # def package_pathway_edge(request, package_id, pathway_id, edge_id):
|
|
||||||
# # pass
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# https://envipath.org/package/<id>/scenario
|
# https://envipath.org/package/<id>/scenario
|
||||||
def package_scenarios(request, package_uuid):
|
def package_scenarios(request, package_uuid):
|
||||||
current_user = _anonymous_or_real(request)
|
current_user = _anonymous_or_real(request)
|
||||||
@ -1158,11 +1433,6 @@ def package_scenario(request, package_uuid, scenario_uuid):
|
|||||||
return render(request, 'objects/scenario.html', context)
|
return render(request, 'objects/scenario.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### END UNTESTED
|
|
||||||
|
|
||||||
|
|
||||||
##############
|
##############
|
||||||
# User/Group #
|
# User/Group #
|
||||||
##############
|
##############
|
||||||
@ -1201,7 +1471,7 @@ def users(request):
|
|||||||
return render(request, 'errors/user_account_inactive.html', status=403)
|
return render(request, 'errors/user_account_inactive.html', status=403)
|
||||||
|
|
||||||
email = temp_user.email
|
email = temp_user.email
|
||||||
except get_user_model().DoesNotExists:
|
except get_user_model().DoesNotExist:
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
user = authenticate(username=email, password=password)
|
user = authenticate(username=email, password=password)
|
||||||
@ -1289,6 +1559,18 @@ def user(request, user_uuid):
|
|||||||
logout(request)
|
logout(request)
|
||||||
return redirect(s.SERVER_URL)
|
return redirect(s.SERVER_URL)
|
||||||
|
|
||||||
|
default_package = request.POST.get('default-package')
|
||||||
|
default_group = request.POST.get('default-group')
|
||||||
|
default_prediction_setting = request.POST.get('default-prediction-setting')
|
||||||
|
|
||||||
|
if any([default_package, default_group, default_prediction_setting]):
|
||||||
|
current_user.default_package = PackageManager.get_package_by_url(current_user, default_package)
|
||||||
|
current_user.default_group = GroupManager.get_group_by_url(current_user, default_group)
|
||||||
|
current_user.default_setting = SettingManager.get_setting_by_url(current_user, default_prediction_setting)
|
||||||
|
current_user.save()
|
||||||
|
|
||||||
|
return redirect(current_user.url)
|
||||||
|
|
||||||
prediction_model_pk = request.POST.get('model')
|
prediction_model_pk = request.POST.get('model')
|
||||||
prediction_threshold = request.POST.get('threshold')
|
prediction_threshold = request.POST.get('threshold')
|
||||||
prediction_max_nodes = request.POST.get('max_nodes')
|
prediction_max_nodes = request.POST.get('max_nodes')
|
||||||
@ -1509,3 +1791,9 @@ def layout(request):
|
|||||||
def depict(request):
|
def depict(request):
|
||||||
if smiles := request.GET.get('smiles'):
|
if smiles := request.GET.get('smiles'):
|
||||||
return HttpResponse(IndigoUtils.mol_to_svg(smiles), content_type='image/svg+xml')
|
return HttpResponse(IndigoUtils.mol_to_svg(smiles), content_type='image/svg+xml')
|
||||||
|
|
||||||
|
elif smirks := request.GET.get('smirks'):
|
||||||
|
query_smirks = request.GET.get('is_query_smirks', False) == 'true'
|
||||||
|
return HttpResponse(IndigoUtils.smirks_to_svg(smirks, query_smirks), content_type='image/svg+xml')
|
||||||
|
else:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|||||||
BIN
static/images/enviPy-screenshot.png
Normal file
BIN
static/images/enviPy-screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 91 KiB |
@ -507,28 +507,6 @@ function makeAccordionPanel(accordionId, panelName, panelContent, collapsed, id)
|
|||||||
+ "</div>";
|
+ "</div>";
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeSearchList(divToAppend, jsonob) {
|
|
||||||
|
|
||||||
if(jsonob.status){
|
|
||||||
$(divToAppend).append('<div class="alert alert-danger" role="alert"><p>'+"No results..."+'</p></div>');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var content = makeAccordionHead("searchAccordion", "Results","");
|
|
||||||
|
|
||||||
for ( var type in jsonob){
|
|
||||||
var obj = jsonob[type];
|
|
||||||
var objs = "";
|
|
||||||
for ( var x in obj) {
|
|
||||||
objs += "<a class='list-group-item' href=\"" + obj[x].id + "\">"
|
|
||||||
+ obj[x].name + "</a>";
|
|
||||||
}
|
|
||||||
content += makeAccordionPanel("searchAccordion", type, objs, true);
|
|
||||||
}
|
|
||||||
$(divToAppend).append(content);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function fillPRCurve(modelUri, onclick){
|
function fillPRCurve(modelUri, onclick){
|
||||||
if (modelUri == '') {
|
if (modelUri == '') {
|
||||||
return;
|
return;
|
||||||
|
|||||||
153
static/js/pw.js
153
static/js/pw.js
@ -1,5 +1,18 @@
|
|||||||
console.log("loaded")
|
console.log("loaded")
|
||||||
|
|
||||||
|
|
||||||
|
function predictFromNode(url) {
|
||||||
|
$.post("", {node: url})
|
||||||
|
.done(function (data) {
|
||||||
|
console.log("Success:", data);
|
||||||
|
window.location.href = data.success;
|
||||||
|
})
|
||||||
|
.fail(function (xhr, status, error) {
|
||||||
|
console.error("Error:", xhr.status, xhr.responseText);
|
||||||
|
// show user-friendly message or log error
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// data = {{ pathway.d3_json | safe }};
|
// data = {{ pathway.d3_json | safe }};
|
||||||
// elem = 'vizdiv'
|
// elem = 'vizdiv'
|
||||||
function draw(pathway, elem) {
|
function draw(pathway, elem) {
|
||||||
@ -48,7 +61,7 @@ function draw(pathway, elem) {
|
|||||||
const avgY = d3.mean(childNodes, d => d.y);
|
const avgY = d3.mean(childNodes, d => d.y);
|
||||||
n.fx = avgX;
|
n.fx = avgX;
|
||||||
// keep level as is
|
// keep level as is
|
||||||
n.fy = n.y;
|
n.fy = n.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -77,8 +90,100 @@ function draw(pathway, elem) {
|
|||||||
d3.select(t).select("circle").classed("highlighted", !d3.select(t).select("circle").classed("highlighted"));
|
d3.select(t).select("circle").classed("highlighted", !d3.select(t).select("circle").classed("highlighted"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait one second before showing popup
|
||||||
|
var popupWaitBeforeShow = 1000;
|
||||||
|
// Keep Popup at least for one second
|
||||||
|
var popushowAtLeast = 1000;
|
||||||
|
|
||||||
const tooltip = d3.select("#tooltip");
|
function pop_show_e(element) {
|
||||||
|
var e = element;
|
||||||
|
setTimeout(function () {
|
||||||
|
if ($(e).is(':hover')) { // if element is still hovered
|
||||||
|
$(e).popover("show");
|
||||||
|
|
||||||
|
// workaround to set fixed positions
|
||||||
|
pop = $(e).attr("aria-describedby")
|
||||||
|
h = $('#' + pop).height();
|
||||||
|
$('#' + pop).attr("style", `position: fixed; top: ${clientY - (h / 2.0)}px; left: ${clientX + 10}px; margin: 0px; max-width: 1000px; display: block;`)
|
||||||
|
setTimeout(function () {
|
||||||
|
var close = setInterval(function () {
|
||||||
|
if (!$(".popover:hover").length // mouse outside popover
|
||||||
|
&& !$(e).is(':hover')) { // mouse outside element
|
||||||
|
$(e).popover('hide');
|
||||||
|
clearInterval(close);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}, popushowAtLeast);
|
||||||
|
}
|
||||||
|
}, popupWaitBeforeShow);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pop_add(objects, title, contentFunction) {
|
||||||
|
objects.attr("id", "pop")
|
||||||
|
.attr("data-container", "body")
|
||||||
|
.attr("data-toggle", "popover")
|
||||||
|
.attr("data-placement", "right")
|
||||||
|
.attr("title", title);
|
||||||
|
|
||||||
|
objects.each(function (d, i) {
|
||||||
|
options = {trigger: "manual", html: true, animation: false};
|
||||||
|
this_ = this;
|
||||||
|
var p = $(this).popover(options).on("mouseenter", function () {
|
||||||
|
pop_show_e(this);
|
||||||
|
});
|
||||||
|
p.on("show.bs.popover", function (e) {
|
||||||
|
// this is to dynamically ajdust the content and bounds of the popup
|
||||||
|
p.attr('data-content', contentFunction(d));
|
||||||
|
p.data("bs.popover").setContent();
|
||||||
|
p.data("bs.popover").tip().css({"max-width": "1000px"});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function node_popup(n) {
|
||||||
|
popupContent = "<a href='" + n.url +"'>" + n.name + "</a><br>";
|
||||||
|
popupContent += "Depth " + n.depth + "<br>"
|
||||||
|
popupContent += "<img src='" + n.image + "' width='"+ 20 * nodeRadius +"'><br>"
|
||||||
|
if (n.scenarios.length > 0) {
|
||||||
|
popupContent += '<b>Half-lives and related scenarios:</b><br>'
|
||||||
|
for (var s of n.scenarios) {
|
||||||
|
popupContent += "<a href='" + s.url + "'>" + s.name + "</a><br>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isLeaf = pathway.links.filter(obj => obj.source.id === n.id).length === 0;
|
||||||
|
if(pathway.isIncremental && isLeaf) {
|
||||||
|
popupContent += '<br><a class="btn btn-primary" onclick="predictFromNode(\'' + n.url + '\')" href="#">Predict from here</a><br>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return popupContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
function edge_popup(e) {
|
||||||
|
popupContent = "<a href='" + e.url +"'>" + e.name + "</a><br>";
|
||||||
|
popupContent += "<img src='" + e.image + "' width='"+ 20 * nodeRadius +"'><br>"
|
||||||
|
if (e.reaction_probability) {
|
||||||
|
popupContent += '<b>Probability:</b><br>' + e.reaction_probability.toFixed(3) + '<br>';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (e.scenarios.length > 0) {
|
||||||
|
popupContent += '<b>Half-lives and related scenarios:</b><br>'
|
||||||
|
for (var s of e.scenarios) {
|
||||||
|
popupContent += "<a href='" + s.url + "'>" + s.name + "</a><br>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return popupContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
var clientX;
|
||||||
|
var clientY;
|
||||||
|
document.addEventListener('mousemove', function(event) {
|
||||||
|
clientX = event.clientX;
|
||||||
|
clientY =event.clientY;
|
||||||
|
});
|
||||||
|
|
||||||
const zoomable = d3.select("#zoomable");
|
const zoomable = d3.select("#zoomable");
|
||||||
|
|
||||||
@ -102,10 +207,10 @@ function draw(pathway, elem) {
|
|||||||
|
|
||||||
orig_depth = n.depth
|
orig_depth = n.depth
|
||||||
// console.log(n.id, parents)
|
// console.log(n.id, parents)
|
||||||
for(idx in parents) {
|
for (idx in parents) {
|
||||||
p = nodes[parents[idx]]
|
p = nodes[parents[idx]]
|
||||||
// console.log(p.depth)
|
// console.log(p.depth)
|
||||||
if(p.depth >= n.depth) {
|
if (p.depth >= n.depth) {
|
||||||
// keep the .5 steps for pseudo nodes
|
// keep the .5 steps for pseudo nodes
|
||||||
n.depth = n.pseudo ? p.depth + 1 : Math.floor(p.depth + 1);
|
n.depth = n.pseudo ? p.depth + 1 : Math.floor(p.depth + 1);
|
||||||
// console.log("Adjusting", orig_depth, Math.floor(p.depth + 1));
|
// console.log("Adjusting", orig_depth, Math.floor(p.depth + 1));
|
||||||
@ -128,13 +233,15 @@ function draw(pathway, elem) {
|
|||||||
.enter().append("line")
|
.enter().append("line")
|
||||||
// Check if target is pseudo and draw marker only if not pseudo
|
// Check if target is pseudo and draw marker only if not pseudo
|
||||||
.attr("class", d => d.target.pseudo ? "link_no_arrow" : "link")
|
.attr("class", d => d.target.pseudo ? "link_no_arrow" : "link")
|
||||||
.on("mouseover", (event, d) => {
|
// .on("mouseover", (event, d) => {
|
||||||
tooltip.style("visibility", "visible")
|
// tooltip.style("visibility", "visible")
|
||||||
.text(`Link: ${d.source.id} → ${d.target.id}`)
|
// .text(`Link: ${d.source.id} → ${d.target.id}`)
|
||||||
.style("top", `${event.pageY + 5}px`)
|
// .style("top", `${event.pageY + 5}px`)
|
||||||
.style("left", `${event.pageX + 5}px`);
|
// .style("left", `${event.pageX + 5}px`);
|
||||||
})
|
// })
|
||||||
.on("mouseout", () => tooltip.style("visibility", "hidden"));
|
// .on("mouseout", () => tooltip.style("visibility", "hidden"));
|
||||||
|
|
||||||
|
pop_add(link, "Reaction", edge_popup);
|
||||||
|
|
||||||
// Knoten zeichnen
|
// Knoten zeichnen
|
||||||
const node = zoomable.append("g")
|
const node = zoomable.append("g")
|
||||||
@ -145,19 +252,19 @@ function draw(pathway, elem) {
|
|||||||
.on("start", dragstarted)
|
.on("start", dragstarted)
|
||||||
.on("drag", dragged)
|
.on("drag", dragged)
|
||||||
.on("end", dragended))
|
.on("end", dragended))
|
||||||
.on("click", function(event, d) {
|
.on("click", function (event, d) {
|
||||||
d3.select(this).select("circle").classed("highlighted", !d3.select(this).select("circle").classed("highlighted"));
|
d3.select(this).select("circle").classed("highlighted", !d3.select(this).select("circle").classed("highlighted"));
|
||||||
})
|
})
|
||||||
.on("mouseover", (event, d) => {
|
// .on("mouseover", (event, d) => {
|
||||||
if (d.pseudo) {
|
// if (d.pseudo) {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
tooltip.style("visibility", "visible")
|
// tooltip.style("visibility", "visible")
|
||||||
.text(`Node: ${d.id} Depth: ${d.depth}`)
|
// .text(`Node: ${d.id} Depth: ${d.depth}`)
|
||||||
.style("top", `${event.pageY + 5}px`)
|
// .style("top", `${event.pageY + 5}px`)
|
||||||
.style("left", `${event.pageX + 5}px`);
|
// .style("left", `${event.pageX + 5}px`);
|
||||||
})
|
// })
|
||||||
.on("mouseout", () => tooltip.style("visibility", "hidden"));
|
// .on("mouseout", () => tooltip.style("visibility", "hidden"));
|
||||||
|
|
||||||
// Kreise für die Knoten hinzufügen
|
// Kreise für die Knoten hinzufügen
|
||||||
node.append("circle")
|
node.append("circle")
|
||||||
@ -172,4 +279,6 @@ function draw(pathway, elem) {
|
|||||||
.attr("y", -nodeRadius)
|
.attr("y", -nodeRadius)
|
||||||
.attr("width", nodeRadius * 2)
|
.attr("width", nodeRadius * 2)
|
||||||
.attr("height", nodeRadius * 2);
|
.attr("height", nodeRadius * 2);
|
||||||
|
|
||||||
|
pop_add(node, "Compound", node_popup);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
<li>
|
{% if meta.can_edit %}
|
||||||
<a role="button" data-toggle="modal" data-target="#new_compound_modal">
|
<li>
|
||||||
<span class="glyphicon glyphicon-plus"></span> New Compound</a>
|
<a role="button" data-toggle="modal" data-target="#new_compound_modal">
|
||||||
</li>
|
<span class="glyphicon glyphicon-plus"></span> New Compound</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
@ -1,4 +1,6 @@
|
|||||||
<li>
|
{% if meta.can_edit %}
|
||||||
<a role="button" data-toggle="modal" data-target="#new_compound_structure_modal">
|
<li>
|
||||||
<span class="glyphicon glyphicon-plus"></span> New Compound Structure</a>
|
<a role="button" data-toggle="modal" data-target="#new_compound_structure_modal">
|
||||||
</li>
|
<span class="glyphicon glyphicon-plus"></span> New Compound Structure</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
6
templates/actions/collections/edge.html
Normal file
6
templates/actions/collections/edge.html
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{% if meta.can_edit %}
|
||||||
|
<li>
|
||||||
|
<a role="button" data-toggle="modal" data-target="#new_edge_modal">
|
||||||
|
<span class="glyphicon glyphicon-plus"></span> New Edge</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
<li>
|
<li>
|
||||||
<a role="button" data-toggle="modal" data-target="#new_group_modal">
|
<a role="button" data-toggle="modal" data-target="#new_group_modal">
|
||||||
<span class="glyphicon glyphicon-plus"></span> New Group</a>
|
<span class="glyphicon glyphicon-plus"></span> New Group</a>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
<li>
|
{% if meta.can_edit %}
|
||||||
<a role="button" data-toggle="modal" data-target="#new_model_modal">
|
<li>
|
||||||
<span class="glyphicon glyphicon-plus"></span> New Model</a>
|
<a role="button" data-toggle="modal" data-target="#new_model_modal">
|
||||||
</li>
|
<span class="glyphicon glyphicon-plus"></span> New Model</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|||||||
6
templates/actions/collections/node.html
Normal file
6
templates/actions/collections/node.html
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{% if meta.can_edit %}
|
||||||
|
<li>
|
||||||
|
<a role="button" data-toggle="modal" data-target="#new_node_modal">
|
||||||
|
<span class="glyphicon glyphicon-plus"></span> New Node</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
@ -1,4 +1,6 @@
|
|||||||
<li>
|
{% if meta.can_edit %}
|
||||||
<a role="button" data-toggle="modal" data-target="#predict_modal">
|
<li>
|
||||||
<span class="glyphicon glyphicon-plus"></span> New Pathway</a>
|
<a role="button" data-toggle="modal" data-target="#predict_modal">
|
||||||
</li>
|
<span class="glyphicon glyphicon-plus"></span> New Pathway</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
@ -1,4 +1,6 @@
|
|||||||
<li>
|
{% if meta.can_edit %}
|
||||||
<a role="button" data-toggle="modal" data-target="#new_reaction_modal">
|
<li>
|
||||||
<span class="glyphicon glyphicon-plus"></span> New Reaction</a>
|
<a role="button" data-toggle="modal" data-target="#new_reaction_modal">
|
||||||
</li>
|
<span class="glyphicon glyphicon-plus"></span> New Reaction</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
@ -1,4 +1,6 @@
|
|||||||
<li>
|
{% if meta.can_edit %}
|
||||||
<a role="button" data-toggle="modal" data-target="#new_rule_modal">
|
<li>
|
||||||
<span class="glyphicon glyphicon-plus"></span> New Rule</a>
|
<a role="button" data-toggle="modal" data-target="#new_rule_modal">
|
||||||
</li>
|
<span class="glyphicon glyphicon-plus"></span> New Rule</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
<li>
|
{% if meta.can_edit %}
|
||||||
<a role="button" data-toggle="modal" data-target="#new_pathway_modal">
|
<li>
|
||||||
<span class="glyphicon glyphicon-plus"></span> New Scenario</a>
|
<a role="button" data-toggle="modal" data-target="#new_pathway_modal">
|
||||||
</li>
|
<span class="glyphicon glyphicon-plus"></span> New Scenario</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
@ -1,4 +1,6 @@
|
|||||||
<li>
|
{% if meta.can_edit %}
|
||||||
<a role="button" data-toggle="modal" data-target="#new_setting_modal">
|
<li>
|
||||||
<span class="glyphicon glyphicon-plus"></span>New Setting</a>
|
<a role="button" data-toggle="modal" data-target="#new_setting_modal">
|
||||||
</li>
|
<span class="glyphicon glyphicon-plus"></span>New Setting</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
@ -1,12 +1,14 @@
|
|||||||
<li>
|
{% if meta.can_edit %}
|
||||||
<a role="button" data-toggle="modal" data-target="#edit_compound_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-edit"></i> Edit Compound</a>
|
<a role="button" data-toggle="modal" data-target="#edit_compound_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-edit"></i> Edit Compound</a>
|
||||||
<li>
|
</li>
|
||||||
<a role="button" data-toggle="modal" data-target="#add_structure_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-plus"></i> Add Structure</a>
|
<a role="button" data-toggle="modal" data-target="#add_structure_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-plus"></i> Add Structure</a>
|
||||||
<li>
|
</li>
|
||||||
<a class="button" data-toggle="modal" data-target="#delete_compound_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-trash"></i> Delete Compound</a>
|
<a class="button" data-toggle="modal" data-target="#delete_compound_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-trash"></i> Delete Compound</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
@ -1,8 +1,10 @@
|
|||||||
<li>
|
{% if meta.can_edit %}
|
||||||
<a role="button" data-toggle="modal" data-target="#edit_compound_structure_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-edit"></i> Edit Compound Structure</a>
|
<a role="button" data-toggle="modal" data-target="#edit_compound_structure_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-edit"></i> Edit Compound Structure</a>
|
||||||
<li>
|
</li>
|
||||||
<a class="button" data-toggle="modal" data-target="#delete_compound_structure_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-trash"></i> Delete Compound Structure</a>
|
<a class="button" data-toggle="modal" data-target="#delete_compound_structure_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-trash"></i> Delete Compound Structure</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
@ -1,8 +1,10 @@
|
|||||||
<li>
|
{% if meta.can_edit %}
|
||||||
<a role="button" data-toggle="modal" data-target="#delete_group_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-trash"></i> Delete Group</a>
|
<a role="button" data-toggle="modal" data-target="#delete_group_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-trash"></i> Delete Group</a>
|
||||||
<li>
|
</li>
|
||||||
<a role="button" data-toggle="modal" data-target="#edit_group_member_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-trash"></i> Add/Remove Member</a>
|
<a role="button" data-toggle="modal" data-target="#edit_group_member_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-trash"></i> Add/Remove Member</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
6
templates/actions/objects/model.html
Normal file
6
templates/actions/objects/model.html
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{% if meta.can_edit %}
|
||||||
|
<li>
|
||||||
|
<a class="button" data-toggle="modal" data-target="#delete_model_modal">
|
||||||
|
<i class="glyphicon glyphicon-trash"></i> Delete Model</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
10
templates/actions/objects/node.html
Normal file
10
templates/actions/objects/node.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{% if meta.can_edit %}
|
||||||
|
<li>
|
||||||
|
<a role="button" data-toggle="modal" data-target="#edit_node_modal">
|
||||||
|
<i class="glyphicon glyphicon-edit"></i> Edit Node</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="button" data-toggle="modal" data-target="#delete_node_modal">
|
||||||
|
<i class="glyphicon glyphicon-trash"></i> Delete Node</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
@ -1,16 +1,18 @@
|
|||||||
<li>
|
{% if meta.can_edit %}
|
||||||
<a role="button" data-toggle="modal" data-target="#edit_package_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-edit"></i> Edit Package</a>
|
<a role="button" data-toggle="modal" data-target="#edit_package_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-edit"></i> Edit Package</a>
|
||||||
<li>
|
</li>
|
||||||
<a role="button" data-toggle="modal" data-target="#edit_package_permissions_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-user"></i> Edit Permissions</a>
|
<a role="button" data-toggle="modal" data-target="#edit_package_permissions_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-user"></i> Edit Permissions</a>
|
||||||
<li>
|
</li>
|
||||||
<a role="button" data-toggle="modal" data-target="#set_license_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-duplicate"></i> License</a>
|
<a role="button" data-toggle="modal" data-target="#set_license_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-duplicate"></i> License</a>
|
||||||
<li>
|
</li>
|
||||||
<a class="button" data-toggle="modal" data-target="#delete_package_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-trash"></i> Delete Package</a>
|
<a class="button" data-toggle="modal" data-target="#delete_package_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-trash"></i> Delete Package</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
@ -1,8 +1,32 @@
|
|||||||
<li>
|
{% if meta.can_edit %}
|
||||||
<a role="button" data-toggle="modal" data-target="#edit_pathway_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-edit"></i> Edit Pathway</a>
|
<a class="button" data-toggle="modal" data-target="#add_pathway_node_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-plus"></i> Add Compound</a>
|
||||||
<li>
|
</li>
|
||||||
<a class="button" data-toggle="modal" data-target="#delete_pathawy_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-trash"></i> Delete Pathway</a>
|
<a class="button" data-toggle="modal" data-target="#add_pathway_edge_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-plus"></i> Add Reaction</a>
|
||||||
|
</li>
|
||||||
|
<li role="separator" class="divider"></li>
|
||||||
|
<li>
|
||||||
|
<a class="button" data-toggle="modal" data-target="#edit_pathway_modal">
|
||||||
|
<i class="glyphicon glyphicon-plus"></i> Edit Pathway</a>
|
||||||
|
</li>
|
||||||
|
{# <li>#}
|
||||||
|
{# <a class="button" data-toggle="modal" data-target="#add_pathway_edge_modal">#}
|
||||||
|
{# <i class="glyphicon glyphicon-plus"></i> Calculate Compound Properties</a>#}
|
||||||
|
{# </li>#}
|
||||||
|
<li role="separator" class="divider"></li>
|
||||||
|
<li>
|
||||||
|
<a class="button" data-toggle="modal" data-target="#delete_pathway_node_modal">
|
||||||
|
<i class="glyphicon glyphicon-trash"></i> Delete Compound</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="button" data-toggle="modal" data-target="#delete_pathway_edge_modal">
|
||||||
|
<i class="glyphicon glyphicon-trash"></i> Delete Reaction</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="button" data-toggle="modal" data-target="#delete_pathway_modal">
|
||||||
|
<i class="glyphicon glyphicon-trash"></i> Delete Pathway</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
@ -1,8 +1,10 @@
|
|||||||
<li>
|
{% if meta.can_edit %}
|
||||||
<a role="button" data-toggle="modal" data-target="#edit_reaction_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-edit"></i> Edit Reaction</a>
|
<a role="button" data-toggle="modal" data-target="#edit_reaction_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-edit"></i> Edit Reaction</a>
|
||||||
<li>
|
</li>
|
||||||
<a class="button" data-toggle="modal" data-target="#delete_reaction_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-trash"></i> Delete Reaction</a>
|
<a class="button" data-toggle="modal" data-target="#delete_reaction_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-trash"></i> Delete Reaction</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
@ -1,4 +1,6 @@
|
|||||||
<li>
|
{% if meta.can_edit %}
|
||||||
<a role="button" data-toggle="modal" data-target="#edit_rule_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-edit"></i> Edit Rule</a>
|
<a role="button" data-toggle="modal" data-target="#edit_rule_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-edit"></i> Edit Rule</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
{% if meta.can_edit %}
|
||||||
|
{% endif %}
|
||||||
@ -1,20 +1,22 @@
|
|||||||
<li>
|
{% if meta.can_edit %}
|
||||||
<a role="button" data-toggle="modal" data-target="#edit_user_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-edit"></i> Update</a>
|
<a role="button" data-toggle="modal" data-target="#edit_user_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-edit"></i> Update</a>
|
||||||
<li>
|
</li>
|
||||||
<a role="button" data-toggle="modal" data-target="#edit_password_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-lock"></i> Update Password</a>
|
<a role="button" data-toggle="modal" data-target="#edit_password_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-lock"></i> Update Password</a>
|
||||||
<li>
|
</li>
|
||||||
<a role="button" data-toggle="modal" data-target="#new_prediction_setting_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-plus"></i> New Prediction Setting</a>
|
<a role="button" data-toggle="modal" data-target="#new_prediction_setting_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-plus"></i> New Prediction Setting</a>
|
||||||
<li>
|
</li>
|
||||||
<a role="button" data-toggle="modal" data-target="#manage_api_token_modal">
|
{# <li>#}
|
||||||
<i class="glyphicon glyphicon-console"></i> Manage API Token</a>
|
{# <a role="button" data-toggle="modal" data-target="#manage_api_token_modal">#}
|
||||||
</li>
|
{# <i class="glyphicon glyphicon-console"></i> Manage API Token</a>#}
|
||||||
<li>
|
{# </li>#}
|
||||||
<a role="button" data-toggle="modal" data-target="#delete_user_modal">
|
<li>
|
||||||
<i class="glyphicon glyphicon-trash"></i> Delete Account</a>
|
<a role="button" data-toggle="modal" data-target="#delete_user_modal">
|
||||||
</li>
|
<i class="glyphicon glyphicon-trash"></i> Delete Account</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
@ -35,12 +35,16 @@
|
|||||||
{% include "modals/collections/new_reaction_modal.html" %}
|
{% include "modals/collections/new_reaction_modal.html" %}
|
||||||
{% elif object_type == 'pathway' %}
|
{% elif object_type == 'pathway' %}
|
||||||
{# {% include "modals/collections/new_pathway_modal.html" %} #}
|
{# {% include "modals/collections/new_pathway_modal.html" %} #}
|
||||||
|
{% elif object_type == 'node' %}
|
||||||
|
{% include "modals/collections/new_node_modal.html" %}
|
||||||
|
{% elif object_type == 'edge' %}
|
||||||
|
{% include "modals/collections/new_edge_modal.html" %}
|
||||||
{% elif object_type == 'scenario' %}
|
{% elif object_type == 'scenario' %}
|
||||||
{% include "modals/collections/new_scenario_modal.html" %}
|
{% include "modals/collections/new_scenario_modal.html" %}
|
||||||
{% elif object_type == 'model' %}
|
{% elif object_type == 'model' %}
|
||||||
{% include "modals/collections/new_model_modal.html" %}
|
{% include "modals/collections/new_model_modal.html" %}
|
||||||
{% elif object_type == 'setting' %}
|
{% elif object_type == 'setting' %}
|
||||||
{% include "modals/collections/new_setting_modal.html" %}
|
{#{% include "modals/collections/new_setting_modal.html" %}#}
|
||||||
{% elif object_type == 'user' %}
|
{% elif object_type == 'user' %}
|
||||||
<div></div>
|
<div></div>
|
||||||
{% elif object_type == 'group' %}
|
{% elif object_type == 'group' %}
|
||||||
@ -63,6 +67,10 @@
|
|||||||
Reactions
|
Reactions
|
||||||
{% elif object_type == 'pathway' %}
|
{% elif object_type == 'pathway' %}
|
||||||
Pathways
|
Pathways
|
||||||
|
{% elif object_type == 'node' %}
|
||||||
|
Nodes
|
||||||
|
{% elif object_type == 'edge' %}
|
||||||
|
Edges
|
||||||
{% elif object_type == 'scenario' %}
|
{% elif object_type == 'scenario' %}
|
||||||
Scenarios
|
Scenarios
|
||||||
{% elif object_type == 'model' %}
|
{% elif object_type == 'model' %}
|
||||||
@ -75,34 +83,38 @@
|
|||||||
Groups
|
Groups
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div id="actionsButton"
|
<div id="actionsButton"
|
||||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;"
|
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"
|
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||||
aria-haspopup="true" aria-expanded="false"><span
|
aria-haspopup="true" aria-expanded="false"><span
|
||||||
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
||||||
style="padding-right:1em"></span></a>
|
style="padding-right:1em"></span></a>
|
||||||
<ul id="actionsList" class="dropdown-menu">
|
<ul id="actionsList" class="dropdown-menu">
|
||||||
{% block actions %}
|
{% block actions %}
|
||||||
{% if object_type == 'package' %}
|
{% if object_type == 'package' %}
|
||||||
{% include "actions/collections/package.html" %}
|
{% include "actions/collections/package.html" %}
|
||||||
{% elif object_type == 'compound' %}
|
{% elif object_type == 'compound' %}
|
||||||
{% include "actions/collections/compound.html" %}
|
{% include "actions/collections/compound.html" %}
|
||||||
{% elif object_type == 'structure' %}
|
{% elif object_type == 'structure' %}
|
||||||
{% include "actions/collections/compound_structure.html" %}
|
{% include "actions/collections/compound_structure.html" %}
|
||||||
{% elif object_type == 'rule' %}
|
{% elif object_type == 'rule' %}
|
||||||
{% include "actions/collections/rule.html" %}
|
{% include "actions/collections/rule.html" %}
|
||||||
{% elif object_type == 'reaction' %}
|
{% elif object_type == 'reaction' %}
|
||||||
{% include "actions/collections/reaction.html" %}
|
{% include "actions/collections/reaction.html" %}
|
||||||
{% elif object_type == 'setting' %}
|
{% elif object_type == 'setting' %}
|
||||||
{% include "actions/collections/setting.html" %}
|
{% include "actions/collections/setting.html" %}
|
||||||
{% elif object_type == 'scenario' %}
|
{% elif object_type == 'scenario' %}
|
||||||
{% include "actions/collections/scenario.html" %}
|
{% include "actions/collections/scenario.html" %}
|
||||||
{% elif object_type == 'model' %}
|
{% elif object_type == 'model' %}
|
||||||
{% include "actions/collections/model.html" %}
|
{% include "actions/collections/model.html" %}
|
||||||
{% elif object_type == 'pathway' %}
|
{% elif object_type == 'pathway' %}
|
||||||
{% include "actions/collections/pathway.html" %}
|
{% include "actions/collections/pathway.html" %}
|
||||||
{% elif object_type == 'group' %}
|
{% elif object_type == 'node' %}
|
||||||
{% include "actions/collections/group.html" %}
|
{% include "actions/collections/node.html" %}
|
||||||
{% endif %}
|
{% elif object_type == 'edge' %}
|
||||||
|
{% include "actions/collections/edge.html" %}
|
||||||
|
{% elif object_type == 'group' %}
|
||||||
|
{% include "actions/collections/group.html" %}
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -133,6 +145,14 @@
|
|||||||
<p>A pathway displays the (predicted) biodegradation of a compound as graph.
|
<p>A pathway displays the (predicted) biodegradation of a compound as graph.
|
||||||
<a target="_blank" href="https://wiki.envipath.org/index.php/pathways" role="button">Learn more
|
<a target="_blank" href="https://wiki.envipath.org/index.php/pathways" role="button">Learn more
|
||||||
>></a></p>
|
>></a></p>
|
||||||
|
{% elif object_type == 'node' %}
|
||||||
|
<p>Nodes represent the (predicted) compounds in a graph.
|
||||||
|
<a target="_blank" href="https://wiki.envipath.org/index.php/nodes" role="button">Learn more
|
||||||
|
>></a></p>
|
||||||
|
{% elif object_type == 'edge' %}
|
||||||
|
<p>Edges represent the links between Nodes in a graph
|
||||||
|
<a target="_blank" href="https://wiki.envipath.org/index.php/edges" role="button">Learn more
|
||||||
|
>></a></p>
|
||||||
{% elif object_type == 'scenario' %}
|
{% elif object_type == 'scenario' %}
|
||||||
<p>A scenario contains meta-information that can be attached to other data (compounds, rules, ..).
|
<p>A scenario contains meta-information that can be attached to other data (compounds, rules, ..).
|
||||||
<a target="_blank" href="https://wiki.envipath.org/index.php/scenarios" role="button">Learn more
|
<a target="_blank" href="https://wiki.envipath.org/index.php/scenarios" role="button">Learn more
|
||||||
@ -185,7 +205,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% for obj in reviewed_objects|slice:":50" %}
|
{% for obj in reviewed_objects|slice:":50" %}
|
||||||
<a class="list-group-item" href="{{ obj.url }}">{{ obj.name }}
|
<a class="list-group-item" href="{{ obj.url }}">{{ obj.name }}{# <i>({{ obj.package.name }})</i> #}
|
||||||
<span class="glyphicon glyphicon-star" aria-hidden="true"
|
<span class="glyphicon glyphicon-star" aria-hidden="true"
|
||||||
style="float:right" data-toggle="tooltip"
|
style="float:right" data-toggle="tooltip"
|
||||||
data-placement="top" title="" data-original-title="Reviewed">
|
data-placement="top" title="" data-original-title="Reviewed">
|
||||||
@ -229,51 +249,52 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
$(function() {
|
$(function () {
|
||||||
$('#modal-form-delete-submit').on('click', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
$('#modal-form-delete').submit();
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#object-search').show();
|
$('#modal-form-delete-submit').on('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
if ($('#load-remaining').length) {
|
$('#modal-form-delete').submit();
|
||||||
$('#load-remaining').on('click', function() {
|
|
||||||
|
|
||||||
makeLoadingGif("#load-all-loading", "{% static '/images/wait.gif' %}");
|
|
||||||
$('#load-all-error').hide();
|
|
||||||
|
|
||||||
$.getJSON('?all=true', function(resp) {
|
|
||||||
$('#ReviewedContent').empty();
|
|
||||||
$('#UnreviewedContent').empty();
|
|
||||||
|
|
||||||
for(o in resp.objects) {
|
|
||||||
obj = resp.objects[o];
|
|
||||||
if (obj.reviewed) {
|
|
||||||
$('#ReviewedContent').append('<a class="list-group-item" href="' + obj.url + '">' + obj.name + '</a>');
|
|
||||||
} else {
|
|
||||||
$('#UnreviewedContent').append('<a class="list-group-item" href="' + obj.url + '">' + obj.name + '</a>');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#load-all-loading').empty();
|
|
||||||
$('#load-remaining').hide();
|
|
||||||
}).fail(function(resp) {
|
|
||||||
$('#load-all-loading').empty();
|
|
||||||
$('#load-all-error').show();
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#object-search').on('keyup', function() {
|
$('#object-search').show();
|
||||||
let query = $(this).val().toLowerCase();
|
|
||||||
$('a.list-group-item').each(function() {
|
|
||||||
let text = $(this).text().toLowerCase();
|
|
||||||
$(this).toggle(text.indexOf(query) !== -1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
if ($('#load-remaining').length) {
|
||||||
</script>
|
$('#load-remaining').on('click', function () {
|
||||||
|
|
||||||
|
makeLoadingGif("#load-all-loading", "{% static '/images/wait.gif' %}");
|
||||||
|
$('#load-all-error').hide();
|
||||||
|
|
||||||
|
$.getJSON('?all=true', function (resp) {
|
||||||
|
$('#ReviewedContent').empty();
|
||||||
|
$('#UnreviewedContent').empty();
|
||||||
|
|
||||||
|
for (o in resp.objects) {
|
||||||
|
obj = resp.objects[o];
|
||||||
|
if (obj.reviewed) {
|
||||||
|
$('#ReviewedContent').append('<a class="list-group-item" href="' + obj.url + '">' + obj.name + '</a>');
|
||||||
|
} else {
|
||||||
|
$('#UnreviewedContent').append('<a class="list-group-item" href="' + obj.url + '">' + obj.name + '</a>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#load-all-loading').empty();
|
||||||
|
$('#load-remaining').hide();
|
||||||
|
}).fail(function (resp) {
|
||||||
|
$('#load-all-loading').empty();
|
||||||
|
$('#load-all-error').show();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#object-search').on('keyup', function () {
|
||||||
|
let query = $(this).val().toLowerCase();
|
||||||
|
$('a.list-group-item').each(function () {
|
||||||
|
let text = $(this).text().toLowerCase();
|
||||||
|
$(this).toggle(text.indexOf(query) !== -1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
14
templates/errors/error.html
Normal file
14
templates/errors/error.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{% extends "framework.html" %}
|
||||||
|
{% load static %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
<h4 class="alert-heading">Something went wrong!</h4>
|
||||||
|
<p>An unexpected Error occurred...</p>
|
||||||
|
<hr>
|
||||||
|
<p class="mb-0">
|
||||||
|
The error was logged and will be investigated.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock content %}
|
||||||
@ -2,9 +2,9 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="alert alert-error" role="alert">
|
<div class="alert alert-danger" role="alert">
|
||||||
<h4 class="alert-heading">Your account has not been activated yet!</h4>
|
<h4 class="alert-heading">Your account has not been activated yet!</h4>
|
||||||
<p>Your account has not been activated yet. If you have question <a href="mailto:admin@envipath.org">contact
|
<p>Your account has not been activated yet. If you have questions <a href="mailto:admin@envipath.org">contact
|
||||||
us.</a>
|
us.</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -14,13 +14,25 @@
|
|||||||
href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.7.3/css/bootstrap-select.min.css">
|
href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.7.3/css/bootstrap-select.min.css">
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.7.3/js/bootstrap-select.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.7.3/js/bootstrap-select.min.js"></script>
|
||||||
<!-- CDN END -->
|
<!-- CDN END -->
|
||||||
|
<meta name="csrf-token" content="{{ csrf_token }}">
|
||||||
|
<script>
|
||||||
|
const csrftoken = document.querySelector('[name=csrf-token]').content;
|
||||||
|
|
||||||
|
// Setup CSRF header for all jQuery AJAX requests
|
||||||
|
$.ajaxSetup({
|
||||||
|
beforeSend: function(xhr, settings) {
|
||||||
|
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type)) {
|
||||||
|
xhr.setRequestHeader("X-CSRFToken", csrftoken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{# Favicon #}
|
{# Favicon #}
|
||||||
<link rel="shortcut icon" type="image/png" href="{% static 'images/favicon.ico' %}"/>
|
<link rel="shortcut icon" type="image/png" href="{% static 'images/favicon.ico' %}"/>
|
||||||
<!-- {# C3 CSS #}-->
|
<!-- {# C3 CSS #}-->
|
||||||
<!-- <link id="css-c3" href="{% static 'css/c3.css' %}" rel="stylesheet" type="text/css"/>-->
|
<!-- <link id="css-c3" href="{% static 'css/c3.css' %}" rel="stylesheet" type="text/css"/>-->
|
||||||
<!-- {# EP CSS #}-->
|
<!-- {# EP CSS #}-->
|
||||||
<!-- <link id="css-pps_white_general" href="{% static 'css/epp.css' %}" rel="stylesheet" type="text/css"/>-->
|
<!-- <link id="css-pps_white_general" href="{% static 'css/epp.css' %}" rel="stylesheet" type="text/css"/>-->
|
||||||
|
|
||||||
|
|
||||||
{# General EP JS #}
|
{# General EP JS #}
|
||||||
@ -29,21 +41,23 @@
|
|||||||
<script src="{% static 'js/jquery-bootstrap-modal-steps.js' %}"></script>
|
<script src="{% static 'js/jquery-bootstrap-modal-steps.js' %}"></script>
|
||||||
|
|
||||||
{% if not debug %}
|
{% if not debug %}
|
||||||
<!-- Matomo -->
|
<!-- Matomo -->
|
||||||
<script>
|
<script>
|
||||||
var _paq = window._paq = window._paq || [];
|
var _paq = window._paq = window._paq || [];
|
||||||
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
||||||
_paq.push(['trackPageView']);
|
_paq.push(['trackPageView']);
|
||||||
_paq.push(['enableLinkTracking']);
|
_paq.push(['enableLinkTracking']);
|
||||||
(function() {
|
(function () {
|
||||||
var u="//matomo.envipath.com/";
|
var u = "//matomo.envipath.com/";
|
||||||
_paq.push(['setTrackerUrl', u+'matomo.php']);
|
_paq.push(['setTrackerUrl', u + 'matomo.php']);
|
||||||
_paq.push(['setSiteId', '7']);
|
_paq.push(['setSiteId', '7']);
|
||||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
|
||||||
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
g.async = true;
|
||||||
})();
|
g.src = u + 'matomo.js';
|
||||||
</script>
|
s.parentNode.insertBefore(g, s);
|
||||||
<!-- End Matomo Code -->
|
})();
|
||||||
|
</script>
|
||||||
|
<!-- End Matomo Code -->
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
@ -52,13 +66,13 @@
|
|||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<!-- Brand and toggle get grouped for better mobile display -->
|
<!-- Brand and toggle get grouped for better mobile display -->
|
||||||
<div class="navbar-header navbar-header-framework">
|
<div class="navbar-header navbar-header-framework">
|
||||||
<!-- <button type="button" class="navbar-toggle navbar-toggle-framework" data-toggle="collapse"-->
|
<!-- <button type="button" class="navbar-toggle navbar-toggle-framework" data-toggle="collapse"-->
|
||||||
<!-- data-target="#navbarCollapse">-->
|
<!-- data-target="#navbarCollapse">-->
|
||||||
<!-- <span class="sr-only">Toggle navigation</span>-->
|
<!-- <span class="sr-only">Toggle navigation</span>-->
|
||||||
<!-- <span class="icon-bar"></span>-->
|
<!-- <span class="icon-bar"></span>-->
|
||||||
<!-- <span class="icon-bar"></span>-->
|
<!-- <span class="icon-bar"></span>-->
|
||||||
<!-- <span class="icon-bar"></span>-->
|
<!-- <span class="icon-bar"></span>-->
|
||||||
<!-- </button>-->
|
<!-- </button>-->
|
||||||
<a id="pictureLink" href="{{ meta.server_url }}" class="navbar-brand">
|
<a id="pictureLink" href="{{ meta.server_url }}" class="navbar-brand">
|
||||||
<img id="image-logo-short-white.svg" src='{% static "/images/logo-short-white.svg" %}' width="100"
|
<img id="image-logo-short-white.svg" src='{% static "/images/logo-short-white.svg" %}' width="100"
|
||||||
alt="enviPath">
|
alt="enviPath">
|
||||||
@ -95,16 +109,16 @@
|
|||||||
<li><a href="{{ meta.server_url }}/reaction" id="reactionLink">Reaction</a></li>
|
<li><a href="{{ meta.server_url }}/reaction" id="reactionLink">Reaction</a></li>
|
||||||
<li><a href="{{ meta.server_url }}/model" id="relative-reasoningLink">Model</a></li>
|
<li><a href="{{ meta.server_url }}/model" id="relative-reasoningLink">Model</a></li>
|
||||||
<li><a href="{{ meta.server_url }}/scenario" id="scenarioLink">Scenario</a></li>
|
<li><a href="{{ meta.server_url }}/scenario" id="scenarioLink">Scenario</a></li>
|
||||||
<li><a href="{{ meta.server_url }}/setting" id="settingLink">Setting</a></li>
|
{# <li><a href="{{ meta.server_url }}/setting" id="settingLink">Setting</a></li>#}
|
||||||
<!-- <li><a href="{{ meta.server_url }}/user" id="userLink">User</a></li>-->
|
{# <li><a href="{{ meta.server_url }}/user" id="userLink">User</a></li>#}
|
||||||
<!-- <li><a href="{{ meta.server_url }}/group" id="groupLink">Group</a></li>-->
|
{# <li><a href="{{ meta.server_url }}/group" id="groupLink">Group</a></li>#}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ul class="nav navbar-nav navbar-right navbar-nav-framework navbar-right-framework">
|
<ul class="nav navbar-nav navbar-right navbar-nav-framework navbar-right-framework">
|
||||||
<!-- <li><a href="{{ meta.server_url }}/search" id="searchLink">Search</a></li>-->
|
{# <li><a href="{{ meta.server_url }}/search" id="searchLink">Search</a></li>#}
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<a data-toggle="dropdown" class="dropdown-toggle" href="#">Info <b class="caret"></b></a>
|
<a data-toggle="dropdown" class="dropdown-toggle" href="#">Info <b class="caret"></b></a>
|
||||||
<ul role="menu" class="dropdown-menu">
|
<ul role="menu" class="dropdown-menu">
|
||||||
@ -120,31 +134,33 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
{% if meta.user.username == 'anonymous' %}
|
{% if meta.user.username == 'anonymous' %}
|
||||||
<li>
|
<li>
|
||||||
<a href="#signup" id="loginButton" data-toggle="modal" data-target="#signupmodal"
|
<a href="#signup" id="loginButton" data-toggle="modal" data-target="#signupmodal"
|
||||||
style="margin-right:10px">Login</a>
|
style="margin-right:10px">Login</a>
|
||||||
</li>
|
</li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<a data-toggle="dropdown" id="loggedInButton" class="dropdown-toggle" id="logedInButton" href="#">
|
<a data-toggle="dropdown" id="loggedInButton" class="dropdown-toggle" id="logedInButton"
|
||||||
<div id="username">
|
href="#">
|
||||||
{{ user.username }}<b class="caret"></b>
|
<div id="username">
|
||||||
</div>
|
{{ user.username }}<b class="caret"></b>
|
||||||
</a>
|
|
||||||
<ul role="menu" class="dropdown-menu">
|
|
||||||
<li>
|
|
||||||
<a href="{{ meta.user.url }}" id="accountbutton">My Account</a>
|
|
||||||
</li>
|
|
||||||
<li class="divider"></li>
|
|
||||||
<form class="navbar-form navbar-left navbar-left-framework" role="logout" action="{{ meta.user.url }}" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="form-group">
|
|
||||||
<input type="hidden" name="logout" value="true">
|
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-default">Logout</button>
|
</a>
|
||||||
</form>
|
<ul role="menu" class="dropdown-menu">
|
||||||
</ul>
|
<li>
|
||||||
</li>
|
<a href="{{ meta.user.url }}" id="accountbutton">My Account</a>
|
||||||
|
</li>
|
||||||
|
<li class="divider"></li>
|
||||||
|
<form class="navbar-form navbar-left navbar-left-framework" role="logout"
|
||||||
|
action="{{ meta.user.url }}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="hidden" name="logout" value="true">
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-default">Logout</button>
|
||||||
|
</form>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -152,26 +168,26 @@
|
|||||||
</nav>
|
</nav>
|
||||||
<div id="docContent" class="content container">
|
<div id="docContent" class="content container">
|
||||||
{% if breadcrumbs %}
|
{% if breadcrumbs %}
|
||||||
<div id="bread">
|
<div id="bread">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
{% for elem in breadcrumbs %}
|
{% for elem in breadcrumbs %}
|
||||||
{% for name, url in elem.items %}
|
{% for name, url in elem.items %}
|
||||||
{% if forloop.parentloop.last %}
|
{% if forloop.parentloop.last %}
|
||||||
<li class="active">{{ name }}</li>
|
<li class="active">{{ name }}</li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ url }}">{{ name }}</a>
|
<a href="{{ url }}">{{ name }}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
</ol>
|
||||||
</ol>
|
</div>
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if message %}
|
{% if message %}
|
||||||
<div id="message">
|
<div id="message">
|
||||||
{{ message }}
|
{{ message }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
@ -206,19 +222,26 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<ul class="nav nav-pills nav-justified">
|
<ul class="nav nav-pills nav-justified">
|
||||||
<!-- <li><a href="https://envipath.com/imprint/" target="_blank">Impressum/Imprint</a></li>-->
|
<!-- <li><a href="https://envipath.com/imprint/" target="_blank">Impressum/Imprint</a></li>-->
|
||||||
<li><a href="mailto:admin@envipath.org" target="_blank">Contact</a></li>
|
<li><a href="mailto:admin@envipath.org" target="_blank">Contact</a></li>
|
||||||
<!-- <li><a href="http://envipath.com" target="_blank"> enviPath UG (haftungsbeschränkt) & Co. KG ©-->
|
<!-- <li><a href="http://envipath.com" target="_blank"> enviPath UG (haftungsbeschränkt) & Co. KG ©-->
|
||||||
<!-- {{ YEAR }}</a></li>-->
|
<!-- {{ YEAR }}</a></li>-->
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
$(function () {
|
||||||
|
// Hide actionsbutton if theres no action defined
|
||||||
|
if ($('#actionsButton ul').children().length > 0) {
|
||||||
|
$('#actionsButton').show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{% block modals %}
|
{% block modals %}
|
||||||
{% include "modals/cite_modal.html" %}
|
{% include "modals/cite_modal.html" %}
|
||||||
{% include "modals/signup_modal.html" %}
|
{% include "modals/signup_modal.html" %}
|
||||||
{% include "modals/predict_modal.html" %}
|
{% include "modals/predict_modal.html" %}
|
||||||
{% include "modals/batch_predict_modal.html" %}
|
{% include "modals/batch_predict_modal.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -2,139 +2,166 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<!-- TODO rename ids as well as remove pathways if modal is closed!-->
|
<!-- TODO rename ids as well as remove pathways if modal is closed!-->
|
||||||
<div class="modal fade" tabindex="-1" id="foundMatching" role="dialog" aria-labelledby="foundModal" aria-hidden="true">
|
<div class="modal fade" tabindex="-1" id="foundMatching" role="dialog" aria-labelledby="foundModal"
|
||||||
<div class="modal-dialog">
|
aria-hidden="true">
|
||||||
<div class="modal-content">
|
<div class="modal-dialog">
|
||||||
<div class="modal-header">
|
<div class="modal-content">
|
||||||
<button type="button" class="close" data-dismiss="modal">
|
<div class="modal-header">
|
||||||
<span aria-hidden="true">×</span>
|
<button type="button" class="close" data-dismiss="modal">
|
||||||
<span class="sr-only">Close</span>
|
<span aria-hidden="true">×</span>
|
||||||
</button>
|
<span class="sr-only">Close</span>
|
||||||
<h4 class="modal-title" id="newPackMod">Found Pathway in Database</h4>
|
</button>
|
||||||
</div>
|
<h4 class="modal-title" id="newPackMod">Found Pathway in Database</h4>
|
||||||
<div class="modal-body">
|
</div>
|
||||||
<p>We found at least one pathway in the database with the given root
|
<div class="modal-body">
|
||||||
compound. Do you want to open any of the existing pathways or
|
<p>We found at least one pathway in the database with the given root
|
||||||
predict a new one? To open an existing pathway, simply click
|
compound. Do you want to open any of the existing pathways or
|
||||||
on the pathway, to predict a new one, click Predict. The predicted
|
predict a new one? To open an existing pathway, simply click
|
||||||
pathway might differ from the ones in the database due to the
|
on the pathway, to predict a new one, click Predict. The predicted
|
||||||
settings used in the prediction.</p>
|
pathway might differ from the ones in the database due to the
|
||||||
<div id="foundPathways"></div>
|
settings used in the prediction.</p>
|
||||||
</div>
|
<div id="foundPathways"></div>
|
||||||
<div class="modal-footer">
|
</div>
|
||||||
<a id="modal-predict" class="btn btn-primary" href="#">Predict</a>
|
<div class="modal-footer">
|
||||||
<button type="button" id="cancel-predict" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
<a id="modal-predict" class="btn btn-primary" href="#">Predict</a>
|
||||||
|
<button type="button" id="cancel-predict" class="btn btn-default" data-dismiss="modal">Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="jumbotron">
|
||||||
<div class="jumbotron">
|
<h1>
|
||||||
<h1>
|
<img id="image-logo-long" class="img-responsive" alt="enviPath" width="1000ex"
|
||||||
<img id="image-logo-long" class="img-responsive" alt="enviPath" width="1000ex"
|
src='{% static "/images/logo-long.svg" %}'/>
|
||||||
src='{% static "/images/logo-long.svg" %}'/>
|
</h1>
|
||||||
</h1>
|
<p>enviPath is a database and prediction system for the microbial
|
||||||
<p>enviPath is a database and prediction system for the microbial
|
biotransformation of organic environmental contaminants. The
|
||||||
biotransformation of organic environmental contaminants. The
|
database provides the possibility to store and view experimentally
|
||||||
database provides the possibility to store and view experimentally
|
observed biotransformation pathways. The pathway prediction system
|
||||||
observed biotransformation pathways. The pathway prediction system
|
provides different relative reasoning models to predict likely biotransformation
|
||||||
provides different relative reasoning models to predict likely biotransformation
|
pathways and products. You can try it out below.
|
||||||
pathways and products. You can try it out below.
|
</p>
|
||||||
</p>
|
<p>
|
||||||
<p>
|
<a class="btn" style="background-color:#222222;color:#9d9d9d" role="button" target="_blank"
|
||||||
<a class="btn" style="background-color:#222222;color:#9d9d9d" role="button" target="_blank"
|
href="https://wiki.envipath.org/index.php/Main_Page">Learn more >></a>
|
||||||
href="https://wiki.envipath.org/index.php/Main_Page">Learn more >></a>
|
</p>
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<p></p>
|
|
||||||
|
|
||||||
<div class="input-group" id="index-form-bar">
|
|
||||||
<div class="input-group-btn">
|
|
||||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
|
||||||
<span class="caret"></span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu" role="menu">
|
|
||||||
<li>
|
|
||||||
<iframe id="index-form-ketcher" src="{% static '/js/ketcher2/ketcher.html' %}" width="100%"
|
|
||||||
height="510"></iframe>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
<input type="text" class="form-control" id='index-form-text-input' placeholder="Enter a SMILES to predict a Pathway or type something to search">
|
<p></p>
|
||||||
<div class="input-group-btn">
|
<form id="index-form" action="{{ meta.current_package.url }}/pathway" method="POST">
|
||||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true"
|
{% csrf_token %}
|
||||||
aria-expanded="false" id="action-button">Predict <span class="caret"></span></button>
|
<div class="input-group" id="index-form-bar">
|
||||||
<ul class="dropdown-menu">
|
<div class="input-group-btn">
|
||||||
<li><a id="dropdown-predict">Predict</a></li>
|
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
|
||||||
<li><a id="dropdown-search">Search</a></li>
|
aria-expanded="false">
|
||||||
</ul>
|
<span class="caret"></span>
|
||||||
<button class="btn" style="background-color:#222222;color:#9d9d9d" type="button" id="run-button">Go!</button>
|
</button>
|
||||||
</div>
|
<ul class="dropdown-menu" role="menu">
|
||||||
</div>
|
<li>
|
||||||
|
<iframe id="index-form-ketcher" src="{% static '/js/ketcher2/ketcher.html' %}" width="100%"
|
||||||
|
height="510"></iframe>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<input type="text" class="form-control" id='index-form-text-input'
|
||||||
|
placeholder="Enter a SMILES to predict a Pathway or type something to search">
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded="false" id="action-button">Predict <span class="caret"></span></button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a id="dropdown-predict">Predict</a></li>
|
||||||
|
<li><a id="dropdown-search">Search</a></li>
|
||||||
|
</ul>
|
||||||
|
<button class="btn" style="background-color:#222222;color:#9d9d9d" type="button" id="run-button">Go!
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" id="index-form-smiles" name="smiles" value="smiles">
|
||||||
|
<input type="hidden" id="index-form-predict" name="predict" value="predict">
|
||||||
|
</form>
|
||||||
|
<div id="loading"></div>
|
||||||
|
<script language="javascript">
|
||||||
|
var currentPackage = "{{ meta.current_package.url }}";
|
||||||
|
|
||||||
<div id="loading"></div>
|
function goButtonClicked() {
|
||||||
<script language="javascript">
|
$(this).prop("disabled", true);
|
||||||
|
|
||||||
function goButtonClicked() {
|
var action = $('#action-button').text().trim();
|
||||||
$(this).prop("disabled", true);
|
|
||||||
|
|
||||||
var action = $('#action-button').text().trim();
|
var textSmiles = $('#index-form-text-input').val().trim();
|
||||||
|
|
||||||
var textSmiles = $('#index-form-text-input').val().trim();
|
if (textSmiles === '') {
|
||||||
var ketcherSmiles = getKetcher('index-form-ketcher').getSmiles().trim();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (action != 'Search' && ketcherSmiles != '' && textSmiles != ketcherSmiles) {
|
var ketcherSmiles = getKetcher('index-form-ketcher').getSmiles().trim();
|
||||||
console.log("Ketcher and TextInput differ!");
|
|
||||||
|
if (action !== 'Search' && ketcherSmiles !== '' && textSmiles !== ketcherSmiles) {
|
||||||
|
console.log("Ketcher and TextInput differ!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action === 'Search') {
|
||||||
|
var par = {};
|
||||||
|
par['search'] = textSmiles;
|
||||||
|
par['mode'] = 'text';
|
||||||
|
var queryString = $.param(par, true);
|
||||||
|
window.location.href = "/search?" + queryString;
|
||||||
|
} else {
|
||||||
|
$('#index-form-smiles').val(textSmiles);
|
||||||
|
$('#index-form').submit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action == 'Search') {
|
function actionDropdownClicked() {
|
||||||
console.log("Searching...");
|
var suffix = ' <span class="caret"></span>';
|
||||||
} else {
|
var dropdownVal = $(this).text();
|
||||||
console.log("Predicting");
|
|
||||||
|
if (dropdownVal === 'Search') {
|
||||||
|
$("#index-form").attr("action", '/search');
|
||||||
|
$("#index-form").attr("method", 'GET');
|
||||||
|
} else {
|
||||||
|
$("#index-form").attr("action", currentPackage + "/pathway");
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#action-button').html(dropdownVal + suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
function ketcherToTextInput() {
|
||||||
|
$('#index-form-text-input').val(this.ketcher.getSmiles());
|
||||||
|
}
|
||||||
|
|
||||||
function actionDropdownClicked() {
|
$(function () {
|
||||||
var suffix = ' <span class="caret"></span>';
|
// Code that should be executed once DOM is ready goes here
|
||||||
var dropdownVal = $(this).text();
|
$('#dropdown-predict').on('click', actionDropdownClicked);
|
||||||
$('#action-button').html(dropdownVal + suffix);
|
$('#dropdown-search').on('click', actionDropdownClicked);
|
||||||
}
|
|
||||||
|
|
||||||
function ketcherToTextInput() {
|
$('#run-button').on('click', goButtonClicked);
|
||||||
$('#index-form-text-input').val(this.ketcher.getSmiles());
|
|
||||||
}
|
|
||||||
|
|
||||||
$(function() {
|
// Update Ketcher Width
|
||||||
// Code that should be executed once DOM is ready goes here
|
var fullWidth = $('#index-form-bar').width();
|
||||||
$('#dropdown-predict').on('click', actionDropdownClicked);
|
$('#index-form-ketcher').width(fullWidth);
|
||||||
$('#dropdown-search').on('click', actionDropdownClicked);
|
|
||||||
|
|
||||||
$('#run-button').on('click', goButtonClicked);
|
// add a listener that gets triggered whenever the structure in ketcher has changed
|
||||||
|
$('#index-form-ketcher').on('load', function () {
|
||||||
|
const checkKetcherReady = () => {
|
||||||
|
win = this.contentWindow
|
||||||
|
if (win.ketcher && 'editor' in win.ketcher) {
|
||||||
|
win.ketcher.editor.event.change.handlers.push({
|
||||||
|
once: false,
|
||||||
|
priority: 0,
|
||||||
|
f: ketcherToTextInput,
|
||||||
|
ketcher: win.ketcher
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setTimeout(checkKetcherReady, 100);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Update Ketcher Width
|
checkKetcherReady();
|
||||||
var fullWidth = $('#index-form-bar').width();
|
});
|
||||||
$('#index-form-ketcher').width(fullWidth);
|
|
||||||
|
|
||||||
// add a listener that gets triggered whenever the structure in ketcher has changed
|
|
||||||
$('#index-form-ketcher').on('load', function() {
|
|
||||||
const checkKetcherReady = () => {
|
|
||||||
win = this.contentWindow
|
|
||||||
if (win.ketcher && 'editor' in win.ketcher) {
|
|
||||||
win.ketcher.editor.event.change.handlers.push({
|
|
||||||
once: false,
|
|
||||||
priority: 0,
|
|
||||||
f: ketcherToTextInput,
|
|
||||||
ketcher: win.ketcher
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setTimeout(checkKetcherReady, 100);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
checkKetcherReady();
|
|
||||||
});
|
});
|
||||||
|
</script>
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
189
templates/login.html
Normal file
189
templates/login.html
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
{% load static %}
|
||||||
|
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Login Modal with Blur</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<!-- Bootstrap 3.3.7 CSS -->
|
||||||
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body, html {
|
||||||
|
margin: 0;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-blur {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: url('{% static "/images/enviPy-screenshot.png" %}') no-repeat center center/cover;
|
||||||
|
filter: blur(8px);
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Optional: dim layer */
|
||||||
|
.bg-dim {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- Blurred Background -->
|
||||||
|
<div class="bg-blur"></div>
|
||||||
|
<div class="bg-dim"></div>
|
||||||
|
|
||||||
|
<!-- Trigger Button -->
|
||||||
|
<div class="center-button">
|
||||||
|
<button class="btn btn-primary btn-lg" data-toggle="modal" data-target="#signupmodal">Login / Sign Up</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bootstrap Modal -->
|
||||||
|
<div class="modal fade bs-modal-sm" id="signupmodal" tabindex="-1" role="dialog"
|
||||||
|
aria-labelledby="mySmallModalLabel"
|
||||||
|
aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-sm">
|
||||||
|
<div class="modal-content">
|
||||||
|
<br>
|
||||||
|
<div class="bs-example bs-example-tabs">
|
||||||
|
<ul id="myTab" class="nav nav-tabs">
|
||||||
|
<li class="active">
|
||||||
|
<a href="#signin" data-toggle="tab">Sign In</a>
|
||||||
|
</li>
|
||||||
|
<li class="">
|
||||||
|
<a href="#signup" data-toggle="tab">Register</a>
|
||||||
|
</li>
|
||||||
|
<li class="">
|
||||||
|
<a href="#why" data-toggle="tab">Why?</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div id="myTabContent" class="tab-content">
|
||||||
|
<div class="tab-pane fade active in" id="signin">
|
||||||
|
<form class="form-horizontal" method="post" action="{{ meta.server_url }}/user">
|
||||||
|
{% csrf_token %}
|
||||||
|
<fieldset>
|
||||||
|
<input type="hidden" name="login" id="login" value="true"/>
|
||||||
|
<div class="control-group">
|
||||||
|
<label class="control-label" for="username">Username:</label>
|
||||||
|
<div class="controls">
|
||||||
|
<input required id="username" name="username" type="text"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="username" autocomplete="username">
|
||||||
|
</div>
|
||||||
|
<label class="control-label" for="passwordinput">Password:</label>
|
||||||
|
<div class="controls">
|
||||||
|
<input required id="passwordinput" name="password" class="form-control"
|
||||||
|
type="password" placeholder="********"
|
||||||
|
autocomplete="current-password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Button -->
|
||||||
|
<div class="control-group">
|
||||||
|
<label class="control-label" for="signin"></label>
|
||||||
|
<div class="controls">
|
||||||
|
<button id="signin" name="signin" class="btn btn-success">Sign In
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Why tab -->
|
||||||
|
<div class="tab-pane fade in" id="why">
|
||||||
|
<p>After you register, you have more permissions on
|
||||||
|
this site, e.g., can create your own
|
||||||
|
packages, submit data for review, and set access
|
||||||
|
permissions to your data.</p>
|
||||||
|
<p></p>
|
||||||
|
<p>
|
||||||
|
<br> Please
|
||||||
|
contact <a href="mailto:admin@envipath.org">admin@envipath.org</a>
|
||||||
|
if you have any questions.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Register -->
|
||||||
|
<div class="tab-pane fade"
|
||||||
|
id="signup">
|
||||||
|
<div id="passwordGuideline" class="alert alert-info">
|
||||||
|
The password must contain 8 to 30 characters<br>
|
||||||
|
The following characters are allowed:
|
||||||
|
- Upper and lower case characters<br>
|
||||||
|
- Digits<br>
|
||||||
|
- Special characters _, -, +<br>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="signup-action" class="form-horizontal" action="{{ meta.server_url }}/user"
|
||||||
|
method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="register" id="register" value="true"/>
|
||||||
|
|
||||||
|
<label class="control-label" for="userid">Username:</label>
|
||||||
|
<input id="userid" name="username" class="form-control" type="text"
|
||||||
|
placeholder="user" required autocomplete="username">
|
||||||
|
|
||||||
|
<label class="control-label" for="email">Email:</label>
|
||||||
|
<input id="email" name="email" class="form-control" type="email"
|
||||||
|
placeholder="user@envipath.org" required>
|
||||||
|
|
||||||
|
<label class="control-label" for="password">Password:</label>
|
||||||
|
<input id="password" name="password" class="form-control" type="password"
|
||||||
|
placeholder="********" required autocomplete="new-password">
|
||||||
|
|
||||||
|
<label class="control-label" for="rpassword">Repeat Password:</label>
|
||||||
|
<input id="rpassword" name="rpassword" class="form-control" type="password"
|
||||||
|
placeholder="********" required autocomplete="new-password">
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<label class="control-label" for="confirmsignup"></label>
|
||||||
|
<div class="controls">
|
||||||
|
<button id="confirmsignup" name="confirmsignup" class="btn btn-success">Sign
|
||||||
|
Up
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<center>
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||||
|
</center>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bootstrap 3.3.7 JS + jQuery -->
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
|
||||||
|
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
0
templates/modals/collections/new_edge_modal.html
Normal file
0
templates/modals/collections/new_edge_modal.html
Normal file
0
templates/modals/collections/new_node_modal.html
Normal file
0
templates/modals/collections/new_node_modal.html
Normal file
@ -16,23 +16,11 @@
|
|||||||
<input id="rule-name" class="form-control" name="rule-name" placeholder="Name"/>
|
<input id="rule-name" class="form-control" name="rule-name" placeholder="Name"/>
|
||||||
<label for="rule-description">Description</label>
|
<label for="rule-description">Description</label>
|
||||||
<input id="rule-description" class="form-control" name="rule-description" placeholder="Description"/>
|
<input id="rule-description" class="form-control" name="rule-description" placeholder="Description"/>
|
||||||
|
<label for="rule-smirks">SMIRKS</label>
|
||||||
|
<input id="rule-smirks" class="form-control" name="rule-smirks" placeholder="SMIRKS"/>
|
||||||
<p></p>
|
<p></p>
|
||||||
<!-- TODO Ruletypes -->
|
<div id="rule-smirks-viz"></div>
|
||||||
<!-- TODO Decide on rules to use?-->
|
|
||||||
<input type="hidden" name="rule-type" id="rule-type" value="SimpleAmbitRule">
|
<input type="hidden" name="rule-type" id="rule-type" value="SimpleAmbitRule">
|
||||||
|
|
||||||
<!-- <select id="rule-type" name="rule-type" class="form-control" data-width='100%'>-->
|
|
||||||
<!-- <option disabled selected>Select Rule Type</option>-->
|
|
||||||
<!-- {% for k, v in rule_types.items %}-->
|
|
||||||
<!-- <option value="{{ v }}">{{ k }}</option>-->
|
|
||||||
<!-- {% endfor %}-->
|
|
||||||
<!-- </select>-->
|
|
||||||
<!-- -->
|
|
||||||
<div>
|
|
||||||
<iframe id="new_rule_ketcher" src="{% static '/js/ketcher2/ketcher.html' %}" width="100%"
|
|
||||||
height="510"></iframe>
|
|
||||||
</div>
|
|
||||||
<input type="hidden" name="rule-smirks" id="rule-smirks">
|
|
||||||
<p></p>
|
<p></p>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -47,13 +35,36 @@
|
|||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
$(function() {
|
$(function() {
|
||||||
|
|
||||||
|
$('#rule-smirks').on('input', function(e) {
|
||||||
|
$('#rule-smirks-viz').empty()
|
||||||
|
|
||||||
|
smirks = $('#rule-smirks').val()
|
||||||
|
|
||||||
|
const img = new Image();
|
||||||
|
img.src = "{% url 'depict' %}?is_query_smirks=true&smirks=" + encodeURIComponent(smirks);
|
||||||
|
img.style.width = '100%';
|
||||||
|
img.style.height = '100%';
|
||||||
|
img.style.objectFit = 'cover';
|
||||||
|
|
||||||
|
img.onload = function () {
|
||||||
|
$('#rule-smirks-viz').append(img);
|
||||||
|
};
|
||||||
|
|
||||||
|
img.onerror = function () {
|
||||||
|
error_tpl = `
|
||||||
|
<div class="alert alert-error" role="alert">
|
||||||
|
<h4 class="alert-heading">Could not render SMIRKS!</h4>
|
||||||
|
<p>Could not render SMIRKS - Have you entered a valid SMIRKS?</a>
|
||||||
|
</p>
|
||||||
|
</div>`
|
||||||
|
$('#rule-smirks-viz').append(error_tpl);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
$('#new_rule_modal_form_submit').on('click', function(e) {
|
$('#new_rule_modal_form_submit').on('click', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$(this).prop("disabled",true);
|
$(this).prop("disabled",true);
|
||||||
|
|
||||||
k = getKetcher('new_rule_ketcher');
|
|
||||||
$('#rule-smirks').val(k.getSmiles());
|
|
||||||
|
|
||||||
// submit form
|
// submit form
|
||||||
$('#new_rule_modal_form').submit();
|
$('#new_rule_modal_form').submit();
|
||||||
});
|
});
|
||||||
|
|||||||
126
templates/modals/objects/add_pathway_edge_modal.html
Normal file
126
templates/modals/objects/add_pathway_edge_modal.html
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
{% 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"
|
||||||
|
role="dialog">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
<h4 class="modal-title">Add a Reaction</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="add_pathway_edge_modal_form" accept-charset="UTF-8"
|
||||||
|
action="{% url 'package pathway edge list' meta.current_package.uuid pathway.uuid %}"
|
||||||
|
data-remote="true" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<label for="edge-name">Name</label>
|
||||||
|
<input id="edge-name" class="form-control" name="edge-name" placeholder="Name"/>
|
||||||
|
<label for="edge-description">Description</label>
|
||||||
|
<input id="edge-description" class="form-control" name="edge-description" placeholder="Description"/>
|
||||||
|
<p></p>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-5">
|
||||||
|
<legend>Substrate(s)</legend>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-2">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-5">
|
||||||
|
<legend>Product(s)</legend>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-5">
|
||||||
|
<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>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-2" style="display: flex; justify-content: center; align-items: center;">
|
||||||
|
<i class="glyphicon glyphicon-arrow-right"></i>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-5">
|
||||||
|
<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>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<p></p>
|
||||||
|
<div class="col-xs-12" id="reaction_image">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary pull-left" data-dismiss="modal">Close
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="add_pathway_edge_modal_form_submit">Submit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
function reactionImage() {
|
||||||
|
var substrates = [];
|
||||||
|
$('#add_pathway_edge_substrates option:selected').each(function () {
|
||||||
|
var smiles = $(this).data('smiles'); // read data-smiles attribute
|
||||||
|
substrates.push(smiles);
|
||||||
|
});
|
||||||
|
|
||||||
|
var products = []
|
||||||
|
$('#add_pathway_edge_products option:selected').each(function () {
|
||||||
|
var smiles = $(this).data('smiles'); // read data-smiles attribute
|
||||||
|
products.push(smiles);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (substrates.length > 0 && products.length > 0) {
|
||||||
|
reaction = substrates.join('.') + ">>" + products.join('.');
|
||||||
|
$('#reaction_image').empty();
|
||||||
|
$('#reaction_image').append(
|
||||||
|
"<img width='100%' src='{% url 'depict' %}?smirks=" + encodeURIComponent(reaction) +"'>"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
$("#add_pathway_edge_substrates").selectpicker();
|
||||||
|
$("#add_pathway_edge_products").selectpicker();
|
||||||
|
|
||||||
|
$("#add_pathway_edge_substrates").on('change', function (e) {
|
||||||
|
reactionImage();
|
||||||
|
})
|
||||||
|
|
||||||
|
$("#add_pathway_edge_products").on('change', function (e) {
|
||||||
|
reactionImage();
|
||||||
|
})
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
$('#add_pathway_edge_modal_form_submit').on('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$(this).prop("disabled", true);
|
||||||
|
|
||||||
|
|
||||||
|
// submit form
|
||||||
|
$('#add_pathway_edge_modal_form').submit();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
78
templates/modals/objects/add_pathway_node_modal.html
Normal file
78
templates/modals/objects/add_pathway_node_modal.html
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
{% load static %}
|
||||||
|
<div class="modal fade bs-modal-lg" id="add_pathway_node_modal" tabindex="-1" aria-labelledby="add_pathway_node_modal" aria-modal="true"
|
||||||
|
role="dialog">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
<h4 class="modal-title">Add a Node</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="add_pathway_node_modal_form" accept-charset="UTF-8" action="{% url 'package pathway node list' meta.current_package.uuid pathway.uuid %}" data-remote="true" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<label for="node-name">Name</label>
|
||||||
|
<input id="node-name" class="form-control" name="node-name" placeholder="Name"/>
|
||||||
|
<label for="node-description">Description</label>
|
||||||
|
<input id="node-description" class="form-control" name="node-description" placeholder="Description"/>
|
||||||
|
<label for="node-smiles">SMILES</label>
|
||||||
|
<input type="text" class="form-control" name="node-smiles" placeholder="SMILES" id="node-smiles">
|
||||||
|
<p></p>
|
||||||
|
<div>
|
||||||
|
<iframe id="add_node_ketcher" src="{% static '/js/ketcher2/ketcher.html' %}" width="100%"
|
||||||
|
height="510"></iframe>
|
||||||
|
</div>
|
||||||
|
<p></p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary pull-left" data-dismiss="modal">Close
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="add_pathway_node_modal_form_submit">Submit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
function newStructureModalketcherToNewStructureModalTextInput() {
|
||||||
|
$('#node-smiles').val(this.ketcher.getSmiles());
|
||||||
|
}
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
|
||||||
|
$('#add_node_ketcher').on('load', function() {
|
||||||
|
const checkKetcherReady = () => {
|
||||||
|
win = this.contentWindow
|
||||||
|
if (win.ketcher && 'editor' in win.ketcher) {
|
||||||
|
win.ketcher.editor.event.change.handlers.push({
|
||||||
|
once: false,
|
||||||
|
priority: 0,
|
||||||
|
f: newStructureModalketcherToNewStructureModalTextInput,
|
||||||
|
ketcher: win.ketcher
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setTimeout(checkKetcherReady, 100);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
checkKetcherReady();
|
||||||
|
})
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$('#add_pathway_node_modal_form_submit').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$(this).prop("disabled",true);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// submit form
|
||||||
|
$('#add_pathway_node_modal_form').submit();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
35
templates/modals/objects/delete_model_modal.html
Normal file
35
templates/modals/objects/delete_model_modal.html
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!-- Delete Model -->
|
||||||
|
<div id="delete_model_modal" class="modal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3 class="modal-title">Delete Model</h3>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Deletes the Model.
|
||||||
|
<form id="delete-model-modal-form" accept-charset="UTF-8" action="" data-remote="true" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" id="hidden" name="hidden" value="delete-model"/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="delete-model-modal-submit">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(function () {
|
||||||
|
|
||||||
|
$('#delete-model-modal-submit').click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$('#delete-model-modal-form').submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
35
templates/modals/objects/delete_node_modal.html
Normal file
35
templates/modals/objects/delete_node_modal.html
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!-- Delete Node -->
|
||||||
|
<div id="delete_node_modal" class="modal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3 class="modal-title">Delete Node</h3>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Deletes the Node as well as ingoing and outgoing edges.
|
||||||
|
<form id="delete-node-modal-form" accept-charset="UTF-8" action="" data-remote="true" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" id="hidden" name="hidden" value="delete-node"/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="delete-node-modal-submit">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(function () {
|
||||||
|
|
||||||
|
$('#delete-node-modal-submit').click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$('#delete-node-modal-form').submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
65
templates/modals/objects/delete_pathway_edge_modal.html
Normal file
65
templates/modals/objects/delete_pathway_edge_modal.html
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!-- Delete Edge -->
|
||||||
|
<div id="delete_pathway_edge_modal" class="modal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3 class="modal-title">Delete Edge</h3>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Deletes the Edge. Nodes referenced by this edge will remain.
|
||||||
|
<p></p>
|
||||||
|
<form id="delete-pathway-edge-modal-form" accept-charset="UTF-8" action="" data-remote="true"
|
||||||
|
method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<select id="delete_pathway_edge_edges" name="edge-url"
|
||||||
|
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>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<input type="hidden" id="hidden" name="hidden" value="delete-edge"/>
|
||||||
|
</form>
|
||||||
|
<p></p>
|
||||||
|
<div id="delete_pathway_edge_image"></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="delete-pathway-edge-modal-submit">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(function () {
|
||||||
|
$("#delete_pathway_edge_edges").selectpicker();
|
||||||
|
|
||||||
|
$("#delete_pathway_edge_edges").on('change', function (e) {
|
||||||
|
edge_url = $('#delete_pathway_edge_edges option:selected').val()
|
||||||
|
|
||||||
|
if (edge_url !== "") {
|
||||||
|
$('#delete_pathway_edge_image').empty();
|
||||||
|
$('#delete_pathway_edge_image').append(
|
||||||
|
"<img width='100%' src='" + edge_url + "?image=svg'>"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$('#delete-pathway-edge-modal-submit').click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
edge_url = $('#delete_pathway_edge_edges option:selected').val()
|
||||||
|
|
||||||
|
if (edge_url === "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#delete-pathway-edge-modal-form').attr('action', edge_url)
|
||||||
|
$('#delete-pathway-edge-modal-form').submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
35
templates/modals/objects/delete_pathway_modal.html
Normal file
35
templates/modals/objects/delete_pathway_modal.html
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!-- Delete Pathway -->
|
||||||
|
<div id="delete_pathway_modal" class="modal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3 class="modal-title">Delete Pathway</h3>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Deletes the Pathway together with all Nodes and Edges.
|
||||||
|
<form id="delete-pathway-modal-form" accept-charset="UTF-8" action="" data-remote="true" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" id="hidden" name="hidden" value="delete-pathway"/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="delete-pathway-modal-submit">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(function () {
|
||||||
|
|
||||||
|
$('#delete-pathway-modal-submit').click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$('#delete-pathway-modal-form').submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
65
templates/modals/objects/delete_pathway_node_modal.html
Normal file
65
templates/modals/objects/delete_pathway_node_modal.html
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!-- Delete Node -->
|
||||||
|
<div id="delete_pathway_node_modal" class="modal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3 class="modal-title">Delete Node</h3>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Deletes the Node. Edges having this Node as Substrate or Product will be removed as well.
|
||||||
|
<p></p>
|
||||||
|
<form id="delete-pathway-node-modal-form" accept-charset="UTF-8" action="" data-remote="true"
|
||||||
|
method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<select id="delete_pathway_node_nodes" name="node-url"
|
||||||
|
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>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<input type="hidden" id="hidden" name="hidden" value="delete-node"/>
|
||||||
|
</form>
|
||||||
|
<p></p>
|
||||||
|
<div id="delete_pathway_node_image"></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="delete-pathway-node-modal-submit">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(function () {
|
||||||
|
$("#delete_pathway_node_nodes").selectpicker();
|
||||||
|
|
||||||
|
$("#delete_pathway_node_nodes").on('change', function (e) {
|
||||||
|
node_url = $('#delete_pathway_node_nodes option:selected').val()
|
||||||
|
|
||||||
|
if (node_url !== "") {
|
||||||
|
$('#delete_pathway_node_image').empty();
|
||||||
|
$('#delete_pathway_node_image').append(
|
||||||
|
"<img width='100%' src='" + node_url + "?image=svg'>"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$('#delete-pathway-node-modal-submit').click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
node_url = $('#delete_pathway_node_nodes option:selected').val()
|
||||||
|
|
||||||
|
if (node_url === "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#delete-pathway-node-modal-form').attr('action', node_url)
|
||||||
|
$('#delete-pathway-node-modal-form').submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
35
templates/modals/objects/delete_reaction_modal.html
Normal file
35
templates/modals/objects/delete_reaction_modal.html
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!-- Delete Reaction -->
|
||||||
|
<div id="delete_reaction_modal" class="modal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3 class="modal-title">Delete Reaction</h3>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Deletes the Reaction.
|
||||||
|
<form id="delete-reaction-modal-form" accept-charset="UTF-8" action="" data-remote="true" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" id="hidden" name="hidden" value="delete-reaction"/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="delete-reaction-modal-submit">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(function () {
|
||||||
|
|
||||||
|
$('#delete-reaction-modal-submit').click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$('#delete-reaction-modal-form').submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@ -4,13 +4,13 @@
|
|||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">Create a Compound</h5>
|
<h5 class="modal-title">Edit Compound</h5>
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>Edit a Compound.</p>
|
<p>Edit Compound.</p>
|
||||||
<form id="edit-compound-modal-form" accept-charset="UTF-8" action="" data-remote="true" method="post">
|
<form id="edit-compound-modal-form" accept-charset="UTF-8" action="" data-remote="true" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
46
templates/modals/objects/edit_node_modal.html
Normal file
46
templates/modals/objects/edit_node_modal.html
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!-- Edit Node -->
|
||||||
|
<div id="edit_node_modal" class="modal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Edit Node</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Edit Node.</p>
|
||||||
|
<form id="edit-node-modal-form" accept-charset="UTF-8" action="" data-remote="true" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<p>
|
||||||
|
<label for="node-name">Name</label>
|
||||||
|
<input id="node-name" class="form-control" name="node-name" value="{{ node.name}}">
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label for="node-description">Description</label>
|
||||||
|
<input id="node-description" type="text" class="form-control"
|
||||||
|
value="{{ node.description }}"
|
||||||
|
name="node-description">
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="edit-node-modal-submit">Create</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(function() {
|
||||||
|
|
||||||
|
$('#edit-node-modal-submit').click(function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
$('#edit-node-modal-form').submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
43
templates/modals/objects/edit_pathway_modal.html
Normal file
43
templates/modals/objects/edit_pathway_modal.html
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!-- Edit Pathway -->
|
||||||
|
<div id="edit_pathway_modal" class="modal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Edit Pathway</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Edit Pathway.</p>
|
||||||
|
<form id="edit-pathway-modal-form" accept-charset="UTF-8" action="" data-remote="true" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<p>
|
||||||
|
<label for="pathway-name">Name</label>
|
||||||
|
<input id="pathway-name" class="form-control" name="pathway-name" value="{{ pathway.name }}">
|
||||||
|
</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>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="edit-pathway-modal-submit">Update</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(function() {
|
||||||
|
|
||||||
|
$('#edit-pathway-modal-submit').click(function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
$('#edit-pathway-modal-form').submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@ -10,7 +10,6 @@
|
|||||||
<h3 class="modal-title">Update Reaction</h3>
|
<h3 class="modal-title">Update Reaction</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<!-- <p>Edit a Reaction.</p>-->
|
|
||||||
<form id="edit-reaction-modal-form" accept-charset="UTF-8" action="" data-remote="true" method="post">
|
<form id="edit-reaction-modal-form" accept-charset="UTF-8" action="" data-remote="true" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@ -0,0 +1,44 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!-- Edit Rule -->
|
||||||
|
<div id="edit_rule_modal" class="modal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
<h3 class="modal-title">Update Rule</h3>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="edit-rule-modal-form" accept-charset="UTF-8" action="" data-remote="true" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<p>
|
||||||
|
<label for="rule-name">Name</label>
|
||||||
|
<input id="rule-name" class="form-control" name="rule-name" value="{{ rule.name }}">
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label for="rule-description">Description</label>
|
||||||
|
<input id="rule-description" type="text" class="form-control"
|
||||||
|
value="{{ rule.description }}" name="rule-description">
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="edit-rule-modal-submit">Update</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(function() {
|
||||||
|
|
||||||
|
$('#edit-rule-modal-submit').click(function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
$('#edit-rule-modal-form').submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|||||||
@ -70,7 +70,6 @@ $(function() {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const formData = $('#request_api_token_form').serialize();
|
const formData = $('#request_api_token_form').serialize();
|
||||||
console.log(formData)
|
|
||||||
$.post('', formData, function(response) {
|
$.post('', formData, function(response) {
|
||||||
$('#new-token-pre').text(response.raw_token);
|
$('#new-token-pre').text(response.raw_token);
|
||||||
$('#new-token').show();
|
$('#new-token').show();
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||||
{{ rule.name }}
|
{{ rule.name }}
|
||||||
<div id="actionsButton"
|
<div id="actionsButton"
|
||||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;"
|
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"
|
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||||
aria-haspopup="true" aria-expanded="false"><span
|
aria-haspopup="true" aria-expanded="false"><span
|
||||||
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
||||||
|
|||||||
@ -2,135 +2,141 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% block action_modals %}
|
{% block action_modals %}
|
||||||
{% include "modals/objects/edit_compound_modal.html" %}
|
{% include "modals/objects/edit_compound_modal.html" %}
|
||||||
{% include "modals/objects/add_structure_modal.html" %}
|
{% include "modals/objects/add_structure_modal.html" %}
|
||||||
{% include "modals/objects/delete_compound_modal.html" %}
|
{% include "modals/objects/delete_compound_modal.html" %}
|
||||||
{% endblock action_modals %}
|
{% endblock action_modals %}
|
||||||
|
|
||||||
<div class="panel-group" id="compound-detail">
|
<div class="panel-group" id="compound-detail">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||||
{{ compound.name }}
|
{{ compound.name }}
|
||||||
<div id="actionsButton"
|
<div id="actionsButton"
|
||||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;"
|
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"
|
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||||
aria-haspopup="true" aria-expanded="false"><span
|
aria-haspopup="true" aria-expanded="false"><span
|
||||||
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
||||||
style="padding-right:1em"></span></a>
|
style="padding-right:1em"></span></a>
|
||||||
<ul id="actionsList" class="dropdown-menu">
|
<ul id="actionsList" class="dropdown-menu">
|
||||||
{% block actions %}
|
{% block actions %}
|
||||||
{% include "actions/objects/compound.html" %}
|
{% include "actions/objects/compound.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<p>
|
|
||||||
The structures stored in this compound can be found at <a target="_blank" href="{{compound.url}}/structure" role="button">Compound structures >></a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Description -->
|
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
|
||||||
<h4 class="panel-title">
|
|
||||||
<a id="compound-desc-link" data-toggle="collapse" data-parent="#compound-detail"
|
|
||||||
href="#compound-desc">Description</a>
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<div id="compound-desc" class="panel-collapse collapse in">
|
|
||||||
<div class="panel-body list-group-item">
|
|
||||||
{{ compound.description }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Image -->
|
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
|
||||||
<h4 class="panel-title">
|
|
||||||
<a id="compound-image-link" data-toggle="collapse" data-parent="#compound-detail"
|
|
||||||
href="#compound-image">Image Representation</a>
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<div id="compound-image" class="panel-collapse collapse in">
|
|
||||||
<div class="panel-body list-group-item">
|
|
||||||
<div id="image-div" align="center">
|
|
||||||
{{ compound.default_structure.as_svg|safe }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="panel-body">
|
||||||
|
<p>
|
||||||
<!-- SMILES -->
|
The structures stored in this compound can be found at <a target="_blank"
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
href="{{ compound.url }}/structure"
|
||||||
<h4 class="panel-title">
|
role="button">Compound structures
|
||||||
<a id="compound-smiles-link" data-toggle="collapse" data-parent="#compound-detail"
|
>></a>
|
||||||
href="#compound-smiles">SMILES Representation</a>
|
</p>
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<div id="compound-smiles" class="panel-collapse collapse in">
|
|
||||||
<div class="panel-body list-group-item">
|
|
||||||
{{ compound.default_structure.smiles }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Canonical SMILES -->
|
<!-- Description -->
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
<h4 class="panel-title">
|
<h4 class="panel-title">
|
||||||
<a id="compound-canonical-smiles-link" data-toggle="collapse" data-parent="#compound-detail"
|
<a id="compound-desc-link" data-toggle="collapse" data-parent="#compound-detail"
|
||||||
href="#compound-canonical-smiles">Canonical SMILES Representation</a>
|
href="#compound-desc">Description</a>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
|
||||||
<div id="compound-canonical-smiles" class="panel-collapse collapse in">
|
|
||||||
<div class="panel-body list-group-item">
|
|
||||||
{{ compound.default_structure.canonical_smiles }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div id="compound-desc" class="panel-collapse collapse in">
|
||||||
|
<div class="panel-body list-group-item">
|
||||||
<!-- InChiKey -->
|
{{ compound.description }}
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
</div>
|
||||||
<h4 class="panel-title">
|
|
||||||
<a id="compound-inchi-link" data-toggle="collapse" data-parent="#compound-detail"
|
|
||||||
href="#compound-inchi">InChIKey</a>
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<div id="compound-inchi" class="panel-collapse collapse in">
|
|
||||||
<div class="panel-body list-group-item">
|
|
||||||
{{ compound.default_structure.InChIKey }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Reactions -->
|
<!-- Image -->
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
<h4 class="panel-title">
|
<h4 class="panel-title">
|
||||||
<a id="compound-reaction-link" data-toggle="collapse" data-parent="#compound-detail"
|
<a id="compound-image-link" data-toggle="collapse" data-parent="#compound-detail"
|
||||||
href="#compound-reaction">Reactions</a>
|
href="#compound-image">Image Representation</a>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div id="compound-image" class="panel-collapse collapse in">
|
||||||
|
<div class="panel-body list-group-item">
|
||||||
<!-- Pathways -->
|
<div id="image-div" align="center">
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
{{ compound.default_structure.as_svg|safe }}
|
||||||
<h4 class="panel-title">
|
</div>
|
||||||
<a id="compound-pathway-link" data-toggle="collapse" data-parent="#compound-detail"
|
</div>
|
||||||
href="#compound-pathway">Pathways</a>
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- External Identifiers -->
|
<!-- SMILES -->
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="compound-smiles-link" data-toggle="collapse" data-parent="#compound-detail"
|
||||||
|
href="#compound-smiles">SMILES Representation</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="compound-smiles" class="panel-collapse collapse in">
|
||||||
|
<div class="panel-body list-group-item">
|
||||||
|
{{ compound.default_structure.smiles }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Canonical SMILES -->
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="compound-canonical-smiles-link" data-toggle="collapse" data-parent="#compound-detail"
|
||||||
|
href="#compound-canonical-smiles">Canonical SMILES Representation</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="compound-canonical-smiles" class="panel-collapse collapse in">
|
||||||
|
<div class="panel-body list-group-item">
|
||||||
|
{{ compound.default_structure.canonical_smiles }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- InChiKey -->
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="compound-inchi-link" data-toggle="collapse" data-parent="#compound-detail"
|
||||||
|
href="#compound-inchi">InChIKey</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="compound-inchi" class="panel-collapse collapse in">
|
||||||
|
<div class="panel-body list-group-item">
|
||||||
|
{{ compound.default_structure.inchikey }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Reactions -->
|
||||||
|
{% if compound.related_reactions %}
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="compound-reaction-link" data-toggle="collapse" data-parent="#compound-detail"
|
||||||
|
href="#compound-reaction">Reactions</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Pathways -->
|
||||||
|
{% if compound.related_pathways %}
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="compound-pathway-link" data-toggle="collapse" data-parent="#compound-detail"
|
||||||
|
href="#compound-pathway">Pathways</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<!-- External Identifiers -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||||
{{ compound_structure.name }}
|
{{ compound_structure.name }}
|
||||||
<div id="actionsButton"
|
<div id="actionsButton"
|
||||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;"
|
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"
|
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||||
aria-haspopup="true" aria-expanded="false"><span
|
aria-haspopup="true" aria-expanded="false"><span
|
||||||
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
||||||
|
|||||||
0
templates/objects/edge.html
Normal file
0
templates/objects/edge.html
Normal file
@ -13,7 +13,7 @@
|
|||||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||||
{{ group.name }}
|
{{ group.name }}
|
||||||
<div id="actionsButton"
|
<div id="actionsButton"
|
||||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;"
|
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"
|
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||||
aria-haspopup="true" aria-expanded="false"><span
|
aria-haspopup="true" aria-expanded="false"><span
|
||||||
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
||||||
|
|||||||
@ -1,263 +1,322 @@
|
|||||||
{% extends "framework.html" %}
|
{% extends "framework.html" %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
{% load envipytags %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% block action_modals %}
|
{% block action_modals %}
|
||||||
{% endblock action_modals %}
|
{% include "modals/objects/delete_model_modal.html" %}
|
||||||
|
{% endblock action_modals %}
|
||||||
|
|
||||||
<!-- Include required libs -->
|
<!-- Include required libs -->
|
||||||
<script src="https://d3js.org/d3.v5.min.js"></script>
|
<script src="https://d3js.org/d3.v5.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/c3@0.7.20/c3.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/c3@0.7.20/c3.min.js"></script>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/c3@0.7.20/c3.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/c3@0.7.20/c3.min.css" rel="stylesheet">
|
||||||
|
|
||||||
<div class="panel-group" id="model-detail">
|
<div class="panel-group" id="model-detail">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||||
{{ model.name }}
|
{{ model.name }}
|
||||||
<div id="actionsButton"
|
<div id="actionsButton"
|
||||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;"
|
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"
|
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||||
aria-haspopup="true" aria-expanded="false"><span
|
aria-haspopup="true" aria-expanded="false"><span
|
||||||
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
||||||
style="padding-right:1em"></span></a>
|
style="padding-right:1em"></span></a>
|
||||||
<ul id="actionsList" class="dropdown-menu">
|
<ul id="actionsList" class="dropdown-menu">
|
||||||
{% block actions %}
|
{% block actions %}
|
||||||
{% endblock %}
|
{% include "actions/objects/model.html" %}
|
||||||
</ul>
|
{% endblock %}
|
||||||
</div>
|
</ul>
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<p> {{ model.description }} </p>
|
|
||||||
</div>
|
|
||||||
<!-- Predict Panel -->
|
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
|
||||||
<h4 class="panel-title">
|
|
||||||
<a id="predict-smiles-link" data-toggle="collapse" data-parent="#model-detail"
|
|
||||||
href="#predict-smiles">Predict</a>
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<div id="predict-smiles" class="panel-collapse collapse in">
|
|
||||||
<div class="panel-body list-group-item">
|
|
||||||
<div class="input-group">
|
|
||||||
<input id="smiles-to-predict" type="text" class="form-control" placeholder="CCN(CC)C(=O)C1=CC(=CC=C1)C">
|
|
||||||
<span class="input-group-btn">
|
|
||||||
<button class="btn btn-default" type="submit" id="predict-button">Predict!</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="loading"></div>
|
|
||||||
<div id="predictResultTable"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="panel-body">
|
||||||
<!-- End Predict Panel -->
|
<p> {{ model.description }} </p>
|
||||||
{% if model.model_status == 'FINISHED' %}
|
</div>
|
||||||
<!-- Single Gen Curve Panel -->
|
{% if model|classname == 'MLRelativeReasoning' %}
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
<!-- Rule Packages -->
|
||||||
<h4 class="panel-title">
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
<a id="sg-curve-link" data-toggle="collapse" data-parent="#model-detail"
|
<h4 class="panel-title">
|
||||||
href="#sg-curve">Predict</a>
|
<a id="rule-package-link" data-toggle="collapse" data-parent="#model-detail"
|
||||||
</h4>
|
href="#rule-package">Rule Packages</a>
|
||||||
</div>
|
</h4>
|
||||||
<div id="sg-curve" class="panel-collapse collapse in">
|
</div>
|
||||||
<div class="panel-body list-group-item">
|
<div id="rule-package" class="panel-collapse collapse in">
|
||||||
<!-- Center container contents -->
|
<div class="panel-body list-group-item">
|
||||||
<div class="container" style="display: flex;justify-content: center;">
|
{% for p in model.rule_packages.all %}
|
||||||
<div id="sg-curve-plotdiv" class="chart">
|
<a class="list-group-item" href="{{ p.url }}">{{ p.name }}</a>
|
||||||
<div id="sg-chart"></div>
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Reaction Packages -->
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="reaction-package-link" data-toggle="collapse" data-parent="#model-detail"
|
||||||
|
href="#reaction-package">Rule Packages</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if model.eval_packages.all|length > 0 %}
|
||||||
|
<!-- Eval Packages -->
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="eval-package-link" data-toggle="collapse" data-parent="#model-detail"
|
||||||
|
href="#eval-package">Rule Packages</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<!-- Model Status -->
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="model-status-link" data-toggle="collapse" data-parent="#model-detail"
|
||||||
|
href="#model-status">Model Status</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="model-status" class="panel-collapse collapse in">
|
||||||
|
<div class="panel-body list-group-item">
|
||||||
|
{{ model.status }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<!-- Predict Panel -->
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="predict-smiles-link" data-toggle="collapse" data-parent="#model-detail"
|
||||||
|
href="#predict-smiles">Predict</a>
|
||||||
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div id="predict-smiles" class="panel-collapse collapse in">
|
||||||
<script>
|
<div class="panel-body list-group-item">
|
||||||
$(function() {
|
<div class="input-group">
|
||||||
if(!($('#sg-chart').length > 0)) {
|
<input id="smiles-to-predict" type="text" class="form-control"
|
||||||
return;
|
placeholder="CCN(CC)C(=O)C1=CC(=CC=C1)C">
|
||||||
}
|
<span class="input-group-btn">
|
||||||
console.log($('#sg-chart').length)
|
<button class="btn btn-default" type="submit" id="predict-button">Predict!</button>
|
||||||
//$('#sg-curve-plotdiv').empty();
|
</span>
|
||||||
|
</div>
|
||||||
|
<div id="loading"></div>
|
||||||
|
<div id="predictResultTable"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- End Predict Panel -->
|
||||||
|
{% if model.model_status == 'FINISHED' %}
|
||||||
|
<!-- Single Gen Curve Panel -->
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="sg-curve-link" data-toggle="collapse" data-parent="#model-detail"
|
||||||
|
href="#sg-curve">Precision Recall Curve</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="sg-curve" class="panel-collapse collapse in">
|
||||||
|
<div class="panel-body list-group-item">
|
||||||
|
<!-- Center container contents -->
|
||||||
|
<div class="container" style="display: flex;justify-content: center;">
|
||||||
|
<div id="sg-curve-plotdiv" class="chart">
|
||||||
|
<div id="sg-chart"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
var x = ['Recall'];
|
</div>
|
||||||
var y = ['Precision'];
|
</div>
|
||||||
var thres = ['threshold'];
|
<script>
|
||||||
|
$(function () {
|
||||||
|
if (!($('#sg-chart').length > 0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Compare function for the given array
|
var x = ['Recall'];
|
||||||
function compare(a, b) {
|
var y = ['Precision'];
|
||||||
if (a.threshold < b.threshold)
|
var thres = ['threshold'];
|
||||||
|
|
||||||
|
// Compare function for the given array
|
||||||
|
function compare(a, b) {
|
||||||
|
if (a.threshold < b.threshold)
|
||||||
|
return -1;
|
||||||
|
else if (a.threshold > b.threshold)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIndexForValue(data, val, val_name) {
|
||||||
|
for (var idx in data) {
|
||||||
|
if (data[idx][val_name] == val) {
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
else if (a.threshold > b.threshold)
|
|
||||||
return 1;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getIndexForValue(data, val, val_name) {
|
|
||||||
for(var idx in data) {
|
|
||||||
if(data[idx][val_name] == val) {
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var data = {{ model.pr_curve|safe }}
|
var data = {{ model.pr_curve|safe }}
|
||||||
var dataLength = Object.keys(data).length;
|
var dataLength = Object.keys(data).length;
|
||||||
data.sort(compare);
|
data.sort(compare);
|
||||||
|
|
||||||
for (var idx in data) {
|
for (var idx in data) {
|
||||||
var d = data[idx];
|
var d = data[idx];
|
||||||
x.push(d.recall);
|
x.push(d.recall);
|
||||||
y.push(d.precision);
|
y.push(d.precision);
|
||||||
thres.push(d.threshold);
|
thres.push(d.threshold);
|
||||||
}
|
}
|
||||||
var chart = c3.generate({
|
var chart = c3.generate({
|
||||||
bindto: '#sg-chart',
|
bindto: '#sg-chart',
|
||||||
data: {
|
data: {
|
||||||
onclick: function (d, e) {
|
onclick: function (d, e) {
|
||||||
var idx = d.index;
|
var idx = d.index;
|
||||||
var thresh = data[dataLength-idx-1].threshold;
|
var thresh = data[dataLength - idx - 1].threshold;
|
||||||
|
|
||||||
//onclick(thresh)
|
//onclick(thresh)
|
||||||
|
|
||||||
},
|
|
||||||
x: 'Recall',
|
|
||||||
y: 'Precision',
|
|
||||||
columns: [
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
//thres
|
|
||||||
]
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
height: 400, // TODO: Make variable to current modal width
|
|
||||||
width: 480
|
|
||||||
},
|
|
||||||
axis: {
|
|
||||||
x: {
|
|
||||||
max: 1,
|
|
||||||
min: 0,
|
|
||||||
label: 'Recall',
|
|
||||||
padding: 0,
|
|
||||||
tick: {
|
|
||||||
fit: true,
|
|
||||||
values: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
max: 1,
|
|
||||||
min: 0,
|
|
||||||
label: 'Precision',
|
|
||||||
padding: 0,
|
|
||||||
tick: {
|
|
||||||
fit: true,
|
|
||||||
values: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
point: {
|
|
||||||
r: 4
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
format: {
|
|
||||||
title: function (recall) {
|
|
||||||
idx = getIndexForValue(data, recall, "recall");
|
|
||||||
if(idx != -1) {
|
|
||||||
return "Threshold: " + data[idx].threshold;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
},
|
},
|
||||||
value: function (precision, ratio, id) {
|
x: 'Recall',
|
||||||
return undefined;
|
y: 'Precision',
|
||||||
|
columns: [
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
//thres
|
||||||
|
]
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
height: 400, // TODO: Make variable to current modal width
|
||||||
|
width: 480
|
||||||
|
},
|
||||||
|
axis: {
|
||||||
|
x: {
|
||||||
|
max: 1,
|
||||||
|
min: 0,
|
||||||
|
label: 'Recall',
|
||||||
|
padding: 0,
|
||||||
|
tick: {
|
||||||
|
fit: true,
|
||||||
|
values: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
max: 1,
|
||||||
|
min: 0,
|
||||||
|
label: 'Precision',
|
||||||
|
padding: 0,
|
||||||
|
tick: {
|
||||||
|
fit: true,
|
||||||
|
values: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
point: {
|
||||||
|
r: 4
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
format: {
|
||||||
|
title: function (recall) {
|
||||||
|
idx = getIndexForValue(data, recall, "recall");
|
||||||
|
if (idx != -1) {
|
||||||
|
return "Threshold: " + data[idx].threshold;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
},
|
||||||
|
value: function (precision, ratio, id) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
zoom: {
|
||||||
|
enabled: true
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
zoom: {
|
|
||||||
enabled: true
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
</script>
|
||||||
</script>
|
<!-- End Single Gen Curve Panel -->
|
||||||
<!-- End Single Gen Curve Panel -->
|
{% endif %}
|
||||||
{% endif %}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
function handleResponse(data) {
|
function handleResponse(data) {
|
||||||
res = "<table class='table table-striped'>"
|
res = "<table class='table table-striped'>"
|
||||||
res += "<thead>"
|
res += "<thead>"
|
||||||
res += "<th scope='col'>#</th>"
|
res += "<th scope='col'>#</th>"
|
||||||
|
|
||||||
columns = ['products', 'image', 'probability', 'btrule']
|
columns = ['products', 'image', 'probability', 'btrule']
|
||||||
|
|
||||||
for(col in columns) {
|
for (col in columns) {
|
||||||
res += "<th scope='col'>" + columns[col] + "</th>"
|
res += "<th scope='col'>" + columns[col] + "</th>"
|
||||||
}
|
|
||||||
|
|
||||||
res += "</thead>"
|
|
||||||
res += "<tbody>"
|
|
||||||
var cnt = 1;
|
|
||||||
for(transformation in data) {
|
|
||||||
res += "<tr>"
|
|
||||||
res += "<th scope='row'>" + cnt + "</th>"
|
|
||||||
res += "<th scope='row'>" + data[transformation]['products'][0].join(', ') + "</th>"
|
|
||||||
res += "<th scope='row'>" + "<img width='400' src='{% url 'depict' %}?smiles=" + encodeURIComponent(data[transformation]['products'][0].join('.')) + "'></th>"
|
|
||||||
res += "<th scope='row'>" + data[transformation]['probability'].toFixed(3) + "</th>"
|
|
||||||
if (data[transformation]['btrule'] != null) {
|
|
||||||
res += "<th scope='row'>" + "<a href='" + data[transformation]['btrule']['url'] + "'>" + data[transformation]['btrule']['name'] + "</a>" + "</th>"
|
|
||||||
} else {
|
|
||||||
res += "<th scope='row'>N/A</th>"
|
|
||||||
}
|
|
||||||
res += "</tr>"
|
|
||||||
cnt += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
res += "</tbody>"
|
|
||||||
res += "</table>"
|
|
||||||
$("#predictResultTable").append(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
function clear() {
|
|
||||||
$("#predictResultTable").removeClass("alert alert-danger");
|
|
||||||
$("#predictResultTable").empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
if($('#predict-button').length > 0) {
|
|
||||||
$("#predict-button").on("click", function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
data = {
|
|
||||||
"smiles": $("#smiles-to-predict").val(),
|
|
||||||
"classify": "ILikeCats!"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clear();
|
res += "</thead>"
|
||||||
|
res += "<tbody>"
|
||||||
|
var cnt = 1;
|
||||||
|
for (transformation in data) {
|
||||||
|
res += "<tr>"
|
||||||
|
res += "<th scope='row'>" + cnt + "</th>"
|
||||||
|
res += "<th scope='row'>" + data[transformation]['products'][0].join(', ') + "</th>"
|
||||||
|
res += "<th scope='row'>" + "<img width='400' src='{% url 'depict' %}?smiles=" + encodeURIComponent(data[transformation]['products'][0].join('.')) + "'></th>"
|
||||||
|
res += "<th scope='row'>" + data[transformation]['probability'].toFixed(3) + "</th>"
|
||||||
|
if (data[transformation]['btrule'] != null) {
|
||||||
|
res += "<th scope='row'>" + "<a href='" + data[transformation]['btrule']['url'] + "'>" + data[transformation]['btrule']['name'] + "</a>" + "</th>"
|
||||||
|
} else {
|
||||||
|
res += "<th scope='row'>N/A</th>"
|
||||||
|
}
|
||||||
|
res += "</tr>"
|
||||||
|
cnt += 1;
|
||||||
|
}
|
||||||
|
|
||||||
makeLoadingGif("#loading", "{% static '/images/wait.gif' %}");
|
res += "</tbody>"
|
||||||
$.ajax({
|
res += "</table>"
|
||||||
type : 'get',
|
$("#predictResultTable").append(res);
|
||||||
data : data,
|
}
|
||||||
url : '',
|
|
||||||
success: function(data, textStatus) {
|
function clear() {
|
||||||
try {
|
$("#predictResultTable").removeClass("alert alert-danger");
|
||||||
$("#loading").empty();
|
$("#predictResultTable").empty();
|
||||||
handleResponse(data);
|
}
|
||||||
} catch (error) {
|
|
||||||
console.log("Error");
|
if ($('#predict-button').length > 0) {
|
||||||
|
$("#predict-button").on("click", function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"smiles": $("#smiles-to-predict").val(),
|
||||||
|
"classify": "ILikeCats!"
|
||||||
|
}
|
||||||
|
|
||||||
|
clear();
|
||||||
|
|
||||||
|
makeLoadingGif("#loading", "{% static '/images/wait.gif' %}");
|
||||||
|
$.ajax({
|
||||||
|
type: 'get',
|
||||||
|
data: data,
|
||||||
|
url: '',
|
||||||
|
success: function (data, textStatus) {
|
||||||
|
try {
|
||||||
|
$("#loading").empty();
|
||||||
|
handleResponse(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error");
|
||||||
|
$("#loading").empty();
|
||||||
|
$("#predictResultTable").addClass("alert alert-danger");
|
||||||
|
$("#predictResultTable").append("Error while processing request :/");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (jqXHR, textStatus, errorThrown) {
|
||||||
$("#loading").empty();
|
$("#loading").empty();
|
||||||
$("#predictResultTable").addClass("alert alert-danger");
|
$("#predictResultTable").addClass("alert alert-danger");
|
||||||
$("#predictResultTable").append("Error while processing request :/");
|
$("#predictResultTable").append("Error while processing request :/");
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
error : function(jqXHR, textStatus, errorThrown) {
|
|
||||||
$("#loading").empty();
|
|
||||||
$("#predictResultTable").addClass("alert alert-danger");
|
|
||||||
$("#predictResultTable").append("Error while processing request :/");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}
|
</script>
|
||||||
</script>
|
|
||||||
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
75
templates/objects/node.html
Normal file
75
templates/objects/node.html
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
{% extends "framework.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{% block action_modals %}
|
||||||
|
{% include "modals/objects/edit_node_modal.html" %}
|
||||||
|
{% include "modals/objects/delete_node_modal.html" %}
|
||||||
|
{% endblock action_modals %}
|
||||||
|
|
||||||
|
<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 }}
|
||||||
|
<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"
|
||||||
|
aria-haspopup="true" aria-expanded="false"><span
|
||||||
|
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
||||||
|
style="padding-right:1em"></span></a>
|
||||||
|
<ul id="actionsList" class="dropdown-menu">
|
||||||
|
{% block actions %}
|
||||||
|
{% include "actions/objects/node.html" %}
|
||||||
|
{% endblock %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
The underlying structure can be found <a href="{{ node.default_node_label.url }}">here</a>.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Description -->
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="node-desc-link" data-toggle="collapse" data-parent="#node-detail"
|
||||||
|
href="#node-desc">Description</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="node-desc" class="panel-collapse collapse in">
|
||||||
|
<div class="panel-body list-group-item">
|
||||||
|
{{ node.description }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Image -->
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="node-image-link" data-toggle="collapse" data-parent="#node-detail"
|
||||||
|
href="#node-image">Image Representation</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="node-image" class="panel-collapse collapse in">
|
||||||
|
<div class="panel-body list-group-item">
|
||||||
|
<div id="image-div" align="center">
|
||||||
|
{{ node.default_node_label.as_svg|safe }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- SMILES -->
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="node-smiles-link" data-toggle="collapse" data-parent="#node-detail"
|
||||||
|
href="#node-smiles">SMILES Representation</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="node-smiles" class="panel-collapse collapse in">
|
||||||
|
<div class="panel-body list-group-item">
|
||||||
|
{{ node.default_node_label.smiles }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||||
{{ package.name }}
|
{{ package.name }}
|
||||||
<div id="actionsButton"
|
<div id="actionsButton"
|
||||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;"
|
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"
|
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||||
aria-haspopup="true" aria-expanded="false"><span
|
aria-haspopup="true" aria-expanded="false"><span
|
||||||
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
||||||
|
|||||||
@ -1,113 +1,182 @@
|
|||||||
{% extends "framework.html" %}
|
{% extends "framework.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
<script src="https://d3js.org/d3.v7.min.js"></script>
|
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||||
<style>
|
<style>
|
||||||
svg { width: 100%; height: 600px; color: red;}
|
svg {
|
||||||
.link { stroke: #999; stroke-opacity: 0.6; marker-end: url(#arrow); }
|
width: 100%;
|
||||||
.link_no_arrow { stroke: #999; stroke-opacity: 0.6; }
|
height: 600px;
|
||||||
.node image { cursor: pointer; }
|
color: red;
|
||||||
.node circle { fill: lightblue; stroke: steelblue; stroke-width: 1.5px; }
|
}
|
||||||
.highlighted { stroke: red; stroke-width: 3px; }
|
|
||||||
.tooltip { position: absolute; background: lightgrey; padding: 5px; border-radius: 5px; visibility: hidden; opacity: 1}
|
|
||||||
</style>
|
|
||||||
<script src="{% static 'js/pw.js' %}"></script>
|
|
||||||
|
|
||||||
<p></p>
|
.link {
|
||||||
|
stroke: #999;
|
||||||
|
stroke-opacity: 0.6;
|
||||||
|
marker-end: url(#arrow);
|
||||||
|
}
|
||||||
|
|
||||||
<div class="panel-group" id="pwAccordion">
|
.link_no_arrow {
|
||||||
<div class="panel panel-default">
|
stroke: #999;
|
||||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
stroke-opacity: 0.6;
|
||||||
{{ pathway.name }}
|
}
|
||||||
</div>
|
|
||||||
</div>
|
.node image {
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
cursor: pointer;
|
||||||
<h4 class="panel-title">
|
}
|
||||||
<a id="vizLink" data-toggle="collapse" data-parent="#pwAccordion" href="#viz">
|
|
||||||
Graphical Representation
|
.node circle {
|
||||||
</a>
|
fill: lightblue;
|
||||||
</h4>
|
stroke: steelblue;
|
||||||
</div>
|
stroke-width: 1.5px;
|
||||||
<div id="viz" class="panel-collapse collapse in">
|
}
|
||||||
<nav role="navigation" class="navbar navbar-default" style="margin: 0;">
|
|
||||||
<div class="navbar-header">
|
.highlighted {
|
||||||
|
stroke: red;
|
||||||
|
stroke-width: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
position: absolute;
|
||||||
|
background: lightgrey;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 1
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="{% static 'js/pw.js' %}"></script>
|
||||||
|
|
||||||
|
{% block action_modals %}
|
||||||
|
{% include "modals/objects/add_pathway_node_modal.html" %}
|
||||||
|
{% include "modals/objects/add_pathway_edge_modal.html" %}
|
||||||
|
{% include "modals/objects/edit_pathway_modal.html" %}
|
||||||
|
{% include "modals/objects/delete_pathway_node_modal.html" %}
|
||||||
|
{% include "modals/objects/delete_pathway_edge_modal.html" %}
|
||||||
|
{% include "modals/objects/delete_pathway_modal.html" %}
|
||||||
|
{% endblock action_modals %}
|
||||||
|
|
||||||
|
<p></p>
|
||||||
|
<div id="pwcontent">
|
||||||
|
<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 }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="editbarCollapse" class="collapse navbar-collapse ">
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
<ul class="nav navbar-nav">
|
<h4 class="panel-title">
|
||||||
<li class="dropdown requiresWritePerm">
|
<a id="vizLink" data-toggle="collapse" data-parent="#pwAccordion" href="#viz">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
|
Graphical Representation
|
||||||
aria-expanded="false">
|
</a>
|
||||||
<span class="glyphicon glyphicon-edit"></span>
|
</h4>
|
||||||
Edit
|
</div>
|
||||||
<span class="caret"></span></a>
|
<div id="viz" class="panel-collapse collapse in">
|
||||||
<ul id="editingList" class="dropdown-menu">
|
<nav role="navigation" class="navbar navbar-default" style="margin: 0;">
|
||||||
<li>
|
<div class="navbar-header">
|
||||||
<a role="button" data-toggle="modal" id="showCompoundNames">
|
</div>
|
||||||
<span class="glyphicon glyphicon-eye-open"></span> Show Compound Names</a>
|
<div id="editbarCollapse" class="collapse navbar-collapse ">
|
||||||
</li>
|
<ul class="nav navbar-nav">
|
||||||
</ul>
|
<li class="dropdown requiresWritePerm">
|
||||||
</li>
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||||
</ul>
|
aria-haspopup="true"
|
||||||
|
aria-expanded="false">
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<span class="glyphicon glyphicon-edit"></span>
|
||||||
<li>
|
Edit
|
||||||
<a role="button" data-toggle="modal" onclick="goFullscreen('pwcontent')">
|
<span class="caret"></span></a>
|
||||||
<span class="glyphicon glyphicon-fullscreen"></span>Fullscreen
|
<ul id="editingList" class="dropdown-menu">
|
||||||
</a>
|
{% block actions %}
|
||||||
</li>
|
{% include "actions/objects/pathway.html" %}
|
||||||
<li>
|
{% endblock %}
|
||||||
<button type="button" class="btn btn-default navbar-btn" data-toggle="tooltip" id="status"
|
</ul>
|
||||||
data-original-title="" title="" data-content="Pathway prediction complete."><span
|
</li>
|
||||||
class="glyphicon glyphicon-ok"></span></button>
|
</ul>
|
||||||
|
|
||||||
</li>
|
<ul class="nav navbar-nav navbar-right">
|
||||||
</ul>
|
<li>
|
||||||
|
<a role="button" data-toggle="modal" onclick="goFullscreen('pwcontent')">
|
||||||
|
<span class="glyphicon glyphicon-fullscreen"></span>
|
||||||
|
Fullscreen
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
{% if pathway.completed %}
|
||||||
|
<button type="button" class="btn btn-default navbar-btn" data-toggle="tooltip"
|
||||||
|
id="status" data-original-title="" title=""
|
||||||
|
data-content="Pathway prediction complete.">
|
||||||
|
<span class="glyphicon glyphicon-ok"></span>
|
||||||
|
</button>
|
||||||
|
{% elif pathway.failed %}
|
||||||
|
<button type="button" class="btn btn-default navbar-btn" data-toggle="tooltip"
|
||||||
|
id="status" data-original-title="" title=""
|
||||||
|
data-content="Pathway prediction failed.">
|
||||||
|
<span class="glyphicon glyphicon-remove"></span>
|
||||||
|
</button>
|
||||||
|
{% else %}
|
||||||
|
<button type="button" class="btn btn-default navbar-btn" data-toggle="tooltip"
|
||||||
|
id="status" data-original-title="" title=""
|
||||||
|
data-content="Pathway prediction running.">
|
||||||
|
<img height="20" src="{% static '/images/wait.gif' %}">
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div id="vizdiv">
|
||||||
|
<svg width="2000" height="2000">
|
||||||
|
{% if debug %}
|
||||||
|
<rect width="100%" height="100%" fill="aliceblue"/>
|
||||||
|
{% endif %}
|
||||||
|
<defs>
|
||||||
|
<marker id="arrow" viewBox="0 0 10 10" refX="43" refY="5" markerWidth="6" markerHeight="6"
|
||||||
|
orient="auto-start-reverse">
|
||||||
|
<path d="M 0 0 L 10 5 L 0 10 z" fill="#999"/>
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
<g id="zoomable"></g>
|
||||||
|
</svg>
|
||||||
|
<div id="tooltip" class="tooltip"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="DescriptionLink" data-toggle="collapse" data-parent="#pathwayAccordion"
|
||||||
|
href="#Description">Description</a></h4>
|
||||||
|
</div>
|
||||||
|
<div id="Description" class="panel-collapse collapse in">
|
||||||
|
<div class="panel-body list-group-item" id="DescriptionContent">
|
||||||
|
{{ pathway.description | safe }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
|
||||||
<div id="vizdiv">
|
|
||||||
<svg width="2000" height="2000">
|
|
||||||
{% if debug %}
|
|
||||||
<rect width="100%" height="100%" fill="aliceblue"/>
|
|
||||||
{% endif %}
|
|
||||||
<defs>
|
|
||||||
<marker id="arrow" viewBox="0 0 10 10" refX="43" refY="5" markerWidth="6" markerHeight="6"
|
|
||||||
orient="auto-start-reverse">
|
|
||||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="#999"/>
|
|
||||||
</marker>
|
|
||||||
</defs>
|
|
||||||
<g id="zoomable"></g>
|
|
||||||
</svg>
|
|
||||||
<div id="tooltip" class="tooltip"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
function goFullscreen(id) {
|
||||||
<h4 class="panel-title">
|
var element = document.getElementById(id);
|
||||||
<a id="DescriptionLink" data-toggle="collapse" data-parent="#pathwayAccordion"
|
if (element.mozRequestFullScreen) {
|
||||||
href="#Description">Description</a></h4>
|
element.mozRequestFullScreen();
|
||||||
</div>
|
} else if (element.webkitRequestFullScreen) {
|
||||||
<div id="Description" class="panel-collapse collapse in">
|
element.webkitRequestFullScreen();
|
||||||
<div class="panel-body list-group-item" id="DescriptionContent">
|
}
|
||||||
{{ pathway.description | safe }}
|
}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
|
|
||||||
function transformReferences(text) {
|
function transformReferences(text) {
|
||||||
return text.replace(/\[\s*(http[^\]|]+)\s*\|\s*([^\]]+)\s*\]/g, '<a target="parent" href="$1">$2</a>');
|
return text.replace(/\[\s*(http[^\]|]+)\s*\|\s*([^\]]+)\s*\]/g, '<a target="parent" href="$1">$2</a>');
|
||||||
}
|
}
|
||||||
|
|
||||||
pathway = {{ pathway.d3_json | safe }};
|
pathway = {{ pathway.d3_json | safe }};
|
||||||
|
|
||||||
$(function() {
|
$(function () {
|
||||||
draw(pathway, 'vizdiv');
|
draw(pathway, 'vizdiv');
|
||||||
// TODO fix somewhere else...
|
// TODO fix somewhere else...
|
||||||
var newDesc = transformReferences($('#DescriptionContent')[0].innerText);
|
var newDesc = transformReferences($('#DescriptionContent')[0].innerText);
|
||||||
$('#DescriptionContent').html(newDesc);
|
$('#DescriptionContent').html(newDesc);
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@ -2,104 +2,125 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% block action_modals %}
|
{% block action_modals %}
|
||||||
{% include "modals/objects/edit_reaction_modal.html" %}
|
{% include "modals/objects/edit_reaction_modal.html" %}
|
||||||
{% endblock action_modals %}
|
{% include "modals/objects/delete_reaction_modal.html" %}
|
||||||
|
{% endblock action_modals %}
|
||||||
|
|
||||||
<div class="panel-group" id="reaction-detail">
|
<div class="panel-group" id="reaction-detail">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||||
{{ reaction.name }}
|
{{ reaction.name }}
|
||||||
<div id="actionsButton"
|
<div id="actionsButton"
|
||||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;"
|
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"
|
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||||
aria-haspopup="true" aria-expanded="false"><span
|
aria-haspopup="true" aria-expanded="false"><span
|
||||||
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
||||||
style="padding-right:1em"></span></a>
|
style="padding-right:1em"></span></a>
|
||||||
<ul id="actionsList" class="dropdown-menu">
|
<ul id="actionsList" class="dropdown-menu">
|
||||||
{% block actions %}
|
{% block actions %}
|
||||||
{% include "actions/objects/reaction.html" %}
|
{% include "actions/objects/reaction.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Description -->
|
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
|
||||||
<h4 class="panel-title">
|
|
||||||
<a id="reaction-desc-link" data-toggle="collapse" data-parent="#reaction-detail"
|
|
||||||
href="#reaction-desc">Description</a>
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<div id="reaction-desc" class="panel-collapse collapse in">
|
|
||||||
<div class="panel-body list-group-item">
|
|
||||||
{{ reaction.description }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Image -->
|
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
|
||||||
<h4 class="panel-title">
|
|
||||||
<a id="reaction-image-link" data-toggle="collapse" data-parent="#reaction-detail"
|
|
||||||
href="#reaction-image">Image Representation</a>
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<div id="reaction-image" class="panel-collapse collapse in">
|
|
||||||
<div class="panel-body list-group-item">
|
|
||||||
<div id="image-div" align="center">
|
|
||||||
{{ reaction.as_svg|safe }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Reaction Description -->
|
<!-- Description -->
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
<h4 class="panel-title">
|
<h4 class="panel-title">
|
||||||
<a id="reaction-description-link" data-toggle="collapse" data-parent="#reaction-description-detail"
|
<a id="reaction-desc-link" data-toggle="collapse" data-parent="#reaction-detail"
|
||||||
href="#reaction-description-smiles">Reaction Description</a>
|
href="#reaction-desc">Description</a>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div id="reaction-description-smiles" class="panel-collapse collapse in">
|
<div id="reaction-desc" class="panel-collapse collapse in">
|
||||||
<div class="panel-body list-group-item">
|
<div class="panel-body list-group-item">
|
||||||
{% for educt in reaction.educts.all %}
|
{{ reaction.description }}
|
||||||
<a class="btn btn-default" href="{{ educt.url }}">{{ educt.name }}</a>
|
</div>
|
||||||
{% 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>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- SMIRKS -->
|
<!-- Image -->
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
<h4 class="panel-title">
|
<h4 class="panel-title">
|
||||||
<a id="reaction-smirks-link" data-toggle="collapse" data-parent="#reaction-detail"
|
<a id="reaction-image-link" data-toggle="collapse" data-parent="#reaction-detail"
|
||||||
href="#reaction-smirks">SMIRKS Representation</a>
|
href="#reaction-image">Image Representation</a>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div id="reaction-smirks" class="panel-collapse collapse in">
|
<div id="reaction-image" class="panel-collapse collapse in">
|
||||||
<div class="panel-body list-group-item">
|
<div class="panel-body list-group-item">
|
||||||
{{ reaction.smirks }}
|
<div id="image-div" align="center">
|
||||||
|
{{ reaction.as_svg|safe }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Pathways -->
|
<!-- Reaction Description -->
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
<h4 class="panel-title">
|
<h4 class="panel-title">
|
||||||
<a id="reaction-pathway-link" data-toggle="collapse" data-parent="#reaction-detail"
|
<a id="reaction-description-link" data-toggle="collapse" data-parent="#reaction-description-detail"
|
||||||
href="#reaction-pathway">Pathways</a>
|
href="#reaction-description-smiles">Reaction Description</a>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<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>
|
||||||
|
{% 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>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- SMIRKS -->
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="reaction-smirks-link" data-toggle="collapse" data-parent="#reaction-detail"
|
||||||
|
href="#reaction-smirks">SMIRKS Representation</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="reaction-smirks" class="panel-collapse collapse in">
|
||||||
|
<div class="panel-body list-group-item">
|
||||||
|
{{ reaction.smirks }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if reaction.rules.all %}
|
||||||
|
<!-- Rules -->
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="reaction-rules-link" data-toggle="collapse" data-parent="#reaction-detail"
|
||||||
|
href="#reaction-rules">Rules</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if reaction.related_pathways %}
|
||||||
|
<!-- Pathways -->
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="reaction-pathway-link" data-toggle="collapse" data-parent="#reaction-detail"
|
||||||
|
href="#reaction-pathway">Pathways</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||||
{{ scenario.name }}
|
{{ scenario.name }}
|
||||||
<div id="actionsButton"
|
<div id="actionsButton"
|
||||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;"
|
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"
|
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||||
aria-haspopup="true" aria-expanded="false"><span
|
aria-haspopup="true" aria-expanded="false"><span
|
||||||
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
||||||
|
|||||||
@ -2,173 +2,184 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% block action_modals %}
|
{% block action_modals %}
|
||||||
{% include "modals/objects/edit_rule_modal.html" %}
|
{% include "modals/objects/edit_rule_modal.html" %}
|
||||||
{% endblock action_modals %}
|
{% endblock action_modals %}
|
||||||
|
|
||||||
<div class="panel-group" id="rule-detail">
|
<div class="panel-group" id="rule-detail">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||||
{{ rule.name }}
|
{{ rule.name }}
|
||||||
<div id="actionsButton"
|
<div id="actionsButton"
|
||||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;"
|
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"
|
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||||
aria-haspopup="true" aria-expanded="false"><span
|
aria-haspopup="true" aria-expanded="false"><span
|
||||||
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
||||||
style="padding-right:1em"></span></a>
|
style="padding-right:1em"></span></a>
|
||||||
<ul id="actionsList" class="dropdown-menu">
|
<ul id="actionsList" class="dropdown-menu">
|
||||||
{% block actions %}
|
{% block actions %}
|
||||||
{% include "actions/objects/rule.html" %}
|
{% include "actions/objects/rule.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<p>
|
|
||||||
{{ rule.description }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Representation -->
|
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
|
||||||
<h4 class="panel-title">
|
|
||||||
<a id="rule-image-link" data-toggle="collapse" data-parent="#rule-detail"
|
|
||||||
href="#rule-image">Image Representation</a>
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<div id="rule-image" class="panel-collapse collapse in">
|
|
||||||
<div class="panel-body list-group-item">
|
|
||||||
<div id="image-div" align="center">
|
|
||||||
{{ rule.as_svg|safe }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="panel-body">
|
||||||
|
<p>
|
||||||
|
{{ rule.description }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- SMIRKS -->
|
<!-- Representation -->
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
<h4 class="panel-title">
|
<h4 class="panel-title">
|
||||||
<a id="rule-smirks-link" data-toggle="collapse" data-parent="#rule-detail"
|
<a id="rule-image-link" data-toggle="collapse" data-parent="#rule-detail"
|
||||||
href="#rule-smirks">SMIRKS Representation</a>
|
href="#rule-image">Image Representation</a>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div id="rule-smirks" class="panel-collapse collapse in">
|
<div id="rule-image" class="panel-collapse collapse in">
|
||||||
<div class="panel-body list-group-item">
|
<div class="panel-body list-group-item">
|
||||||
<p> {{ rule.smirks }} </p>
|
<div id="image-div" align="center">
|
||||||
|
{{ rule.as_svg|safe }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Reactants SMARTS -->
|
<!-- SMIRKS -->
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
<h4 class="panel-title">
|
<h4 class="panel-title">
|
||||||
<a id="rule-reactants-smarts-link" data-toggle="collapse" data-parent="#rule-detail"
|
<a id="rule-smirks-link" data-toggle="collapse" data-parent="#rule-detail"
|
||||||
href="#rule-reactants-smarts">Reactant SMARTS</a>
|
href="#rule-smirks">SMIRKS Representation</a>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div id="rule-reactants-smarts" class="panel-collapse collapse in">
|
<div id="rule-smirks" class="panel-collapse collapse in">
|
||||||
<div class="panel-body list-group-item">
|
<div class="panel-body list-group-item">
|
||||||
<p> {{ rule.reactants_smarts }} </p>
|
<p> {{ rule.smirks }} </p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Reactant Filter SMARTS -->
|
<!-- Reactants SMARTS -->
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
<h4 class="panel-title">
|
<h4 class="panel-title">
|
||||||
<a id="rule-reactant-filter-smarts-link" data-toggle="collapse" data-parent="#rule-detail"
|
<a id="rule-reactants-smarts-link" data-toggle="collapse" data-parent="#rule-detail"
|
||||||
href="#rule-reactant-filter-smarts">Reactant Filter SMARTS</a>
|
href="#rule-reactants-smarts">Reactant SMARTS</a>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div id="rule-reactant-filter-smarts" class="panel-collapse collapse in">
|
<div id="rule-reactants-smarts" class="panel-collapse collapse in">
|
||||||
<div class="panel-body list-group-item">
|
<div class="panel-body list-group-item">
|
||||||
<p> {{ rule.reactant_filter_smarts }} </p>
|
<p> {{ rule.reactants_smarts }} </p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Products SMARTS -->
|
<!-- Reactant Filter SMARTS -->
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
{% if rule.reactant_filter_smarts %}
|
||||||
<h4 class="panel-title">
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
<a id="rule-products-smarts-link" data-toggle="collapse" data-parent="#rule-detail"
|
<h4 class="panel-title">
|
||||||
href="#rule-products-smarts">Reactant SMARTS</a>
|
<a id="rule-reactant-filter-smarts-link" data-toggle="collapse" data-parent="#rule-detail"
|
||||||
</h4>
|
href="#rule-reactant-filter-smarts">Reactant Filter SMARTS</a>
|
||||||
</div>
|
</h4>
|
||||||
<div id="rule-products-smarts" class="panel-collapse collapse in">
|
</div>
|
||||||
<div class="panel-body list-group-item">
|
<div id="rule-reactant-filter-smarts" class="panel-collapse collapse in">
|
||||||
<p> {{ rule.products_smarts }} </p>
|
<div class="panel-body list-group-item">
|
||||||
</div>
|
<p> {{ rule.reactant_filter_smarts }} </p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<!-- Product Filter SMARTS -->
|
<!-- Products SMARTS -->
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
<h4 class="panel-title">
|
<h4 class="panel-title">
|
||||||
<a id="rule-product-filter-smarts-link" data-toggle="collapse" data-parent="#rule-detail"
|
<a id="rule-products-smarts-link" data-toggle="collapse" data-parent="#rule-detail"
|
||||||
href="#rule-product-filter-smarts">Product Filter SMARTS</a>
|
href="#rule-products-smarts">Reactant SMARTS</a>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div id="rule-product-filter-smarts" class="panel-collapse collapse in">
|
<div id="rule-products-smarts" class="panel-collapse collapse in">
|
||||||
<div class="panel-body list-group-item">
|
<div class="panel-body list-group-item">
|
||||||
<p> {{ rule.product_filter_smarts }} </p>
|
<p> {{ rule.products_smarts }} </p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Included in Composite Rules -->
|
<!-- Product Filter SMARTS -->
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
{% if rule.product_filter_smarts %}
|
||||||
<h4 class="panel-title">
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
<a id="rule-composite-rule-link" data-toggle="collapse" data-parent="#rule-detail"
|
<h4 class="panel-title">
|
||||||
href="#rule-composite-rule">Included in Composite Rules</a>
|
<a id="rule-product-filter-smarts-link" data-toggle="collapse" data-parent="#rule-detail"
|
||||||
</h4>
|
href="#rule-product-filter-smarts">Product Filter SMARTS</a>
|
||||||
</div>
|
</h4>
|
||||||
<div id="rule-composite-rule" class="panel-collapse collapse in">
|
</div>
|
||||||
<div class="panel-body list-group-item">
|
<div id="rule-product-filter-smarts" class="panel-collapse collapse in">
|
||||||
{% for cr in rule.parallelrule_set.all %}
|
<div class="panel-body list-group-item">
|
||||||
<a class="list-group-item" href="{{ cr.url }}">{{ cr.name }}</a>
|
<p> {{ rule.product_filter_smarts }} </p>
|
||||||
{% endfor %}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{% endif %}
|
||||||
|
|
||||||
<!-- Scenarios -->
|
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
|
||||||
<h4 class="panel-title">
|
|
||||||
<a id="rule-scenario-link" data-toggle="collapse" data-parent="#rule-detail"
|
|
||||||
href="#rule-scenario">Scenarios</a>
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Reactions -->
|
<!-- Included in Composite Rules -->
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
{% if rule.parallelrule_set.all %}
|
||||||
<h4 class="panel-title">
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
<a id="rule-reaction-link" data-toggle="collapse" data-parent="#rule-detail"
|
<h4 class="panel-title">
|
||||||
href="#rule-reaction">Reactions</a>
|
<a id="rule-composite-rule-link" data-toggle="collapse" data-parent="#rule-detail"
|
||||||
</h4>
|
href="#rule-composite-rule">Included in Composite Rules</a>
|
||||||
</div>
|
</h4>
|
||||||
<div id="rule-reaction" class="panel-collapse collapse">
|
</div>
|
||||||
<div class="panel-body list-group-item">
|
<div id="rule-composite-rule" class="panel-collapse collapse in">
|
||||||
{% for r in rule.related_reactions %}
|
<div class="panel-body list-group-item">
|
||||||
<a class="list-group-item" href="{{ r.url }}">{{ r.name }}</a>
|
{% for cr in rule.parallelrule_set.all %}
|
||||||
{% endfor %}
|
<a class="list-group-item" href="{{ cr.url }}">{{ cr.name }}</a>
|
||||||
</div>
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<!-- Pathways -->
|
{% endif %}
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
|
||||||
<h4 class="panel-title">
|
|
||||||
<a id="rule-pathway-link" data-toggle="collapse" data-parent="#rule-detail"
|
|
||||||
href="#rule-pathway">Pathways</a>
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<!-- Scenarios -->
|
||||||
|
{% if rule.scenarios.all %}
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="rule-scenario-link" data-toggle="collapse" data-parent="#rule-detail"
|
||||||
|
href="#rule-scenario">Scenarios</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Reactions -->
|
||||||
|
{% if rule.related_reactions %}
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="rule-reaction-link" data-toggle="collapse" data-parent="#rule-detail"
|
||||||
|
href="#rule-reaction">Reactions</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Pathways -->
|
||||||
|
{% if rule.related_pathways %}
|
||||||
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a id="rule-pathway-link" data-toggle="collapse" data-parent="#rule-detail"
|
||||||
|
href="#rule-pathway">Pathways</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
|
||||||
{{ user.username }}
|
{{ user.username }}
|
||||||
<div id="actionsButton"
|
<div id="actionsButton"
|
||||||
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;"
|
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"
|
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||||
aria-haspopup="true" aria-expanded="false"><span
|
aria-haspopup="true" aria-expanded="false"><span
|
||||||
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
class="glyphicon glyphicon-wrench"></span> Actions <span class="caret"></span><span
|
||||||
|
|||||||
@ -1,62 +1,160 @@
|
|||||||
{% extends "framework.html" %}
|
{% extends "framework.html" %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<!--<script>-->
|
|
||||||
<!--$(document).arrive(".selPackages", function() {-->
|
|
||||||
<!-- // selectpicker triggers 'bootstrap-select' library-->
|
|
||||||
<!-- $(this).selectpicker();-->
|
|
||||||
<!--});-->
|
|
||||||
<!--</script>-->
|
|
||||||
|
|
||||||
<div id=searchContent>
|
<div id=searchContent>
|
||||||
<div id="packSelector">
|
<div id="packSelector">
|
||||||
<label>Select Packages</label><br>
|
<label>Select Packages</label><br>
|
||||||
<select id="selPackages" name="selPackages" data-actions-box='true' class="selPackages" multiple
|
<select id="selPackages" name="selPackages" data-actions-box='true' class="selPackages" multiple
|
||||||
data-width='100%'>
|
data-width='100%'>
|
||||||
{% if unreviewed_objects %}
|
{% if unreviewed_objects %}
|
||||||
<option disabled>Reviewed Packages</option>
|
<option disabled>Reviewed Packages</option>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for obj in reviewed_objects %}
|
{% for obj in reviewed_objects %}
|
||||||
<option value="{{ obj.url }}" selected>{{ obj.name }}</option>
|
<option value="{{ obj.url }}" selected>{{ obj.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if unreviewed_objects %}
|
{% if unreviewed_objects %}
|
||||||
<option disabled>Unreviewed Packages</option>
|
<option disabled>Unreviewed Packages</option>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for obj in unreviewed_objects %}
|
{% for obj in unreviewed_objects %}
|
||||||
<option value="{{ obj.url }}">{{ obj.name }}</option>
|
<option value="{{ obj.url }}">{{ obj.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<p></p>
|
<p></p>
|
||||||
<div>
|
<div>
|
||||||
<label>Search Term</label><br>
|
<label>Search Term</label><br>
|
||||||
<div class="input-group" id="index-form-bar">
|
<div class="input-group" id="index-form-bar">
|
||||||
<input type="text" class="form-control" id='index-form-text-input' placeholder="Benfuracarb">
|
<input type="text" class="form-control" id='searchbar' placeholder="Benfuracarb">
|
||||||
<div class="input-group-btn">
|
<div class="input-group-btn">
|
||||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" id="action-button"
|
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
|
||||||
aria-haspopup="true" aria-expanded="false">Text <span class="caret"></span></button>
|
id="mode-button"
|
||||||
<ul class="dropdown-menu">
|
aria-haspopup="true" aria-expanded="false">Text <span class="caret"></span></button>
|
||||||
<li class="dropdown-header">Text</li>
|
<ul class="dropdown-menu">
|
||||||
<li><a id="dropdown-predict-text-text">Text</a></li>
|
<li class="dropdown-header">Text</li>
|
||||||
<li class="dropdown-header">SMILES</li>
|
<li><a id="dropdown-predict-text-text">Text</a></li>
|
||||||
<li><a id="dropdown-search-smiles-default" data-toggle="tooltip">Default</a></li>
|
<li class="dropdown-header">SMILES</li>
|
||||||
<li><a id="dropdown-search-smiles-canonical">Canonical</a></li>
|
<li><a id="dropdown-search-smiles-default" data-toggle="tooltip">Default</a></li>
|
||||||
<li><a id="dropdown-search-smiles-exact">Exact</a></li>
|
<li><a id="dropdown-search-smiles-canonical">Canonical</a></li>
|
||||||
<li class="dropdown-header">InChI</li>
|
<li><a id="dropdown-search-smiles-exact">Exact</a></li>
|
||||||
<li><a id="dropdown-search-inchi-inchikey">InChIKey</a></li>
|
<li class="dropdown-header">InChI</li>
|
||||||
</ul>
|
<li><a id="dropdown-search-inchi-inchikey">InChIKey</a></li>
|
||||||
<button class="btn" style="background-color:#222222;color:#9d9d9d" type="button" id="run-button">Go!
|
</ul>
|
||||||
</button>
|
<button class="btn" style="background-color:#222222;color:#9d9d9d" type="button" id="search-button">
|
||||||
|
Go!
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<p></p>
|
||||||
|
<div id="results"></div>
|
||||||
|
<p></p>
|
||||||
|
<div id="loading"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="results">
|
|
||||||
</div>
|
|
||||||
<div id="loading"></div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
|
function modeDropdownClicked() {
|
||||||
|
var suffix = ' <span class="caret"></span>';
|
||||||
|
var dropdownVal = $(this).text();
|
||||||
|
$('#mode-button').html(dropdownVal + suffix);
|
||||||
|
}
|
||||||
|
|
||||||
$(function() {
|
function handleSearchResponse(id, data) {
|
||||||
|
content = `
|
||||||
|
<div class='panel-group' id='search-accordion'>
|
||||||
|
<div class='panel panel-default'>
|
||||||
|
<div class='panel-heading' id='headingPanel' style='font-size:2rem;height: 46px'>
|
||||||
|
Results
|
||||||
|
</div>
|
||||||
|
<div id='descDiv'></div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
function makeContent(objs) {
|
||||||
|
links = "";
|
||||||
|
for (idx in objs) {
|
||||||
|
obj = objs[idx];
|
||||||
|
links += `<a class='list-group-item' href='${obj.url}'>${obj.name}</a>`
|
||||||
|
}
|
||||||
|
return links;
|
||||||
|
}
|
||||||
|
|
||||||
|
allEmpty = true;
|
||||||
|
for (key in data) {
|
||||||
|
if (data[key].length < 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
allEmpty = false;
|
||||||
|
content += `
|
||||||
|
<div class='panel panel-default panel-heading list-group-item' style='background-color:silver'>
|
||||||
|
<h4 class='panel-title'>
|
||||||
|
<a id='${key}_link' data-toggle='collapse' data-parent='#search-accordion' href='#${key}_panel'>
|
||||||
|
${key}
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id='${key}_panel' class='panel-collapse collapse in'>
|
||||||
|
<div class='panel-body list-group-item'>
|
||||||
|
${makeContent(data[key])}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
if (allEmpty) {
|
||||||
|
$('#' + id).append('<div class="alert alert-danger" role="alert"><p>' + "No results..." + '</p></div>');
|
||||||
|
} else {
|
||||||
|
$('#' + id).append(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function search(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
query = $("#searchbar").val()
|
||||||
|
|
||||||
|
if (!query) {
|
||||||
|
// Nothing to search...
|
||||||
|
console.log("Search phrase empty, won't do search")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selPacks = [];
|
||||||
|
$("#selPackages :selected").each(function () {
|
||||||
|
var id = this.value;
|
||||||
|
selPacks.push(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (selPacks.length < 1) {
|
||||||
|
console.log("No package selected, won't do search")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var mode = $('#mode-button').text().trim().toLowerCase();
|
||||||
|
|
||||||
|
var par = {};
|
||||||
|
par['packages'] = selPacks;
|
||||||
|
par['search'] = query;
|
||||||
|
par['mode'] = mode;
|
||||||
|
|
||||||
|
console.log(par);
|
||||||
|
|
||||||
|
var queryString = $.param(par, true);
|
||||||
|
|
||||||
|
makeLoadingGif("#loading", "{% static '/images/wait.gif' %}");
|
||||||
|
|
||||||
|
$("#results").empty();
|
||||||
|
|
||||||
|
$.getJSON("{{ SERVER_BASE }}/search?" + queryString, function (result) {
|
||||||
|
handleSearchResponse("results", result);
|
||||||
|
$("#loading").empty();
|
||||||
|
}).fail(function (d) {
|
||||||
|
$("#loading").empty();
|
||||||
|
console.log(d.responseText);
|
||||||
|
handleError(JSON.parse(d.responseText));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
|
||||||
tooltips = {
|
tooltips = {
|
||||||
'dropdown-predict-text-text': 'The inserted pattern will be searched on all enviPath object names and descriptions',
|
'dropdown-predict-text-text': 'The inserted pattern will be searched on all enviPath object names and descriptions',
|
||||||
@ -71,44 +169,17 @@
|
|||||||
placement: "top",
|
placement: "top",
|
||||||
title: tooltips[key]
|
title: tooltips[key]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#' + key).on('click', modeDropdownClicked);
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#selPackages").selectpicker();
|
$("#selPackages").selectpicker();
|
||||||
|
$("#search-button").on("click", search);
|
||||||
|
|
||||||
$("#search-button").on("click", function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
if (!hasValue("searchbar")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var query = $("#searchbar").val();
|
|
||||||
|
|
||||||
selPackages = [];
|
|
||||||
var selPacks = $("#selPackages :selected");
|
|
||||||
selPacks.each(function () {
|
|
||||||
var id = this.value;
|
|
||||||
selPackages.push(id);
|
|
||||||
});
|
|
||||||
|
|
||||||
var par = {};
|
|
||||||
par['packages'] = selPackages;
|
|
||||||
par['search'] = query;
|
|
||||||
var queryString = $.param(par);
|
|
||||||
|
|
||||||
makeLoadingGif("#loading", "{% static '/images/wait.gif' %}");
|
|
||||||
|
|
||||||
$("#results").empty();
|
|
||||||
|
|
||||||
$.getJSON("{{ SERVER_BASE }}/search?" + queryString, function (result) {
|
|
||||||
makeSearchList("#results", result);
|
|
||||||
$("#loading").empty();
|
|
||||||
}).fail(function (d) {
|
|
||||||
handleError(JSON.parse(d.responseText));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
{% if search_result %}
|
||||||
|
handleSearchResponse("results", {{ search_result|safe }});
|
||||||
|
{% endif %}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ from epdb.models import Compound, User, CompoundStructure
|
|||||||
|
|
||||||
|
|
||||||
class CompoundTest(TestCase):
|
class CompoundTest(TestCase):
|
||||||
fixtures = ["test_fixture.json.gz"]
|
fixtures = ["test_fixture.cleaned.json"]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
pass
|
pass
|
||||||
@ -53,6 +53,14 @@ class CompoundTest(TestCase):
|
|||||||
description='No Desc'
|
description='No Desc'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
_ = Compound.create(
|
||||||
|
self.package,
|
||||||
|
smiles=' ',
|
||||||
|
name='Afoxolaner',
|
||||||
|
description='No Desc'
|
||||||
|
)
|
||||||
|
|
||||||
def test_smiles_are_trimmed(self):
|
def test_smiles_are_trimmed(self):
|
||||||
c = Compound.create(
|
c = Compound.create(
|
||||||
self.package,
|
self.package,
|
||||||
|
|||||||
196
tests/test_reaction_model.py
Normal file
196
tests/test_reaction_model.py
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from epdb.logic import PackageManager
|
||||||
|
from epdb.models import Compound, User, CompoundStructure, Reaction, Rule
|
||||||
|
|
||||||
|
|
||||||
|
class ReactionTest(TestCase):
|
||||||
|
fixtures = ["test_fixture.cleaned.json"]
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(ReactionTest, cls).setUpClass()
|
||||||
|
cls.user = User.objects.get(username='anonymous')
|
||||||
|
cls.package = PackageManager.create_package(cls.user, 'Anon Test Package', 'No Desc')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_smoke(self):
|
||||||
|
educt = Compound.create(
|
||||||
|
self.package,
|
||||||
|
smiles='C(CCl)Cl',
|
||||||
|
name='1,2-Dichloroethane',
|
||||||
|
description='Eawag BBD compound c0001'
|
||||||
|
).default_structure
|
||||||
|
|
||||||
|
product = Compound.create(
|
||||||
|
self.package,
|
||||||
|
smiles='C(CO)Cl',
|
||||||
|
name='2-Chloroethanol',
|
||||||
|
description='Eawag BBD compound c0005'
|
||||||
|
).default_structure
|
||||||
|
|
||||||
|
r = Reaction.create(
|
||||||
|
package=self.package,
|
||||||
|
name='Eawag BBD reaction r0001',
|
||||||
|
educts=[educt],
|
||||||
|
products=[product],
|
||||||
|
multi_step=False
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(r.smirks(), 'C(CCl)Cl>>C(CO)Cl')
|
||||||
|
self.assertEqual(r.name, 'Eawag BBD reaction r0001')
|
||||||
|
self.assertEqual(r.description, 'no description')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def test_string_educts_and_products(self):
|
||||||
|
r = Reaction.create(
|
||||||
|
package=self.package,
|
||||||
|
name='Eawag BBD reaction r0001',
|
||||||
|
educts=['C(CCl)Cl'],
|
||||||
|
products=['C(CO)Cl'],
|
||||||
|
multi_step=False
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(r.smirks(), 'C(CCl)Cl>>C(CO)Cl')
|
||||||
|
|
||||||
|
def test_missing_smiles(self):
|
||||||
|
educt = Compound.create(
|
||||||
|
self.package,
|
||||||
|
smiles='C(CCl)Cl',
|
||||||
|
name='1,2-Dichloroethane',
|
||||||
|
description='Eawag BBD compound c0001'
|
||||||
|
).default_structure
|
||||||
|
|
||||||
|
product = Compound.create(
|
||||||
|
self.package,
|
||||||
|
smiles='C(CO)Cl',
|
||||||
|
name='2-Chloroethanol',
|
||||||
|
description='Eawag BBD compound c0005'
|
||||||
|
).default_structure
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
_ = Reaction.create(
|
||||||
|
package=self.package,
|
||||||
|
name='Eawag BBD reaction r0001',
|
||||||
|
educts=[educt],
|
||||||
|
products=[],
|
||||||
|
multi_step=False
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
_ = Reaction.create(
|
||||||
|
package=self.package,
|
||||||
|
name='Eawag BBD reaction r0001',
|
||||||
|
educts=[],
|
||||||
|
products=[product],
|
||||||
|
multi_step=False
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
_ = Reaction.create(
|
||||||
|
package=self.package,
|
||||||
|
name='Eawag BBD reaction r0001',
|
||||||
|
educts=[],
|
||||||
|
products=[],
|
||||||
|
multi_step=False
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_empty_name_and_description_are_ignored(self):
|
||||||
|
r = Reaction.create(
|
||||||
|
package=self.package,
|
||||||
|
name='',
|
||||||
|
description='',
|
||||||
|
educts=['C(CCl)Cl'],
|
||||||
|
products=['C(CO)Cl'],
|
||||||
|
multi_step=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(r.name, 'no name')
|
||||||
|
self.assertEqual(r.description, 'no description')
|
||||||
|
|
||||||
|
def test_deduplication(self):
|
||||||
|
rule = Rule.create(
|
||||||
|
package=self.package,
|
||||||
|
rule_type='SimpleAmbitRule',
|
||||||
|
name='bt0022-2833',
|
||||||
|
description='Dihalomethyl derivative + Halomethyl derivative > 1-Halo-1-methylalcohol derivative + 1-Methylalcohol derivative',
|
||||||
|
smirks='[H:5][C:1]([#6:6])([#1,#9,#17,#35,#53:4])[#9,#17,#35,#53]>>[H:5][C:1]([#6:6])([#8])[#1,#9,#17,#35,#53:4]',
|
||||||
|
)
|
||||||
|
|
||||||
|
r1 = Reaction.create(
|
||||||
|
package=self.package,
|
||||||
|
name='Eawag BBD reaction r0001',
|
||||||
|
educts=['C(CCl)Cl'],
|
||||||
|
products=['C(CO)Cl'],
|
||||||
|
rules=[rule],
|
||||||
|
multi_step=False
|
||||||
|
)
|
||||||
|
|
||||||
|
r2 = Reaction.create(
|
||||||
|
package=self.package,
|
||||||
|
name='Eawag BBD reaction r0001',
|
||||||
|
educts=['C(CCl)Cl'],
|
||||||
|
products=['C(CO)Cl'],
|
||||||
|
rules=[rule],
|
||||||
|
multi_step=False
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if create detects that this Compound already exist
|
||||||
|
# In this case the existing object should be returned
|
||||||
|
self.assertEqual(r1.pk, r2.pk)
|
||||||
|
self.assertEqual(len(self.package.reactions), 1)
|
||||||
|
|
||||||
|
def test_deduplication_without_rules(self):
|
||||||
|
r1 = Reaction.create(
|
||||||
|
package=self.package,
|
||||||
|
name='Eawag BBD reaction r0001',
|
||||||
|
educts=['C(CCl)Cl'],
|
||||||
|
products=['C(CO)Cl'],
|
||||||
|
multi_step=False
|
||||||
|
)
|
||||||
|
|
||||||
|
r2 = Reaction.create(
|
||||||
|
package=self.package,
|
||||||
|
name='Eawag BBD reaction r0001',
|
||||||
|
educts=['C(CCl)Cl'],
|
||||||
|
products=['C(CO)Cl'],
|
||||||
|
multi_step=False
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if create detects that this Compound already exist
|
||||||
|
# In this case the existing object should be returned
|
||||||
|
self.assertEqual(r1.pk, r2.pk)
|
||||||
|
self.assertEqual(len(self.package.reactions), 1)
|
||||||
|
|
||||||
|
def test_wrong_smiles(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
_ = Reaction.create(
|
||||||
|
package=self.package,
|
||||||
|
name='Eawag BBD reaction r0001',
|
||||||
|
educts=['ASDF'],
|
||||||
|
products=['C(CO)Cl'],
|
||||||
|
multi_step=False
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_delete(self):
|
||||||
|
r = Reaction.create(
|
||||||
|
package=self.package,
|
||||||
|
name='Eawag BBD reaction r0001',
|
||||||
|
educts=['C(CCl)Cl'],
|
||||||
|
products=['C(CO)Cl'],
|
||||||
|
multi_step=False
|
||||||
|
)
|
||||||
|
|
||||||
|
r.delete()
|
||||||
|
|
||||||
|
self.assertEqual(Reaction.objects.filter(package=self.package).count(), 0)
|
||||||
116
tests/test_rule_model.py
Normal file
116
tests/test_rule_model.py
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from epdb.logic import PackageManager
|
||||||
|
from epdb.models import Rule, User
|
||||||
|
|
||||||
|
|
||||||
|
class RuleTest(TestCase):
|
||||||
|
fixtures = ["test_fixture.cleaned.json"]
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(RuleTest, cls).setUpClass()
|
||||||
|
cls.user = User.objects.get(username='anonymous')
|
||||||
|
cls.package = PackageManager.create_package(cls.user, 'Anon Test Package', 'No Desc')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_smoke(self):
|
||||||
|
r = Rule.create(
|
||||||
|
rule_type='SimpleAmbitRule',
|
||||||
|
package=self.package,
|
||||||
|
name='bt0022-2833',
|
||||||
|
description='Dihalomethyl derivative + Halomethyl derivative > 1-Halo-1-methylalcohol derivative + 1-Methylalcohol derivative',
|
||||||
|
smirks='[H:5][C:1]([#6:6])([#1,#9,#17,#35,#53:4])[#9,#17,#35,#53]>>[H:5][C:1]([#6:6])([#8])[#1,#9,#17,#35,#53:4]',
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(r.smirks,
|
||||||
|
'[H:5][C:1]([#6:6])([#1,#9,#17,#35,#53:4])[#9,#17,#35,#53]>>[H:5][C:1]([#6:6])([#8])[#1,#9,#17,#35,#53:4]')
|
||||||
|
self.assertEqual(r.name, 'bt0022-2833')
|
||||||
|
self.assertEqual(r.description,
|
||||||
|
'Dihalomethyl derivative + Halomethyl derivative > 1-Halo-1-methylalcohol derivative + 1-Methylalcohol derivative')
|
||||||
|
|
||||||
|
def test_smirks_are_trimmed(self):
|
||||||
|
r = Rule.create(
|
||||||
|
rule_type='SimpleAmbitRule',
|
||||||
|
package=self.package,
|
||||||
|
name='bt0022-2833',
|
||||||
|
description='Dihalomethyl derivative + Halomethyl derivative > 1-Halo-1-methylalcohol derivative + 1-Methylalcohol derivative',
|
||||||
|
smirks=' [H:5][C:1]([#6:6])([#1,#9,#17,#35,#53:4])[#9,#17,#35,#53]>>[H:5][C:1]([#6:6])([#8])[#1,#9,#17,#35,#53:4] ',
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(r.smirks,
|
||||||
|
'[H:5][C:1]([#6:6])([#1,#9,#17,#35,#53:4])[#9,#17,#35,#53]>>[H:5][C:1]([#6:6])([#8])[#1,#9,#17,#35,#53:4]')
|
||||||
|
|
||||||
|
def test_name_and_description_optional(self):
|
||||||
|
r = Rule.create(
|
||||||
|
rule_type='SimpleAmbitRule',
|
||||||
|
package=self.package,
|
||||||
|
smirks='[H:5][C:1]([#6:6])([#1,#9,#17,#35,#53:4])[#9,#17,#35,#53]>>[H:5][C:1]([#6:6])([#8])[#1,#9,#17,#35,#53:4]',
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertRegex(r.name, 'Rule \\d+')
|
||||||
|
self.assertEqual(r.description, 'no description')
|
||||||
|
|
||||||
|
def test_empty_name_and_description_are_ignored(self):
|
||||||
|
r = Rule.create(
|
||||||
|
rule_type='SimpleAmbitRule',
|
||||||
|
package=self.package,
|
||||||
|
smirks='[H:5][C:1]([#6:6])([#1,#9,#17,#35,#53:4])[#9,#17,#35,#53]>>[H:5][C:1]([#6:6])([#8])[#1,#9,#17,#35,#53:4]',
|
||||||
|
name='',
|
||||||
|
description='',
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertRegex(r.name, 'Rule \\d+')
|
||||||
|
self.assertEqual(r.description, 'no description')
|
||||||
|
|
||||||
|
def test_deduplication(self):
|
||||||
|
r1 = Rule.create(
|
||||||
|
rule_type='SimpleAmbitRule',
|
||||||
|
package=self.package,
|
||||||
|
smirks='[H:5][C:1]([#6:6])([#1,#9,#17,#35,#53:4])[#9,#17,#35,#53]>>[H:5][C:1]([#6:6])([#8])[#1,#9,#17,#35,#53:4]',
|
||||||
|
name='',
|
||||||
|
description='',
|
||||||
|
)
|
||||||
|
|
||||||
|
r2 = Rule.create(
|
||||||
|
rule_type='SimpleAmbitRule',
|
||||||
|
package=self.package,
|
||||||
|
smirks='[H:5][C:1]([#6:6])([#1,#9,#17,#35,#53:4])[#9,#17,#35,#53]>>[H:5][C:1]([#6:6])([#8])[#1,#9,#17,#35,#53:4]',
|
||||||
|
name='',
|
||||||
|
description='',
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(r1.pk, r2.pk)
|
||||||
|
self.assertEqual(len(self.package.rules), 1)
|
||||||
|
|
||||||
|
def test_valid_smirks(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
r = Rule.create(
|
||||||
|
rule_type='SimpleAmbitRule',
|
||||||
|
package=self.package,
|
||||||
|
smirks='This is not a valid SMIRKS',
|
||||||
|
name='',
|
||||||
|
description='',
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_delete(self):
|
||||||
|
r = Rule.create(
|
||||||
|
rule_type='SimpleAmbitRule',
|
||||||
|
package=self.package,
|
||||||
|
smirks='[H:5][C:1]([#6:6])([#1,#9,#17,#35,#53:4])[#9,#17,#35,#53]>>[H:5][C:1]([#6:6])([#8])[#1,#9,#17,#35,#53:4]',
|
||||||
|
name='',
|
||||||
|
description='',
|
||||||
|
)
|
||||||
|
|
||||||
|
r.delete()
|
||||||
|
|
||||||
|
self.assertEqual(Rule.objects.filter(package=self.package).count(), 0)
|
||||||
@ -228,6 +228,14 @@ class FormatConverter(object):
|
|||||||
#
|
#
|
||||||
# return list(res)
|
# return list(res)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_valid_smirks(smirks: str) -> bool:
|
||||||
|
try:
|
||||||
|
rdChemReactions.ReactionFromSmarts(smirks)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def apply(smiles: str, smirks: str, preprocess_smiles: bool = True, bracketize: bool = False,
|
def apply(smiles: str, smirks: str, preprocess_smiles: bool = True, bracketize: bool = False,
|
||||||
standardize: bool = True, kekulize: bool = True) -> List['ProductSet']:
|
standardize: bool = True, kekulize: bool = True) -> List['ProductSet']:
|
||||||
|
|||||||
Reference in New Issue
Block a user