11 Commits

Author SHA1 Message Date
ab43e0b5fe minor 2025-11-11 19:59:03 +01:00
91a52c254d minor 2025-11-11 19:57:50 +01:00
f7d88e4235 ..: 2025-11-11 19:26:23 +01:00
b627a3850b ... 2025-11-11 15:51:30 +01:00
1e5b8f5a63 ... 2025-11-11 15:47:03 +01:00
2a3e418f5b added missing migrations 2025-11-11 15:24:09 +01:00
393ec72523 add make migration step 2025-11-11 15:07:10 +01:00
932caa637b Merge branch 'develop' into enhancement/licenses 2025-11-12 03:00:54 +13:00
ac58d58479 Merge branch 'develop' into enhancement/licenses 2025-11-12 02:19:13 +13:00
94abfc90b5 comments 2025-11-10 14:57:31 +13:00
42b1e77fb9 refactor of license django model #119 2025-11-10 14:51:44 +13:00
11 changed files with 707 additions and 740 deletions

View File

@ -1,15 +1,15 @@
import csv import csv
import io import io
import logging import logging
from datetime import datetime
from typing import Any, Callable, List, Optional from typing import Any, Callable, List, Optional
from uuid import uuid4 from uuid import uuid4
from celery import shared_task from celery import shared_task
from celery.utils.functional import LRUCache from celery.utils.functional import LRUCache
from django.utils import timezone
from epdb.logic import SPathway from epdb.logic import SPathway
from epdb.models import Edge, EPModel, JobLog, Node, Package, Pathway, Rule, Setting, User from epdb.models import EPModel, JobLog, Node, Package, Pathway, Rule, Setting, User, Edge
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
ML_CACHE = LRUCache(3) # Cache the three most recent ML models to reduce load times. ML_CACHE = LRUCache(3) # Cache the three most recent ML models to reduce load times.
@ -29,7 +29,7 @@ def dispatch_eager(user: "User", job: Callable, *args, **kwargs):
log.task_id = uuid4() log.task_id = uuid4()
log.job_name = job.__name__ log.job_name = job.__name__
log.status = "SUCCESS" log.status = "SUCCESS"
log.done_at = timezone.now() log.done_at = datetime.now()
log.task_result = str(x) if x else None log.task_result = str(x) if x else None
log.save() log.save()

View File

@ -142,11 +142,6 @@ urlpatterns = [
v.package_pathway, v.package_pathway,
name="package pathway detail", name="package pathway detail",
), ),
re_path(
rf"^package/(?P<package_uuid>{UUID})/predict$",
v.package_predict_pathway,
name="package predict pathway",
),
# Pathway Nodes # Pathway Nodes
re_path( re_path(
rf"^package/(?P<package_uuid>{UUID})/pathway/(?P<pathway_uuid>{UUID})/node$", rf"^package/(?P<package_uuid>{UUID})/pathway/(?P<pathway_uuid>{UUID})/node$",

View File

@ -374,22 +374,6 @@ def predict_pathway(request):
return render(request, "predict_pathway.html", context) return render(request, "predict_pathway.html", context)
@package_permission_required()
def package_predict_pathway(request, package_uuid):
"""Package-specific predict pathway view."""
if request.method != "GET":
return HttpResponseNotAllowed(["GET"])
current_user = _anonymous_or_real(request)
current_package = PackageManager.get_package_by_id(current_user, package_uuid)
context = get_base_context(request)
context["title"] = f"enviPath - {current_package.name} - Predict Pathway"
context["meta"]["current_package"] = current_package
return render(request, "predict_pathway.html", context)
def packages(request): def packages(request):
current_user = _anonymous_or_real(request) current_user = _anonymous_or_real(request)
@ -971,12 +955,6 @@ def package_model(request, package_uuid, model_uuid):
] ]
dispatch(current_user, evaluate_model, current_model.pk, multigen, eval_package_ids) dispatch(current_user, evaluate_model, current_model.pk, multigen, eval_package_ids)
return redirect(current_model.url)
elif hidden == "retrain":
from .tasks import dispatch, retrain
dispatch(current_user, retrain, current_model.pk)
return redirect(current_model.url) return redirect(current_model.url)
else: else:
return HttpResponseBadRequest() return HttpResponseBadRequest()

View File

@ -1,9 +1,6 @@
{% if meta.can_edit %} {% if meta.can_edit %}
<li> <li>
<a <a href="{{ meta.server_url }}/predict">
href="{% if meta.current_package %}{{ meta.current_package.url }}/predict{% else %}{{ meta.server_url }}/predict{% endif %}" <span class="glyphicon glyphicon-plus"></span> New Pathway</a>
> </li>
<span class="glyphicon glyphicon-plus"></span> New Pathway</a
>
</li>
{% endif %} {% endif %}

View File

@ -178,23 +178,6 @@
} }
}); });
// Open search modal function
function openSearchModal() {
const searchModal = document.getElementById("search_modal");
if (searchModal) {
searchModal.showModal();
}
}
// Click handler for search badge
const searchTrigger = document.getElementById("search-trigger");
if (searchTrigger) {
searchTrigger.addEventListener("click", function (event) {
event.preventDefault();
openSearchModal();
});
}
// Global keyboard shortcut for search (Cmd+K on Mac, Ctrl+K on Windows/Linux) // Global keyboard shortcut for search (Cmd+K on Mac, Ctrl+K on Windows/Linux)
document.addEventListener("keydown", function (event) { document.addEventListener("keydown", function (event) {
// Check if user is typing in an input field // Check if user is typing in an input field
@ -215,7 +198,7 @@
if (isCorrectModifier && event.key === "k") { if (isCorrectModifier && event.key === "k") {
event.preventDefault(); event.preventDefault();
openSearchModal(); search_modal.showModal();
} }
}); });
</script> </script>

View File

@ -57,7 +57,7 @@
<div class="navbar-end"> <div class="navbar-end">
{% if not public_mode %} {% if not public_mode %}
<a id="search-trigger" role="button" class="cursor-pointer"> <a href="/search" role="button">
<div <div
class="flex items-center badge badge-dash space-x-1 bg-base-200 text-base-content/50 p-2 m-1" class="flex items-center badge badge-dash space-x-1 bg-base-200 text-base-content/50 p-2 m-1"
> >

View File

@ -2,86 +2,74 @@
{% load static %} {% load static %}
{% block main_content %} {% block main_content %}
<!-- Hero Section with Logo and Search --> <!-- Hero Section with Logo and Search -->
<section class="hero relative mx-auto h-fit w-full max-w-5xl shadow-none"> <section class="hero h-fit max-w-5xl w-full shadow-none mx-auto relative">
<div <div
class="hero from-primary-800 to-primary-600 min-h-[calc(100vh*0.4)] bg-gradient-to-br" class="hero min-h-[calc(100vh*0.4)] bg-gradient-to-br from-primary-800 to-primary-600"
style="background-image: url('{% static "/images/hero.png" %}'); background-size: cover; background-position: center;" style="background-image: url('{% static "/images/hero.png" %}'); background-size: cover; background-position: center;"
> >
<div class="hero-overlay"></div> <div class="hero-overlay"></div>
<!-- Predict Pathway text over the image --> <!-- Predict Pathway text over the image -->
<div class="absolute bottom-40 left-1/8 z-10 -translate-x-8"> <div class="absolute bottom-40 left-1/8 -translate-x-8 z-10">
<h2 class="text-base-100 text-left text-3xl text-shadow-lg"> <h2 class="text-3xl text-base-100 text-shadow-lg text-left">
Predict Your Pathway Predict Your Pathway
</h2> </h2>
</div> </div>
</div> </div>
</section> </section>
<div class="bg-base-200 mx-auto max-w-5xl shadow-md"> <div class="shadow-md max-w-5xl mx-auto bg-base-200">
<!-- Predict Pathway Section --> <!-- Predict Pathway Section -->
<div <div
class="relative z-20 mx-auto -mt-32 mb-10 w-full flex-col lg:flex-row-reverse" class="flex-col lg:flex-row-reverse w-full mx-auto -mt-32 relative z-20 mb-10 "
> >
<div <div
class="card bg-base-100 mx-auto w-3/4 shrink-0 shadow-xl transition-all duration-300 ease-in-out" class="card bg-base-100 shrink-0 shadow-xl w-3/4 mx-auto transition-all duration-300 ease-in-out"
> >
<div class="card-body"> <div class="card-body">
<div class="my-4 ml-8 flex h-fit flex-row items-center justify-start"> <!-- Input Mode Toggle - Fixed position outside fieldset -->
<div class="flex items-center gap-1"> <div class="flex flex-row justify-start items-center h-fit ml-8 my-4">
<label class="swap btn btn-ghost btn-sm p-1" title="Input Mode"> <div class="flex items-center gap-2">
<!-- <span class="text-sm text-neutral-500">Input Mode:</span> -->
<label class="toggle text-base-content toggle-md">
<input type="checkbox" /> <input type="checkbox" />
<span class="swap-on flex items-center gap-1"> <svg
<div aria-label="smiles mode"
class="bg-neutral/50 text-neutral-content flex items-center justify-center rounded-full p-1" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
class="size-5"
>
<g
stroke-linejoin="round"
stroke-linecap="round"
stroke-width="2"
fill="currentColor"
stroke="none"
> >
<svg <path
aria-label="smiles mode" fill-rule="evenodd"
xmlns="http://www.w3.org/2000/svg" d="M8 2.75A.75.75 0 0 1 8.75 2h7.5a.75.75 0 0 1 0 1.5h-3.215l-4.483 13h2.698a.75.75 0 0 1 0 1.5h-7.5a.75.75 0 0 1 0-1.5h3.215l4.483-13H8.75A.75.75 0 0 1 8 2.75Z"
viewBox="0 0 20 20" clip-rule="evenodd"
class="size-5" />
> </g>
<g </svg>
stroke-linejoin="round" <svg
stroke-linecap="round" aria-label="draw mode"
stroke-width="2" xmlns="http://www.w3.org/2000/svg"
fill="currentColor" viewBox="0 0 20 20"
stroke="none" fill="currentColor"
> stroke="none"
<path class="size-5"
fill-rule="evenodd" >
d="M8 2.75A.75.75 0 0 1 8.75 2h7.5a.75.75 0 0 1 0 1.5h-3.215l-4.483 13h2.698a.75.75 0 0 1 0 1.5h-7.5a.75.75 0 0 1 0-1.5h3.215l4.483-13H8.75A.75.75 0 0 1 8 2.75Z" <path
clip-rule="evenodd" d="m2.695 14.762-1.262 3.155a.5.5 0 0 0 .65.65l3.155-1.262a4 4 0 0 0 1.343-.886L17.5 5.501a2.121 2.121 0 0 0-3-3L3.58 13.419a4 4 0 0 0-.885 1.343Z"
/> />
</g> </svg>
</svg>
</div>
<span class="ext-xs">SMILES</span>
</span>
<span class="swap-off flex items-center gap-1">
<div
class="bg-neutral/50 text-neutral-content flex items-center justify-center rounded-full p-1"
>
<svg
aria-label="draw mode"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
stroke="none"
class="size-5"
>
<path
d="m2.695 14.762-1.262 3.155a.5.5 0 0 0 .65.65l3.155-1.262a4 4 0 0 0 1.343-.886L17.5 5.501a2.121 2.121 0 0 0-3-3L3.58 13.419a4 4 0 0 0-.885 1.343Z"
/>
</svg>
</div>
<span class="text-base/50 text-xs">Draw</span>
</span>
</label> </label>
</div> </div>
</div> </div>
<fieldset <fieldset
class="fieldset overflow-hidden transition-all duration-300 ease-in-out" class="fieldset transition-all duration-300 ease-in-out overflow-hidden"
> >
<form <form
id="index-form" id="index-form"
@ -91,29 +79,29 @@
{% csrf_token %} {% csrf_token %}
<div <div
id="text-input-container" id="text-input-container"
class="scale-100 transform opacity-100 transition-all duration-300 ease-in-out" class="transition-all duration-300 ease-in-out opacity-100 transform scale-100"
> >
<div class="join mx-auto w-full"> <div class="join w-full mx-auto">
<input <input
type="text" type="text"
id="index-form-text-input" id="index-form-text-input"
placeholder="canonical SMILES string" placeholder="canonical SMILES string"
class="input input-md join-item grow" class="input grow input-md join-item"
/> />
<button class="btn btn-neutral join-item">Predict!</button> <button class="btn btn-neutral join-item">Predict!</button>
</div> </div>
<div class="label relative mt-1 w-full"> <div class="label relative w-full mt-1">
<div class="flex gap-2"> <div class="flex gap-2">
<a <a
href="#" href="#"
class="example-link hover:text-primary cursor-pointer" class="example-link cursor-pointer hover:text-primary"
data-smiles="CN1C=NC2=C1C(=O)N(C(=O)N2C)C" data-smiles="CN1C=NC2=C1C(=O)N(C(=O)N2C)C"
title="load example" title="load example"
>Caffeine</a >Caffeine</a
> >
<a <a
href="#" href="#"
class="example-link hover:text-primary cursor-pointer" class="example-link cursor-pointer hover:text-primary"
data-smiles="CC(C)CC1=CC=C(C=C1)C(C)C(=O)O" data-smiles="CC(C)CC1=CC=C(C=C1)C(C)C(=O)O"
title="load example" title="load example"
>Ibuprofen</a >Ibuprofen</a
@ -126,7 +114,7 @@
</div> </div>
<div <div
id="ketcher-container" id="ketcher-container"
class="hidden w-full scale-95 transform opacity-0 transition-all duration-300 ease-in-out" class="hidden w-full transition-all duration-300 ease-in-out opacity-0 transform scale-95"
> >
<iframe <iframe
id="index-ketcher" id="index-ketcher"
@ -136,13 +124,11 @@
class="rounded-lg" class="rounded-lg"
></iframe> ></iframe>
<button <button
class="btn btn-lg bg-primary-950 text-primary-50 join-item mt-2 w-full" class="btn btn-lg bg-primary-950 text-primary-50 join-item w-full mt-2"
> >
Predict! Predict!
</button> </button>
<div class="mt-1 flex w-full justify-end"> <a class="label mx-auto w-full mt-1" href="#">Advanced</a>
<a class="label justify-end" href="/predict">Advanced</a>
</div>
</div> </div>
<input <input
type="hidden" type="hidden"
@ -164,18 +150,18 @@
</div> </div>
<!-- Community News Section --> <!-- Community News Section -->
<section class="bg-base-200 z-10 mx-8 py-16"> <section class="py-16 bg-base-200 z-10 mx-8">
<div class="mx-auto max-w-7xl px-4"> <div class="max-w-7xl mx-auto px-4">
<h2 class="h2 mb-8 text-left font-bold">Community Updates</h2> <h2 class="h2 font-bold text-left mb-8">Community Updates</h2>
<div id="community-news-container" class="flex justify-center gap-4"> <div id="community-news-container" class="flex gap-4 justify-center">
<!-- News cards will be populated here --> <!-- News cards will be populated here -->
<div id="loading" class="flex w-full justify-center"> <div id="loading" class="flex justify-center w-full">
<span class="loading loading-spinner loading-lg"></span> <span class="loading loading-spinner loading-lg"></span>
</div> </div>
</div> </div>
<div class="mt-6 text-right"> <div class="text-right mt-6">
<a <a
href="https://community.envipath.org/c/announcements/10" href="https://community.envipath.org/c/announcements/10"
target="_blank" target="_blank"
@ -191,18 +177,18 @@
</section> </section>
<!-- Mission Statement Section --> <!-- Mission Statement Section -->
<section class="from-base-200 to-base-100 bg-gradient-to-b py-16"> <section class="py-16 from-base-200 to-base-100 bg-gradient-to-b">
<div class="mx-auto px-8 md:px-12"> <div class="mx-auto px-8 md:px-12">
<div class="flex flex-row gap-4"> <div class="flex flex-row gap-4">
<div class="w-1/3"> <div class="w-1/3">
<img <img
src="{% static "/images/ep-rule-artwork.png" %}" src="{% static "/images/ep-rule-artwork.png" %}"
alt="rule-based iterative tree greneration" alt="rule-based iterative tree greneration"
class="h-full w-full object-contain" class="w-full h-full object-contain"
/> />
</div> </div>
<div class="mr-8 w-2/3 space-y-4 text-left"> <div class="space-y-4 text-left w-2/3 mr-8">
<h2 class="h2 mb-8 font-bold">About enviPath</h2> <h2 class="h2 font-bold mb-8">About enviPath</h2>
<p class=""> <p class="">
enviPath is a database and prediction system for the microbial enviPath is a database and prediction system for the microbial
biotransformation of organic environmental contaminants. The biotransformation of organic environmental contaminants. The
@ -215,7 +201,7 @@
products. Explore our tools and contribute to advancing products. Explore our tools and contribute to advancing
environmental biotransformation research. environmental biotransformation research.
</p> </p>
<div class="float-right flex flex-row gap-4"> <div class="flex flex-row gap-4 float-right">
<a href="/about" class="btn btn-ghost-neutral">Read More</a> <a href="/about" class="btn btn-ghost-neutral">Read More</a>
<a href="/about" class="btn btn-neutral">Publications</a> <a href="/about" class="btn btn-neutral">Publications</a>
</div> </div>
@ -225,7 +211,7 @@
</section> </section>
<!-- Partners Section --> <!-- Partners Section -->
<section class="bg-base-100 py-14 sm:py-12"> <section class="py-14 sm:py-12 bg-base-100">
<div class="mx-auto px-6 lg:px-8"> <div class="mx-auto px-6 lg:px-8">
<div class="divider"> <div class="divider">
<h2 class="text-center text-lg/8 font-semibold">Backed by Science</h2> <h2 class="text-center text-lg/8 font-semibold">Backed by Science</h2>
@ -236,12 +222,12 @@
<img <img
src="{% static "/images/uoa-logo-small.png" %}" src="{% static "/images/uoa-logo-small.png" %}"
alt="The University of Auckland" alt="The University of Auckland"
class="max-h-20 w-full object-contain lg:col-span-1" class=" max-h-20 w-full object-contain lg:col-span-1"
/> />
<img <img
src="{% static "/images/logo-eawag.svg" %}" src="{% static "/images/logo-eawag.svg" %}"
alt="Eawag" alt="Eawag"
class="max-h-12 w-full object-contain lg:col-span-1" class=" max-h-12 w-full object-contain lg:col-span-1"
/> />
<img <img
src="{% static "/images/uzh-logo.svg" %}" src="{% static "/images/uzh-logo.svg" %}"

View File

@ -1,55 +1,43 @@
<div <div class="modal fade" tabindex="-1" id="retrain_model_modal" role="dialog" aria-labelledby="retrain_model_modal"
class="modal fade" aria-hidden="true">
tabindex="-1" <div class="modal-dialog modal-lg">
id="retrain_model_modal" <div class="modal-content">
role="dialog" <div class="modal-header">
aria-labelledby="retrain_model_modal" <button type="button" class="close" data-dismiss="modal">
aria-hidden="true" <span aria-hidden="true">&times;</span>
> <span class="sr-only">Close</span>
<div class="modal-dialog modal-lg"> </button>
<div class="modal-content"> <h4 class="modal-title">Retrain Model</h4>
<div class="modal-header"> </div>
<button type="button" class="close" data-dismiss="modal"> <div class="modal-body">
<span aria-hidden="true">&times;</span> <form id="retrain_model_form" accept-charset="UTF-8" action="{{ meta.current_package.url }}/model"
<span class="sr-only">Close</span> data-remote="true" method="post">
</button> <div class="jumbotron">
<h4 class="modal-title">Retrain Model</h4> To reflect changes in the rule or data packages, you can use the "Retrain" button,
</div> to let the model reflect the changes without creating a new model.
<div class="modal-body"> While the model is retraining, it will be unavailable for prediction.
<form </div>
id="retrain_model_form" {% csrf_token %}
accept-charset="UTF-8" <input type="hidden" name="action" value="retrain">
action="{{ meta.current_object.url }}" </form>
data-remote="true" </div>
method="post" <div class="modal-footer">
> <a id="retrain_model_form_submit" class="btn btn-primary" href="#">Retrain</a>
<div class="jumbotron"> <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
To reflect changes in the rule or data packages, you can use the </div>
"Retrain" button, to let the model reflect the changes without </div>
creating a new model. While the model is retraining, it will be
unavailable for prediction.
</div>
{% csrf_token %}
<input type="hidden" name="hidden" value="retrain" />
</form>
</div>
<div class="modal-footer">
<a id="retrain_model_form_submit" class="btn btn-primary" href="#"
>Retrain</a
>
<button type="button" class="btn btn-default" data-dismiss="modal">
Cancel
</button>
</div>
</div> </div>
</div>
</div> </div>
<script> <script>
$(function () {
$("#retrain_model_form_submit").on("click", function (e) { $(function () {
e.preventDefault();
$("#retrain_model_form").submit(); $('#retrain_model_form_submit').on('click', function (e) {
e.preventDefault();
$('#retrain_model_form').submit();
});
}); });
});
</script> </script>

File diff suppressed because it is too large Load Diff

View File

@ -2,13 +2,7 @@
{% load static %} {% load static %}
{% block content %} {% block content %}
<div class="mx-auto w-full p-8"> <div class="mx-auto w-full p-8">
<h1 class="h1 mb-4 text-3xl font-bold"> <h1 class="h1 mb-4 text-3xl font-bold">Predict a Pathway</h1>
Predict a Pathway
<span class="text-base-content/50 text-xs"
>in <strong>{{ meta.current_package.name|safe }}</strong>
</span>
</h1>
<form <form
id="predict_form" id="predict_form"

197
templates/search.html Normal file
View File

@ -0,0 +1,197 @@
{% extends "framework.html" %}
{% load static %}
{% block content %}
<div id=searchContent>
<div id="packSelector">
<label>Select Packages</label><br>
<select id="selPackages" name="selPackages" data-actions-box='true' class="selPackages" multiple
data-width='100%'>
{% if unreviewed_objects %}
<option disabled>Reviewed Packages</option>
{% endif %}
{% for obj in reviewed_objects %}
<option value="{{ obj.url }}" selected>{{ obj.name|safe }}</option>
{% endfor %}
{% if unreviewed_objects %}
<option disabled>Unreviewed Packages</option>
{% endif %}
{% for obj in unreviewed_objects %}
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
{% endfor %}
</select>
</div>
<p></p>
<div>
<label>Search Term</label><br>
<div class="input-group" id="index-form-bar">
<input type="text" class="form-control" id='searchbar' placeholder="Benfuracarb">
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
id="mode-button"
aria-haspopup="true" aria-expanded="false">Text <span class="caret"></span></button>
<ul class="dropdown-menu">
<li class="dropdown-header">Text</li>
<li><a id="dropdown-predict-text-text">Text</a></li>
<li class="dropdown-header">SMILES</li>
<li><a id="dropdown-search-smiles-default" data-toggle="tooltip">Default</a></li>
<li><a id="dropdown-search-smiles-canonical">Canonical</a></li>
<li><a id="dropdown-search-smiles-exact">Exact</a></li>
<li class="dropdown-header">InChI</li>
<li><a id="dropdown-search-inchi-inchikey">InChIKey</a></li>
</ul>
<button class="btn" style="background-color:#222222;color:#9d9d9d" type="button" id="search-button">
Go!
</button>
</div>
</div>
<p></p>
<div id="results"></div>
<p></p>
<div id="loading"></div>
</div>
</div>
<script>
function modeDropdownClicked() {
var suffix = ' <span class="caret"></span>';
var dropdownVal = $(this).text();
$('#mode-button').html(dropdownVal + suffix);
}
function handleSearchResponse(id, data) {
content = `
<div class='panel-group' id='search-accordion'>
<div class='panel panel-default'>
<div class='panel-heading' id='headingPanel' style='font-size:2rem;height: 46px'>
Results
</div>
<div id='descDiv'></div>
</div>`;
function makeContent(objs) {
links = "";
for (idx in objs) {
obj = objs[idx];
links += `<a class='list-group-item' href='${obj.url}'>${obj.name}</a>`
}
return links;
}
allEmpty = true;
for (key in data) {
if (key === 'searchterm') {
continue;
}
if (data[key].length < 1) {
continue;
}
allEmpty = false;
content += `
<div class='panel panel-default panel-heading list-group-item' style='background-color:silver'>
<h4 class='panel-title'>
<a id='${key}_link' data-toggle='collapse' data-parent='#search-accordion' href='#${key}_panel'>
${key}
</a>
</h4>
</div>
<div id='${key}_panel' class='panel-collapse collapse in'>
<div class='panel-body list-group-item'>
${makeContent(data[key])}
</div>
</div>
`;
}
if (allEmpty) {
$('#' + id).append('<div class="alert alert-danger" role="alert"><p>' + "No results..." + '</p></div>');
} else {
$('#' + id).append(content);
}
}
function search(e) {
e.preventDefault();
query = $("#searchbar").val()
if (!query) {
// Nothing to search...
console.log("Search phrase empty, won't do search")
return;
}
var selPacks = [];
$("#selPackages :selected").each(function () {
var id = this.value;
selPacks.push(id);
});
if (selPacks.length < 1) {
console.log("No package selected, won't do search")
return;
}
var mode = $('#mode-button').text().trim().toLowerCase();
var par = {};
par['packages'] = selPacks;
par['search'] = query;
par['mode'] = mode;
console.log(par);
var queryString = $.param(par, true);
makeLoadingGif("#loading", "{% static '/images/wait.gif' %}");
$("#results").empty();
$.getJSON("{{ SERVER_BASE }}/search?" + queryString, function (result) {
handleSearchResponse("results", result);
$("#loading").empty();
}).fail(function (d) {
$("#loading").empty();
console.log(d.responseText);
handleError(JSON.parse(d.responseText));
});
}
$(function () {
tooltips = {
'dropdown-predict-text-text': 'The inserted pattern will be searched on all enviPath object names and descriptions',
'dropdown-search-smiles-default': 'Search by SMILES, stereochemistry and charge are ignored',
'dropdown-search-smiles-canonical': 'Search by SMILES, stereochemistry is ignored but charge is preserved',
'dropdown-search-smiles-exact': 'Search by SMILES, exact match for stereochemistry and charge',
'dropdown-search-inchi-inchikey': 'Search by InChIKey',
}
Object.keys(tooltips).forEach(key => {
$('#' + key).tooltip({
placement: "top",
title: tooltips[key]
});
$('#' + key).on('click', modeDropdownClicked);
});
$("#selPackages").selectpicker();
$("#search-button").on("click", search);
$("#searchbar").on("keydown", function (e) {
if (e.key === "Enter") {
e.preventDefault();
search(e);
}
});
});
{% if search_result %}
$('#searchbar').val('{{ search_result.searchterm }}')
handleSearchResponse("results", {{ search_result|safe }});
{% endif %}
</script>
{% endblock content %}