Merge branch 'develop' into enhancement/licenses

This commit is contained in:
2025-11-12 03:00:54 +13:00
9 changed files with 939 additions and 530 deletions

View File

@ -48,6 +48,7 @@ urlpatterns = [
re_path(r"^user$", v.users, name="users"),
re_path(r"^group$", v.groups, name="groups"),
re_path(r"^search$", v.search, name="search"),
re_path(r"^predict$", v.predict_pathway, name="predict_pathway"),
# User Detail
re_path(rf"^user/(?P<user_uuid>{UUID})", v.user, name="user"),
# Group Detail

View File

@ -362,6 +362,18 @@ def index(request):
return render(request, "index/index.html", context)
def predict_pathway(request):
"""Top-level predict pathway view using user's default package."""
if request.method != "GET":
return HttpResponseNotAllowed(["GET"])
context = get_base_context(request)
context["title"] = "enviPath - Predict Pathway"
context["meta"]["current_package"] = context["meta"]["user"].default_package
return render(request, "predict_pathway.html", context)
def packages(request):
current_user = _anonymous_or_real(request)
@ -1288,7 +1300,7 @@ def package_compound_structures(request, package_uuid, compound_uuid):
elif request.method == "POST":
structure_name = request.POST.get("structure-name")
structure_smiles = request.POST.get("structure-smiles").strip()
structure_smiles = request.POST.get("structure-smiles")
structure_description = request.POST.get("structure-description")
try:
@ -1470,11 +1482,11 @@ def package_rules(request, package_uuid):
# Obtain parameters as required by rule type
if rule_type == "SimpleAmbitRule":
params["smirks"] = request.POST.get("rule-smirks").strip()
params["smirks"] = request.POST.get("rule-smirks")
params["reactant_filter_smarts"] = request.POST.get("rule-reactant-smarts")
params["product_filter_smarts"] = request.POST.get("rule-product-smarts")
elif rule_type == "SimpleRDKitRule":
params["reaction_smarts"] = request.POST.get("rule-reaction-smarts").strip()
params["reaction_smarts"] = request.POST.get("rule-reaction-smarts")
elif rule_type == "ParallelRule":
pass
elif rule_type == "SequentialRule":
@ -1671,7 +1683,7 @@ def package_reactions(request, package_uuid):
elif request.method == "POST":
reaction_name = request.POST.get("reaction-name")
reaction_description = request.POST.get("reaction-description")
reactions_smirks = request.POST.get("reaction-smirks").strip()
reactions_smirks = request.POST.get("reaction-smirks")
educts = reactions_smirks.split(">>")[0].split(".")
products = reactions_smirks.split(">>")[1].split(".")

View File

@ -1,6 +1,6 @@
{% if meta.can_edit %}
<li>
<a role="button" data-toggle="modal" data-target="#predict_modal">
<a href="{{ meta.server_url }}/predict">
<span class="glyphicon glyphicon-plus"></span> New Pathway</a>
</li>
{% endif %}

View File

@ -1,11 +1,15 @@
<!DOCTYPE html>
<!doctype html>
<html data-theme="envipath">
{% load static %}
<head>
{% load static %}
<head>
<title>{{ title }}</title>
<meta name="csrf-token" content="{{ csrf_token }}">
<meta name="csrf-token" content="{{ csrf_token }}" />
{# Favicon #}
<link rel="shortcut icon" type="image/png" href="{% static 'images/favicon.ico' %}"/>
<link
rel="shortcut icon"
type="image/png"
href="{% static 'images/favicon.ico' %}"
/>
{# Tailwind CSS disabled for legacy Bootstrap framework #}
{# Pages using this framework will be migrated to framework_modern.html incrementally #}
@ -13,36 +17,45 @@
{# Legacy Bootstrap 3.3.7 - scoped to .legacy-bootstrap #}
<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"
/>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.templates/beta1/jquery.tmpl.js"></script>
<link href="https://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.7.3/css/bootstrap-select.min.css">
<link
href="https://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.7.3/css/bootstrap-select.min.css"
/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.7.3/js/bootstrap-select.min.js"></script>
<script src="https://community.envipath.org/javascripts/embed-topics.js"></script>
<!-- CDN END -->
{# Bootstrap compatibility styles #}
<style>
/* Ensure proper viewport behavior */
html, body {
height: 100%; /* ensure body fills viewport */
overflow-x: hidden; /* prevent horizontal scroll */
}
/* Ensure proper viewport behavior */
html,
body {
height: 100%; /* ensure body fills viewport */
overflow-x: hidden; /* prevent horizontal scroll */
}
</style>
<script>
const csrftoken = document.querySelector('[name=csrf-token]').content;
const csrftoken = document.querySelector("[name=csrf-token]").content;
// Setup CSRF header for all jQuery AJAX requests
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type)) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
// Setup CSRF header for all jQuery AJAX requests
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type)) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
},
});
</script>
<!-- {# C3 CSS #}-->
@ -50,40 +63,44 @@
<!-- {# EP CSS #}-->
<!-- <link id="css-pps_white_general" href="{% static 'css/epp.css' %}" rel="stylesheet" type="text/css"/>-->
{# General EP JS #}
<script src="{% static 'js/pps.js' %}"></script>
{# Modal Steps for Stepwise Modal Wizards #}
<script src="{% static 'js/jquery-bootstrap-modal-steps.js' %}"></script>
{% if not debug %}
<!-- Matomo -->
<script>
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function () {
var u = "//matomo.envipath.com/";
_paq.push(['setTrackerUrl', u + 'matomo.php']);
_paq.push(['setSiteId', '{{ meta.site_id }}']);
var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
g.async = true;
g.src = u + 'matomo.js';
s.parentNode.insertBefore(g, s);
})();
</script>
<!-- End Matomo Code -->
<!-- Matomo -->
<script>
var _paq = (window._paq = window._paq || []);
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(["trackPageView"]);
_paq.push(["enableLinkTracking"]);
(function () {
var u = "//matomo.envipath.com/";
_paq.push(["setTrackerUrl", u + "matomo.php"]);
_paq.push(["setSiteId", "{{ meta.site_id }}"]);
var d = document,
g = d.createElement("script"),
s = d.getElementsByTagName("script")[0];
g.async = true;
g.src = u + "matomo.js";
s.parentNode.insertBefore(g, s);
})();
</script>
<!-- End Matomo Code -->
{% endif %}
</head>
<body>
<!-- Legacy Bootstrap navbar - isolated from Tailwind -->
<div class="legacy-bootstrap">
<nav class="navbar navbar-default navbar-inverse" style="border-radius:0px;" role="navigation">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header navbar-header-framework">
</head>
<body>
<!-- Legacy Bootstrap navbar - isolated from Tailwind -->
<div class="legacy-bootstrap">
<nav
class="navbar navbar-default navbar-inverse"
style="border-radius:0px;"
role="navigation"
>
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header navbar-header-framework">
<!-- <button type="button" class="navbar-toggle navbar-toggle-framework" data-toggle="collapse"-->
<!-- data-target="#navbarCollapse">-->
<!-- <span class="sr-only">Toggle navigation</span>-->
@ -91,203 +108,315 @@
<!-- <span class="icon-bar"></span>-->
<!-- <span class="icon-bar"></span>-->
<!-- </button>-->
<a id="pictureLink" href="{{ meta.server_url }}" class="navbar-brand">
<img id="image-logo-short-white.svg" src='{% static "/images/logo-short-white.svg" %}' width="100"
alt="enviPath">
<a
id="pictureLink"
href="{{ meta.server_url }}"
class="navbar-brand"
>
<img
id="image-logo-short-white.svg"
src="{% static "/images/logo-short-white.svg" %}"
width="100"
alt="enviPath"
/>
</a>
</div>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse collapse-framework navbar-collapse-framework" id="navbarCollapse">
<!-- Collect the nav links, forms, and other content for toggling -->
<div
class="collapse navbar-collapse collapse-framework navbar-collapse-framework"
id="navbarCollapse"
>
<ul class="nav navbar-nav navbar-nav-framework">
<li>
<a href="{{ meta.server_url }}/predict"> Predict Pathway </a>
</li>
{# <li class="dropdown">#}
{# <a data-toggle="dropdown" class="dropdown-toggle" href="#">Predict Pathway<b class="caret"></b></a>#}
{# <ul role="menu" class="dropdown-menu">#}
{# <li>#}
{# <a class="button" data-toggle="modal" data-target="#predict_modal">#}
{# <i class=" glyphicon glyphicon-tag"></i> Predict Pathway#}
{# </a>#}
{# </li>#}
{# <li>#}
{# <a class="button" data-toggle="modal" data-target="#batch_predict_modal">#}
{# <i class=" glyphicon glyphicon-tags"></i> Batch Prediction#}
{# </a>#}
{# </li>#}
{# </ul>#}
{# </li>#}
<li>
<a href="{{ meta.server_url }}/package" id="packageLink"
>Package</a
>
</li>
<li>
<a href="{{ meta.server_url }}/search" id="searchLink"
>Search</a
>
</li>
<li>
<a href="{{ meta.server_url }}/model" id="modelLink"
>Modelling</a
>
</li>
<li class="dropdown">
<a data-toggle="dropdown" class="dropdown-toggle" href="#"
>Browse Data<b class="caret"></b
></a>
<ul role="menu" class="dropdown-menu">
<li>
<a href="{{ meta.server_url }}/pathway" id="pathwayLink"
>Pathway</a
>
</li>
<li>
<a href="{{ meta.server_url }}/rule" id="ruleLink">Rule</a>
</li>
<li>
<a href="{{ meta.server_url }}/compound" id="compoundLink"
>Compound</a
>
</li>
<li>
<a href="{{ meta.server_url }}/reaction" id="reactionLink"
>Reaction</a
>
</li>
<li>
<a
href="{{ meta.server_url }}/model"
id="relative-reasoningLink"
>Model</a
>
</li>
<li>
<a href="{{ meta.server_url }}/scenario" id="scenarioLink"
>Scenario</a
>
</li>
{# <li><a href="{{ meta.server_url }}/setting" id="settingLink">Setting</a></li>#}
{# <li><a href="{{ meta.server_url }}/user" id="userLink">User</a></li>#}
{# <li><a href="{{ meta.server_url }}/group" id="groupLink">Group</a></li>#}
</ul>
</li>
</ul>
<ul
class="nav navbar-nav navbar-right navbar-nav-framework navbar-right-framework"
>
<li>
<a href="https://community.envipath.org/" id="communityLink"
>Community</a
>
</li>
<li class="dropdown">
<a data-toggle="dropdown" class="dropdown-toggle" href="#"
>Info <b class="caret"></b
></a>
<ul role="menu" class="dropdown-menu">
<!--<li><a href="{{ meta.server_url }}/funding" id="fundingLink">Funding</a></li>-->
<li>
<a
href="https://community.envipath.org/t/envipath-license/109"
id="licenceLink"
>Licences</a
>
</li>
<li class="divider"></li>
<li>
<a
target="_blank"
href="https://wiki.envipath.org/"
id="wikiLink"
>Documentation Wiki</a
>
</li>
<li>
<a
href="#"
id="citeButton"
data-toggle="modal"
data-target="#citemodal"
>How to cite enviPath</a
>
</li>
<li class="divider"></li>
<li><a>Version: {{ meta.version }}</a></li>
</ul>
</li>
{% if meta.user.username == 'anonymous' %}
<li>
<a href="#" data-toggle="modal" data-target="#predict_modal">
Predict Pathway
</a>
<a
href="{% url 'login' %}"
id="loginButton"
style="margin-right:10px"
>Login</a
>
</li>
{# <li class="dropdown">#}
{# <a data-toggle="dropdown" class="dropdown-toggle" href="#">Predict Pathway<b class="caret"></b></a>#}
{# <ul role="menu" class="dropdown-menu">#}
{# <li>#}
{# <a class="button" data-toggle="modal" data-target="#predict_modal">#}
{# <i class=" glyphicon glyphicon-tag"></i> Predict Pathway#}
{# </a>#}
{# </li>#}
{# <li>#}
{# <a class="button" data-toggle="modal" data-target="#batch_predict_modal">#}
{# <i class=" glyphicon glyphicon-tags"></i> Batch Prediction#}
{# </a>#}
{# </li>#}
{# </ul>#}
{# </li>#}
<li><a href="{{ meta.server_url }}/package" id="packageLink">Package</a></li>
<li><a href="{{ meta.server_url }}/search" id="searchLink">Search</a></li>
<li><a href="{{ meta.server_url }}/model" id="modelLink">Modelling</a></li>
{% else %}
<li class="dropdown">
<a data-toggle="dropdown" class="dropdown-toggle" href="#">Browse Data<b class="caret"></b></a>
<ul role="menu" class="dropdown-menu">
<li><a href="{{ meta.server_url }}/pathway" id="pathwayLink">Pathway</a></li>
<li><a href="{{ meta.server_url }}/rule" id="ruleLink">Rule</a></li>
<li><a href="{{ meta.server_url }}/compound" id="compoundLink">Compound</a></li>
<li><a href="{{ meta.server_url }}/reaction" id="reactionLink">Reaction</a></li>
<li><a href="{{ meta.server_url }}/model" id="relative-reasoningLink">Model</a></li>
<li><a href="{{ meta.server_url }}/scenario" id="scenarioLink">Scenario</a></li>
{# <li><a href="{{ meta.server_url }}/setting" id="settingLink">Setting</a></li>#}
{# <li><a href="{{ meta.server_url }}/user" id="userLink">User</a></li>#}
{# <li><a href="{{ meta.server_url }}/group" id="groupLink">Group</a></li>#}
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right navbar-nav-framework navbar-right-framework">
<li><a href="https://community.envipath.org/" id="communityLink">Community</a></li>
<li class="dropdown">
<a data-toggle="dropdown" class="dropdown-toggle" href="#">Info <b class="caret"></b></a>
<ul role="menu" class="dropdown-menu">
<!--<li><a href="{{ meta.server_url }}/funding" id="fundingLink">Funding</a></li>-->
<li><a href="https://community.envipath.org/t/envipath-license/109" id="licenceLink">Licences</a></li>
<li class="divider"></li>
<li><a target="_blank" href="https://wiki.envipath.org/" id="wikiLink">Documentation Wiki</a>
</li>
<li><a href="#" id="citeButton" data-toggle="modal" data-target="#citemodal">How to cite
enviPath</a></li>
<li class="divider"></li>
<li><a>Version: {{ meta.version }}</a></li>
</ul>
</li>
{% if meta.user.username == 'anonymous' %}
<a
data-toggle="dropdown"
id="loggedInButton"
class="dropdown-toggle"
id="logedInButton"
href="#"
>
<div id="username">
{{ user.username }}<b class="caret"></b>
</div>
</a>
<ul role="menu" class="dropdown-menu">
<li>
<a href="{% url 'login' %}" id="loginButton" style="margin-right:10px">Login</a>
<a href="{{ meta.user.url }}" id="accountbutton"
>My Account</a
>
</li>
{% else %}
<li class="dropdown">
<a data-toggle="dropdown" id="loggedInButton" class="dropdown-toggle" id="logedInButton"
href="#">
<div id="username">
{{ user.username }}<b class="caret"></b>
</div>
</a>
<ul role="menu" class="dropdown-menu">
<li>
<a href="{{ meta.user.url }}" id="accountbutton">My Account</a>
</li>
<li class="divider"></li>
<form class="navbar-form navbar-left navbar-left-framework" role="logout"
action="{% url 'logout' %}" method="post">
{% csrf_token %}
<div class="form-group">
<input type="hidden" name="logout" value="true">
</div>
<button type="submit" class="btn btn-default">Logout</button>
</form>
</ul>
</li>
{% endif %}
<li class="divider"></li>
<form
class="navbar-form navbar-left navbar-left-framework"
role="logout"
action="{% url 'logout' %}"
method="post"
>
{% csrf_token %}
<div class="form-group">
<input type="hidden" name="logout" value="true" />
</div>
<button type="submit" class="btn btn-default">
Logout
</button>
</form>
</ul>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
</div>
</nav>
</div>
<!-- End legacy Bootstrap navbar -->
<!-- End legacy Bootstrap navbar -->
<div id="docContent" class="content container">
{% if breadcrumbs %}
<div id="docContent" class="content container">
{% if breadcrumbs %}
<div id="bread">
<ol class="breadcrumb">
{% for elem in breadcrumbs %}
{% for name, url in elem.items %}
{% if forloop.parentloop.last %}
<li class="active">{{ name }}</li>
{% else %}
<li>
<a href="{{ url }}">{{ name }}</a>
</li>
{% endif %}
{% endfor %}
{% endfor %}
</ol>
<ol class="breadcrumb">
{% for elem in breadcrumbs %}
{% for name, url in elem.items %}
{% if forloop.parentloop.last %}
<li class="active">{{ name }}</li>
{% else %}
<li>
<a href="{{ url }}">{{ name }}</a>
</li>
{% endif %}
{% endfor %}
{% endfor %}
</ol>
</div>
{% endif %}
{% if message %}
<div id="message">
{{ message }}
</div>
{% endif %}
{% block content %}
{% endblock content %}
{% if meta.url_contains_package and meta.current_package.license %}
{% endif %}
{% if message %}
<div id="message">{{ message }}</div>
{% endif %}
{% block 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
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 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 id="license" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
<a target="_blank" href="{{ meta.current_package.license.link }}">
<img src="{{ meta.current_package.license.image_link }}" />
</a>
</div>
</div>
</div>
{% endif %}
</div>
<!-- FOOTER - Legacy Bootstrap -->
<div class="legacy-bootstrap">
<div class="container text-center">
<hr/>
<div class="row">
<div class="col-lg-12">
<ul class="nav nav-pills nav-justified">
<li>
<a href="http://ml.auckland.ac.nz" target="_blank">
<img id="image-uoalogo" height="60" src='{% static "/images/UoA-Logo-Primary-RGB-Small.png" %}'
alt="The Univserity of Auckland"/>
</a>
</li>
<li>
<a href="https://eawag.ch" target="_blank">
<img id="image-ealogo" height="60" src='{% static "/images/ealogo.gif" %}' alt="Eawag"/>
</a>
</li>
<li>
<a href="https://www.uzh.ch/" target="_blank">
<img id="image-ufzlogo" height="60" src='{% static "/images/uzh-logo.svg" %}'
alt="University of Zurich"/>
</a>
</li>
</ul>
</div>
{% endif %}
</div>
</div>
<div class="row">
<div class="col-lg-12">
<ul class="nav nav-pills nav-justified">
<!-- FOOTER - Legacy Bootstrap -->
<div class="legacy-bootstrap">
<div class="container text-center">
<hr />
<div class="row">
<div class="col-lg-12">
<ul class="nav nav-pills nav-justified">
<li>
<a href="http://ml.auckland.ac.nz" target="_blank">
<img
id="image-uoalogo"
height="60"
src="{% static "/images/UoA-Logo-Primary-RGB-Small.png" %}"
alt="The Univserity of Auckland"
/>
</a>
</li>
<li>
<a href="https://eawag.ch" target="_blank">
<img
id="image-ealogo"
height="60"
src="{% static "/images/ealogo.gif" %}"
alt="Eawag"
/>
</a>
</li>
<li>
<a href="https://www.uzh.ch/" target="_blank">
<img
id="image-ufzlogo"
height="60"
src="{% static "/images/uzh-logo.svg" %}"
alt="University of Zurich"
/>
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<ul class="nav nav-pills nav-justified">
<!-- <li><a href="https://envipath.com/imprint/" target="_blank">Impressum/Imprint</a></li>-->
<li><a href="mailto:admin@envipath.org" target="_blank">Contact</a></li>
<li>
<a href="mailto:admin@envipath.org" target="_blank">Contact</a>
</li>
<!-- <li><a href="http://envipath.com" target="_blank"> enviPath UG (haftungsbeschr&auml;nkt) &amp; Co. KG &copy;-->
<!-- {{ YEAR }}</a></li>-->
</ul>
</ul>
</div>
</div>
</div>
</div>
</div>
<!-- End legacy Bootstrap footer -->
<!-- End legacy Bootstrap footer -->
<script>
$(function () {
<script>
$(function () {
// Hide actionsbutton if theres no action defined
if ($('#actionsButton ul').children().length > 0) {
$('#actionsButton').show();
if ($("#actionsButton ul").children().length > 0) {
$("#actionsButton").show();
}
});
</script>
{% block modals %}
{% include "modals/cite_modal.html" %}
{% include "modals/predict_modal.html" %}
{% include "modals/batch_predict_modal.html" %}
{% endblock %}
</body>
});
</script>
{% block modals %}
{% include "modals/cite_modal.html" %}
{% include "modals/predict_modal.html" %}
{% include "modals/batch_predict_modal.html" %}
{% endblock %}
</body>
</html>

View File

@ -72,7 +72,7 @@
<!-- End Matomo Code -->
{% endif %}
</head>
<body class="min-h-screen bg-base-300">
<body class="bg-base-300 min-h-screen">
{% include "includes/navbar.html" %}
{# Main Content Area #}
@ -81,7 +81,7 @@
{# Breadcrumbs - outside main content, optional #}
{% if breadcrumbs %}
<div id="bread" class="max-w-7xl mx-auto px-4 py-4">
<div class="text-sm breadcrumbs">
<div class="breadcrumbs text-sm">
<ul>
{% for elem in breadcrumbs %}
{% for name, url in elem.items %}
@ -100,7 +100,7 @@
{# Main content container - paper effect on medium+ screens #}
<div
id="docContent"
class="w-full xl:w-xl md:mx-auto md:my-8 bg-base-100 md:shadow-2xl md:rounded-lg border-2"
class="bg-base-100 mx-auto md:my-8 md:max-w-6xl md:rounded-lg md:shadow-xl"
>
{# Messages - inside paper #}
{% if message %}
@ -141,7 +141,7 @@
<a
href="https://community.envipath.org/"
target="_blank"
class="flex items-center justify-center btn btn-secondary hover:btn-secondary-focus text-secondary-content text-sm shadow-lg transition-all duration-300 hover:scale-105 hover:-translate-x-1"
class="btn btn-secondary hover:btn-secondary-focus text-secondary-content flex items-center justify-center text-sm shadow-lg transition-all duration-300 hover:-translate-x-1 hover:scale-105"
title="Get Help from the Community"
>
<svg

View File

@ -11,7 +11,11 @@
{% if not public_mode %}
<div class="navbar-center hidden lg:flex">
<a href="#" role="button" class="btn btn-ghost" id="predictLink"
<a
href="{{ meta.server_url }}/predict"
role="button"
class="btn btn-ghost"
id="predictLink"
>Predict</a
>
<!-- <li><a href="{{ meta.server_url }}/package" id="packageLink">Package</a></li> -->
@ -53,11 +57,7 @@
<div class="navbar-end">
{% if not public_mode %}
<button
type="button"
onclick="search_modal.showModal()"
class="btn btn-ghost"
>
<a href="/search" role="button">
<div
class="flex items-center badge badge-dash space-x-1 bg-base-200 text-base-content/50 p-2 m-1"
>
@ -78,7 +78,7 @@
</svg>
<span id="search-shortcut">⌘K</span>
</div>
</button>
</a>
{% endif %}
{% if meta.user.username == 'anonymous' or public_mode %}
<a href="{% url 'login' %}" id="loginButton" class="p-2">Login</a>

View File

@ -1,190 +1,269 @@
{% extends "framework_modern.html" %}
{% load static %}
{% block main_content %}
<!-- Hero Section with Logo and Search -->
<section class="hero h-fit max-w-5xl w-full shadow-none mx-auto relative">
<div 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;"
>
<div class="hero-overlay"></div>
<!-- Predict Pathway text over the image -->
<div class="absolute bottom-40 left-1/8 -translate-x-8 z-10">
<h2 class="text-3xl text-base-100 text-shadow-lg text-left">Predict Your Pathway</h2>
</div>
</div>
</section>
<div class="shadow-md max-w-5xl mx-auto bg-base-200">
<!-- Predict Pathway Section -->
<div class="flex-col lg:flex-row-reverse w-full mx-auto -mt-32 relative z-20 mb-10 ">
<div 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">
<!-- Input Mode Toggle - Fixed position outside fieldset -->
<div class="flex flex-row justify-start items-center h-fit ml-8 my-4">
<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" />
<svg aria-label="smiles mode" 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"
>
<path 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" clip-rule="evenodd" />
</g>
</svg>
<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>
</label>
</div>
</div>
<fieldset class="fieldset transition-all duration-300 ease-in-out overflow-hidden">
<form id="index-form" action="{{ meta.current_package.url }}/pathway" method="POST">
{% csrf_token %}
<div id="text-input-container" class="transition-all duration-300 ease-in-out opacity-100 transform scale-100">
<div class="join w-full mx-auto">
<input type="text" id="index-form-text-input" placeholder="canonical SMILES string" class="input grow input-md join-item" />
<button class="btn btn-neutral join-item">Predict!</button>
</div>
<div class="label relative w-full mt-1" >
<div class="flex gap-2">
<a href="#" class="example-link cursor-pointer hover:text-primary"
data-smiles="CN1C=NC2=C1C(=O)N(C(=O)N2C)C"
title="load example">Caffeine</a>
<a href="#" class="example-link cursor-pointer hover:text-primary"
data-smiles="CC(C)CC1=CC=C(C=C1)C(C)C(=O)O"
title="load example">Ibuprofen</a>
</div>
<a class="absolute top-0 left-[calc(100%-5.4rem)]" href="#">Advanced</a>
</div>
</div>
<div id="ketcher-container" class="hidden w-full transition-all duration-300 ease-in-out opacity-0 transform scale-95">
<iframe id="index-ketcher" src="{% static '/js/ketcher2/ketcher.html' %}"
width="100%" height="400" class="rounded-lg"></iframe>
<button class="btn btn-lg bg-primary-950 text-primary-50 join-item w-full mt-2">Predict!</button>
<a class="label mx-auto w-full mt-1" href="#">Advanced</a>
</div>
<input type="hidden" id="index-form-smiles" name="smiles" value="smiles">
<input type="hidden" id="index-form-predict" name="predict" value="predict">
<input type="hidden" id="current-action" value="predict">
</form>
</fieldset>
</div>
</div>
<!-- Hero Section with Logo and Search -->
<section class="hero h-fit max-w-5xl w-full shadow-none mx-auto relative">
<div
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;"
>
<div class="hero-overlay"></div>
<!-- Predict Pathway text over the image -->
<div class="absolute bottom-40 left-1/8 -translate-x-8 z-10">
<h2 class="text-3xl text-base-100 text-shadow-lg text-left">
Predict Your Pathway
</h2>
</div>
</div>
</section>
<div class="shadow-md max-w-5xl mx-auto bg-base-200">
<!-- Predict Pathway Section -->
<div
class="flex-col lg:flex-row-reverse w-full mx-auto -mt-32 relative z-20 mb-10 "
>
<div
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">
<!-- Input Mode Toggle - Fixed position outside fieldset -->
<div class="flex flex-row justify-start items-center h-fit ml-8 my-4">
<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" />
<svg
aria-label="smiles mode"
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"
>
<path
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"
clip-rule="evenodd"
/>
</g>
</svg>
<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>
</label>
</div>
</div>
<fieldset
class="fieldset transition-all duration-300 ease-in-out overflow-hidden"
>
<form
id="index-form"
action="{{ meta.current_package.url }}/pathway"
method="POST"
>
{% csrf_token %}
<div
id="text-input-container"
class="transition-all duration-300 ease-in-out opacity-100 transform scale-100"
>
<div class="join w-full mx-auto">
<input
type="text"
id="index-form-text-input"
placeholder="canonical SMILES string"
class="input grow input-md join-item"
/>
<button class="btn btn-neutral join-item">Predict!</button>
</div>
<div class="label relative w-full mt-1">
<div class="flex gap-2">
<a
href="#"
class="example-link cursor-pointer hover:text-primary"
data-smiles="CN1C=NC2=C1C(=O)N(C(=O)N2C)C"
title="load example"
>Caffeine</a
>
<a
href="#"
class="example-link cursor-pointer hover:text-primary"
data-smiles="CC(C)CC1=CC=C(C=C1)C(C)C(=O)O"
title="load example"
>Ibuprofen</a
>
</div>
<a class="absolute top-0 left-[calc(100%-5.4rem)]" href="#"
>Advanced</a
>
</div>
</div>
<div
id="ketcher-container"
class="hidden w-full transition-all duration-300 ease-in-out opacity-0 transform scale-95"
>
<iframe
id="index-ketcher"
src="{% static '/js/ketcher2/ketcher.html' %}"
width="100%"
height="400"
class="rounded-lg"
></iframe>
<button
class="btn btn-lg bg-primary-950 text-primary-50 join-item w-full mt-2"
>
Predict!
</button>
<a class="label mx-auto w-full mt-1" href="#">Advanced</a>
</div>
<input
type="hidden"
id="index-form-smiles"
name="smiles"
value="smiles"
/>
<input
type="hidden"
id="index-form-predict"
name="predict"
value="predict"
/>
<input type="hidden" id="current-action" value="predict" />
</form>
</fieldset>
</div>
</div>
</div>
<!-- Community News Section -->
<section class="py-16 bg-base-200 z-10 mx-8">
<div class="max-w-7xl mx-auto px-4">
<h2 class="h2 font-bold text-left mb-8">Community Updates</h2>
<div class="max-w-7xl mx-auto px-4">
<h2 class="h2 font-bold text-left mb-8">Community Updates</h2>
<div id="community-news-container" class="flex gap-4 justify-center">
<!-- News cards will be populated here -->
<div id="loading" class="flex justify-center w-full">
<span class="loading loading-spinner loading-lg"></span>
</div>
</div>
<div class="text-right mt-6">
<a href="https://community.envipath.org/c/announcements/10" target="_blank" class="btn btn-ghost btn-sm">
Read More Announcements
</a>
</div>
<!-- Discourse API integration -->
<script src="{% static 'js/discourse-api.js' %}"></script>
<div id="community-news-container" class="flex gap-4 justify-center">
<!-- News cards will be populated here -->
<div id="loading" class="flex justify-center w-full">
<span class="loading loading-spinner loading-lg"></span>
</div>
</div>
<div class="text-right mt-6">
<a
href="https://community.envipath.org/c/announcements/10"
target="_blank"
class="btn btn-ghost btn-sm"
>
Read More Announcements
</a>
</div>
<!-- Discourse API integration -->
<script src="{% static 'js/discourse-api.js' %}"></script>
</div>
</section>
<!-- Mission Statement Section -->
<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="flex flex-row gap-4">
<div class="w-1/3">
<img src="{% static "/images/ep-rule-artwork.png" %}" alt="rule-based iterative tree greneration" class="w-full h-full object-contain" />
</div>
<div class="space-y-4 text-left w-2/3 mr-8">
<h2 class="h2 font-bold mb-8">About enviPath</h2>
<p class="">
enviPath is a database and prediction system for the microbial
biotransformation of organic environmental contaminants. The
database provides the possibility to store and view experimentally
observed biotransformation pathways.
</p>
<p class="">
The pathway prediction system provides different relative reasoning models
to predict likely biotransformation pathways and products. Explore our tools
and contribute to advancing environmental biotransformation research.
</p>
<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-neutral">Publications</a>
</div>
</div>
<div class="mx-auto px-8 md:px-12">
<div class="flex flex-row gap-4">
<div class="w-1/3">
<img
src="{% static "/images/ep-rule-artwork.png" %}"
alt="rule-based iterative tree greneration"
class="w-full h-full object-contain"
/>
</div>
<div class="space-y-4 text-left w-2/3 mr-8">
<h2 class="h2 font-bold mb-8">About enviPath</h2>
<p class="">
enviPath is a database and prediction system for the microbial
biotransformation of organic environmental contaminants. The
database provides the possibility to store and view experimentally
observed biotransformation pathways.
</p>
<p class="">
The pathway prediction system provides different relative
reasoning models to predict likely biotransformation pathways and
products. Explore our tools and contribute to advancing
environmental biotransformation research.
</p>
<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-neutral">Publications</a>
</div>
</div>
</div>
</div>
</section>
<!-- Partners Section -->
<section class="py-14 sm:py-12 bg-base-100">
<div class="mx-auto px-6 lg:px-8">
<div class="divider"><h2 class="text-center text-lg/8 font-semibold">Backed by Science</h2></div>
<div class="mx-auto mt-10 grid max-w-lg grid-cols-4 items-center gap-x-8 gap-y-10 sm:max-w-xl sm:grid-cols-6 sm:gap-x-10 lg:mx-0 lg:max-w-none lg:grid-cols-3">
<img src="{% static "/images/uoa-logo-small.png" %}" alt="The University of Auckland" class=" max-h-20 w-full object-contain lg:col-span-1" />
<img src="{% static "/images/logo-eawag.svg" %}" alt="Eawag" class=" max-h-12 w-full object-contain lg:col-span-1" />
<img src="{% static "/images/uzh-logo.svg" %}" alt="University of Zurich" class="2 max-h-16 w-full object-contain lg:col-span-1" />
</div>
<div class="mx-auto px-6 lg:px-8">
<div class="divider">
<h2 class="text-center text-lg/8 font-semibold">Backed by Science</h2>
</div>
<div
class="mx-auto mt-10 grid max-w-lg grid-cols-4 items-center gap-x-8 gap-y-10 sm:max-w-xl sm:grid-cols-6 sm:gap-x-10 lg:mx-0 lg:max-w-none lg:grid-cols-3"
>
<img
src="{% static "/images/uoa-logo-small.png" %}"
alt="The University of Auckland"
class=" max-h-20 w-full object-contain lg:col-span-1"
/>
<img
src="{% static "/images/logo-eawag.svg" %}"
alt="Eawag"
class=" max-h-12 w-full object-contain lg:col-span-1"
/>
<img
src="{% static "/images/uzh-logo.svg" %}"
alt="University of Zurich"
class="2 max-h-16 w-full object-contain lg:col-span-1"
/>
</div>
</div>
</section>
</div>
<script language="javascript">
var currentPackage = "{{ meta.current_package.url }}";
// Discourse API integration is now handled by discourse-api.js
// Function to render Discourse topics into cards
function renderDiscourseTopics(topics) {
const container = document.getElementById("community-news-container");
if (!container) return;
</div>
// Clear container
container.innerHTML = "";
<script language="javascript">
var currentPackage = "{{ meta.current_package.url }}";
// Create cards for each topic
topics.forEach((topic) => {
const card = createDiscourseCard(topic);
container.insertAdjacentHTML("beforeend", card);
});
}
// Discourse API integration is now handled by discourse-api.js
// Function to create HTML card for a topic
function createDiscourseCard(topic) {
const date = new Date(topic.created_at).toLocaleDateString();
// Function to render Discourse topics into cards
function renderDiscourseTopics(topics) {
const container = document.getElementById('community-news-container');
if (!container) return;
// Clear container
container.innerHTML = '';
// Create cards for each topic
topics.forEach(topic => {
const card = createDiscourseCard(topic);
container.insertAdjacentHTML('beforeend', card);
});
}
// Function to create HTML card for a topic
function createDiscourseCard(topic) {
const date = new Date(topic.created_at).toLocaleDateString();
return `
return `
<div class="card bg-white shadow-xs hover:shadow-lg transition-shadow duration-300 h-64 w-75 flex-shrink-0">
<div class="card-body flex flex-col h-full">
<h3 class="card-title leading-tight font-normal tracking-tight h-12 mb-2 line-clamp-2 text-ellipsis wrap-break-word overflow-hidden">
@ -215,146 +294,146 @@
</div>
</div>
`;
}
// Make render function globally available
window.renderDiscourseTopics = renderDiscourseTopics;
// Toggle functionality with smooth animations
function toggleInputMode() {
const toggle = $('input[type="checkbox"]');
const textContainer = $("#text-input-container");
const ketcherContainer = $("#ketcher-container");
const formCard = $(".card");
const fieldset = $(".fieldset");
if (toggle.is(":checked")) {
// Draw mode - show Ketcher, hide text input
textContainer.addClass("opacity-0 transform scale-95");
textContainer.removeClass("opacity-100 transform scale-100");
// Adjust fieldset padding for Ketcher mode - reduce padding and make more compact
fieldset.removeClass("p-8");
fieldset.addClass("p-4");
// Wait for fade out to complete, then hide and show new content
setTimeout(() => {
textContainer.addClass("hidden");
ketcherContainer.removeClass("hidden opacity-0 transform scale-95");
ketcherContainer.addClass("opacity-100 transform scale-100");
// Force re-evaluation of iframe size
const iframe = document.getElementById("index-ketcher");
if (iframe) {
iframe.style.height = "400px";
}
}, 300);
} else {
// SMILES mode - show text input, hide Ketcher
ketcherContainer.addClass("opacity-0 transform scale-95");
ketcherContainer.removeClass("opacity-100 transform scale-100");
// Restore fieldset padding for text input mode
fieldset.removeClass("p-4");
fieldset.addClass("p-8");
// Wait for fade out to complete, then hide and show new content
setTimeout(() => {
ketcherContainer.addClass("hidden");
textContainer.removeClass("hidden opacity-0 transform scale-95");
textContainer.addClass("opacity-100 transform scale-100");
}, 300);
// Transfer SMILES from Ketcher to text input if available
if (window.indexKetcher && window.indexKetcher.getSmiles) {
const smiles = window.indexKetcher.getSmiles();
if (smiles && smiles.trim() !== "") {
$("#index-form-text-input").val(smiles);
}
}
}
}
// Ketcher integration
function indexKetcherToTextInput() {
$("#index-form-smiles").val(this.ketcher.getSmiles());
}
$(function () {
// Initialize fieldset with proper padding
$(".fieldset").addClass("p-8");
// Toggle event listener
$('input[type="checkbox"]').on("change", toggleInputMode);
// Ketcher iframe load handler
$("#index-ketcher").on("load", function () {
const checkKetcherReady = () => {
const win = this.contentWindow;
if (win.ketcher && "editor" in win.ketcher) {
window.indexKetcher = win.ketcher;
win.ketcher.editor.event.change.handlers.push({
once: false,
priority: 0,
f: indexKetcherToTextInput,
ketcher: win.ketcher,
});
} else {
setTimeout(checkKetcherReady, 100);
}
};
checkKetcherReady();
});
// Handle example link clicks
$(".example-link").on("click", function (e) {
e.preventDefault();
const smiles = $(this).data("smiles");
const title = $(this).attr("title");
// Check if we're in Ketcher mode or text input mode
if ($('input[type="checkbox"]').is(":checked")) {
// In Ketcher mode - set the SMILES in Ketcher
if (window.indexKetcher && window.indexKetcher.setMolecule) {
window.indexKetcher.setMolecule(smiles);
}
} else {
// In text input mode - set the SMILES in the text input
$("#index-form-text-input").val(smiles);
}
// Make render function globally available
window.renderDiscourseTopics = renderDiscourseTopics;
// Show a brief feedback
const originalText = $(this).text();
$(this).text(`loaded!`);
setTimeout(() => {
$(this).text(originalText);
}, 1000);
});
// Toggle functionality with smooth animations
function toggleInputMode() {
const toggle = $('input[type="checkbox"]');
const textContainer = $('#text-input-container');
const ketcherContainer = $('#ketcher-container');
const formCard = $('.card');
const fieldset = $('.fieldset');
// Handle form submission on Enter
$("#index-form").on("submit", function (e) {
e.preventDefault();
if (toggle.is(':checked')) {
// Draw mode - show Ketcher, hide text input
textContainer.addClass('opacity-0 transform scale-95');
textContainer.removeClass('opacity-100 transform scale-100');
var textSmiles = "";
// Adjust fieldset padding for Ketcher mode - reduce padding and make more compact
fieldset.removeClass('p-8');
fieldset.addClass('p-4');
// Wait for fade out to complete, then hide and show new content
setTimeout(() => {
textContainer.addClass('hidden');
ketcherContainer.removeClass('hidden opacity-0 transform scale-95');
ketcherContainer.addClass('opacity-100 transform scale-100');
// Force re-evaluation of iframe size
const iframe = document.getElementById('index-ketcher');
if (iframe) {
iframe.style.height = '400px';
}
}, 300);
} else {
// SMILES mode - show text input, hide Ketcher
ketcherContainer.addClass('opacity-0 transform scale-95');
ketcherContainer.removeClass('opacity-100 transform scale-100');
// Restore fieldset padding for text input mode
fieldset.removeClass('p-4');
fieldset.addClass('p-8');
// Wait for fade out to complete, then hide and show new content
setTimeout(() => {
ketcherContainer.addClass('hidden');
textContainer.removeClass('hidden opacity-0 transform scale-95');
textContainer.addClass('opacity-100 transform scale-100');
}, 300);
// Transfer SMILES from Ketcher to text input if available
if (window.indexKetcher && window.indexKetcher.getSmiles) {
const smiles = window.indexKetcher.getSmiles();
if (smiles && smiles.trim() !== '') {
$('#index-form-text-input').val(smiles);
}
}
}
// Check if we're in Ketcher mode and extract SMILES
if ($('input[type="checkbox"]').is(":checked") && window.indexKetcher) {
textSmiles = window.indexKetcher.getSmiles().trim();
} else {
textSmiles = $("#index-form-text-input").val().trim();
}
// Ketcher integration
function indexKetcherToTextInput() {
$('#index-form-smiles').val(this.ketcher.getSmiles());
if (textSmiles === "") {
return;
}
$(function () {
// Initialize fieldset with proper padding
$('.fieldset').addClass('p-8');
$("#index-form-smiles").val(textSmiles);
$("#index-form").attr("action", currentPackage + "/pathway");
$("#index-form").attr("method", "POST");
this.submit();
});
// Toggle event listener
$('input[type="checkbox"]').on('change', toggleInputMode);
// Ketcher iframe load handler
$('#index-ketcher').on('load', function() {
const checkKetcherReady = () => {
const win = this.contentWindow;
if (win.ketcher && 'editor' in win.ketcher) {
window.indexKetcher = win.ketcher;
win.ketcher.editor.event.change.handlers.push({
once: false,
priority: 0,
f: indexKetcherToTextInput,
ketcher: win.ketcher
});
} else {
setTimeout(checkKetcherReady, 100);
}
};
checkKetcherReady();
});
// Handle example link clicks
$('.example-link').on('click', function(e) {
e.preventDefault();
const smiles = $(this).data('smiles');
const title = $(this).attr('title');
// Check if we're in Ketcher mode or text input mode
if ($('input[type="checkbox"]').is(':checked')) {
// In Ketcher mode - set the SMILES in Ketcher
if (window.indexKetcher && window.indexKetcher.setMolecule) {
window.indexKetcher.setMolecule(smiles);
}
} else {
// In text input mode - set the SMILES in the text input
$('#index-form-text-input').val(smiles);
}
// Show a brief feedback
const originalText = $(this).text();
$(this).text(`loaded!`);
setTimeout(() => {
$(this).text(originalText);
}, 1000);
});
// Handle form submission on Enter
$('#index-form').on("submit", function (e) {
e.preventDefault();
var textSmiles = '';
// Check if we're in Ketcher mode and extract SMILES
if ($('input[type="checkbox"]').is(':checked') && window.indexKetcher) {
textSmiles = window.indexKetcher.getSmiles().trim();
} else {
textSmiles = $('#index-form-text-input').val().trim();
}
if (textSmiles === '') {
return;
}
$('#index-form-smiles').val(textSmiles);
$("#index-form").attr("action", currentPackage + "/pathway");
$("#index-form").attr("method", 'POST');
this.submit();
});
// Discourse topics are now loaded automatically by discourse-api.js
});
</script>
// Discourse topics are now loaded automatically by discourse-api.js
});
</script>
{% endblock main_content %}

View File

@ -0,0 +1,188 @@
{% extends "framework_modern.html" %}
{% load static %}
{% block content %}
<div class="mx-auto w-full p-8">
<h1 class="h1 mb-4 text-3xl font-bold">Predict a Pathway</h1>
<form
id="predict_form"
accept-charset="UTF-8"
action="{{ meta.current_package.url }}/pathway"
method="post"
>
{% csrf_token %}
<div class="mb-8 flex flex-col gap-8 md:flex-row md:items-end">
<fieldset class="flex flex-col gap-4 md:flex-3/4">
<label class="floating-label" for="name">
<input
type="text"
name="name"
placeholder="Name"
id="name"
class="input input-md w-full"
/>
<span>Name</span>
</label>
<label class="floating-label" for="description">
<input
type="text"
name="description"
placeholder="Description"
id="description"
class="input input-md w-full"
/>
<span>Description</span>
</label>
</fieldset>
<fieldset
class="fieldset flex shrink-0 flex-row items-start gap-3 md:flex-1/4 md:flex-col"
>
<label class="fieldset-label text-base-content/50">Mode</label>
<label class="label">
<input
type="radio"
name="predict"
id="radioPredict"
value="predict"
checked
class="radio radio-neutral"
/>
Predict
</label>
<label class="label">
<input
type="radio"
name="predict"
id="radioIncremental"
value="incremental"
class="radio radio-neutral"
/>
Incremental
</label>
<label class="label">
<input
type="radio"
name="predict"
id="radioBuild"
value="build"
class="radio radio-neutral"
/>
Build
</label>
</fieldset>
</div>
<label class="floating-label" for="predict-smiles">
<input
type="text"
name="smiles"
placeholder="SMILES"
id="predict-smiles"
class="input input-md w-full"
/>
<span>SMILES</span>
</label>
<div class="divider text-base-content/50">OR</div>
<div class="mb-8 w-full">
<label class="text-base-content/50 mb-4 text-xs font-medium"
>Draw Structure</label
>
<iframe
id="predict-ketcher"
src="{% static '/js/ketcher2/ketcher.html' %}"
width="100%"
height="510"
></iframe>
</div>
<label class="select mb-8 w-full">
<span class="label">Predictor</span>
<select id="prediction-setting" name="prediction-setting">
<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 %}
(User default)
{% endif %}
</option>
{% endfor %}
</select>
</label>
<div class="flex justify-end gap-2">
<a href="{{ meta.current_package.url }}/pathway" class="btn btn-outline"
>Cancel</a
>
<button
type="submit"
id="predict-submit-button"
class="btn btn-primary"
>
Predict
</button>
</div>
</form>
</div>
<script>
function predictKetcherToTextInput() {
$("#predict-smiles").val(this.ketcher.getSmiles());
}
$(function () {
$("#predict-ketcher").on("load", function () {
const checkKetcherReady = () => {
const win = this.contentWindow;
if (win.ketcher && "editor" in win.ketcher) {
window.predictKetcher = win.ketcher;
win.ketcher.editor.event.change.handlers.push({
once: false,
priority: 0,
f: predictKetcherToTextInput,
ketcher: win.ketcher,
});
} else {
setTimeout(checkKetcherReady, 100);
}
};
checkKetcherReady();
});
$("#predict-submit-button").on("click", function (e) {
e.preventDefault();
const button = $(this);
button.prop("disabled", true);
button.text("Predicting...");
// Get SMILES from either input or Ketcher
let smiles = $("#predict-smiles").val().trim();
// If SMILES input is empty, try to get from Ketcher
if (!smiles && window.predictKetcher) {
smiles = window.predictKetcher.getSmiles().trim();
if (smiles) {
$("#predict-smiles").val(smiles);
}
}
// Basic validation
if (!smiles) {
alert("Please enter a SMILES string or draw a structure.");
button.prop("disabled", false);
button.text("Predict");
return;
}
// Submit form
$("#predict_form").submit();
});
});
</script>
{% endblock content %}

View File

@ -105,7 +105,7 @@
</div>
</div>
<form method="post" action="{% url 'register' %}" class="space-y-4">
<form method="post" action="{% url 'login' %}" class="space-y-4">
{% csrf_token %}
<input type="hidden" name="register" value="true" />