forked from enviPath/enviPy
Compare commits
30 Commits
beta_#1
...
beta_2025-
| Author | SHA1 | Date | |
|---|---|---|---|
| 4158bd36cb | |||
| 4e02910c62 | |||
| 2babe7f7e2 | |||
| 7da3880a9b | |||
| 52931526c1 | |||
| dd7b28046c | |||
| 8592cfae50 | |||
| ec2b941a85 | |||
| 02d84a9b29 | |||
| 00d9188c0c | |||
| 13816ecaf3 | |||
| 6a4c8d96c3 | |||
| 97d0527565 | |||
| b45c99f7d3 | |||
| b95ec98a2f | |||
| c79a1f2040 | |||
| 6e6b394289 | |||
| ec387cc12e | |||
| a7637d046a | |||
| fc8192fb0d | |||
| c3c1d4f5cf | |||
| 3308d47071 | |||
| 1267ca8ace | |||
| ec52b8872d | |||
| 579cd519d0 | |||
| 280ddc7205 | |||
| c9d6d8b024 | |||
| a1aebfa54d | |||
| 79b4b1586c | |||
| aec61151ce |
@ -1,7 +1,14 @@
|
|||||||
from epdb.api import router as epdb_app_router
|
from epdb.api import router as epdb_app_router
|
||||||
|
from epdb.legacy_api import router as epdb_legacy_app_router
|
||||||
from ninja import NinjaAPI
|
from ninja import NinjaAPI
|
||||||
|
|
||||||
api = NinjaAPI()
|
api = NinjaAPI()
|
||||||
|
|
||||||
api.add_router("/", epdb_app_router)
|
from ninja import NinjaAPI
|
||||||
|
|
||||||
|
api_v1 = NinjaAPI(title="API V1 Docs", urls_namespace="api-v1")
|
||||||
|
api_legacy = NinjaAPI(title="Legacy API Docs", urls_namespace="api-legacy")
|
||||||
|
|
||||||
|
# Add routers
|
||||||
|
api_v1.add_router("/", epdb_app_router)
|
||||||
|
api_legacy.add_router("/", epdb_legacy_app_router)
|
||||||
|
|||||||
@ -260,6 +260,8 @@ CELERY_RESULT_BACKEND = 'redis://localhost:6379/1'
|
|||||||
CELERY_ACCEPT_CONTENT = ['json']
|
CELERY_ACCEPT_CONTENT = ['json']
|
||||||
CELERY_TASK_SERIALIZER = '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 = {
|
DEFAULT_RF_MODEL_PARAMS = {
|
||||||
'base_clf': RandomForestClassifier(
|
'base_clf': RandomForestClassifier(
|
||||||
n_estimators=100,
|
n_estimators=100,
|
||||||
@ -273,14 +275,14 @@ DEFAULT_RF_MODEL_PARAMS = {
|
|||||||
'num_chains': 10,
|
'num_chains': 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFAULT_DT_MODEL_PARAMS = {
|
DEFAULT_MODEL_PARAMS = {
|
||||||
'base_clf': DecisionTreeClassifier(
|
'base_clf': DecisionTreeClassifier(
|
||||||
criterion='entropy',
|
criterion='entropy',
|
||||||
max_depth=3,
|
max_depth=3,
|
||||||
min_samples_split=5,
|
min_samples_split=5,
|
||||||
min_samples_leaf=5,
|
# min_samples_leaf=5,
|
||||||
max_features='sqrt',
|
max_features='sqrt',
|
||||||
class_weight='balanced',
|
# class_weight='balanced',
|
||||||
random_state=42
|
random_state=42
|
||||||
),
|
),
|
||||||
'num_chains': 10,
|
'num_chains': 10,
|
||||||
@ -306,9 +308,30 @@ SENTRY_ENABLED = os.environ.get('SENTRY_ENABLED', 'False') == 'True'
|
|||||||
if SENTRY_ENABLED:
|
if SENTRY_ENABLED:
|
||||||
import sentry_sdk
|
import sentry_sdk
|
||||||
|
|
||||||
|
def before_send(event, hint):
|
||||||
|
# Check if was a handled exception by one of our loggers
|
||||||
|
if event.get('logger'):
|
||||||
|
for log_path in LOGGING.get('loggers').keys():
|
||||||
|
if event['logger'].startswith(log_path):
|
||||||
|
return None
|
||||||
|
|
||||||
|
return event
|
||||||
|
|
||||||
sentry_sdk.init(
|
sentry_sdk.init(
|
||||||
dsn=os.environ.get('SENTRY_DSN'),
|
dsn=os.environ.get('SENTRY_DSN'),
|
||||||
# Add data like request headers and IP for users,
|
# Add data like request headers and IP for users,
|
||||||
# see https://docs.sentry.io/platforms/python/data-management/data-collected/ for more info
|
# see https://docs.sentry.io/platforms/python/data-management/data-collected/ for more info
|
||||||
send_default_pii=True,
|
send_default_pii=True,
|
||||||
|
environment=os.environ.get('SENTRY_ENVIRONMENT', 'development'),
|
||||||
|
before_send=before_send,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 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,
|
||||||
|
}
|
||||||
|
|||||||
@ -17,11 +17,12 @@ Including another URLconf
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
|
|
||||||
from .api import api
|
from .api import api_v1, api_legacy
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", include("epdb.urls")),
|
path("", include("epdb.urls")),
|
||||||
path("", include("migration.urls")),
|
path("", include("migration.urls")),
|
||||||
path("admin/", admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
path("api/", api.urls),
|
path("api/v1/", api_v1.urls),
|
||||||
|
path("api/legacy/", api_legacy.urls),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,40 +1,105 @@
|
|||||||
from django.contrib import admin
|
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):
|
class UserAdmin(admin.ModelAdmin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class GroupAdmin(admin.ModelAdmin):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class UserPackagePermissionAdmin(admin.ModelAdmin):
|
class UserPackagePermissionAdmin(admin.ModelAdmin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class GroupAdmin(admin.ModelAdmin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class GroupPackagePermissionAdmin(admin.ModelAdmin):
|
class GroupPackagePermissionAdmin(admin.ModelAdmin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SettingAdmin(admin.ModelAdmin):
|
class EPAdmin(admin.ModelAdmin):
|
||||||
|
search_fields = ['name', 'description']
|
||||||
|
|
||||||
|
|
||||||
|
class PackageAdmin(EPAdmin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class MLRelativeReasoningAdmin(EPAdmin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SimpleAmbitRuleAdmin(admin.ModelAdmin):
|
class CompoundAdmin(EPAdmin):
|
||||||
pass
|
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
|
pass
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(User, UserAdmin)
|
admin.site.register(User, UserAdmin)
|
||||||
admin.site.register(Group, GroupAdmin)
|
|
||||||
admin.site.register(UserPackagePermission, UserPackagePermissionAdmin)
|
admin.site.register(UserPackagePermission, UserPackagePermissionAdmin)
|
||||||
|
admin.site.register(Group, GroupAdmin)
|
||||||
admin.site.register(GroupPackagePermission, GroupPackagePermissionAdmin)
|
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(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)
|
admin.site.register(Scenario, ScenarioAdmin)
|
||||||
|
|||||||
@ -4,3 +4,6 @@ from django.apps import AppConfig
|
|||||||
class EPDBConfig(AppConfig):
|
class EPDBConfig(AppConfig):
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
name = 'epdb'
|
name = 'epdb'
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
import epdb.signals # noqa: F401
|
||||||
|
|||||||
739
epdb/legacy_api.py
Normal file
739
epdb/legacy_api.py
Normal file
@ -0,0 +1,739 @@
|
|||||||
|
from typing import List, Dict, Optional, Any
|
||||||
|
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from ninja import Router, Schema, Field, Form
|
||||||
|
|
||||||
|
from utilities.chem import FormatConverter
|
||||||
|
from .logic import PackageManager
|
||||||
|
from .models import Compound, CompoundStructure, Package, User, UserPackagePermission, Rule, Reaction, Scenario, Pathway
|
||||||
|
|
||||||
|
|
||||||
|
def _anonymous_or_real(request):
|
||||||
|
if request.user.is_authenticated and not request.user.is_anonymous:
|
||||||
|
return request.user
|
||||||
|
return get_user_model().objects.get(username='anonymous')
|
||||||
|
|
||||||
|
|
||||||
|
# router = Router(auth=SessionAuth())
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
|
||||||
|
class Error(Schema):
|
||||||
|
message: str
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleObject(Schema):
|
||||||
|
id: str = Field(None, alias="url")
|
||||||
|
name: str = Field(None, alias="name")
|
||||||
|
reviewStatus: bool = Field(None, alias="package.reviewed")
|
||||||
|
|
||||||
|
|
||||||
|
################
|
||||||
|
# Login/Logout #
|
||||||
|
################
|
||||||
|
class SimpleUser(Schema):
|
||||||
|
id: str = Field(None, alias="url")
|
||||||
|
identifier: str = 'user'
|
||||||
|
name: str = Field(None, alias='username')
|
||||||
|
email: str = Field(None, alias='email')
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/", response={200: SimpleUser, 403: Error})
|
||||||
|
def login(request, loginusername: Form[str], loginpassword: Form[str], hiddenMethod: Form[str]):
|
||||||
|
from django.contrib.auth import authenticate
|
||||||
|
from django.contrib.auth import login
|
||||||
|
email = User.objects.get(username=loginusername).email
|
||||||
|
user = authenticate(username=email, password=loginpassword)
|
||||||
|
if user:
|
||||||
|
login(request, user)
|
||||||
|
return user
|
||||||
|
else:
|
||||||
|
return 403, {'message': 'Invalid username or password'}
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleGroup(Schema):
|
||||||
|
id: str
|
||||||
|
identifier: str = 'group'
|
||||||
|
name: str
|
||||||
|
|
||||||
|
|
||||||
|
###########
|
||||||
|
# Package #
|
||||||
|
###########
|
||||||
|
class SimplePackage(SimpleObject):
|
||||||
|
identifier: str = 'package'
|
||||||
|
reviewStatus: bool = Field(None, alias="reviewed")
|
||||||
|
|
||||||
|
|
||||||
|
class PackageSchema(Schema):
|
||||||
|
description: str = Field(None, alias="description")
|
||||||
|
id: str = Field(None, alias="url")
|
||||||
|
links: List[Dict[str, List[str | int]]] = Field([], alias="links")
|
||||||
|
name: str = Field(None, alias="name")
|
||||||
|
primaryGroup: Optional[SimpleGroup] = None
|
||||||
|
readers: List[Dict[str, str]] = Field([], alias="readers")
|
||||||
|
reviewComment: str = Field(None, alias="review_comment")
|
||||||
|
reviewStatus: str = Field(None, alias="review_status")
|
||||||
|
writers: List[Dict[str, str]] = Field([], alias="writers")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_links(obj: Package):
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'Pathways': [
|
||||||
|
f'{obj.url}/pathway', obj.pathways.count()
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
'Rules': [
|
||||||
|
f'{obj.url}/rule', obj.rules.count()
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
'Compounds': [
|
||||||
|
f'{obj.url}/compound', obj.compounds.count()
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
'Reactions': [
|
||||||
|
f'{obj.url}/reaction', obj.reactions.count()
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
'Relative Reasoning': [
|
||||||
|
f'{obj.url}/relative-reasoning', obj.models.count()
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
'Scenarios': [
|
||||||
|
f'{obj.url}/scenario', obj.scenarios.count()
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_readers(obj: Package):
|
||||||
|
users = User.objects.filter(
|
||||||
|
id__in=UserPackagePermission.objects.filter(
|
||||||
|
package=obj,
|
||||||
|
permission=UserPackagePermission.READ[0]
|
||||||
|
).values_list('user', flat=True)
|
||||||
|
).distinct()
|
||||||
|
|
||||||
|
return [{u.id: u.name} for u in users]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_writers(obj: Package):
|
||||||
|
users = User.objects.filter(
|
||||||
|
id__in=UserPackagePermission.objects.filter(
|
||||||
|
package=obj,
|
||||||
|
permission=UserPackagePermission.WRITE[0]
|
||||||
|
).values_list('user', flat=True)
|
||||||
|
).distinct()
|
||||||
|
|
||||||
|
return [{u.id: u.name} for u in users]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_review_comment(obj):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_review_status(obj):
|
||||||
|
return 'reviewed' if obj.reviewed else 'unreviewed'
|
||||||
|
|
||||||
|
|
||||||
|
class PackageWrapper(Schema):
|
||||||
|
package: List['PackageSchema']
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/package", response={200: PackageWrapper, 403: Error})
|
||||||
|
def get_packages(request):
|
||||||
|
return {'package': PackageManager.get_all_readable_packages(request.user, include_reviewed=True)}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/package/{uuid:package_uuid}", response={200: PackageSchema, 403: Error})
|
||||||
|
def get_package(request, package_uuid):
|
||||||
|
try:
|
||||||
|
return PackageManager.get_package_by_id(request.user, package_uuid)
|
||||||
|
except ValueError:
|
||||||
|
return 403, {
|
||||||
|
'message': f'Getting Package with id {package_uuid} failed due to insufficient rights!'}
|
||||||
|
|
||||||
|
|
||||||
|
################################
|
||||||
|
# Compound / CompoundStructure #
|
||||||
|
################################
|
||||||
|
class SimpleCompound(SimpleObject):
|
||||||
|
identifier: str = 'compound'
|
||||||
|
|
||||||
|
|
||||||
|
class CompoundPathwayScenario(Schema):
|
||||||
|
scenarioId: str
|
||||||
|
scenarioName: str
|
||||||
|
scenarioType: str
|
||||||
|
|
||||||
|
|
||||||
|
class CompoundSchema(Schema):
|
||||||
|
aliases: List[str] = Field([], alias="aliases")
|
||||||
|
description: str = Field(None, alias="description")
|
||||||
|
externalReferences: Dict[str, List[str]] = Field(None, alias="external_references")
|
||||||
|
id: str = Field(None, alias="url")
|
||||||
|
halflifes: List[Dict[str, str]] = Field([], alias="halflifes")
|
||||||
|
identifier: str = 'compound'
|
||||||
|
imageSize: int = 600
|
||||||
|
name: str = Field(None, alias="name")
|
||||||
|
pathwayScenarios: List[CompoundPathwayScenario] = Field([], alias="pathway_scenarios")
|
||||||
|
pathways: List['SimplePathway'] = Field([], alias="related_pathways")
|
||||||
|
pubchemCompoundReferences: List[str] = Field([], alias="pubchem_compound_references")
|
||||||
|
reactions: List['SimpleReaction'] = Field([], alias="related_reactions")
|
||||||
|
reviewStatus: str = Field(False, alias="review_status")
|
||||||
|
scenarios: List['SimpleScenario'] = Field([], alias="scenarios")
|
||||||
|
structures: List['CompoundStructureSchema'] = []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_review_status(obj: CompoundStructure):
|
||||||
|
return 'reviewed' if obj.package.reviewed else 'unreviewed'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_external_references(obj: Compound):
|
||||||
|
# TODO
|
||||||
|
return {}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_structures(obj: Compound):
|
||||||
|
return CompoundStructure.objects.filter(compound=obj)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_halflifes(obj: Compound):
|
||||||
|
return []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_pubchem_compound_references(obj: Compound):
|
||||||
|
return []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_pathway_scenarios(obj: Compound):
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'scenarioId': 'https://envipath.org/package/5882df9c-dae1-4d80-a40e-db4724271456/scenario/cd8350cd-4249-4111-ba9f-4e2209338501',
|
||||||
|
'scenarioName': 'Fritz, R. & Brauner, A. (1989) - (00004)',
|
||||||
|
'scenarioType': 'Soil'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class CompoundWrapper(Schema):
|
||||||
|
compound: List['SimpleCompound']
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleCompoundStructure(SimpleObject):
|
||||||
|
identifier: str = 'structure'
|
||||||
|
reviewStatus: bool = Field(None, alias="compound.package.reviewed")
|
||||||
|
|
||||||
|
|
||||||
|
class CompoundStructureSchema(Schema):
|
||||||
|
InChI: str = Field(None, alias="inchi")
|
||||||
|
aliases: List[str] = Field([], alias="aliases")
|
||||||
|
canonicalSmiles: str = Field(None, alias="canonical_smiles")
|
||||||
|
charge: int = Field(None, alias="charge")
|
||||||
|
description: str = Field(None, alias="description")
|
||||||
|
externalReferences: Dict[str, List[str]] = Field(None, alias="external_references")
|
||||||
|
formula: str = Field(None, alias="formula")
|
||||||
|
halflifes: List[Dict[str, str]] = Field([], alias="halflifes")
|
||||||
|
id: str = Field(None, alias="url")
|
||||||
|
identifier: str = 'structure'
|
||||||
|
imageSize: int = 600
|
||||||
|
inchikey: str = Field(None, alias="inchikey")
|
||||||
|
isDefaultStructure: bool = Field(None, alias="is_default_structure")
|
||||||
|
mass: float = Field(None, alias="mass")
|
||||||
|
name: str = Field(None, alias="name")
|
||||||
|
pathways: List['SimplePathway'] = Field([], alias="related_pathways")
|
||||||
|
pubchemCompoundReferences: List[str] = Field([], alias="pubchem_compound_references")
|
||||||
|
reactions: List['SimpleReaction'] = Field([], alias="related_reactions")
|
||||||
|
reviewStatus: str = Field(None, alias="review_status")
|
||||||
|
scenarios: List['SimpleScenario'] = Field([], alias="scenarios")
|
||||||
|
smiles: str = Field(None, alias="smiles")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_review_status(obj: CompoundStructure):
|
||||||
|
return 'reviewed' if obj.compound.package.reviewed else 'unreviewed'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_inchi(obj: CompoundStructure):
|
||||||
|
return FormatConverter.InChI(obj.smiles)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_charge(obj: CompoundStructure):
|
||||||
|
print(obj.smiles)
|
||||||
|
print(FormatConverter.charge(obj.smiles))
|
||||||
|
return FormatConverter.charge(obj.smiles)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_formula(obj: CompoundStructure):
|
||||||
|
return FormatConverter.formula(obj.smiles)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_mass(obj: CompoundStructure):
|
||||||
|
return FormatConverter.mass(obj.smiles)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_external_references(obj: CompoundStructure):
|
||||||
|
# TODO
|
||||||
|
return {}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_halflifes(obj: CompoundStructure):
|
||||||
|
return []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_pubchem_compound_references(obj: CompoundStructure):
|
||||||
|
return []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_pathway_scenarios(obj: CompoundStructure):
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'scenarioId': 'https://envipath.org/package/5882df9c-dae1-4d80-a40e-db4724271456/scenario/cd8350cd-4249-4111-ba9f-4e2209338501',
|
||||||
|
'scenarioName': 'Fritz, R. & Brauner, A. (1989) - (00004)',
|
||||||
|
'scenarioType': 'Soil'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class CompoundStructureWrapper(Schema):
|
||||||
|
structure: List['SimpleCompoundStructure']
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/compound", response={200: CompoundWrapper, 403: Error})
|
||||||
|
def get_compounds(request):
|
||||||
|
qs = Compound.objects.none()
|
||||||
|
for p in PackageManager.get_reviewed_packages():
|
||||||
|
qs |= Compound.objects.filter(package=p)
|
||||||
|
return {'compound': qs}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/package/{uuid:package_uuid}/compound", response={200: CompoundWrapper, 403: Error})
|
||||||
|
def get_package_compounds(request, package_uuid):
|
||||||
|
try:
|
||||||
|
p = PackageManager.get_package_by_id(request.user, package_uuid)
|
||||||
|
return {'compound': Compound.objects.filter(package=p).prefetch_related('package')}
|
||||||
|
except ValueError:
|
||||||
|
return 403, {
|
||||||
|
'message': f'Getting Compounds for Package with id {package_uuid} failed due to insufficient rights!'}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/package/{uuid:package_uuid}/compound/{uuid:compound_uuid}", response={200: CompoundSchema, 403: Error})
|
||||||
|
def get_package_compound(request, package_uuid, compound_uuid):
|
||||||
|
try:
|
||||||
|
p = PackageManager.get_package_by_id(request.user, package_uuid)
|
||||||
|
return Compound.objects.get(package=p, uuid=compound_uuid)
|
||||||
|
except ValueError:
|
||||||
|
return 403, {
|
||||||
|
'message': f'Getting Compound with id {compound_uuid} failed due to insufficient rights!'}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/package/{uuid:package_uuid}/compound/{uuid:compound_uuid}/structure",
|
||||||
|
response={200: CompoundStructureWrapper, 403: Error})
|
||||||
|
def get_package_compound_structures(request, package_uuid, compound_uuid):
|
||||||
|
try:
|
||||||
|
p = PackageManager.get_package_by_id(request.user, package_uuid)
|
||||||
|
return {'structure': Compound.objects.get(package=p, uuid=compound_uuid).structures.all()}
|
||||||
|
except ValueError:
|
||||||
|
return 403, {
|
||||||
|
'message': f'Getting CompoundStructures for Compound with id {compound_uuid} failed due to insufficient rights!'}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/package/{uuid:package_uuid}/compound/{uuid:compound_uuid}/structure/{uuid:structure_uuid}",
|
||||||
|
response={200: CompoundStructureSchema, 403: Error})
|
||||||
|
def get_package_compound_structure(request, package_uuid, compound_uuid, structure_uuid):
|
||||||
|
try:
|
||||||
|
p = PackageManager.get_package_by_id(request.user, package_uuid)
|
||||||
|
return CompoundStructure.objects.get(uuid=structure_uuid,
|
||||||
|
compound=Compound.objects.get(package=p, uuid=compound_uuid))
|
||||||
|
except ValueError:
|
||||||
|
return 403, {
|
||||||
|
'message': f'Getting CompoundStructure with id {structure_uuid} failed due to insufficient rights!'}
|
||||||
|
|
||||||
|
|
||||||
|
#########
|
||||||
|
# Rules #
|
||||||
|
#########
|
||||||
|
class SimpleRule(SimpleObject):
|
||||||
|
identifier: str = 'rule'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_url(obj: Rule):
|
||||||
|
return obj.url.replace('-ambit-', '-').replace('-rdkit-', '-')
|
||||||
|
|
||||||
|
|
||||||
|
class RuleWrapper(Schema):
|
||||||
|
rule: List['SimpleRule']
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleRuleSchema(Schema):
|
||||||
|
aliases: List[str] = Field([], alias="aliases")
|
||||||
|
description: str = Field(None, alias="description")
|
||||||
|
ecNumbers: List[Dict[str, str]] = Field([], alias="ec_numbers")
|
||||||
|
engine: str = 'ambit'
|
||||||
|
id: str = Field(None, alias="url")
|
||||||
|
identifier: str = Field(None, alias="identifier")
|
||||||
|
isCompositeRule: bool = False
|
||||||
|
name: str = Field(None, alias="name")
|
||||||
|
pathways: List['SimplePathway'] = Field([], alias="related_pathways")
|
||||||
|
productFilterSmarts: str = Field("", alias="product_filter_smarts")
|
||||||
|
productSmarts: str = Field(None, alias="products_smarts")
|
||||||
|
reactantFilterSmarts: str = Field("", alias="reactant_filter_smarts")
|
||||||
|
reactantSmarts: str = Field(None, alias="reactants_smarts")
|
||||||
|
reactions: List['SimpleReaction'] = Field([], alias="related_reactions")
|
||||||
|
reviewStatus: str = Field(None, alias="review_status")
|
||||||
|
scenarios: List['SimpleScenario'] = Field([], alias="scenarios")
|
||||||
|
smirks: str = Field("", alias="smirks")
|
||||||
|
# TODO
|
||||||
|
transformations: str = Field("", alias="transformations")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_url(obj: Rule):
|
||||||
|
return obj.url.replace('-ambit-', '-').replace('-rdkit-', '-')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_identifier(obj: Rule):
|
||||||
|
if 'simple-rule' in obj.url:
|
||||||
|
return 'simple-rule'
|
||||||
|
if 'simple-ambit-rule' in obj.url:
|
||||||
|
return 'simple-rule'
|
||||||
|
elif 'parallel-rule' in obj.url:
|
||||||
|
return 'parallel-rule'
|
||||||
|
elif 'sequential-rule' in obj.url:
|
||||||
|
return 'sequential-rule'
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_review_status(obj: Rule):
|
||||||
|
return 'reviewed' if obj.package.reviewed else 'unreviewed'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_product_filter_smarts(obj: Rule):
|
||||||
|
return obj.product_filter_smarts if obj.product_filter_smarts else ''
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_reactant_filter_smarts(obj: Rule):
|
||||||
|
return obj.reactant_filter_smarts if obj.reactant_filter_smarts else ''
|
||||||
|
|
||||||
|
|
||||||
|
class CompositeRuleSchema(Schema):
|
||||||
|
aliases: List[str] = Field([], alias="aliases")
|
||||||
|
description: str = Field(None, alias="description")
|
||||||
|
ecNumbers: List[Dict[str, str]] = Field([], alias="ec_numbers")
|
||||||
|
id: str = Field(None, alias="url")
|
||||||
|
identifier: str = Field(None, alias="identifier")
|
||||||
|
isCompositeRule: bool = True
|
||||||
|
name: str = Field(None, alias="name")
|
||||||
|
pathways: List['SimplePathway'] = Field([], alias="related_pathways")
|
||||||
|
productFilterSmarts: str = Field("", alias="product_filter_smarts")
|
||||||
|
reactantFilterSmarts: str = Field("", alias="reactant_filter_smarts")
|
||||||
|
reactions: List['SimpleReaction'] = Field([], alias="related_reactions")
|
||||||
|
reviewStatus: str = Field(None, alias="review_status")
|
||||||
|
scenarios: List['SimpleScenario'] = Field([], alias="scenarios")
|
||||||
|
simpleRules: List['SimpleRule'] = Field([], alias="simple_rules")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_ec_numbers(obj: Rule):
|
||||||
|
return []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_url(obj: Rule):
|
||||||
|
return obj.url.replace('-ambit-', '-').replace('-rdkit-', '-')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_identifier(obj: Rule):
|
||||||
|
if 'simple-rule' in obj.url:
|
||||||
|
return 'simple-rule'
|
||||||
|
if 'simple-ambit-rule' in obj.url:
|
||||||
|
return 'simple-rule'
|
||||||
|
elif 'parallel-rule' in obj.url:
|
||||||
|
return 'parallel-rule'
|
||||||
|
elif 'sequential-rule' in obj.url:
|
||||||
|
return 'sequential-rule'
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_review_status(obj: Rule):
|
||||||
|
return 'reviewed' if obj.package.reviewed else 'unreviewed'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_product_filter_smarts(obj: Rule):
|
||||||
|
return obj.product_filter_smarts if obj.product_filter_smarts else ''
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_reactant_filter_smarts(obj: Rule):
|
||||||
|
return obj.reactant_filter_smarts if obj.reactant_filter_smarts else ''
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/rule", response={200: RuleWrapper, 403: Error})
|
||||||
|
def get_rules(request):
|
||||||
|
qs = Rule.objects.none()
|
||||||
|
for p in PackageManager.get_reviewed_packages():
|
||||||
|
qs |= Rule.objects.filter(package=p)
|
||||||
|
return {'rule': qs}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/package/{uuid:package_uuid}/rule", response={200: RuleWrapper, 403: Error})
|
||||||
|
def get_package_rules(request, package_uuid):
|
||||||
|
try:
|
||||||
|
p = PackageManager.get_package_by_id(request.user, package_uuid)
|
||||||
|
return {'rule': Rule.objects.filter(package=p).prefetch_related('package')}
|
||||||
|
except ValueError:
|
||||||
|
return 403, {
|
||||||
|
'message': f'Getting Rules for Package with id {package_uuid} failed due to insufficient rights!'}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/package/{uuid:package_uuid}/rule/{uuid:rule_uuid}",
|
||||||
|
response={200: SimpleRuleSchema | CompositeRuleSchema, 403: Error})
|
||||||
|
def get_package_rule(request, package_uuid, rule_uuid):
|
||||||
|
return _get_package_rule(request, package_uuid, rule_uuid)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/package/{uuid:package_uuid}/simple-rule/{uuid:rule_uuid}",
|
||||||
|
response={200: SimpleRuleSchema | CompositeRuleSchema, 403: Error})
|
||||||
|
def get_package_simple_rule(request, package_uuid, rule_uuid):
|
||||||
|
return _get_package_rule(request, package_uuid, rule_uuid)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/package/{uuid:package_uuid}/parallel-rule/{uuid:rule_uuid}",
|
||||||
|
response={200: SimpleRuleSchema | CompositeRuleSchema, 403: Error})
|
||||||
|
def get_package_parallel_rule(request, package_uuid, rule_uuid):
|
||||||
|
return _get_package_rule(request, package_uuid, rule_uuid)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_package_rule(request, package_uuid, rule_uuid):
|
||||||
|
try:
|
||||||
|
p = PackageManager.get_package_by_id(request.user, package_uuid)
|
||||||
|
return Rule.objects.get(package=p, uuid=rule_uuid)
|
||||||
|
except ValueError:
|
||||||
|
return 403, {
|
||||||
|
'message': f'Getting Rule with id {rule_uuid} failed due to insufficient rights!'}
|
||||||
|
|
||||||
|
|
||||||
|
# POST
|
||||||
|
@router.post("/package/{uuid:package_uuid}/rule/{uuid:rule_uuid}", response={200: str | Any, 403: Error})
|
||||||
|
def post_package_rule(request, package_uuid, rule_uuid, compound: Form[str] = None):
|
||||||
|
return _post_package_rule(request, package_uuid, rule_uuid, compound=compound)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/package/{uuid:package_uuid}/simple-rule/{uuid:rule_uuid}", response={200: str | Any, 403: Error})
|
||||||
|
def post_package_simple_rule(request, package_uuid, rule_uuid, compound: Form[str] = None):
|
||||||
|
return _post_package_rule(request, package_uuid, rule_uuid, compound=compound)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/package/{uuid:package_uuid}/parallel-rule/{uuid:rule_uuid}", response={200: str | Any, 403: Error})
|
||||||
|
def post_package_parallel_rule(request, package_uuid, rule_uuid, compound: Form[str] = None):
|
||||||
|
return _post_package_rule(request, package_uuid, rule_uuid, compound=compound)
|
||||||
|
|
||||||
|
|
||||||
|
def _post_package_rule(request, package_uuid, rule_uuid, compound: Form[str]):
|
||||||
|
try:
|
||||||
|
p = PackageManager.get_package_by_id(request.user, package_uuid)
|
||||||
|
r = Rule.objects.get(package=p, uuid=rule_uuid)
|
||||||
|
|
||||||
|
if compound is not None:
|
||||||
|
if not compound.split():
|
||||||
|
return 400, {'message': 'Compound is empty'}
|
||||||
|
|
||||||
|
product_sets = r.apply(compound)
|
||||||
|
|
||||||
|
res = []
|
||||||
|
for p_set in product_sets:
|
||||||
|
for product in p_set:
|
||||||
|
res.append(product)
|
||||||
|
|
||||||
|
return HttpResponse('\n'.join(res), content_type="text/plain")
|
||||||
|
|
||||||
|
return r
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
return 403, {
|
||||||
|
'message': f'Getting Rule with id {rule_uuid} failed due to insufficient rights!'}
|
||||||
|
|
||||||
|
|
||||||
|
############
|
||||||
|
# Reaction #
|
||||||
|
############
|
||||||
|
class SimpleReaction(SimpleObject):
|
||||||
|
identifier: str = 'reaction'
|
||||||
|
|
||||||
|
|
||||||
|
class ReactionWrapper(Schema):
|
||||||
|
reaction: List['SimpleReaction']
|
||||||
|
|
||||||
|
|
||||||
|
class ReactionCompoundStructure(Schema):
|
||||||
|
compoundName: str = Field(None, alias="name")
|
||||||
|
id: str = Field(None, alias="url")
|
||||||
|
smiles: str = Field(None, alias="smiles")
|
||||||
|
|
||||||
|
|
||||||
|
class ReactionSchema(Schema):
|
||||||
|
aliases: List[str] = Field([], alias="aliases")
|
||||||
|
description: str = Field(None, alias="description")
|
||||||
|
ecNumbers: List[Dict[str, str]] = Field([], alias="ec_numbers")
|
||||||
|
educts: List['ReactionCompoundStructure'] = Field([], alias="educts")
|
||||||
|
id: str = Field(None, alias="url")
|
||||||
|
identifier: str = 'reaction'
|
||||||
|
medlineRefs: List[str] = Field([], alias="medline_references")
|
||||||
|
multistep: bool = Field(None, alias="multi_step")
|
||||||
|
name: str = Field(None, alias="name")
|
||||||
|
pathways: List['SimplePathway'] = Field([], alias="related_pathways")
|
||||||
|
products: List['ReactionCompoundStructure'] = Field([], alias="products")
|
||||||
|
references: List[Dict[str, List[str]]] = Field([], alias="references")
|
||||||
|
reviewStatus: str = Field(None, alias="review_status")
|
||||||
|
scenarios: List['SimpleScenario'] = Field([], alias="scenarios")
|
||||||
|
smirks: str = Field("", alias="smirks")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_smirks(obj: Reaction):
|
||||||
|
return obj.smirks()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_ec_numbers(obj: Reaction):
|
||||||
|
# TODO fetch via scenario EnzymeAI
|
||||||
|
return []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_references(obj: Reaction):
|
||||||
|
# TODO
|
||||||
|
return []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_medline_references(obj: Reaction):
|
||||||
|
# TODO
|
||||||
|
return []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_review_status(obj: Rule):
|
||||||
|
return 'reviewed' if obj.package.reviewed else 'unreviewed'
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/reaction", response={200: ReactionWrapper, 403: Error})
|
||||||
|
def get_reactions(request):
|
||||||
|
qs = Reaction.objects.none()
|
||||||
|
for p in PackageManager.get_reviewed_packages():
|
||||||
|
qs |= Reaction.objects.filter(package=p)
|
||||||
|
return {'reaction': qs}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/package/{uuid:package_uuid}/reaction", response={200: ReactionWrapper, 403: Error})
|
||||||
|
def get_package_reactions(request, package_uuid):
|
||||||
|
try:
|
||||||
|
p = PackageManager.get_package_by_id(request.user, package_uuid)
|
||||||
|
return {'reaction': Reaction.objects.filter(package=p).prefetch_related('package')}
|
||||||
|
except ValueError:
|
||||||
|
return 403, {
|
||||||
|
'message': f'Getting Reactions for Package with id {package_uuid} failed due to insufficient rights!'}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/package/{uuid:package_uuid}/reaction/{uuid:reaction_uuid}", response={200: ReactionSchema, 403: Error})
|
||||||
|
def get_package_reaction(request, package_uuid, reaction_uuid):
|
||||||
|
try:
|
||||||
|
p = PackageManager.get_package_by_id(request.user, package_uuid)
|
||||||
|
return Reaction.objects.get(package=p, uuid=reaction_uuid)
|
||||||
|
except ValueError:
|
||||||
|
return 403, {
|
||||||
|
'message': f'Getting Reaction with id {reaction_uuid} failed due to insufficient rights!'}
|
||||||
|
|
||||||
|
|
||||||
|
############
|
||||||
|
# Scenario #
|
||||||
|
############
|
||||||
|
class SimpleScenario(SimpleObject):
|
||||||
|
identifier: str = 'scenario'
|
||||||
|
|
||||||
|
|
||||||
|
class ScenarioWrapper(Schema):
|
||||||
|
scenario: List['SimpleScenario']
|
||||||
|
|
||||||
|
|
||||||
|
class ScenarioSchema(Schema):
|
||||||
|
aliases: List[str] = Field([], alias="aliases")
|
||||||
|
collection: Dict['str', List[Dict[str, Any]]] = Field([], alias="collection")
|
||||||
|
collectionID: Optional[str] = None
|
||||||
|
description: str = Field(None, alias="description")
|
||||||
|
id: str = Field(None, alias="url")
|
||||||
|
identifier: str = 'scenario'
|
||||||
|
linkedTo: List[Dict[str, str]] = Field({}, alias="linked_to")
|
||||||
|
name: str = Field(None, alias="name")
|
||||||
|
pathways: List['SimplePathway'] = Field([], alias="related_pathways")
|
||||||
|
relatedScenarios: List[Dict[str, str]] = Field([], alias="related_scenarios")
|
||||||
|
reviewStatus: str = Field(None, alias="review_status")
|
||||||
|
scenarios: List['SimpleScenario'] = Field([], alias="scenarios")
|
||||||
|
type: str = Field(None, alias="scenario_type")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_collection(obj: Scenario):
|
||||||
|
return obj.additional_information
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_review_status(obj: Rule):
|
||||||
|
return 'reviewed' if obj.package.reviewed else 'unreviewed'
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/scenario", response={200: ScenarioWrapper, 403: Error})
|
||||||
|
def get_scenarios(request):
|
||||||
|
qs = Scenario.objects.none()
|
||||||
|
for p in PackageManager.get_reviewed_packages():
|
||||||
|
qs |= Scenario.objects.filter(package=p)
|
||||||
|
return {'scenario': qs}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/package/{uuid:package_uuid}/scenario", response={200: ScenarioWrapper, 403: Error})
|
||||||
|
def get_package_scenarios(request, package_uuid):
|
||||||
|
try:
|
||||||
|
p = PackageManager.get_package_by_id(request.user, package_uuid)
|
||||||
|
return {'scenario': Scenario.objects.filter(package=p).prefetch_related('package')}
|
||||||
|
except ValueError:
|
||||||
|
return 403, {
|
||||||
|
'message': f'Getting Scenarios for Package with id {package_uuid} failed due to insufficient rights!'}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/package/{uuid:package_uuid}/scenario/{uuid:scenario_uuid}", response={200: ScenarioSchema, 403: Error})
|
||||||
|
def get_package_scenario(request, package_uuid, scenario_uuid):
|
||||||
|
try:
|
||||||
|
p = PackageManager.get_package_by_id(request.user, package_uuid)
|
||||||
|
return Scenario.objects.get(package=p, uuid=scenario_uuid)
|
||||||
|
except ValueError:
|
||||||
|
return 403, {
|
||||||
|
'message': f'Getting Scenario with id {scenario_uuid} failed due to insufficient rights!'}
|
||||||
|
|
||||||
|
|
||||||
|
###########
|
||||||
|
# Pathway #
|
||||||
|
###########
|
||||||
|
class SimplePathway(SimpleObject):
|
||||||
|
identifier: str = 'pathway'
|
||||||
|
|
||||||
|
class PathwayWrapper(Schema):
|
||||||
|
pathway: List['SimplePathway']
|
||||||
|
|
||||||
|
@router.get("/pathway", response={200: PathwayWrapper, 403: Error})
|
||||||
|
def get_pathways(request):
|
||||||
|
qs = Pathway.objects.none()
|
||||||
|
for p in PackageManager.get_reviewed_packages():
|
||||||
|
qs |= Pathway.objects.filter(package=p)
|
||||||
|
return {'pathway': qs}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/package/{uuid:package_uuid}/pathway", response={200: PathwayWrapper, 403: Error})
|
||||||
|
def get_package_pathways(request, package_uuid):
|
||||||
|
try:
|
||||||
|
p = PackageManager.get_package_by_id(request.user, package_uuid)
|
||||||
|
return {'pathway': Pathway.objects.filter(package=p).prefetch_related('package')}
|
||||||
|
except ValueError:
|
||||||
|
return 403, {
|
||||||
|
'message': f'Getting Pathways for Package with id {package_uuid} failed due to insufficient rights!'}
|
||||||
|
|
||||||
|
|
||||||
|
# @router.get("/package/{uuid:package_uuid}/pathway/{uuid:pathway_uuid}", response={200: Pathway, 403: Error})
|
||||||
|
# def get_package_pathway(request, package_uuid, pathway_uuid):
|
||||||
|
# try:
|
||||||
|
# p = PackageManager.get_package_by_id(request.user, package_uuid)
|
||||||
|
# return Pathway.objects.get(package=p, uuid=pathway_uuid)
|
||||||
|
# except ValueError:
|
||||||
|
# return 403, {
|
||||||
|
# 'message': f'Getting Pathway with id {pathway_uuid} failed due to insufficient rights!'}
|
||||||
224
epdb/logic.py
224
epdb/logic.py
@ -1,6 +1,7 @@
|
|||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
from typing import Union, List, Optional, Set, Dict
|
import json
|
||||||
|
from typing import Union, List, Optional, Set, Dict, Any
|
||||||
|
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
@ -13,6 +14,132 @@ from utilities.chem import FormatConverter
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class EPDBURLParser:
|
||||||
|
|
||||||
|
UUID_PATTERN = r'[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}'
|
||||||
|
|
||||||
|
MODEL_PATTERNS = {
|
||||||
|
'epdb.User': re.compile(rf'^.*/user/{UUID_PATTERN}'),
|
||||||
|
'epdb.Group': re.compile(rf'^.*/group/{UUID_PATTERN}'),
|
||||||
|
'epdb.Package': re.compile(rf'^.*/package/{UUID_PATTERN}'),
|
||||||
|
'epdb.Compound': re.compile(rf'^.*/package/{UUID_PATTERN}/compound/{UUID_PATTERN}'),
|
||||||
|
'epdb.CompoundStructure': re.compile(rf'^.*/package/{UUID_PATTERN}/compound/{UUID_PATTERN}/structure/{UUID_PATTERN}'),
|
||||||
|
'epdb.Rule': re.compile(rf'^.*/package/{UUID_PATTERN}/(?:simple-ambit-rule|simple-rdkit-rule|parallel-rule|sequential-rule|rule)/{UUID_PATTERN}'),
|
||||||
|
'epdb.Reaction': re.compile(rf'^.*/package/{UUID_PATTERN}/reaction/{UUID_PATTERN}$'),
|
||||||
|
'epdb.Pathway': re.compile(rf'^.*/package/{UUID_PATTERN}/pathway/{UUID_PATTERN}'),
|
||||||
|
'epdb.Node': re.compile(rf'^.*/package/{UUID_PATTERN}/pathway/{UUID_PATTERN}/node/{UUID_PATTERN}'),
|
||||||
|
'epdb.Edge': re.compile(rf'^.*/package/{UUID_PATTERN}/pathway/{UUID_PATTERN}/edge/{UUID_PATTERN}'),
|
||||||
|
'epdb.Scenario': re.compile(rf'^.*/package/{UUID_PATTERN}/scenario/{UUID_PATTERN}'),
|
||||||
|
'epdb.EPModel': re.compile(rf'^.*/package/{UUID_PATTERN}/model/{UUID_PATTERN}'),
|
||||||
|
'epdb.Setting': re.compile(rf'^.*/setting/{UUID_PATTERN}'),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, url: str):
|
||||||
|
self.url = url
|
||||||
|
self._matches = {}
|
||||||
|
self._analyze_url()
|
||||||
|
|
||||||
|
def _analyze_url(self):
|
||||||
|
for model_path, pattern in self.MODEL_PATTERNS.items():
|
||||||
|
match = pattern.findall(self.url)
|
||||||
|
if match:
|
||||||
|
self._matches[model_path] = match[0]
|
||||||
|
|
||||||
|
def _get_model_class(self, model_path: str):
|
||||||
|
try:
|
||||||
|
from django.apps import apps
|
||||||
|
app_label, model_name = model_path.split('.')[-2:]
|
||||||
|
return apps.get_model(app_label, model_name)
|
||||||
|
except (ImportError, LookupError, ValueError):
|
||||||
|
raise ValueError(f"Model {model_path} does not exist!")
|
||||||
|
|
||||||
|
def _get_object_by_url(self, model_path: str, url: str):
|
||||||
|
model_class = self._get_model_class(model_path)
|
||||||
|
return model_class.objects.get(url=url)
|
||||||
|
|
||||||
|
def is_package_url(self) -> bool:
|
||||||
|
return bool(re.compile(rf'^.*/package/{self.UUID_PATTERN}$').findall(self.url))
|
||||||
|
|
||||||
|
def contains_package_url(self):
|
||||||
|
return bool(self.MODEL_PATTERNS['epdb.Package'].findall(self.url)) and not self.is_package_url()
|
||||||
|
|
||||||
|
def is_user_url(self) -> bool:
|
||||||
|
return bool(self.MODEL_PATTERNS['epdb.User'].findall(self.url))
|
||||||
|
|
||||||
|
def is_group_url(self) -> bool:
|
||||||
|
return bool(self.MODEL_PATTERNS['epdb.Group'].findall(self.url))
|
||||||
|
|
||||||
|
def is_setting_url(self) -> bool:
|
||||||
|
return bool(self.MODEL_PATTERNS['epdb.Setting'].findall(self.url))
|
||||||
|
|
||||||
|
def get_object(self) -> Optional[Any]:
|
||||||
|
# Define priority order from most specific to least specific
|
||||||
|
priority_order = [
|
||||||
|
# 3rd level
|
||||||
|
'epdb.CompoundStructure',
|
||||||
|
'epdb.Node',
|
||||||
|
'epdb.Edge',
|
||||||
|
# 2nd level
|
||||||
|
'epdb.Compound',
|
||||||
|
'epdb.Rule',
|
||||||
|
'epdb.Reaction',
|
||||||
|
'epdb.Scenario',
|
||||||
|
'epdb.EPModel',
|
||||||
|
'epdb.Pathway',
|
||||||
|
# 1st level
|
||||||
|
'epdb.Package',
|
||||||
|
'epdb.Setting',
|
||||||
|
'epdb.Group',
|
||||||
|
'epdb.User',
|
||||||
|
]
|
||||||
|
|
||||||
|
for model_path in priority_order:
|
||||||
|
if model_path in self._matches:
|
||||||
|
url = self._matches[model_path]
|
||||||
|
return self._get_object_by_url(model_path, url)
|
||||||
|
|
||||||
|
raise ValueError(f"No object found for URL {self.url}")
|
||||||
|
|
||||||
|
def get_objects(self) -> List[Any]:
|
||||||
|
"""
|
||||||
|
Get all Django model objects along the URL path in hierarchical order.
|
||||||
|
Returns objects from parent to child (e.g., Package -> Compound -> Structure).
|
||||||
|
"""
|
||||||
|
objects = []
|
||||||
|
|
||||||
|
hierarchy_order = [
|
||||||
|
# 1st level
|
||||||
|
'epdb.Package',
|
||||||
|
'epdb.Setting',
|
||||||
|
'epdb.Group',
|
||||||
|
'epdb.User',
|
||||||
|
# 2nd level
|
||||||
|
'epdb.Compound',
|
||||||
|
'epdb.Rule',
|
||||||
|
'epdb.Reaction',
|
||||||
|
'epdb.Scenario',
|
||||||
|
'epdb.EPModel',
|
||||||
|
'epdb.Pathway',
|
||||||
|
# 3rd level
|
||||||
|
'epdb.CompoundStructure',
|
||||||
|
'epdb.Node',
|
||||||
|
'epdb.Edge',
|
||||||
|
]
|
||||||
|
|
||||||
|
for model_path in hierarchy_order:
|
||||||
|
if model_path in self._matches:
|
||||||
|
url = self._matches[model_path]
|
||||||
|
objects.append(self._get_object_by_url(model_path, url))
|
||||||
|
|
||||||
|
return objects
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"EPDBURLParser(url='{self.url}')"
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"EPDBURLParser(url='{self.url}', matches={list(self._matches.keys())})"
|
||||||
|
|
||||||
|
|
||||||
class UserManager(object):
|
class UserManager(object):
|
||||||
user_pattern = re.compile(r".*/user/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}")
|
user_pattern = re.compile(r".*/user/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}")
|
||||||
|
|
||||||
@ -62,7 +189,7 @@ class UserManager(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_by_id(user, user_uuid: str):
|
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!")
|
raise ValueError("Getting user failed!")
|
||||||
return get_user_model().objects.get(uuid=user_uuid)
|
return get_user_model().objects.get(uuid=user_uuid)
|
||||||
|
|
||||||
@ -183,6 +310,25 @@ class PackageManager(object):
|
|||||||
return True
|
return True
|
||||||
return False
|
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
|
@staticmethod
|
||||||
def has_package_permission(user: 'User', package: Union[str, 'Package'], permission: str):
|
def has_package_permission(user: 'User', package: Union[str, 'Package'], permission: str):
|
||||||
|
|
||||||
@ -339,7 +485,7 @@ class PackageManager(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@transaction.atomic
|
@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 uuid import UUID, uuid4
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
@ -349,7 +495,12 @@ class PackageManager(object):
|
|||||||
|
|
||||||
pack = Package()
|
pack = Package()
|
||||||
pack.uuid = UUID(data['id'].split('/')[-1]) if keep_ids else uuid4()
|
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.reviewed = True if data['reviewStatus'] == 'reviewed' else False
|
||||||
pack.description = data['description']
|
pack.description = data['description']
|
||||||
pack.save()
|
pack.save()
|
||||||
@ -402,11 +553,13 @@ class PackageManager(object):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
res = AdditionalInformationConverter.convert(name, addinf_data)
|
res = AdditionalInformationConverter.convert(name, addinf_data)
|
||||||
|
res_cls_name = res.__class__.__name__
|
||||||
|
ai_data = json.loads(res.model_dump_json())
|
||||||
|
ai_data['uuid'] = f"{uuid4()}"
|
||||||
|
new_add_inf[res_cls_name].append(ai_data)
|
||||||
except:
|
except:
|
||||||
logger.error(f"Failed to convert {name} with {addinf_data}")
|
logger.error(f"Failed to convert {name} with {addinf_data}")
|
||||||
|
|
||||||
new_add_inf[name].append(res.model_dump_json())
|
|
||||||
|
|
||||||
scen.additional_information = new_add_inf
|
scen.additional_information = new_add_inf
|
||||||
scen.save()
|
scen.save()
|
||||||
|
|
||||||
@ -890,9 +1043,10 @@ class SearchManager(object):
|
|||||||
|
|
||||||
class SNode(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.smiles = smiles
|
||||||
self.depth = depth
|
self.depth = depth
|
||||||
|
self.app_domain_assessment = app_domain_assessment
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash(self.smiles)
|
return hash(self.smiles)
|
||||||
@ -1035,7 +1189,7 @@ class SPathway(object):
|
|||||||
def depth(self):
|
def depth(self):
|
||||||
return max([v.depth for v in self.smiles_to_node.values()])
|
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:
|
if depth == 0:
|
||||||
return self.root_nodes
|
return self.root_nodes
|
||||||
|
|
||||||
@ -1046,7 +1200,7 @@ class SPathway(object):
|
|||||||
|
|
||||||
return sorted(res, key=lambda x: x.smiles)
|
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 = []
|
res = []
|
||||||
for e in self.edges:
|
for e in self.edges:
|
||||||
for n in e.educts:
|
for n in e.educts:
|
||||||
@ -1061,7 +1215,7 @@ class SPathway(object):
|
|||||||
if from_depth is not None:
|
if from_depth is not None:
|
||||||
substrates = self._get_nodes_for_depth(from_depth)
|
substrates = self._get_nodes_for_depth(from_depth)
|
||||||
elif from_node is not None:
|
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:
|
if from_node == v:
|
||||||
substrates = [k]
|
substrates = [k]
|
||||||
break
|
break
|
||||||
@ -1071,15 +1225,44 @@ class SPathway(object):
|
|||||||
new_tp = False
|
new_tp = False
|
||||||
if substrates:
|
if substrates:
|
||||||
for sub in 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 = 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:
|
for cand_set in candidates:
|
||||||
if cand_set:
|
if cand_set:
|
||||||
new_tp = True
|
new_tp = True
|
||||||
|
# cand_set is a PredictionResult object that can consist of multiple candidate reactions
|
||||||
for cand in cand_set:
|
for cand in cand_set:
|
||||||
cand_nodes = []
|
cand_nodes = []
|
||||||
|
# candidate reactions can have multiple fragments
|
||||||
for c in cand:
|
for c in cand:
|
||||||
if c not in self.smiles_to_node:
|
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]
|
node = self.smiles_to_node[c]
|
||||||
cand_nodes.append(node)
|
cand_nodes.append(node)
|
||||||
@ -1092,18 +1275,30 @@ class SPathway(object):
|
|||||||
if len(substrates) == 0 or from_node is not None:
|
if len(substrates) == 0 or from_node is not None:
|
||||||
self.done = True
|
self.done = True
|
||||||
|
|
||||||
# Check if we need to write back data to database
|
# Check if we need to write back data to the database
|
||||||
if new_tp and self.persist:
|
if new_tp and self.persist:
|
||||||
self._sync_to_pathway()
|
self._sync_to_pathway()
|
||||||
# call save to update internal modified field
|
# call save to update the internal modified field
|
||||||
self.persist.save()
|
self.persist.save()
|
||||||
|
|
||||||
def _sync_to_pathway(self):
|
def _sync_to_pathway(self) -> None:
|
||||||
logger.info("Updating Pathway with SPathway")
|
logger.info("Updating Pathway with SPathway")
|
||||||
|
|
||||||
for snode in self.smiles_to_node.values():
|
for snode in self.smiles_to_node.values():
|
||||||
if snode not in self.snode_persist_lookup:
|
if snode not in self.snode_persist_lookup:
|
||||||
n = Node.create(self.persist, snode.smiles, snode.depth)
|
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
|
self.snode_persist_lookup[snode] = n
|
||||||
|
|
||||||
for sedge in self.edges:
|
for sedge in self.edges:
|
||||||
@ -1125,7 +1320,6 @@ class SPathway(object):
|
|||||||
self.sedge_persist_lookup[sedge] = e
|
self.sedge_persist_lookup[sedge] = e
|
||||||
|
|
||||||
logger.info("Update done!")
|
logger.info("Update done!")
|
||||||
pass
|
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
nodes = []
|
nodes = []
|
||||||
|
|||||||
@ -5,7 +5,7 @@ from django.core.management.base import BaseCommand
|
|||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
|
||||||
from epdb.logic import UserManager, GroupManager, PackageManager, SettingManager
|
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):
|
class Command(BaseCommand):
|
||||||
@ -58,7 +58,7 @@ class Command(BaseCommand):
|
|||||||
return anon, admin, g, jebus
|
return anon, admin, g, jebus
|
||||||
|
|
||||||
def import_package(self, data, owner):
|
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):
|
def create_default_setting(self, owner, packages):
|
||||||
s = SettingManager.create_setting(
|
s = SettingManager.create_setting(
|
||||||
@ -74,6 +74,76 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
return s
|
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
|
@transaction.atomic
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
# Create users
|
# Create users
|
||||||
@ -117,17 +187,17 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
# Create RR
|
# Create RR
|
||||||
ml_model = MLRelativeReasoning.create(
|
ml_model = MLRelativeReasoning.create(
|
||||||
pack,
|
package=pack,
|
||||||
'ECC - BBD - T0.5',
|
rule_packages=[mapping['EAWAG-BBD']],
|
||||||
'ML Relative Reasoning',
|
data_packages=[mapping['EAWAG-BBD']],
|
||||||
[mapping['EAWAG-BBD']],
|
eval_packages=[],
|
||||||
[mapping['EAWAG-BBD']],
|
threshold=0.5,
|
||||||
[],
|
name='ECC - BBD - T0.5',
|
||||||
0.5
|
description='ML Relative Reasoning',
|
||||||
)
|
)
|
||||||
|
|
||||||
X, y = ml_model.build_dataset()
|
ml_model.build_dataset()
|
||||||
ml_model.build_model(X, y)
|
ml_model.build_model()
|
||||||
# ml_model.evaluate_model()
|
# ml_model.evaluate_model()
|
||||||
|
|
||||||
# If available create EnviFormerModel
|
# If available create EnviFormerModel
|
||||||
|
|||||||
50
epdb/management/commands/localize_urls.py
Normal file
50
epdb/management/commands/localize_urls.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
from django.apps import apps
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
|
from django.db.models import F, Value
|
||||||
|
from django.db.models.functions import Replace
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'--old',
|
||||||
|
type=str,
|
||||||
|
help='Old Host, most likely https://envipath.org/',
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--new',
|
||||||
|
type=str,
|
||||||
|
help='New Host, most likely http://localhost:8000/',
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
MODELS = [
|
||||||
|
'User',
|
||||||
|
'Group',
|
||||||
|
'Package',
|
||||||
|
'Compound',
|
||||||
|
'CompoundStructure',
|
||||||
|
'Pathway',
|
||||||
|
'Edge',
|
||||||
|
'Node',
|
||||||
|
'Reaction',
|
||||||
|
'SimpleAmbitRule',
|
||||||
|
'SimpleRDKitRule',
|
||||||
|
'ParallelRule',
|
||||||
|
'SequentialRule',
|
||||||
|
'Scenario',
|
||||||
|
'Setting',
|
||||||
|
'MLRelativeReasoning',
|
||||||
|
'EnviFormer',
|
||||||
|
'ApplicabilityDomain',
|
||||||
|
]
|
||||||
|
for model in MODELS:
|
||||||
|
obj_cls = apps.get_model("epdb", model)
|
||||||
|
print(f"Localizing urls for {model}")
|
||||||
|
obj_cls.objects.update(
|
||||||
|
url=Replace(F('url'), Value(options['old']), Value(options['new']))
|
||||||
|
)
|
||||||
@ -2,7 +2,6 @@ from django.conf import settings
|
|||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
|
|
||||||
class LoginRequiredMiddleware:
|
class LoginRequiredMiddleware:
|
||||||
def __init__(self, get_response):
|
def __init__(self, get_response):
|
||||||
self.get_response = get_response
|
self.get_response = get_response
|
||||||
@ -11,6 +10,7 @@ class LoginRequiredMiddleware:
|
|||||||
reverse('logout'),
|
reverse('logout'),
|
||||||
reverse('admin:login'),
|
reverse('admin:login'),
|
||||||
reverse('admin:index'),
|
reverse('admin:index'),
|
||||||
|
'/api/legacy/'
|
||||||
] + getattr(settings, 'LOGIN_EXEMPT_URLS', [])
|
] + getattr(settings, 'LOGIN_EXEMPT_URLS', [])
|
||||||
|
|
||||||
def __call__(self, request):
|
def __call__(self, request):
|
||||||
|
|||||||
594
epdb/migrations/0001_initial.py
Normal file
594
epdb/migrations/0001_initial.py
Normal file
@ -0,0 +1,594 @@
|
|||||||
|
# Generated by Django 5.2.1 on 2025-07-22 20:58
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import django.contrib.auth.models
|
||||||
|
import django.contrib.auth.validators
|
||||||
|
import django.contrib.postgres.fields
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
import model_utils.fields
|
||||||
|
import uuid
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('auth', '0012_alter_user_first_name_max_length'),
|
||||||
|
('contenttypes', '0002_remove_content_type_name'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Compound',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')),
|
||||||
|
('name', models.TextField(default='no name', verbose_name='Name')),
|
||||||
|
('description', models.TextField(default='no description', verbose_name='Descriptions')),
|
||||||
|
('kv', models.JSONField(blank=True, default=dict, null=True)),
|
||||||
|
('aliases', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), default=list, size=None, verbose_name='Aliases')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='EPModel',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')),
|
||||||
|
('name', models.TextField(default='no name', verbose_name='Name')),
|
||||||
|
('description', models.TextField(default='no description', verbose_name='Descriptions')),
|
||||||
|
('kv', models.JSONField(blank=True, default=dict, null=True)),
|
||||||
|
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
'base_manager_name': 'objects',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Permission',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
|
||||||
|
('permission', models.CharField(choices=[('read', 'Read'), ('write', 'Write'), ('all', 'All')], max_length=32)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='License',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('link', models.URLField(verbose_name='link')),
|
||||||
|
('image_link', models.URLField(verbose_name='Image link')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Rule',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')),
|
||||||
|
('name', models.TextField(default='no name', verbose_name='Name')),
|
||||||
|
('description', models.TextField(default='no description', verbose_name='Descriptions')),
|
||||||
|
('kv', models.JSONField(blank=True, default=dict, null=True)),
|
||||||
|
('aliases', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), default=list, size=None, verbose_name='Aliases')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
'base_manager_name': 'objects',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='User',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||||
|
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||||
|
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||||
|
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
||||||
|
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
||||||
|
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||||
|
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||||
|
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||||
|
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||||
|
('email', models.EmailField(max_length=254, unique=True)),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')),
|
||||||
|
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
|
||||||
|
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'user',
|
||||||
|
'verbose_name_plural': 'users',
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
managers=[
|
||||||
|
('objects', django.contrib.auth.models.UserManager()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='APIToken',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('hashed_key', models.CharField(max_length=128, unique=True)),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('expires_at', models.DateTimeField(blank=True, default=datetime.datetime(2025, 10, 20, 20, 58, 48, 351675, tzinfo=datetime.timezone.utc), null=True)),
|
||||||
|
('name', models.CharField(blank=True, help_text='Optional name for the token', max_length=100)),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CompoundStructure',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')),
|
||||||
|
('name', models.TextField(default='no name', verbose_name='Name')),
|
||||||
|
('description', models.TextField(default='no description', verbose_name='Descriptions')),
|
||||||
|
('kv', models.JSONField(blank=True, default=dict, null=True)),
|
||||||
|
('aliases', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), default=list, size=None, verbose_name='Aliases')),
|
||||||
|
('smiles', models.TextField(verbose_name='SMILES')),
|
||||||
|
('canonical_smiles', models.TextField(verbose_name='Canonical SMILES')),
|
||||||
|
('inchikey', models.TextField(max_length=27, verbose_name='InChIKey')),
|
||||||
|
('normalized_structure', models.BooleanField(default=False)),
|
||||||
|
('compound', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.compound')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='compound',
|
||||||
|
name='default_structure',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='compound_default_structure', to='epdb.compoundstructure', verbose_name='Default Structure'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Edge',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')),
|
||||||
|
('name', models.TextField(default='no name', verbose_name='Name')),
|
||||||
|
('description', models.TextField(default='no description', verbose_name='Descriptions')),
|
||||||
|
('kv', models.JSONField(blank=True, default=dict, null=True)),
|
||||||
|
('aliases', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), default=list, size=None, verbose_name='Aliases')),
|
||||||
|
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
'base_manager_name': 'objects',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='EnviFormer',
|
||||||
|
fields=[
|
||||||
|
('epmodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='epdb.epmodel')),
|
||||||
|
('threshold', models.FloatField(default=0.5)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
'base_manager_name': 'objects',
|
||||||
|
},
|
||||||
|
bases=('epdb.epmodel',),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='PluginModel',
|
||||||
|
fields=[
|
||||||
|
('epmodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='epdb.epmodel')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
'base_manager_name': 'objects',
|
||||||
|
},
|
||||||
|
bases=('epdb.epmodel',),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='RuleBaseRelativeReasoning',
|
||||||
|
fields=[
|
||||||
|
('epmodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='epdb.epmodel')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
'base_manager_name': 'objects',
|
||||||
|
},
|
||||||
|
bases=('epdb.epmodel',),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Group',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')),
|
||||||
|
('name', models.TextField(verbose_name='Group name')),
|
||||||
|
('public', models.BooleanField(default=False, verbose_name='Public Group')),
|
||||||
|
('description', models.TextField(default='no description', verbose_name='Descriptions')),
|
||||||
|
('group_member', models.ManyToManyField(related_name='groups_in_group', to='epdb.group', verbose_name='Group member')),
|
||||||
|
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Group Owner')),
|
||||||
|
('user_member', models.ManyToManyField(related_name='users_in_group', to=settings.AUTH_USER_MODEL, verbose_name='User members')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='user',
|
||||||
|
name='default_group',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_group', to='epdb.group', verbose_name='Default Group'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Node',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')),
|
||||||
|
('name', models.TextField(default='no name', verbose_name='Name')),
|
||||||
|
('description', models.TextField(default='no description', verbose_name='Descriptions')),
|
||||||
|
('kv', models.JSONField(blank=True, default=dict, null=True)),
|
||||||
|
('aliases', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), default=list, size=None, verbose_name='Aliases')),
|
||||||
|
('depth', models.IntegerField(verbose_name='Node depth')),
|
||||||
|
('default_node_label', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='default_node_structure', to='epdb.compoundstructure', verbose_name='Default Node Label')),
|
||||||
|
('node_labels', models.ManyToManyField(related_name='node_structures', to='epdb.compoundstructure', verbose_name='All Node Labels')),
|
||||||
|
('out_edges', models.ManyToManyField(to='epdb.edge', verbose_name='Outgoing Edges')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='edge',
|
||||||
|
name='end_nodes',
|
||||||
|
field=models.ManyToManyField(related_name='edge_products', to='epdb.node', verbose_name='End Nodes'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='edge',
|
||||||
|
name='start_nodes',
|
||||||
|
field=models.ManyToManyField(related_name='edge_educts', to='epdb.node', verbose_name='Start Nodes'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Package',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')),
|
||||||
|
('name', models.TextField(default='no name', verbose_name='Name')),
|
||||||
|
('description', models.TextField(default='no description', verbose_name='Descriptions')),
|
||||||
|
('kv', models.JSONField(blank=True, default=dict, null=True)),
|
||||||
|
('reviewed', models.BooleanField(default=False, verbose_name='Reviewstatus')),
|
||||||
|
('license', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='epdb.license', verbose_name='License')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='epmodel',
|
||||||
|
name='package',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.package', verbose_name='Package'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='compound',
|
||||||
|
name='package',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.package', verbose_name='Package'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='user',
|
||||||
|
name='default_package',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='epdb.package', verbose_name='Default Package'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SequentialRule',
|
||||||
|
fields=[
|
||||||
|
('rule_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='epdb.rule')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
'base_manager_name': 'objects',
|
||||||
|
},
|
||||||
|
bases=('epdb.rule',),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SimpleRule',
|
||||||
|
fields=[
|
||||||
|
('rule_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='epdb.rule')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
'base_manager_name': 'objects',
|
||||||
|
},
|
||||||
|
bases=('epdb.rule',),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='rule',
|
||||||
|
name='package',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.package', verbose_name='Package'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='rule',
|
||||||
|
name='polymorphic_ctype',
|
||||||
|
field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Pathway',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')),
|
||||||
|
('name', models.TextField(default='no name', verbose_name='Name')),
|
||||||
|
('description', models.TextField(default='no description', verbose_name='Descriptions')),
|
||||||
|
('kv', models.JSONField(blank=True, default=dict, null=True)),
|
||||||
|
('aliases', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), default=list, size=None, verbose_name='Aliases')),
|
||||||
|
('package', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.package', verbose_name='Package')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='node',
|
||||||
|
name='pathway',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.pathway', verbose_name='belongs to'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='edge',
|
||||||
|
name='pathway',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.pathway', verbose_name='belongs to'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Reaction',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')),
|
||||||
|
('name', models.TextField(default='no name', verbose_name='Name')),
|
||||||
|
('description', models.TextField(default='no description', verbose_name='Descriptions')),
|
||||||
|
('kv', models.JSONField(blank=True, default=dict, null=True)),
|
||||||
|
('aliases', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), default=list, size=None, verbose_name='Aliases')),
|
||||||
|
('multi_step', models.BooleanField(verbose_name='Multistep Reaction')),
|
||||||
|
('medline_references', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), null=True, size=None, verbose_name='Medline References')),
|
||||||
|
('educts', models.ManyToManyField(related_name='reaction_educts', to='epdb.compoundstructure', verbose_name='Educts')),
|
||||||
|
('package', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.package', verbose_name='Package')),
|
||||||
|
('products', models.ManyToManyField(related_name='reaction_products', to='epdb.compoundstructure', verbose_name='Products')),
|
||||||
|
('rules', models.ManyToManyField(related_name='reaction_rule', to='epdb.rule', verbose_name='Rule')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='edge',
|
||||||
|
name='edge_label',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='epdb.reaction', verbose_name='Edge label'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Scenario',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')),
|
||||||
|
('name', models.TextField(default='no name', verbose_name='Name')),
|
||||||
|
('description', models.TextField(default='no description', verbose_name='Descriptions')),
|
||||||
|
('kv', models.JSONField(blank=True, default=dict, null=True)),
|
||||||
|
('scenario_date', models.CharField(default='No date', max_length=256)),
|
||||||
|
('scenario_type', models.CharField(default='Not specified', max_length=256)),
|
||||||
|
('additional_information', models.JSONField(verbose_name='Additional Information')),
|
||||||
|
('package', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.package', verbose_name='Package')),
|
||||||
|
('parent', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='epdb.scenario')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='rule',
|
||||||
|
name='scenarios',
|
||||||
|
field=models.ManyToManyField(to='epdb.scenario', verbose_name='Attached Scenarios'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='reaction',
|
||||||
|
name='scenarios',
|
||||||
|
field=models.ManyToManyField(to='epdb.scenario', verbose_name='Attached Scenarios'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='pathway',
|
||||||
|
name='scenarios',
|
||||||
|
field=models.ManyToManyField(to='epdb.scenario', verbose_name='Attached Scenarios'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='node',
|
||||||
|
name='scenarios',
|
||||||
|
field=models.ManyToManyField(to='epdb.scenario', verbose_name='Attached Scenarios'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='edge',
|
||||||
|
name='scenarios',
|
||||||
|
field=models.ManyToManyField(to='epdb.scenario', verbose_name='Attached Scenarios'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='compoundstructure',
|
||||||
|
name='scenarios',
|
||||||
|
field=models.ManyToManyField(to='epdb.scenario', verbose_name='Attached Scenarios'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='compound',
|
||||||
|
name='scenarios',
|
||||||
|
field=models.ManyToManyField(to='epdb.scenario', verbose_name='Attached Scenarios'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Setting',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')),
|
||||||
|
('name', models.TextField(default='no name', verbose_name='Name')),
|
||||||
|
('description', models.TextField(default='no description', verbose_name='Descriptions')),
|
||||||
|
('kv', models.JSONField(blank=True, default=dict, null=True)),
|
||||||
|
('public', models.BooleanField(default=False)),
|
||||||
|
('global_default', models.BooleanField(default=False)),
|
||||||
|
('max_depth', models.IntegerField(default=5, verbose_name='Setting Max Depth')),
|
||||||
|
('max_nodes', models.IntegerField(default=30, verbose_name='Setting Max Number of Nodes')),
|
||||||
|
('model_threshold', models.FloatField(blank=True, default=0.25, null=True, verbose_name='Setting Model Threshold')),
|
||||||
|
('model', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='epdb.epmodel', verbose_name='Setting EPModel')),
|
||||||
|
('rule_packages', models.ManyToManyField(blank=True, related_name='setting_rule_packages', to='epdb.package', verbose_name='Setting Rule Packages')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='pathway',
|
||||||
|
name='setting',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='epdb.setting', verbose_name='Setting'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='user',
|
||||||
|
name='default_setting',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='epdb.setting', verbose_name='The users default settings'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='MLRelativeReasoning',
|
||||||
|
fields=[
|
||||||
|
('epmodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='epdb.epmodel')),
|
||||||
|
('threshold', models.FloatField(default=0.5)),
|
||||||
|
('model_status', models.CharField(choices=[('INITIAL', 'Initial'), ('INITIALIZING', 'Model is initializing.'), ('BUILDING', 'Model is building.'), ('BUILT_NOT_EVALUATED', 'Model is built and can be used for predictions, Model is not evaluated yet.'), ('EVALUATING', 'Model is evaluating'), ('FINISHED', 'Model has finished building and evaluation.'), ('ERROR', 'Model has failed.')], default='INITIAL')),
|
||||||
|
('eval_results', models.JSONField(blank=True, default=dict, null=True)),
|
||||||
|
('data_packages', models.ManyToManyField(related_name='data_packages', to='epdb.package', verbose_name='Data Packages')),
|
||||||
|
('eval_packages', models.ManyToManyField(related_name='eval_packages', to='epdb.package', verbose_name='Evaluation Packages')),
|
||||||
|
('rule_packages', models.ManyToManyField(related_name='rule_packages', to='epdb.package', verbose_name='Rule Packages')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
'base_manager_name': 'objects',
|
||||||
|
},
|
||||||
|
bases=('epdb.epmodel',),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ApplicabilityDomain',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID of this object')),
|
||||||
|
('name', models.TextField(default='no name', verbose_name='Name')),
|
||||||
|
('description', models.TextField(default='no description', verbose_name='Descriptions')),
|
||||||
|
('kv', models.JSONField(blank=True, default=dict, null=True)),
|
||||||
|
('num_neighbours', models.FloatField(default=5)),
|
||||||
|
('reliability_threshold', models.FloatField(default=0.5)),
|
||||||
|
('local_compatibilty_threshold', models.FloatField(default=0.5)),
|
||||||
|
('model', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.mlrelativereasoning')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SimpleAmbitRule',
|
||||||
|
fields=[
|
||||||
|
('simplerule_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='epdb.simplerule')),
|
||||||
|
('smirks', models.TextField(verbose_name='SMIRKS')),
|
||||||
|
('reactant_filter_smarts', models.TextField(null=True, verbose_name='Reactant Filter SMARTS')),
|
||||||
|
('product_filter_smarts', models.TextField(null=True, verbose_name='Product Filter SMARTS')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
'base_manager_name': 'objects',
|
||||||
|
},
|
||||||
|
bases=('epdb.simplerule',),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SimpleRDKitRule',
|
||||||
|
fields=[
|
||||||
|
('simplerule_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='epdb.simplerule')),
|
||||||
|
('reaction_smarts', models.TextField(verbose_name='SMIRKS')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
'base_manager_name': 'objects',
|
||||||
|
},
|
||||||
|
bases=('epdb.simplerule',),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SequentialRuleOrdering',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('order_index', models.IntegerField()),
|
||||||
|
('sequential_rule', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.sequentialrule')),
|
||||||
|
('simple_rule', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.simplerule')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='sequentialrule',
|
||||||
|
name='simple_rules',
|
||||||
|
field=models.ManyToManyField(through='epdb.SequentialRuleOrdering', to='epdb.simplerule', verbose_name='Simple rules'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ParallelRule',
|
||||||
|
fields=[
|
||||||
|
('rule_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='epdb.rule')),
|
||||||
|
('simple_rules', models.ManyToManyField(to='epdb.simplerule', verbose_name='Simple rules')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
'base_manager_name': 'objects',
|
||||||
|
},
|
||||||
|
bases=('epdb.rule',),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='compound',
|
||||||
|
unique_together={('uuid', 'package')},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='GroupPackagePermission',
|
||||||
|
fields=[
|
||||||
|
('permission_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, to='epdb.permission')),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False, verbose_name='UUID of this object')),
|
||||||
|
('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.group', verbose_name='Permission to')),
|
||||||
|
('package', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.package', verbose_name='Permission on')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'unique_together': {('package', 'group')},
|
||||||
|
},
|
||||||
|
bases=('epdb.permission',),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserPackagePermission',
|
||||||
|
fields=[
|
||||||
|
('permission_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, to='epdb.permission')),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False, verbose_name='UUID of this object')),
|
||||||
|
('package', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.package', verbose_name='Permission on')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Permission to')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'unique_together': {('package', 'user')},
|
||||||
|
},
|
||||||
|
bases=('epdb.permission',),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserSettingPermission',
|
||||||
|
fields=[
|
||||||
|
('permission_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, to='epdb.permission')),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False, verbose_name='UUID of this object')),
|
||||||
|
('setting', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.setting', verbose_name='Permission on')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Permission to')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'unique_together': {('setting', 'user')},
|
||||||
|
},
|
||||||
|
bases=('epdb.permission',),
|
||||||
|
),
|
||||||
|
]
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,128 @@
|
|||||||
|
# Generated by Django 5.2.1 on 2025-08-25 18:07
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
import model_utils.fields
|
||||||
|
import uuid
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('contenttypes', '0002_remove_content_type_name'),
|
||||||
|
('epdb', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ExternalDatabase',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
|
||||||
|
('name', models.CharField(max_length=100, unique=True, verbose_name='Database Name')),
|
||||||
|
('full_name', models.CharField(blank=True, max_length=255, verbose_name='Full Database Name')),
|
||||||
|
('description', models.TextField(blank=True, verbose_name='Description')),
|
||||||
|
('base_url', models.URLField(blank=True, null=True, verbose_name='Base URL')),
|
||||||
|
('url_pattern', models.CharField(blank=True, help_text="URL pattern with {id} placeholder, e.g., 'https://pubchem.ncbi.nlm.nih.gov/compound/{id}'", max_length=500, verbose_name='URL Pattern')),
|
||||||
|
('is_active', models.BooleanField(default=True, verbose_name='Is Active')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'External Database',
|
||||||
|
'verbose_name_plural': 'External Databases',
|
||||||
|
'db_table': 'epdb_external_database',
|
||||||
|
'ordering': ['name'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='apitoken',
|
||||||
|
options={'ordering': ['-created'], 'verbose_name': 'API Token', 'verbose_name_plural': 'API Tokens'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='edge',
|
||||||
|
options={},
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='edge',
|
||||||
|
name='polymorphic_ctype',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='apitoken',
|
||||||
|
name='is_active',
|
||||||
|
field=models.BooleanField(default=True, help_text='Whether this token is active'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='apitoken',
|
||||||
|
name='modified',
|
||||||
|
field=model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='applicabilitydomain',
|
||||||
|
name='functional_groups',
|
||||||
|
field=models.JSONField(blank=True, default=dict, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='mlrelativereasoning',
|
||||||
|
name='app_domain',
|
||||||
|
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='epdb.applicabilitydomain'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='apitoken',
|
||||||
|
name='created',
|
||||||
|
field=model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='apitoken',
|
||||||
|
name='expires_at',
|
||||||
|
field=models.DateTimeField(blank=True, help_text='Token expiration time (null for no expiration)', null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='apitoken',
|
||||||
|
name='hashed_key',
|
||||||
|
field=models.CharField(help_text='SHA-256 hash of the token key', max_length=128, unique=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='apitoken',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(help_text='Descriptive name for this token', max_length=100),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='apitoken',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(help_text='User who owns this token', on_delete=django.db.models.deletion.CASCADE, related_name='api_tokens', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='applicabilitydomain',
|
||||||
|
name='num_neighbours',
|
||||||
|
field=models.IntegerField(default=5),
|
||||||
|
),
|
||||||
|
migrations.AlterModelTable(
|
||||||
|
name='apitoken',
|
||||||
|
table='epdb_api_token',
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ExternalIdentifier',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
|
||||||
|
('object_id', models.IntegerField()),
|
||||||
|
('identifier_value', models.CharField(max_length=255, verbose_name='Identifier Value')),
|
||||||
|
('url', models.URLField(blank=True, null=True, verbose_name='Direct URL')),
|
||||||
|
('is_primary', models.BooleanField(default=False, help_text='Mark this as the primary identifier for this database', verbose_name='Is Primary')),
|
||||||
|
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
|
||||||
|
('database', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='epdb.externaldatabase', verbose_name='External Database')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'External Identifier',
|
||||||
|
'verbose_name_plural': 'External Identifiers',
|
||||||
|
'db_table': 'epdb_external_identifier',
|
||||||
|
'indexes': [models.Index(fields=['content_type', 'object_id'], name='epdb_extern_content_b76813_idx'), models.Index(fields=['database', 'identifier_value'], name='epdb_extern_databas_486422_idx')],
|
||||||
|
'unique_together': {('content_type', 'object_id', 'database', 'identifier_value')},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -0,0 +1,228 @@
|
|||||||
|
# Generated by Django 5.2.1 on 2025-08-26 17:05
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
def populate_url(apps, schema_editor):
|
||||||
|
MODELS = [
|
||||||
|
'User',
|
||||||
|
'Group',
|
||||||
|
'Package',
|
||||||
|
'Compound',
|
||||||
|
'CompoundStructure',
|
||||||
|
'Pathway',
|
||||||
|
'Edge',
|
||||||
|
'Node',
|
||||||
|
'Reaction',
|
||||||
|
'SimpleAmbitRule',
|
||||||
|
'SimpleRDKitRule',
|
||||||
|
'ParallelRule',
|
||||||
|
'SequentialRule',
|
||||||
|
'Scenario',
|
||||||
|
'Setting',
|
||||||
|
'MLRelativeReasoning',
|
||||||
|
'EnviFormer',
|
||||||
|
'ApplicabilityDomain',
|
||||||
|
]
|
||||||
|
for model in MODELS:
|
||||||
|
obj_cls = apps.get_model("epdb", model)
|
||||||
|
for obj in obj_cls.objects.all():
|
||||||
|
obj.url = assemble_url(obj)
|
||||||
|
if obj.url is None:
|
||||||
|
raise ValueError(f"Could not assemble url for {obj}")
|
||||||
|
obj.save()
|
||||||
|
|
||||||
|
|
||||||
|
def assemble_url(obj):
|
||||||
|
from django.conf import settings as s
|
||||||
|
match obj.__class__.__name__:
|
||||||
|
case 'User':
|
||||||
|
return '{}/user/{}'.format(s.SERVER_URL, obj.uuid)
|
||||||
|
case 'Group':
|
||||||
|
return '{}/group/{}'.format(s.SERVER_URL, obj.uuid)
|
||||||
|
case 'Package':
|
||||||
|
return '{}/package/{}'.format(s.SERVER_URL, obj.uuid)
|
||||||
|
case 'Compound':
|
||||||
|
return '{}/compound/{}'.format(obj.package.url, obj.uuid)
|
||||||
|
case 'CompoundStructure':
|
||||||
|
return '{}/structure/{}'.format(obj.compound.url, obj.uuid)
|
||||||
|
case 'SimpleAmbitRule':
|
||||||
|
return '{}/simple-ambit-rule/{}'.format(obj.package.url, obj.uuid)
|
||||||
|
case 'SimpleRDKitRule':
|
||||||
|
return '{}/simple-rdkit-rule/{}'.format(obj.package.url, obj.uuid)
|
||||||
|
case 'ParallelRule':
|
||||||
|
return '{}/parallel-rule/{}'.format(obj.package.url, obj.uuid)
|
||||||
|
case 'SequentialRule':
|
||||||
|
return '{}/sequential-rule/{}'.format(obj.compound.url, obj.uuid)
|
||||||
|
case 'Reaction':
|
||||||
|
return '{}/reaction/{}'.format(obj.package.url, obj.uuid)
|
||||||
|
case 'Pathway':
|
||||||
|
return '{}/pathway/{}'.format(obj.package.url, obj.uuid)
|
||||||
|
case 'Node':
|
||||||
|
return '{}/node/{}'.format(obj.pathway.url, obj.uuid)
|
||||||
|
case 'Edge':
|
||||||
|
return '{}/edge/{}'.format(obj.pathway.url, obj.uuid)
|
||||||
|
case 'MLRelativeReasoning':
|
||||||
|
return '{}/model/{}'.format(obj.package.url, obj.uuid)
|
||||||
|
case 'EnviFormer':
|
||||||
|
return '{}/model/{}'.format(obj.package.url, obj.uuid)
|
||||||
|
case 'ApplicabilityDomain':
|
||||||
|
return '{}/model/{}/applicability-domain/{}'.format(obj.model.package.url, obj.model.uuid, obj.uuid)
|
||||||
|
case 'Scenario':
|
||||||
|
return '{}/scenario/{}'.format(obj.package.url, obj.uuid)
|
||||||
|
case 'Setting':
|
||||||
|
return '{}/setting/{}'.format(s.SERVER_URL, obj.uuid)
|
||||||
|
case _:
|
||||||
|
raise ValueError(f"Unknown model {obj.__class__.__name__}")
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('epdb', '0002_externaldatabase_alter_apitoken_options_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='applicabilitydomain',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=False, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='compound',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=False, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='compoundstructure',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=False, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='edge',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=False, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='epmodel',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=False, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='group',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=False, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='node',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=False, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='package',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=False, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='pathway',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=False, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='reaction',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=False, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='rule',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=False, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='scenario',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=False, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='setting',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=False, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='user',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=False, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
|
||||||
|
migrations.RunPython(populate_url, reverse_code=migrations.RunPython.noop),
|
||||||
|
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='applicabilitydomain',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=True, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='compound',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=True, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='compoundstructure',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=True, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='edge',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=True, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='epmodel',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=True, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='group',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=True, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='node',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=True, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='package',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=True, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='pathway',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=True, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='reaction',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=True, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='rule',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=True, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='scenario',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=True, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='setting',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=True, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='user',
|
||||||
|
name='url',
|
||||||
|
field=models.TextField(null=True, unique=True, verbose_name='URL'),
|
||||||
|
),
|
||||||
|
]
|
||||||
1564
epdb/models.py
1564
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')
|
@shared_task(queue='model')
|
||||||
def build_model(model_pk: int):
|
def build_model(model_pk: int):
|
||||||
mod = EPModel.objects.get(id=model_pk)
|
mod = EPModel.objects.get(id=model_pk)
|
||||||
X, y = mod.build_dataset()
|
mod.build_dataset()
|
||||||
mod.build_model(X, y)
|
mod.build_model()
|
||||||
|
|
||||||
|
|
||||||
@shared_task(queue='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)
|
spw.predict_step(from_depth=level)
|
||||||
level += 1
|
level += 1
|
||||||
|
|
||||||
# break in case we are in incremental model
|
# break in case we are in incremental mode
|
||||||
if limit != -1:
|
if limit != -1:
|
||||||
if level >= limit:
|
if level >= limit:
|
||||||
break
|
break
|
||||||
|
|||||||
425
epdb/views.py
425
epdb/views.py
@ -7,10 +7,12 @@ from django.contrib.auth import get_user_model
|
|||||||
from django.http import JsonResponse, HttpResponse, HttpResponseNotAllowed, HttpResponseBadRequest
|
from django.http import JsonResponse, HttpResponse, HttpResponseNotAllowed, HttpResponseBadRequest
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
from envipy_additional_information import NAME_MAPPING
|
||||||
|
|
||||||
from utilities.chem import FormatConverter, IndigoUtils
|
from utilities.chem import FormatConverter, IndigoUtils
|
||||||
from utilities.decorators import package_permission_required
|
from utilities.decorators import package_permission_required
|
||||||
from .logic import GroupManager, PackageManager, UserManager, SettingManager, SearchManager
|
from utilities.misc import HTMLGenerator
|
||||||
|
from .logic import GroupManager, PackageManager, UserManager, SettingManager, SearchManager, EPDBURLParser
|
||||||
from .models import Package, GroupPackagePermission, Group, CompoundStructure, Compound, Reaction, Rule, Pathway, Node, \
|
from .models import Package, GroupPackagePermission, Group, CompoundStructure, Compound, Reaction, Rule, Pathway, Node, \
|
||||||
EPModel, EnviFormer, MLRelativeReasoning, RuleBaseRelativeReasoning, Scenario, SimpleAmbitRule, APIToken, \
|
EPModel, EnviFormer, MLRelativeReasoning, RuleBaseRelativeReasoning, Scenario, SimpleAmbitRule, APIToken, \
|
||||||
UserPackagePermission, Permission, License, User, Edge
|
UserPackagePermission, Permission, License, User, Edge
|
||||||
@ -103,7 +105,10 @@ def login(request):
|
|||||||
else:
|
else:
|
||||||
context['message'] = "Account has been created! You'll receive a mail to activate your account shortly."
|
context['message'] = "Account has been created! You'll receive a mail to activate your account shortly."
|
||||||
return render(request, 'login.html', context)
|
return render(request, 'login.html', context)
|
||||||
|
else:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
else:
|
||||||
|
return HttpResponseNotAllowed(['GET', 'POST'])
|
||||||
|
|
||||||
def logout(request):
|
def logout(request):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
@ -136,7 +141,7 @@ def editable(request, user):
|
|||||||
f"{s.SERVER_URL}/group", f"{s.SERVER_URL}/search"]:
|
f"{s.SERVER_URL}/group", f"{s.SERVER_URL}/search"]:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
print(f"Unknown url: {url}")
|
logger.debug(f"Unknown url: {url}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@ -144,6 +149,12 @@ def get_base_context(request, for_user=None) -> Dict[str, Any]:
|
|||||||
current_user = _anonymous_or_real(request)
|
current_user = _anonymous_or_real(request)
|
||||||
can_edit = editable(request, current_user)
|
can_edit = editable(request, current_user)
|
||||||
|
|
||||||
|
parser = EPDBURLParser(request.build_absolute_uri(request.path))
|
||||||
|
|
||||||
|
url_contains_package = False
|
||||||
|
if parser.contains_package_url() or parser.is_package_url():
|
||||||
|
url_contains_package = True
|
||||||
|
|
||||||
if for_user:
|
if for_user:
|
||||||
current_user = for_user
|
current_user = for_user
|
||||||
|
|
||||||
@ -154,11 +165,12 @@ def get_base_context(request, for_user=None) -> Dict[str, Any]:
|
|||||||
'server_url': s.SERVER_URL,
|
'server_url': s.SERVER_URL,
|
||||||
'user': current_user,
|
'user': current_user,
|
||||||
'can_edit': can_edit,
|
'can_edit': can_edit,
|
||||||
|
'url_contains_package': url_contains_package,
|
||||||
'readable_packages': PackageManager.get_all_readable_packages(current_user, include_reviewed=True),
|
'readable_packages': PackageManager.get_all_readable_packages(current_user, include_reviewed=True),
|
||||||
'writeable_packages': PackageManager.get_all_writeable_packages(current_user),
|
'writeable_packages': PackageManager.get_all_writeable_packages(current_user),
|
||||||
'available_groups': GroupManager.get_groups(current_user),
|
'available_groups': GroupManager.get_groups(current_user),
|
||||||
'available_settings': SettingManager.get_all_settings(current_user),
|
'available_settings': SettingManager.get_all_settings(current_user),
|
||||||
'enabled_features': [],
|
'enabled_features': s.FLAGS,
|
||||||
'debug': s.DEBUG,
|
'debug': s.DEBUG,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -196,6 +208,41 @@ def breadcrumbs(first_level_object=None, second_level_namespace=None, second_lev
|
|||||||
return bread
|
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 copy_object(current_user, target_package: 'Package', source_object_url: str):
|
||||||
|
# Ensures that source is readable
|
||||||
|
source_package = PackageManager.get_package_by_url(current_user, source_object_url)
|
||||||
|
|
||||||
|
parser = EPDBURLParser(source_object_url)
|
||||||
|
|
||||||
|
# if the url won't contain a package or is a plain package
|
||||||
|
if not parser.contains_package_url():
|
||||||
|
raise ValueError(f"Object {source_object_url} can't be copied!")
|
||||||
|
|
||||||
|
# Gets the most specific object
|
||||||
|
source_object = parser.get_object()
|
||||||
|
|
||||||
|
if hasattr(source_object, 'copy'):
|
||||||
|
mapping = dict()
|
||||||
|
copy = source_object.copy(target_package, mapping)
|
||||||
|
|
||||||
|
if s.DEBUG:
|
||||||
|
for k, v in mapping.items():
|
||||||
|
logger.debug(f"Mapping {k.url} to {v.url}")
|
||||||
|
|
||||||
|
return copy
|
||||||
|
|
||||||
|
raise ValueError(f"Object {source_object} can't be copied!")
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
context = get_base_context(request)
|
context = get_base_context(request)
|
||||||
context['title'] = 'enviPath - Home'
|
context['title'] = 'enviPath - Home'
|
||||||
@ -424,8 +471,7 @@ def scenarios(request):
|
|||||||
if request.GET.get('all'):
|
if request.GET.get('all'):
|
||||||
return JsonResponse({
|
return JsonResponse({
|
||||||
"objects": [
|
"objects": [
|
||||||
{"name": pw.name, "url": pw.url, "reviewed": True}
|
{"name": s.name, "url": s.url, "reviewed": True} for s in reviewed_scenario_qs
|
||||||
for pw in reviewed_scenario_qs
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -505,6 +551,7 @@ def search(request):
|
|||||||
packages = PackageManager.get_reviewed_packages()
|
packages = PackageManager.get_reviewed_packages()
|
||||||
|
|
||||||
search_result = SearchManager.search(packages, searchterm, mode)
|
search_result = SearchManager.search(packages, searchterm, mode)
|
||||||
|
|
||||||
return JsonResponse(search_result, safe=False)
|
return JsonResponse(search_result, safe=False)
|
||||||
|
|
||||||
context = get_base_context(request)
|
context = get_base_context(request)
|
||||||
@ -530,6 +577,7 @@ def search(request):
|
|||||||
packages = PackageManager.get_reviewed_packages()
|
packages = PackageManager.get_reviewed_packages()
|
||||||
|
|
||||||
context['search_result'] = SearchManager.search(packages, searchterm, mode)
|
context['search_result'] = SearchManager.search(packages, searchterm, mode)
|
||||||
|
context['search_result']['searchterm'] = searchterm
|
||||||
|
|
||||||
return render(request, 'search.html', context)
|
return render(request, 'search.html', context)
|
||||||
|
|
||||||
@ -572,15 +620,21 @@ def package_models(request, package_uuid):
|
|||||||
context['model_types'] = {
|
context['model_types'] = {
|
||||||
'ML Relative Reasoning': 'ml-relative-reasoning',
|
'ML Relative Reasoning': 'ml-relative-reasoning',
|
||||||
'Rule Based Relative Reasoning': 'rule-based-relative-reasoning',
|
'Rule Based Relative Reasoning': 'rule-based-relative-reasoning',
|
||||||
'EnviFormer': 'enviformer',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v in s.CLASSIFIER_PLUGINS.items():
|
if s.FLAGS.get('ENVIFORMER', False):
|
||||||
context['model_types'][v.display()] = k
|
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)
|
return render(request, 'collections/objects_list.html', context)
|
||||||
|
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
|
|
||||||
|
log_post_params(request)
|
||||||
|
|
||||||
name = request.POST.get('model-name')
|
name = request.POST.get('model-name')
|
||||||
description = request.POST.get('model-description')
|
description = request.POST.get('model-description')
|
||||||
|
|
||||||
@ -603,14 +657,25 @@ def package_models(request, package_uuid):
|
|||||||
data_package_objs = [PackageManager.get_package_by_url(current_user, p) for p in data_packages]
|
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]
|
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(
|
mod = MLRelativeReasoning.create(
|
||||||
current_package,
|
package=current_package,
|
||||||
name,
|
name=name,
|
||||||
description,
|
description=description,
|
||||||
rule_package_objs,
|
rule_packages=rule_package_objs,
|
||||||
data_package_objs,
|
data_packages=data_package_objs,
|
||||||
eval_packages_objs,
|
eval_packages=eval_packages_objs,
|
||||||
threshold
|
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
|
from .tasks import build_model
|
||||||
@ -646,7 +711,7 @@ def package_model(request, package_uuid, model_uuid):
|
|||||||
if len(pr) > 0:
|
if len(pr) > 0:
|
||||||
products = []
|
products = []
|
||||||
for prod_set in pr.product_sets:
|
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]))
|
products.append(tuple([x for x in prod_set]))
|
||||||
|
|
||||||
res.append({
|
res.append({
|
||||||
@ -657,6 +722,12 @@ def package_model(request, package_uuid, model_uuid):
|
|||||||
|
|
||||||
return JsonResponse(res, safe=False)
|
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 = get_base_context(request)
|
||||||
context['title'] = f'enviPath - {current_package.name} - {current_model.name}'
|
context['title'] = f'enviPath - {current_package.name} - {current_model.name}'
|
||||||
|
|
||||||
@ -665,12 +736,13 @@ def package_model(request, package_uuid, model_uuid):
|
|||||||
context['breadcrumbs'] = breadcrumbs(current_package, 'model', current_model)
|
context['breadcrumbs'] = breadcrumbs(current_package, 'model', current_model)
|
||||||
|
|
||||||
context['model'] = current_model
|
context['model'] = current_model
|
||||||
|
context['current_object'] = current_model
|
||||||
|
|
||||||
return render(request, 'objects/model.html', context)
|
return render(request, 'objects/model.html', context)
|
||||||
|
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
if hidden := request.POST.get('hidden', None):
|
if hidden := request.POST.get('hidden', None):
|
||||||
if hidden == 'delete-model':
|
if hidden == 'delete':
|
||||||
current_model.delete()
|
current_model.delete()
|
||||||
return redirect(current_package.url + '/model')
|
return redirect(current_package.url + '/model')
|
||||||
else:
|
else:
|
||||||
@ -696,8 +768,6 @@ def package(request, package_uuid):
|
|||||||
context['breadcrumbs'] = breadcrumbs(current_package)
|
context['breadcrumbs'] = breadcrumbs(current_package)
|
||||||
|
|
||||||
context['package'] = 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)
|
user_perms = UserPackagePermission.objects.filter(package=current_package)
|
||||||
users = get_user_model().objects.exclude(
|
users = get_user_model().objects.exclude(
|
||||||
@ -716,14 +786,24 @@ def package(request, package_uuid):
|
|||||||
|
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
|
|
||||||
if s.DEBUG:
|
log_post_params(request)
|
||||||
for k, v in request.POST.items():
|
|
||||||
logger.debug(f"{k}\t{v}")
|
|
||||||
|
|
||||||
if hidden := request.POST.get('hidden', None):
|
if hidden := request.POST.get('hidden', None):
|
||||||
if hidden == 'delete-package':
|
if hidden == 'delete':
|
||||||
logger.debug(current_package.delete())
|
logger.debug(current_package.delete())
|
||||||
return redirect(s.SERVER_URL + '/package')
|
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)
|
||||||
|
elif hidden == 'copy':
|
||||||
|
object_to_copy = request.POST.get('object_to_copy')
|
||||||
|
|
||||||
|
if not object_to_copy:
|
||||||
|
return error(request, 'Invalid target package.', 'Please select a target package.')
|
||||||
|
|
||||||
|
copied_object = copy_object(current_user, current_package, object_to_copy)
|
||||||
|
return JsonResponse({'success': copied_object.url})
|
||||||
else:
|
else:
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
@ -855,17 +935,24 @@ def package_compound(request, package_uuid, compound_uuid):
|
|||||||
context['breadcrumbs'] = breadcrumbs(current_package, 'compound', current_compound)
|
context['breadcrumbs'] = breadcrumbs(current_package, 'compound', current_compound)
|
||||||
|
|
||||||
context['compound'] = current_compound
|
context['compound'] = current_compound
|
||||||
|
context['current_object'] = current_compound
|
||||||
|
|
||||||
return render(request, 'objects/compound.html', context)
|
return render(request, 'objects/compound.html', context)
|
||||||
|
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
if hidden := request.POST.get('hidden', None):
|
if hidden := request.POST.get('hidden', None):
|
||||||
if hidden == 'delete-compound':
|
if hidden == 'delete':
|
||||||
current_compound.delete()
|
current_compound.delete()
|
||||||
return redirect(current_package.url + '/compound')
|
return redirect(current_package.url + '/compound')
|
||||||
else:
|
else:
|
||||||
return HttpResponseBadRequest()
|
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_name = request.POST.get('compound-name')
|
||||||
new_compound_description = request.POST.get('compound-description')
|
new_compound_description = request.POST.get('compound-description')
|
||||||
|
|
||||||
@ -897,6 +984,7 @@ def package_compound_structures(request, package_uuid, compound_uuid):
|
|||||||
|
|
||||||
context['meta']['current_package'] = current_package
|
context['meta']['current_package'] = current_package
|
||||||
context['object_type'] = 'structure'
|
context['object_type'] = 'structure'
|
||||||
|
context['breadcrumbs'] = breadcrumbs(current_package, 'compound', current_compound, 'structure')
|
||||||
|
|
||||||
reviewed_compound_structure_qs = CompoundStructure.objects.none()
|
reviewed_compound_structure_qs = CompoundStructure.objects.none()
|
||||||
unreviewed_compound_structure_qs = CompoundStructure.objects.none()
|
unreviewed_compound_structure_qs = CompoundStructure.objects.none()
|
||||||
@ -936,12 +1024,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['title'] = f'enviPath - {current_package.name} - {current_compound.name} - {current_structure.name}'
|
||||||
|
|
||||||
context['meta']['current_package'] = current_package
|
context['meta']['current_package'] = current_package
|
||||||
context['object_type'] = 'compound'
|
context['object_type'] = 'structure'
|
||||||
|
|
||||||
context['compound_structure'] = current_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)
|
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:
|
else:
|
||||||
return HttpResponseNotAllowed(['GET', ])
|
return HttpResponseNotAllowed(['GET', ])
|
||||||
|
|
||||||
@ -1022,6 +1143,24 @@ def package_rule(request, package_uuid, rule_uuid):
|
|||||||
|
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
context = get_base_context(request)
|
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['title'] = f'enviPath - {current_package.name} - {current_rule.name}'
|
||||||
|
|
||||||
context['meta']['current_package'] = current_package
|
context['meta']['current_package'] = current_package
|
||||||
@ -1029,6 +1168,8 @@ def package_rule(request, package_uuid, rule_uuid):
|
|||||||
context['breadcrumbs'] = breadcrumbs(current_package, 'rule', current_rule)
|
context['breadcrumbs'] = breadcrumbs(current_package, 'rule', current_rule)
|
||||||
|
|
||||||
context['rule'] = current_rule
|
context['rule'] = current_rule
|
||||||
|
context['current_object'] = current_rule
|
||||||
|
|
||||||
if isinstance(current_rule, SimpleAmbitRule):
|
if isinstance(current_rule, SimpleAmbitRule):
|
||||||
return render(request, 'objects/simple_rule.html', context)
|
return render(request, 'objects/simple_rule.html', context)
|
||||||
else: # isinstance(current_rule, ParallelRule) or isinstance(current_rule, SequentialRule):
|
else: # isinstance(current_rule, ParallelRule) or isinstance(current_rule, SequentialRule):
|
||||||
@ -1036,12 +1177,18 @@ def package_rule(request, package_uuid, rule_uuid):
|
|||||||
|
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
if hidden := request.POST.get('hidden', None):
|
if hidden := request.POST.get('hidden', None):
|
||||||
if hidden == 'delete-rule':
|
if hidden == 'delete':
|
||||||
current_rule.delete()
|
current_rule.delete()
|
||||||
return redirect(current_package.url + '/rule')
|
return redirect(current_package.url + '/rule')
|
||||||
else:
|
else:
|
||||||
return HttpResponseBadRequest()
|
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_name = request.POST.get('rule-name', '').strip()
|
||||||
rule_description = request.POST.get('rule-description', '').strip()
|
rule_description = request.POST.get('rule-description', '').strip()
|
||||||
|
|
||||||
@ -1127,17 +1274,24 @@ def package_reaction(request, package_uuid, reaction_uuid):
|
|||||||
context['breadcrumbs'] = breadcrumbs(current_package, 'reaction', current_reaction)
|
context['breadcrumbs'] = breadcrumbs(current_package, 'reaction', current_reaction)
|
||||||
|
|
||||||
context['reaction'] = current_reaction
|
context['reaction'] = current_reaction
|
||||||
|
context['current_object'] = current_reaction
|
||||||
|
|
||||||
return render(request, 'objects/reaction.html', context)
|
return render(request, 'objects/reaction.html', context)
|
||||||
|
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
if hidden := request.POST.get('hidden', None):
|
if hidden := request.POST.get('hidden', None):
|
||||||
if hidden == 'delete-reaction':
|
if hidden == 'delete':
|
||||||
current_reaction.delete()
|
current_reaction.delete()
|
||||||
return redirect(current_package.url + '/reaction')
|
return redirect(current_package.url + '/reaction')
|
||||||
else:
|
else:
|
||||||
return HttpResponseBadRequest()
|
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_name = request.POST.get('reaction-name')
|
||||||
new_reaction_description = request.POST.get('reaction-description')
|
new_reaction_description = request.POST.get('reaction-description')
|
||||||
|
|
||||||
@ -1195,8 +1349,8 @@ def package_pathways(request, package_uuid):
|
|||||||
|
|
||||||
log_post_params(request)
|
log_post_params(request)
|
||||||
|
|
||||||
name = request.POST.get('name', 'Pathway ' + str(Pathway.objects.filter(package=current_package).count()))
|
name = request.POST.get('name')
|
||||||
description = request.POST.get('description', s.DEFAULT_VALUES['description'])
|
description = request.POST.get('description')
|
||||||
pw_mode = request.POST.get('predict', 'predict')
|
pw_mode = request.POST.get('predict', 'predict')
|
||||||
smiles = request.POST.get('smiles')
|
smiles = request.POST.get('smiles')
|
||||||
|
|
||||||
@ -1217,7 +1371,14 @@ def package_pathways(request, package_uuid):
|
|||||||
return error(request, "Pathway prediction failed!",
|
return error(request, "Pathway prediction failed!",
|
||||||
f'Pathway prediction failed as received mode "{pw_mode}" is none of {modes}')
|
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)
|
pw = Pathway.create(current_package, stand_smiles, name=name, description=description)
|
||||||
|
|
||||||
# set mode
|
# set mode
|
||||||
pw.kv.update({'mode': pw_mode})
|
pw.kv.update({'mode': pw_mode})
|
||||||
pw.save()
|
pw.save()
|
||||||
@ -1230,12 +1391,11 @@ def package_pathways(request, package_uuid):
|
|||||||
if pw_mode == 'incremental':
|
if pw_mode == 'incremental':
|
||||||
limit = 1
|
limit = 1
|
||||||
|
|
||||||
pred_setting = current_user.prediction_settings()
|
pw.setting = prediction_setting
|
||||||
pw.setting = pred_setting
|
|
||||||
pw.save()
|
pw.save()
|
||||||
|
|
||||||
from .tasks import predict
|
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)
|
return redirect(pw.url)
|
||||||
|
|
||||||
@ -1254,6 +1414,14 @@ def package_pathway(request, package_uuid, pathway_uuid):
|
|||||||
if request.GET.get("last_modified", False):
|
if request.GET.get("last_modified", False):
|
||||||
return JsonResponse({'modified': current_pathway.modified.strftime('%Y-%m-%d %H:%M:%S')})
|
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 = get_base_context(request)
|
||||||
context['title'] = f'enviPath - {current_package.name} - {current_pathway.name}'
|
context['title'] = f'enviPath - {current_package.name} - {current_pathway.name}'
|
||||||
|
|
||||||
@ -1262,6 +1430,7 @@ def package_pathway(request, package_uuid, pathway_uuid):
|
|||||||
context['breadcrumbs'] = breadcrumbs(current_package, 'pathway', current_pathway)
|
context['breadcrumbs'] = breadcrumbs(current_package, 'pathway', current_pathway)
|
||||||
|
|
||||||
context['pathway'] = current_pathway
|
context['pathway'] = current_pathway
|
||||||
|
context['current_object'] = current_pathway
|
||||||
|
|
||||||
context['breadcrumbs'] = [
|
context['breadcrumbs'] = [
|
||||||
{'Home': s.SERVER_URL},
|
{'Home': s.SERVER_URL},
|
||||||
@ -1276,12 +1445,18 @@ def package_pathway(request, package_uuid, pathway_uuid):
|
|||||||
|
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
if hidden := request.POST.get('hidden', None):
|
if hidden := request.POST.get('hidden', None):
|
||||||
if hidden == 'delete-pathway':
|
if hidden == 'delete':
|
||||||
current_pathway.delete()
|
current_pathway.delete()
|
||||||
return redirect(current_package.url + '/pathway')
|
return redirect(current_package.url + '/pathway')
|
||||||
else:
|
else:
|
||||||
return HttpResponseBadRequest()
|
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_name = request.POST.get('pathway-name')
|
||||||
pathway_description = request.POST.get('pathway-description')
|
pathway_description = request.POST.get('pathway-description')
|
||||||
|
|
||||||
@ -1402,19 +1577,33 @@ def package_pathway_node(request, package_uuid, pathway_uuid, node_uuid):
|
|||||||
]
|
]
|
||||||
|
|
||||||
context['node'] = current_node
|
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)
|
return render(request, 'objects/node.html', context)
|
||||||
|
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
if s.DEBUG:
|
|
||||||
for k, v in request.POST.items():
|
log_post_params(request)
|
||||||
print(k, v)
|
|
||||||
|
|
||||||
if hidden := request.POST.get('hidden', None):
|
if hidden := request.POST.get('hidden', None):
|
||||||
if hidden == 'delete-node':
|
if hidden == 'delete':
|
||||||
current_node.delete()
|
|
||||||
return redirect(current_pathway.url)
|
|
||||||
|
|
||||||
|
# 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:
|
else:
|
||||||
return HttpResponseNotAllowed(['GET', 'POST'])
|
return HttpResponseNotAllowed(['GET', 'POST'])
|
||||||
|
|
||||||
@ -1463,6 +1652,8 @@ def package_pathway_edges(request, package_uuid, pathway_uuid):
|
|||||||
|
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
|
|
||||||
|
log_post_params(request)
|
||||||
|
|
||||||
edge_name = request.POST.get('edge-name')
|
edge_name = request.POST.get('edge-name')
|
||||||
edge_description = request.POST.get('edge-description')
|
edge_description = request.POST.get('edge-description')
|
||||||
edge_substrates = request.POST.getlist('edge-substrates')
|
edge_substrates = request.POST.getlist('edge-substrates')
|
||||||
@ -1499,22 +1690,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}'
|
'title'] = f'enviPath - {current_package.name} - {current_pathway.name} - {current_edge.edge_label.name}'
|
||||||
|
|
||||||
context['meta']['current_package'] = current_package
|
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['breadcrumbs'] = breadcrumbs(current_package, 'pathway', current_pathway, 'edge', current_edge)
|
||||||
context['edge'] = current_edge
|
context['edge'] = current_edge
|
||||||
|
context['current_object'] = current_edge
|
||||||
|
|
||||||
return render(request, 'objects/edge.html', context)
|
return render(request, 'objects/edge.html', context)
|
||||||
|
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
if s.DEBUG:
|
|
||||||
for k, v in request.POST.items():
|
log_post_params(request)
|
||||||
print(k, v)
|
|
||||||
|
|
||||||
if hidden := request.POST.get('hidden', None):
|
if hidden := request.POST.get('hidden', None):
|
||||||
if hidden == 'delete-edge':
|
if hidden == 'delete':
|
||||||
current_edge.delete()
|
current_edge.delete()
|
||||||
return redirect(current_pathway.url)
|
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:
|
else:
|
||||||
return HttpResponseNotAllowed(['GET', 'POST'])
|
return HttpResponseNotAllowed(['GET', 'POST'])
|
||||||
|
|
||||||
@ -1525,12 +1724,18 @@ def package_scenarios(request, package_uuid):
|
|||||||
current_package = PackageManager.get_package_by_id(current_user, package_uuid)
|
current_package = PackageManager.get_package_by_id(current_user, package_uuid)
|
||||||
|
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
|
|
||||||
|
if 'application/json' in request.META.get('HTTP_ACCEPT') and not request.GET.get('all', False):
|
||||||
|
scens = Scenario.objects.filter(package=current_package).order_by('name')
|
||||||
|
res = [{'name': s.name, 'url': s.url, 'uuid': s.uuid} for s in scens]
|
||||||
|
return JsonResponse(res, safe=False)
|
||||||
|
|
||||||
context = get_base_context(request)
|
context = get_base_context(request)
|
||||||
context['title'] = f'enviPath - {current_package.name} - Scenarios'
|
context['title'] = f'enviPath - {current_package.name} - Scenarios'
|
||||||
|
|
||||||
context['meta']['current_package'] = current_package
|
context['meta']['current_package'] = current_package
|
||||||
context['object_type'] = 'scenario'
|
context['object_type'] = 'scenario'
|
||||||
context['breadcrumbs'] = breadcrumbs(current_package, 'pathway')
|
context['breadcrumbs'] = breadcrumbs(current_package, 'scenario')
|
||||||
|
|
||||||
reviewed_scenario_qs = Scenario.objects.none()
|
reviewed_scenario_qs = Scenario.objects.none()
|
||||||
unreviewed_scenario_qs = Scenario.objects.none()
|
unreviewed_scenario_qs = Scenario.objects.none()
|
||||||
@ -1551,8 +1756,57 @@ def package_scenarios(request, package_uuid):
|
|||||||
context['reviewed_objects'] = reviewed_scenario_qs
|
context['reviewed_objects'] = reviewed_scenario_qs
|
||||||
context['unreviewed_objects'] = unreviewed_scenario_qs
|
context['unreviewed_objects'] = unreviewed_scenario_qs
|
||||||
|
|
||||||
return render(request, 'collections/objects_list.html', context)
|
from envipy_additional_information import SLUDGE_ADDITIONAL_INFORMATION, SOIL_ADDITIONAL_INFORMATION, \
|
||||||
|
SEDIMENT_ADDITIONAL_INFORMATION
|
||||||
|
context['scenario_types'] = {
|
||||||
|
'Soil Data': {
|
||||||
|
'name': 'soil',
|
||||||
|
'widgets': [HTMLGenerator.generate_html(ai, prefix=f'soil_{0}') for ai in
|
||||||
|
[x for s in SOIL_ADDITIONAL_INFORMATION.values() for x in s]]
|
||||||
|
},
|
||||||
|
'Sludge Data': {
|
||||||
|
'name': 'sludge',
|
||||||
|
'widgets': [HTMLGenerator.generate_html(ai, prefix=f'sludge_{0}') for ai in
|
||||||
|
[x for s in SLUDGE_ADDITIONAL_INFORMATION.values() for x in s]]
|
||||||
|
},
|
||||||
|
'Water-Sediment System Data': {
|
||||||
|
'name': 'sediment',
|
||||||
|
'widgets': [HTMLGenerator.generate_html(ai, prefix=f'sediment_{0}') for ai in
|
||||||
|
[x for s in SEDIMENT_ADDITIONAL_INFORMATION.values() for x in s]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context['sludge_additional_information'] = SLUDGE_ADDITIONAL_INFORMATION
|
||||||
|
context['soil_additional_information'] = SOIL_ADDITIONAL_INFORMATION
|
||||||
|
context['sediment_additional_information'] = SEDIMENT_ADDITIONAL_INFORMATION
|
||||||
|
|
||||||
|
return render(request, 'collections/objects_list.html', context)
|
||||||
|
elif request.method == 'POST':
|
||||||
|
|
||||||
|
log_post_params(request)
|
||||||
|
|
||||||
|
scenario_name = request.POST.get('scenario-name')
|
||||||
|
scenario_description = request.POST.get('scenario-description')
|
||||||
|
scenario_date_year = request.POST.get('scenario-date-year')
|
||||||
|
scenario_date_month = request.POST.get('scenario-date-month')
|
||||||
|
scenario_date_day = request.POST.get('scenario-date-day')
|
||||||
|
|
||||||
|
scenario_date = scenario_date_year
|
||||||
|
if scenario_date_month is not None and scenario_date_month.strip() != '':
|
||||||
|
scenario_date += f'-{int(scenario_date_month):02d}'
|
||||||
|
if scenario_date_day is not None and scenario_date_day.strip() != '':
|
||||||
|
scenario_date += f'-{int(scenario_date_day):02d}'
|
||||||
|
|
||||||
|
scenario_type = request.POST.get('scenario-type')
|
||||||
|
|
||||||
|
additional_information = HTMLGenerator.build_models(request.POST.dict())
|
||||||
|
additional_information = [x for s in additional_information.values() for x in s]
|
||||||
|
|
||||||
|
s = Scenario.create(current_package, name=scenario_name, description=scenario_description,
|
||||||
|
scenario_date=scenario_date, scenario_type=scenario_type,
|
||||||
|
additional_information=additional_information)
|
||||||
|
|
||||||
|
return redirect(s.url)
|
||||||
else:
|
else:
|
||||||
return HttpResponseNotAllowed(['GET', ])
|
return HttpResponseNotAllowed(['GET', ])
|
||||||
|
|
||||||
@ -1573,10 +1827,63 @@ def package_scenario(request, package_uuid, scenario_uuid):
|
|||||||
|
|
||||||
context['scenario'] = current_scenario
|
context['scenario'] = current_scenario
|
||||||
|
|
||||||
|
available_add_infs = []
|
||||||
|
for add_inf in NAME_MAPPING.values():
|
||||||
|
available_add_infs.append({
|
||||||
|
'display_name': add_inf.property_name(None),
|
||||||
|
'name': add_inf.__name__,
|
||||||
|
'widget': HTMLGenerator.generate_html(add_inf, prefix=f'{0}')
|
||||||
|
})
|
||||||
|
context['available_additional_information'] = available_add_infs
|
||||||
|
|
||||||
|
context['update_widgets'] = [HTMLGenerator.generate_html(ai, prefix=f'{i}') for i, ai in enumerate(current_scenario.get_additional_information())]
|
||||||
|
|
||||||
return render(request, 'objects/scenario.html', context)
|
return render(request, 'objects/scenario.html', context)
|
||||||
|
|
||||||
|
elif request.method == 'POST':
|
||||||
|
|
||||||
|
log_post_params(request)
|
||||||
|
|
||||||
|
if hidden := request.POST.get('hidden', None):
|
||||||
|
|
||||||
|
if hidden == 'delete':
|
||||||
|
current_scenario.delete()
|
||||||
|
return redirect(current_package.url + '/scenario')
|
||||||
|
elif hidden == 'delete-additional-information':
|
||||||
|
uuid = request.POST.get('uuid')
|
||||||
|
current_scenario.remove_additional_information(uuid)
|
||||||
|
return redirect(current_scenario.url)
|
||||||
|
elif hidden == 'delete-all-additional-information':
|
||||||
|
current_scenario.additional_information = dict()
|
||||||
|
current_scenario.save()
|
||||||
|
return redirect(current_scenario.url)
|
||||||
|
elif hidden == 'set-additional-information':
|
||||||
|
ais = HTMLGenerator.build_models(request.POST.dict())
|
||||||
|
|
||||||
|
if s.DEBUG:
|
||||||
|
logger.info(ais)
|
||||||
|
|
||||||
|
current_scenario.set_additional_information(ais)
|
||||||
|
return redirect(current_scenario.url)
|
||||||
|
elif hidden == 'add-additional-information':
|
||||||
|
ais = HTMLGenerator.build_models(request.POST.dict())
|
||||||
|
|
||||||
|
if len(ais.keys()) != 1:
|
||||||
|
raise ValueError('Only one additional information field can be added at a time.')
|
||||||
|
|
||||||
|
ai = list(ais.values())[0][0]
|
||||||
|
|
||||||
|
if s.DEBUG:
|
||||||
|
logger.info(ais)
|
||||||
|
|
||||||
|
current_scenario.add_additional_information(ai)
|
||||||
|
return redirect(current_scenario.url)
|
||||||
|
else:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
else:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
else:
|
else:
|
||||||
return HttpResponseNotAllowed(['GET', ])
|
return HttpResponseNotAllowed(['GET', 'POST'])
|
||||||
|
|
||||||
|
|
||||||
##############
|
##############
|
||||||
@ -1587,7 +1894,6 @@ def users(request):
|
|||||||
context = get_base_context(request)
|
context = get_base_context(request)
|
||||||
context['title'] = f'enviPath - Users'
|
context['title'] = f'enviPath - Users'
|
||||||
|
|
||||||
context['meta']['current_package'] = context['meta']['user'].default_package
|
|
||||||
context['object_type'] = 'user'
|
context['object_type'] = 'user'
|
||||||
context['breadcrumbs'] = [
|
context['breadcrumbs'] = [
|
||||||
{'Home': s.SERVER_URL},
|
{'Home': s.SERVER_URL},
|
||||||
@ -1611,27 +1917,27 @@ def user(request, user_uuid):
|
|||||||
if str(current_user.uuid) != user_uuid and not current_user.is_superuser:
|
if str(current_user.uuid) != user_uuid and not current_user.is_superuser:
|
||||||
return HttpResponseBadRequest()
|
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['title'] = f'enviPath - User'
|
||||||
|
|
||||||
context['object_type'] = 'user'
|
context['object_type'] = 'user'
|
||||||
context['breadcrumbs'] = [
|
context['breadcrumbs'] = [
|
||||||
{'Home': s.SERVER_URL},
|
{'Home': s.SERVER_URL},
|
||||||
{'User': s.SERVER_URL + '/user'},
|
{'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()
|
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
|
model_qs |= p.models
|
||||||
|
|
||||||
context['models'] = model_qs
|
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)
|
return render(request, 'objects/user.html', context)
|
||||||
|
|
||||||
@ -1700,8 +2006,6 @@ def user(request, user_uuid):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
print(setting)
|
|
||||||
|
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -1715,7 +2019,6 @@ def groups(request):
|
|||||||
context = get_base_context(request)
|
context = get_base_context(request)
|
||||||
context['title'] = f'enviPath - Groups'
|
context['title'] = f'enviPath - Groups'
|
||||||
|
|
||||||
context['meta']['current_package'] = context['meta']['user'].default_package
|
|
||||||
context['object_type'] = 'group'
|
context['object_type'] = 'group'
|
||||||
context['breadcrumbs'] = [
|
context['breadcrumbs'] = [
|
||||||
{'Home': s.SERVER_URL},
|
{'Home': s.SERVER_URL},
|
||||||
@ -1764,12 +2067,10 @@ def group(request, group_uuid):
|
|||||||
|
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
|
|
||||||
if s.DEBUG:
|
log_post_params(request)
|
||||||
for k, v in request.POST.items():
|
|
||||||
print(k, v)
|
|
||||||
|
|
||||||
if hidden := request.POST.get('hidden', None):
|
if hidden := request.POST.get('hidden', None):
|
||||||
if hidden == 'delete-group':
|
if hidden == 'delete':
|
||||||
current_group.delete()
|
current_group.delete()
|
||||||
return redirect(s.SERVER_URL + '/group')
|
return redirect(s.SERVER_URL + '/group')
|
||||||
else:
|
else:
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -38,10 +38,8 @@ def migration(request):
|
|||||||
res = True
|
res = True
|
||||||
|
|
||||||
for comp, ambit_prod in zip(bt_rule['compounds'], bt_rule['products']):
|
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 = []
|
all_rdkit_prods = []
|
||||||
for ps in products:
|
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':
|
# if comp['smiles'] != 'CC1=C(C(=C(C=N1)CO)C=O)O':
|
||||||
# continue
|
# continue
|
||||||
|
|
||||||
products = FormatConverter.apply(comp['smiles'], smirks, preprocess_smiles=True, bracketize=False)
|
products = FormatConverter.apply(comp['smiles'], smirks)
|
||||||
|
|
||||||
all_rdkit_prods = []
|
all_rdkit_prods = []
|
||||||
for ps in products:
|
for ps in products:
|
||||||
|
|||||||
@ -29,4 +29,4 @@ dependencies = [
|
|||||||
[tool.uv.sources]
|
[tool.uv.sources]
|
||||||
enviformer = { git = "ssh://git@git.envipath.com/enviPath/enviformer.git", rev = "v0.1.0" }
|
enviformer = { git = "ssh://git@git.envipath.com/enviPath/enviformer.git", rev = "v0.1.0" }
|
||||||
envipy-plugins = { git = "ssh://git@git.envipath.com/enviPath/enviPy-plugins.git", rev = "v0.1.0" }
|
envipy-plugins = { git = "ssh://git@git.envipath.com/enviPath/enviPy-plugins.git", rev = "v0.1.0" }
|
||||||
envipy-additional-information = { git = "ssh://git@git.envipath.com/enviPath/enviPy-additional-information.git" }
|
envipy-additional-information = { git = "ssh://git@git.envipath.com/enviPath/enviPy-additional-information.git", rev = "v0.1.4"}
|
||||||
|
|||||||
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);
|
||||||
|
|
||||||
|
}
|
||||||
226
static/js/pw.js
226
static/js/pw.js
@ -1,5 +1,4 @@
|
|||||||
console.log("loaded")
|
console.log("loaded pw.js")
|
||||||
|
|
||||||
|
|
||||||
function predictFromNode(url) {
|
function predictFromNode(url) {
|
||||||
$.post("", {node: url})
|
$.post("", {node: url})
|
||||||
@ -28,62 +27,165 @@ function draw(pathway, elem) {
|
|||||||
const horizontalSpacing = 75; // horizontal space between nodes
|
const horizontalSpacing = 75; // horizontal space between nodes
|
||||||
const depthMap = new Map();
|
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)) {
|
if (!depthMap.has(node.depth)) {
|
||||||
depthMap.set(node.depth, 0);
|
depthMap.set(node.depth, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodesInLevel = nodes.filter(n => n.depth === node.depth).length;
|
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);
|
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() {
|
function ticked() {
|
||||||
|
// Update pseudo node positions first
|
||||||
|
updatePseudoNodePositions();
|
||||||
|
|
||||||
link.attr("x1", d => d.source.x)
|
link.attr("x1", d => d.source.x)
|
||||||
.attr("y1", d => d.source.y)
|
.attr("y1", d => d.source.y)
|
||||||
.attr("x2", d => d.target.x)
|
.attr("x2", d => d.target.x)
|
||||||
.attr("y2", d => d.target.y);
|
.attr("y2", d => d.target.y);
|
||||||
|
|
||||||
node.attr("transform", d => `translate(${d.x},${d.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) {
|
function dragstarted(event, d) {
|
||||||
if (!event.active) simulation.alphaTarget(0.3).restart();
|
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;
|
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) {
|
function dragged(event, d) {
|
||||||
d.fx = event.x; // Position direkt an Maus anpassen
|
d.fx = event.x;
|
||||||
d.fy = event.y;
|
d.fy = event.y;
|
||||||
|
|
||||||
|
// Update connected pseudo nodes in real-time
|
||||||
|
if (!d.pseudo) {
|
||||||
|
updateConnectedPseudoNodes(d);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function dragended(event, d) {
|
function dragended(event, d) {
|
||||||
if (!event.active) simulation.alphaTarget(0);
|
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
|
// t -> ref to "this" from d3
|
||||||
function nodeClick(event, node, t) {
|
function nodeClick(event, node, t) {
|
||||||
console.log(node);
|
console.log(node);
|
||||||
@ -105,7 +207,7 @@ function draw(pathway, elem) {
|
|||||||
pop = $(e).attr("aria-describedby")
|
pop = $(e).attr("aria-describedby")
|
||||||
h = $('#' + pop).height();
|
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;`)
|
$('#' + 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 () {
|
var close = setInterval(function () {
|
||||||
if (!$(".popover:hover").length // mouse outside popover
|
if (!$(".popover:hover").length // mouse outside popover
|
||||||
&& !$(e).is(':hover')) { // mouse outside element
|
&& !$(e).is(':hover')) { // mouse outside element
|
||||||
@ -140,11 +242,20 @@ function draw(pathway, elem) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function node_popup(n) {
|
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 += "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) {
|
if (n.scenarios.length > 0) {
|
||||||
popupContent += '<b>Half-lives and related scenarios:</b><br>'
|
popupContent += '<b>Half-lives and related scenarios:</b><br>'
|
||||||
for (var s of n.scenarios) {
|
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;
|
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>';
|
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) {
|
function edge_popup(e) {
|
||||||
popupContent = "<a href='" + e.url +"'>" + e.name + "</a><br>";
|
popupContent = "<a href='" + e.url + "'>" + e.name + "</a><br>";
|
||||||
popupContent += "<img src='" + e.image + "' width='"+ 20 * nodeRadius +"'><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) {
|
if (e.reaction_probability) {
|
||||||
popupContent += '<b>Probability:</b><br>' + e.reaction_probability.toFixed(3) + '<br>';
|
popupContent += '<b>Probability:</b><br>' + e.reaction_probability.toFixed(3) + '<br>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (e.scenarios.length > 0) {
|
if (e.scenarios.length > 0) {
|
||||||
popupContent += '<b>Half-lives and related scenarios:</b><br>'
|
popupContent += '<b>Half-lives and related scenarios:</b><br>'
|
||||||
for (var s of e.scenarios) {
|
for (var s of e.scenarios) {
|
||||||
@ -180,9 +302,9 @@ function draw(pathway, elem) {
|
|||||||
|
|
||||||
var clientX;
|
var clientX;
|
||||||
var clientY;
|
var clientY;
|
||||||
document.addEventListener('mousemove', function(event) {
|
document.addEventListener('mousemove', function (event) {
|
||||||
clientX = event.clientX;
|
clientX = event.clientX;
|
||||||
clientY =event.clientY;
|
clientY = event.clientY;
|
||||||
});
|
});
|
||||||
|
|
||||||
const zoomable = d3.select("#zoomable");
|
const zoomable = d3.select("#zoomable");
|
||||||
@ -233,13 +355,12 @@ function draw(pathway, elem) {
|
|||||||
.enter().append("line")
|
.enter().append("line")
|
||||||
// Check if target is pseudo and draw marker only if not pseudo
|
// Check if target is pseudo and draw marker only if not pseudo
|
||||||
.attr("class", d => d.target.pseudo ? "link_no_arrow" : "link")
|
.attr("class", d => d.target.pseudo ? "link_no_arrow" : "link")
|
||||||
// .on("mouseover", (event, d) => {
|
.attr("marker-end", d => d.target.pseudo ? '' : 'url(#arrow)')
|
||||||
// tooltip.style("visibility", "visible")
|
|
||||||
// .text(`Link: ${d.source.id} → ${d.target.id}`)
|
// add element to links array
|
||||||
// .style("top", `${event.pageY + 5}px`)
|
link.each(function (d) {
|
||||||
// .style("left", `${event.pageX + 5}px`);
|
d.el = this; // attach the DOM element to the data object
|
||||||
// })
|
});
|
||||||
// .on("mouseout", () => tooltip.style("visibility", "hidden"));
|
|
||||||
|
|
||||||
pop_add(link, "Reaction", edge_popup);
|
pop_add(link, "Reaction", edge_popup);
|
||||||
|
|
||||||
@ -255,20 +376,10 @@ function draw(pathway, elem) {
|
|||||||
.on("click", function (event, d) {
|
.on("click", function (event, d) {
|
||||||
d3.select(this).select("circle").classed("highlighted", !d3.select(this).select("circle").classed("highlighted"));
|
d3.select(this).select("circle").classed("highlighted", !d3.select(this).select("circle").classed("highlighted"));
|
||||||
})
|
})
|
||||||
// .on("mouseover", (event, d) => {
|
|
||||||
// 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
|
// Kreise für die Knoten hinzufügen
|
||||||
node.append("circle")
|
node.append("circle")
|
||||||
// make radius "invisible"
|
// make radius "invisible" for pseudo nodes
|
||||||
.attr("r", d => d.pseudo ? 0.01 : nodeRadius)
|
.attr("r", d => d.pseudo ? 0.01 : nodeRadius)
|
||||||
.style("fill", "#e8e8e8");
|
.style("fill", "#e8e8e8");
|
||||||
|
|
||||||
@ -280,5 +391,10 @@ function draw(pathway, elem) {
|
|||||||
.attr("width", nodeRadius * 2)
|
.attr("width", nodeRadius * 2)
|
||||||
.attr("height", 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>
|
<li>
|
||||||
<a role="button" data-toggle="modal" data-target="#new_model_modal">
|
<a role="button" data-toggle="modal" data-target="#new_model_modal">
|
||||||
<span class="glyphicon glyphicon-plus"></span> New Model</a>
|
<span class="glyphicon glyphicon-plus"></span> New Model</a>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{% if meta.can_edit %}
|
{% if meta.can_edit %}
|
||||||
<li>
|
<li>
|
||||||
<a role="button" data-toggle="modal" data-target="#new_pathway_modal">
|
<a role="button" data-toggle="modal" data-target="#new_scenario_modal">
|
||||||
<span class="glyphicon glyphicon-plus"></span> New Scenario</a>
|
<span class="glyphicon glyphicon-plus"></span> New Scenario</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -8,7 +8,17 @@
|
|||||||
<i class="glyphicon glyphicon-plus"></i> Add Structure</a>
|
<i class="glyphicon glyphicon-plus"></i> Add Structure</a>
|
||||||
</li>
|
</li>
|
||||||
<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>
|
||||||
|
{% endif %}
|
||||||
|
<li>
|
||||||
|
<a role="button" data-toggle="modal" data-target="#generic_copy_object_modal">
|
||||||
|
<i class="glyphicon glyphicon-duplicate"></i> Copy</a>
|
||||||
|
</li>
|
||||||
|
{% if meta.can_edit %}
|
||||||
|
<li>
|
||||||
|
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
|
||||||
<i class="glyphicon glyphicon-trash"></i> Delete Compound</a>
|
<i class="glyphicon glyphicon-trash"></i> Delete Compound</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -4,7 +4,11 @@
|
|||||||
<i class="glyphicon glyphicon-edit"></i> Edit Compound Structure</a>
|
<i class="glyphicon glyphicon-edit"></i> Edit Compound Structure</a>
|
||||||
</li>
|
</li>
|
||||||
<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>
|
<i class="glyphicon glyphicon-trash"></i> Delete Compound Structure</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% 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 %}
|
{% if meta.can_edit %}
|
||||||
<li>
|
<li>
|
||||||
<a role="button" data-toggle="modal" data-target="#delete_group_modal">
|
<a role="button" data-toggle="modal" data-target="#edit_group_member_modal">
|
||||||
<i class="glyphicon glyphicon-trash"></i> Delete Group</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a role="button" data-toggle="modal" data-target="#edit_group_member_modal">
|
|
||||||
<i class="glyphicon glyphicon-trash"></i> Add/Remove Member</a>
|
<i class="glyphicon glyphicon-trash"></i> Add/Remove Member</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a role="button" data-toggle="modal" data-target="#generic_delete_modal">
|
||||||
|
<i class="glyphicon glyphicon-trash"></i> Delete Group</a>
|
||||||
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{% if meta.can_edit %}
|
{% if meta.can_edit %}
|
||||||
<li>
|
<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>
|
<i class="glyphicon glyphicon-trash"></i> Delete Model</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -4,7 +4,11 @@
|
|||||||
<i class="glyphicon glyphicon-edit"></i> Edit Node</a>
|
<i class="glyphicon glyphicon-edit"></i> Edit Node</a>
|
||||||
</li>
|
</li>
|
||||||
<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>
|
<i class="glyphicon glyphicon-trash"></i> Delete Node</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@ -7,12 +7,16 @@
|
|||||||
<a role="button" data-toggle="modal" data-target="#edit_package_permissions_modal">
|
<a role="button" data-toggle="modal" data-target="#edit_package_permissions_modal">
|
||||||
<i class="glyphicon glyphicon-user"></i> Edit Permissions</a>
|
<i class="glyphicon glyphicon-user"></i> Edit Permissions</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a role="button" data-toggle="modal" data-target="#publish_package_modal">
|
||||||
|
<i class="glyphicon glyphicon-bullhorn"></i> Publish Package</a>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a role="button" data-toggle="modal" data-target="#set_license_modal">
|
<a role="button" data-toggle="modal" data-target="#set_license_modal">
|
||||||
<i class="glyphicon glyphicon-duplicate"></i> License</a>
|
<i class="glyphicon glyphicon-duplicate"></i> License</a>
|
||||||
</li>
|
</li>
|
||||||
<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>
|
<i class="glyphicon glyphicon-trash"></i> Delete Package</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -8,25 +8,40 @@
|
|||||||
<i class="glyphicon glyphicon-plus"></i> Add Reaction</a>
|
<i class="glyphicon glyphicon-plus"></i> Add Reaction</a>
|
||||||
</li>
|
</li>
|
||||||
<li role="separator" class="divider"></li>
|
<li role="separator" class="divider"></li>
|
||||||
|
{% endif %}
|
||||||
|
<li>
|
||||||
|
<a role="button" data-toggle="modal" data-target="#generic_copy_object_modal">
|
||||||
|
<i class="glyphicon glyphicon-duplicate"></i> Copy</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="button" data-toggle="modal" data-target="#download_pathway_modal">
|
||||||
|
<i class="glyphicon glyphicon-floppy-save"></i> Download Pathway</a>
|
||||||
|
</li>
|
||||||
|
{% if meta.can_edit %}
|
||||||
|
<li role="separator" class="divider"></li>
|
||||||
<li>
|
<li>
|
||||||
<a class="button" data-toggle="modal" data-target="#edit_pathway_modal">
|
<a class="button" data-toggle="modal" data-target="#edit_pathway_modal">
|
||||||
<i class="glyphicon glyphicon-plus"></i> Edit Pathway</a>
|
<i class="glyphicon glyphicon-edit"></i> Edit Pathway</a>
|
||||||
</li>
|
</li>
|
||||||
{# <li>#}
|
<li>
|
||||||
{# <a class="button" data-toggle="modal" data-target="#add_pathway_edge_modal">#}
|
<a role="button" data-toggle="modal" data-target="#set_scenario_modal">
|
||||||
{# <i class="glyphicon glyphicon-plus"></i> Calculate Compound Properties</a>#}
|
<i class="glyphicon glyphicon-plus"></i> Set Scenarios</a>
|
||||||
{# </li>#}
|
</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 role="separator" class="divider"></li>
|
||||||
<li>
|
<li>
|
||||||
<a class="button" data-toggle="modal" data-target="#delete_pathway_node_modal">
|
<a class="button" data-toggle="modal" data-target="#delete_pathway_node_modal">
|
||||||
<i class="glyphicon glyphicon-trash"></i> Delete Compound</a>
|
<i class="glyphicon glyphicon-trash"></i> Delete Compound</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a class="button" data-toggle="modal" data-target="#delete_pathway_edge_modal">
|
<a class="button" data-toggle="modal" data-target="#delete_pathway_edge_modal">
|
||||||
<i class="glyphicon glyphicon-trash"></i> Delete Reaction</a>
|
<i class="glyphicon glyphicon-trash"></i> Delete Reaction</a>
|
||||||
</li>
|
</li>
|
||||||
<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>
|
<i class="glyphicon glyphicon-trash"></i> Delete Pathway</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -4,7 +4,17 @@
|
|||||||
<i class="glyphicon glyphicon-edit"></i> Edit Reaction</a>
|
<i class="glyphicon glyphicon-edit"></i> Edit Reaction</a>
|
||||||
</li>
|
</li>
|
||||||
<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>
|
||||||
|
{% endif %}
|
||||||
|
<li>
|
||||||
|
<a role="button" data-toggle="modal" data-target="#generic_copy_object_modal">
|
||||||
|
<i class="glyphicon glyphicon-duplicate"></i> Copy</a>
|
||||||
|
</li>
|
||||||
|
{% if meta.can_edit %}
|
||||||
|
<li>
|
||||||
|
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
|
||||||
<i class="glyphicon glyphicon-trash"></i> Delete Reaction</a>
|
<i class="glyphicon glyphicon-trash"></i> Delete Reaction</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -3,4 +3,18 @@
|
|||||||
<a role="button" data-toggle="modal" data-target="#edit_rule_modal">
|
<a role="button" data-toggle="modal" data-target="#edit_rule_modal">
|
||||||
<i class="glyphicon glyphicon-edit"></i> Edit Rule</a>
|
<i class="glyphicon glyphicon-edit"></i> Edit Rule</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a role="button" data-toggle="modal" data-target="#set_scenario_modal">
|
||||||
|
<i class="glyphicon glyphicon-plus"></i> Set Scenarios</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
<li>
|
||||||
|
<a role="button" data-toggle="modal" data-target="#generic_copy_object_modal">
|
||||||
|
<i class="glyphicon glyphicon-duplicate"></i> Copy</a>
|
||||||
|
</li>
|
||||||
|
{% if meta.can_edit %}
|
||||||
|
<li>
|
||||||
|
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
|
||||||
|
<i class="glyphicon glyphicon-trash"></i> Delete Rule</a>
|
||||||
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -1,2 +1,14 @@
|
|||||||
{% if meta.can_edit %}
|
{% if meta.can_edit %}
|
||||||
|
<li>
|
||||||
|
<a class="button" data-toggle="modal" data-target="#add_additional_information_modal">
|
||||||
|
<i class="glyphicon glyphicon-trash"></i> Add Additional Information</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="button" data-toggle="modal" data-target="#update_scenario_additional_information_modal">
|
||||||
|
<i class="glyphicon glyphicon-trash"></i> Set Additional Information</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
|
||||||
|
<i class="glyphicon glyphicon-trash"></i> Delete Scenario</a>
|
||||||
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -16,7 +16,7 @@
|
|||||||
{# <i class="glyphicon glyphicon-console"></i> Manage API Token</a>#}
|
{# <i class="glyphicon glyphicon-console"></i> Manage API Token</a>#}
|
||||||
{# </li>#}
|
{# </li>#}
|
||||||
<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>
|
<i class="glyphicon glyphicon-trash"></i> Delete Account</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -2,27 +2,21 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% if reviewed_objects.count > 50 or unreviewed_objects.count > 50 %}
|
{% if object_type != 'package' %}
|
||||||
{% if object_type != 'package' %}
|
<div>
|
||||||
<div id="load-remaining-button-div">
|
<div id="load-all-error" style="display: none;">
|
||||||
<button class="btn btn-secondary btn-lg btn-block" type="button" id="load-remaining">Load all {% if reviewed_objects.count > 0 %} {{ reviewed_objects.count }} {% else %} {{ unreviewed_objects.count }} {% endif %} {{ object_type }}s
|
<div class="alert alert-danger" role="alert">
|
||||||
</button>
|
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
|
||||||
<p></p>
|
<span class="sr-only">Error:</span>
|
||||||
<div id="load-all-loading"></div>
|
Getting objects failed!
|
||||||
<p></p>
|
</div>
|
||||||
<div id="load-all-error" style="display: none;">
|
</div>
|
||||||
<div class="alert alert-danger" role="alert">
|
|
||||||
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
|
|
||||||
<span class="sr-only">Error:</span>
|
|
||||||
Getting objects failed!
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input type="text" id="object-search" class="form-control" placeholder="Search by name" style="display: none;">
|
<input type="text" id="object-search" class="form-control" placeholder="Search by name"
|
||||||
<p></p>
|
style="display: none;">
|
||||||
</div>
|
<p></p>
|
||||||
{% endif %}
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% block action_modals %}
|
{% block action_modals %}
|
||||||
{% if object_type == 'package' %}
|
{% if object_type == 'package' %}
|
||||||
@ -248,21 +242,34 @@
|
|||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
<style>
|
||||||
|
.spinner-widget {
|
||||||
|
position: fixed; /* stays in place on scroll */
|
||||||
|
bottom: 20px; /* distance from bottom */
|
||||||
|
right: 20px; /* distance from right */
|
||||||
|
z-index: 9999; /* above most elements */
|
||||||
|
width: 60px; /* adjust to gif size */
|
||||||
|
height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner-widget img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% if object_type != 'package' %}
|
||||||
|
<div id="load-all-loading" class="spinner-widget">
|
||||||
|
<img id="loading-gif" src="{% static '/images/wait.gif' %}" alt="Loading...">
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
$(function () {
|
$(function () {
|
||||||
|
|
||||||
$('#modal-form-delete-submit').on('click', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
$('#modal-form-delete').submit();
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#object-search').show();
|
$('#object-search').show();
|
||||||
|
|
||||||
if ($('#load-remaining').length) {
|
{% if object_type != 'package' %}
|
||||||
$('#load-remaining').on('click', function () {
|
setTimeout(function () {
|
||||||
|
|
||||||
makeLoadingGif("#load-all-loading", "{% static '/images/wait.gif' %}");
|
|
||||||
$('#load-all-error').hide();
|
$('#load-all-error').hide();
|
||||||
|
|
||||||
$.getJSON('?all=true', function (resp) {
|
$.getJSON('?all=true', function (resp) {
|
||||||
@ -272,20 +279,26 @@
|
|||||||
for (o in resp.objects) {
|
for (o in resp.objects) {
|
||||||
obj = resp.objects[o];
|
obj = resp.objects[o];
|
||||||
if (obj.reviewed) {
|
if (obj.reviewed) {
|
||||||
$('#ReviewedContent').append('<a class="list-group-item" href="' + obj.url + '">' + obj.name + '</a>');
|
$('#ReviewedContent').append('<a class="list-group-item" href="' + obj.url + '">' + obj.name + ' <span class="glyphicon glyphicon-star" aria-hidden="true" style="float:right" data-toggle="tooltip" data-placement="top" title="" data-original-title="Reviewed"></span></a>');
|
||||||
} else {
|
} else {
|
||||||
$('#UnreviewedContent').append('<a class="list-group-item" href="' + obj.url + '">' + obj.name + '</a>');
|
$('#UnreviewedContent').append('<a class="list-group-item" href="' + obj.url + '">' + obj.name + '</a>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#load-all-loading').empty();
|
$('#load-all-loading').hide();
|
||||||
$('#load-remaining').hide();
|
$('#load-remaining').hide();
|
||||||
}).fail(function (resp) {
|
}).fail(function (resp) {
|
||||||
$('#load-all-loading').empty();
|
$('#load-all-loading').hide();
|
||||||
$('#load-all-error').show();
|
$('#load-all-error').show();
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}, 2500);
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
$('#modal-form-delete-submit').on('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$('#modal-form-delete').submit();
|
||||||
|
});
|
||||||
|
|
||||||
$('#object-search').on('keyup', function () {
|
$('#object-search').on('keyup', function () {
|
||||||
let query = $(this).val().toLowerCase();
|
let query = $(this).val().toLowerCase();
|
||||||
|
|||||||
@ -3,7 +3,12 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
<head>
|
<head>
|
||||||
<title>{{ title }}</title>
|
<title>{{ title }}</title>
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
height: 100%; /* ensure body fills viewport */
|
||||||
|
overflow-x: hidden; /* prevent horizontal scroll */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
{# TODO use bundles from bootstrap 3.3.7 #}
|
{# TODO use bundles from bootstrap 3.3.7 #}
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
|
||||||
@ -51,7 +56,7 @@
|
|||||||
(function () {
|
(function () {
|
||||||
var u = "//matomo.envipath.com/";
|
var u = "//matomo.envipath.com/";
|
||||||
_paq.push(['setTrackerUrl', u + 'matomo.php']);
|
_paq.push(['setTrackerUrl', u + 'matomo.php']);
|
||||||
_paq.push(['setSiteId', '7']);
|
_paq.push(['setSiteId', '10']);
|
||||||
var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
|
var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
|
||||||
g.async = true;
|
g.async = true;
|
||||||
g.src = u + 'matomo.js';
|
g.src = u + 'matomo.js';
|
||||||
@ -83,21 +88,26 @@
|
|||||||
<!-- Collect the nav links, forms, and other content for toggling -->
|
<!-- Collect the nav links, forms, and other content for toggling -->
|
||||||
<div class="collapse navbar-collapse collapse-framework navbar-collapse-framework" id="navbarCollapse">
|
<div class="collapse navbar-collapse collapse-framework navbar-collapse-framework" id="navbarCollapse">
|
||||||
<ul class="nav navbar-nav navbar-nav-framework">
|
<ul class="nav navbar-nav navbar-nav-framework">
|
||||||
<li class="dropdown">
|
<li>
|
||||||
<a data-toggle="dropdown" class="dropdown-toggle" href="#">Predict Pathway<b class="caret"></b></a>
|
<a class="button" data-toggle="modal" data-target="#predict_modal">
|
||||||
<ul role="menu" class="dropdown-menu">
|
Predict Pathway
|
||||||
<li>
|
</a>
|
||||||
<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>
|
||||||
|
{# <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 }}/package" id="packageLink">Package</a></li>
|
||||||
<li><a href="{{ meta.server_url }}/search" id="searchLink">Search</a></li>
|
<li><a href="{{ meta.server_url }}/search" id="searchLink">Search</a></li>
|
||||||
<li><a href="{{ meta.server_url }}/model" id="modelLink">Modelling</a></li>
|
<li><a href="{{ meta.server_url }}/model" id="modelLink">Modelling</a></li>
|
||||||
@ -119,7 +129,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ul class="nav navbar-nav navbar-right navbar-nav-framework navbar-right-framework">
|
<ul class="nav navbar-nav navbar-right navbar-nav-framework navbar-right-framework">
|
||||||
{# <li><a href="{{ meta.server_url }}/search" id="searchLink">Search</a></li>#}
|
<li><a href="https://community.envipath.org/" id="communityLink">Community</a></li>
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<a data-toggle="dropdown" class="dropdown-toggle" href="#">Info <b class="caret"></b></a>
|
<a data-toggle="dropdown" class="dropdown-toggle" href="#">Info <b class="caret"></b></a>
|
||||||
<ul role="menu" class="dropdown-menu">
|
<ul role="menu" class="dropdown-menu">
|
||||||
@ -192,6 +202,23 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
{% if meta.url_contains_package and 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>
|
</div>
|
||||||
|
|
||||||
<!-- FOOTER -->
|
<!-- FOOTER -->
|
||||||
|
|||||||
@ -143,6 +143,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$(function () {
|
$(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
|
// Code that should be executed once DOM is ready goes here
|
||||||
$('#dropdown-predict').on('click', actionDropdownClicked);
|
$('#dropdown-predict').on('click', actionDropdownClicked);
|
||||||
$('#dropdown-search').on('click', actionDropdownClicked);
|
$('#dropdown-search').on('click', actionDropdownClicked);
|
||||||
|
|||||||
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
.center-button {
|
.center-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 70%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
.center-message {
|
.center-message {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 40%;
|
top: 35%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|||||||
@ -16,14 +16,14 @@
|
|||||||
<div class="jumbotron">Create a new Model to
|
<div class="jumbotron">Create a new Model to
|
||||||
limit the number of degradation products in the
|
limit the number of degradation products in the
|
||||||
prediction. You just need to set a name and the packages
|
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
|
you want the object to be based on. There are multiple types of models available.
|
||||||
default options suggested by us, simply click Submit,
|
For additional information have a look at our
|
||||||
otherwise click Advanced Options.
|
<a target="_blank" href="https://wiki.envipath.org/index.php/relative-reasoning" role="button">wiki >></a>
|
||||||
</div>
|
</div>
|
||||||
<label for="name">Name</label>
|
<label for="model-name">Name</label>
|
||||||
<input id="name" name="model-name" class="form-control" placeholder="Name"/>
|
<input id="model-name" name="model-name" class="form-control" placeholder="Name"/>
|
||||||
<label for="description">Description</label>
|
<label for="model-description">Description</label>
|
||||||
<input id="description" name="model-description" class="form-control"
|
<input id="model-description" name="model-description" class="form-control"
|
||||||
placeholder="Description"/>
|
placeholder="Description"/>
|
||||||
<label for="model-type">Model Type</label>
|
<label for="model-type">Model Type</label>
|
||||||
<select id="model-type" name="model-type" class="form-control" data-width='100%'>
|
<select id="model-type" name="model-type" class="form-control" data-width='100%'>
|
||||||
@ -35,7 +35,7 @@
|
|||||||
<!-- ML Based Form-->
|
<!-- ML Based Form-->
|
||||||
<div id="ml-relative-reasoning-specific-form">
|
<div id="ml-relative-reasoning-specific-form">
|
||||||
<!-- Rule Packages -->
|
<!-- 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"
|
<select id="ml-relative-reasoning-rule-packages" name="ml-relative-reasoning-rule-packages"
|
||||||
data-actions-box='true' class="form-control" multiple data-width='100%'>
|
data-actions-box='true' class="form-control" multiple data-width='100%'>
|
||||||
<option disabled>Reviewed Packages</option>
|
<option disabled>Reviewed Packages</option>
|
||||||
@ -53,7 +53,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<!-- Data Packages -->
|
<!-- 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"
|
<select id="ml-relative-reasoning-data-packages" name="ml-relative-reasoning-data-packages"
|
||||||
data-actions-box='true' class="form-control" multiple data-width='100%'>
|
data-actions-box='true' class="form-control" multiple data-width='100%'>
|
||||||
<option disabled>Reviewed Packages</option>
|
<option disabled>Reviewed Packages</option>
|
||||||
@ -77,22 +77,24 @@
|
|||||||
class="form-control">
|
class="form-control">
|
||||||
<option value="MACCS" selected>MACCS Fingerprinter</option>
|
<option value="MACCS" selected>MACCS Fingerprinter</option>
|
||||||
</select>
|
</select>
|
||||||
{% if 'plugins' in meta.enabled_features %}
|
{% if meta.enabled_features.PLUGINS and additional_descriptors %}
|
||||||
<!-- Property Plugins go here -->
|
<!-- Property Plugins go here -->
|
||||||
<label for="ml-relative-reasoning-additional-fingerprinter">Fingerprinter</label>
|
<label for="ml-relative-reasoning-additional-fingerprinter">Additional Fingerprinter / Descriptors</label>
|
||||||
<select id="ml-relative-reasoning-additional-fingerprinter"
|
<select id="ml-relative-reasoning-additional-fingerprinter" name="ml-relative-reasoning-additional-fingerprinter" class="form-control">
|
||||||
name="ml-relative-reasoning-additional-fingerprinter"
|
<option disabled selected>Select Additional Fingerprinter / Descriptor</option>
|
||||||
class="form-control">
|
{% for k, v in additional_descriptors.items %}
|
||||||
|
<option value="{{ v }}">{{ k }}</option>
|
||||||
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<label for="ml-relative-reasoning-threshold">Threshold</label>
|
<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"
|
id="ml-relative-reasoning-threshold"
|
||||||
name="ml-relative-reasoning-threshold" class="form-control">
|
name="ml-relative-reasoning-threshold" class="form-control">
|
||||||
|
|
||||||
<!-- Evaluation -->
|
<!-- Evaluation -->
|
||||||
|
<label for="ml-relative-reasoning-evaluation-packages">Evaluation Packages</label>
|
||||||
<label>Evaluation Packages</label><br>
|
|
||||||
<select id="ml-relative-reasoning-evaluation-packages" name="ml-relative-reasoning-evaluation-packages"
|
<select id="ml-relative-reasoning-evaluation-packages" name="ml-relative-reasoning-evaluation-packages"
|
||||||
data-actions-box='true' class="form-control" multiple data-width='100%'>
|
data-actions-box='true' class="form-control" multiple data-width='100%'>
|
||||||
<option disabled>Reviewed Packages</option>
|
<option disabled>Reviewed Packages</option>
|
||||||
@ -110,6 +112,26 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</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>
|
</div>
|
||||||
<!-- Rule Based Based Form-->
|
<!-- Rule Based Based Form-->
|
||||||
<div id="rule-based-relative-reasoning-specific-form">
|
<div id="rule-based-relative-reasoning-specific-form">
|
||||||
@ -118,47 +140,9 @@
|
|||||||
<!-- EnviFormer-->
|
<!-- EnviFormer-->
|
||||||
<div id="enviformer-specific-form">
|
<div id="enviformer-specific-form">
|
||||||
<label for="enviformer-threshold">Threshold</label>
|
<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">
|
name="enviformer-threshold" class="form-control">
|
||||||
</div>
|
</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>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
@ -179,6 +163,9 @@ $(function() {
|
|||||||
$("#ml-relative-reasoning-rule-packages").selectpicker();
|
$("#ml-relative-reasoning-rule-packages").selectpicker();
|
||||||
$("#ml-relative-reasoning-data-packages").selectpicker();
|
$("#ml-relative-reasoning-data-packages").selectpicker();
|
||||||
$("#ml-relative-reasoning-evaluation-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
|
// On change hide all and show only selected
|
||||||
$("#model-type").change(function() {
|
$("#model-type").change(function() {
|
||||||
|
|||||||
@ -1,115 +1,95 @@
|
|||||||
<div class="modal fade" tabindex="-1" id="new_scenario_modal" role="dialog" aria-labelledby="newScenGenMod"
|
<div class="modal fade" tabindex="-1" id="new_scenario_modal" role="dialog" aria-labelledby="new_scenario_modal"
|
||||||
aria-hidden="true">
|
aria-hidden="true">
|
||||||
<div class="modal-dialog modal-lg">
|
<div class="modal-dialog modal-lg">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal">
|
<button type="button" class="close" data-dismiss="modal">
|
||||||
<span aria-hidden="true">×</span> <span
|
<span aria-hidden="true">×</span>
|
||||||
class="sr-only">Close</span>
|
<span class="sr-only">Close</span>
|
||||||
</button>
|
</button>
|
||||||
<h4 class="js-title-step"></h4>
|
<h4 class="modal-title">New Scenario</h4>
|
||||||
</div>
|
</div>
|
||||||
<form id="base-scenario-form" accept-charset="UTF-8" action="" data-remote="true" method="POST">
|
<div class="modal-body">
|
||||||
<div class="modal-body hide" data-step="1" data-title="New Scenario - Step 1">
|
<form id="new_scenario_form" accept-charset="UTF-8" action="{{ meta.current_package.url }}/scenario"
|
||||||
<div class="jumbotron">Please enter name, description,
|
data-remote="true" method="post">
|
||||||
and date of scenario. Date should be associated to the
|
{% csrf_token %}
|
||||||
data, not the current date. For example, this could
|
<div class="jumbotron">Please enter name, description, and date of scenario. Date should be
|
||||||
reflect the publishing date of a study. You can leave
|
associated to the data, not the current date. For example, this could reflect the publishing
|
||||||
all fields but the name empty and fill them in
|
date of a study. You can leave all fields but the name empty and fill them in later.
|
||||||
later.
|
<a target="_blank" href="https://wiki.envipath.org/index.php/scenario" role="button">wiki
|
||||||
|
>></a>
|
||||||
</div>
|
</div>
|
||||||
<label for="name">Name</label>
|
<label for="scenario-name">Name</label>
|
||||||
<input id="name" name="studyname" placeholder="Name" class="form-control"/>
|
<input id="scenario-name" name="scenario-name" class="form-control" placeholder="Name"/>
|
||||||
<label for="name">Description</label>
|
<label for="scenario-description">Description</label>
|
||||||
<input id="description" name="studydescription" placeholder="Description" class="form-control"/>
|
<input id="scenario-description" name="scenario-description" class="form-control"
|
||||||
|
placeholder="Description"/>
|
||||||
<label id="dateField" for="dateYear">Date</label>
|
<label id="dateField" for="dateYear">Date</label>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
<input type="number" id="dateYear" name="dateYear" class="form-control" placeholder="YYYY">
|
<input type="number" id="dateYear" name="scenario-date-year" class="form-control"
|
||||||
|
placeholder="YYYY">
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
<input type="number" id="dateMonth" name="dateMonth" min="1" max="12"
|
<input type="number" id="dateMonth" name="scenario-date-month" min="1" max="12"
|
||||||
class="form-control" placeholder="MM" align="">
|
class="form-control" placeholder="MM" align="">
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
<input type="number" id="dateDay" name="dateDay" min="1" max="31" class="form-control"
|
<input type="number" id="dateDay" name="scenario-date-day" min="1" max="31" class="form-control"
|
||||||
placeholder="DD">
|
placeholder="DD">
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
<label for="scenario-type">Scenario Type</label>
|
||||||
<div class="modal-body hide" data-step="2" data-title="New Scenario - Step 2">
|
<select id="scenario-type" name="scenario-type" class="form-control" data-width='100%'>
|
||||||
<div class="jumbotron">
|
<option value="empty" selected>Empty Scenario</option>
|
||||||
Do you want to create an empty scenario and fill it
|
{% for k, v in scenario_types.items %}
|
||||||
with your own set of attributes, or fill in a
|
<option value="{{ v.name }}">{{ k }}</option>
|
||||||
pre-defined set of attributes for soil, sludge or sediment
|
{% endfor %}
|
||||||
data?
|
</select>
|
||||||
</div>
|
|
||||||
<div class="radio">
|
{% for type in scenario_types.values %}
|
||||||
<label>
|
<div id="{{ type.name }}-specific-inputs">
|
||||||
<input type="radio" name="type" id="radioEmpty" checked>Empty Scenario
|
{% for widget in type.widgets %}
|
||||||
</label>
|
{{ widget|safe }}
|
||||||
</div>
|
{% endfor %}
|
||||||
<div class="radio">
|
</div>
|
||||||
<label>
|
{% endfor %}
|
||||||
<input type="radio" name="type" id="radioSoil" value="soil" >Soil Data
|
|
||||||
</label>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="radio">
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="type" id="radioSludge" value="sludge">Sludge Data
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="radio">
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="type" id="radioSediment" value="sediment">Water-Sediment System Data
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body hide" data-step="3" data-title="New Scenario - Step 3">
|
|
||||||
<div class="jumbotron" id="finaljumbo">
|
|
||||||
All done! Click Submit!
|
|
||||||
</div>
|
|
||||||
<div style="float: left"><button
|
|
||||||
id="addColumnButton" type="button"
|
|
||||||
class="btn btn-default">Add
|
|
||||||
another Scenario
|
|
||||||
</button></div>
|
|
||||||
<input type="hidden" name="fullScenario" value="true"/>
|
|
||||||
{% include "tables/scenario.html" %}
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-default js-btn-step pull-left" data-orientation="cancel" data-dismiss="modal"></button>
|
<a id="new_scenario_modal_form_submit" class="btn btn-primary" href="#">Submit</a>
|
||||||
<button type="button" class="btn btn-default js-btn-step" data-orientation="previous"></button>
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
<button type="button" class="btn btn-default js-btn-step"
|
|
||||||
data-orientation="next" id="nextbutton"></button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(function () {
|
||||||
|
// Initially hide all "specific" forms
|
||||||
|
$("div[id$='-specific-inputs']").each(function () {
|
||||||
|
$(this).hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
// On change hide all and show only selected
|
||||||
|
$("#scenario-type").change(function () {
|
||||||
|
$("div[id$='-specific-inputs']").each(function () {
|
||||||
|
$(this).hide();
|
||||||
|
});
|
||||||
|
val = $('option:selected', this).val();
|
||||||
|
$("#" + val + "-specific-inputs").show();
|
||||||
|
});
|
||||||
|
|
||||||
<p></p>
|
$('#new_scenario_modal_form_submit').on('click', function (e) {
|
||||||
<div id="scenariocontent"></div>
|
e.preventDefault();
|
||||||
|
$('#new_scenario_form').submit();
|
||||||
|
});
|
||||||
|
|
||||||
<!--Template index -->
|
|
||||||
<script language="javascript">
|
|
||||||
$(function() {
|
|
||||||
|
|
||||||
// Hide additional columns per default
|
|
||||||
$('.col-2').hide();
|
|
||||||
$('.col-3').hide();
|
|
||||||
|
|
||||||
//TODO just to see modal
|
|
||||||
$('#new_scenario_modal').modalSteps({
|
|
||||||
btnCancelHtml: 'Cancel',
|
|
||||||
btnPreviousHtml: 'Previous',
|
|
||||||
btnNextHtml: 'Next',
|
|
||||||
btnLastStepHtml: 'Submit',
|
|
||||||
disableNextButton: false,
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,61 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!-- Add Additional Information-->
|
||||||
|
<div id="add_additional_information_modal" class="modal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
<h3 class="modal-title">Add Additional Information</h3>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<select id="select-additional-information-type" data-actions-box='true' class="form-control" data-width='100%'>
|
||||||
|
<option selected disabled>Select the type to add</option>
|
||||||
|
{% for add_inf in available_additional_information %}
|
||||||
|
<option value="{{ add_inf.name }}">{{ add_inf.display_name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
{% for add_inf in available_additional_information %}
|
||||||
|
<div class="aiform {{ add_inf.name }}" style="display: none;">
|
||||||
|
<form id="add_{{ add_inf.name }}_add-additional-information-modal-form" accept-charset="UTF-8"
|
||||||
|
action="" data-remote="true" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ add_inf.widget|safe }}
|
||||||
|
<input type="hidden" name="hidden" value="add-additional-information">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</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="add-additional-information-modal-submit">Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(function() {
|
||||||
|
|
||||||
|
$('#select-additional-information-type').change(function(e){
|
||||||
|
var selectedType = $("#select-additional-information-type :selected").val();
|
||||||
|
$('.aiform').hide();
|
||||||
|
$('.' + selectedType).show();
|
||||||
|
})
|
||||||
|
|
||||||
|
$('#add-additional-information-modal-submit').click(function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var selectedType = $("#select-additional-information-type :selected").val();
|
||||||
|
console.log(selectedType);
|
||||||
|
if (selectedType !== null && selectedType !== undefined && selectedType !== '') {
|
||||||
|
$('.' + selectedType + ' >form').submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
@ -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>
|
<option value="{{ e.url }}">{{ e.edge_label.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<input type="hidden" id="hidden" name="hidden" value="delete-edge"/>
|
<input type="hidden" id="hidden" name="hidden" value="delete"/>
|
||||||
</form>
|
</form>
|
||||||
<p></p>
|
<p></p>
|
||||||
<div id="delete_pathway_edge_image"></div>
|
<div id="delete_pathway_edge_image"></div>
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
<option value="{{ n.url }}">{{ n.default_node_label.name }}</option>
|
<option value="{{ n.url }}">{{ n.default_node_label.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<input type="hidden" id="hidden" name="hidden" value="delete-node"/>
|
<input type="hidden" id="hidden" name="hidden" value="delete"/>
|
||||||
</form>
|
</form>
|
||||||
<p></p>
|
<p></p>
|
||||||
<div id="delete_pathway_node_image"></div>
|
<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 %}
|
{% load static %}
|
||||||
<!-- Delete Pathway -->
|
<!-- Download Pathway -->
|
||||||
<div id="delete_pathway_modal" class="modal" tabindex="-1">
|
<div id="download_pathway_modal" class="modal" tabindex="-1">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<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">
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
Deletes the Pathway together with all Nodes and Edges.
|
By clicking on Download the Pathway will be converted into a CSV and directly downloaded.
|
||||||
<form id="delete-pathway-modal-form" accept-charset="UTF-8" action="" data-remote="true" method="post">
|
<form id="download-pathway-modal-form" accept-charset="UTF-8" action="{{ pathway.url }}"
|
||||||
{% csrf_token %}
|
data-remote="true" method="GET">
|
||||||
<input type="hidden" id="hidden" name="hidden" value="delete-pathway"/>
|
<input type="hidden" name="download" value="true"/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -26,9 +26,10 @@
|
|||||||
<script>
|
<script>
|
||||||
$(function () {
|
$(function () {
|
||||||
|
|
||||||
$('#delete-pathway-modal-submit').click(function (e) {
|
$('#download-pathway-modal-submit').click(function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$('#delete-pathway-modal-form').submit();
|
$('#download-pathway-modal-form').submit();
|
||||||
|
$('#download_pathway_modal').modal('hide');
|
||||||
});
|
});
|
||||||
|
|
||||||
})
|
})
|
||||||
@ -27,7 +27,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
<button type="button" class="btn btn-primary" id="edit-compound-modal-submit">Create</button>
|
<button type="button" class="btn btn-primary" id="edit-compound-modal-submit">Update</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
61
templates/modals/objects/generic_copy_object_modal.html
Normal file
61
templates/modals/objects/generic_copy_object_modal.html
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!-- Copy Object -->
|
||||||
|
<div id="generic_copy_object_modal" class="modal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3 class="modal-title">Copy {{ 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">
|
||||||
|
<form id="generic-copy-object-modal-form" accept-charset="UTF-8" data-remote="true" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<label for="target-package">Select the Target Package you want to copy this {{ object_type }}
|
||||||
|
into</label>
|
||||||
|
<select id="target-package" name="target-package" data-actions-box='true' class="form-control"
|
||||||
|
data-width='100%'>
|
||||||
|
<option disabled selected>Select Target Package</option>
|
||||||
|
{% for p in meta.writeable_packages %}
|
||||||
|
<option value="{{ p.url }}">{{ p.name }}</option>`
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<input type="hidden" name="hidden" value="copy">
|
||||||
|
</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-copy-object-modal-form-submit">
|
||||||
|
Copy
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(function () {
|
||||||
|
|
||||||
|
$('#generic-copy-object-modal-form-submit').click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const packageUrl = $('#target-package').find(":selected").val();
|
||||||
|
|
||||||
|
if (packageUrl === 'Select Target Package' || packageUrl === '' || packageUrl === null || packageUrl === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const formData = {
|
||||||
|
hidden: 'copy',
|
||||||
|
object_to_copy: '{{ current_object.url }}',
|
||||||
|
}
|
||||||
|
|
||||||
|
$.post(packageUrl, formData, function (response) {
|
||||||
|
if (response.success) {
|
||||||
|
window.location.href = response.success;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
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 %}
|
{% load static %}
|
||||||
<!-- Delete Compound -->
|
<!-- Publish a Package -->
|
||||||
<div id="delete_compound_modal" class="modal" tabindex="-1">
|
<div id="publish_package_modal" class="modal" tabindex="-1">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<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">
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
Deletes the Compound and associated Structures.
|
<p>Clicking on Publish will make this Package publicly available!</p>
|
||||||
<form id="delete-compound-modal-form" accept-charset="UTF-8" action="" data-remote="true" method="post">
|
<form id="publish-package-modal-form" accept-charset="UTF-8" action="{{ current_package.url }}" data-remote="true" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" id="hidden" name="hidden" value="delete-compound"/>
|
<input type="hidden" name="hidden" value="publish-package">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -26,9 +26,9 @@
|
|||||||
<script>
|
<script>
|
||||||
$(function() {
|
$(function() {
|
||||||
|
|
||||||
$('#delete-compound-modal-submit').click(function(e){
|
$('#publish-package-modal-form-submit').click(function(e){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$('#delete-compound-modal-form').submit();
|
$('#publish-package-modal-form').submit();
|
||||||
});
|
});
|
||||||
|
|
||||||
})
|
})
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!-- Update Scenario Additional Information-->
|
||||||
|
<div id="update_scenario_additional_information_modal" class="modal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
<h3 class="modal-title">Update Additional Information</h3>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="edit-scenario-additional-information-modal-form" accept-charset="UTF-8" action="" data-remote="true" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% for widget in update_widgets %}
|
||||||
|
{{ widget|safe }}
|
||||||
|
{% endfor %}
|
||||||
|
<input type="hidden" name="hidden" value="set-additional-information">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="edit-scenario-additional-information-modal-submit">Update</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(function() {
|
||||||
|
|
||||||
|
$('#edit-scenario-additional-information-modal-submit').click(function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
$('#edit-scenario-additional-information-modal-form').submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
@ -49,6 +49,18 @@
|
|||||||
<iframe id="predict-modal-ketcher" src="{% static '/js/ketcher2/ketcher.html' %}" width="100%"
|
<iframe id="predict-modal-ketcher" src="{% static '/js/ketcher2/ketcher.html' %}" width="100%"
|
||||||
height="510"></iframe>
|
height="510"></iframe>
|
||||||
</div>
|
</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>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
|||||||
@ -4,6 +4,9 @@
|
|||||||
|
|
||||||
{% block action_modals %}
|
{% block action_modals %}
|
||||||
{% include "modals/objects/edit_rule_modal.html" %}
|
{% include "modals/objects/edit_rule_modal.html" %}
|
||||||
|
{% include "modals/objects/generic_set_scenario_modal.html" %}
|
||||||
|
{% include "modals/objects/generic_copy_object_modal.html" %}
|
||||||
|
{% include "modals/objects/generic_delete_modal.html" %}
|
||||||
{% endblock action_modals %}
|
{% endblock action_modals %}
|
||||||
|
|
||||||
<div class="panel-group" id="rule-detail">
|
<div class="panel-group" id="rule-detail">
|
||||||
@ -49,7 +52,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</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 -->
|
<!-- EC Numbers -->
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
|
|||||||
@ -5,7 +5,9 @@
|
|||||||
{% block action_modals %}
|
{% block action_modals %}
|
||||||
{% include "modals/objects/edit_compound_modal.html" %}
|
{% include "modals/objects/edit_compound_modal.html" %}
|
||||||
{% include "modals/objects/add_structure_modal.html" %}
|
{% include "modals/objects/add_structure_modal.html" %}
|
||||||
{% include "modals/objects/delete_compound_modal.html" %}
|
{% include "modals/objects/generic_set_scenario_modal.html" %}
|
||||||
|
{% include "modals/objects/generic_copy_object_modal.html" %}
|
||||||
|
{% include "modals/objects/generic_delete_modal.html" %}
|
||||||
{% endblock action_modals %}
|
{% endblock action_modals %}
|
||||||
|
|
||||||
<div class="panel-group" id="compound-detail">
|
<div class="panel-group" id="compound-detail">
|
||||||
@ -134,7 +136,69 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% 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 -->
|
<!-- 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>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
{% block action_modals %}
|
{% block action_modals %}
|
||||||
{% include "modals/objects/edit_compound_structure_modal.html" %}
|
{% 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 %}
|
{% endblock action_modals %}
|
||||||
|
|
||||||
<div class="panel-group" id="compound-structure-detail">
|
<div class="panel-group" id="compound-structure-detail">
|
||||||
@ -54,6 +56,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</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 -->
|
<!-- Reactions -->
|
||||||
|
|
||||||
<!-- Pathways -->
|
<!-- Pathways -->
|
||||||
|
|||||||
@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
{% block action_modals %}
|
{% block action_modals %}
|
||||||
{# {% include "modals/objects/edit_edge_modal.html" %}#}
|
{# {% 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 %}
|
{% endblock action_modals %}
|
||||||
|
|
||||||
<div class="panel-group" id="edge-detail">
|
<div class="panel-group" id="edge-detail">
|
||||||
@ -19,7 +20,7 @@
|
|||||||
style="padding-right:1em"></span></a>
|
style="padding-right:1em"></span></a>
|
||||||
<ul id="actionsList" class="dropdown-menu">
|
<ul id="actionsList" class="dropdown-menu">
|
||||||
{% block actions %}
|
{% block actions %}
|
||||||
{# {% include "actions/objects/edge.html" %}#}
|
{% include "actions/objects/edge.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -103,6 +104,21 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% 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>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
{% block action_modals %}
|
{% block action_modals %}
|
||||||
{% include "modals/objects/edit_group_modal.html" %}
|
{% include "modals/objects/edit_group_modal.html" %}
|
||||||
{% include "modals/objects/edit_group_member_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 %}
|
{% endblock action_modals %}
|
||||||
|
|
||||||
<div class="panel-group" id="package-detail">
|
<div class="panel-group" id="package-detail">
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% block action_modals %}
|
{% block action_modals %}
|
||||||
{% include "modals/objects/delete_model_modal.html" %}
|
{% include "modals/objects/generic_delete_modal.html" %}
|
||||||
{% endblock action_modals %}
|
{% endblock action_modals %}
|
||||||
|
|
||||||
<!-- Include required libs -->
|
<!-- Include required libs -->
|
||||||
@ -90,27 +90,53 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<!-- Predict Panel -->
|
{% if model.ready_for_prediction %}
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
<!-- Predict Panel -->
|
||||||
<h4 class="panel-title">
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
<a id="predict-smiles-link" data-toggle="collapse" data-parent="#model-detail"
|
<h4 class="panel-title">
|
||||||
href="#predict-smiles">Predict</a>
|
<a id="predict-smiles-link" data-toggle="collapse" data-parent="#model-detail"
|
||||||
</h4>
|
href="#predict-smiles">Predict</a>
|
||||||
</div>
|
</h4>
|
||||||
<div id="predict-smiles" class="panel-collapse collapse in">
|
</div>
|
||||||
<div class="panel-body list-group-item">
|
<div id="predict-smiles" class="panel-collapse collapse in">
|
||||||
<div class="input-group">
|
<div class="panel-body list-group-item">
|
||||||
<input id="smiles-to-predict" type="text" class="form-control"
|
<div class="input-group">
|
||||||
placeholder="CCN(CC)C(=O)C1=CC(=CC=C1)C">
|
<input id="smiles-to-predict" type="text" class="form-control"
|
||||||
<span class="input-group-btn">
|
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>
|
<button class="btn btn-default" type="submit" id="predict-button">Predict!</button>
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
|
<div id="predictLoading"></div>
|
||||||
|
<div id="predictResultTable"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="loading"></div>
|
|
||||||
<div id="predictResultTable"></div>
|
|
||||||
</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' %}
|
{% if model.model_status == 'FINISHED' %}
|
||||||
<!-- Single Gen Curve Panel -->
|
<!-- Single Gen Curve Panel -->
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
@ -243,7 +269,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
function handleResponse(data) {
|
function handlePredictionResponse(data) {
|
||||||
res = "<table class='table table-striped'>"
|
res = "<table class='table table-striped'>"
|
||||||
res += "<thead>"
|
res += "<thead>"
|
||||||
res += "<th scope='col'>#</th>"
|
res += "<th scope='col'>#</th>"
|
||||||
@ -277,9 +303,9 @@
|
|||||||
$("#predictResultTable").append(res);
|
$("#predictResultTable").append(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
function clear() {
|
function clear(divid) {
|
||||||
$("#predictResultTable").removeClass("alert alert-danger");
|
$("#" + divid).removeClass("alert alert-danger");
|
||||||
$("#predictResultTable").empty();
|
$("#" + divid).empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($('#predict-button').length > 0) {
|
if ($('#predict-button').length > 0) {
|
||||||
@ -291,32 +317,70 @@
|
|||||||
"classify": "ILikeCats!"
|
"classify": "ILikeCats!"
|
||||||
}
|
}
|
||||||
|
|
||||||
clear();
|
clear("predictResultTable");
|
||||||
|
|
||||||
makeLoadingGif("#loading", "{% static '/images/wait.gif' %}");
|
makeLoadingGif("#predictLoading", "{% static '/images/wait.gif' %}");
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'get',
|
type: 'get',
|
||||||
data: data,
|
data: data,
|
||||||
url: '',
|
url: '',
|
||||||
success: function (data, textStatus) {
|
success: function (data, textStatus) {
|
||||||
try {
|
try {
|
||||||
$("#loading").empty();
|
$("#predictLoading").empty();
|
||||||
handleResponse(data);
|
handlePredictionResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("Error");
|
console.log("Error");
|
||||||
$("#loading").empty();
|
$("#predictLoading").empty();
|
||||||
$("#predictResultTable").addClass("alert alert-danger");
|
$("#predictResultTable").addClass("alert alert-danger");
|
||||||
$("#predictResultTable").append("Error while processing request :/");
|
$("#predictResultTable").append("Error while processing request :/");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function (jqXHR, textStatus, errorThrown) {
|
error: function (jqXHR, textStatus, errorThrown) {
|
||||||
$("#loading").empty();
|
$("#predictLoading").empty();
|
||||||
$("#predictResultTable").addClass("alert alert-danger");
|
$("#predictResultTable").addClass("alert alert-danger");
|
||||||
$("#predictResultTable").append("Error while processing request :/");
|
$("#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>
|
</script>
|
||||||
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
{% block action_modals %}
|
{% block action_modals %}
|
||||||
{% include "modals/objects/edit_node_modal.html" %}
|
{% 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 %}
|
{% endblock action_modals %}
|
||||||
|
|
||||||
<div class="panel-group" id="node-detail">
|
<div class="panel-group" id="node-detail">
|
||||||
@ -69,7 +70,34 @@
|
|||||||
</div>
|
</div>
|
||||||
</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>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@ -5,8 +5,9 @@
|
|||||||
{% block action_modals %}
|
{% block action_modals %}
|
||||||
{% include "modals/objects/edit_package_modal.html" %}
|
{% include "modals/objects/edit_package_modal.html" %}
|
||||||
{% include "modals/objects/edit_package_permissions_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/set_license_modal.html" %}
|
||||||
{% include "modals/objects/delete_package_modal.html" %}
|
{% include "modals/objects/generic_delete_modal.html" %}
|
||||||
{% endblock action_modals %}
|
{% endblock action_modals %}
|
||||||
|
|
||||||
<div class="panel-group" id="package-detail">
|
<div class="panel-group" id="package-detail">
|
||||||
@ -52,23 +53,5 @@
|
|||||||
|
|
||||||
</div>
|
</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>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@ -4,16 +4,22 @@
|
|||||||
|
|
||||||
<script src="https://d3js.org/d3.v7.min.js"></script>
|
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||||
<style>
|
<style>
|
||||||
svg {
|
#vizdiv {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 600px;
|
height: 600px;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pwsvg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link {
|
.link {
|
||||||
stroke: #999;
|
stroke: #999;
|
||||||
stroke-opacity: 0.6;
|
stroke-opacity: 0.6;
|
||||||
marker-end: url(#arrow);
|
//marker-end: url(#arrow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.link_no_arrow {
|
.link_no_arrow {
|
||||||
@ -31,6 +37,31 @@
|
|||||||
stroke-width: 1.5px;
|
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 {
|
.highlighted {
|
||||||
stroke: red;
|
stroke: red;
|
||||||
stroke-width: 3px;
|
stroke-width: 3px;
|
||||||
@ -50,10 +81,13 @@
|
|||||||
{% block action_modals %}
|
{% block action_modals %}
|
||||||
{% include "modals/objects/add_pathway_node_modal.html" %}
|
{% include "modals/objects/add_pathway_node_modal.html" %}
|
||||||
{% include "modals/objects/add_pathway_edge_modal.html" %}
|
{% include "modals/objects/add_pathway_edge_modal.html" %}
|
||||||
|
{% include "modals/objects/download_pathway_modal.html" %}
|
||||||
|
{% include "modals/objects/generic_copy_object_modal.html" %}
|
||||||
{% include "modals/objects/edit_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_node_modal.html" %}
|
||||||
{% include "modals/objects/delete_pathway_edge_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 %}
|
{% endblock action_modals %}
|
||||||
|
|
||||||
<p></p>
|
<p></p>
|
||||||
@ -79,8 +113,7 @@
|
|||||||
<ul class="nav navbar-nav">
|
<ul class="nav navbar-nav">
|
||||||
<li class="dropdown requiresWritePerm">
|
<li class="dropdown requiresWritePerm">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true" aria-expanded="false">
|
||||||
aria-expanded="false">
|
|
||||||
<span class="glyphicon glyphicon-edit"></span>
|
<span class="glyphicon glyphicon-edit"></span>
|
||||||
Edit
|
Edit
|
||||||
<span class="caret"></span></a>
|
<span class="caret"></span></a>
|
||||||
@ -90,11 +123,26 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</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>
|
||||||
|
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
<li>
|
<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>
|
<span class="glyphicon glyphicon-fullscreen"></span>
|
||||||
Fullscreen
|
Fullscreen
|
||||||
</a>
|
</a>
|
||||||
@ -125,16 +173,24 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div id="vizdiv">
|
<div id="vizdiv" >
|
||||||
<svg width="2000" height="2000">
|
<svg id="pwsvg">
|
||||||
{% if debug %}
|
{% if debug %}
|
||||||
<rect width="100%" height="100%" fill="aliceblue"/>
|
<rect width="100%" height="100%" fill="aliceblue"/>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<defs>
|
<defs>
|
||||||
<marker id="arrow" viewBox="0 0 10 10" refX="43" refY="5" markerWidth="6" markerHeight="6"
|
<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"/>
|
<path d="M 0 0 L 10 5 L 0 10 z" fill="#999"/>
|
||||||
</marker>
|
</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>
|
</defs>
|
||||||
<g id="zoomable"></g>
|
<g id="zoomable"></g>
|
||||||
</svg>
|
</svg>
|
||||||
@ -153,13 +209,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</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 %}
|
{% if pathway.setting %}
|
||||||
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
|
||||||
<h4 class="panel-title">
|
<h4 class="panel-title">
|
||||||
<a id="pathwaySettingLink" data-toggle="collapse" data-parent="#pathwayAccordion"
|
<a id="pathwaySettingLink" data-toggle="collapse" data-parent="#pathwayAccordion"
|
||||||
href="#pathwaySetting">Setting</a></h4>
|
href="#pathwaySetting">Setting</a></h4>
|
||||||
</div>
|
</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">
|
<div class="panel-body list-group-item" id="pathwaySettingContent">
|
||||||
<table class="table table-bordered table-hover">
|
<table class="table table-bordered table-hover">
|
||||||
<tr style="background-color: rgba(0, 0, 0, 0.08);">
|
<tr style="background-color: rgba(0, 0, 0, 0.08);">
|
||||||
@ -245,6 +317,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
|
// Globla switch for app domain view
|
||||||
|
var appDomainViewEnabled = false;
|
||||||
|
|
||||||
function goFullscreen(id) {
|
function goFullscreen(id) {
|
||||||
var element = document.getElementById(id);
|
var element = document.getElementById(id);
|
||||||
@ -266,6 +340,51 @@
|
|||||||
// TODO fix somewhere else...
|
// TODO fix somewhere else...
|
||||||
var newDesc = transformReferences($('#DescriptionContent')[0].innerText);
|
var newDesc = transformReferences($('#DescriptionContent')[0].innerText);
|
||||||
$('#DescriptionContent').html(newDesc);
|
$('#DescriptionContent').html(newDesc);
|
||||||
|
|
||||||
|
|
||||||
|
$('#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>
|
</script>
|
||||||
|
|||||||
@ -4,7 +4,9 @@
|
|||||||
|
|
||||||
{% block action_modals %}
|
{% block action_modals %}
|
||||||
{% include "modals/objects/edit_reaction_modal.html" %}
|
{% 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_copy_object_modal.html" %}
|
||||||
|
{% include "modals/objects/generic_delete_modal.html" %}
|
||||||
{% endblock action_modals %}
|
{% endblock action_modals %}
|
||||||
|
|
||||||
<div class="panel-group" id="reaction-detail">
|
<div class="panel-group" id="reaction-detail">
|
||||||
@ -118,9 +120,70 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
{% endif %}
|
{% 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>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@ -3,7 +3,9 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% block action_modals %}
|
{% block action_modals %}
|
||||||
|
{% include "modals/objects/add_additional_information_modal.html" %}
|
||||||
|
{% include "modals/objects/update_scenario_additional_information_modal.html" %}
|
||||||
|
{% include "modals/objects/generic_delete_modal.html" %}
|
||||||
{% endblock action_modals %}
|
{% endblock action_modals %}
|
||||||
<div class="panel-group" id="scenario-detail">
|
<div class="panel-group" id="scenario-detail">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
@ -24,6 +26,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">Description</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
{{ scenario.description }}
|
||||||
|
<br>
|
||||||
|
{{ scenario.scenario_type }}
|
||||||
|
<br>
|
||||||
|
Reported {{ scenario.scenario_date }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table id="scenario-table" class="table table-bordered table-striped table-hover">
|
<table id="scenario-table" class="table table-bordered table-striped table-hover">
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -31,18 +45,42 @@
|
|||||||
<th>Property</th>
|
<th>Property</th>
|
||||||
<th>Value</th>
|
<th>Value</th>
|
||||||
<th>Unit</th>
|
<th>Unit</th>
|
||||||
|
{% if meta.can_edit %}
|
||||||
<th>Remove</th>
|
<th>Remove</th>
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
{% for ai in scenario.get_additional_information %}
|
{% for ai in scenario.get_additional_information %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ ai.property_name|safe }} </td>
|
<td> {{ ai.property_name|safe }} </td>
|
||||||
<td> {{ ai.property_data|safe }} </td>
|
<td> {{ ai.property_data|safe }} </td>
|
||||||
<td> {{ ai.property_unit|safe }} </td>
|
<td> {{ ai.property_unit|safe }} </td>
|
||||||
<td></td>
|
{% if meta.can_edit %}
|
||||||
|
<td>
|
||||||
|
<form action="{% url 'package scenario detail' scenario.package.uuid scenario.uuid %}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="uuid" value="{{ ai.uuid }}">
|
||||||
|
<input type="hidden" name="hidden" value="delete-additional-information">
|
||||||
|
<button type="submit" class="btn"><span class="glyphicon glyphicon-minus"></span></button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% if meta.can_edit %}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td>Delete all</td>
|
||||||
|
<td>
|
||||||
|
<form action="{% url 'package scenario detail' scenario.package.uuid scenario.uuid %}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="hidden" value="delete-all-additional-information">
|
||||||
|
<button type="submit" class="btn"><span class="glyphicon glyphicon-trash"></span></button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,6 +4,9 @@
|
|||||||
|
|
||||||
{% block action_modals %}
|
{% block action_modals %}
|
||||||
{% include "modals/objects/edit_rule_modal.html" %}
|
{% include "modals/objects/edit_rule_modal.html" %}
|
||||||
|
{% include "modals/objects/generic_set_scenario_modal.html" %}
|
||||||
|
{% include "modals/objects/generic_copy_object_modal.html" %}
|
||||||
|
{% include "modals/objects/generic_delete_modal.html" %}
|
||||||
{% endblock action_modals %}
|
{% endblock action_modals %}
|
||||||
|
|
||||||
<div class="panel-group" id="rule-detail">
|
<div class="panel-group" id="rule-detail">
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
{% include "modals/objects/edit_password_modal.html" %}
|
{% include "modals/objects/edit_password_modal.html" %}
|
||||||
{% include "modals/collections/new_prediction_setting_modal.html" %}
|
{% include "modals/collections/new_prediction_setting_modal.html" %}
|
||||||
{% include "modals/objects/manage_api_token_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 %}
|
{% endblock action_modals %}
|
||||||
|
|
||||||
<div class="panel-group" id="user-detail">
|
<div class="panel-group" id="user-detail">
|
||||||
|
|||||||
@ -79,6 +79,10 @@
|
|||||||
|
|
||||||
allEmpty = true;
|
allEmpty = true;
|
||||||
for (key in data) {
|
for (key in data) {
|
||||||
|
if (key === 'searchterm') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (data[key].length < 1) {
|
if (data[key].length < 1) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -176,8 +180,16 @@
|
|||||||
$("#selPackages").selectpicker();
|
$("#selPackages").selectpicker();
|
||||||
$("#search-button").on("click", search);
|
$("#search-button").on("click", search);
|
||||||
|
|
||||||
|
$("#searchbar").on("keydown", function (e) {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
search(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
{% if search_result %}
|
{% if search_result %}
|
||||||
|
$('#searchbar').val('{{ search_result.searchterm }}')
|
||||||
handleSearchResponse("results", {{ search_result|safe }});
|
handleSearchResponse("results", {{ search_result|safe }});
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,72 +0,0 @@
|
|||||||
{% for obj in available_additional_information.types %}
|
|
||||||
<div id="table-{{obj.type}}-Div">
|
|
||||||
<table class="table table-bordered table-hover">
|
|
||||||
<tbody>
|
|
||||||
<tr id="{{ obj.type }}GroupRow" style="background-color: rgba(0, 0, 0, 0.08);">
|
|
||||||
<td><p style="font-size:18px">{{obj.title}}</p></td>
|
|
||||||
</tr>
|
|
||||||
<!-- Loop through all AIs and attach the ones without subtype -->
|
|
||||||
{% for ai in available_additional_information.ais %}
|
|
||||||
<tr>
|
|
||||||
{% if obj.type in ai.types and ai.sub_type is not defined %}
|
|
||||||
<td><span title="">{{ ai.name }}</span></td>
|
|
||||||
<!-- #TODO -->
|
|
||||||
{% for c in "1 2 3"|make_list %}
|
|
||||||
<td class="col-{{ c }}">
|
|
||||||
{% for form in ai.forms %}
|
|
||||||
<!-- Build input -->
|
|
||||||
{% if form.type == 'select' %}
|
|
||||||
<select class="form-control" name="{{ form.param_name}}">
|
|
||||||
<option value="">{{ form.placeholder }}</option>
|
|
||||||
{% for choice in form.choices %}
|
|
||||||
<option value="{{ choice.value }}">
|
|
||||||
{{ choice.name }}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
{% else %}
|
|
||||||
<input type="{{ form.type }}" name="{{ form.param_name }}" class="form-control" placeholder="{{ form.placeholder|safe }}"/>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</td>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</tr>
|
|
||||||
{% for subtype in available_additional_information.subtypes %}
|
|
||||||
<tr id="{{ subtype.type }}GroupRow" style="background-color: rgba(0, 0, 0, 0.08);">
|
|
||||||
<td><p style="font-size:18px">{{subtype.title}}</p></td>
|
|
||||||
</tr>
|
|
||||||
<!-- Loop through all AIs and attach the ones with the same subtype -->
|
|
||||||
{% for ai in available_additional_information.ais %}
|
|
||||||
<tr>
|
|
||||||
{% if obj.type in ai.types and subtype.type == ai.sub_type %}
|
|
||||||
<td><span title="">{{ ai.name }}</span></td>
|
|
||||||
<!-- #TODO -->
|
|
||||||
{% for c in "1 2 3"|make_list %}
|
|
||||||
<td class="col-{{ c }}">
|
|
||||||
{% for form in ai.forms %}
|
|
||||||
<!-- Build input -->
|
|
||||||
{% if form.type == 'select' %}
|
|
||||||
<select class="form-control" name="{{ form.param_name }}">
|
|
||||||
<option value="">{{ form.placeholder }}</option>
|
|
||||||
{% for choice in form.choices %}
|
|
||||||
<option value="{{ choice.value }}">
|
|
||||||
{{ choice.name }}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
{% else %}
|
|
||||||
<input type="{{ form.type }}" name="{{ form.param_name }}" class="form-control" placeholder="{{ form.placeholder|safe }}">
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</td>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
200
tests/test_copy_objects.py
Normal file
200
tests/test_copy_objects.py
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from epdb.logic import PackageManager
|
||||||
|
from epdb.models import Compound, User, CompoundStructure, Reaction, Rule, MLRelativeReasoning, Pathway
|
||||||
|
|
||||||
|
|
||||||
|
class CopyTest(TestCase):
|
||||||
|
fixtures = ["test_fixture.cleaned.json"]
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(CopyTest, cls).setUpClass()
|
||||||
|
cls.user = User.objects.get(username='anonymous')
|
||||||
|
cls.package = PackageManager.create_package(cls.user, 'Source Package', 'No Desc')
|
||||||
|
cls.AFOXOLANER = Compound.create(
|
||||||
|
cls.package,
|
||||||
|
smiles='C1C(=NOC1(C2=CC(=CC(=C2)Cl)C(F)(F)F)C(F)(F)F)C3=CC=C(C4=CC=CC=C43)C(=O)NCC(=O)NCC(F)(F)F',
|
||||||
|
name='Afoxolaner',
|
||||||
|
description='Test compound for copying'
|
||||||
|
)
|
||||||
|
|
||||||
|
cls.FOUR_NITROBENZOIC_ACID = Compound.create(
|
||||||
|
cls.package,
|
||||||
|
smiles='[O-][N+](=O)c1ccc(C(=O)[O-])cc1', # Normalized: O=C(O)C1=CC=C([N+](=O)[O-])C=C1',
|
||||||
|
name='Test Compound',
|
||||||
|
description='Compound with multiple structures'
|
||||||
|
)
|
||||||
|
|
||||||
|
cls.ETHANOL = Compound.create(
|
||||||
|
cls.package,
|
||||||
|
smiles='CCO',
|
||||||
|
name='Ethanol',
|
||||||
|
description='Simple alcohol'
|
||||||
|
)
|
||||||
|
cls.target_package = PackageManager.create_package(cls.user, 'Target Package', 'No Desc')
|
||||||
|
|
||||||
|
cls.reaction_educt = Compound.create(
|
||||||
|
cls.package,
|
||||||
|
smiles='C(CCl)Cl',
|
||||||
|
name='1,2-Dichloroethane',
|
||||||
|
description='Eawag BBD compound c0001'
|
||||||
|
).default_structure
|
||||||
|
|
||||||
|
cls.reaction_product = Compound.create(
|
||||||
|
cls.package,
|
||||||
|
smiles='C(CO)Cl',
|
||||||
|
name='2-Chloroethanol',
|
||||||
|
description='Eawag BBD compound c0005'
|
||||||
|
).default_structure
|
||||||
|
|
||||||
|
cls.REACTION = Reaction.create(
|
||||||
|
package=cls.package,
|
||||||
|
name='Eawag BBD reaction r0001',
|
||||||
|
educts=[cls.reaction_educt],
|
||||||
|
products=[cls.reaction_product],
|
||||||
|
multi_step=False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_compound_copy_basic(self):
|
||||||
|
"""Test basic compound copying functionality"""
|
||||||
|
mapping = dict()
|
||||||
|
copied_compound = self.AFOXOLANER.copy(self.target_package, mapping)
|
||||||
|
|
||||||
|
self.assertNotEqual(self.AFOXOLANER.uuid, copied_compound.uuid)
|
||||||
|
self.assertEqual(self.AFOXOLANER.name, copied_compound.name)
|
||||||
|
self.assertEqual(self.AFOXOLANER.description, copied_compound.description)
|
||||||
|
self.assertEqual(copied_compound.package, self.target_package)
|
||||||
|
self.assertEqual(self.AFOXOLANER.package, self.package)
|
||||||
|
self.assertEqual(self.AFOXOLANER.default_structure.smiles, copied_compound.default_structure.smiles)
|
||||||
|
|
||||||
|
def test_compound_copy_with_multiple_structures(self):
|
||||||
|
"""Test copying a compound with multiple structures"""
|
||||||
|
|
||||||
|
original_structure_count = self.FOUR_NITROBENZOIC_ACID.structures.count()
|
||||||
|
|
||||||
|
mapping = dict()
|
||||||
|
# Copy the compound
|
||||||
|
copied_compound = self.FOUR_NITROBENZOIC_ACID.copy(self.target_package, mapping)
|
||||||
|
|
||||||
|
# Verify all structures were copied
|
||||||
|
self.assertEqual(copied_compound.structures.count(), original_structure_count)
|
||||||
|
|
||||||
|
# Verify default_structure is properly set
|
||||||
|
self.assertIsNotNone(copied_compound.default_structure)
|
||||||
|
self.assertEqual(
|
||||||
|
copied_compound.default_structure.smiles,
|
||||||
|
self.FOUR_NITROBENZOIC_ACID.default_structure.smiles
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_compound_copy_preserves_aliases(self):
|
||||||
|
"""Test that compound copying preserves aliases"""
|
||||||
|
# Create a compound and add aliases
|
||||||
|
original_compound = self.ETHANOL
|
||||||
|
|
||||||
|
# Add aliases if the method exists
|
||||||
|
if hasattr(original_compound, 'add_alias'):
|
||||||
|
original_compound.add_alias('Ethyl alcohol')
|
||||||
|
original_compound.add_alias('Grain alcohol')
|
||||||
|
|
||||||
|
mapping = dict()
|
||||||
|
copied_compound = original_compound.copy(self.target_package, mapping)
|
||||||
|
|
||||||
|
# Verify aliases were copied if they exist
|
||||||
|
if hasattr(original_compound, 'aliases') and hasattr(copied_compound, 'aliases'):
|
||||||
|
original_aliases = original_compound.aliases
|
||||||
|
copied_aliases = copied_compound.aliases
|
||||||
|
self.assertEqual(original_aliases, copied_aliases)
|
||||||
|
|
||||||
|
def test_compound_copy_preserves_external_identifiers(self):
|
||||||
|
"""Test that external identifiers are preserved during copying"""
|
||||||
|
original_compound = self.ETHANOL
|
||||||
|
|
||||||
|
# Add external identifiers if the methods exist
|
||||||
|
if hasattr(original_compound, 'add_cas_number'):
|
||||||
|
original_compound.add_cas_number('64-17-5')
|
||||||
|
if hasattr(original_compound, 'add_pubchem_compound_id'):
|
||||||
|
original_compound.add_pubchem_compound_id('702')
|
||||||
|
|
||||||
|
mapping = dict()
|
||||||
|
copied_compound = original_compound.copy(self.target_package, mapping)
|
||||||
|
|
||||||
|
# Verify external identifiers were copied
|
||||||
|
original_ext_ids = original_compound.external_identifiers.all()
|
||||||
|
copied_ext_ids = copied_compound.external_identifiers.all()
|
||||||
|
|
||||||
|
self.assertEqual(original_ext_ids.count(), copied_ext_ids.count())
|
||||||
|
|
||||||
|
# Check that identifier values match
|
||||||
|
original_values = set(ext_id.identifier_value for ext_id in original_ext_ids)
|
||||||
|
copied_values = set(ext_id.identifier_value for ext_id in copied_ext_ids)
|
||||||
|
self.assertEqual(original_values, copied_values)
|
||||||
|
|
||||||
|
def test_compound_copy_structure_properties(self):
|
||||||
|
"""Test that structure properties are properly copied"""
|
||||||
|
original_compound = self.ETHANOL
|
||||||
|
|
||||||
|
mapping = dict()
|
||||||
|
copied_compound = original_compound.copy(self.target_package, mapping)
|
||||||
|
|
||||||
|
# Verify structure properties
|
||||||
|
original_structure = original_compound.default_structure
|
||||||
|
copied_structure = copied_compound.default_structure
|
||||||
|
|
||||||
|
self.assertEqual(original_structure.smiles, copied_structure.smiles)
|
||||||
|
self.assertEqual(original_structure.canonical_smiles, copied_structure.canonical_smiles)
|
||||||
|
self.assertEqual(original_structure.inchikey, copied_structure.inchikey)
|
||||||
|
self.assertEqual(original_structure.normalized_structure, copied_structure.normalized_structure)
|
||||||
|
|
||||||
|
# Verify they are different objects
|
||||||
|
self.assertNotEqual(original_structure.uuid, copied_structure.uuid)
|
||||||
|
self.assertEqual(copied_structure.compound, copied_compound)
|
||||||
|
|
||||||
|
def test_reaction_copy_basic(self):
|
||||||
|
"""Test basic reaction copying functionality"""
|
||||||
|
mapping = dict()
|
||||||
|
copied_reaction = self.REACTION.copy(self.target_package, mapping)
|
||||||
|
|
||||||
|
self.assertNotEqual(self.REACTION.uuid, copied_reaction.uuid)
|
||||||
|
self.assertEqual(self.REACTION.name, copied_reaction.name)
|
||||||
|
self.assertEqual(self.REACTION.description, copied_reaction.description)
|
||||||
|
self.assertEqual(self.REACTION.multi_step, copied_reaction.multi_step)
|
||||||
|
self.assertEqual(copied_reaction.package, self.target_package)
|
||||||
|
self.assertEqual(self.REACTION.package, self.package)
|
||||||
|
|
||||||
|
|
||||||
|
def test_reaction_copy_structures(self):
|
||||||
|
"""Test basic reaction copying functionality"""
|
||||||
|
mapping = dict()
|
||||||
|
copied_reaction = self.REACTION.copy(self.target_package, mapping)
|
||||||
|
|
||||||
|
for orig_educt, copy_educt in zip(self.REACTION.educts.all(), copied_reaction.educts.all()):
|
||||||
|
self.assertNotEqual(orig_educt.uuid, copy_educt.uuid)
|
||||||
|
self.assertEqual(orig_educt.name, copy_educt.name)
|
||||||
|
self.assertEqual(orig_educt.description, copy_educt.description)
|
||||||
|
self.assertEqual(copy_educt.compound.package, self.target_package)
|
||||||
|
self.assertEqual(orig_educt.compound.package, self.package)
|
||||||
|
self.assertEqual(orig_educt.smiles, copy_educt.smiles)
|
||||||
|
|
||||||
|
for orig_product, copy_product in zip(self.REACTION.products.all(), copied_reaction.products.all()):
|
||||||
|
self.assertNotEqual(orig_product.uuid, copy_product.uuid)
|
||||||
|
self.assertEqual(orig_product.name, copy_product.name)
|
||||||
|
self.assertEqual(orig_product.description, copy_product.description)
|
||||||
|
self.assertEqual(copy_product.compound.package, self.target_package)
|
||||||
|
self.assertEqual(orig_product.compound.package, self.package)
|
||||||
|
self.assertEqual(orig_product.smiles, copy_product.smiles)
|
||||||
|
|
||||||
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
|
@classmethod
|
||||||
def tearDownClass(cls):
|
def tearDownClass(cls):
|
||||||
from collections import Counter
|
print(f"\nTotal Errors across Rules {len(cls.error_smiles)}")
|
||||||
# print(Counter(cls.error_smiles))
|
# print(cls.error_smiles)
|
||||||
pass
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
print(f"\nTotal errors {self.total_errors}")
|
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']):
|
for comp, ambit_prod in zip(bt_rule['compounds'], bt_rule['products']):
|
||||||
|
|
||||||
smi = comp['smiles']
|
smi = comp['smiles']
|
||||||
products = FormatConverter.apply(smi, smirks, preprocess_smiles=True, bracketize=False)
|
products = FormatConverter.apply(smi, smirks)
|
||||||
|
|
||||||
all_rdkit_prods = []
|
all_rdkit_prods = []
|
||||||
for ps in products:
|
for ps in products:
|
||||||
@ -53,15 +52,15 @@ class RuleApplicationTest(TestCase):
|
|||||||
|
|
||||||
# TODO mode "intersection"
|
# TODO mode "intersection"
|
||||||
# partial_res = (len(set(ambit_smiles).intersection(set(rdkit_smiles))) > 0) or (len(ambit_smiles) == 0)
|
# 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"
|
# TODO mode = "full ambit"
|
||||||
# partial_res = len(set(ambit_smiles).intersection(set(rdkit_smiles))) == len(ambit_smiles)
|
# partial_res = len(set(ambit_smiles).intersection(set(rdkit_smiles))) == len(ambit_smiles)
|
||||||
# FAILED (failures=52)
|
# FAILED (failures=44)
|
||||||
|
|
||||||
# TODO mode = "equality"
|
# TODO mode = "equality"
|
||||||
partial_res = set(ambit_smiles) == set(rdkit_smiles)
|
partial_res = set(ambit_smiles) == set(rdkit_smiles)
|
||||||
# FAILED (failures=71)
|
# FAILED (failures=64)
|
||||||
|
|
||||||
if len(ambit_smiles) and not partial_res:
|
if len(ambit_smiles) and not partial_res:
|
||||||
print(f"""
|
print(f"""
|
||||||
|
|||||||
@ -8,10 +8,12 @@ from indigo import Indigo, IndigoException, IndigoObject
|
|||||||
from indigo.renderer import IndigoRenderer
|
from indigo.renderer import IndigoRenderer
|
||||||
from rdkit import Chem
|
from rdkit import Chem
|
||||||
from rdkit import RDLogger
|
from rdkit import RDLogger
|
||||||
from rdkit.Chem import MACCSkeys
|
from rdkit.Chem import MACCSkeys, Descriptors
|
||||||
from rdkit.Chem import rdChemReactions
|
from rdkit.Chem import rdChemReactions
|
||||||
from rdkit.Chem.Draw import rdMolDraw2D
|
from rdkit.Chem.Draw import rdMolDraw2D
|
||||||
from rdkit.Chem.MolStandardize import rdMolStandardize
|
from rdkit.Chem.MolStandardize import rdMolStandardize
|
||||||
|
from rdkit.Chem.rdmolops import GetMolFrags
|
||||||
|
from rdkit.Contrib.IFG import ifg
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
RDLogger.DisableLog('rdApp.*')
|
RDLogger.DisableLog('rdApp.*')
|
||||||
@ -65,6 +67,18 @@ class PredictionResult(object):
|
|||||||
|
|
||||||
class FormatConverter(object):
|
class FormatConverter(object):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def mass(smiles):
|
||||||
|
return Descriptors.MolWt(FormatConverter.from_smiles(smiles))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def charge(smiles):
|
||||||
|
return Chem.GetFormalCharge(FormatConverter.from_smiles(smiles))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def formula(smiles):
|
||||||
|
return Chem.rdMolDescriptors.CalcMolFormula(FormatConverter.from_smiles(smiles))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_smiles(smiles):
|
def from_smiles(smiles):
|
||||||
return Chem.MolFromSmiles(smiles)
|
return Chem.MolFromSmiles(smiles)
|
||||||
@ -77,6 +91,10 @@ class FormatConverter(object):
|
|||||||
def InChIKey(smiles):
|
def InChIKey(smiles):
|
||||||
return Chem.MolToInchiKey(FormatConverter.from_smiles(smiles))
|
return Chem.MolToInchiKey(FormatConverter.from_smiles(smiles))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def InChI(smiles):
|
||||||
|
return Chem.MolToInchi(FormatConverter.from_smiles(smiles))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def canonicalize(smiles: str):
|
def canonicalize(smiles: str):
|
||||||
return FormatConverter.to_smiles(FormatConverter.from_smiles(smiles), canonical=True)
|
return FormatConverter.to_smiles(FormatConverter.from_smiles(smiles), canonical=True)
|
||||||
@ -87,6 +105,21 @@ class FormatConverter(object):
|
|||||||
bitvec = MACCSkeys.GenMACCSKeys(mol)
|
bitvec = MACCSkeys.GenMACCSKeys(mol)
|
||||||
return bitvec.ToList()
|
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
|
@staticmethod
|
||||||
def to_svg(smiles, mol_size=(200, 150), kekulize=True):
|
def to_svg(smiles, mol_size=(200, 150), kekulize=True):
|
||||||
mol = FormatConverter.from_smiles(smiles)
|
mol = FormatConverter.from_smiles(smiles)
|
||||||
@ -131,6 +164,24 @@ class FormatConverter(object):
|
|||||||
# TODO call to AMBIT Service
|
# TODO call to AMBIT Service
|
||||||
return smiles
|
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
|
@staticmethod
|
||||||
def standardize(smiles):
|
def standardize(smiles):
|
||||||
# Taken from https://bitsilla.com/blog/2021/06/standardizing-a-molecule-using-rdkit/
|
# Taken from https://bitsilla.com/blog/2021/06/standardizing-a-molecule-using-rdkit/
|
||||||
@ -180,54 +231,6 @@ class FormatConverter(object):
|
|||||||
atom.UpdatePropertyCache()
|
atom.UpdatePropertyCache()
|
||||||
return mol
|
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
|
@staticmethod
|
||||||
def is_valid_smirks(smirks: str) -> bool:
|
def is_valid_smirks(smirks: str) -> bool:
|
||||||
try:
|
try:
|
||||||
@ -237,7 +240,7 @@ class FormatConverter(object):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def apply(smiles: str, smirks: str, preprocess_smiles: bool = True, bracketize: bool = False,
|
def apply(smiles: str, smirks: str, preprocess_smiles: bool = True, bracketize: bool = True,
|
||||||
standardize: bool = True, kekulize: bool = True) -> List['ProductSet']:
|
standardize: bool = True, kekulize: bool = True) -> List['ProductSet']:
|
||||||
logger.debug(f'Applying {smirks} on {smiles}')
|
logger.debug(f'Applying {smirks} on {smiles}')
|
||||||
|
|
||||||
@ -266,8 +269,10 @@ class FormatConverter(object):
|
|||||||
for product in product_set:
|
for product in product_set:
|
||||||
try:
|
try:
|
||||||
Chem.SanitizeMol(product)
|
Chem.SanitizeMol(product)
|
||||||
|
product = GetMolFrags(product, asMols=True)
|
||||||
product = FormatConverter.standardize(Chem.MolToSmiles(product))
|
for p in product:
|
||||||
|
p = FormatConverter.standardize(Chem.MolToSmiles(p))
|
||||||
|
prods.append(p)
|
||||||
|
|
||||||
# if kekulize:
|
# if kekulize:
|
||||||
# # from rdkit.Chem import MolStandardize
|
# # from rdkit.Chem import MolStandardize
|
||||||
@ -292,13 +297,12 @@ class FormatConverter(object):
|
|||||||
# # bond.SetIsAromatic(False)
|
# # bond.SetIsAromatic(False)
|
||||||
# Chem.Kekulize(product)
|
# Chem.Kekulize(product)
|
||||||
|
|
||||||
prods.append(product)
|
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logger.error(f'Sanitizing and converting failed:\n{e}')
|
logger.error(f'Sanitizing and converting failed:\n{e}')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# TODO doc!
|
if len(prods):
|
||||||
if len(prods) and len(prods) == len(product_set):
|
|
||||||
ps = ProductSet(prods)
|
ps = ProductSet(prods)
|
||||||
pss.add(ps)
|
pss.add(ps)
|
||||||
|
|
||||||
@ -651,20 +655,23 @@ class IndigoUtils(object):
|
|||||||
environment.add(mappedAtom.index())
|
environment.add(mappedAtom.index())
|
||||||
|
|
||||||
for k, v in functional_groups.items():
|
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):
|
for atom in query.iterateAtoms():
|
||||||
if match is not None:
|
mappedAtom = match.mapAtom(atom)
|
||||||
|
if mappedAtom is None or mappedAtom.index() in environment:
|
||||||
|
continue
|
||||||
|
|
||||||
for atom in query.iterateAtoms():
|
counts[mappedAtom.index()] = max(v, counts[mappedAtom.index()])
|
||||||
mappedAtom = match.mapAtom(atom)
|
|
||||||
if mappedAtom is None or mappedAtom.index() in environment:
|
|
||||||
continue
|
|
||||||
|
|
||||||
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():
|
for k, v in counts.items():
|
||||||
if is_reaction:
|
if is_reaction:
|
||||||
@ -691,7 +698,10 @@ class IndigoUtils(object):
|
|||||||
i.setOption("render-image-size", width, height)
|
i.setOption("render-image-size", width, height)
|
||||||
i.setOption("render-bond-line-width", 2.0)
|
i.setOption("render-bond-line-width", 2.0)
|
||||||
|
|
||||||
mol = i.loadMolecule(mol_data)
|
if '~' in mol_data:
|
||||||
|
mol = i.loadSmarts(mol_data)
|
||||||
|
else:
|
||||||
|
mol = i.loadMolecule(mol_data)
|
||||||
|
|
||||||
if len(functional_groups.keys()) > 0:
|
if len(functional_groups.keys()) > 0:
|
||||||
IndigoUtils._colorize(i, mol, functional_groups, False)
|
IndigoUtils._colorize(i, mol, functional_groups, False)
|
||||||
|
|||||||
168
utilities/misc.py
Normal file
168
utilities/misc.py
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
import html
|
||||||
|
import logging
|
||||||
|
from collections import defaultdict
|
||||||
|
from enum import Enum
|
||||||
|
from types import NoneType
|
||||||
|
from typing import Dict, List, Any
|
||||||
|
|
||||||
|
from envipy_additional_information import Interval, EnviPyModel
|
||||||
|
from envipy_additional_information import NAME_MAPPING
|
||||||
|
from pydantic import BaseModel, HttpUrl
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class HTMLGenerator:
|
||||||
|
registry = {x.__name__: x for x in NAME_MAPPING.values()}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def generate_html(additional_information: 'EnviPyModel', prefix='') -> str:
|
||||||
|
from typing import get_origin, get_args, Union
|
||||||
|
|
||||||
|
if isinstance(additional_information, type):
|
||||||
|
clz_name = additional_information.__name__
|
||||||
|
else:
|
||||||
|
clz_name = additional_information.__class__.__name__
|
||||||
|
|
||||||
|
widget = f'<h4>{clz_name}</h4>'
|
||||||
|
|
||||||
|
if hasattr(additional_information, 'uuid'):
|
||||||
|
uuid = additional_information.uuid
|
||||||
|
widget += f'<input type="hidden" name="{clz_name}__{prefix}__uuid" value="{uuid}">'
|
||||||
|
|
||||||
|
for name, field in additional_information.model_fields.items():
|
||||||
|
value = getattr(additional_information, name, None)
|
||||||
|
full_name = f"{clz_name}__{prefix}__{name}"
|
||||||
|
annotation = field.annotation
|
||||||
|
base_type = get_origin(annotation) or annotation
|
||||||
|
|
||||||
|
# Optional[Interval[float]] alias for Union[X, None]
|
||||||
|
if base_type is Union:
|
||||||
|
for arg in get_args(annotation):
|
||||||
|
if arg is not NoneType:
|
||||||
|
field_type = arg
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
field_type = base_type
|
||||||
|
|
||||||
|
is_interval_float = (
|
||||||
|
field_type == Interval[float] or
|
||||||
|
str(field_type) == str(Interval[float]) or
|
||||||
|
'Interval[float]' in str(field_type)
|
||||||
|
)
|
||||||
|
|
||||||
|
if is_interval_float:
|
||||||
|
widget += f"""
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="{full_name}__start">{' '.join([x.capitalize() for x in name.split('_')])} Start</label>
|
||||||
|
<input type="number" class="form-control" id="{full_name}__start" name="{full_name}__start" value="{value.start if value else ''}">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="{full_name}__end">{' '.join([x.capitalize() for x in name.split('_')])} End</label>
|
||||||
|
<input type="number" class="form-control" id="{full_name}__end" name="{full_name}__end" value="{value.end if value else ''}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
elif issubclass(field_type, Enum):
|
||||||
|
options: str = ''
|
||||||
|
for e in field_type:
|
||||||
|
options += f'<option value="{e.value}" {"selected" if e == value else ""}>{html.escape(e.name)}</option>'
|
||||||
|
|
||||||
|
widget += f"""
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{full_name}">{' '.join([x.capitalize() for x in name.split('_')])}</label>
|
||||||
|
<select class="form-control" id="{full_name}" name="{full_name}">
|
||||||
|
<option value="" disabled selected>Select {' '.join([x.capitalize() for x in name.split('_')])}</option>
|
||||||
|
{options}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
else:
|
||||||
|
if field_type == str or field_type == HttpUrl:
|
||||||
|
input_type = 'text'
|
||||||
|
elif field_type == float or field_type == int:
|
||||||
|
input_type = 'number'
|
||||||
|
elif field_type == bool:
|
||||||
|
input_type = 'checkbox'
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Could not parse field type {field_type} for {name}")
|
||||||
|
|
||||||
|
value_to_use = value if value and field_type != bool else ''
|
||||||
|
|
||||||
|
widget += f"""
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{full_name}">{' '.join([x.capitalize() for x in name.split('_')])}</label>
|
||||||
|
<input type="{input_type}" class="form-control" id="{full_name}" name="{full_name}" value="{value_to_use}" {"checked" if value and field_type == bool else ""}>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
|
||||||
|
return widget + "<hr>"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def build_models(params) -> Dict[str, List['EnviPyModel']]:
|
||||||
|
|
||||||
|
def has_non_none(d):
|
||||||
|
"""
|
||||||
|
Recursively checks if any value in a (possibly nested) dict is not None.
|
||||||
|
"""
|
||||||
|
for value in d.values():
|
||||||
|
if isinstance(value, dict):
|
||||||
|
if has_non_none(value): # recursive check
|
||||||
|
return True
|
||||||
|
elif value is not None:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
"""
|
||||||
|
Build Pydantic model instances from flattened HTML parameters.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
params: dict of {param_name: value}, e.g. form data
|
||||||
|
model_registry: mapping of class names (strings) to Pydantic model classes
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: {ClassName: [list of model instances]}
|
||||||
|
"""
|
||||||
|
grouped: Dict[str, Dict[str, Dict[str, Any]]] = {}
|
||||||
|
|
||||||
|
# Step 1: group fields by ClassName and Number
|
||||||
|
for key, value in params.items():
|
||||||
|
if value == '':
|
||||||
|
value = None
|
||||||
|
|
||||||
|
parts = key.split("__")
|
||||||
|
if len(parts) < 3:
|
||||||
|
continue # skip invalid keys
|
||||||
|
|
||||||
|
class_name, number, *field_parts = parts
|
||||||
|
grouped.setdefault(class_name, {}).setdefault(number, {})
|
||||||
|
|
||||||
|
# handle nested fields like interval__start
|
||||||
|
target = grouped[class_name][number]
|
||||||
|
current = target
|
||||||
|
for p in field_parts[:-1]:
|
||||||
|
current = current.setdefault(p, {})
|
||||||
|
current[field_parts[-1]] = value
|
||||||
|
|
||||||
|
# Step 2: instantiate Pydantic models
|
||||||
|
instances: Dict[str, List[BaseModel]] = defaultdict(list)
|
||||||
|
for class_name, number_dict in grouped.items():
|
||||||
|
model_cls = HTMLGenerator.registry.get(class_name)
|
||||||
|
|
||||||
|
if not model_cls:
|
||||||
|
logger.info(f"Could not find model class for {class_name}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
for number, fields in number_dict.items():
|
||||||
|
if not has_non_none(fields):
|
||||||
|
print(f"Skipping empty {class_name} {number} {fields}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
uuid = fields.pop('uuid', None)
|
||||||
|
instance = model_cls(**fields)
|
||||||
|
if uuid:
|
||||||
|
instance.__dict__['uuid'] = uuid
|
||||||
|
instances[class_name].append(instance)
|
||||||
|
|
||||||
|
return instances
|
||||||
540
utilities/ml.py
540
utilities/ml.py
@ -1,46 +1,29 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import dataclasses
|
import logging
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import List, Optional
|
from typing import List, Dict, Set, Tuple
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from sklearn.base import BaseEstimator, ClassifierMixin
|
from sklearn.base import BaseEstimator, ClassifierMixin
|
||||||
from sklearn.decomposition import PCA
|
from sklearn.decomposition import PCA
|
||||||
|
from sklearn.ensemble import RandomForestClassifier
|
||||||
from sklearn.metrics import accuracy_score
|
from sklearn.metrics import accuracy_score
|
||||||
from sklearn.multioutput import ClassifierChain
|
from sklearn.multioutput import ClassifierChain
|
||||||
from sklearn.preprocessing import StandardScaler
|
from sklearn.preprocessing import StandardScaler
|
||||||
from sklearn.tree import DecisionTreeClassifier
|
|
||||||
from sklearn.ensemble import RandomForestClassifier
|
|
||||||
|
|
||||||
# @dataclasses.dataclass
|
logger = logging.getLogger(__name__)
|
||||||
# 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
|
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
from utilities.chem import FormatConverter
|
from utilities.chem import FormatConverter, PredictionResult
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Compound:
|
class SCompound:
|
||||||
smiles: str
|
smiles: str
|
||||||
uuid: str = field(default=None, compare=False, hash=False)
|
uuid: str = field(default=None, compare=False, hash=False)
|
||||||
|
|
||||||
@ -53,10 +36,10 @@ class Compound:
|
|||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Reaction:
|
class SReaction:
|
||||||
educts: List[Compound]
|
educts: List[SCompound]
|
||||||
products: List[Compound]
|
products: List[SCompound]
|
||||||
rule_uuid: str = field(default=None, compare=False, hash=False)
|
rule_uuid: SRule = field(default=None, compare=False, hash=False)
|
||||||
reaction_uuid: str = field(default=None, compare=False, hash=False)
|
reaction_uuid: str = field(default=None, compare=False, hash=False)
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
@ -68,77 +51,304 @@ class Reaction:
|
|||||||
return self._hash
|
return self._hash
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if not isinstance(other, Reaction):
|
if not isinstance(other, SReaction):
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
return (
|
return (
|
||||||
sorted(self.educts, key=lambda x: x.smiles) == sorted(other.educts, key=lambda x: x.smiles) and
|
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.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]]):
|
@abstractmethod
|
||||||
self.headers = headers
|
def apply(self):
|
||||||
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):
|
|
||||||
pass
|
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
|
@staticmethod
|
||||||
def generate_dataset(compounds: List[Compound], reactions: List[Reaction], applicable_rules: 'Rule',
|
def generate_dataset(reactions: List['Reaction'], applicable_rules: List['Rule'], educts_only: bool = True) -> Dataset:
|
||||||
compounds_to_exclude: Optional[Compound] = None, educts_only: bool = False) -> Dataset:
|
_structures = set()
|
||||||
|
|
||||||
rows = []
|
for r in reactions:
|
||||||
|
for e in r.educts.all():
|
||||||
|
_structures.add(e)
|
||||||
|
|
||||||
if educts_only:
|
if not educts_only:
|
||||||
compounds = set()
|
for e in r.products:
|
||||||
for r in reactions:
|
_structures.add(e)
|
||||||
for e in r.educts:
|
|
||||||
compounds.add(e)
|
|
||||||
compounds = list(compounds)
|
|
||||||
|
|
||||||
total = len(compounds)
|
compounds = sorted(_structures, key=lambda x: x.url)
|
||||||
for i, c in enumerate(compounds):
|
|
||||||
row = []
|
triggered: Dict[str, Set[str]] = defaultdict(set)
|
||||||
print(f"{i + 1}/{total} - {c.smiles}")
|
observed: Set[str] = set()
|
||||||
for r in applicable_rules:
|
|
||||||
product_sets = r.rule.apply(c.smiles)
|
# 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:
|
if len(product_sets) == 0:
|
||||||
row.append([])
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
#triggered.add(f"{r.uuid} + {c.uuid}")
|
key = f"{rule.uuid} + {comp.uuid}"
|
||||||
reacts = set()
|
|
||||||
for ps in product_sets:
|
|
||||||
products = []
|
|
||||||
for p in ps:
|
|
||||||
products.append(Compound(FormatConverter.standardize(p)))
|
|
||||||
|
|
||||||
reacts.add(Reaction([c], products, r))
|
if key in triggered:
|
||||||
row.append(list(reacts))
|
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):
|
class SparseLabelECC(BaseEstimator, ClassifierMixin):
|
||||||
@ -166,8 +376,7 @@ class SparseLabelECC(BaseEstimator, ClassifierMixin):
|
|||||||
self.keep_columns_.append(col)
|
self.keep_columns_.append(col)
|
||||||
|
|
||||||
y_reduced = y[:, self.keep_columns_]
|
y_reduced = y[:, self.keep_columns_]
|
||||||
self.chains_ = [ClassifierChain(self.base_clf, order='random', random_state=i)
|
self.chains_ = [ClassifierChain(self.base_clf) for i in range(self.num_chains)]
|
||||||
for i in range(self.num_chains)]
|
|
||||||
|
|
||||||
for i, chain in enumerate(self.chains_):
|
for i, chain in enumerate(self.chains_):
|
||||||
print(f"{datetime.now()} fitting {i + 1}/{self.num_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)
|
return accuracy_score(y_true, y_pred, sample_weight=sample_weight)
|
||||||
|
|
||||||
|
|
||||||
class ApplicabilityDomain(PCA):
|
|
||||||
|
|
||||||
def __init__(self, n_components=5):
|
import copy
|
||||||
super().__init__(n_components=n_components)
|
|
||||||
|
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.scaler = StandardScaler()
|
||||||
|
self.num_neighbours = num_neighbours
|
||||||
self.min_vals = None
|
self.min_vals = None
|
||||||
self.max_vals = None
|
self.max_vals = None
|
||||||
|
|
||||||
def build(self, X):
|
def build(self, train_dataset: 'Dataset'):
|
||||||
# transform
|
# transform
|
||||||
X_scaled = self.scaler.fit_transform(X)
|
X_scaled = self.scaler.fit_transform(train_dataset.X())
|
||||||
# fit pca
|
# fit pca
|
||||||
X_pca = self.fit_transform(X_scaled)
|
X_pca = self.fit_transform(X_scaled)
|
||||||
|
|
||||||
self.max_vals = np.max(X_pca, axis=0)
|
self.max_vals = np.max(X_pca, axis=0)
|
||||||
self.min_vals = np.min(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_scaled = self.scaler.transform(instances)
|
||||||
instances_pca = self.transform(instances_scaled)
|
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 = []
|
is_applicable = []
|
||||||
for i, instance in enumerate(instances_pca):
|
for i, instance in enumerate(instances_pca):
|
||||||
@ -237,3 +589,17 @@ class ApplicabilityDomain(PCA):
|
|||||||
is_applicable[i] = False
|
is_applicable[i] = False
|
||||||
|
|
||||||
return is_applicable
|
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))
|
||||||
|
|||||||
4
uv.lock
generated
4
uv.lock
generated
@ -426,7 +426,7 @@ requires-dist = [
|
|||||||
{ name = "django-ninja", specifier = ">=1.4.1" },
|
{ name = "django-ninja", specifier = ">=1.4.1" },
|
||||||
{ name = "django-polymorphic", specifier = ">=4.1.0" },
|
{ name = "django-polymorphic", specifier = ">=4.1.0" },
|
||||||
{ name = "enviformer", git = "ssh://git@git.envipath.com/enviPath/enviformer.git?rev=v0.1.0" },
|
{ name = "enviformer", git = "ssh://git@git.envipath.com/enviPath/enviformer.git?rev=v0.1.0" },
|
||||||
{ name = "envipy-additional-information", git = "ssh://git@git.envipath.com/enviPath/enviPy-additional-information.git" },
|
{ name = "envipy-additional-information", git = "ssh://git@git.envipath.com/enviPath/enviPy-additional-information.git?rev=v0.1.4" },
|
||||||
{ name = "envipy-plugins", git = "ssh://git@git.envipath.com/enviPath/enviPy-plugins.git?rev=v0.1.0" },
|
{ name = "envipy-plugins", git = "ssh://git@git.envipath.com/enviPath/enviPy-plugins.git?rev=v0.1.0" },
|
||||||
{ name = "epam-indigo", specifier = ">=1.30.1" },
|
{ name = "epam-indigo", specifier = ">=1.30.1" },
|
||||||
{ name = "gunicorn", specifier = ">=23.0.0" },
|
{ name = "gunicorn", specifier = ">=23.0.0" },
|
||||||
@ -443,7 +443,7 @@ requires-dist = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "envipy-additional-information"
|
name = "envipy-additional-information"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = { git = "ssh://git@git.envipath.com/enviPath/enviPy-additional-information.git#4804b24b3479bed6108a49e4401bff8947c03cbd" }
|
source = { git = "ssh://git@git.envipath.com/enviPath/enviPy-additional-information.git?rev=v0.1.4#4da604090bf7cf1f3f552d69485472dbc623030a" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "pydantic" },
|
{ name = "pydantic" },
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user