forked from enviPath/enviPy
Compare commits
19 Commits
feature/no
...
beta_2025-
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a4c8d96c3 | |||
| 97d0527565 | |||
| b45c99f7d3 | |||
| b95ec98a2f | |||
| c79a1f2040 | |||
| 6e6b394289 | |||
| ec387cc12e | |||
| a7637d046a | |||
| fc8192fb0d | |||
| c3c1d4f5cf | |||
| 3308d47071 | |||
| 1267ca8ace | |||
| ec52b8872d | |||
| 579cd519d0 | |||
| 280ddc7205 | |||
| c9d6d8b024 | |||
| a1aebfa54d | |||
| 79b4b1586c | |||
| aec61151ce |
@ -260,6 +260,8 @@ CELERY_RESULT_BACKEND = 'redis://localhost:6379/1'
|
||||
CELERY_ACCEPT_CONTENT = ['json']
|
||||
CELERY_TASK_SERIALIZER = 'json'
|
||||
|
||||
MODEL_BUILDING_ENABLED = os.environ.get('MODEL_BUILDING_ENABLED', 'False') == 'True'
|
||||
APPLICABILITY_DOMAIN_ENABLED = os.environ.get('APPLICABILITY_DOMAIN_ENABLED', 'False') == 'True'
|
||||
DEFAULT_RF_MODEL_PARAMS = {
|
||||
'base_clf': RandomForestClassifier(
|
||||
n_estimators=100,
|
||||
@ -273,14 +275,14 @@ DEFAULT_RF_MODEL_PARAMS = {
|
||||
'num_chains': 10,
|
||||
}
|
||||
|
||||
DEFAULT_DT_MODEL_PARAMS = {
|
||||
DEFAULT_MODEL_PARAMS = {
|
||||
'base_clf': DecisionTreeClassifier(
|
||||
criterion='entropy',
|
||||
max_depth=3,
|
||||
min_samples_split=5,
|
||||
min_samples_leaf=5,
|
||||
# min_samples_leaf=5,
|
||||
max_features='sqrt',
|
||||
class_weight='balanced',
|
||||
# class_weight='balanced',
|
||||
random_state=42
|
||||
),
|
||||
'num_chains': 10,
|
||||
@ -312,3 +314,13 @@ if SENTRY_ENABLED:
|
||||
# see https://docs.sentry.io/platforms/python/data-management/data-collected/ for more info
|
||||
send_default_pii=True,
|
||||
)
|
||||
|
||||
# compile into digestible flags
|
||||
FLAGS = {
|
||||
'MODEL_BUILDING': MODEL_BUILDING_ENABLED,
|
||||
'CELERY': FLAG_CELERY_PRESENT,
|
||||
'PLUGINS': PLUGINS_ENABLED,
|
||||
'SENTRY': SENTRY_ENABLED,
|
||||
'ENVIFORMER': ENVIFORMER_PRESENT,
|
||||
'APPLICABILITY_DOMAIN': APPLICABILITY_DOMAIN_ENABLED,
|
||||
}
|
||||
|
||||
@ -1,40 +1,105 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import User, Group, UserPackagePermission, GroupPackagePermission, Setting, SimpleAmbitRule, Scenario
|
||||
from .models import (
|
||||
User,
|
||||
UserPackagePermission,
|
||||
Group,
|
||||
GroupPackagePermission,
|
||||
Package,
|
||||
MLRelativeReasoning,
|
||||
Compound,
|
||||
CompoundStructure,
|
||||
SimpleAmbitRule,
|
||||
ParallelRule,
|
||||
Reaction,
|
||||
Pathway,
|
||||
Node,
|
||||
Edge,
|
||||
Scenario,
|
||||
Setting
|
||||
)
|
||||
|
||||
|
||||
class UserAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class GroupAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class UserPackagePermissionAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class GroupAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class GroupPackagePermissionAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class SettingAdmin(admin.ModelAdmin):
|
||||
class EPAdmin(admin.ModelAdmin):
|
||||
search_fields = ['name', 'description']
|
||||
|
||||
|
||||
class PackageAdmin(EPAdmin):
|
||||
pass
|
||||
|
||||
class MLRelativeReasoningAdmin(EPAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class SimpleAmbitRuleAdmin(admin.ModelAdmin):
|
||||
class CompoundAdmin(EPAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class ScenarioAdmin(admin.ModelAdmin):
|
||||
class CompoundStructureAdmin(EPAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class SimpleAmbitRuleAdmin(EPAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class ParallelRuleAdmin(EPAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class ReactionAdmin(EPAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class PathwayAdmin(EPAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class NodeAdmin(EPAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class EdgeAdmin(EPAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class ScenarioAdmin(EPAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class SettingAdmin(EPAdmin):
|
||||
pass
|
||||
|
||||
|
||||
admin.site.register(User, UserAdmin)
|
||||
admin.site.register(Group, GroupAdmin)
|
||||
admin.site.register(UserPackagePermission, UserPackagePermissionAdmin)
|
||||
admin.site.register(Group, GroupAdmin)
|
||||
admin.site.register(GroupPackagePermission, GroupPackagePermissionAdmin)
|
||||
admin.site.register(Setting, SettingAdmin)
|
||||
admin.site.register(Package, PackageAdmin)
|
||||
admin.site.register(MLRelativeReasoning, MLRelativeReasoningAdmin)
|
||||
admin.site.register(Compound, CompoundAdmin)
|
||||
admin.site.register(CompoundStructure, CompoundStructureAdmin)
|
||||
admin.site.register(SimpleAmbitRule, SimpleAmbitRuleAdmin)
|
||||
admin.site.register(ParallelRule, ParallelRuleAdmin)
|
||||
admin.site.register(Reaction, ReactionAdmin)
|
||||
admin.site.register(Pathway, PathwayAdmin)
|
||||
admin.site.register(Node, NodeAdmin)
|
||||
admin.site.register(Edge, EdgeAdmin)
|
||||
admin.site.register(Setting, SettingAdmin)
|
||||
admin.site.register(Scenario, ScenarioAdmin)
|
||||
|
||||
@ -4,3 +4,6 @@ from django.apps import AppConfig
|
||||
class EPDBConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'epdb'
|
||||
|
||||
def ready(self):
|
||||
import epdb.signals # noqa: F401
|
||||
|
||||
@ -62,7 +62,7 @@ class UserManager(object):
|
||||
|
||||
@staticmethod
|
||||
def get_user_by_id(user, user_uuid: str):
|
||||
if user.uuid != user_uuid and not user.is_superuser:
|
||||
if str(user.uuid) != user_uuid and not user.is_superuser:
|
||||
raise ValueError("Getting user failed!")
|
||||
return get_user_model().objects.get(uuid=user_uuid)
|
||||
|
||||
@ -183,6 +183,25 @@ class PackageManager(object):
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def administrable(user, package):
|
||||
if UserPackagePermission.objects.filter(package=package, user=user, permission=Permission.ALL[0]).exists() or \
|
||||
GroupPackagePermission.objects.filter(package=package, group__in=GroupManager.get_groups(user), permission=Permission.ALL[0]).exists() or \
|
||||
user.is_superuser:
|
||||
return True
|
||||
return False
|
||||
|
||||
# @staticmethod
|
||||
# def get_package_permission(user: 'User', package: Union[str, 'Package']):
|
||||
# if PackageManager.administrable(user, package):
|
||||
# return Permission.ALL[0]
|
||||
# elif PackageManager.writable(user, package):
|
||||
# return Permission.WRITE[0]
|
||||
# elif PackageManager.readable(user, package):
|
||||
# return Permission.READ[0]
|
||||
# else:
|
||||
# return None
|
||||
|
||||
@staticmethod
|
||||
def has_package_permission(user: 'User', package: Union[str, 'Package'], permission: str):
|
||||
|
||||
@ -339,7 +358,7 @@ class PackageManager(object):
|
||||
|
||||
@staticmethod
|
||||
@transaction.atomic
|
||||
def import_package(data: dict, owner: User, keep_ids=False):
|
||||
def import_package(data: dict, owner: User, keep_ids=False, add_import_timestamp=True):
|
||||
from uuid import UUID, uuid4
|
||||
from datetime import datetime
|
||||
from collections import defaultdict
|
||||
@ -349,7 +368,12 @@ class PackageManager(object):
|
||||
|
||||
pack = Package()
|
||||
pack.uuid = UUID(data['id'].split('/')[-1]) if keep_ids else uuid4()
|
||||
pack.name = '{} - {}'.format(data['name'], datetime.now().strftime('%Y-%m-%d %H:%M'))
|
||||
|
||||
if add_import_timestamp:
|
||||
pack.name = '{} - {}'.format(data['name'], datetime.now().strftime('%Y-%m-%d %H:%M'))
|
||||
else:
|
||||
pack.name = data['name']
|
||||
|
||||
pack.reviewed = True if data['reviewStatus'] == 'reviewed' else False
|
||||
pack.description = data['description']
|
||||
pack.save()
|
||||
@ -890,9 +914,10 @@ class SearchManager(object):
|
||||
|
||||
class SNode(object):
|
||||
|
||||
def __init__(self, smiles: str, depth: int):
|
||||
def __init__(self, smiles: str, depth: int, app_domain_assessment: dict = None):
|
||||
self.smiles = smiles
|
||||
self.depth = depth
|
||||
self.app_domain_assessment = app_domain_assessment
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.smiles)
|
||||
@ -1035,7 +1060,7 @@ class SPathway(object):
|
||||
def depth(self):
|
||||
return max([v.depth for v in self.smiles_to_node.values()])
|
||||
|
||||
def _get_nodes_for_depth(self, depth: int):
|
||||
def _get_nodes_for_depth(self, depth: int) -> List[SNode]:
|
||||
if depth == 0:
|
||||
return self.root_nodes
|
||||
|
||||
@ -1046,7 +1071,7 @@ class SPathway(object):
|
||||
|
||||
return sorted(res, key=lambda x: x.smiles)
|
||||
|
||||
def _get_edges_for_depth(self, depth: int):
|
||||
def _get_edges_for_depth(self, depth: int) -> List[SEdge]:
|
||||
res = []
|
||||
for e in self.edges:
|
||||
for n in e.educts:
|
||||
@ -1061,7 +1086,7 @@ class SPathway(object):
|
||||
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():
|
||||
for k, v in self.snode_persist_lookup.items():
|
||||
if from_node == v:
|
||||
substrates = [k]
|
||||
break
|
||||
@ -1071,15 +1096,44 @@ class SPathway(object):
|
||||
new_tp = False
|
||||
if substrates:
|
||||
for sub in substrates:
|
||||
|
||||
if sub.app_domain_assessment is None:
|
||||
if self.prediction_setting.model:
|
||||
if self.prediction_setting.model.app_domain:
|
||||
app_domain_assessment = self.prediction_setting.model.app_domain.assess(sub.smiles)[0]
|
||||
|
||||
if self.persist is not None:
|
||||
n = self.snode_persist_lookup[sub]
|
||||
|
||||
assert n.id is not None, "Node has no id! Should have been saved already... aborting!"
|
||||
node_data = n.simple_json()
|
||||
node_data['image'] = f"{n.url}?image=svg"
|
||||
app_domain_assessment['assessment']['node'] = node_data
|
||||
|
||||
n.kv['app_domain_assessment'] = app_domain_assessment
|
||||
n.save()
|
||||
|
||||
sub.app_domain_assessment = app_domain_assessment
|
||||
|
||||
|
||||
candidates = self.prediction_setting.expand(self, sub)
|
||||
# candidates is a List of PredictionResult. The length of the List is equal to the number of rules
|
||||
for cand_set in candidates:
|
||||
if cand_set:
|
||||
new_tp = True
|
||||
# cand_set is a PredictionResult object that can consist of multiple candidate reactions
|
||||
for cand in cand_set:
|
||||
cand_nodes = []
|
||||
# candidate reactions can have multiple fragments
|
||||
for c in cand:
|
||||
if c not in self.smiles_to_node:
|
||||
self.smiles_to_node[c] = SNode(c, sub.depth + 1)
|
||||
# For new nodes do an AppDomain Assessment if an AppDomain is attached
|
||||
app_domain_assessment = None
|
||||
if self.prediction_setting.model:
|
||||
if self.prediction_setting.model.app_domain:
|
||||
app_domain_assessment = self.prediction_setting.model.app_domain.assess(c)[0]
|
||||
|
||||
self.smiles_to_node[c] = SNode(c, sub.depth + 1, app_domain_assessment)
|
||||
|
||||
node = self.smiles_to_node[c]
|
||||
cand_nodes.append(node)
|
||||
@ -1092,18 +1146,30 @@ class SPathway(object):
|
||||
if len(substrates) == 0 or from_node is not None:
|
||||
self.done = True
|
||||
|
||||
# Check if we need to write back data to database
|
||||
# Check if we need to write back data to the database
|
||||
if new_tp and self.persist:
|
||||
self._sync_to_pathway()
|
||||
# call save to update internal modified field
|
||||
# call save to update the internal modified field
|
||||
self.persist.save()
|
||||
|
||||
def _sync_to_pathway(self):
|
||||
def _sync_to_pathway(self) -> None:
|
||||
logger.info("Updating Pathway with SPathway")
|
||||
|
||||
for snode in self.smiles_to_node.values():
|
||||
if snode not in self.snode_persist_lookup:
|
||||
n = Node.create(self.persist, snode.smiles, snode.depth)
|
||||
|
||||
if snode.app_domain_assessment is not None:
|
||||
app_domain_assessment = snode.app_domain_assessment
|
||||
|
||||
assert n.id is not None, "Node has no id! Should have been saved already... aborting!"
|
||||
node_data = n.simple_json()
|
||||
node_data['image'] = f"{n.url}?image=svg"
|
||||
app_domain_assessment['assessment']['node'] = node_data
|
||||
|
||||
n.kv['app_domain_assessment'] = app_domain_assessment
|
||||
n.save()
|
||||
|
||||
self.snode_persist_lookup[snode] = n
|
||||
|
||||
for sedge in self.edges:
|
||||
@ -1125,7 +1191,6 @@ class SPathway(object):
|
||||
self.sedge_persist_lookup[sedge] = e
|
||||
|
||||
logger.info("Update done!")
|
||||
pass
|
||||
|
||||
def to_json(self):
|
||||
nodes = []
|
||||
|
||||
@ -5,7 +5,7 @@ from django.core.management.base import BaseCommand
|
||||
from django.db import transaction
|
||||
|
||||
from epdb.logic import UserManager, GroupManager, PackageManager, SettingManager
|
||||
from epdb.models import UserSettingPermission, MLRelativeReasoning, EnviFormer, Permission, User
|
||||
from epdb.models import UserSettingPermission, MLRelativeReasoning, EnviFormer, Permission, User, ExternalDatabase
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
@ -58,7 +58,7 @@ class Command(BaseCommand):
|
||||
return anon, admin, g, jebus
|
||||
|
||||
def import_package(self, data, owner):
|
||||
return PackageManager.import_package(data, owner, keep_ids=True)
|
||||
return PackageManager.import_package(data, owner, keep_ids=True, add_import_timestamp=False)
|
||||
|
||||
def create_default_setting(self, owner, packages):
|
||||
s = SettingManager.create_setting(
|
||||
@ -74,6 +74,76 @@ class Command(BaseCommand):
|
||||
|
||||
return s
|
||||
|
||||
def populate_common_external_databases(self):
|
||||
"""
|
||||
Helper function to populate common external databases.
|
||||
This can be called from a Django management command.
|
||||
"""
|
||||
databases = [
|
||||
{
|
||||
'name': 'PubChem Compound',
|
||||
'full_name': 'PubChem Compound Database',
|
||||
'description': 'Chemical database of small organic molecules',
|
||||
'base_url': 'https://pubchem.ncbi.nlm.nih.gov',
|
||||
'url_pattern': 'https://pubchem.ncbi.nlm.nih.gov/compound/{id}'
|
||||
},
|
||||
{
|
||||
'name': 'PubChem Substance',
|
||||
'full_name': 'PubChem Substance Database',
|
||||
'description': 'Database of chemical substances',
|
||||
'base_url': 'https://pubchem.ncbi.nlm.nih.gov',
|
||||
'url_pattern': 'https://pubchem.ncbi.nlm.nih.gov/substance/{id}'
|
||||
},
|
||||
{
|
||||
'name': 'ChEBI',
|
||||
'full_name': 'Chemical Entities of Biological Interest',
|
||||
'description': 'Dictionary of molecular entities',
|
||||
'base_url': 'https://www.ebi.ac.uk/chebi',
|
||||
'url_pattern': 'https://www.ebi.ac.uk/chebi/searchId.do?chebiId=CHEBI:{id}'
|
||||
},
|
||||
{
|
||||
'name': 'RHEA',
|
||||
'full_name': 'RHEA Reaction Database',
|
||||
'description': 'Comprehensive resource of biochemical reactions',
|
||||
'base_url': 'https://www.rhea-db.org',
|
||||
'url_pattern': 'https://www.rhea-db.org/rhea/{id}'
|
||||
},
|
||||
{
|
||||
'name': 'CAS',
|
||||
'full_name': 'Chemical Abstracts Service Registry',
|
||||
'description': 'Registry of chemical substances',
|
||||
'base_url': 'https://www.cas.org',
|
||||
'url_pattern': None # CAS doesn't have a free public URL pattern
|
||||
},
|
||||
{
|
||||
'name': 'KEGG Reaction',
|
||||
'full_name': 'KEGG Reaction Database',
|
||||
'description': 'Database of biochemical reactions',
|
||||
'base_url': 'https://www.genome.jp',
|
||||
'url_pattern': 'https://www.genome.jp/entry/reaction+{id}'
|
||||
},
|
||||
{
|
||||
'name': 'MetaCyc',
|
||||
'full_name': 'MetaCyc Metabolic Pathway Database',
|
||||
'description': 'Database of metabolic pathways and enzymes',
|
||||
'base_url': 'https://metacyc.org',
|
||||
'url_pattern': None
|
||||
},
|
||||
{
|
||||
'name': 'UniProt',
|
||||
'full_name': 'MetaCyc Metabolic Pathway Database',
|
||||
'description': 'UniProt is a freely accessible database of protein sequence and functional information',
|
||||
'base_url': 'https://www.uniprot.org',
|
||||
'url_pattern': 'https://www.uniprot.org/uniprotkb?query="{id}"'
|
||||
}
|
||||
]
|
||||
|
||||
for db_info in databases:
|
||||
ExternalDatabase.objects.get_or_create(
|
||||
name=db_info['name'],
|
||||
defaults=db_info
|
||||
)
|
||||
|
||||
@transaction.atomic
|
||||
def handle(self, *args, **options):
|
||||
# Create users
|
||||
@ -117,17 +187,17 @@ class Command(BaseCommand):
|
||||
|
||||
# Create RR
|
||||
ml_model = MLRelativeReasoning.create(
|
||||
pack,
|
||||
'ECC - BBD - T0.5',
|
||||
'ML Relative Reasoning',
|
||||
[mapping['EAWAG-BBD']],
|
||||
[mapping['EAWAG-BBD']],
|
||||
[],
|
||||
0.5
|
||||
package=pack,
|
||||
rule_packages=[mapping['EAWAG-BBD']],
|
||||
data_packages=[mapping['EAWAG-BBD']],
|
||||
eval_packages=[],
|
||||
threshold=0.5,
|
||||
name='ECC - BBD - T0.5',
|
||||
description='ML Relative Reasoning',
|
||||
)
|
||||
|
||||
X, y = ml_model.build_dataset()
|
||||
ml_model.build_model(X, y)
|
||||
ml_model.build_dataset()
|
||||
ml_model.build_model()
|
||||
# ml_model.evaluate_model()
|
||||
|
||||
# If available create EnviFormerModel
|
||||
|
||||
1116
epdb/models.py
1116
epdb/models.py
File diff suppressed because it is too large
Load Diff
20
epdb/signals.py
Normal file
20
epdb/signals.py
Normal file
@ -0,0 +1,20 @@
|
||||
from django.db import transaction
|
||||
from django.db.models.signals import pre_delete
|
||||
from django.dispatch import receiver
|
||||
|
||||
from epdb.models import Node, Edge
|
||||
|
||||
|
||||
@receiver(pre_delete, sender=Node)
|
||||
@transaction.atomic
|
||||
def delete_orphan_edges(sender, instance, **kwargs):
|
||||
# check if the node that is about to be deleted is the only start node
|
||||
for edge in Edge.objects.filter(start_nodes=instance):
|
||||
if edge.start_nodes.count() == 1:
|
||||
edge.delete()
|
||||
|
||||
# same for end_nodes
|
||||
for edge in Edge.objects.filter(end_nodes=instance):
|
||||
# check if the node that is about to be deleted is the only start node
|
||||
if edge.end_nodes.count() == 1:
|
||||
edge.delete()
|
||||
@ -31,8 +31,8 @@ def send_registration_mail(user_pk: int):
|
||||
@shared_task(queue='model')
|
||||
def build_model(model_pk: int):
|
||||
mod = EPModel.objects.get(id=model_pk)
|
||||
X, y = mod.build_dataset()
|
||||
mod.build_model(X, y)
|
||||
mod.build_dataset()
|
||||
mod.build_model()
|
||||
|
||||
|
||||
@shared_task(queue='model')
|
||||
@ -58,7 +58,7 @@ def predict(pw_pk: int, pred_setting_pk: int, limit: Optional[int] = None, node_
|
||||
spw.predict_step(from_depth=level)
|
||||
level += 1
|
||||
|
||||
# break in case we are in incremental model
|
||||
# break in case we are in incremental mode
|
||||
if limit != -1:
|
||||
if level >= limit:
|
||||
break
|
||||
|
||||
286
epdb/views.py
286
epdb/views.py
@ -4,6 +4,9 @@ from typing import List, Dict, Any
|
||||
|
||||
from django.conf import settings as s
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.db.models import F, Value
|
||||
from django.db.models.fields import CharField
|
||||
from django.db.models.functions import Concat
|
||||
from django.http import JsonResponse, HttpResponse, HttpResponseNotAllowed, HttpResponseBadRequest
|
||||
from django.shortcuts import render, redirect
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
@ -103,7 +106,10 @@ def login(request):
|
||||
else:
|
||||
context['message'] = "Account has been created! You'll receive a mail to activate your account shortly."
|
||||
return render(request, 'login.html', context)
|
||||
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
else:
|
||||
return HttpResponseNotAllowed(['GET', 'POST'])
|
||||
|
||||
def logout(request):
|
||||
if request.method == 'POST':
|
||||
@ -136,7 +142,7 @@ def editable(request, user):
|
||||
f"{s.SERVER_URL}/group", f"{s.SERVER_URL}/search"]:
|
||||
return True
|
||||
else:
|
||||
print(f"Unknown url: {url}")
|
||||
logger.debug(f"Unknown url: {url}")
|
||||
return False
|
||||
|
||||
|
||||
@ -158,7 +164,7 @@ def get_base_context(request, for_user=None) -> Dict[str, Any]:
|
||||
'writeable_packages': PackageManager.get_all_writeable_packages(current_user),
|
||||
'available_groups': GroupManager.get_groups(current_user),
|
||||
'available_settings': SettingManager.get_all_settings(current_user),
|
||||
'enabled_features': [],
|
||||
'enabled_features': s.FLAGS,
|
||||
'debug': s.DEBUG,
|
||||
},
|
||||
}
|
||||
@ -196,6 +202,15 @@ def breadcrumbs(first_level_object=None, second_level_namespace=None, second_lev
|
||||
return bread
|
||||
|
||||
|
||||
def set_scenarios(current_user, attach_object, scenario_urls: List[str]):
|
||||
scens = []
|
||||
for scenario_url in scenario_urls:
|
||||
package = PackageManager.get_package_by_url(current_user, scenario_url)
|
||||
scen = Scenario.objects.get(package=package, uuid=scenario_url.split('/')[-1])
|
||||
scens.append(scen)
|
||||
|
||||
attach_object.set_scenarios(scens)
|
||||
|
||||
def index(request):
|
||||
context = get_base_context(request)
|
||||
context['title'] = 'enviPath - Home'
|
||||
@ -424,8 +439,16 @@ def scenarios(request):
|
||||
if request.GET.get('all'):
|
||||
return JsonResponse({
|
||||
"objects": [
|
||||
{"name": pw.name, "url": pw.url, "reviewed": True}
|
||||
for pw in reviewed_scenario_qs
|
||||
{"name": s.name, "url": s.full_url, "reviewed": True}
|
||||
for s in reviewed_scenario_qs.annotate(
|
||||
full_url=Concat(
|
||||
Value(s.SERVER_URL + '/package/'),
|
||||
F("package__uuid"),
|
||||
Value("/scenario/"),
|
||||
F("uuid"),
|
||||
output_field=CharField(),
|
||||
)
|
||||
)
|
||||
]
|
||||
})
|
||||
|
||||
@ -505,6 +528,7 @@ def search(request):
|
||||
packages = PackageManager.get_reviewed_packages()
|
||||
|
||||
search_result = SearchManager.search(packages, searchterm, mode)
|
||||
|
||||
return JsonResponse(search_result, safe=False)
|
||||
|
||||
context = get_base_context(request)
|
||||
@ -530,6 +554,7 @@ def search(request):
|
||||
packages = PackageManager.get_reviewed_packages()
|
||||
|
||||
context['search_result'] = SearchManager.search(packages, searchterm, mode)
|
||||
context['search_result']['searchterm'] = searchterm
|
||||
|
||||
return render(request, 'search.html', context)
|
||||
|
||||
@ -572,15 +597,21 @@ def package_models(request, package_uuid):
|
||||
context['model_types'] = {
|
||||
'ML Relative Reasoning': 'ml-relative-reasoning',
|
||||
'Rule Based Relative Reasoning': 'rule-based-relative-reasoning',
|
||||
'EnviFormer': 'enviformer',
|
||||
}
|
||||
|
||||
for k, v in s.CLASSIFIER_PLUGINS.items():
|
||||
context['model_types'][v.display()] = k
|
||||
if s.FLAGS.get('ENVIFORMER', False):
|
||||
context['model_types']['EnviFormer'] = 'enviformer'
|
||||
|
||||
if s.FLAGS.get('PLUGINS', False):
|
||||
for k, v in s.CLASSIFIER_PLUGINS.items():
|
||||
context['model_types'][v.display()] = k
|
||||
|
||||
return render(request, 'collections/objects_list.html', context)
|
||||
|
||||
elif request.method == 'POST':
|
||||
|
||||
log_post_params(request)
|
||||
|
||||
name = request.POST.get('model-name')
|
||||
description = request.POST.get('model-description')
|
||||
|
||||
@ -603,14 +634,25 @@ def package_models(request, package_uuid):
|
||||
data_package_objs = [PackageManager.get_package_by_url(current_user, p) for p in data_packages]
|
||||
eval_packages_objs = [PackageManager.get_package_by_url(current_user, p) for p in eval_packages]
|
||||
|
||||
# App Domain related parameters
|
||||
build_ad = request.POST.get('build-app-domain', False) == 'on'
|
||||
num_neighbors = request.POST.get('num-neighbors', 5)
|
||||
reliability_threshold = request.POST.get('reliability-threshold', 0.5)
|
||||
local_compatibility_threshold = request.POST.get('local-compatibility-threshold', 0.5)
|
||||
|
||||
mod = MLRelativeReasoning.create(
|
||||
current_package,
|
||||
name,
|
||||
description,
|
||||
rule_package_objs,
|
||||
data_package_objs,
|
||||
eval_packages_objs,
|
||||
threshold
|
||||
package=current_package,
|
||||
name=name,
|
||||
description=description,
|
||||
rule_packages=rule_package_objs,
|
||||
data_packages=data_package_objs,
|
||||
eval_packages=eval_packages_objs,
|
||||
threshold=threshold,
|
||||
# fingerprinter=fingerprinter,
|
||||
build_app_domain=build_ad,
|
||||
app_domain_num_neighbours=num_neighbors,
|
||||
app_domain_reliability_threshold=reliability_threshold,
|
||||
app_domain_local_compatibility_threshold=local_compatibility_threshold,
|
||||
)
|
||||
|
||||
from .tasks import build_model
|
||||
@ -646,7 +688,7 @@ def package_model(request, package_uuid, model_uuid):
|
||||
if len(pr) > 0:
|
||||
products = []
|
||||
for prod_set in pr.product_sets:
|
||||
print(f"Checking {prod_set}")
|
||||
logger.debug(f"Checking {prod_set}")
|
||||
products.append(tuple([x for x in prod_set]))
|
||||
|
||||
res.append({
|
||||
@ -657,6 +699,12 @@ def package_model(request, package_uuid, model_uuid):
|
||||
|
||||
return JsonResponse(res, safe=False)
|
||||
|
||||
elif request.GET.get('app-domain-assessment', False):
|
||||
smiles = request.GET['smiles']
|
||||
stand_smiles = FormatConverter.standardize(smiles)
|
||||
app_domain_assessment = current_model.app_domain.assess(stand_smiles)[0]
|
||||
return JsonResponse(app_domain_assessment, safe=False)
|
||||
|
||||
context = get_base_context(request)
|
||||
context['title'] = f'enviPath - {current_package.name} - {current_model.name}'
|
||||
|
||||
@ -665,12 +713,13 @@ def package_model(request, package_uuid, model_uuid):
|
||||
context['breadcrumbs'] = breadcrumbs(current_package, 'model', current_model)
|
||||
|
||||
context['model'] = current_model
|
||||
|
||||
context['current_object'] = current_model
|
||||
|
||||
return render(request, 'objects/model.html', context)
|
||||
|
||||
elif request.method == 'POST':
|
||||
if hidden := request.POST.get('hidden', None):
|
||||
if hidden == 'delete-model':
|
||||
if hidden == 'delete':
|
||||
current_model.delete()
|
||||
return redirect(current_package.url + '/model')
|
||||
else:
|
||||
@ -696,8 +745,6 @@ def package(request, package_uuid):
|
||||
context['breadcrumbs'] = breadcrumbs(current_package)
|
||||
|
||||
context['package'] = current_package
|
||||
# context['package_group'] = GroupPackagePermission.objects.filter(package=current_package,
|
||||
# permission=GroupPackagePermission.ALL)
|
||||
|
||||
user_perms = UserPackagePermission.objects.filter(package=current_package)
|
||||
users = get_user_model().objects.exclude(
|
||||
@ -716,14 +763,16 @@ def package(request, package_uuid):
|
||||
|
||||
elif request.method == 'POST':
|
||||
|
||||
if s.DEBUG:
|
||||
for k, v in request.POST.items():
|
||||
logger.debug(f"{k}\t{v}")
|
||||
log_post_params(request)
|
||||
|
||||
if hidden := request.POST.get('hidden', None):
|
||||
if hidden == 'delete-package':
|
||||
if hidden == 'delete':
|
||||
logger.debug(current_package.delete())
|
||||
return redirect(s.SERVER_URL + '/package')
|
||||
elif hidden == 'publish-package':
|
||||
for g in Group.objects.filter(public=True):
|
||||
PackageManager.update_permissions(current_user, current_package, g, Permission.READ[0])
|
||||
return redirect(current_package.url)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
@ -855,17 +904,24 @@ def package_compound(request, package_uuid, compound_uuid):
|
||||
context['breadcrumbs'] = breadcrumbs(current_package, 'compound', current_compound)
|
||||
|
||||
context['compound'] = current_compound
|
||||
context['current_object'] = current_compound
|
||||
|
||||
return render(request, 'objects/compound.html', context)
|
||||
|
||||
elif request.method == 'POST':
|
||||
if hidden := request.POST.get('hidden', None):
|
||||
if hidden == 'delete-compound':
|
||||
if hidden == 'delete':
|
||||
current_compound.delete()
|
||||
return redirect(current_package.url + '/compound')
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
selected_scenarios = request.POST.getlist('selected-scenarios')
|
||||
|
||||
if selected_scenarios:
|
||||
set_scenarios(current_user, current_compound, selected_scenarios)
|
||||
return redirect(current_compound.url)
|
||||
|
||||
new_compound_name = request.POST.get('compound-name')
|
||||
new_compound_description = request.POST.get('compound-description')
|
||||
|
||||
@ -897,6 +953,7 @@ def package_compound_structures(request, package_uuid, compound_uuid):
|
||||
|
||||
context['meta']['current_package'] = current_package
|
||||
context['object_type'] = 'structure'
|
||||
context['breadcrumbs'] = breadcrumbs(current_package, 'compound', current_compound, 'structure')
|
||||
|
||||
reviewed_compound_structure_qs = CompoundStructure.objects.none()
|
||||
unreviewed_compound_structure_qs = CompoundStructure.objects.none()
|
||||
@ -936,12 +993,45 @@ def package_compound_structure(request, package_uuid, compound_uuid, structure_u
|
||||
context['title'] = f'enviPath - {current_package.name} - {current_compound.name} - {current_structure.name}'
|
||||
|
||||
context['meta']['current_package'] = current_package
|
||||
context['object_type'] = 'compound'
|
||||
context['object_type'] = 'structure'
|
||||
|
||||
context['compound_structure'] = current_structure
|
||||
context['current_object'] = current_structure
|
||||
context['breadcrumbs'] = breadcrumbs(current_package, 'compound', current_compound, 'structure', current_structure)
|
||||
|
||||
return render(request, 'objects/compound_structure.html', context)
|
||||
|
||||
elif request.method == 'POST':
|
||||
|
||||
if hidden := request.POST.get('hidden', None):
|
||||
if hidden == 'delete':
|
||||
# Check if we have to delete the compound as no structure is left
|
||||
if len(current_structure.compound.structures.all()) == 1:
|
||||
# This will delete the structure as well
|
||||
current_compound.delete()
|
||||
return redirect(current_package.url + '/compound')
|
||||
else:
|
||||
if current_structure.normalized_structure:
|
||||
current_compound.delete()
|
||||
return redirect(current_package.url + '/compound')
|
||||
else:
|
||||
if current_compound.default_structure == current_structure:
|
||||
current_structure.delete()
|
||||
current_compound.default_structure = current_compound.structures.all().first()
|
||||
return redirect(current_compound.url + '/structure')
|
||||
else:
|
||||
current_structure.delete()
|
||||
return redirect(current_compound.url + '/structure')
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
selected_scenarios = request.POST.getlist('selected-scenarios')
|
||||
|
||||
if selected_scenarios:
|
||||
set_scenarios(current_user, current_structure, selected_scenarios)
|
||||
return redirect(current_structure.url)
|
||||
|
||||
return HttpResponseBadRequest()
|
||||
else:
|
||||
return HttpResponseNotAllowed(['GET', ])
|
||||
|
||||
@ -1022,6 +1112,24 @@ def package_rule(request, package_uuid, rule_uuid):
|
||||
|
||||
if request.method == 'GET':
|
||||
context = get_base_context(request)
|
||||
|
||||
if smiles := request.GET.get('smiles', False):
|
||||
stand_smiles = FormatConverter.standardize(smiles)
|
||||
res = current_rule.apply(stand_smiles)
|
||||
if len(res) > 1:
|
||||
logger.info(f"Rule {current_rule.uuid} returned multiple product sets on {smiles}, picking the first one.")
|
||||
|
||||
smirks = f"{stand_smiles}>>{'.'.join(sorted(res[0]))}"
|
||||
# Usually the functional groups are a mapping of fg -> count
|
||||
# As we are doing it on the fly here fake a high count to ensure that its properly highlighted
|
||||
educt_functional_groups = {x: 1000 for x in current_rule.reactants_smarts}
|
||||
product_functional_groups = {x: 1000 for x in current_rule.products_smarts}
|
||||
return HttpResponse(
|
||||
IndigoUtils.smirks_to_svg(smirks, False, 0, 0,
|
||||
educt_functional_groups=educt_functional_groups,
|
||||
product_functional_groups=product_functional_groups),
|
||||
content_type='image/svg+xml')
|
||||
|
||||
context['title'] = f'enviPath - {current_package.name} - {current_rule.name}'
|
||||
|
||||
context['meta']['current_package'] = current_package
|
||||
@ -1029,6 +1137,8 @@ def package_rule(request, package_uuid, rule_uuid):
|
||||
context['breadcrumbs'] = breadcrumbs(current_package, 'rule', current_rule)
|
||||
|
||||
context['rule'] = current_rule
|
||||
context['current_object'] = current_rule
|
||||
|
||||
if isinstance(current_rule, SimpleAmbitRule):
|
||||
return render(request, 'objects/simple_rule.html', context)
|
||||
else: # isinstance(current_rule, ParallelRule) or isinstance(current_rule, SequentialRule):
|
||||
@ -1036,12 +1146,18 @@ def package_rule(request, package_uuid, rule_uuid):
|
||||
|
||||
elif request.method == 'POST':
|
||||
if hidden := request.POST.get('hidden', None):
|
||||
if hidden == 'delete-rule':
|
||||
if hidden == 'delete':
|
||||
current_rule.delete()
|
||||
return redirect(current_package.url + '/rule')
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
selected_scenarios = request.POST.getlist('selected-scenarios')
|
||||
|
||||
if selected_scenarios:
|
||||
set_scenarios(current_user, current_rule, selected_scenarios)
|
||||
return redirect(current_rule.url)
|
||||
|
||||
rule_name = request.POST.get('rule-name', '').strip()
|
||||
rule_description = request.POST.get('rule-description', '').strip()
|
||||
|
||||
@ -1127,17 +1243,24 @@ def package_reaction(request, package_uuid, reaction_uuid):
|
||||
context['breadcrumbs'] = breadcrumbs(current_package, 'reaction', current_reaction)
|
||||
|
||||
context['reaction'] = current_reaction
|
||||
context['current_object'] = current_reaction
|
||||
|
||||
return render(request, 'objects/reaction.html', context)
|
||||
|
||||
elif request.method == 'POST':
|
||||
if hidden := request.POST.get('hidden', None):
|
||||
if hidden == 'delete-reaction':
|
||||
if hidden == 'delete':
|
||||
current_reaction.delete()
|
||||
return redirect(current_package.url + '/reaction')
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
selected_scenarios = request.POST.getlist('selected-scenarios')
|
||||
|
||||
if selected_scenarios:
|
||||
set_scenarios(current_user, current_reaction, selected_scenarios)
|
||||
return redirect(current_reaction.url)
|
||||
|
||||
new_reaction_name = request.POST.get('reaction-name')
|
||||
new_reaction_description = request.POST.get('reaction-description')
|
||||
|
||||
@ -1195,8 +1318,8 @@ def package_pathways(request, package_uuid):
|
||||
|
||||
log_post_params(request)
|
||||
|
||||
name = request.POST.get('name', 'Pathway ' + str(Pathway.objects.filter(package=current_package).count()))
|
||||
description = request.POST.get('description', s.DEFAULT_VALUES['description'])
|
||||
name = request.POST.get('name')
|
||||
description = request.POST.get('description')
|
||||
pw_mode = request.POST.get('predict', 'predict')
|
||||
smiles = request.POST.get('smiles')
|
||||
|
||||
@ -1217,7 +1340,14 @@ def package_pathways(request, package_uuid):
|
||||
return error(request, "Pathway prediction failed!",
|
||||
f'Pathway prediction failed as received mode "{pw_mode}" is none of {modes}')
|
||||
|
||||
prediction_setting = request.POST.get('prediction-setting', None)
|
||||
if prediction_setting:
|
||||
prediction_setting = SettingManager.get_setting_by_url(current_user, prediction_setting)
|
||||
else:
|
||||
prediction_setting = current_user.prediction_settings()
|
||||
|
||||
pw = Pathway.create(current_package, stand_smiles, name=name, description=description)
|
||||
|
||||
# set mode
|
||||
pw.kv.update({'mode': pw_mode})
|
||||
pw.save()
|
||||
@ -1230,12 +1360,11 @@ def package_pathways(request, package_uuid):
|
||||
if pw_mode == 'incremental':
|
||||
limit = 1
|
||||
|
||||
pred_setting = current_user.prediction_settings()
|
||||
pw.setting = pred_setting
|
||||
pw.setting = prediction_setting
|
||||
pw.save()
|
||||
|
||||
from .tasks import predict
|
||||
predict.delay(pw.pk, pred_setting.pk, limit=limit)
|
||||
predict.delay(pw.pk, prediction_setting.pk, limit=limit)
|
||||
|
||||
return redirect(pw.url)
|
||||
|
||||
@ -1254,6 +1383,14 @@ def package_pathway(request, package_uuid, pathway_uuid):
|
||||
if request.GET.get("last_modified", False):
|
||||
return JsonResponse({'modified': current_pathway.modified.strftime('%Y-%m-%d %H:%M:%S')})
|
||||
|
||||
if request.GET.get("download", False) == "true":
|
||||
filename = f"{current_pathway.name.replace(' ', '_')}_{current_pathway.uuid}.csv"
|
||||
csv_pw = current_pathway.to_csv()
|
||||
response = HttpResponse(csv_pw, content_type='text/csv')
|
||||
response['Content-Disposition'] = f'attachment; filename="{filename}"'
|
||||
|
||||
return response
|
||||
|
||||
context = get_base_context(request)
|
||||
context['title'] = f'enviPath - {current_package.name} - {current_pathway.name}'
|
||||
|
||||
@ -1262,6 +1399,7 @@ def package_pathway(request, package_uuid, pathway_uuid):
|
||||
context['breadcrumbs'] = breadcrumbs(current_package, 'pathway', current_pathway)
|
||||
|
||||
context['pathway'] = current_pathway
|
||||
context['current_object'] = current_pathway
|
||||
|
||||
context['breadcrumbs'] = [
|
||||
{'Home': s.SERVER_URL},
|
||||
@ -1276,12 +1414,18 @@ def package_pathway(request, package_uuid, pathway_uuid):
|
||||
|
||||
elif request.method == 'POST':
|
||||
if hidden := request.POST.get('hidden', None):
|
||||
if hidden == 'delete-pathway':
|
||||
if hidden == 'delete':
|
||||
current_pathway.delete()
|
||||
return redirect(current_package.url + '/pathway')
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
selected_scenarios = request.POST.getlist('selected-scenarios')
|
||||
|
||||
if selected_scenarios:
|
||||
set_scenarios(current_user, current_pathway, selected_scenarios)
|
||||
return redirect(current_pathway.url)
|
||||
|
||||
pathway_name = request.POST.get('pathway-name')
|
||||
pathway_description = request.POST.get('pathway-description')
|
||||
|
||||
@ -1402,19 +1546,33 @@ def package_pathway_node(request, package_uuid, pathway_uuid, node_uuid):
|
||||
]
|
||||
|
||||
context['node'] = current_node
|
||||
context['current_object'] = current_node
|
||||
|
||||
context['app_domain_assessment_data'] = json.dumps(current_node.get_app_domain_assessment_data())
|
||||
|
||||
return render(request, 'objects/node.html', context)
|
||||
|
||||
elif request.method == 'POST':
|
||||
if s.DEBUG:
|
||||
for k, v in request.POST.items():
|
||||
print(k, v)
|
||||
|
||||
log_post_params(request)
|
||||
|
||||
if hidden := request.POST.get('hidden', None):
|
||||
if hidden == 'delete-node':
|
||||
current_node.delete()
|
||||
return redirect(current_pathway.url)
|
||||
if hidden == 'delete':
|
||||
|
||||
# pre_delete signal will take care of edge deletion
|
||||
current_node.delete()
|
||||
|
||||
return redirect(current_pathway.url)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
selected_scenarios = request.POST.getlist('selected-scenarios')
|
||||
|
||||
if selected_scenarios:
|
||||
set_scenarios(current_user, current_node, selected_scenarios)
|
||||
return redirect(current_node.url)
|
||||
|
||||
return HttpResponseBadRequest()
|
||||
else:
|
||||
return HttpResponseNotAllowed(['GET', 'POST'])
|
||||
|
||||
@ -1463,6 +1621,8 @@ def package_pathway_edges(request, package_uuid, pathway_uuid):
|
||||
|
||||
elif request.method == 'POST':
|
||||
|
||||
log_post_params(request)
|
||||
|
||||
edge_name = request.POST.get('edge-name')
|
||||
edge_description = request.POST.get('edge-description')
|
||||
edge_substrates = request.POST.getlist('edge-substrates')
|
||||
@ -1499,22 +1659,30 @@ def package_pathway_edge(request, package_uuid, pathway_uuid, edge_uuid):
|
||||
'title'] = f'enviPath - {current_package.name} - {current_pathway.name} - {current_edge.edge_label.name}'
|
||||
|
||||
context['meta']['current_package'] = current_package
|
||||
context['object_type'] = 'reaction'
|
||||
context['object_type'] = 'edge'
|
||||
context['breadcrumbs'] = breadcrumbs(current_package, 'pathway', current_pathway, 'edge', current_edge)
|
||||
context['edge'] = current_edge
|
||||
context['current_object'] = current_edge
|
||||
|
||||
return render(request, 'objects/edge.html', context)
|
||||
|
||||
elif request.method == 'POST':
|
||||
if s.DEBUG:
|
||||
for k, v in request.POST.items():
|
||||
print(k, v)
|
||||
|
||||
log_post_params(request)
|
||||
|
||||
if hidden := request.POST.get('hidden', None):
|
||||
if hidden == 'delete-edge':
|
||||
if hidden == 'delete':
|
||||
current_edge.delete()
|
||||
return redirect(current_pathway.url)
|
||||
|
||||
selected_scenarios = request.POST.getlist('selected-scenarios')
|
||||
|
||||
if selected_scenarios:
|
||||
set_scenarios(current_user, current_edge, selected_scenarios)
|
||||
return redirect(current_edge.url)
|
||||
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
else:
|
||||
return HttpResponseNotAllowed(['GET', 'POST'])
|
||||
|
||||
@ -1525,6 +1693,12 @@ def package_scenarios(request, package_uuid):
|
||||
current_package = PackageManager.get_package_by_id(current_user, package_uuid)
|
||||
|
||||
if request.method == 'GET':
|
||||
|
||||
if 'application/json' in request.META.get('HTTP_ACCEPT'): #request.headers.get('Accept') == 'application/json':
|
||||
scens = Scenario.objects.filter(package=current_package).order_by('name')
|
||||
res = [{'name': s.name, 'url': s.url, 'uuid': s.uuid} for s in scens]
|
||||
return JsonResponse(res, safe=False)
|
||||
|
||||
context = get_base_context(request)
|
||||
context['title'] = f'enviPath - {current_package.name} - Scenarios'
|
||||
|
||||
@ -1587,7 +1761,6 @@ def users(request):
|
||||
context = get_base_context(request)
|
||||
context['title'] = f'enviPath - Users'
|
||||
|
||||
context['meta']['current_package'] = context['meta']['user'].default_package
|
||||
context['object_type'] = 'user'
|
||||
context['breadcrumbs'] = [
|
||||
{'Home': s.SERVER_URL},
|
||||
@ -1611,27 +1784,27 @@ def user(request, user_uuid):
|
||||
if str(current_user.uuid) != user_uuid and not current_user.is_superuser:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
user = UserManager.get_user_by_id(current_user, user_uuid)
|
||||
requested_user = UserManager.get_user_by_id(current_user, user_uuid)
|
||||
|
||||
context = get_base_context(request)
|
||||
context = get_base_context(request, for_user=requested_user)
|
||||
context['title'] = f'enviPath - User'
|
||||
|
||||
context['object_type'] = 'user'
|
||||
context['breadcrumbs'] = [
|
||||
{'Home': s.SERVER_URL},
|
||||
{'User': s.SERVER_URL + '/user'},
|
||||
{current_user.username: current_user.url}
|
||||
{current_user.username: requested_user.url}
|
||||
]
|
||||
|
||||
context['user'] = user
|
||||
context['user'] = requested_user
|
||||
|
||||
model_qs = EPModel.objects.none()
|
||||
for p in PackageManager.get_all_readable_packages(current_user, include_reviewed=True):
|
||||
for p in PackageManager.get_all_readable_packages(requested_user, include_reviewed=True):
|
||||
model_qs |= p.models
|
||||
|
||||
context['models'] = model_qs
|
||||
|
||||
context['tokens'] = APIToken.objects.filter(user=current_user)
|
||||
context['tokens'] = APIToken.objects.filter(user=requested_user)
|
||||
|
||||
return render(request, 'objects/user.html', context)
|
||||
|
||||
@ -1700,8 +1873,6 @@ def user(request, user_uuid):
|
||||
}
|
||||
}
|
||||
|
||||
print(setting)
|
||||
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
else:
|
||||
@ -1715,7 +1886,6 @@ def groups(request):
|
||||
context = get_base_context(request)
|
||||
context['title'] = f'enviPath - Groups'
|
||||
|
||||
context['meta']['current_package'] = context['meta']['user'].default_package
|
||||
context['object_type'] = 'group'
|
||||
context['breadcrumbs'] = [
|
||||
{'Home': s.SERVER_URL},
|
||||
@ -1764,12 +1934,10 @@ def group(request, group_uuid):
|
||||
|
||||
elif request.method == 'POST':
|
||||
|
||||
if s.DEBUG:
|
||||
for k, v in request.POST.items():
|
||||
print(k, v)
|
||||
log_post_params(request)
|
||||
|
||||
if hidden := request.POST.get('hidden', None):
|
||||
if hidden == 'delete-group':
|
||||
if hidden == 'delete':
|
||||
current_group.delete()
|
||||
return redirect(s.SERVER_URL + '/group')
|
||||
else:
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -38,10 +38,8 @@ def migration(request):
|
||||
res = True
|
||||
|
||||
for comp, ambit_prod in zip(bt_rule['compounds'], bt_rule['products']):
|
||||
# if comp['smiles'] != 'CC1=C(C(=C(C=N1)CO)C=O)O':
|
||||
# continue
|
||||
|
||||
products = FormatConverter.apply(comp['smiles'], smirks, preprocess_smiles=True, bracketize=False)
|
||||
products = FormatConverter.apply(comp['smiles'], smirks)
|
||||
|
||||
all_rdkit_prods = []
|
||||
for ps in products:
|
||||
@ -130,7 +128,7 @@ def migration_detail(request, package_uuid, rule_uuid):
|
||||
# if comp['smiles'] != 'CC1=C(C(=C(C=N1)CO)C=O)O':
|
||||
# continue
|
||||
|
||||
products = FormatConverter.apply(comp['smiles'], smirks, preprocess_smiles=True, bracketize=False)
|
||||
products = FormatConverter.apply(comp['smiles'], smirks)
|
||||
|
||||
all_rdkit_prods = []
|
||||
for ps in products:
|
||||
|
||||
147
static/js/pps.js
147
static/js/pps.js
@ -638,3 +638,150 @@ function fillPRCurve(modelUri, onclick){
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function handleAssessmentResponse(depict_url, data) {
|
||||
var inside_app_domain = "<a class='list-group-item'>This compound is " + (data["assessment"]["inside_app_domain"] ? "inside" : "outside") + " the Applicability Domain derived from the chemical (PCA) space constructed using the training data." + "</a>";
|
||||
var functionalGroupsImgSrc = null;
|
||||
var reactivityCentersImgSrc = null;
|
||||
|
||||
if (data['assessment']['node'] !== undefined) {
|
||||
functionalGroupsImgSrc = "<img width='400' src='" + data['assessment']['node']['image'] + "'>";
|
||||
reactivityCentersImgSrc = "<img width='400' src='" + data['assessment']['node']['image'] + "'>"
|
||||
} else {
|
||||
functionalGroupsImgSrc = "<img width='400' src=\"" + depict_url + "?smiles=" + encodeURIComponent(data['assessment']['smiles']) + "\">";
|
||||
reactivityCentersImgSrc = "<img width='400' src=\"" + depict_url + "?smiles=" + encodeURIComponent(data['assessment']['smiles']) + "\">"
|
||||
}
|
||||
|
||||
tpl = `<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||
<h4 class="panel-title">
|
||||
<a id="app-domain-assessment-functional-groups-link" data-toggle="collapse" data-parent="#app-domain-assessment" href="#app-domain-assessment-functional-groups">Functional Groups Covered by Model</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="app-domain-assessment-functional-groups" class="panel-collapse collapse">
|
||||
<div class="panel-body list-group-item">
|
||||
${inside_app_domain}
|
||||
<p></p>
|
||||
<div id="image-div" align="center">
|
||||
${functionalGroupsImgSrc}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||
<h4 class="panel-title">
|
||||
<a id="app-domain-assessment-reactivity-centers-link" data-toggle="collapse" data-parent="#app-domain-assessment" href="#app-domain-assessment-reactivity-centers">Reactivity Centers</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="app-domain-assessment-reactivity-centers" class="panel-collapse collapse">
|
||||
<div class="panel-body list-group-item">
|
||||
<div id="image-div" align="center">
|
||||
${reactivityCentersImgSrc}
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
var transformations = '';
|
||||
|
||||
for (t in data['assessment']['transformations']) {
|
||||
transObj = data['assessment']['transformations'][t];
|
||||
var neighbors = '';
|
||||
for (n in transObj['neighbors']) {
|
||||
neighObj = transObj['neighbors'][n];
|
||||
var neighImg = "<img width='100%' src='" + transObj['rule']['url'] + "?smiles=" + encodeURIComponent(neighObj['smiles']) + "'>";
|
||||
var objLink = `<a class='list-group-item' href="${neighObj['url']}">${neighObj['name']}</a>`
|
||||
var neighPredProb = "<a class='list-group-item'>Predicted probability: " + neighObj['probability'].toFixed(2) + "</a>";
|
||||
|
||||
var pwLinks = '';
|
||||
for (pw in neighObj['related_pathways']) {
|
||||
var pwObj = neighObj['related_pathways'][pw];
|
||||
pwLinks += "<a class='list-group-item' href=" + pwObj['url'] + ">" + pwObj['name'] + "</a>";
|
||||
}
|
||||
|
||||
var expPathways = `
|
||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||
<h4 class="panel-title">
|
||||
<a id="transformation-${t}-neighbor-${n}-exp-pathway-link" data-toggle="collapse" data-parent="#transformation-${t}-neighbor-${n}" href="#transformation-${t}-neighbor-${n}-exp-pathway">Experimental Pathways</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="transformation-${t}-neighbor-${n}-exp-pathway" class="panel-collapse collapse">
|
||||
<div class="panel-body list-group-item">
|
||||
${pwLinks}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
if (pwLinks === '') {
|
||||
expPathways = ''
|
||||
}
|
||||
|
||||
neighbors += `
|
||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||
<h4 class="panel-title">
|
||||
<a id="transformation-${t}-neighbor-${n}-link" data-toggle="collapse" data-parent="#transformation-${t}" href="#transformation-${t}-neighbor-${n}">Analog Transformation on ${neighObj['name']}</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="transformation-${t}-neighbor-${n}" class="panel-collapse collapse">
|
||||
<div class="panel-body list-group-item">
|
||||
${objLink}
|
||||
${neighPredProb}
|
||||
${expPathways}
|
||||
<p></p>
|
||||
<div id="image-div" align="center">
|
||||
${neighImg}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
var panelName = null;
|
||||
var objLink = null;
|
||||
if (transObj['is_predicted']) {
|
||||
panelName = `Predicted Transformation by ${transObj['rule']['name']}`;
|
||||
for (e in transObj['edges']) {
|
||||
objLink = `<a class='list-group-item' href="${transObj['edges'][e]['url']}">${transObj['edges'][e]['name']}</a>`
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
panelName = `Potential Transformation by applying ${transObj['rule']['name']}`;
|
||||
objLink = `<a class='list-group-item' href="${transObj['rule']['url']}">${transObj['rule']['name']}</a>`
|
||||
}
|
||||
|
||||
var predProb = "<a class='list-group-item'>Predicted probability: " + transObj['probability'].toFixed(2) + "</a>";
|
||||
var timesTriggered = "<a class='list-group-item'>This rule has triggered " + transObj['times_triggered'] + " times in the training set</a>";
|
||||
var reliability = "<a class='list-group-item'>Reliability: " + transObj['reliability'].toFixed(2) + " (" + (transObj['reliability'] > data['ad_params']['reliability_threshold'] ? ">" : "<") + " Reliability Threshold of " + data['ad_params']['reliability_threshold'] + ") </a>";
|
||||
var localCompatibility = "<a class='list-group-item'>Local Compatibility: " + transObj['local_compatibility'].toFixed(2) + " (" + (transObj['local_compatibility'] > data['ad_params']['local_compatibilty_threshold'] ? ">" : "<") + " Local Compatibility Threshold of " + data['ad_params']['local_compatibilty_threshold'] + ")</a>";
|
||||
|
||||
var transImg = "<img width='100%' src='" + transObj['rule']['url'] + "?smiles=" + encodeURIComponent(data['assessment']['smiles']) + "'>";
|
||||
|
||||
var transformation = `
|
||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||
<h4 class="panel-title">
|
||||
<a id="transformation-${t}-link" data-toggle="collapse" data-parent="#transformation-${t}" href="#transformation-${t}">${panelName}</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="transformation-${t}" class="panel-collapse collapse">
|
||||
<div class="panel-body list-group-item">
|
||||
${objLink}
|
||||
${predProb}
|
||||
${timesTriggered}
|
||||
${reliability}
|
||||
${localCompatibility}
|
||||
<p></p>
|
||||
<div id="image-div" align="center">
|
||||
${transImg}
|
||||
</div>
|
||||
<p></p>
|
||||
${neighbors}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
transformations += transformation;
|
||||
}
|
||||
|
||||
res = tpl + transformations;
|
||||
|
||||
$("#appDomainAssessmentResultTable").append(res);
|
||||
|
||||
}
|
||||
228
static/js/pw.js
228
static/js/pw.js
@ -1,5 +1,4 @@
|
||||
console.log("loaded")
|
||||
|
||||
console.log("loaded pw.js")
|
||||
|
||||
function predictFromNode(url) {
|
||||
$.post("", {node: url})
|
||||
@ -28,62 +27,165 @@ function draw(pathway, elem) {
|
||||
const horizontalSpacing = 75; // horizontal space between nodes
|
||||
const depthMap = new Map();
|
||||
|
||||
nodes.forEach(node => {
|
||||
// Sort nodes by depth first to minimize crossings
|
||||
const sortedNodes = [...nodes].sort((a, b) => a.depth - b.depth);
|
||||
|
||||
sortedNodes.forEach(node => {
|
||||
if (!depthMap.has(node.depth)) {
|
||||
depthMap.set(node.depth, 0);
|
||||
}
|
||||
|
||||
const nodesInLevel = nodes.filter(n => n.depth === node.depth).length;
|
||||
node.fx = width / 2 + depthMap.get(node.depth) * horizontalSpacing - ((nodesInLevel - 1) * horizontalSpacing) / 2;
|
||||
node.fy = node.depth * levelSpacing + 50;
|
||||
|
||||
// For pseudo nodes, try to position them to minimize crossings
|
||||
if (node.pseudo) {
|
||||
const parentLinks = links.filter(l => l.target.id === node.id);
|
||||
const childLinks = links.filter(l => l.source.id === node.id);
|
||||
|
||||
if (parentLinks.length > 0 && childLinks.length > 0) {
|
||||
const parentX = parentLinks[0].source.x || (width / 2);
|
||||
const childrenX = childLinks.map(l => l.target.x || (width / 2));
|
||||
const avgChildX = childrenX.reduce((sum, x) => sum + x, 0) / childrenX.length;
|
||||
|
||||
// Position pseudo node between parent and average child position
|
||||
node.fx = (parentX + avgChildX) / 2;
|
||||
} else {
|
||||
node.fx = width / 2 + depthMap.get(node.depth) * horizontalSpacing - ((nodesInLevel - 1) * horizontalSpacing) / 2;
|
||||
}
|
||||
} else {
|
||||
node.fx = width / 2 + depthMap.get(node.depth) * horizontalSpacing - ((nodesInLevel - 1) * horizontalSpacing) / 2;
|
||||
}
|
||||
|
||||
node.fy = node.depth * levelSpacing + 50;
|
||||
depthMap.set(node.depth, depthMap.get(node.depth) + 1);
|
||||
});
|
||||
}
|
||||
|
||||
// Funktion für das Update der Positionen
|
||||
// Function to update pseudo node positions based on connected nodes
|
||||
function updatePseudoNodePositions() {
|
||||
nodes.forEach(node => {
|
||||
if (node.pseudo && !node.isDragging) { // Don't auto-update if being dragged
|
||||
const parentLinks = links.filter(l => l.target.id === node.id);
|
||||
const childLinks = links.filter(l => l.source.id === node.id);
|
||||
|
||||
if (parentLinks.length > 0 && childLinks.length > 0) {
|
||||
const parent = parentLinks[0].source;
|
||||
const children = childLinks.map(l => l.target);
|
||||
|
||||
// Calculate optimal position to minimize crossing
|
||||
const parentX = parent.x;
|
||||
const parentY = parent.y;
|
||||
const childrenX = children.map(c => c.x);
|
||||
const childrenY = children.map(c => c.y);
|
||||
const avgChildX = d3.mean(childrenX);
|
||||
const avgChildY = d3.mean(childrenY);
|
||||
|
||||
// Position pseudo node between parent and average child position
|
||||
node.fx = (parentX + avgChildX) / 2;
|
||||
node.fy = (parentY + avgChildY) / 2; // Allow vertical movement too
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Enhanced ticked function
|
||||
function ticked() {
|
||||
// Update pseudo node positions first
|
||||
updatePseudoNodePositions();
|
||||
|
||||
link.attr("x1", d => d.source.x)
|
||||
.attr("y1", d => d.source.y)
|
||||
.attr("x2", d => d.target.x)
|
||||
.attr("y2", d => d.target.y);
|
||||
|
||||
node.attr("transform", d => `translate(${d.x},${d.y})`);
|
||||
|
||||
nodes.forEach(n => {
|
||||
if (n.pseudo) {
|
||||
// Alle Kinder dieses Pseudonodes finden
|
||||
const childLinks = links.filter(l => l.source.id === n.id);
|
||||
const childNodes = childLinks.map(l => l.target);
|
||||
if (childNodes.length > 0) {
|
||||
// Durchschnitt der Kinderpositionen berechnen
|
||||
const avgX = d3.mean(childNodes, d => d.x);
|
||||
const avgY = d3.mean(childNodes, d => d.y);
|
||||
n.fx = avgX;
|
||||
// keep level as is
|
||||
n.fy = n.y;
|
||||
}
|
||||
}
|
||||
});
|
||||
//simulation.alpha(0.3).restart();
|
||||
}
|
||||
|
||||
function dragstarted(event, d) {
|
||||
if (!event.active) simulation.alphaTarget(0.3).restart();
|
||||
d.fx = d.x; // Setzt die Fixierung auf die aktuelle Position
|
||||
d.fx = d.x;
|
||||
d.fy = d.y;
|
||||
|
||||
// Mark if this node is being dragged
|
||||
d.isDragging = true;
|
||||
|
||||
// If dragging a non-pseudo node, mark connected pseudo nodes for update
|
||||
if (!d.pseudo) {
|
||||
markConnectedPseudoNodes(d);
|
||||
}
|
||||
}
|
||||
|
||||
function dragged(event, d) {
|
||||
d.fx = event.x; // Position direkt an Maus anpassen
|
||||
d.fx = event.x;
|
||||
d.fy = event.y;
|
||||
|
||||
// Update connected pseudo nodes in real-time
|
||||
if (!d.pseudo) {
|
||||
updateConnectedPseudoNodes(d);
|
||||
}
|
||||
}
|
||||
|
||||
function dragended(event, d) {
|
||||
if (!event.active) simulation.alphaTarget(0);
|
||||
// Knoten bleibt an der neuen Position und wird nicht zurückgezogen
|
||||
|
||||
// Mark that dragging has ended
|
||||
d.isDragging = false;
|
||||
|
||||
// Final update of connected pseudo nodes
|
||||
if (!d.pseudo) {
|
||||
updateConnectedPseudoNodes(d);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to mark connected pseudo nodes
|
||||
function markConnectedPseudoNodes(draggedNode) {
|
||||
// Find pseudo nodes connected to this node
|
||||
const connectedPseudos = new Set();
|
||||
|
||||
// Check as parent of pseudo nodes
|
||||
links.filter(l => l.source.id === draggedNode.id && l.target.pseudo)
|
||||
.forEach(l => connectedPseudos.add(l.target));
|
||||
|
||||
// Check as child of pseudo nodes
|
||||
links.filter(l => l.target.id === draggedNode.id && l.source.pseudo)
|
||||
.forEach(l => connectedPseudos.add(l.source));
|
||||
|
||||
return connectedPseudos;
|
||||
}
|
||||
|
||||
// Helper function to update connected pseudo nodes
|
||||
function updateConnectedPseudoNodes(draggedNode) {
|
||||
const connectedPseudos = markConnectedPseudoNodes(draggedNode);
|
||||
|
||||
connectedPseudos.forEach(pseudoNode => {
|
||||
if (!pseudoNode.isDragging) { // Don't update if pseudo node is being dragged
|
||||
const parentLinks = links.filter(l => l.target.id === pseudoNode.id);
|
||||
const childLinks = links.filter(l => l.source.id === pseudoNode.id);
|
||||
|
||||
if (parentLinks.length > 0 && childLinks.length > 0) {
|
||||
const parent = parentLinks[0].source;
|
||||
const children = childLinks.map(l => l.target);
|
||||
|
||||
const parentX = parent.fx || parent.x;
|
||||
const parentY = parent.fy || parent.y;
|
||||
const childrenX = children.map(c => c.fx || c.x);
|
||||
const childrenY = children.map(c => c.fy || c.y);
|
||||
const avgChildX = d3.mean(childrenX);
|
||||
const avgChildY = d3.mean(childrenY);
|
||||
|
||||
// Update pseudo node position - allow both X and Y movement
|
||||
pseudoNode.fx = (parentX + avgChildX) / 2;
|
||||
pseudoNode.fy = (parentY + avgChildY) / 2;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Restart simulation with lower alpha to smooth the transition
|
||||
simulation.alpha(0.1).restart();
|
||||
}
|
||||
|
||||
|
||||
// t -> ref to "this" from d3
|
||||
function nodeClick(event, node, t) {
|
||||
console.log(node);
|
||||
@ -105,7 +207,7 @@ function draw(pathway, elem) {
|
||||
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 () {
|
||||
setTimeout(function () {
|
||||
var close = setInterval(function () {
|
||||
if (!$(".popover:hover").length // mouse outside popover
|
||||
&& !$(e).is(':hover')) { // mouse outside element
|
||||
@ -140,11 +242,20 @@ function draw(pathway, elem) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function node_popup(n) {
|
||||
popupContent = "<a href='" + n.url +"'>" + n.name + "</a><br>";
|
||||
popupContent = "<a href='" + n.url + "'>" + n.name + "</a><br>";
|
||||
popupContent += "Depth " + n.depth + "<br>"
|
||||
popupContent += "<img src='" + n.image + "' width='"+ 20 * nodeRadius +"'><br>"
|
||||
|
||||
if (appDomainViewEnabled) {
|
||||
if (n.app_domain != null) {
|
||||
popupContent += "This compound is " + (n.app_domain['inside_app_domain'] ? "inside" : "outside") + " the Applicability Domain derived from the chemical (PCA) space constructed using the training data." + "<br>"
|
||||
if (n.app_domain['uncovered_functional_groups']) {
|
||||
popupContent += "Compound contains functional groups not covered by the training set <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) {
|
||||
@ -153,7 +264,7 @@ function draw(pathway, elem) {
|
||||
}
|
||||
|
||||
var isLeaf = pathway.links.filter(obj => obj.source.id === n.id).length === 0;
|
||||
if(pathway.isIncremental && isLeaf) {
|
||||
if (pathway.isIncremental && isLeaf) {
|
||||
popupContent += '<br><a class="btn btn-primary" onclick="predictFromNode(\'' + n.url + '\')" href="#">Predict from here</a><br>';
|
||||
}
|
||||
|
||||
@ -161,13 +272,24 @@ function draw(pathway, elem) {
|
||||
}
|
||||
|
||||
function edge_popup(e) {
|
||||
popupContent = "<a href='" + e.url +"'>" + e.name + "</a><br>";
|
||||
popupContent += "<img src='" + e.image + "' width='"+ 20 * nodeRadius +"'><br>"
|
||||
popupContent = "<a href='" + e.url + "'>" + e.name + "</a><br>";
|
||||
|
||||
if (e.app_domain) {
|
||||
adcontent = "<p>";
|
||||
if (e.app_domain["times_triggered"]) {
|
||||
adcontent += "This rule triggered " + e.app_domain["times_triggered"] + " times in the training set<br>";
|
||||
}
|
||||
adcontent += "Reliability " + e.app_domain["reliability"].toFixed(2) + " (" + (e.app_domain["reliability"] > e.app_domain["reliability_threshold"] ? ">" : "<") + " Reliability Threshold of " + e.app_domain["reliability_threshold"] + ")<br>";
|
||||
adcontent += "Local Compatibility " + e.app_domain["local_compatibility"].toFixed(2) + " (" + (e.app_domain["local_compatibility"] > e.app_domain["local_compatibility_threshold"] ? ">" : "<") + " Local Compatibility Threshold of " + e.app_domain["local_compatibility_threshold"] + ")<br>";
|
||||
adcontent += "</p>";
|
||||
popupContent += adcontent;
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -180,9 +302,9 @@ function draw(pathway, elem) {
|
||||
|
||||
var clientX;
|
||||
var clientY;
|
||||
document.addEventListener('mousemove', function(event) {
|
||||
document.addEventListener('mousemove', function (event) {
|
||||
clientX = event.clientX;
|
||||
clientY =event.clientY;
|
||||
clientY = event.clientY;
|
||||
});
|
||||
|
||||
const zoomable = d3.select("#zoomable");
|
||||
@ -233,13 +355,12 @@ function draw(pathway, elem) {
|
||||
.enter().append("line")
|
||||
// Check if target is pseudo and draw marker only if not pseudo
|
||||
.attr("class", d => d.target.pseudo ? "link_no_arrow" : "link")
|
||||
// .on("mouseover", (event, d) => {
|
||||
// tooltip.style("visibility", "visible")
|
||||
// .text(`Link: ${d.source.id} → ${d.target.id}`)
|
||||
// .style("top", `${event.pageY + 5}px`)
|
||||
// .style("left", `${event.pageX + 5}px`);
|
||||
// })
|
||||
// .on("mouseout", () => tooltip.style("visibility", "hidden"));
|
||||
.attr("marker-end", d => d.target.pseudo ? '' : 'url(#arrow)')
|
||||
|
||||
// add element to links array
|
||||
link.each(function (d) {
|
||||
d.el = this; // attach the DOM element to the data object
|
||||
});
|
||||
|
||||
pop_add(link, "Reaction", edge_popup);
|
||||
|
||||
@ -255,20 +376,10 @@ function draw(pathway, elem) {
|
||||
.on("click", function (event, d) {
|
||||
d3.select(this).select("circle").classed("highlighted", !d3.select(this).select("circle").classed("highlighted"));
|
||||
})
|
||||
// .on("mouseover", (event, d) => {
|
||||
// if (d.pseudo) {
|
||||
// return
|
||||
// }
|
||||
// tooltip.style("visibility", "visible")
|
||||
// .text(`Node: ${d.id} Depth: ${d.depth}`)
|
||||
// .style("top", `${event.pageY + 5}px`)
|
||||
// .style("left", `${event.pageX + 5}px`);
|
||||
// })
|
||||
// .on("mouseout", () => tooltip.style("visibility", "hidden"));
|
||||
|
||||
// Kreise für die Knoten hinzufügen
|
||||
node.append("circle")
|
||||
// make radius "invisible"
|
||||
// make radius "invisible" for pseudo nodes
|
||||
.attr("r", d => d.pseudo ? 0.01 : nodeRadius)
|
||||
.style("fill", "#e8e8e8");
|
||||
|
||||
@ -280,5 +391,10 @@ function draw(pathway, elem) {
|
||||
.attr("width", nodeRadius * 2)
|
||||
.attr("height", nodeRadius * 2);
|
||||
|
||||
pop_add(node, "Compound", node_popup);
|
||||
}
|
||||
// add element to nodes array
|
||||
node.each(function (d) {
|
||||
d.el = this; // attach the DOM element to the data object
|
||||
});
|
||||
|
||||
pop_add(node, "Compound", node_popup);
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
{% if meta.can_edit %}
|
||||
{% if meta.can_edit and meta.enabled_features.MODEL_BUILDING %}
|
||||
<li>
|
||||
<a role="button" data-toggle="modal" data-target="#new_model_modal">
|
||||
<span class="glyphicon glyphicon-plus"></span> New Model</a>
|
||||
|
||||
@ -8,7 +8,11 @@
|
||||
<i class="glyphicon glyphicon-plus"></i> Add Structure</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="button" data-toggle="modal" data-target="#delete_compound_modal">
|
||||
<a role="button" data-toggle="modal" data-target="#set_scenario_modal">
|
||||
<i class="glyphicon glyphicon-plus"></i> Set Scenarios</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
|
||||
<i class="glyphicon glyphicon-trash"></i> Delete Compound</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
@ -4,7 +4,11 @@
|
||||
<i class="glyphicon glyphicon-edit"></i> Edit Compound Structure</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="button" data-toggle="modal" data-target="#delete_compound_structure_modal">
|
||||
<a role="button" data-toggle="modal" data-target="#set_scenario_modal">
|
||||
<i class="glyphicon glyphicon-plus"></i> Set Scenarios</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
|
||||
<i class="glyphicon glyphicon-trash"></i> Delete Compound Structure</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
10
templates/actions/objects/edge.html
Normal file
10
templates/actions/objects/edge.html
Normal file
@ -0,0 +1,10 @@
|
||||
{% if meta.can_edit %}
|
||||
<li>
|
||||
<a role="button" data-toggle="modal" data-target="#set_scenario_modal">
|
||||
<i class="glyphicon glyphicon-plus"></i> Set Scenarios</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
|
||||
<i class="glyphicon glyphicon-trash"></i> Delete Edge</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
@ -1,10 +1,10 @@
|
||||
{% if meta.can_edit %}
|
||||
<li>
|
||||
<a role="button" data-toggle="modal" data-target="#delete_group_modal">
|
||||
<i class="glyphicon glyphicon-trash"></i> Delete Group</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button" data-toggle="modal" data-target="#edit_group_member_modal">
|
||||
<a role="button" data-toggle="modal" data-target="#edit_group_member_modal">
|
||||
<i class="glyphicon glyphicon-trash"></i> Add/Remove Member</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button" data-toggle="modal" data-target="#generic_delete_modal">
|
||||
<i class="glyphicon glyphicon-trash"></i> Delete Group</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
@ -1,6 +1,6 @@
|
||||
{% if meta.can_edit %}
|
||||
<li>
|
||||
<a class="button" data-toggle="modal" data-target="#delete_model_modal">
|
||||
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
|
||||
<i class="glyphicon glyphicon-trash"></i> Delete Model</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
@ -4,7 +4,11 @@
|
||||
<i class="glyphicon glyphicon-edit"></i> Edit Node</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="button" data-toggle="modal" data-target="#delete_node_modal">
|
||||
<a role="button" data-toggle="modal" data-target="#set_scenario_modal">
|
||||
<i class="glyphicon glyphicon-plus"></i> Set Scenarios</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
|
||||
<i class="glyphicon glyphicon-trash"></i> Delete Node</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
@ -7,12 +7,16 @@
|
||||
<a role="button" data-toggle="modal" data-target="#edit_package_permissions_modal">
|
||||
<i class="glyphicon glyphicon-user"></i> Edit Permissions</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button" data-toggle="modal" data-target="#publish_package_modal">
|
||||
<i class="glyphicon glyphicon-bullhorn"></i> Publish Package</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button" data-toggle="modal" data-target="#set_license_modal">
|
||||
<i class="glyphicon glyphicon-duplicate"></i> License</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="button" data-toggle="modal" data-target="#delete_package_modal">
|
||||
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
|
||||
<i class="glyphicon glyphicon-trash"></i> Delete Package</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
@ -9,24 +9,33 @@
|
||||
</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>
|
||||
<a class="button" data-toggle="modal" data-target="#download_pathway_modal">
|
||||
<i class="glyphicon glyphicon-floppy-save"></i> Download 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="#edit_pathway_modal">
|
||||
<i class="glyphicon glyphicon-edit"></i> Edit Pathway</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button" data-toggle="modal" data-target="#set_scenario_modal">
|
||||
<i class="glyphicon glyphicon-plus"></i> Set Scenarios</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>
|
||||
<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">
|
||||
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
|
||||
<i class="glyphicon glyphicon-trash"></i> Delete Pathway</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
@ -4,7 +4,11 @@
|
||||
<i class="glyphicon glyphicon-edit"></i> Edit Reaction</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="button" data-toggle="modal" data-target="#delete_reaction_modal">
|
||||
<a role="button" data-toggle="modal" data-target="#set_scenario_modal">
|
||||
<i class="glyphicon glyphicon-plus"></i> Set Scenarios</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
|
||||
<i class="glyphicon glyphicon-trash"></i> Delete Reaction</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
@ -3,4 +3,12 @@
|
||||
<a role="button" data-toggle="modal" data-target="#edit_rule_modal">
|
||||
<i class="glyphicon glyphicon-edit"></i> Edit Rule</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button" data-toggle="modal" data-target="#set_scenario_modal">
|
||||
<i class="glyphicon glyphicon-plus"></i> Set Scenarios</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
|
||||
<i class="glyphicon glyphicon-trash"></i> Delete Rule</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
@ -16,7 +16,7 @@
|
||||
{# <i class="glyphicon glyphicon-console"></i> Manage API Token</a>#}
|
||||
{# </li>#}
|
||||
<li>
|
||||
<a role="button" data-toggle="modal" data-target="#delete_user_modal">
|
||||
<a role="button" data-toggle="modal" data-target="#generic_delete_modal">
|
||||
<i class="glyphicon glyphicon-trash"></i> Delete Account</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
@ -51,7 +51,7 @@
|
||||
(function () {
|
||||
var u = "//matomo.envipath.com/";
|
||||
_paq.push(['setTrackerUrl', u + 'matomo.php']);
|
||||
_paq.push(['setSiteId', '7']);
|
||||
_paq.push(['setSiteId', '10']);
|
||||
var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
|
||||
g.async = true;
|
||||
g.src = u + 'matomo.js';
|
||||
@ -83,21 +83,26 @@
|
||||
<!-- Collect the nav links, forms, and other content for toggling -->
|
||||
<div class="collapse navbar-collapse collapse-framework navbar-collapse-framework" id="navbarCollapse">
|
||||
<ul class="nav navbar-nav navbar-nav-framework">
|
||||
<li class="dropdown">
|
||||
<a data-toggle="dropdown" class="dropdown-toggle" href="#">Predict Pathway<b class="caret"></b></a>
|
||||
<ul role="menu" class="dropdown-menu">
|
||||
<li>
|
||||
<a class="button" data-toggle="modal" data-target="#predict_modal">
|
||||
<i class=" glyphicon glyphicon-tag"></i> Predict Pathway
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="button" data-toggle="modal" data-target="#batch_predict_modal">
|
||||
<i class=" glyphicon glyphicon-tags"></i> Batch Prediction
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<li>
|
||||
<a class="button" data-toggle="modal" data-target="#predict_modal">
|
||||
Predict Pathway
|
||||
</a>
|
||||
</li>
|
||||
{# <li class="dropdown">#}
|
||||
{# <a data-toggle="dropdown" class="dropdown-toggle" href="#">Predict Pathway<b class="caret"></b></a>#}
|
||||
{# <ul role="menu" class="dropdown-menu">#}
|
||||
{# <li>#}
|
||||
{# <a class="button" data-toggle="modal" data-target="#predict_modal">#}
|
||||
{# <i class=" glyphicon glyphicon-tag"></i> Predict Pathway#}
|
||||
{# </a>#}
|
||||
{# </li>#}
|
||||
{# <li>#}
|
||||
{# <a class="button" data-toggle="modal" data-target="#batch_predict_modal">#}
|
||||
{# <i class=" glyphicon glyphicon-tags"></i> Batch Prediction#}
|
||||
{# </a>#}
|
||||
{# </li>#}
|
||||
{# </ul>#}
|
||||
{# </li>#}
|
||||
<li><a href="{{ meta.server_url }}/package" id="packageLink">Package</a></li>
|
||||
<li><a href="{{ meta.server_url }}/search" id="searchLink">Search</a></li>
|
||||
<li><a href="{{ meta.server_url }}/model" id="modelLink">Modelling</a></li>
|
||||
@ -192,6 +197,23 @@
|
||||
{% endif %}
|
||||
{% block content %}
|
||||
{% endblock content %}
|
||||
{% if meta.current_package.license %}
|
||||
<p></p>
|
||||
<div class="panel-group" id="license_accordion">
|
||||
<div class="panel panel-default list-group-item" style="background-color:#f5f5f5">
|
||||
<div class="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#licence_accordion" href="#license">License</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="license" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
<a target="_blank" href="{{ meta.current_package.license.link }}">
|
||||
<img src="{{ meta.current_package.license.image_link }}">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
|
||||
@ -143,6 +143,14 @@
|
||||
}
|
||||
|
||||
$(function () {
|
||||
|
||||
$('#index-form').on("keydown", function (e) {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
goButtonClicked();
|
||||
}
|
||||
});
|
||||
|
||||
// Code that should be executed once DOM is ready goes here
|
||||
$('#dropdown-predict').on('click', actionDropdownClicked);
|
||||
$('#dropdown-search').on('click', actionDropdownClicked);
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
|
||||
.center-button {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
top: 70%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 1;
|
||||
@ -38,7 +38,7 @@
|
||||
|
||||
.center-message {
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
top: 35%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 1;
|
||||
|
||||
@ -16,14 +16,14 @@
|
||||
<div class="jumbotron">Create a new Model to
|
||||
limit the number of degradation products in the
|
||||
prediction. You just need to set a name and the packages
|
||||
you want the object to be based on. If you want to use the
|
||||
default options suggested by us, simply click Submit,
|
||||
otherwise click Advanced Options.
|
||||
you want the object to be based on. There are multiple types of models available.
|
||||
For additional information have a look at our
|
||||
<a target="_blank" href="https://wiki.envipath.org/index.php/relative-reasoning" role="button">wiki >></a>
|
||||
</div>
|
||||
<label for="name">Name</label>
|
||||
<input id="name" name="model-name" class="form-control" placeholder="Name"/>
|
||||
<label for="description">Description</label>
|
||||
<input id="description" name="model-description" class="form-control"
|
||||
<label for="model-name">Name</label>
|
||||
<input id="model-name" name="model-name" class="form-control" placeholder="Name"/>
|
||||
<label for="model-description">Description</label>
|
||||
<input id="model-description" name="model-description" class="form-control"
|
||||
placeholder="Description"/>
|
||||
<label for="model-type">Model Type</label>
|
||||
<select id="model-type" name="model-type" class="form-control" data-width='100%'>
|
||||
@ -35,7 +35,7 @@
|
||||
<!-- ML Based Form-->
|
||||
<div id="ml-relative-reasoning-specific-form">
|
||||
<!-- Rule Packages -->
|
||||
<label>Rule Packages</label><br>
|
||||
<label for="ml-relative-reasoning-rule-packages">Rule Packages</label>
|
||||
<select id="ml-relative-reasoning-rule-packages" name="ml-relative-reasoning-rule-packages"
|
||||
data-actions-box='true' class="form-control" multiple data-width='100%'>
|
||||
<option disabled>Reviewed Packages</option>
|
||||
@ -53,7 +53,7 @@
|
||||
{% endfor %}
|
||||
</select>
|
||||
<!-- Data Packages -->
|
||||
<label>Data Packages</label><br>
|
||||
<label for="ml-relative-reasoning-data-packages" >Data Packages</label>
|
||||
<select id="ml-relative-reasoning-data-packages" name="ml-relative-reasoning-data-packages"
|
||||
data-actions-box='true' class="form-control" multiple data-width='100%'>
|
||||
<option disabled>Reviewed Packages</option>
|
||||
@ -77,22 +77,24 @@
|
||||
class="form-control">
|
||||
<option value="MACCS" selected>MACCS Fingerprinter</option>
|
||||
</select>
|
||||
{% if 'plugins' in meta.enabled_features %}
|
||||
{% if meta.enabled_features.PLUGINS and additional_descriptors %}
|
||||
<!-- Property Plugins go here -->
|
||||
<label for="ml-relative-reasoning-additional-fingerprinter">Fingerprinter</label>
|
||||
<select id="ml-relative-reasoning-additional-fingerprinter"
|
||||
name="ml-relative-reasoning-additional-fingerprinter"
|
||||
class="form-control">
|
||||
<label for="ml-relative-reasoning-additional-fingerprinter">Additional Fingerprinter / Descriptors</label>
|
||||
<select id="ml-relative-reasoning-additional-fingerprinter" name="ml-relative-reasoning-additional-fingerprinter" class="form-control">
|
||||
<option disabled selected>Select Additional Fingerprinter / Descriptor</option>
|
||||
{% for k, v in additional_descriptors.items %}
|
||||
<option value="{{ v }}">{{ k }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% endif %}
|
||||
|
||||
<label for="ml-relative-reasoning-threshold">Threshold</label>
|
||||
<input type="number" min="0" , max="1" step="0.05" value="0.5"
|
||||
<input type="number" min="0" max="1" step="0.05" value="0.5"
|
||||
id="ml-relative-reasoning-threshold"
|
||||
name="ml-relative-reasoning-threshold" class="form-control">
|
||||
|
||||
<!-- Evaluation -->
|
||||
|
||||
<label>Evaluation Packages</label><br>
|
||||
<label for="ml-relative-reasoning-evaluation-packages">Evaluation Packages</label>
|
||||
<select id="ml-relative-reasoning-evaluation-packages" name="ml-relative-reasoning-evaluation-packages"
|
||||
data-actions-box='true' class="form-control" multiple data-width='100%'>
|
||||
<option disabled>Reviewed Packages</option>
|
||||
@ -110,6 +112,26 @@
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
{% if meta.enabled_features.APPLICABILITY_DOMAIN %}
|
||||
<!-- Build AD? -->
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="build-app-domain" name="build-app-domain">Also build an Applicability Domain?
|
||||
</label>
|
||||
</div>
|
||||
<!-- Num Neighbors -->
|
||||
<label for="num-neighbors">Number of Neighbors</label>
|
||||
<input id="num-neighbors" name="num-neighbors" type="number" class="form-control" value="5"
|
||||
step="1" min="0" max="10">
|
||||
<!-- Local Compatibility -->
|
||||
<label for="local-compatibility-threshold">Local Compatibility Threshold</label>
|
||||
<input id="local-compatibility-threshold" name="local-compatibility-threshold" type="number"
|
||||
class="form-control" value="0.5" step="0.01" min="0" max="1">
|
||||
<!-- Reliability -->
|
||||
<label for="reliability-threshold">Reliability Threshold</label>
|
||||
<input id="reliability-threshold" name="reliability-threshold" type="number"
|
||||
class="form-control" value="0.5" step="0.01" min="0" max="1">
|
||||
{% endif %}
|
||||
</div>
|
||||
<!-- Rule Based Based Form-->
|
||||
<div id="rule-based-relative-reasoning-specific-form">
|
||||
@ -118,47 +140,9 @@
|
||||
<!-- EnviFormer-->
|
||||
<div id="enviformer-specific-form">
|
||||
<label for="enviformer-threshold">Threshold</label>
|
||||
<input type="number" min="0" , max="1" step="0.05" value="0.5" id="enviformer-threshold"
|
||||
<input type="number" min="0" max="1" step="0.05" value="0.5" id="enviformer-threshold"
|
||||
name="enviformer-threshold" class="form-control">
|
||||
</div>
|
||||
|
||||
{% if 'applicability_domain' in enabled_features %}
|
||||
<div class="modal-body hide" data-step="3" data-title="Advanced Options II">
|
||||
<div class="jumbotron">Selection of parameter values for the Applicability Domain process.
|
||||
Number of Neighbours refers to a requirement on the minimum number of compounds from the
|
||||
training
|
||||
dataset that has at least one triggered transformation rule that is common with the compound
|
||||
being
|
||||
analyzed.
|
||||
Reliability Threshold is a requirement on the average tanimoto distance to the set number of
|
||||
"nearest neighbours" (Number of neighbours with the smallest tanimoto distances).
|
||||
Local Compatibility Threshold is a requirement on the average F1 score determined from the
|
||||
number of
|
||||
nearest neighbours, using their respective precision and recall values computed from the
|
||||
agreement
|
||||
between their observed and triggered rules.
|
||||
You can learn more about it in our wiki!
|
||||
</div>
|
||||
<!-- Use AD? -->
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="buildAD" name="buildAD">Also build an Applicability Domain?
|
||||
</label>
|
||||
</div>
|
||||
<!-- Num Neighbours -->
|
||||
<label for="adK">Number of Neighbours</label>
|
||||
<input id="adK" name="adK" type="number" class="form-control" value="5" step="1" min="0"
|
||||
max="10">
|
||||
<!-- F1 Threshold -->
|
||||
<label for="localCompatibilityThreshold">Local Compatibility Threshold</label>
|
||||
<input id="localCompatibilityThreshold" name="localCompatibilityThreshold" type="number"
|
||||
class="form-control" value="0.5" step="0.01" min="0" max="1">
|
||||
<!-- Percentile Threshold -->
|
||||
<label for="reliabilityThreshold">Reliability Threshold</label>
|
||||
<input id="reliabilityThreshold" name="reliabilityThreshold" type="number" class="form-control"
|
||||
value="0.5" step="0.01" min="0" max="1">
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@ -179,6 +163,9 @@ $(function() {
|
||||
$("#ml-relative-reasoning-rule-packages").selectpicker();
|
||||
$("#ml-relative-reasoning-data-packages").selectpicker();
|
||||
$("#ml-relative-reasoning-evaluation-packages").selectpicker();
|
||||
if ($('#ml-relative-reasoning-additional-fingerprinter').length > 0) {
|
||||
$("#ml-relative-reasoning-additional-fingerprinter").selectpicker();
|
||||
}
|
||||
|
||||
// On change hide all and show only selected
|
||||
$("#model-type").change(function() {
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
{% load static %}
|
||||
<!-- Delete Group -->
|
||||
<div id="delete_group_modal" class="modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">Delete Group</h3>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-danger">
|
||||
Clicking "Delete" will <strong>permanently</strong> delete the Group.
|
||||
This action can't be undone!
|
||||
</div>
|
||||
<form id="delete-group-modal-form" accept-charset="UTF-8" action="" data-remote="true" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="hidden" value="delete-group">
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-danger" id="delete-group-modal-submit">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(function() {
|
||||
|
||||
$('#delete-group-modal-submit').click(function(e){
|
||||
e.preventDefault();
|
||||
$('#delete-group-modal-form').submit();
|
||||
});
|
||||
|
||||
})
|
||||
</script>
|
||||
@ -1,35 +0,0 @@
|
||||
{% 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>
|
||||
@ -1,35 +0,0 @@
|
||||
{% 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>
|
||||
@ -1,36 +0,0 @@
|
||||
{% load static %}
|
||||
<!-- Delete Package -->
|
||||
<div id="delete_package_modal" class="modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">Delete Package</h3>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Deleting a Package deletes the very Package
|
||||
as well as all Objects stored in the Package.
|
||||
<form id="delete-package-modal-form" accept-charset="UTF-8" action="" data-remote="true" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" id="hidden" name="hidden" value="delete-package"/>
|
||||
</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-package-modal-submit">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(function() {
|
||||
|
||||
$('#delete-package-modal-submit').click(function(e){
|
||||
e.preventDefault();
|
||||
$('#delete-package-modal-form').submit();
|
||||
});
|
||||
|
||||
})
|
||||
</script>
|
||||
@ -22,7 +22,7 @@
|
||||
<option value="{{ e.url }}">{{ e.edge_label.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="hidden" id="hidden" name="hidden" value="delete-edge"/>
|
||||
<input type="hidden" id="hidden" name="hidden" value="delete"/>
|
||||
</form>
|
||||
<p></p>
|
||||
<div id="delete_pathway_edge_image"></div>
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
<option value="{{ n.url }}">{{ n.default_node_label.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="hidden" id="hidden" name="hidden" value="delete-node"/>
|
||||
<input type="hidden" id="hidden" name="hidden" value="delete"/>
|
||||
</form>
|
||||
<p></p>
|
||||
<div id="delete_pathway_node_image"></div>
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
{% 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>
|
||||
@ -1,38 +0,0 @@
|
||||
{% load static %}
|
||||
<!-- Delete User -->
|
||||
<div id="delete_user_modal" class="modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">Delete User</h3>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-danger">
|
||||
Clicking "Delete" will <strong>permanently</strong> delete the User and associated data.
|
||||
This action can't be undone!
|
||||
</div>
|
||||
<form id="delete-user-modal-form" accept-charset="UTF-8" action="" data-remote="true" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="hidden" value="delete-user">
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-danger" id="delete-user-modal-submit">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(function() {
|
||||
|
||||
$('#delete-user-modal-submit').click(function(e){
|
||||
e.preventDefault();
|
||||
$('#delete-user-modal-form').submit();
|
||||
});
|
||||
|
||||
})
|
||||
</script>
|
||||
@ -1,24 +1,24 @@
|
||||
{% load static %}
|
||||
<!-- Delete Pathway -->
|
||||
<div id="delete_pathway_modal" class="modal" tabindex="-1">
|
||||
<!-- Download Pathway -->
|
||||
<div id="download_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>
|
||||
<h3 class="modal-title">Download 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"/>
|
||||
By clicking on Download the Pathway will be converted into a CSV and directly downloaded.
|
||||
<form id="download-pathway-modal-form" accept-charset="UTF-8" action="{{ pathway.url }}"
|
||||
data-remote="true" method="GET">
|
||||
<input type="hidden" name="download" value="true"/>
|
||||
</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>
|
||||
<button type="button" class="btn btn-primary" id="download-pathway-modal-submit">Download</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -26,9 +26,10 @@
|
||||
<script>
|
||||
$(function () {
|
||||
|
||||
$('#delete-pathway-modal-submit').click(function (e) {
|
||||
$('#download-pathway-modal-submit').click(function (e) {
|
||||
e.preventDefault();
|
||||
$('#delete-pathway-modal-form').submit();
|
||||
$('#download-pathway-modal-form').submit();
|
||||
$('#download_pathway_modal').modal('hide');
|
||||
});
|
||||
|
||||
})
|
||||
42
templates/modals/objects/generic_delete_modal.html
Normal file
42
templates/modals/objects/generic_delete_modal.html
Normal file
@ -0,0 +1,42 @@
|
||||
{% load static %}
|
||||
<!-- Delete Object -->
|
||||
<div id="generic_delete_modal" class="modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">Delete {{ object_type|capfirst }}</h3>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{% if object_type == 'user' %}
|
||||
Clicking "Delete" will <strong>permanently</strong> delete the User and associated data.
|
||||
This action can't be undone!
|
||||
{% else %}
|
||||
Deletes the {{ object_type|capfirst }}. Related objects that depend on this {{ object_type|capfirst }}
|
||||
will be deleted as well.
|
||||
{% endif %}
|
||||
<form id="generic-delete-modal-form" accept-charset="UTF-8" action="{{ current_object.url }}"
|
||||
data-remote="true" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" id="hidden" name="hidden" value="delete"/>
|
||||
</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="generic-delete-modal-form-submit">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(function () {
|
||||
|
||||
$('#generic-delete-modal-form-submit').click(function (e) {
|
||||
e.preventDefault();
|
||||
$('#generic-delete-modal-form').submit();
|
||||
});
|
||||
|
||||
})
|
||||
</script>
|
||||
72
templates/modals/objects/generic_set_scenario_modal.html
Normal file
72
templates/modals/objects/generic_set_scenario_modal.html
Normal file
@ -0,0 +1,72 @@
|
||||
{% load static %}
|
||||
<div class="modal fade bs-modal-lg" id="set_scenario_modal" tabindex="-1" aria-labelledby="set_scenario_modal"
|
||||
aria-modal="true" role="dialog">
|
||||
<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">Set Scenarios for {{ current_object.name }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="loading_scenario_div" class="text-center"></div>
|
||||
<form id="set_scenario_modal_form" accept-charset="UTF-8" action="{{ current_object.url }}"
|
||||
data-remote="true" method="post">
|
||||
{% csrf_token %}
|
||||
<label for="scenario-select">Scenarios</label>
|
||||
<select id="scenario-select" name="selected-scenarios" data-actions-box='true' class="form-control"
|
||||
multiple data-width='100%'>
|
||||
<option disabled>Select Scenarios</option>
|
||||
</select>
|
||||
</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="set_scenario_modal_form_submit">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script>
|
||||
|
||||
$(function () {
|
||||
var loaded = false;
|
||||
|
||||
var attachedScenarios = []
|
||||
{% if current_object.scenarios.all %}
|
||||
{% for scen in current_object.scenarios.all %}
|
||||
attachedScenarios.push('{{ scen.url }}')
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
$('#scenario-select').selectpicker();
|
||||
|
||||
$('#set_scenario_modal').on('shown.bs.modal', function () {
|
||||
|
||||
if (!loaded) {
|
||||
makeLoadingGif("#loading_scenario_div", "{% static '/images/wait.gif' %}");
|
||||
$('#loading_scenario_div').append("<p></p><div class='alert alert-info'>Loading Scenarios...</div>");
|
||||
|
||||
$.getJSON("{% url 'package scenario list' meta.current_package.uuid %}").then(function (data) {
|
||||
for(s in data) {
|
||||
scenario = data[s]
|
||||
var selected = attachedScenarios.includes(scenario.url);
|
||||
$('#scenario-select').append(`<option value="${scenario.url}" ${selected ? 'selected' : ''}>${scenario.name}</option>`);
|
||||
}
|
||||
$('#scenario-select').selectpicker('refresh');
|
||||
$("#loading_scenario_div").empty();
|
||||
});
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
$('#set_scenario_modal_form_submit').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
$('#set_scenario_modal_form').submit();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
@ -1,24 +1,24 @@
|
||||
{% load static %}
|
||||
<!-- Delete Compound -->
|
||||
<div id="delete_compound_modal" class="modal" tabindex="-1">
|
||||
<!-- Publish a Package -->
|
||||
<div id="publish_package_modal" class="modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">Delete Compound</h3>
|
||||
<h5 class="modal-title">Publish Package</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Deletes the Compound and associated Structures.
|
||||
<form id="delete-compound-modal-form" accept-charset="UTF-8" action="" data-remote="true" method="post">
|
||||
<p>Clicking on Publish will make this Package publicly available!</p>
|
||||
<form id="publish-package-modal-form" accept-charset="UTF-8" action="{{ current_package.url }}" data-remote="true" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" id="hidden" name="hidden" value="delete-compound"/>
|
||||
<input type="hidden" name="hidden" value="publish-package">
|
||||
</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-compound-modal-submit">Delete</button>
|
||||
<button type="button" class="btn btn-primary" id="publish-package-modal-form-submit">Publish</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -26,9 +26,9 @@
|
||||
<script>
|
||||
$(function() {
|
||||
|
||||
$('#delete-compound-modal-submit').click(function(e){
|
||||
$('#publish-package-modal-form-submit').click(function(e){
|
||||
e.preventDefault();
|
||||
$('#delete-compound-modal-form').submit();
|
||||
$('#publish-package-modal-form').submit();
|
||||
});
|
||||
|
||||
})
|
||||
@ -49,6 +49,18 @@
|
||||
<iframe id="predict-modal-ketcher" src="{% static '/js/ketcher2/ketcher.html' %}" width="100%"
|
||||
height="510"></iframe>
|
||||
</div>
|
||||
|
||||
<label for="prediction-setting">Default Prediction Setting</label>
|
||||
<select id="prediction-setting" name="prediction-setting" class="form-control"
|
||||
data-width='100%'>
|
||||
<option disabled>Select a Setting</option>
|
||||
{% for s in meta.available_settings %}
|
||||
<option value="{{ s.url }}"{% if s.id == meta.user.default_setting.id %}selected{% endif %}>
|
||||
{{ s.name }}{% if s.id == meta.user.default_setting.id %} <i>(User default)</i>{% endif %}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
|
||||
{% block action_modals %}
|
||||
{% include "modals/objects/edit_rule_modal.html" %}
|
||||
{% include "modals/objects/generic_set_scenario_modal.html" %}
|
||||
{% include "modals/objects/generic_delete_modal.html" %}
|
||||
{% endblock action_modals %}
|
||||
|
||||
<div class="panel-group" id="rule-detail">
|
||||
@ -49,7 +51,22 @@
|
||||
</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 }} <i>({{ s.package.name }})</i></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- EC Numbers -->
|
||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||
|
||||
@ -5,7 +5,8 @@
|
||||
{% block action_modals %}
|
||||
{% include "modals/objects/edit_compound_modal.html" %}
|
||||
{% include "modals/objects/add_structure_modal.html" %}
|
||||
{% include "modals/objects/delete_compound_modal.html" %}
|
||||
{% include "modals/objects/generic_set_scenario_modal.html" %}
|
||||
{% include "modals/objects/generic_delete_modal.html" %}
|
||||
{% endblock action_modals %}
|
||||
|
||||
<div class="panel-group" id="compound-detail">
|
||||
@ -134,7 +135,69 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Scenarios -->
|
||||
{% if compound.scenarios.all %}
|
||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||
<h4 class="panel-title">
|
||||
<a id="compound-scenario-link" data-toggle="collapse" data-parent="#compound-detail"
|
||||
href="#compound-scenario">Scenarios</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="compound-scenario" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for s in compound.scenarios.all %}
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- External Identifiers -->
|
||||
{% if compound.get_external_identifiers %}
|
||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||
<h4 class="panel-title">
|
||||
<a id="compound-external-identifier-link" data-toggle="collapse" data-parent="#compound-detail"
|
||||
href="#compound-external-identifier">External Identifier</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="compound-external-identifier" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% if compound.get_pubchem_identifiers %}
|
||||
<div class="panel panel-default panel-heading list-group-item"
|
||||
style="background-color:silver">
|
||||
<h4 class="panel-title">
|
||||
<a id="compound-pubchem-identifier-link" data-toggle="collapse"
|
||||
data-parent="#compound-external-identifier"
|
||||
href="#compound-pubchem-identifier">PubChem Compound Identifier</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="compound-pubchem-identifier" class="panel-collapse collapse in">
|
||||
{% for eid in compound.get_pubchem_identifiers %}
|
||||
<a class="list-group-item"
|
||||
href="{{ eid.external_url }}">CID{{ eid.identifier_value }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if compound.get_chebi_identifiers %}
|
||||
<div class="panel panel-default panel-heading list-group-item"
|
||||
style="background-color:silver">
|
||||
<h4 class="panel-title">
|
||||
<a id="compound-chebi-identifier-link" data-toggle="collapse"
|
||||
data-parent="#compound-external-identifier"
|
||||
href="#compound-chebi-identifier">ChEBI Identifier</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="compound-chebi-identifier" class="panel-collapse collapse in">
|
||||
{% for eid in compound.get_chebi_identifiers %}
|
||||
<a class="list-group-item"
|
||||
href="{{ eid.external_url }}">CHEBI:{{ eid.identifier_value }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
|
||||
{% block action_modals %}
|
||||
{% include "modals/objects/edit_compound_structure_modal.html" %}
|
||||
{% include "modals/objects/generic_set_scenario_modal.html" %}
|
||||
{% include "modals/objects/generic_delete_modal.html" %}
|
||||
{% endblock action_modals %}
|
||||
|
||||
<div class="panel-group" id="compound-structure-detail">
|
||||
@ -54,6 +56,22 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if compound_structure.scenarios.all %}
|
||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||
<h4 class="panel-title">
|
||||
<a id="compound_structure-scenario-link" data-toggle="collapse" data-parent="#compound-structure-detail"
|
||||
href="#compound-structure-scenario">Scenarios</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="compound-structure-scenario" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for s in compound_structure.scenarios.all %}
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Reactions -->
|
||||
|
||||
<!-- Pathways -->
|
||||
|
||||
@ -4,7 +4,8 @@
|
||||
|
||||
{% block action_modals %}
|
||||
{# {% include "modals/objects/edit_edge_modal.html" %}#}
|
||||
{# {% include "modals/objects/delete_edge_modal.html" %}#}
|
||||
{% include "modals/objects/generic_set_scenario_modal.html" %}
|
||||
{% include "modals/objects/generic_delete_modal.html" %}
|
||||
{% endblock action_modals %}
|
||||
|
||||
<div class="panel-group" id="edge-detail">
|
||||
@ -19,7 +20,7 @@
|
||||
style="padding-right:1em"></span></a>
|
||||
<ul id="actionsList" class="dropdown-menu">
|
||||
{% block actions %}
|
||||
{# {% include "actions/objects/edge.html" %}#}
|
||||
{% include "actions/objects/edge.html" %}
|
||||
{% endblock %}
|
||||
</ul>
|
||||
</div>
|
||||
@ -103,6 +104,21 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if edge.scenarios.all %}
|
||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||
<h4 class="panel-title">
|
||||
<a id="edge-scenario-link" data-toggle="collapse" data-parent="#edge-detail"
|
||||
href="#edge-scenario">Scenarios</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="edge-scenario" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for s in edge.scenarios.all %}
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
{% block action_modals %}
|
||||
{% include "modals/objects/edit_group_modal.html" %}
|
||||
{% include "modals/objects/edit_group_member_modal.html" %}
|
||||
{% include "modals/objects/delete_group_modal.html" %}
|
||||
{% include "modals/objects/generic_delete_modal.html" %}
|
||||
{% endblock action_modals %}
|
||||
|
||||
<div class="panel-group" id="package-detail">
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
{% block content %}
|
||||
|
||||
{% block action_modals %}
|
||||
{% include "modals/objects/delete_model_modal.html" %}
|
||||
{% include "modals/objects/generic_delete_modal.html" %}
|
||||
{% endblock action_modals %}
|
||||
|
||||
<!-- Include required libs -->
|
||||
@ -90,27 +90,53 @@
|
||||
</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 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">
|
||||
{% if model.ready_for_prediction %}
|
||||
<!-- 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 id="predictLoading"></div>
|
||||
<div id="predictResultTable"></div>
|
||||
</div>
|
||||
<div id="loading"></div>
|
||||
<div id="predictResultTable"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End Predict Panel -->
|
||||
<!-- End Predict Panel -->
|
||||
{% endif %}
|
||||
|
||||
{% if model.app_domain %}
|
||||
<!-- App Domain -->
|
||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||
<h4 class="panel-title">
|
||||
<a id="app-domain-assessment-link" data-toggle="collapse" data-parent="#model-detail"
|
||||
href="#app-domain-assessment">Applicability Domain Assessment</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="app-domain-assessment" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
<div class="input-group">
|
||||
<input id="smiles-to-assess" 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="assess-button">Assess!</button>
|
||||
</span>
|
||||
</div>
|
||||
<div id="appDomainLoading"></div>
|
||||
<div id="appDomainAssessmentResultTable"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End App Domain -->
|
||||
{% endif %}
|
||||
|
||||
{% if model.model_status == 'FINISHED' %}
|
||||
<!-- Single Gen Curve Panel -->
|
||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||
@ -243,7 +269,7 @@
|
||||
|
||||
<script>
|
||||
|
||||
function handleResponse(data) {
|
||||
function handlePredictionResponse(data) {
|
||||
res = "<table class='table table-striped'>"
|
||||
res += "<thead>"
|
||||
res += "<th scope='col'>#</th>"
|
||||
@ -277,9 +303,9 @@
|
||||
$("#predictResultTable").append(res);
|
||||
}
|
||||
|
||||
function clear() {
|
||||
$("#predictResultTable").removeClass("alert alert-danger");
|
||||
$("#predictResultTable").empty();
|
||||
function clear(divid) {
|
||||
$("#" + divid).removeClass("alert alert-danger");
|
||||
$("#" + divid).empty();
|
||||
}
|
||||
|
||||
if ($('#predict-button').length > 0) {
|
||||
@ -291,32 +317,70 @@
|
||||
"classify": "ILikeCats!"
|
||||
}
|
||||
|
||||
clear();
|
||||
clear("predictResultTable");
|
||||
|
||||
makeLoadingGif("#loading", "{% static '/images/wait.gif' %}");
|
||||
makeLoadingGif("#predictLoading", "{% static '/images/wait.gif' %}");
|
||||
$.ajax({
|
||||
type: 'get',
|
||||
data: data,
|
||||
url: '',
|
||||
success: function (data, textStatus) {
|
||||
try {
|
||||
$("#loading").empty();
|
||||
handleResponse(data);
|
||||
$("#predictLoading").empty();
|
||||
handlePredictionResponse(data);
|
||||
} catch (error) {
|
||||
console.log("Error");
|
||||
$("#loading").empty();
|
||||
$("#predictLoading").empty();
|
||||
$("#predictResultTable").addClass("alert alert-danger");
|
||||
$("#predictResultTable").append("Error while processing request :/");
|
||||
}
|
||||
},
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
$("#loading").empty();
|
||||
$("#predictLoading").empty();
|
||||
$("#predictResultTable").addClass("alert alert-danger");
|
||||
$("#predictResultTable").append("Error while processing request :/");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if ($('#assess-button').length > 0) {
|
||||
$("#assess-button").on("click", function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
data = {
|
||||
"smiles": $("#smiles-to-assess").val(),
|
||||
"app-domain-assessment": "ILikeCats!"
|
||||
}
|
||||
|
||||
clear("appDomainAssessmentResultTable");
|
||||
|
||||
makeLoadingGif("#appDomainLoading", "{% static '/images/wait.gif' %}");
|
||||
$.ajax({
|
||||
type: 'get',
|
||||
data: data,
|
||||
url: '',
|
||||
success: function (data, textStatus) {
|
||||
try {
|
||||
$("#appDomainLoading").empty();
|
||||
handleAssessmentResponse("{% url 'depict' %}", data);
|
||||
console.log(data);
|
||||
} catch (error) {
|
||||
console.log("Error");
|
||||
$("#appDomainLoading").empty();
|
||||
$("#appDomainAssessmentResultTable").addClass("alert alert-danger");
|
||||
$("#appDomainAssessmentResultTable").append("Error while processing request :/");
|
||||
}
|
||||
},
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
$("#appDomainLoading").empty();
|
||||
$("#appDomainAssessmentResultTable").addClass("alert alert-danger");
|
||||
$("#appDomainAssessmentResultTable").append("Error while processing request :/");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
@ -4,7 +4,8 @@
|
||||
|
||||
{% block action_modals %}
|
||||
{% include "modals/objects/edit_node_modal.html" %}
|
||||
{% include "modals/objects/delete_node_modal.html" %}
|
||||
{% include "modals/objects/generic_set_scenario_modal.html" %}
|
||||
{% include "modals/objects/generic_delete_modal.html" %}
|
||||
{% endblock action_modals %}
|
||||
|
||||
<div class="panel-group" id="node-detail">
|
||||
@ -69,7 +70,34 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if node.scenarios.all %}
|
||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||
<h4 class="panel-title">
|
||||
<a id="node-scenario-link" data-toggle="collapse" data-parent="#node-detail"
|
||||
href="#node-scenario">Scenarios</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="node-scenario" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for s in node.scenarios.all %}
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if app_domain_assessment_data %}
|
||||
<div id="appDomainAssessmentResultTable"></div>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
handleAssessmentResponse("{% url 'depict' %}", {{ app_domain_assessment_data|safe }})
|
||||
})
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
@ -5,8 +5,9 @@
|
||||
{% block action_modals %}
|
||||
{% include "modals/objects/edit_package_modal.html" %}
|
||||
{% include "modals/objects/edit_package_permissions_modal.html" %}
|
||||
{% include "modals/objects/publish_package_modal.html" %}
|
||||
{% include "modals/objects/set_license_modal.html" %}
|
||||
{% include "modals/objects/delete_package_modal.html" %}
|
||||
{% include "modals/objects/generic_delete_modal.html" %}
|
||||
{% endblock action_modals %}
|
||||
|
||||
<div class="panel-group" id="package-detail">
|
||||
@ -52,23 +53,5 @@
|
||||
|
||||
</div>
|
||||
|
||||
{% if package.license %}
|
||||
<p></p>
|
||||
<div class="panel-group" id="license_accordion">
|
||||
<div class="panel panel-default list-group-item" style="background-color:#f5f5f5">
|
||||
<div class="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#licence_accordion" href="#license">License</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="license" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
<a target="_blank" href="{{ package.license.link }}">
|
||||
<img src="{{ package.license.image_link }}">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
@ -4,16 +4,22 @@
|
||||
|
||||
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||
<style>
|
||||
svg {
|
||||
#vizdiv {
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
#pwsvg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.link {
|
||||
stroke: #999;
|
||||
stroke-opacity: 0.6;
|
||||
marker-end: url(#arrow);
|
||||
//marker-end: url(#arrow);
|
||||
}
|
||||
|
||||
.link_no_arrow {
|
||||
@ -31,6 +37,31 @@
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
|
||||
.inside_app_domain {
|
||||
fill: green;
|
||||
stroke: green;
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
|
||||
.outside_app_domain {
|
||||
fill: red;
|
||||
stroke: red;
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
|
||||
.passes_app_domain {
|
||||
stroke: green;
|
||||
stroke-width: 1.5px;
|
||||
stroke-opacity: 0.6;
|
||||
}
|
||||
|
||||
.fails_app_domain {
|
||||
stroke: red;
|
||||
stroke-width: 1.5px;
|
||||
stroke-opacity: 0.6;
|
||||
}
|
||||
|
||||
|
||||
.highlighted {
|
||||
stroke: red;
|
||||
stroke-width: 3px;
|
||||
@ -50,10 +81,12 @@
|
||||
{% block action_modals %}
|
||||
{% include "modals/objects/add_pathway_node_modal.html" %}
|
||||
{% include "modals/objects/add_pathway_edge_modal.html" %}
|
||||
{% include "modals/objects/download_pathway_modal.html" %}
|
||||
{% include "modals/objects/edit_pathway_modal.html" %}
|
||||
{% include "modals/objects/generic_set_scenario_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" %}
|
||||
{% include "modals/objects/generic_delete_modal.html" %}
|
||||
{% endblock action_modals %}
|
||||
|
||||
<p></p>
|
||||
@ -79,8 +112,7 @@
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="dropdown requiresWritePerm">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false">
|
||||
aria-haspopup="true" aria-expanded="false">
|
||||
<span class="glyphicon glyphicon-edit"></span>
|
||||
Edit
|
||||
<span class="caret"></span></a>
|
||||
@ -90,11 +122,26 @@
|
||||
{% endblock %}
|
||||
</ul>
|
||||
</li>
|
||||
{% if pathway.setting.model.app_domain %}
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||
aria-haspopup="true" aria-expanded="false">
|
||||
<span class="glyphicon glyphicon-eye-open"></span>
|
||||
View
|
||||
<span class="caret"></span></a>
|
||||
<ul id="editingList" class="dropdown-menu">
|
||||
<li>
|
||||
<a class="button" id="app-domain-toggle-button">
|
||||
<i id="app-domain-toggle-button" class="glyphicon glyphicon-eye-open"></i> App Domain View</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<a role="button" data-toggle="modal" onclick="goFullscreen('pwcontent')">
|
||||
<a role="button" data-toggle="modal" onclick="goFullscreen('vizdiv')">
|
||||
<span class="glyphicon glyphicon-fullscreen"></span>
|
||||
Fullscreen
|
||||
</a>
|
||||
@ -125,16 +172,24 @@
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
<div id="vizdiv">
|
||||
<svg width="2000" height="2000">
|
||||
<div id="vizdiv" >
|
||||
<svg id="pwsvg">
|
||||
{% 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">
|
||||
orient="auto-start-reverse" markerUnits="userSpaceOnUse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="#999"/>
|
||||
</marker>
|
||||
<marker id="arrow_passes_app_domain" viewBox="0 0 10 10" refX="43" refY="5" markerWidth="6" markerHeight="6"
|
||||
orient="auto-start-reverse" markerUnits="userSpaceOnUse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="green"/>
|
||||
</marker>
|
||||
<marker id="arrow_fails_app_domain" viewBox="0 0 10 10" refX="43" refY="5" markerWidth="6" markerHeight="6"
|
||||
orient="auto-start-reverse" markerUnits="userSpaceOnUse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="red"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<g id="zoomable"></g>
|
||||
</svg>
|
||||
@ -153,13 +208,29 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if pathway.scenarios.all %}
|
||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||
<h4 class="panel-title">
|
||||
<a id="pathway-scenario-link" data-toggle="collapse" data-parent="#pathway-detail"
|
||||
href="#pathway-scenario">Scenarios</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="pathway-scenario" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for s in pathway.scenarios.all %}
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if pathway.setting %}
|
||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||
<h4 class="panel-title">
|
||||
<a id="pathwaySettingLink" data-toggle="collapse" data-parent="#pathwayAccordion"
|
||||
href="#pathwaySetting">Setting</a></h4>
|
||||
</div>
|
||||
<div id="pathwaySetting" class="panel-collapse collapse in">
|
||||
<div id="pathwaySetting" class="panel-collapse collapse">
|
||||
<div class="panel-body list-group-item" id="pathwaySettingContent">
|
||||
<table class="table table-bordered table-hover">
|
||||
<tr style="background-color: rgba(0, 0, 0, 0.08);">
|
||||
@ -245,6 +316,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// Globla switch for app domain view
|
||||
var appDomainViewEnabled = false;
|
||||
|
||||
function goFullscreen(id) {
|
||||
var element = document.getElementById(id);
|
||||
@ -266,6 +339,51 @@
|
||||
// TODO fix somewhere else...
|
||||
var newDesc = transformReferences($('#DescriptionContent')[0].innerText);
|
||||
$('#DescriptionContent').html(newDesc);
|
||||
|
||||
|
||||
$('#app-domain-toggle-button').on('click', function () {
|
||||
// glyphicon glyphicon-eye-close
|
||||
// glyphicon glyphicon-eye-open
|
||||
appDomainViewEnabled = !appDomainViewEnabled;
|
||||
|
||||
if (appDomainViewEnabled) {
|
||||
$('#app-domain-toggle-button > i').removeClass('glyphicon-eye-open');
|
||||
$('#app-domain-toggle-button > i').addClass('glyphicon-eye-close');
|
||||
nodes.forEach((x) => {
|
||||
if(x.app_domain) {
|
||||
if (x.app_domain.inside_app_domain) {
|
||||
d3.select(x.el).select("circle").classed("inside_app_domain", true);
|
||||
} else {
|
||||
d3.select(x.el).select("circle").classed("outside_app_domain", true);
|
||||
}
|
||||
}
|
||||
});
|
||||
links.forEach((x) => {
|
||||
if(x.app_domain) {
|
||||
if (x.app_domain.passes_app_domain) {
|
||||
d3.select(x.el).attr("marker-end", d => d.target.pseudo ? "" : "url(#arrow_passes_app_domain)");
|
||||
d3.select(x.el).classed("passes_app_domain", true);
|
||||
} else {
|
||||
d3.select(x.el).attr("marker-end", d => d.target.pseudo ? "" : "url(#arrow_fails_app_domain)");
|
||||
d3.select(x.el).classed("fails_app_domain", true);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$('#app-domain-toggle-button > i').removeClass('glyphicon-eye-close');
|
||||
$('#app-domain-toggle-button > i').addClass('glyphicon-eye-open');
|
||||
nodes.forEach((x) => {
|
||||
d3.select(x.el).select("circle").classed("inside_app_domain", false);
|
||||
d3.select(x.el).select("circle").classed("outside_app_domain", false);
|
||||
});
|
||||
links.forEach((x) => {
|
||||
d3.select(x.el).attr("marker-end", d => d.target.pseudo ? "" : "url(#arrow)");
|
||||
d3.select(x.el).classed("passes_app_domain", false);
|
||||
d3.select(x.el).classed("fails_app_domain", false);
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
@ -4,7 +4,8 @@
|
||||
|
||||
{% block action_modals %}
|
||||
{% include "modals/objects/edit_reaction_modal.html" %}
|
||||
{% include "modals/objects/delete_reaction_modal.html" %}
|
||||
{% include "modals/objects/generic_set_scenario_modal.html" %}
|
||||
{% include "modals/objects/generic_delete_modal.html" %}
|
||||
{% endblock action_modals %}
|
||||
|
||||
<div class="panel-group" id="reaction-detail">
|
||||
@ -118,9 +119,70 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if reaction.scenarios.all %}
|
||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||
<h4 class="panel-title">
|
||||
<a id="reaction-scenario-link" data-toggle="collapse" data-parent="#reaction-detail"
|
||||
href="#reaction-scenario">Scenarios</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="reaction-scenario" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% for s in reaction.scenarios.all %}
|
||||
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- External Identifiers -->
|
||||
{% if reaction.get_external_identifiers %}
|
||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||
<h4 class="panel-title">
|
||||
<a id="reaction-external-identifier-link" data-toggle="collapse" data-parent="#reaction-detail"
|
||||
href="#reaction-external-identifier">External Identifier</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="reaction-external-identifier" class="panel-collapse collapse in">
|
||||
<div class="panel-body list-group-item">
|
||||
{% if reaction.get_rhea_identifiers %}
|
||||
<div class="panel panel-default panel-heading list-group-item"
|
||||
style="background-color:silver">
|
||||
<h4 class="panel-title">
|
||||
<a id="reaction-rhea-identifier-link" data-toggle="collapse"
|
||||
data-parent="#reaction-external-identifier"
|
||||
href="#reaction-rhea-identifier">Rhea</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="reaction-rhea-identifier" class="panel-collapse collapse in">
|
||||
{% for eid in reaction.get_rhea_identifiers %}
|
||||
<a class="list-group-item"
|
||||
href="{{ eid.external_url }}">{{ eid.identifier_value }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if reaction.get_uniprot_identifiers %}
|
||||
<div class="panel panel-default panel-heading list-group-item"
|
||||
style="background-color:silver">
|
||||
<h4 class="panel-title">
|
||||
<a id="reaction-uniprot-identifier-link" data-toggle="collapse"
|
||||
data-parent="#reaction-external-identifier"
|
||||
href="#reaction-uniprot-identifier">UniProt</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="reaction-uniprot-identifier" class="panel-collapse collapse in">
|
||||
{% for eid in reaction.get_uniprot_identifiers %}
|
||||
<a class="list-group-item"
|
||||
href="{{ eid.external_url }}">10 SwissProt entries ({{ eid.identifier_value }})</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
{% block content %}
|
||||
|
||||
{% block action_modals %}
|
||||
|
||||
{% include "modals/objects/generic_delete_modal.html" %}
|
||||
{% endblock action_modals %}
|
||||
<div class="panel-group" id="scenario-detail">
|
||||
<div class="panel panel-default">
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
|
||||
{% block action_modals %}
|
||||
{% include "modals/objects/edit_rule_modal.html" %}
|
||||
{% include "modals/objects/generic_set_scenario_modal.html" %}
|
||||
{% include "modals/objects/generic_delete_modal.html" %}
|
||||
{% endblock action_modals %}
|
||||
|
||||
<div class="panel-group" id="rule-detail">
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
{% include "modals/objects/edit_password_modal.html" %}
|
||||
{% include "modals/collections/new_prediction_setting_modal.html" %}
|
||||
{% include "modals/objects/manage_api_token_modal.html" %}
|
||||
{% include "modals/objects/delete_user_modal.html" %}
|
||||
{% include "modals/objects/generic_delete_modal.html" %}
|
||||
{% endblock action_modals %}
|
||||
|
||||
<div class="panel-group" id="user-detail">
|
||||
|
||||
@ -79,6 +79,10 @@
|
||||
|
||||
allEmpty = true;
|
||||
for (key in data) {
|
||||
if (key === 'searchterm') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data[key].length < 1) {
|
||||
continue;
|
||||
}
|
||||
@ -176,8 +180,16 @@
|
||||
$("#selPackages").selectpicker();
|
||||
$("#search-button").on("click", search);
|
||||
|
||||
$("#searchbar").on("keydown", function (e) {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
search(e);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
{% if search_result %}
|
||||
$('#searchbar').val('{{ search_result.searchterm }}')
|
||||
handleSearchResponse("results", {{ search_result|safe }});
|
||||
{% endif %}
|
||||
</script>
|
||||
|
||||
52
tests/test_dataset.py
Normal file
52
tests/test_dataset.py
Normal file
@ -0,0 +1,52 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from epdb.logic import PackageManager
|
||||
from epdb.models import Reaction, Compound, User, Rule
|
||||
from utilities.ml import Dataset
|
||||
|
||||
|
||||
class DatasetTest(TestCase):
|
||||
fixtures = ["test_fixture.cleaned.json"]
|
||||
|
||||
def setUp(self):
|
||||
self.cs1 = Compound.create(
|
||||
self.package,
|
||||
name='2,6-Dibromohydroquinone',
|
||||
description='http://localhost:8000/package/32de3cf4-e3e6-4168-956e-32fa5ddb0ce1/compound/d6435251-1a54-4327-b4b1-fd6e9a8f4dc9/structure/d8a0225c-dbb5-4e6c-a642-730081c09c5b',
|
||||
smiles='C1=C(C(=C(C=C1O)Br)O)Br',
|
||||
).default_structure
|
||||
|
||||
self.cs2 = Compound.create(
|
||||
self.package,
|
||||
smiles='O=C(O)CC(=O)/C=C(/Br)C(=O)O',
|
||||
).default_structure
|
||||
|
||||
self.rule1 = Rule.create(
|
||||
rule_type='SimpleAmbitRule',
|
||||
package=self.package,
|
||||
smirks='[#8:8]([H])-[c:4]1[c:3]([H])[c:2](-[#1,#17,#35:9])[c:1](-[#8:7]([H]))[c:6](-[#1,#17,#35])[c:5]([H])1>>[#8-]-[#6:6](=O)-[#6:5]-[#6:4](=[O:8])\[#6:3]=[#6:2](\[#1,#17,#35:9])-[#6:1](-[#8-])=[O:7]',
|
||||
description='http://localhost:8000/package/32de3cf4-e3e6-4168-956e-32fa5ddb0ce1/simple-ambit-rule/f6a56c0f-a4a0-4ee3-b006-d765b4767cf6'
|
||||
)
|
||||
|
||||
self.reaction1 = Reaction.create(
|
||||
package=self.package,
|
||||
educts=[self.cs1],
|
||||
products=[self.cs2],
|
||||
rules=[self.rule1],
|
||||
multi_step=False
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(DatasetGeneratorTest, cls).setUpClass()
|
||||
cls.user = User.objects.get(username='anonymous')
|
||||
cls.package = PackageManager.create_package(cls.user, 'Anon Test Package', 'No Desc')
|
||||
|
||||
def test_smoke(self):
|
||||
reactions = [r for r in Reaction.objects.filter(package=self.package)]
|
||||
applicable_rules = [self.rule1]
|
||||
|
||||
ds = Dataset.generate_dataset(reactions, applicable_rules)
|
||||
|
||||
self.assertEqual(len(ds.y()), 1)
|
||||
self.assertEqual(sum(ds.y()[0]), 1)
|
||||
@ -1,111 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from epdb.models import ParallelRule
|
||||
from utilities.ml import Compound, Reaction, DatasetGenerator
|
||||
|
||||
|
||||
class CompoundTest(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.c1 = Compound(smiles="CCN(CC)C(=O)C1=CC(=CC=C1)C", uuid='c1')
|
||||
self.c2 = Compound(smiles="CCN(CC)C(=O)C1=CC(=CC=C1)C", uuid='c2')
|
||||
|
||||
def test_compound_eq_ignores_uuid(self):
|
||||
self.assertEqual(self.c1, self.c2)
|
||||
|
||||
|
||||
class ReactionTest(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.c1 = Compound(smiles="CCN(CC)C(=O)C1=CC(=CC=C1)C")
|
||||
self.c2 = Compound(smiles="CCN(CCO)C(=O)C1=CC(C)=CC=C1")
|
||||
# self.r1 = Rule(uuid="bt0334")
|
||||
# c1 --r1--> c2
|
||||
self.c3_1 = Compound(smiles="CCNC(=O)C1=CC(C)=CC=C1")
|
||||
self.c3_2 = Compound(smiles="CC=O")
|
||||
# self.r2 = Rule(uuid="bt0243")
|
||||
# c1 --r2--> c3_1, c3_2
|
||||
|
||||
def test_reaction_equality_ignores_uuid(self):
|
||||
r1 = Reaction([self.c1], [self.c2], self.r1, uuid="abc")
|
||||
r2 = Reaction([self.c1], [self.c2], self.r1, uuid="xyz")
|
||||
self.assertEqual(r1, r2)
|
||||
|
||||
def test_reaction_inequality_on_data_change(self):
|
||||
r1 = Reaction([self.c1], [self.c2], self.r1)
|
||||
r2 = Reaction([self.c1], [self.c3_1], self.r1)
|
||||
self.assertNotEqual(r1, r2)
|
||||
|
||||
def test_reaction_is_hashable(self):
|
||||
r = Reaction([self.c1], [self.c2], self.r1)
|
||||
reactions = {r}
|
||||
self.assertIn(Reaction([self.c1], [self.c2], self.r1), reactions)
|
||||
|
||||
def test_rule_is_optional(self):
|
||||
r = Reaction([self.c1], [self.c2])
|
||||
self.assertIsNone(r.rule)
|
||||
|
||||
def test_uuid_is_optional(self):
|
||||
r = Reaction([self.c1], [self.c2], self.r1)
|
||||
self.assertIsNone(r.uuid)
|
||||
|
||||
def test_repr_includes_uuid(self):
|
||||
r = Reaction([self.c1], [self.c2], self.r1, uuid="abc")
|
||||
self.assertIn("abc", repr(r))
|
||||
|
||||
def test_reaction_equality_with_multiple_compounds_different_ordering(self):
|
||||
r1 = Reaction([self.c1], [self.c3_1, self.c3_2], self.r2)
|
||||
r2 = Reaction([self.c1], [self.c3_2, self.c3_1], self.r2)
|
||||
|
||||
self.assertEqual(r1, r2, "Reaction equality should not rely on list order")
|
||||
|
||||
|
||||
class RuleTest(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
# self.r1 = Rule(uuid="bt0334")
|
||||
# self.r2 = Rule(uuid="bt0243")
|
||||
|
||||
|
||||
class DatasetGeneratorTest(TestCase):
|
||||
fixtures = ['bootstrap.json']
|
||||
|
||||
def setUp(self):
|
||||
self.c1 = Compound(smiles="CCN(CC)C(=O)C1=CC(=CC=C1)C")
|
||||
self.c2 = Compound(smiles="CCN(CCO)C(=O)C1=CC(C)=CC=C1")
|
||||
self.c3_1 = Compound(smiles="CCNC(=O)C1=CC(C)=CC=C1")
|
||||
self.c3_2 = Compound(smiles="CC=O")
|
||||
|
||||
# self.r1 = Rule(uuid="bt0334") # trig
|
||||
# self.r2 = Rule(uuid="bt0243") # trig
|
||||
# self.r3 = Rule(uuid="bt0003") # no trig
|
||||
|
||||
self.reaction1 = Reaction([self.c1], [self.c2], self.r3)
|
||||
self.reaction2 = Reaction([self.c1], [self.c3_1, self.c3_2], self.r2)
|
||||
|
||||
|
||||
|
||||
|
||||
def test_test(self):
|
||||
compounds = [
|
||||
self.c1,
|
||||
self.c2,
|
||||
self.c3_1,
|
||||
self.c3_2,
|
||||
]
|
||||
|
||||
reactions = [
|
||||
self.reaction1,
|
||||
self.reaction2,
|
||||
]
|
||||
|
||||
applicable_rules = [
|
||||
# Rule('bt0334', ParallelRule.objects.get(name='bt0334')),
|
||||
# Rule('bt0243', ParallelRule.objects.get(name='bt0243')),
|
||||
# Rule('bt0003', ParallelRule.objects.get(name='bt0003')),
|
||||
]
|
||||
|
||||
ds = DatasetGenerator.generate_dataset(compounds, reactions, applicable_rules)
|
||||
|
||||
self.assertIsNotNone(ds)
|
||||
55
tests/test_model.py
Normal file
55
tests/test_model.py
Normal file
@ -0,0 +1,55 @@
|
||||
import json
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
from epdb.logic import PackageManager
|
||||
from epdb.models import Compound, User, CompoundStructure, Reaction, Rule, MLRelativeReasoning
|
||||
|
||||
|
||||
class ModelTest(TestCase):
|
||||
fixtures = ["test_fixture.cleaned.json"]
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(ModelTest, cls).setUpClass()
|
||||
cls.user = User.objects.get(username='anonymous')
|
||||
cls.package = PackageManager.create_package(cls.user, 'Anon Test Package', 'No Desc')
|
||||
bbd_data = json.load(open('fixtures/packages/2025-07-18/EAWAG-BBD.json'))
|
||||
cls.BBD = PackageManager.import_package(bbd_data, cls.user)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_smoke(self):
|
||||
threshold = float(0.5)
|
||||
|
||||
# get Package objects from urls
|
||||
rule_package_objs = [self.BBD]
|
||||
data_package_objs = [self.BBD]
|
||||
eval_packages_objs = []
|
||||
|
||||
mod = MLRelativeReasoning.create(
|
||||
self.package,
|
||||
rule_package_objs,
|
||||
data_package_objs,
|
||||
eval_packages_objs,
|
||||
threshold,
|
||||
'ECC - BBD - 0.5',
|
||||
'Created MLRelativeReasoning in Testcase',
|
||||
)
|
||||
ds = mod.load_dataset()
|
||||
|
||||
mod.build_model()
|
||||
print("Model built!")
|
||||
mod.evaluate_model()
|
||||
print("Model Evaluated")
|
||||
|
||||
results = mod.predict('CCN(CC)C(=O)C1=CC(=CC=C1)C')
|
||||
print(results)
|
||||
@ -19,9 +19,8 @@ class RuleApplicationTest(TestCase):
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
from collections import Counter
|
||||
# print(Counter(cls.error_smiles))
|
||||
pass
|
||||
print(f"\nTotal Errors across Rules {len(cls.error_smiles)}")
|
||||
# print(cls.error_smiles)
|
||||
|
||||
def tearDown(self):
|
||||
print(f"\nTotal errors {self.total_errors}")
|
||||
@ -36,7 +35,7 @@ class RuleApplicationTest(TestCase):
|
||||
for comp, ambit_prod in zip(bt_rule['compounds'], bt_rule['products']):
|
||||
|
||||
smi = comp['smiles']
|
||||
products = FormatConverter.apply(smi, smirks, preprocess_smiles=True, bracketize=False)
|
||||
products = FormatConverter.apply(smi, smirks)
|
||||
|
||||
all_rdkit_prods = []
|
||||
for ps in products:
|
||||
@ -53,15 +52,15 @@ class RuleApplicationTest(TestCase):
|
||||
|
||||
# TODO mode "intersection"
|
||||
# partial_res = (len(set(ambit_smiles).intersection(set(rdkit_smiles))) > 0) or (len(ambit_smiles) == 0)
|
||||
# FAILED (failures=42)
|
||||
# FAILED (failures=33)
|
||||
|
||||
# TODO mode = "full ambit"
|
||||
# partial_res = len(set(ambit_smiles).intersection(set(rdkit_smiles))) == len(ambit_smiles)
|
||||
# FAILED (failures=52)
|
||||
# FAILED (failures=44)
|
||||
|
||||
# TODO mode = "equality"
|
||||
partial_res = set(ambit_smiles) == set(rdkit_smiles)
|
||||
# FAILED (failures=71)
|
||||
# FAILED (failures=64)
|
||||
|
||||
if len(ambit_smiles) and not partial_res:
|
||||
print(f"""
|
||||
|
||||
@ -12,6 +12,8 @@ from rdkit.Chem import MACCSkeys
|
||||
from rdkit.Chem import rdChemReactions
|
||||
from rdkit.Chem.Draw import rdMolDraw2D
|
||||
from rdkit.Chem.MolStandardize import rdMolStandardize
|
||||
from rdkit.Chem.rdmolops import GetMolFrags
|
||||
from rdkit.Contrib.IFG import ifg
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
RDLogger.DisableLog('rdApp.*')
|
||||
@ -87,6 +89,21 @@ class FormatConverter(object):
|
||||
bitvec = MACCSkeys.GenMACCSKeys(mol)
|
||||
return bitvec.ToList()
|
||||
|
||||
@staticmethod
|
||||
def get_functional_groups(smiles: str) -> List[str]:
|
||||
res = list()
|
||||
|
||||
try:
|
||||
m = Chem.MolFromSmiles(smiles)
|
||||
fgs = ifg.identify_functional_groups(m)
|
||||
for fg in fgs:
|
||||
# TODO atoms or type?
|
||||
res.append(fg.atoms)
|
||||
except AttributeError:
|
||||
logger.debug(f"Could not get functional groups for {smiles}")
|
||||
|
||||
return res
|
||||
|
||||
@staticmethod
|
||||
def to_svg(smiles, mol_size=(200, 150), kekulize=True):
|
||||
mol = FormatConverter.from_smiles(smiles)
|
||||
@ -131,6 +148,24 @@ class FormatConverter(object):
|
||||
# TODO call to AMBIT Service
|
||||
return smiles
|
||||
|
||||
@staticmethod
|
||||
def ep_standardize(smiles):
|
||||
change = True
|
||||
while change:
|
||||
change = False
|
||||
for standardizer in MATCH_STANDARDIZER:
|
||||
tmp_smiles = standardizer.standardize(smiles)
|
||||
|
||||
if tmp_smiles != smiles:
|
||||
print(f"change {smiles} to {tmp_smiles}")
|
||||
change = True
|
||||
smiles = tmp_smiles
|
||||
|
||||
if change is False:
|
||||
print(f"nothing changed")
|
||||
|
||||
return smiles
|
||||
|
||||
@staticmethod
|
||||
def standardize(smiles):
|
||||
# Taken from https://bitsilla.com/blog/2021/06/standardizing-a-molecule-using-rdkit/
|
||||
@ -180,54 +215,6 @@ class FormatConverter(object):
|
||||
atom.UpdatePropertyCache()
|
||||
return mol
|
||||
|
||||
# @staticmethod
|
||||
# def apply(smiles, smirks, preprocess_smiles=True, bracketize=False, standardize=True):
|
||||
# logger.debug(f'Applying {smirks} on {smiles}')
|
||||
#
|
||||
# if bracketize:
|
||||
# smirks = smirks.split('>>')[0] + ">>(" + smirks.split('>>')[1] + ")"
|
||||
#
|
||||
# res = set()
|
||||
# try:
|
||||
# rxn = rdChemReactions.ReactionFromSmarts(smirks)
|
||||
# mol = Chem.MolFromSmiles(smiles)
|
||||
#
|
||||
# # Inplace
|
||||
# if preprocess_smiles:
|
||||
# Chem.SanitizeMol(mol)
|
||||
# mol = Chem.AddHs(mol)
|
||||
#
|
||||
# # apply!
|
||||
# reacts = rxn.RunReactants((mol,))
|
||||
# if len(reacts):
|
||||
# # Sanitize mols
|
||||
# for product_set in reacts:
|
||||
# prod_set = list()
|
||||
# for product in product_set:
|
||||
# # Fixes
|
||||
# # [2025-01-30 23:00:50] ERROR chem - Sanitizing and converting failed:
|
||||
# # non-ring atom 3 marked aromatic
|
||||
# # But does not improve overall performance
|
||||
# #
|
||||
# # for a in product.GetAtoms():
|
||||
# # if (not a.IsInRing()) and a.GetIsAromatic():
|
||||
# # a.SetIsAromatic(False)
|
||||
# # for b in product.GetBonds():
|
||||
# # if (not b.IsInRing()) and b.GetIsAromatic():
|
||||
# # b.SetIsAromatic(False)
|
||||
#
|
||||
# try:
|
||||
# Chem.SanitizeMol(product)
|
||||
# prod_set.append(FormatConverter.standardize(Chem.MolToSmiles(product)))
|
||||
# except ValueError as e:
|
||||
# logger.error(f'Sanitizing and converting failed:\n{e}')
|
||||
# continue
|
||||
# res.add(tuple(list(set(prod_set))))
|
||||
# except Exception as e:
|
||||
# logger.error(f'Applying {smirks} on {smiles} failed:\n{e}')
|
||||
#
|
||||
# return list(res)
|
||||
|
||||
@staticmethod
|
||||
def is_valid_smirks(smirks: str) -> bool:
|
||||
try:
|
||||
@ -237,7 +224,7 @@ class FormatConverter(object):
|
||||
return False
|
||||
|
||||
@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 = True,
|
||||
standardize: bool = True, kekulize: bool = True) -> List['ProductSet']:
|
||||
logger.debug(f'Applying {smirks} on {smiles}')
|
||||
|
||||
@ -266,8 +253,10 @@ class FormatConverter(object):
|
||||
for product in product_set:
|
||||
try:
|
||||
Chem.SanitizeMol(product)
|
||||
|
||||
product = FormatConverter.standardize(Chem.MolToSmiles(product))
|
||||
product = GetMolFrags(product, asMols=True)
|
||||
for p in product:
|
||||
p = FormatConverter.standardize(Chem.MolToSmiles(p))
|
||||
prods.append(p)
|
||||
|
||||
# if kekulize:
|
||||
# # from rdkit.Chem import MolStandardize
|
||||
@ -292,13 +281,12 @@ class FormatConverter(object):
|
||||
# # bond.SetIsAromatic(False)
|
||||
# Chem.Kekulize(product)
|
||||
|
||||
prods.append(product)
|
||||
|
||||
except ValueError as e:
|
||||
logger.error(f'Sanitizing and converting failed:\n{e}')
|
||||
continue
|
||||
|
||||
# TODO doc!
|
||||
if len(prods) and len(prods) == len(product_set):
|
||||
if len(prods):
|
||||
ps = ProductSet(prods)
|
||||
pss.add(ps)
|
||||
|
||||
@ -651,20 +639,23 @@ class IndigoUtils(object):
|
||||
environment.add(mappedAtom.index())
|
||||
|
||||
for k, v in functional_groups.items():
|
||||
try:
|
||||
sanitized = IndigoUtils.sanitize_functional_group(k)
|
||||
|
||||
sanitized = IndigoUtils.sanitize_functional_group(k)
|
||||
query = indigo.loadSmarts(sanitized)
|
||||
|
||||
query = indigo.loadSmarts(sanitized)
|
||||
for match in matcher.iterateMatches(query):
|
||||
if match is not None:
|
||||
|
||||
for match in matcher.iterateMatches(query):
|
||||
if match is not None:
|
||||
for atom in query.iterateAtoms():
|
||||
mappedAtom = match.mapAtom(atom)
|
||||
if mappedAtom is None or mappedAtom.index() in environment:
|
||||
continue
|
||||
|
||||
for atom in query.iterateAtoms():
|
||||
mappedAtom = match.mapAtom(atom)
|
||||
if mappedAtom is None or mappedAtom.index() in environment:
|
||||
continue
|
||||
counts[mappedAtom.index()] = max(v, counts[mappedAtom.index()])
|
||||
|
||||
counts[mappedAtom.index()] = max(v, counts[mappedAtom.index()])
|
||||
except IndigoException as e:
|
||||
logger.debug(f'Colorizing failed due to {e}')
|
||||
|
||||
for k, v in counts.items():
|
||||
if is_reaction:
|
||||
|
||||
540
utilities/ml.py
540
utilities/ml.py
@ -1,46 +1,29 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
import logging
|
||||
from abc import ABC, abstractmethod
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
from typing import List, Dict, Set, Tuple
|
||||
|
||||
import numpy as np
|
||||
from sklearn.base import BaseEstimator, ClassifierMixin
|
||||
from sklearn.decomposition import PCA
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
from sklearn.metrics import accuracy_score
|
||||
from sklearn.multioutput import ClassifierChain
|
||||
from sklearn.preprocessing import StandardScaler
|
||||
from sklearn.tree import DecisionTreeClassifier
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
|
||||
# @dataclasses.dataclass
|
||||
# class Feature:
|
||||
# name: str
|
||||
# value: float
|
||||
#
|
||||
#
|
||||
#
|
||||
# class Row:
|
||||
# def __init__(self, compound_uuid: str, compound_smiles: str, descriptors: List[int]):
|
||||
# self.data = {}
|
||||
#
|
||||
#
|
||||
#
|
||||
# class DataSet(object):
|
||||
#
|
||||
# def __init__(self):
|
||||
# self.rows: List[Row] = []
|
||||
#
|
||||
# def add_row(self, row: Row):
|
||||
# pass
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from utilities.chem import FormatConverter
|
||||
from utilities.chem import FormatConverter, PredictionResult
|
||||
|
||||
|
||||
@dataclass
|
||||
class Compound:
|
||||
class SCompound:
|
||||
smiles: str
|
||||
uuid: str = field(default=None, compare=False, hash=False)
|
||||
|
||||
@ -53,10 +36,10 @@ class Compound:
|
||||
|
||||
|
||||
@dataclass
|
||||
class Reaction:
|
||||
educts: List[Compound]
|
||||
products: List[Compound]
|
||||
rule_uuid: str = field(default=None, compare=False, hash=False)
|
||||
class SReaction:
|
||||
educts: List[SCompound]
|
||||
products: List[SCompound]
|
||||
rule_uuid: SRule = field(default=None, compare=False, hash=False)
|
||||
reaction_uuid: str = field(default=None, compare=False, hash=False)
|
||||
|
||||
def __hash__(self):
|
||||
@ -68,77 +51,304 @@ class Reaction:
|
||||
return self._hash
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Reaction):
|
||||
if not isinstance(other, SReaction):
|
||||
return NotImplemented
|
||||
return (
|
||||
sorted(self.educts, key=lambda x: x.smiles) == sorted(other.educts, key=lambda x: x.smiles) and
|
||||
sorted(self.products, key=lambda x: x.smiles) == sorted(other.products, key=lambda x: x.smiles)
|
||||
sorted(self.educts, key=lambda x: x.smiles) == sorted(other.educts, key=lambda x: x.smiles) and
|
||||
sorted(self.products, key=lambda x: x.smiles) == sorted(other.products, key=lambda x: x.smiles)
|
||||
)
|
||||
|
||||
|
||||
class Dataset(object):
|
||||
@dataclass
|
||||
class SRule(ABC):
|
||||
|
||||
def __init__(self, headers=List['str'], data=List[List[str|int|float]]):
|
||||
self.headers = headers
|
||||
self.data = data
|
||||
|
||||
|
||||
def features(self):
|
||||
pass
|
||||
|
||||
def labels(self):
|
||||
pass
|
||||
|
||||
def to_json(self):
|
||||
pass
|
||||
|
||||
def to_csv(self):
|
||||
pass
|
||||
|
||||
def to_arff(self):
|
||||
@abstractmethod
|
||||
def apply(self):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass
|
||||
class SSimpleRule:
|
||||
pass
|
||||
|
||||
class DatasetGenerator(object):
|
||||
|
||||
@dataclass
|
||||
class SParallelRule:
|
||||
pass
|
||||
|
||||
|
||||
class Dataset:
|
||||
|
||||
def __init__(self, columns: List[str], num_labels: int, data: List[List[str | int | float]] = None):
|
||||
self.columns: List[str] = columns
|
||||
self.num_labels: int = num_labels
|
||||
|
||||
if data is None:
|
||||
self.data: List[List[str | int | float]] = list()
|
||||
else:
|
||||
self.data = data
|
||||
|
||||
self.num_features: int = len(columns) - self.num_labels
|
||||
self._struct_features: Tuple[int, int] = self._block_indices('feature_')
|
||||
self._triggered: Tuple[int, int] = self._block_indices('trig_')
|
||||
self._observed: Tuple[int, int] = self._block_indices('obs_')
|
||||
|
||||
def _block_indices(self, prefix) -> Tuple[int, int]:
|
||||
indices: List[int] = []
|
||||
for i, feature in enumerate(self.columns):
|
||||
if feature.startswith(prefix):
|
||||
indices.append(i)
|
||||
|
||||
return min(indices), max(indices)
|
||||
|
||||
def structure_id(self):
|
||||
return self.data[0][0]
|
||||
|
||||
def add_row(self, row: List[str | int | float]):
|
||||
if len(self.columns) != len(row):
|
||||
raise ValueError(f"Header and Data are not aligned {len(self.columns)} vs. {len(row)}")
|
||||
self.data.append(row)
|
||||
|
||||
def times_triggered(self, rule_uuid) -> int:
|
||||
idx = self.columns.index(f'trig_{rule_uuid}')
|
||||
|
||||
times_triggered = 0
|
||||
for row in self.data:
|
||||
if row[idx] == 1:
|
||||
times_triggered += 1
|
||||
|
||||
return times_triggered
|
||||
|
||||
def struct_features(self) -> Tuple[int, int]:
|
||||
return self._struct_features
|
||||
|
||||
def triggered(self) -> Tuple[int, int]:
|
||||
return self._triggered
|
||||
|
||||
def observed(self) -> Tuple[int, int]:
|
||||
return self._observed
|
||||
|
||||
def at(self, position: int) -> Dataset:
|
||||
return Dataset(self.columns, self.num_labels, [self.data[position]])
|
||||
|
||||
def limit(self, limit: int) -> Dataset:
|
||||
return Dataset(self.columns, self.num_labels, self.data[:limit])
|
||||
|
||||
def __iter__(self):
|
||||
return (self.at(i) for i, _ in enumerate(self.data))
|
||||
|
||||
|
||||
def classification_dataset(self, structures: List[str | 'CompoundStructure'], applicable_rules: List['Rule']) -> Tuple[Dataset, List[List[PredictionResult]]]:
|
||||
classify_data = []
|
||||
classify_products = []
|
||||
for struct in structures:
|
||||
|
||||
if isinstance(struct, str):
|
||||
struct_id = None
|
||||
struct_smiles = struct
|
||||
else:
|
||||
struct_id = str(struct.uuid)
|
||||
struct_smiles = struct.smiles
|
||||
|
||||
features = FormatConverter.maccs(struct_smiles)
|
||||
|
||||
trig = []
|
||||
prods = []
|
||||
for rule in applicable_rules:
|
||||
products = rule.apply(struct_smiles)
|
||||
|
||||
if len(products):
|
||||
trig.append(1)
|
||||
prods.append(products)
|
||||
else:
|
||||
trig.append(0)
|
||||
prods.append([])
|
||||
|
||||
classify_data.append([struct_id] + features + trig + ([-1] * len(trig)))
|
||||
classify_products.append(prods)
|
||||
|
||||
return Dataset(columns=self.columns, num_labels=self.num_labels, data=classify_data), classify_products
|
||||
|
||||
@staticmethod
|
||||
def generate_dataset(compounds: List[Compound], reactions: List[Reaction], applicable_rules: 'Rule',
|
||||
compounds_to_exclude: Optional[Compound] = None, educts_only: bool = False) -> Dataset:
|
||||
def generate_dataset(reactions: List['Reaction'], applicable_rules: List['Rule'], educts_only: bool = True) -> Dataset:
|
||||
_structures = set()
|
||||
|
||||
rows = []
|
||||
for r in reactions:
|
||||
for e in r.educts.all():
|
||||
_structures.add(e)
|
||||
|
||||
if educts_only:
|
||||
compounds = set()
|
||||
for r in reactions:
|
||||
for e in r.educts:
|
||||
compounds.add(e)
|
||||
compounds = list(compounds)
|
||||
if not educts_only:
|
||||
for e in r.products:
|
||||
_structures.add(e)
|
||||
|
||||
total = len(compounds)
|
||||
for i, c in enumerate(compounds):
|
||||
row = []
|
||||
print(f"{i + 1}/{total} - {c.smiles}")
|
||||
for r in applicable_rules:
|
||||
product_sets = r.rule.apply(c.smiles)
|
||||
compounds = sorted(_structures, key=lambda x: x.url)
|
||||
|
||||
triggered: Dict[str, Set[str]] = defaultdict(set)
|
||||
observed: Set[str] = set()
|
||||
|
||||
# Apply rules on collected compounds and store tps
|
||||
for i, comp in enumerate(compounds):
|
||||
logger.debug(f"{i + 1}/{len(compounds)}...")
|
||||
|
||||
for rule in applicable_rules:
|
||||
product_sets = rule.apply(comp.smiles)
|
||||
|
||||
if len(product_sets) == 0:
|
||||
row.append([])
|
||||
continue
|
||||
|
||||
#triggered.add(f"{r.uuid} + {c.uuid}")
|
||||
reacts = set()
|
||||
for ps in product_sets:
|
||||
products = []
|
||||
for p in ps:
|
||||
products.append(Compound(FormatConverter.standardize(p)))
|
||||
key = f"{rule.uuid} + {comp.uuid}"
|
||||
|
||||
reacts.add(Reaction([c], products, r))
|
||||
row.append(list(reacts))
|
||||
if key in triggered:
|
||||
logger.info(f"{key} already present. Duplicate reaction?")
|
||||
|
||||
rows.append(row)
|
||||
for prod_set in product_sets:
|
||||
for smi in prod_set:
|
||||
|
||||
return rows
|
||||
try:
|
||||
smi = FormatConverter.standardize(smi)
|
||||
except Exception:
|
||||
# :shrug:
|
||||
logger.debug(f'Standardizing SMILES failed for {smi}')
|
||||
pass
|
||||
|
||||
triggered[key].add(smi)
|
||||
|
||||
for i, r in enumerate(reactions):
|
||||
logger.debug(f"{i + 1}/{len(reactions)}...")
|
||||
|
||||
if len(r.educts.all()) != 1:
|
||||
logger.debug(f"Skipping {r.url} as it has {len(r.educts.all())} substrates!")
|
||||
continue
|
||||
|
||||
for comp in r.educts.all():
|
||||
for rule in applicable_rules:
|
||||
key = f"{rule.uuid} + {comp.uuid}"
|
||||
|
||||
if key not in triggered:
|
||||
continue
|
||||
|
||||
# standardize products from reactions for comparison
|
||||
standardized_products = []
|
||||
for cs in r.products.all():
|
||||
smi = cs.smiles
|
||||
|
||||
try:
|
||||
smi = FormatConverter.standardize(smi)
|
||||
except Exception as e:
|
||||
# :shrug:
|
||||
logger.debug(f'Standardizing SMILES failed for {smi}')
|
||||
pass
|
||||
|
||||
standardized_products.append(smi)
|
||||
|
||||
if len(set(standardized_products).difference(triggered[key])) == 0:
|
||||
observed.add(key)
|
||||
else:
|
||||
pass
|
||||
|
||||
ds = None
|
||||
|
||||
for i, comp in enumerate(compounds):
|
||||
# Features
|
||||
feat = FormatConverter.maccs(comp.smiles)
|
||||
trig = []
|
||||
obs = []
|
||||
|
||||
for rule in applicable_rules:
|
||||
key = f"{rule.uuid} + {comp.uuid}"
|
||||
|
||||
# Check triggered
|
||||
if key in triggered:
|
||||
trig.append(1)
|
||||
else:
|
||||
trig.append(0)
|
||||
|
||||
# Check obs
|
||||
if key in observed:
|
||||
obs.append(1)
|
||||
elif key not in triggered:
|
||||
obs.append(None)
|
||||
else:
|
||||
obs.append(0)
|
||||
|
||||
if ds is None:
|
||||
header = ['structure_id'] + \
|
||||
[f'feature_{i}' for i, _ in enumerate(feat)] \
|
||||
+ [f'trig_{r.uuid}' for r in applicable_rules] \
|
||||
+ [f'obs_{r.uuid}' for r in applicable_rules]
|
||||
ds = Dataset(header, len(applicable_rules))
|
||||
|
||||
ds.add_row([str(comp.uuid)] + feat + trig + obs)
|
||||
|
||||
return ds
|
||||
|
||||
|
||||
def X(self, exclude_id_col=True, na_replacement=0):
|
||||
res = self.__getitem__((slice(None), slice(1 if exclude_id_col else 0, len(self.columns) - self.num_labels)))
|
||||
if na_replacement is not None:
|
||||
res = [[x if x is not None else na_replacement for x in row] for row in res]
|
||||
return res
|
||||
|
||||
|
||||
def y(self, na_replacement=0):
|
||||
res = self.__getitem__((slice(None), slice(len(self.columns) - self.num_labels, None)))
|
||||
if na_replacement is not None:
|
||||
res = [[x if x is not None else na_replacement for x in row] for row in res]
|
||||
return res
|
||||
|
||||
|
||||
def __getitem__(self, key):
|
||||
if not isinstance(key, tuple):
|
||||
raise TypeError("Dataset must be indexed with dataset[rows, columns]")
|
||||
|
||||
row_key, col_key = key
|
||||
|
||||
# Normalize rows
|
||||
if isinstance(row_key, int):
|
||||
rows = [self.data[row_key]]
|
||||
else:
|
||||
rows = self.data[row_key]
|
||||
|
||||
# Normalize columns
|
||||
if isinstance(col_key, int):
|
||||
res = [row[col_key] for row in rows]
|
||||
else:
|
||||
res = [[row[i] for i in range(*col_key.indices(len(row)))] if isinstance(col_key, slice)
|
||||
else [row[i] for i in col_key] for row in rows]
|
||||
|
||||
return res
|
||||
|
||||
def save(self, path: 'Path'):
|
||||
import pickle
|
||||
with open(path, "wb") as fh:
|
||||
pickle.dump(self, fh)
|
||||
|
||||
@staticmethod
|
||||
def load(path: 'Path'):
|
||||
import pickle
|
||||
return pickle.load(open(path, "rb"))
|
||||
|
||||
def to_arff(self, path: 'Path'):
|
||||
arff = f"@relation 'enviPy-dataset: -C {self.num_labels}'\n"
|
||||
arff += "\n"
|
||||
for c in self.columns[-self.num_labels:] + self.columns[:self.num_features]:
|
||||
if c == 'structure_id':
|
||||
arff += f"@attribute {c} string\n"
|
||||
else:
|
||||
arff += f"@attribute {c} {{0,1}}\n"
|
||||
|
||||
arff += f"\n@data\n"
|
||||
for d in self.data:
|
||||
ys = ','.join([str(v if v is not None else '?') for v in d[-self.num_labels:]])
|
||||
xs = ','.join([str(v if v is not None else '?') for v in d[:self.num_features]])
|
||||
arff += f'{ys},{xs}\n'
|
||||
|
||||
with open(path, "w") as fh:
|
||||
fh.write(arff)
|
||||
fh.flush()
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Dataset #rows={len(self.data)} #cols={len(self.columns)} #labels={self.num_labels}>"
|
||||
|
||||
|
||||
class SparseLabelECC(BaseEstimator, ClassifierMixin):
|
||||
@ -166,8 +376,7 @@ class SparseLabelECC(BaseEstimator, ClassifierMixin):
|
||||
self.keep_columns_.append(col)
|
||||
|
||||
y_reduced = y[:, self.keep_columns_]
|
||||
self.chains_ = [ClassifierChain(self.base_clf, order='random', random_state=i)
|
||||
for i in range(self.num_chains)]
|
||||
self.chains_ = [ClassifierChain(self.base_clf) for i in range(self.num_chains)]
|
||||
|
||||
for i, chain in enumerate(self.chains_):
|
||||
print(f"{datetime.now()} fitting {i + 1}/{self.num_chains}")
|
||||
@ -208,26 +417,169 @@ class SparseLabelECC(BaseEstimator, ClassifierMixin):
|
||||
return accuracy_score(y_true, y_pred, sample_weight=sample_weight)
|
||||
|
||||
|
||||
class ApplicabilityDomain(PCA):
|
||||
|
||||
def __init__(self, n_components=5):
|
||||
super().__init__(n_components=n_components)
|
||||
import copy
|
||||
|
||||
import numpy as np
|
||||
from sklearn.dummy import DummyClassifier
|
||||
from sklearn.tree import DecisionTreeClassifier
|
||||
|
||||
|
||||
class BinaryRelevance:
|
||||
def __init__(self, baseline_clf):
|
||||
self.clf = baseline_clf
|
||||
self.classifiers = None
|
||||
|
||||
def fit(self, X, Y):
|
||||
if self.classifiers is None:
|
||||
self.classifiers = []
|
||||
|
||||
for l in range(len(Y[0])):
|
||||
X_l = X[~np.isnan(Y[:, l])]
|
||||
Y_l = (Y[~np.isnan(Y[:, l]), l])
|
||||
if len(X_l) == 0: # all labels are nan -> predict 0
|
||||
clf = DummyClassifier(strategy='constant', constant=0)
|
||||
clf.fit([X[0]], [0])
|
||||
self.classifiers.append(clf)
|
||||
continue
|
||||
elif len(np.unique(Y_l)) == 1: # only one class -> predict that class
|
||||
clf = DummyClassifier(strategy='most_frequent')
|
||||
else:
|
||||
clf = copy.deepcopy(self.clf)
|
||||
clf.fit(X_l, Y_l)
|
||||
self.classifiers.append(clf)
|
||||
|
||||
def predict(self, X):
|
||||
labels = []
|
||||
for clf in self.classifiers:
|
||||
labels.append(clf.predict(X))
|
||||
return np.column_stack(labels)
|
||||
|
||||
def predict_proba(self, X):
|
||||
labels = np.empty((len(X), 0))
|
||||
for clf in self.classifiers:
|
||||
pred = clf.predict_proba(X)
|
||||
if pred.shape[1] > 1:
|
||||
pred = pred[:, 1]
|
||||
else:
|
||||
pred = pred * clf.predict([X[0]])[0]
|
||||
labels = np.column_stack((labels, pred))
|
||||
return labels
|
||||
|
||||
|
||||
class MissingValuesClassifierChain:
|
||||
def __init__(self, base_clf):
|
||||
self.base_clf = base_clf
|
||||
self.permutation = None
|
||||
self.classifiers = None
|
||||
|
||||
def fit(self, X, Y):
|
||||
X = np.array(X)
|
||||
Y = np.array(Y)
|
||||
if self.permutation is None:
|
||||
self.permutation = np.random.permutation(len(Y[0]))
|
||||
|
||||
Y = Y[:, self.permutation]
|
||||
|
||||
if self.classifiers is None:
|
||||
self.classifiers = []
|
||||
|
||||
for p in range(len(self.permutation)):
|
||||
X_p = X[~np.isnan(Y[:, p])]
|
||||
Y_p = Y[~np.isnan(Y[:, p]), p]
|
||||
if len(X_p) == 0: # all labels are nan -> predict 0
|
||||
clf = DummyClassifier(strategy='constant', constant=0)
|
||||
self.classifiers.append(clf.fit([X[0]], [0]))
|
||||
elif len(np.unique(Y_p)) == 1: # only one class -> predict that class
|
||||
clf = DummyClassifier(strategy='most_frequent')
|
||||
self.classifiers.append(clf.fit(X_p, Y_p))
|
||||
else:
|
||||
clf = copy.deepcopy(self.base_clf)
|
||||
self.classifiers.append(clf.fit(X_p, Y_p))
|
||||
newcol = Y[:, p]
|
||||
pred = clf.predict(X)
|
||||
newcol[np.isnan(newcol)] = pred[np.isnan(newcol)] # fill in missing values with clf predictions
|
||||
X = np.column_stack((X, newcol))
|
||||
|
||||
def predict(self, X):
|
||||
labels = np.empty((len(X), 0))
|
||||
for clf in self.classifiers:
|
||||
pred = clf.predict(np.column_stack((X, labels)))
|
||||
labels = np.column_stack((labels, pred))
|
||||
return labels[:, np.argsort(self.permutation)]
|
||||
|
||||
def predict_proba(self, X):
|
||||
labels = np.empty((len(X), 0))
|
||||
for clf in self.classifiers:
|
||||
pred = clf.predict_proba(np.column_stack((X, np.round(labels))))
|
||||
if pred.shape[1] > 1:
|
||||
pred = pred[:, 1]
|
||||
else:
|
||||
pred = pred * clf.predict(np.column_stack(([X[0]], np.round([labels[0]]))))[0]
|
||||
labels = np.column_stack((labels, pred))
|
||||
return labels[:, np.argsort(self.permutation)]
|
||||
|
||||
|
||||
class EnsembleClassifierChain:
|
||||
def __init__(self, base_clf, num_chains=10):
|
||||
self.base_clf = base_clf
|
||||
self.num_chains = num_chains
|
||||
self.num_labels = None
|
||||
self.classifiers = None
|
||||
|
||||
def fit(self, X, Y):
|
||||
if self.classifiers is None:
|
||||
self.classifiers = []
|
||||
|
||||
if self.num_labels is None:
|
||||
self.num_labels = len(Y[0])
|
||||
|
||||
for p in range(self.num_chains):
|
||||
print(f"{datetime.now()} fitting {p + 1}/{self.num_chains}")
|
||||
clf = MissingValuesClassifierChain(self.base_clf)
|
||||
clf.fit(X, Y)
|
||||
self.classifiers.append(clf)
|
||||
|
||||
def predict(self, X):
|
||||
labels = np.zeros((len(X), self.num_labels))
|
||||
for clf in self.classifiers:
|
||||
labels += clf.predict(X)
|
||||
return np.round(labels / self.num_chains)
|
||||
|
||||
def predict_proba(self, X):
|
||||
labels = np.zeros((len(X), self.num_labels))
|
||||
for clf in self.classifiers:
|
||||
labels += clf.predict_proba(X)
|
||||
return labels / self.num_chains
|
||||
|
||||
|
||||
|
||||
|
||||
class ApplicabilityDomainPCA(PCA):
|
||||
|
||||
def __init__(self, num_neighbours: int = 5):
|
||||
super().__init__(n_components=num_neighbours)
|
||||
self.scaler = StandardScaler()
|
||||
self.num_neighbours = num_neighbours
|
||||
self.min_vals = None
|
||||
self.max_vals = None
|
||||
|
||||
def build(self, X):
|
||||
def build(self, train_dataset: 'Dataset'):
|
||||
# transform
|
||||
X_scaled = self.scaler.fit_transform(X)
|
||||
X_scaled = self.scaler.fit_transform(train_dataset.X())
|
||||
# fit pca
|
||||
X_pca = self.fit_transform(X_scaled)
|
||||
|
||||
self.max_vals = np.max(X_pca, axis=0)
|
||||
self.min_vals = np.min(X_pca, axis=0)
|
||||
|
||||
def is_applicable(self, instances):
|
||||
def __transform(self, instances):
|
||||
instances_scaled = self.scaler.transform(instances)
|
||||
instances_pca = self.transform(instances_scaled)
|
||||
return instances_pca
|
||||
|
||||
def is_applicable(self, classify_instances: 'Dataset'):
|
||||
instances_pca = self.__transform(classify_instances.X())
|
||||
|
||||
is_applicable = []
|
||||
for i, instance in enumerate(instances_pca):
|
||||
@ -237,3 +589,17 @@ class ApplicabilityDomain(PCA):
|
||||
is_applicable[i] = False
|
||||
|
||||
return is_applicable
|
||||
|
||||
|
||||
def tanimoto_distance(a: List[int], b: List[int]):
|
||||
if len(a) != len(b):
|
||||
raise ValueError(f"Lists must be the same length {len(a)} != {len(b)}")
|
||||
|
||||
sum_a = sum(a)
|
||||
sum_b = sum(b)
|
||||
sum_c = sum(v1 and v2 for v1, v2 in zip(a, b))
|
||||
|
||||
if sum_a + sum_b - sum_c == 0:
|
||||
return 0.0
|
||||
|
||||
return 1 - (sum_c / (sum_a + sum_b - sum_c))
|
||||
|
||||
Reference in New Issue
Block a user