[Feature] Modern UI roll out (#236)

This PR moves all the collection pages into the new UI in a rough push.
I did not put the same amount of care into these as into search, index, and predict.

## Major changes

- All modals are now migrated to a state based alpine.js implementation.
- jQuery is no longer present in the base layout; ajax is replace by native fetch api
- most of the pps.js is now obsolte (as I understand it; the code is not referenced any more @jebus  please double check)
- in-memory pagination for large result lists (set to 50; we can make that configurable later; performance degrades at around 1k) stukk a bit rough tracked in #235

## Minor things

- Sarch and index also use alpine now
- The loading spinner is now CSS animated (not sure if it currently gets correctly called)

## Not done

- Ihave not even cheked the admin pages. Not sure If these need migrations
- The temporary migration pages still use the old template. Not sure what is supposed to happen with those? @jebus

## What I did to test

- opend all pages in browse, and user ; plus all pages reachable from there.
- Interacted and tested the functionality of each modal superfically with exception of the API key modal (no functional test).

---
This PR is massive sorry for that; just did not want to push half-brokenn state.
@jebus @liambrydon I would be glad if you could click around and try to break it :)

Finally closes #133

Co-authored-by: Tim Lorsbach <tim@lorsba.ch>
Reviewed-on: enviPath/enviPy#236
Co-authored-by: Tobias O <tobias.olenyi@envipath.com>
Co-committed-by: Tobias O <tobias.olenyi@envipath.com>
This commit is contained in:
2025-11-26 23:16:44 +13:00
committed by jebus
parent 7f6f209b4a
commit 1a2c9bb543
110 changed files with 10784 additions and 9465 deletions

View File

@ -1,6 +1,9 @@
{% if meta.can_edit %}
<li>
<a role="button" data-toggle="modal" data-target="#new_compound_modal">
<a
role="button"
onclick="document.getElementById('new_compound_modal').showModal(); return false;"
>
<span class="glyphicon glyphicon-plus"></span> New Compound</a
>
</li>

View File

@ -2,8 +2,7 @@
<li>
<a
role="button"
data-toggle="modal"
data-target="#new_compound_structure_modal"
onclick="document.getElementById('new_compound_structure_modal').showModal(); return false;"
>
<span class="glyphicon glyphicon-plus"></span> New Compound Structure</a
>

View File

@ -1,6 +1,9 @@
{% if meta.can_edit %}
<li>
<a role="button" data-toggle="modal" data-target="#new_edge_modal">
<a
role="button"
onclick="document.getElementById('new_edge_modal').showModal(); return false;"
>
<span class="glyphicon glyphicon-plus"></span> New Edge</a
>
</li>

View File

@ -1,5 +1,8 @@
<li>
<a role="button" data-toggle="modal" data-target="#new_group_modal">
<a
role="button"
onclick="document.getElementById('new_group_modal').showModal(); return false;"
>
<span class="glyphicon glyphicon-plus"></span> New Group</a
>
</li>

View File

@ -1,6 +1,9 @@
{% if meta.can_edit and meta.enabled_features.MODEL_BUILDING %}
<li>
<a role="button" data-toggle="modal" data-target="#new_model_modal">
<a
role="button"
onclick="document.getElementById('new_model_modal').showModal(); return false;"
>
<span class="glyphicon glyphicon-plus"></span> New Model</a
>
</li>

View File

@ -1,6 +1,9 @@
{% if meta.can_edit %}
<li>
<a role="button" data-toggle="modal" data-target="#new_node_modal">
<a
role="button"
onclick="document.getElementById('new_node_modal').showModal(); return false;"
>
<span class="glyphicon glyphicon-plus"></span> New Node</a
>
</li>

View File

@ -1,18 +1,23 @@
<li>
<a role="button" data-toggle="modal" data-target="#new_package_modal">
<a
role="button"
onclick="document.getElementById('new_package_modal').showModal(); return false;"
>
<span class="glyphicon glyphicon-plus"></span> New Package</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#import_package_modal">
<a
role="button"
onclick="document.getElementById('import_package_modal').showModal(); return false;"
>
<span class="glyphicon glyphicon-import"></span> Import Package from JSON</a
>
</li>
<li>
<a
role="button"
data-toggle="modal"
data-target="#import_legacy_package_modal"
onclick="document.getElementById('import_legacy_package_modal').showModal(); return false;"
>
<span class="glyphicon glyphicon-import"></span> Import Package from legacy
JSON</a

View File

@ -1,6 +1,9 @@
{% if meta.can_edit %}
<li>
<a role="button" data-toggle="modal" data-target="#new_reaction_modal">
<a
role="button"
onclick="document.getElementById('new_reaction_modal').showModal(); return false;"
>
<span class="glyphicon glyphicon-plus"></span> New Reaction</a
>
</li>

View File

@ -1,6 +1,9 @@
{% if meta.can_edit %}
<li>
<a role="button" data-toggle="modal" data-target="#new_rule_modal">
<a
role="button"
onclick="document.getElementById('new_rule_modal').showModal(); return false;"
>
<span class="glyphicon glyphicon-plus"></span> New Rule</a
>
</li>

View File

@ -1,6 +1,9 @@
{% if meta.can_edit %}
<li>
<a role="button" data-toggle="modal" data-target="#new_scenario_modal">
<a
role="button"
onclick="document.getElementById('new_scenario_modal').showModal(); return false;"
>
<span class="glyphicon glyphicon-plus"></span> New Scenario</a
>
</li>

View File

@ -1,6 +1,9 @@
{% if meta.can_edit %}
<li>
<a role="button" data-toggle="modal" data-target="#new_setting_modal">
<a
role="button"
onclick="document.getElementById('new_setting_modal').showModal(); return false;"
>
<span class="glyphicon glyphicon-plus"></span>New Setting</a
>
</li>

View File

@ -1,42 +1,59 @@
{% if meta.can_edit %}
<li>
<a role="button" data-toggle="modal" data-target="#edit_compound_modal">
<a
role="button"
onclick="document.getElementById('edit_compound_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-edit"></i> Edit Compound</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#set_aliases_modal">
<a
role="button"
onclick="document.getElementById('set_aliases_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Set Aliases</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#add_structure_modal">
<a
role="button"
onclick="document.getElementById('add_structure_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Add Structure</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#set_scenario_modal">
<a
role="button"
onclick="document.getElementById('set_scenario_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Set Scenarios</a
>
</li>
<li>
<a
role="button"
data-toggle="modal"
data-target="#generic_set_external_reference_modal"
onclick="document.getElementById('generic_set_external_reference_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Set External Reference</a
>
</li>
{% endif %}
<li>
<a role="button" data-toggle="modal" data-target="#generic_copy_object_modal">
<a
role="button"
onclick="document.getElementById('generic_copy_object_modal').showModal(); return false;"
>
<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">
<a
class="button"
onclick="document.getElementById('generic_delete_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-trash"></i> Delete Compound</a
>
</li>

View File

@ -2,33 +2,40 @@
<li>
<a
role="button"
data-toggle="modal"
data-target="#edit_compound_structure_modal"
onclick="document.getElementById('edit_compound_structure_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-edit"></i> Edit Compound Structure</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#set_aliases_modal">
<a
role="button"
onclick="document.getElementById('set_aliases_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Set Aliases</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#set_scenario_modal">
<a
role="button"
onclick="document.getElementById('set_scenario_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Set Scenarios</a
>
</li>
<li>
<a
role="button"
data-toggle="modal"
data-target="#generic_set_external_reference_modal"
onclick="document.getElementById('generic_set_external_reference_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Set External Reference</a
>
</li>
<li>
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
<a
class="button"
onclick="document.getElementById('generic_delete_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-trash"></i> Delete Compound Structure</a
>
</li>

View File

@ -1,16 +1,25 @@
{% if meta.can_edit %}
<li>
<a role="button" data-toggle="modal" data-target="#set_aliases_modal">
<a
role="button"
onclick="document.getElementById('set_aliases_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Set Aliases</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#set_scenario_modal">
<a
role="button"
onclick="document.getElementById('set_scenario_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Set Scenarios</a
>
</li>
<li>
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
<a
class="button"
onclick="document.getElementById('generic_delete_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-trash"></i> Delete Edge</a
>
</li>

View File

@ -1,11 +1,17 @@
{% if meta.can_edit %}
<li>
<a role="button" data-toggle="modal" data-target="#edit_group_member_modal">
<a
role="button"
onclick="document.getElementById('edit_group_member_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-trash"></i> Add/Remove Member</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#generic_delete_modal">
<a
class="button"
onclick="document.getElementById('generic_delete_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-trash"></i> Delete Group</a
>
</li>

View File

@ -1,21 +1,33 @@
{% if meta.can_edit %}
<li>
<a role="button" data-toggle="modal" data-target="#edit_model_modal">
<a
role="button"
onclick="document.getElementById('edit_model_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-edit"></i> Edit Model</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#evaluate_model_modal">
<a
role="button"
onclick="document.getElementById('evaluate_model_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-ok"></i> Evaluate Model</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#retrain_model_modal">
<a
role="button"
onclick="document.getElementById('retrain_model_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-repeat"></i> Retrain Model</a
>
</li>
<li>
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
<a
class="button"
onclick="document.getElementById('generic_delete_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-trash"></i> Delete Model</a
>
</li>

View File

@ -1,21 +1,33 @@
{% if meta.can_edit %}
<li>
<a role="button" data-toggle="modal" data-target="#edit_node_modal">
<a
role="button"
onclick="document.getElementById('edit_node_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-edit"></i> Edit Node</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#set_aliases_modal">
<a
role="button"
onclick="document.getElementById('set_aliases_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Set Aliases</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#set_scenario_modal">
<a
role="button"
onclick="document.getElementById('set_scenario_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Set Scenarios</a
>
</li>
<li>
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
<a
class="button"
onclick="document.getElementById('generic_delete_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-trash"></i> Delete Node</a
>
</li>

View File

@ -1,35 +1,49 @@
{% if meta.can_edit %}
<li>
<a role="button" data-toggle="modal" data-target="#edit_package_modal">
<a
role="button"
onclick="document.getElementById('edit_package_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-edit"></i> Edit Package</a
>
</li>
<li>
<a
role="button"
data-toggle="modal"
data-target="#edit_package_permissions_modal"
onclick="document.getElementById('edit_package_permissions_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-user"></i> Edit Permissions</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#publish_package_modal">
<a
role="button"
onclick="document.getElementById('publish_package_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-bullhorn"></i> Publish Package</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#export_package_modal">
<a
role="button"
onclick="document.getElementById('export_package_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-bullhorn"></i> Export Package as JSON</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#set_license_modal">
<a
role="button"
onclick="document.getElementById('set_license_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-duplicate"></i> License</a
>
</li>
<li>
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
<a
class="button"
onclick="document.getElementById('generic_delete_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-trash"></i> Delete Package</a
>
</li>

View File

@ -1,26 +1,34 @@
{% if meta.can_edit %}
<li>
<a class="button" data-toggle="modal" data-target="#add_pathway_node_modal">
<a
class="button"
onclick="document.getElementById('add_pathway_node_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Add Compound</a
>
</li>
<li>
<a class="button" data-toggle="modal" data-target="#add_pathway_edge_modal">
<a
class="button"
onclick="document.getElementById('add_pathway_edge_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Add Reaction</a
>
</li>
<li role="separator" class="divider"></li>
{% endif %}
<li>
<a role="button" data-toggle="modal" data-target="#generic_copy_object_modal">
<a
role="button"
onclick="document.getElementById('generic_copy_object_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-duplicate"></i> Copy</a
>
</li>
<li>
<a
class="button"
data-toggle="modal"
data-target="#download_pathway_csv_modal"
onclick="document.getElementById('download_pathway_csv_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-floppy-save"></i> Download Pathway as CSV</a
>
@ -28,8 +36,7 @@
<li>
<a
class="button"
data-toggle="modal"
data-target="#download_pathway_image_modal"
onclick="document.getElementById('download_pathway_image_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-floppy-save"></i> Download Pathway as Image</a
>
@ -38,8 +45,7 @@
<li>
<a
class="button"
data-toggle="modal"
data-target="#identify_missing_rules_modal"
onclick="document.getElementById('identify_missing_rules_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-question-sign"></i> Identify Missing
Rules</a
@ -47,30 +53,34 @@
</li>
<li role="separator" class="divider"></li>
<li>
<a class="button" data-toggle="modal" data-target="#edit_pathway_modal">
<a
class="button"
onclick="document.getElementById('edit_pathway_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-edit"></i> Edit Pathway</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#set_scenario_modal">
<a
role="button"
onclick="document.getElementById('set_scenario_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Set Scenarios</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#set_aliases_modal">
<a
role="button"
onclick="document.getElementById('set_aliases_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Set Aliases</a
>
</li>
{# <li>#}
{# <a class="button" data-toggle="modal" data-target="#add_pathway_edge_modal">#}
{# <i class="glyphicon glyphicon-plus"></i> Calculate Compound Properties</a>#}
{# </li>#}
<li role="separator" class="divider"></li>
<li>
<a
class="button"
data-toggle="modal"
data-target="#delete_pathway_node_modal"
onclick="document.getElementById('delete_pathway_node_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-trash"></i> Delete Compound</a
>
@ -78,14 +88,16 @@
<li>
<a
class="button"
data-toggle="modal"
data-target="#delete_pathway_edge_modal"
onclick="document.getElementById('delete_pathway_edge_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-trash"></i> Delete Reaction</a
>
</li>
<li>
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
<a
class="button"
onclick="document.getElementById('generic_delete_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-trash"></i> Delete Pathway</a
>
</li>

View File

@ -1,37 +1,51 @@
{% if meta.can_edit %}
<li>
<a role="button" data-toggle="modal" data-target="#edit_reaction_modal">
<a
role="button"
onclick="document.getElementById('edit_reaction_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-edit"></i> Edit Reaction</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#set_aliases_modal">
<a
role="button"
onclick="document.getElementById('set_aliases_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Set Aliases</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#set_scenario_modal">
<a
role="button"
onclick="document.getElementById('set_scenario_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Set Scenarios</a
>
</li>
<li>
<a
role="button"
data-toggle="modal"
data-target="#generic_set_external_reference_modal"
onclick="document.getElementById('generic_set_external_reference_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Set External Reference</a
>
</li>
{% endif %}
<li>
<a role="button" data-toggle="modal" data-target="#generic_copy_object_modal">
<a
role="button"
onclick="document.getElementById('generic_copy_object_modal').showModal(); return false;"
>
<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">
<a
class="button"
onclick="document.getElementById('generic_delete_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-trash"></i> Delete Reaction</a
>
</li>

View File

@ -1,28 +1,43 @@
{% if meta.can_edit %}
<li>
<a role="button" data-toggle="modal" data-target="#edit_rule_modal">
<a
role="button"
onclick="document.getElementById('edit_rule_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-edit"></i> Edit Rule</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#set_aliases_modal">
<a
role="button"
onclick="document.getElementById('set_aliases_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Set Aliases</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#set_scenario_modal">
<a
role="button"
onclick="document.getElementById('set_scenario_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> Set Scenarios</a
>
</li>
{% endif %}
<li>
<a role="button" data-toggle="modal" data-target="#generic_copy_object_modal">
<a
role="button"
onclick="document.getElementById('generic_copy_object_modal').showModal(); return false;"
>
<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">
<a
class="button"
onclick="document.getElementById('generic_delete_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-trash"></i> Delete Rule</a
>
</li>

View File

@ -2,8 +2,7 @@
<li>
<a
class="button"
data-toggle="modal"
data-target="#add_additional_information_modal"
onclick="document.getElementById('add_additional_information_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-trash"></i> Add Additional Information</a
>
@ -11,14 +10,16 @@
<li>
<a
class="button"
data-toggle="modal"
data-target="#update_scenario_additional_information_modal"
onclick="document.getElementById('update_scenario_additional_information_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-trash"></i> Set Additional Information</a
>
</li>
<li>
<a class="button" data-toggle="modal" data-target="#generic_delete_modal">
<a
class="button"
onclick="document.getElementById('generic_delete_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-trash"></i> Delete Scenario</a
>
</li>

View File

@ -1,19 +1,24 @@
{% if meta.can_edit %}
<li>
<a role="button" data-toggle="modal" data-target="#edit_user_modal">
<a
role="button"
onclick="document.getElementById('edit_user_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-edit"></i> Update</a
>
</li>
<li>
<a role="button" data-toggle="modal" data-target="#edit_password_modal">
<a
role="button"
onclick="document.getElementById('edit_password_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-lock"></i> Update Password</a
>
</li>
<li>
<a
role="button"
data-toggle="modal"
data-target="#new_prediction_setting_modal"
onclick="document.getElementById('new_prediction_setting_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-plus"></i> New Prediction Setting</a
>
@ -23,7 +28,10 @@
{# <i class="glyphicon glyphicon-console"></i> Manage API Token</a>#}
{# </li>#}
<li>
<a role="button" data-toggle="modal" data-target="#generic_delete_modal">
<a
class="button"
onclick="document.getElementById('generic_delete_modal').showModal(); return false;"
>
<i class="glyphicon glyphicon-trash"></i> Delete Account</a
>
</li>

View File

@ -1,49 +1,33 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% load static %}
{% load envipytags %}
{% block content %}
<div class="panel-group" id="reviewListAccordion">
<div class="panel panel-default">
<div
class="panel-heading"
id="headingPanel"
style="font-size:2rem;height: 46px"
>
Jobs
</div>
<div class="panel-body">
<p>Job Logs Desc</p>
<div class="space-y-2 p-4">
<!-- Header Section -->
<div class="card bg-base-100">
<div class="card-body">
<h2 class="card-title text-2xl">User Prediction Jobs</h2>
<p class="mt-2">Job Logs Desc</p>
</div>
</div>
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="job-accordion-link"
data-toggle="collapse"
data-parent="#job-accordion"
href="#jobs"
>
Jobs
</a>
</h4>
</div>
<div id="jobs" class="panel-collapse in collapse">
<div class="panel-body list-group-item" id="job-content">
<table class="table-bordered table-hover table">
<tr style="background-color: rgba(0, 0, 0, 0.08);">
{% if meta.user.is_superuser %}
<th scope="col">User</th>
{% endif %}
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Status</th>
<th scope="col">Queued</th>
<th scope="col">Done</th>
<th scope="col">Result</th>
</tr>
<!-- Jobs -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Recent Jobs</div>
<div class="collapse-content" id="job-content">
<div class="overflow-x-auto">
<table class="table-zebra table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Status</th>
<th>Queued</th>
<th>Done</th>
<th>Result</th>
</tr>
</thead>
<tbody>
{% for job in jobs %}
<tr>
@ -58,7 +42,11 @@
<td>{{ job.created }}</td>
<td>{{ job.done_at }}</td>
{% if job.task_result and job.task_result|is_url == True %}
<td><a href="{{ job.task_result }}">Result</a></td>
<td>
<a href="{{ job.task_result }}" class="link link-primary"
>Result</a
>
</td>
{% elif job.task_result %}
<td>{{ job.task_result|slice:"40" }}...</td>
{% else %}
@ -70,19 +58,31 @@
</table>
</div>
</div>
<!-- Unreviewable objects such as User / Group / Setting -->
<ul class="list-group">
{% for obj in objects %}
{% if object_type == 'user' %}
<a class="list-group-item" href="{{ obj.url }}"
>{{ obj.username }}</a
>
{% else %}
<a class="list-group-item" href="{{ obj.url }}">{{ obj.name }}</a>
{% endif %}
{% endfor %}
</ul>
</div>
{% if objects %}
<!-- Unreviewable objects such as User / Group / Setting -->
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<ul class="menu bg-base-200 rounded-box">
{% for obj in objects %}
{% if object_type == 'user' %}
<li>
<a href="{{ obj.url }}" class="hover:bg-base-300"
>{{ obj.username }}</a
>
</li>
{% else %}
<li>
<a href="{{ obj.url }}" class="hover:bg-base-300"
>{{ obj.name }}</a
>
</li>
{% endif %}
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>
{% endblock content %}

View File

@ -1,28 +1,32 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% load static %}
{% block content %}
{% if object_type != 'package' %}
<div>
<div id="load-all-error" style="display: none;">
<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>
{# Serialize objects data for Alpine pagination #}
{# prettier-ignore-start #}
{# FIXME: This is a hack to get the objects data into the JavaScript code. #}
<script>
window.reviewedObjects = [
{% for obj in reviewed_objects %}
{ "name": "{{ obj.name|escapejs }}", "url": "{{ obj.url }}" }{% if not forloop.last %},{% endif %}
{% endfor %}
];
window.unreviewedObjects = [
{% for obj in unreviewed_objects %}
{ "name": "{{ obj.name|escapejs }}", "url": "{{ obj.url }}" }{% if not forloop.last %},{% endif %}
{% endfor %}
];
</script>
{# prettier-ignore-end #}
{% if object_type != 'package' %}
<div class="px-8 py-4">
<input
type="text"
id="object-search"
class="form-control"
class="input input-bordered hidden w-full max-w-xs"
placeholder="Search by name"
style="display: none;"
/>
<p></p>
</div>
{% endif %}
@ -56,423 +60,474 @@
{% endif %}
{% endblock action_modals %}
<div class="panel-group" id="reviewListAccordion">
<div class="panel panel-default">
<div
class="panel-heading"
id="headingPanel"
style="font-size:2rem;height: 46px"
>
{% if object_type == 'package' %}
Packages
{% elif object_type == 'compound' %}
Compounds
{% elif object_type == 'structure' %}
Compound structures
{% elif object_type == 'rule' %}
Rules
{% elif object_type == 'reaction' %}
Reactions
{% elif object_type == 'pathway' %}
Pathways
{% elif object_type == 'node' %}
Nodes
{% elif object_type == 'edge' %}
Edges
{% elif object_type == 'scenario' %}
Scenarios
{% elif object_type == 'model' %}
Model
{% elif object_type == 'setting' %}
Settings
{% elif object_type == 'user' %}
Users
{% elif object_type == 'group' %}
Groups
{% endif %}
<div
id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"
>
<a
href="#"
class="dropdown-toggle"
data-toggle="dropdown"
role="button"
aria-haspopup="true"
aria-expanded="false"
><span class="glyphicon glyphicon-wrench"></span> Actions
<span class="caret"></span><span style="padding-right:1em"></span
></a>
<ul id="actionsList" class="dropdown-menu">
{% block actions %}
{% if object_type == 'package' %}
{% include "actions/collections/package.html" %}
{% elif object_type == 'compound' %}
{% include "actions/collections/compound.html" %}
{% elif object_type == 'structure' %}
{% include "actions/collections/compound_structure.html" %}
{% elif object_type == 'rule' %}
{% include "actions/collections/rule.html" %}
{% elif object_type == 'reaction' %}
{% include "actions/collections/reaction.html" %}
{% elif object_type == 'setting' %}
{% include "actions/collections/setting.html" %}
{% elif object_type == 'scenario' %}
{% include "actions/collections/scenario.html" %}
{% elif object_type == 'model' %}
{% include "actions/collections/model.html" %}
{% elif object_type == 'pathway' %}
{% include "actions/collections/pathway.html" %}
{% elif object_type == 'node' %}
{% include "actions/collections/node.html" %}
{% elif object_type == 'edge' %}
{% include "actions/collections/edge.html" %}
{% elif object_type == 'group' %}
{% include "actions/collections/group.html" %}
{% endif %}
{% endblock %}
</ul>
<div class="px-8 py-4">
<!-- Header Section -->
<div class="card bg-base-100">
<div class="card-body px-0 py-4">
<div class="flex items-center justify-between">
<h2 class="card-title text-2xl">
{% if object_type == 'package' %}
Packages
{% elif object_type == 'compound' %}
Compounds
{% elif object_type == 'structure' %}
Compound structures
{% elif object_type == 'rule' %}
Rules
{% elif object_type == 'reaction' %}
Reactions
{% elif object_type == 'pathway' %}
Pathways
{% elif object_type == 'node' %}
Nodes
{% elif object_type == 'edge' %}
Edges
{% elif object_type == 'scenario' %}
Scenarios
{% elif object_type == 'model' %}
Model
{% elif object_type == 'setting' %}
Settings
{% elif object_type == 'user' %}
Users
{% elif object_type == 'group' %}
Groups
{% endif %}
</h2>
<div id="actionsButton" class="dropdown dropdown-end hidden">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-wrench"
>
<path
d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"
/>
</svg>
Actions
</div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2"
>
{% block actions %}
{% if object_type == 'package' %}
{% include "actions/collections/package.html" %}
{% elif object_type == 'compound' %}
{% include "actions/collections/compound.html" %}
{% elif object_type == 'structure' %}
{% include "actions/collections/compound_structure.html" %}
{% elif object_type == 'rule' %}
{% include "actions/collections/rule.html" %}
{% elif object_type == 'reaction' %}
{% include "actions/collections/reaction.html" %}
{% elif object_type == 'setting' %}
{% include "actions/collections/setting.html" %}
{% elif object_type == 'scenario' %}
{% include "actions/collections/scenario.html" %}
{% elif object_type == 'model' %}
{% include "actions/collections/model.html" %}
{% elif object_type == 'pathway' %}
{% include "actions/collections/pathway.html" %}
{% elif object_type == 'node' %}
{% include "actions/collections/node.html" %}
{% elif object_type == 'edge' %}
{% include "actions/collections/edge.html" %}
{% elif object_type == 'group' %}
{% include "actions/collections/group.html" %}
{% endif %}
{% endblock %}
</ul>
</div>
</div>
</div>
<div class="panel-body">
<!-- Set Text above links -->
{% if object_type == 'package' %}
<p>
A package contains pathways, rules, etc. and can reflect specific
experimental conditions.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/packages"
role="button"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'compound' %}
<p>
A compound stores the structure of a molecule and can include
meta-information.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/compounds"
role="button"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'structure' %}
<p>
The structures stored in this compound
<a
target="_blank"
href="https://wiki.envipath.org/index.php/compounds"
role="button"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'rule' %}
<p>
A rule describes a biotransformation reaction template that is
defined as SMIRKS.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/Rules"
role="button"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'reaction' %}
<p>
A reaction is a specific biotransformation from educt compounds to
product compounds.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/reactions"
role="button"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'pathway' %}
<p>
A pathway displays the (predicted) biodegradation of a compound as
graph.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/pathways"
role="button"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'node' %}
<p>
Nodes represent the (predicted) compounds in a graph.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/nodes"
role="button"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'edge' %}
<p>
Edges represent the links between Nodes in a graph
<a
target="_blank"
href="https://wiki.envipath.org/index.php/edges"
role="button"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'scenario' %}
<p>
A scenario contains meta-information that can be attached to other
data (compounds, rules, ..).
<a
target="_blank"
href="https://wiki.envipath.org/index.php/scenarios"
role="button"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'model' %}
<p>
A model applies machine learning to limit the combinatorial
explosion.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/relative_reasoning"
role="button"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'setting' %}
<p>
A setting includes configuration parameters for pathway predictions.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/settings"
role="button"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'user' %}
<p>
Register now to create own packages and to submit and manage your
data.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/users"
role="button"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'group' %}
<p>
Users can team up in groups to share packages.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/groups"
role="button"
>Learn more &gt;&gt;</a
>
</p>
{% endif %}
<!-- If theres nothing to show extend the text above -->
{% if reviewed_objects and unreviewed_objects %}
{% if reviewed_objects|length == 0 and unreviewed_objects|length == 0 %}
<div class="mt-2">
<!-- Set Text above links -->
{% if object_type == 'package' %}
<p>
Nothing found. There are two possible reasons: <br /><br />1.
There is no content yet.<br />2. You have no reading
permissions.<br /><br />Please be sure you have at least reading
permissions.
A package contains pathways, rules, etc. and can reflect specific
experimental conditions.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/packages"
class="link link-primary"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'compound' %}
<p>
A compound stores the structure of a molecule and can include
meta-information.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/compounds"
class="link link-primary"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'structure' %}
<p>
The structures stored in this compound
<a
target="_blank"
href="https://wiki.envipath.org/index.php/compounds"
class="link link-primary"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'rule' %}
<p>
A rule describes a biotransformation reaction template that is
defined as SMIRKS.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/Rules"
class="link link-primary"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'reaction' %}
<p>
A reaction is a specific biotransformation from educt compounds to
product compounds.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/reactions"
class="link link-primary"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'pathway' %}
<p>
A pathway displays the (predicted) biodegradation of a compound as
graph.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/pathways"
class="link link-primary"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'node' %}
<p>
Nodes represent the (predicted) compounds in a graph.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/nodes"
class="link link-primary"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'edge' %}
<p>
Edges represent the links between Nodes in a graph
<a
target="_blank"
href="https://wiki.envipath.org/index.php/edges"
class="link link-primary"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'scenario' %}
<p>
A scenario contains meta-information that can be attached to other
data (compounds, rules, ..).
<a
target="_blank"
href="https://wiki.envipath.org/index.php/scenarios"
class="link link-primary"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'model' %}
<p>
A model applies machine learning to limit the combinatorial
explosion.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/relative_reasoning"
class="link link-primary"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'setting' %}
<p>
A setting includes configuration parameters for pathway
predictions.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/settings"
class="link link-primary"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'user' %}
<p>
Register now to create own packages and to submit and manage your
data.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/users"
class="link link-primary"
>Learn more &gt;&gt;</a
>
</p>
{% elif object_type == 'group' %}
<p>
Users can team up in groups to share packages.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/groups"
class="link link-primary"
>Learn more &gt;&gt;</a
>
</p>
{% endif %}
{% endif %}
<!-- If theres nothing to show extend the text above -->
{% if reviewed_objects and unreviewed_objects %}
{% if reviewed_objects|length == 0 and unreviewed_objects|length == 0 %}
<p class="mt-4">
Nothing found. There are two possible reasons: <br /><br />1.
There is no content yet.<br />2. You have no reading
permissions.<br /><br />Please be sure you have at least reading
permissions.
</p>
{% endif %}
{% endif %}
</div>
</div>
</div>
<!-- Lists Container - Full Width with Reviewed on Right -->
<div class="w-full">
{% if reviewed_objects %}
{% if reviewed_objects|length > 0 %}
<!-- Reviewed -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
class="collapse-arrow bg-base-200 collapse order-2 w-full"
x-data="paginatedList(window.reviewedObjects || [], { isReviewed: true, instanceId: 'reviewed' })"
>
<h4 class="panel-title">
<a
id="ReviewedLink"
data-toggle="collapse"
data-parent="#reviewListAccordion"
href="#Reviewed"
>Reviewed</a
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
Reviewed
<span
class="badge badge-sm badge-neutral ml-2"
x-text="totalItems"
></span>
</div>
<div class="collapse-content w-full">
<ul class="menu bg-base-100 rounded-box w-full">
<template x-for="obj in paginatedItems" :key="obj.url">
<li>
<a :href="obj.url" class="hover:bg-base-200">
<span x-text="obj.name"></span>
<span
class="tooltip tooltip-left ml-auto"
data-tip="Reviewed"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-star"
>
<polygon
points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"
/>
</svg>
</span>
</a>
</li>
</template>
</ul>
<!-- Pagination Controls -->
<div
x-show="totalPages > 1"
class="mt-4 flex items-center justify-between px-2"
>
</h4>
</div>
<div id="Reviewed" class="panel-collapse in collapse">
<div class="panel-body list-group-item" id="ReviewedContent">
{% if object_type == 'package' %}
{% for obj in reviewed_objects %}
<a class="list-group-item" href="{{ obj.url }}"
>{{ obj.name|safe }}
<span
class="glyphicon glyphicon-star"
aria-hidden="true"
style="float:right"
data-toggle="tooltip"
data-placement="top"
title=""
data-original-title="Reviewed"
>
</span>
</a>
{% endfor %}
{% else %}
{% for obj in reviewed_objects|slice:":50" %}
<a class="list-group-item" href="{{ obj.url }}"
>{{ obj.name|safe }}{# <i>({{ obj.package.name }})</i> #}
<span
class="glyphicon glyphicon-star"
aria-hidden="true"
style="float:right"
data-toggle="tooltip"
data-placement="top"
title=""
data-original-title="Reviewed"
>
</span>
</a>
{% endfor %}
{% endif %}
<span class="text-base-content/70 text-sm">
Showing <span x-text="showingStart"></span>-<span
x-text="showingEnd"
></span>
of <span x-text="totalItems"></span>
</span>
<div class="join">
<button
class="join-item btn btn-sm"
:disabled="currentPage === 1"
@click="prevPage()"
>
«
</button>
<template x-for="item in pageNumbers" :key="item.key">
<button
class="join-item btn btn-sm"
:class="{ 'btn-active': item.page === currentPage }"
:disabled="item.isEllipsis"
@click="!item.isEllipsis && goToPage(item.page)"
x-text="item.page"
></button>
</template>
<button
class="join-item btn btn-sm"
:disabled="currentPage === totalPages"
@click="nextPage()"
>
»
</button>
</div>
</div>
</div>
</div>
{% endif %}
{% endif %}
{% if unreviewed_objects %}
<!-- Unreviewed -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
class="collapse-arrow bg-base-200 collapse order-1 w-full"
x-data="paginatedList(window.unreviewedObjects || [], { isReviewed: false, instanceId: 'unreviewed' })"
>
<h4 class="panel-title">
<a
id="UnreviewedLink"
data-toggle="collapse"
data-parent="#unReviewListAccordion"
href="#Unreviewed"
>Unreviewed</a
<input
type="checkbox"
{% if reviewed_objects|length == 0 or object_type == 'package' %}checked{% endif %}
/>
<div class="collapse-title text-xl font-medium">
Unreviewed
<span
class="badge badge-sm badge-neutral ml-2"
x-text="totalItems"
></span>
</div>
<div class="collapse-content w-full">
<ul class="menu bg-base-100 rounded-box w-full">
<template x-for="obj in paginatedItems" :key="obj.url">
<li>
<a
:href="obj.url"
class="hover:bg-base-200"
x-text="obj.name"
></a>
</li>
</template>
</ul>
<!-- Pagination Controls -->
<div
x-show="totalPages > 1"
class="mt-4 flex items-center justify-between px-2"
>
</h4>
</div>
<div
id="Unreviewed"
class="panel-collapse {% if reviewed_objects|length == 0 or object_type == 'package' %}in{% endif %} collapse"
>
<div class="panel-body list-group-item" id="UnreviewedContent">
{% if object_type == 'package' %}
{% for obj in unreviewed_objects %}
<a class="list-group-item" href="{{ obj.url }}"
>{{ obj.name|safe }}</a
<span class="text-base-content/70 text-sm">
Showing <span x-text="showingStart"></span>-<span
x-text="showingEnd"
></span>
of <span x-text="totalItems"></span>
</span>
<div class="join">
<button
class="join-item btn btn-sm"
:disabled="currentPage === 1"
@click="prevPage()"
>
{% endfor %}
{% else %}
{% for obj in unreviewed_objects|slice:":50" %}
<a class="list-group-item" href="{{ obj.url }}"
>{{ obj.name|safe }}</a
«
</button>
<template x-for="item in pageNumbers" :key="item.key">
<button
class="join-item btn btn-sm"
:class="{ 'btn-active': item.page === currentPage }"
:disabled="item.isEllipsis"
@click="!item.isEllipsis && goToPage(item.page)"
x-text="item.page"
></button>
</template>
<button
class="join-item btn btn-sm"
:disabled="currentPage === totalPages"
@click="nextPage()"
>
{% endfor %}
{% endif %}
»
</button>
</div>
</div>
</div>
</div>
{% endif %}
{% if objects %}
<!-- Unreviewable objects such as User / Group / Setting -->
<ul class="list-group">
{% for obj in objects %}
{% if object_type == 'user' %}
<a class="list-group-item" href="{{ obj.url }}"
>{{ obj.username|safe }}</a
>
{% else %}
<a class="list-group-item" href="{{ obj.url }}"
>{{ obj.name|safe }}</a
>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</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>
<div id="load-all-loading" class="spinner-widget" style="display: none">
<img
id="loading-gif"
src="{% static '/images/wait.gif' %}"
alt="Loading..."
/>
</div>
{% if objects %}
<!-- Unreviewable objects such as User / Group / Setting -->
<div class="card bg-base-100">
<div class="card-body">
<ul class="menu bg-base-200 rounded-box">
{% for obj in objects %}
{% if object_type == 'user' %}
<li>
<a href="{{ obj.url }}" class="hover:bg-base-300"
>{{ obj.username }}</a
>
</li>
{% else %}
<li>
<a href="{{ obj.url }}" class="hover:bg-base-300"
>{{ obj.name }}</a
>
</li>
{% endif %}
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>
{# prettier-ignore-start #}
<script>
$(function () {
$('#object-search').show();
<script>
document.addEventListener("DOMContentLoaded", function () {
// Show actions button if there are actions
const actionsButton = document.getElementById("actionsButton");
const actionsList = actionsButton?.querySelector("ul");
if (actionsList && actionsList.children.length > 0) {
actionsButton?.classList.remove("hidden");
}
{% if object_type != 'package' and object_type != 'user' and object_type != 'group' %}
{% if reviewed_objects|length > 50 or unreviewed_objects|length > 50 %}
$('#load-all-loading').show()
setTimeout(function () {
$('#load-all-error').hide();
$.getJSON('?all=true', function (resp) {
$('#ReviewedContent').empty();
$('#UnreviewedContent').empty();
for (o in resp.objects) {
obj = resp.objects[o];
if (obj.reviewed) {
$('#ReviewedContent').append('<a class="list-group-item" href="' + obj.url + '">' + obj.name + ' <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 {
$('#UnreviewedContent').append('<a class="list-group-item" href="' + obj.url + '">' + obj.name + '</a>');
}
}
$('#load-all-loading').hide();
$('#load-remaining').hide();
}).fail(function (resp) {
$('#load-all-loading').hide();
$('#load-all-error').show();
});
}, 2500);
{% endif %}
{% endif %}
$('#modal-form-delete-submit').on('click', function (e) {
e.preventDefault();
$('#modal-form-delete').submit();
// Show search input and connect to Alpine pagination
const objectSearch = document.getElementById("object-search");
if (objectSearch) {
objectSearch.classList.remove("hidden");
objectSearch.addEventListener("input", function () {
const query = this.value;
// Dispatch search to all paginatedList components
document
.querySelectorAll('[x-data*="paginatedList"]')
.forEach((el) => {
if (el._x_dataStack && el._x_dataStack[0]) {
el._x_dataStack[0].search(query);
}
});
$('#object-search').on('keyup', function () {
let query = $(this).val().toLowerCase();
$('a.list-group-item').each(function () {
let text = $(this).text().toLowerCase();
$(this).toggle(text.indexOf(query) !== -1);
});
});
});
</script>
{# prettier-ignore-end #}
}
// Delete form submit handler
const deleteSubmit = document.getElementById("modal-form-delete-submit");
const deleteForm = document.getElementById("modal-form-delete");
if (deleteSubmit && deleteForm) {
deleteSubmit.addEventListener("click", function (e) {
e.preventDefault();
deleteForm.submit();
});
}
});
</script>
{% endblock content %}

View File

@ -1,18 +1,77 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% load static %}
{% block content %}
<div class="alert alert-error" role="alert">
<h4 class="alert-heading">Bad Request!</h4>
<p>Lorem</p>
<hr />
<p class="mb-0">
You can find out more about permissions in our
<a
target="_blank"
href="https://wiki.envipath.org/index.php/packages"
role="button"
>Wiki &gt;&gt;</a
>
</p>
<div class="flex min-h-[60vh] flex-col items-center justify-center p-8">
<div class="w-full max-w-2xl">
<div class="alert alert-error mb-6 shadow-lg">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8 shrink-0 stroke-current"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<div class="flex flex-col">
<h3 class="text-lg font-bold">Bad Request</h3>
<p class="text-sm">The request you sent was invalid or malformed.</p>
</div>
</div>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title mb-4 text-2xl">What happened?</h2>
<p class="text-base-content/70 mb-4">
The server couldn't process your request because it contains invalid
data or parameters.
</p>
<div class="card-actions mt-6 justify-end">
<a href="/" class="btn btn-primary">
<svg
xmlns="http://www.w3.org/2000/svg"
class="mr-2 h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
/>
</svg>
Go Home
</a>
<a
href="https://wiki.envipath.org/index.php/packages"
target="_blank"
class="btn btn-outline"
>
Learn More
<svg
xmlns="http://www.w3.org/2000/svg"
class="ml-2 h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
/>
</svg>
</a>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}

View File

@ -1,18 +1,80 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% load static %}
{% block content %}
<div class="alert alert-error" role="alert">
<h4 class="alert-heading">Access Denied!</h4>
<p>Access to X denied.</p>
<hr />
<p class="mb-0">
You can find out more about permissions in our
<a
target="_blank"
href="https://wiki.envipath.org/index.php/packages"
role="button"
>Wiki &gt;&gt;</a
>
</p>
<div class="flex min-h-[60vh] flex-col items-center justify-center p-8">
<div class="w-full max-w-2xl">
<div class="alert alert-warning mb-6 shadow-lg">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8 shrink-0 stroke-current"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<div class="flex flex-col">
<h3 class="text-lg font-bold">Access Denied</h3>
<p class="text-sm">
You don't have permission to access this resource.
</p>
</div>
</div>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title mb-4 text-2xl">Permission Required</h2>
<p class="text-base-content/70 mb-4">
You need the appropriate permissions to access this content. If you
believe this is an error, please contact the package owner or
administrator.
</p>
<div class="card-actions mt-6 justify-end">
<a href="/" class="btn btn-primary">
<svg
xmlns="http://www.w3.org/2000/svg"
class="mr-2 h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
/>
</svg>
Go Home
</a>
<a
href="https://wiki.envipath.org/index.php/packages"
target="_blank"
class="btn btn-outline"
>
Learn More
<svg
xmlns="http://www.w3.org/2000/svg"
class="ml-2 h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
/>
</svg>
</a>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}

View File

@ -1,18 +1,77 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% load static %}
{% block content %}
<div class="alert alert-error" role="alert">
<h4 class="alert-heading">Not Found!</h4>
<p>Does not exist</p>
<hr />
<p class="mb-0">
You can find out more about permissions in our
<a
target="_blank"
href="https://wiki.envipath.org/index.php/packages"
role="button"
>Wiki &gt;&gt;</a
>
</p>
<div class="flex min-h-[60vh] flex-col items-center justify-center p-8">
<div class="w-full max-w-2xl">
<div class="alert alert-info mb-6 shadow-lg">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8 shrink-0 stroke-current"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<div class="flex flex-col">
<h3 class="text-lg font-bold">Page Not Found</h3>
<p class="text-sm">The page you're looking for doesn't exist.</p>
</div>
</div>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title mb-4 text-2xl">404 Error</h2>
<p class="text-base-content/70 mb-4">
The page or resource you requested could not be found. It may have
been moved, deleted, or the URL might be incorrect.
</p>
<div class="card-actions mt-6 justify-end">
<a href="/" class="btn btn-primary">
<svg
xmlns="http://www.w3.org/2000/svg"
class="mr-2 h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
/>
</svg>
Go Home
</a>
<a
href="https://wiki.envipath.org/index.php/packages"
target="_blank"
class="btn btn-outline"
>
Learn More
<svg
xmlns="http://www.w3.org/2000/svg"
class="ml-2 h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
/>
</svg>
</a>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}

View File

@ -1,9 +1,76 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% load static %}
{% block content %}
<div class="alert alert-danger" role="alert">
<h4 class="alert-heading">{{ error_message }}</h4>
<hr />
<p class="mb-0">{{ error_detail }}</p>
<div class="flex min-h-[60vh] flex-col items-center justify-center p-8">
<div class="w-full max-w-2xl">
<div class="alert alert-error mb-6 shadow-lg">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8 shrink-0 stroke-current"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<div class="flex flex-col">
<h3 class="text-lg font-bold">
{{ error_message|default:"An Error Occurred" }}
</h3>
<p class="text-sm">
{{ error_detail|default:"Something went wrong. Please try again later." }}
</p>
</div>
</div>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title mb-4 text-2xl">Oops! Something went wrong</h2>
<p class="text-base-content/70 mb-4">
{{ error_description|default:"We encountered an unexpected error while processing your request. Our team has been notified and is working to resolve the issue." }}
</p>
<div class="card-actions mt-6 justify-end">
<a href="/" class="btn btn-primary">
<svg
xmlns="http://www.w3.org/2000/svg"
class="mr-2 h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
/>
</svg>
Go Home
</a>
<button onclick="window.history.back()" class="btn btn-outline">
<svg
xmlns="http://www.w3.org/2000/svg"
class="mr-2 h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 19l-7-7m0 0l7-7m-7 7h18"
/>
</svg>
Go Back
</button>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}

View File

@ -1,11 +1,81 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% load static %}
{% block content %}
<div class="alert alert-danger" role="alert">
<h4 class="alert-heading">Your account has not been activated yet!</h4>
<p>
Your account has not been activated yet. If you have questions
<a href="mailto:admin@envipath.org">contact us.</a>
</p>
<div class="flex min-h-[60vh] flex-col items-center justify-center p-8">
<div class="w-full max-w-2xl">
<div class="alert alert-warning mb-6 shadow-lg">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8 shrink-0 stroke-current"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<div class="flex flex-col">
<h3 class="text-lg font-bold">Account Not Activated</h3>
<p class="text-sm">Your account is pending activation.</p>
</div>
</div>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title mb-4 text-2xl">Account Activation Required</h2>
<p class="text-base-content/70 mb-4">
Your account has not been activated yet. An administrator needs to
approve your account before you can access all features. This
process typically takes 24-48 hours.
</p>
<div class="divider"></div>
<p class="text-base-content/70 mb-4">
If you have questions or believe this is an error, please
<a href="mailto:admin@envipath.org" class="link link-primary"
>contact us</a
>.
</p>
<div class="card-actions mt-6 justify-end">
<a href="/" class="btn btn-primary">
<svg
xmlns="http://www.w3.org/2000/svg"
class="mr-2 h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
/>
</svg>
Go Home
</a>
<a href="mailto:admin@envipath.org" class="btn btn-outline">
<svg
xmlns="http://www.w3.org/2000/svg"
class="mr-2 h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
/>
</svg>
Contact Admin
</a>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}

View File

@ -228,15 +228,7 @@
>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>
@ -408,10 +400,5 @@
}
});
</script>
{% block modals %}
{% include "modals/cite_modal.html" %}
{% include "modals/predict_modal.html" %}
{% include "modals/batch_predict_modal.html" %}
{% endblock %}
</body>
</html>

View File

@ -21,8 +21,14 @@
type="text/css"
/>
{# jQuery - Keep for compatibility with existing JS #}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
{# Alpine.js - For reactive components #}
<script
defer
src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"
></script>
<script src="{% static 'js/alpine/index.js' %}"></script>
<script src="{% static 'js/alpine/search.js' %}"></script>
<script src="{% static 'js/alpine/pagination.js' %}"></script>
{# Font Awesome #}
<link
@ -35,21 +41,10 @@
<script>
const csrftoken = document.querySelector("[name=csrf-token]").content;
// Setup CSRF header for all jQuery AJAX requests
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type)) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
},
});
</script>
{# 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 -->
@ -171,10 +166,11 @@
{% endblock %}
<script>
$(function () {
// Hide actionsbutton if there's no action defined
if ($("#actionsButton ul").children().length > 0) {
$("#actionsButton").show();
document.addEventListener("DOMContentLoaded", function () {
// Show actions button if there are actions defined
const actionsButtonUl = document.querySelector("#actionsButton ul");
if (actionsButtonUl && actionsButtonUl.children.length > 0) {
document.getElementById("actionsButton").style.display = "";
}
});

View File

@ -1,140 +1,267 @@
{% load static %}
{# Modern DaisyUI Navbar #}
<div class="navbar x-50 bg-neutral-50 text-neutral-950 shadow-lg">
<div class="navbar-start">
<a href="{{ meta.server_url }}" class="btn btn-ghost text-xl normal-case">
<svg class="fill-base-content h-8" viewBox="0 0 104 26" role="img">
<use href="{% static "/images/logo-name.svg" %}#ep-logo-name" />
</svg>
</a>
</div>
{% if not public_mode %}
<div class="navbar-center hidden lg:flex">
<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> -->
<!--<li><a href="{{ meta.server_url }}/browse" id="browseLink">Browse</a></li>-->
<div class="dropdown dropdown-center">
<div tabindex="0" role="button" class="btn btn-ghost">Browse</div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm"
{# Modern DaisyUI Navbar with Mobile Drawer Menu #}
<div class="drawer drawer-mobile">
<input id="drawer-toggle" type="checkbox" class="drawer-toggle" />
<div class="drawer-content flex flex-col">
{# Navbar #}
<div class="navbar x-50 bg-neutral-50 text-neutral-950 shadow-lg">
<div class="navbar-start">
{# Hamburger menu button - visible on mobile, hidden on desktop #}
{% if not public_mode %}
<label
for="drawer-toggle"
class="btn btn-square btn-ghost drawer-button lg:hidden"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
class="inline-block h-5 w-5 stroke-current"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"
></path>
</svg>
</label>
{% endif %}
<a
href="{{ meta.server_url }}"
class="btn btn-ghost text-xl normal-case"
>
<li>
<a href="{{ meta.server_url }}/package" id="packageLink">Package</a>
</li>
<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
<svg class="fill-base-content h-8" viewBox="0 0 104 26" role="img">
<use href="{% static "/images/logo-name.svg" %}#ep-logo-name" />
</svg>
</a>
</div>
{% if not public_mode %}
{# Desktop menu - hidden on mobile, visible on desktop #}
<div class="navbar-center hidden lg:flex">
<a
href="{{ meta.server_url }}/predict"
role="button"
class="btn btn-ghost"
id="predictLink"
>Predict</a
>
<div class="dropdown dropdown-center">
<div tabindex="0" role="button" class="btn btn-ghost">Browse</div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm"
>
</li>
<li>
<a href="{{ meta.server_url }}/reaction" id="reactionLink"
>Reaction</a
<li>
<a href="{{ meta.server_url }}/package" id="packageLink"
>Package</a
>
</li>
<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>
</ul>
</div>
</div>
{% endif %}
<div class="navbar-end">
{% if not public_mode %}
<a id="search-trigger" role="button" class="cursor-pointer">
<div
class="badge badge-dash bg-base-200 text-base-content/50 m-1 flex items-center space-x-1 p-2"
>
</li>
<li>
<a href="{{ meta.server_url }}/model" id="relative-reasoningLink"
>Model</a
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-search-icon lucide-search"
>
<path d="m21 21-4.34-4.34" />
<circle cx="11" cy="11" r="8" />
</svg>
<span id="search-shortcut">⌘K</span>
</div>
</a>
{% endif %}
{% if meta.user.username == 'anonymous' or public_mode %}
<a href="{% url 'login' %}" id="loginButton" class="p-2">Login</a>
{% else %}
<div class="dropdown dropdown-end">
<div
tabindex="0"
role="button"
class="btn btn-ghost btn-circle m-1"
id="loggedInButton"
>
</li>
<li>
<a href="{{ meta.server_url }}/scenario" id="scenarioLink"
>Scenario</a
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-circle-user-icon lucide-circle-user"
>
<circle cx="12" cy="12" r="10" />
<circle cx="12" cy="10" r="3" />
<path d="M7 20.662V19a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v1.662" />
</svg>
</div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2 shadow-sm"
>
</li>
</ul>
<li>
<a href="{{ meta.user.url }}" id="accountbutton">Settings</a>
</li>
<li>
<form
id="logoutForm"
action="{% url 'logout' %}"
method="post"
style="display: none;"
>
{% csrf_token %}
<input type="hidden" name="logout" value="true" />
</form>
<a
href="#"
id="logoutButton"
onclick="event.preventDefault(); document.getElementById('logoutForm').submit();"
>Logout</a
>
</li>
</ul>
</div>
{% endif %}
</div>
</div>
{% endif %}
<div class="navbar-end">
{% if not public_mode %}
<a id="search-trigger" role="button" class="cursor-pointer">
<div
class="badge badge-dash bg-base-200 text-base-content/50 m-1 flex items-center space-x-1 p-2"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-search-icon lucide-search"
</div>
{# Mobile drawer menu - slides in from the left #}
<div class="drawer-side">
<label for="drawer-toggle" class="drawer-overlay"></label>
<ul class="menu min-h-full w-80 bg-base-200 p-4 text-base-content">
{# Drawer header with close button #}
<li class="mb-4">
<div class="flex items-center justify-between">
<span class="font-bold text-lg">Menu</span>
<label
for="drawer-toggle"
class="btn btn-sm btn-circle btn-ghost"
aria-label="Close menu"
>
<path d="m21 21-4.34-4.34" />
<circle cx="11" cy="11" r="8" />
</svg>
<span id="search-shortcut">⌘K</span>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</label>
</div>
</a>
{% endif %}
{% if meta.user.username == 'anonymous' or public_mode %}
<a href="{% url 'login' %}" id="loginButton" class="p-2">Login</a>
{% else %}
<div class="dropdown dropdown-end">
<div
tabindex="0"
role="button"
class="btn btn-ghost btn-circle m-1"
id="loggedInButton"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-circle-user-icon lucide-circle-user"
</li>
{% if not public_mode %}
{# Predict link #}
<li>
<a
href="{{ meta.server_url }}/predict"
class="text-lg"
id="predictLinkMobile"
>Predict</a
>
<circle cx="12" cy="12" r="10" />
<circle cx="12" cy="10" r="3" />
<path d="M7 20.662V19a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v1.662" />
</svg>
</div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2 shadow-sm"
>
<li><a href="{{ meta.user.url }}" id="accountbutton">Settings</a></li>
<li>
<form
id="logoutForm"
action="{% url 'logout' %}"
method="post"
style="display: none;"
>
{% csrf_token %}
<input type="hidden" name="logout" value="true" />
</form>
<a
href="#"
id="logoutButton"
onclick="event.preventDefault(); document.getElementById('logoutForm').submit();"
>Logout</a
>
</li>
</ul>
</div>
{% endif %}
</li>
{# Browse menu with submenu #}
<li>
<details>
<summary class="text-lg">Browse</summary>
<ul>
<li>
<a href="{{ meta.server_url }}/package" id="packageLinkMobile"
>Package</a
>
</li>
<li>
<a href="{{ meta.server_url }}/pathway" id="pathwayLinkMobile"
>Pathway</a
>
</li>
<li>
<a href="{{ meta.server_url }}/rule" id="ruleLinkMobile"
>Rule</a
>
</li>
<li>
<a href="{{ meta.server_url }}/compound" id="compoundLinkMobile"
>Compound</a
>
</li>
<li>
<a href="{{ meta.server_url }}/reaction" id="reactionLinkMobile"
>Reaction</a
>
</li>
<li>
<a
href="{{ meta.server_url }}/model"
id="relative-reasoningLinkMobile"
>Model</a
>
</li>
<li>
<a href="{{ meta.server_url }}/scenario" id="scenarioLinkMobile"
>Scenario</a
>
</li>
</ul>
</details>
</li>
{% endif %}
</ul>
</div>
</div>

View File

@ -9,7 +9,7 @@
>
<div class="hero-overlay"></div>
<!-- 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">
<h2 class="text-base-100 text-left text-3xl text-shadow-lg">
Predict Your Pathway
</h2>
@ -20,16 +20,68 @@
<div class="bg-base-200 mx-auto max-w-5xl shadow-md">
<!-- Predict Pathway Section -->
<div
class="relative z-20 mx-auto -mt-32 mb-10 w-full flex-col lg:flex-row-reverse"
class="relative mx-auto -mt-32 mb-10 w-full flex-col lg:flex-row-reverse"
>
<div
class="card bg-base-100 mx-auto w-3/4 shrink-0 shadow-xl transition-all duration-300 ease-in-out"
x-data="{
drawMode: false,
smiles: '',
loadExample(smilesStr, linkEl) {
if (this.drawMode && window.indexKetcher && window.indexKetcher.setMolecule) {
window.indexKetcher.setMolecule(smilesStr);
} else {
this.smiles = smilesStr;
}
const original = linkEl.textContent;
linkEl.textContent = 'loaded!';
setTimeout(() => linkEl.textContent = original, 1000);
},
syncFromKetcher() {
const ketcher = getKetcherInstance('index-ketcher');
if (ketcher && ketcher.getSmiles) {
try {
const s = ketcher.getSmiles();
if (s && s.trim()) this.smiles = s;
} catch (err) {
console.error('Failed to sync from Ketcher:', err);
}
}
},
submitForm() {
let finalSmiles = '';
if (this.drawMode) {
const ketcher = getKetcherInstance('index-ketcher');
if (ketcher && ketcher.getSmiles) {
try {
finalSmiles = ketcher.getSmiles().trim();
} catch (err) {
console.error('Failed to get SMILES from Ketcher:', err);
alert('Unable to extract structure. Please try again or switch to SMILES input.');
return;
}
} else {
alert('The drawing editor is still loading. Please wait and try again.');
return;
}
} else {
finalSmiles = this.smiles.trim();
}
if (!finalSmiles) {
alert('Please enter a SMILES string or draw a structure.');
return;
}
document.getElementById('index-form-smiles').value = finalSmiles;
document.getElementById('index-form').submit();
}
}"
x-init="$watch('drawMode', value => { if (!value) syncFromKetcher(); })"
>
<div class="card-body">
<div class="my-4 ml-8 flex h-fit flex-row items-center justify-start">
<div class="flex items-center gap-1">
<label class="swap btn btn-ghost btn-sm p-1" title="Input Mode">
<input type="checkbox" />
<input type="checkbox" x-model="drawMode" />
<span class="swap-on flex items-center gap-1">
<div
class="bg-neutral/50 text-neutral-content flex items-center justify-center rounded-full p-1"
@ -82,16 +134,24 @@
<fieldset
class="fieldset overflow-hidden transition-all duration-300 ease-in-out"
:class="drawMode ? 'p-4' : 'p-8'"
>
<form
id="index-form"
action="{{ meta.current_package.url }}/pathway"
method="POST"
@submit.prevent="submitForm()"
>
{% csrf_token %}
<div
id="text-input-container"
class="scale-100 transform opacity-100 transition-all duration-300 ease-in-out"
x-show="!drawMode"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-300"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
>
<div class="join mx-auto w-full">
<input
@ -99,6 +159,7 @@
id="index-form-text-input"
placeholder="canonical SMILES string"
class="input input-md join-item grow"
x-model="smiles"
/>
<button class="btn btn-neutral join-item">Predict!</button>
</div>
@ -107,15 +168,15 @@
<a
href="#"
class="example-link hover:text-primary cursor-pointer"
data-smiles="CN1C=NC2=C1C(=O)N(C(=O)N2C)C"
title="load example"
@click.prevent="loadExample('CN1C=NC2=C1C(=O)N(C(=O)N2C)C', $el)"
>Caffeine</a
>
<a
href="#"
class="example-link hover:text-primary cursor-pointer"
data-smiles="CC(C)CC1=CC=C(C=C1)C(C)C(=O)O"
title="load example"
@click.prevent="loadExample('CC(C)CC1=CC=C(C=C1)C(C)C(=O)O', $el)"
>Ibuprofen</a
>
</div>
@ -128,7 +189,14 @@
</div>
<div
id="ketcher-container"
class="hidden w-full scale-95 transform opacity-0 transition-all duration-300 ease-in-out"
x-show="drawMode"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-300"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
class="w-full"
>
<iframe
id="index-ketcher"
@ -337,166 +405,20 @@
// 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
const ketcher = getKetcherInstance("index-ketcher");
if (ketcher && ketcher.getSmiles) {
try {
const smiles = ketcher.getSmiles();
if (smiles && smiles.trim() !== "") {
$("#index-form-text-input").val(smiles);
}
} catch (err) {
console.error("Failed to sync Ketcher to text input:", err);
// Non-critical error, just log it
}
}
}
}
// 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 () {
// Ketcher iframe load handler - set up change event to sync SMILES
document.addEventListener("DOMContentLoaded", function () {
const indexKetcher = document.getElementById("index-ketcher");
indexKetcher.addEventListener("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")) {
// Use the robust getter function
const ketcher = getKetcherInstance("index-ketcher");
if (ketcher && ketcher.getSmiles) {
try {
textSmiles = ketcher.getSmiles().trim();
} catch (err) {
console.error("Failed to get SMILES from Ketcher:", err);
alert(
"Unable to extract structure from the drawing editor. Please try again or switch to SMILES input mode.",
);
return;
}
} else {
console.warn("Ketcher not available, possibly still loading");
alert(
"The drawing editor is still loading. Please wait a moment and try again.",
);
return;
}
} else {
textSmiles = $("#index-form-text-input").val().trim();
}
if (textSmiles === "") {
alert("Please enter a SMILES string or draw a structure.");
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>
{% endblock main_content %}

View File

@ -1,48 +0,0 @@
<div
class="modal fade bs-modal-lg"
id="citemodal"
tabindex="-1"
role="dialog"
aria-labelledby="myLargeModalLabel"
aria-hidden="true"
>
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h3>How to cite enviPath</h3>
</div>
<div class="modal-body">
<ol class="list-group list-group-numbered">
<li class="list-group-item">
Hafner, J., Lorsbach, T., Schmidt, S. <em>et al.</em>
<cite
>Advancements in biotransformation pathway prediction:
enhancements, datasets, and novel functionalities in
enviPath.</cite
>
<a href="https://doi.org/10.1186/s13321-024-00881-6" target="_blank"
>J Cheminform 16, 93 (2024)</a
>
</li>
<li class="list-group-item">
Wicker, J., Lorsbach, T., Gütlein, M., Schmid, E., Latino, D.,
Kramer, S., Fenner, K.
<cite
>enviPath - The environmental contaminant biotransformation
pathway resource</cite
>
<a href="https://doi.org/10.1093/nar/gkv1229" target="_blank">
Nucleic Acids Research, Volume 44, Issue D1, 4 January 2016, Pages
D502-D508
</a>
</li>
</ol>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">
Close
</button>
</div>
</div>
</div>
</div>

View File

@ -1,70 +1,85 @@
<div
class="modal fade"
tabindex="-1"
<dialog
id="import_legacy_package_modal"
role="dialog"
aria-labelledby="import_legacy_package_modal"
aria-hidden="true"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
<h4 class="modal-title">Import Package from legacy System</h4>
</div>
<div class="modal-body">
<p>Create a Package based on the JSON Export of the legacy system.</p>
<form
id="import-legacy-package-modal-form"
accept-charset="UTF-8"
data-remote="true"
method="post"
enctype="multipart/form-data"
>
{% csrf_token %}
<p>
<label class="btn btn-primary" for="legacyJsonFile">
<input
id="legacyJsonFile"
name="file"
type="file"
style="display:none;"
onchange="$('#upload-legacy-file-info').html(this.files[0].name)"
/>
Choose JSON File
</label>
<span class="label label-info" id="upload-legacy-file-info"></span>
<input
type="hidden"
value="import-legacy-package-json"
name="hidden"
readonly=""
/>
</p>
</form>
</div>
<div class="modal-footer">
<a
id="import-legacy-package-modal-form-submit"
class="btn btn-primary"
href="#"
>Submit</a
>
<button type="button" class="btn btn-default" data-dismiss="modal">
Cancel
</button>
</div>
<div class="modal-box">
<!-- Header -->
<h3 class="text-lg font-bold">Import Package from Legacy System</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<p class="mb-4">
Create a Package based on the JSON Export of the legacy system.
</p>
<form
id="import-legacy-package-modal-form"
accept-charset="UTF-8"
method="post"
enctype="multipart/form-data"
>
{% csrf_token %}
<div class="form-control">
<label class="label">
<span class="label-text">Legacy JSON File</span>
</label>
<input
type="file"
id="legacyJsonFile"
name="file"
class="file-input file-input-bordered w-full"
accept=".json"
required
/>
</div>
<input
type="hidden"
value="import-legacy-package-json"
name="hidden"
readonly
/>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Cancel
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('import-legacy-package-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Submit</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Importing...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#import-legacy-package-modal-form-submit").on("click", function (e) {
e.preventDefault();
$("#import-legacy-package-modal-form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,70 +1,83 @@
<div
class="modal fade"
tabindex="-1"
<dialog
id="import_package_modal"
role="dialog"
aria-labelledby="import_package_modal"
aria-hidden="true"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
<h4 class="modal-title">Import Package</h4>
</div>
<div class="modal-body">
<p>Create a Package based on a JSON Export.</p>
<form
id="import-package-modal-form"
accept-charset="UTF-8"
data-remote="true"
method="post"
enctype="multipart/form-data"
>
{% csrf_token %}
<p>
<label class="btn btn-primary" for="jsonFile">
<input
id="jsonFile"
name="file"
type="file"
style="display:none;"
onchange="$('#upload-file-info').html(this.files[0].name)"
/>
Choose JSON File
</label>
<span class="label label-info" id="upload-file-info"></span>
<input
type="hidden"
value="import-package-json"
name="hidden"
readonly=""
/>
</p>
</form>
</div>
<div class="modal-footer">
<a
id="import-package-modal-form-submit"
class="btn btn-primary"
href="#"
>Submit</a
>
<button type="button" class="btn btn-default" data-dismiss="modal">
Cancel
</button>
</div>
<div class="modal-box">
<!-- Header -->
<h3 class="text-lg font-bold">Import Package</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<p class="mb-4">Create a Package based on a JSON Export.</p>
<form
id="import-package-modal-form"
accept-charset="UTF-8"
method="post"
enctype="multipart/form-data"
>
{% csrf_token %}
<div class="form-control">
<label class="label">
<span class="label-text">JSON File</span>
</label>
<input
type="file"
id="jsonFile"
name="file"
class="file-input file-input-bordered w-full"
accept=".json"
required
/>
</div>
<input
type="hidden"
value="import-package-json"
name="hidden"
readonly
/>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Cancel
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('import-package-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Submit</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Importing...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#import-package-modal-form-submit").on("click", function (e) {
e.preventDefault();
$("#import-package-modal-form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,119 +1,137 @@
{% load static %}
<div
class="modal fade bs-modal-lg"
<dialog
id="new_compound_modal"
tabindex="-1"
aria-labelledby="new_compound_modal"
aria-modal="true"
role="dialog"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<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">Create a new Compound</h4>
</div>
<div class="modal-body">
<form
id="new_compound_modal_form"
accept-charset="UTF-8"
action="{% url 'package compound list' meta.current_package.uuid %}"
data-remote="true"
method="post"
>
{% csrf_token %}
<label for="compound-name">Name</label>
<div class="modal-box max-w-3xl">
<!-- Header -->
<h3 class="text-lg font-bold">Create a new Compound</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="new-compound-modal-form"
accept-charset="UTF-8"
action="{% url 'package compound list' meta.current_package.uuid %}"
method="post"
>
{% csrf_token %}
<div class="form-control mb-3">
<label class="label" for="compound-name">
<span class="label-text">Name</span>
</label>
<input
id="compound-name"
class="form-control"
class="input input-bordered w-full"
name="compound-name"
placeholder="Name"
required
/>
<label for="compound-description">Description</label>
</div>
<div class="form-control mb-3">
<label class="label" for="compound-description">
<span class="label-text">Description</span>
</label>
<input
id="compound-description"
class="form-control"
class="input input-bordered w-full"
name="compound-description"
placeholder="Description"
/>
<label for="compound-smiles">SMILES</label>
</div>
<div class="form-control mb-3">
<label class="label" for="compound-smiles">
<span class="label-text">SMILES</span>
</label>
<input
type="text"
class="form-control"
class="input input-bordered w-full"
name="compound-smiles"
placeholder="SMILES"
id="compound-smiles"
/>
<p></p>
<div>
<iframe
id="new_compound_ketcher"
src="{% static '/js/ketcher2/ketcher.html' %}"
width="100%"
height="510"
></iframe>
</div>
<p></p>
</form>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary pull-left"
data-dismiss="modal"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
id="new_compound_modal_form_submit"
>
Submit
</button>
</div>
</div>
<div class="mb-3">
<iframe
id="new_compound_ketcher"
src="{% static '/js/ketcher2/ketcher.html' %}"
width="100%"
height="510"
></iframe>
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('new-compound-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Submit</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Creating...</span>
</button>
</div>
</div>
</div>
<script>
function newCompoundModalketcherToNewCompoundModalTextInput() {
$("#compound-smiles").val(this.ketcher.getSmiles());
}
$(function () {
$("#new_compound_ketcher").on("load", function () {
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>
<script>
document
.getElementById("new_compound_ketcher")
.addEventListener("load", function () {
const iframe = this;
const checkKetcherReady = () => {
win = this.contentWindow;
const win = iframe.contentWindow;
if (win.ketcher && "editor" in win.ketcher) {
win.ketcher.editor.event.change.handlers.push({
once: false,
priority: 0,
f: newCompoundModalketcherToNewCompoundModalTextInput,
f: function () {
document.getElementById("compound-smiles").value =
this.ketcher.getSmiles();
},
ketcher: win.ketcher,
});
} else {
setTimeout(checkKetcherReady, 100);
}
};
checkKetcherReady();
});
$(function () {
$("#new_compound_modal_form_submit").on("click", function (e) {
e.preventDefault();
$(this).prop("disabled", true);
// submit form
$("#new_compound_modal_form").submit();
});
});
});
</script>

View File

@ -1,70 +1,96 @@
<div
class="modal fade"
tabindex="-1"
{% load static %}
<dialog
id="new_group_modal"
role="dialog"
aria-labelledby="new_group_modal"
aria-hidden="true"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
<h4 class="modal-title">New Group</h4>
</div>
<div class="modal-body">
<p>
Create new Group. You can assign users to the group once it is
created. Description can be changed after creation.
</p>
<form
id="new_group_modal_form"
accept-charset="UTF-8"
action="{{ SERVER_BASE }}/group"
data-remote="true"
method="post"
>
{% csrf_token %}
<p>
<label for="name">Name</label>
<input
id="name"
type="text"
name="group-name"
class="form-control"
placeholder="Name"
/>
</p>
<p>
<label for="description">Description</label>
<input
id="description"
type="text"
class="form-control"
placeholder="Description..."
name="group-description"
/>
</p>
</form>
</div>
<div class="modal-footer">
<a id="new_group_modal_form_submit" class="btn btn-primary" href="#"
>Submit</a
>
<button type="button" class="btn btn-default" data-dismiss="modal">
Cancel
</button>
</div>
<div class="modal-box">
<!-- Header -->
<h3 class="font-bold text-lg">New Group</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<p class="mb-4">
Create new Group. You can assign users to the group once it is created.
Description can be changed after creation.
</p>
<form
id="new-group-modal-form"
accept-charset="UTF-8"
action="{{ SERVER_BASE }}/group"
method="post"
>
{% csrf_token %}
<div class="form-control mb-3">
<label class="label" for="group-name">
<span class="label-text">Name</span>
</label>
<input
id="group-name"
class="input input-bordered w-full"
name="group-name"
placeholder="Name"
required
/>
</div>
<div class="form-control mb-3">
<label class="label" for="group-description">
<span class="label-text">Description</span>
</label>
<input
id="group-description"
type="text"
class="input input-bordered w-full"
placeholder="Description..."
name="group-description"
/>
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Cancel
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('new-group-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Submit</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Creating...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#new_group_modal_form_submit").on("click", function () {
$("#new_group_modal_form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,30 +1,66 @@
<div
class="modal fade"
tabindex="-1"
<dialog
id="new_model_modal"
role="dialog"
aria-labelledby="new_model_modal"
aria-hidden="true"
class="modal"
x-data="{
isSubmitting: false,
modelType: '',
buildAppDomain: false,
reset() {
this.isSubmitting = false;
this.modelType = '';
this.buildAppDomain = false;
},
get showMlrr() {
return this.modelType === 'mlrr';
},
get showRbrr() {
return this.modelType === 'rbrr';
},
get showEnviformer() {
return this.modelType === 'enviformer';
},
submit(formId) {
const form = document.getElementById(formId);
if (form && form.checkValidity()) {
this.isSubmitting = true;
form.submit();
} else if (form) {
form.reportValidity();
}
}
}"
@close="reset()"
>
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
<h4 class="modal-title">New Model</h4>
</div>
<div class="modal-body">
<form
id="new_model_form"
accept-charset="UTF-8"
action="{{ meta.current_package.url }}/model"
data-remote="true"
method="post"
>
{% csrf_token %}
<div class="jumbotron">
<div class="modal-box max-w-3xl">
<!-- Header -->
<h3 class="text-lg font-bold">New Model</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="new_model_form"
accept-charset="UTF-8"
action="{{ meta.current_package.url }}/model"
method="post"
>
{% csrf_token %}
<div class="alert alert-info mb-4">
<span>
Create a new Model to limit the number of degradation products in
the prediction. You just need to set a name and the packages you
want the object to be based on. There are multiple types of models
@ -32,239 +68,270 @@
<a
target="_blank"
href="https://wiki.envipath.org/index.php/relative-reasoning"
role="button"
class="link"
>wiki &gt;&gt;</a
>
</div>
<!-- Name -->
<label for="model-name">Name</label>
</span>
</div>
<!-- Name -->
<div class="form-control mb-3">
<label class="label" for="model-name">
<span class="label-text">Name</span>
</label>
<input
id="model-name"
name="model-name"
class="form-control"
class="input input-bordered w-full"
placeholder="Name"
required
/>
</div>
<!-- Description -->
<label for="model-description">Description</label>
<!-- Description -->
<div class="form-control mb-3">
<label class="label" for="model-description">
<span class="label-text">Description</span>
</label>
<input
id="model-description"
name="model-description"
class="form-control"
class="input input-bordered w-full"
placeholder="Description"
/>
</div>
<!-- Model Type -->
<label for="model-type">Model Type</label>
<!-- Model Type -->
<div class="form-control mb-3">
<label class="label" for="model-type">
<span class="label-text">Model Type</span>
</label>
<select
id="model-type"
name="model-type"
class="form-control"
data-width="100%"
class="select select-bordered w-full"
x-model="modelType"
required
>
<option disabled selected>Select Model Type</option>
<option value="" disabled selected>Select Model Type</option>
{% for k, v in model_types.items %}
<option value="{{ v }}">{{ k }}</option>
{% endfor %}
</select>
</div>
<!-- Rule Packages -->
<div id="rule-packages" class="ep-model-param mlrr rbrr">
<label for="model-rule-packages">Rule Packages</label>
<select
id="model-rule-packages"
name="model-rule-packages"
data-actions-box="true"
class="form-control"
multiple
data-width="100%"
>
<option disabled>Reviewed Packages</option>
<!-- Rule Packages (MLRR, RBRR) -->
<div class="form-control mb-3" x-show="showMlrr || showRbrr" x-cloak>
<label class="label" for="model-rule-packages">
<span class="label-text">Rule Packages</span>
</label>
<select
id="model-rule-packages"
name="model-rule-packages"
class="select select-bordered w-full h-32"
multiple
>
<optgroup label="Reviewed Packages">
{% for obj in meta.readable_packages %}
{% if obj.reviewed %}
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
{% endif %}
{% endfor %}
<option disabled>Unreviewed Packages</option>
</optgroup>
<optgroup label="Unreviewed Packages">
{% for obj in meta.readable_packages %}
{% if not obj.reviewed %}
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
{% endif %}
{% endfor %}
</select>
</div>
</optgroup>
</select>
<label class="label">
<span class="label-text-alt">Hold Ctrl/Cmd to select multiple</span>
</label>
</div>
<!-- Data Packages -->
<div id="data-packages" class="ep-model-param mlrr rbrr enviformer">
<label for="model-data-packages">Data Packages</label>
<select
id="model-data-packages"
name="model-data-packages"
data-actions-box="true"
class="form-control"
multiple
data-width="100%"
>
<option disabled>Reviewed Packages</option>
<!-- Data Packages (MLRR, RBRR, Enviformer) -->
<div
class="form-control mb-3"
x-show="showMlrr || showRbrr || showEnviformer"
x-cloak
>
<label class="label" for="model-data-packages">
<span class="label-text">Data Packages</span>
</label>
<select
id="model-data-packages"
name="model-data-packages"
class="select select-bordered w-full h-32"
multiple
>
<optgroup label="Reviewed Packages">
{% for obj in meta.readable_packages %}
{% if obj.reviewed %}
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
{% endif %}
{% endfor %}
<option disabled>Unreviewed Packages</option>
</optgroup>
<optgroup label="Unreviewed Packages">
{% for obj in meta.readable_packages %}
{% if not obj.reviewed %}
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
{% endif %}
{% endfor %}
</select>
</div>
</optgroup>
</select>
<label class="label">
<span class="label-text-alt">Hold Ctrl/Cmd to select multiple</span>
</label>
</div>
<!-- Fingerprinter -->
<div id="fingerprinter" class="ep-model-param mlrr">
<label for="model-fingerprinter">Fingerprinter</label>
<select
id="model-fingerprinter"
name="model-fingerprinter"
data-actions-box="true"
class="form-control"
multiple
data-width="100%"
>
<option value="MACCS" selected>MACCS Fingerprinter</option>
{% if meta.enabled_features.PLUGINS and additional_descriptors %}
<option disabled selected>
Select Additional Fingerprinter / Descriptor
</option>
<!-- Fingerprinter (MLRR) -->
<div class="form-control mb-3" x-show="showMlrr" x-cloak>
<label class="label" for="model-fingerprinter">
<span class="label-text">Fingerprinter</span>
</label>
<select
id="model-fingerprinter"
name="model-fingerprinter"
class="select select-bordered w-full h-32"
multiple
>
<option value="MACCS" selected>MACCS Fingerprinter</option>
{% if meta.enabled_features.PLUGINS and additional_descriptors %}
<optgroup label="Additional Fingerprinter / Descriptor">
{% for k, v in additional_descriptors.items %}
<option value="{{ v }}">{{ k }}</option>
{% endfor %}
{% endif %}
</select>
</div>
</optgroup>
{% endif %}
</select>
<label class="label">
<span class="label-text-alt">Hold Ctrl/Cmd to select multiple</span>
</label>
</div>
<!-- Threshold -->
<div id="threshold" class="ep-model-param mlrr enviformer">
<label for="model-threshold">Threshold</label>
<input
type="number"
min="0"
max="1"
step="0.05"
value="0.5"
id="model-threshold"
name="model-threshold"
class="form-control"
/>
</div>
<!-- Threshold (MLRR, Enviformer) -->
<div
class="form-control mb-3"
x-show="showMlrr || showEnviformer"
x-cloak
>
<label class="label" for="model-threshold">
<span class="label-text">Threshold</span>
</label>
<input
type="number"
min="0"
max="1"
step="0.05"
value="0.5"
id="model-threshold"
name="model-threshold"
class="input input-bordered w-full"
/>
</div>
<div id="appdomain" class="ep-model-param mlrr">
{% 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?
<!-- Applicability Domain (MLRR) -->
{% if meta.enabled_features.APPLICABILITY_DOMAIN %}
<div x-show="showMlrr" x-cloak>
<div class="form-control mb-3">
<label class="label cursor-pointer justify-start gap-3">
<input
type="checkbox"
id="build-app-domain"
name="build-app-domain"
class="checkbox"
x-model="buildAppDomain"
/>
<span class="label-text"
>Also build an Applicability Domain?</span
>
</label>
</div>
<div x-show="buildAppDomain" x-cloak class="ml-4 space-y-3">
<div class="form-control">
<label class="label" for="num-neighbors">
<span class="label-text">Number of Neighbors</span>
</label>
</div>
<div id="ad-params" style="display:none">
<!-- Num Neighbors -->
<label for="num-neighbors">Number of Neighbors</label>
<input
id="num-neighbors"
name="num-neighbors"
type="number"
class="form-control"
class="input input-bordered w-full"
value="5"
step="1"
min="0"
max="10"
/>
<!-- Local Compatibility -->
<label for="local-compatibility-threshold"
>Local Compatibility Threshold</label
>
</div>
<div class="form-control">
<label class="label" for="local-compatibility-threshold">
<span class="label-text">Local Compatibility Threshold</span>
</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"
class="input input-bordered w-full"
value="0.5"
step="0.01"
min="0"
max="1"
/>
</div>
{% endif %}
<div class="form-control">
<label class="label" for="reliability-threshold">
<span class="label-text">Reliability Threshold</span>
</label>
<input
id="reliability-threshold"
name="reliability-threshold"
type="number"
class="input input-bordered w-full"
value="0.5"
step="0.01"
min="0"
max="1"
/>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<a id="new_model_modal_form_submit" class="btn btn-primary" href="#"
>Submit</a
>
<button type="button" class="btn btn-default" data-dismiss="modal">
Cancel
</button>
</div>
{% endif %}
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Cancel
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('new_model_form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Submit</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Creating...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
// Built in Model Types
var nativeModelTypes = ["mlrr", "rbrr", "enviformer"];
// Initially hide all "specific" forms
$(".ep-model-param").each(function () {
$(this).hide();
});
$("#model-type").selectpicker();
$("#model-fingerprinter").selectpicker();
$("#model-rule-packages").selectpicker();
$("#model-data-packages").selectpicker();
$("#build-app-domain").change(function () {
if ($(this).is(":checked")) {
$("#ad-params").show();
} else {
$("#ad-params").hide();
}
});
// On change hide all and show only selected
$("#model-type").change(function () {
$(".ep-model-param").hide();
var modelType = $("#model-type").val();
if (nativeModelTypes.indexOf(modelType) !== -1) {
$("." + modelType).show();
} else {
// do nothing
}
});
$("#new_model_modal_form_submit").on("click", function (e) {
e.preventDefault();
$("#new_model_form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,68 +1,93 @@
<div
class="modal fade"
tabindex="-1"
{% load static %}
<dialog
id="new_package_modal"
role="dialog"
aria-labelledby="new_package_modal"
aria-hidden="true"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
<h4 class="modal-title">New Package</h4>
</div>
<div class="modal-body">
<p>Create new package. Description can be changed later.</p>
<form
id="new_package_modal_form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<p>
<label for="name">Name</label>
<input
id="name"
class="form-control"
name="package-name"
placeholder="Name"
/>
</p>
<p>
<label for="description">Description</label>
<input
id="description"
type="text"
rows="3"
class="form-control"
placeholder="Description..."
name="package-description"
/>
</p>
</form>
</div>
<div class="modal-footer">
<a id="new_package_modal_form_submit" class="btn btn-primary" href="#"
>Submit</a
>
<button type="button" class="btn btn-default" data-dismiss="modal">
Cancel
</button>
</div>
<div class="modal-box">
<!-- Header -->
<h3 class="font-bold text-lg">New Package</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<p class="mb-4">Create new package. Description can be changed later.</p>
<form
id="new-package-modal-form"
accept-charset="UTF-8"
action=""
method="post"
>
{% csrf_token %}
<div class="form-control mb-3">
<label class="label" for="package-name">
<span class="label-text">Name</span>
</label>
<input
id="package-name"
class="input input-bordered w-full"
name="package-name"
placeholder="Name"
required
/>
</div>
<div class="form-control mb-3">
<label class="label" for="package-description">
<span class="label-text">Description</span>
</label>
<input
id="package-description"
type="text"
class="input input-bordered w-full"
placeholder="Description..."
name="package-description"
/>
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Cancel
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('new-package-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Submit</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Creating...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#new_package_modal_form_submit").on("click", function (e) {
e.preventDefault();
$("#new_package_modal_form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,376 +0,0 @@
{% load static %}
<div
class="modal fade"
tabindex="-1"
id="new_pathway_modal"
role="dialog"
aria-labelledby="new_pathway_modal"
aria-hidden="true"
style="overflow-y: auto;"
>
<!-- FIXME: make width dynamic-->
<div class="modal-dialog" id="new_pathway_modal_dialog" style="width:900px">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
<h4 class="js-title-step"></h4>
</div>
<div class="modal-body hide" data-step="1" data-title="New Pathway">
<div class="jumbotron">
Create a new pathway by entering the root compound and a name. Then
select if you want to use the prediction engine to generate a
predicted pathway or create an empty pathway that you fill in by
yourself. If you choose to predict a pathway, you can modify the
settings for the prediction, or use the default settings and just
click Submit.
</div>
<div class="modal-body">
{% if current_user.name == 'anonymous' %}
<div class="alert alert-warning">
You are currently logged in as Anonymous. Please note: Pathways
entered or predicted as anonymous user will be deleted after 30
days. Please log in to save your results.
</div>
{% endif %}
</div>
<div class="row">
<div class="col-md-6">
<label for="name">Name</label>
<input
id="name"
class="form-control"
name="name"
placeholder="Name"
/>
<label for="description">Description</label>
<input
id="description"
class="form-control"
name="description"
placeholder="no description"
/>
</div>
<div class="col-md-6">
<label for="predict">Predict pathway or build yourself?</label>
<div class="radio" id="predict">
<p>
<label>
<input
type="radio"
name="predict"
id="radioPredict"
value="predict"
checked
/>Predict pathway
</label>
</p>
<p>
<label>
<input
type="radio"
name="predict"
id="radioIncremental"
value="incremental"
/>Incremental prediction
</label>
</p>
<p>
<label>
<input
type="radio"
name="predict"
id="radioBuild"
value="build"
/>Build pathway
</label>
</p>
</div>
</div>
</div>
<label for="smilesinput">SMILES</label>
<table style="width: 100%">
<colgroup>
<col span="1" style="width: 90%;" />
<col span="1" style="width: 10%;" />
</colgroup>
<tr>
<td>
<input
id="smilesinput"
class="form-control"
name="smilesinput"
placeholder="C1CCCCC1"
autocapitalize="none"
/>
</td>
<td>
<button type="button" class="btn btn-default" id="render-button">
Render
</button>
</td>
</tr>
</table>
<p id="ketcher_container"></p>
<div>
<iframe
id="ifKetcher"
src="{% static '/js/ketcher/ketcher.html' %}"
width="850"
height="510"
></iframe>
</div>
</div>
<div
class="modal-body hide"
data-step="2"
data-title="New Pathway - Advanced Settings"
>
<div class="jumbotron">
Choose if you want to use an existing setting, or create a new one for
this pathway prediction. Then click Submit to use the specified
setting, or click next to set the parameters.
</div>
<div id="settings">
<div class="radio" id="settingRadio">
<p>
<label>
<input
type="radio"
name="existing"
id="radioDefault"
value="exisiting"
checked
/>
Use Default
</label>
</p>
<p>
<label>
<input
type="radio"
name="existing"
id="radioExists"
value="exisiting"
/>
Select Existing
</label>
</p>
<p>
<label>
<input
type="radio"
name="existing"
id="radioNew"
value="temporary"
/>
Create New
</label>
</p>
</div>
<select id="settingSelect" name="settingSelect" class="form-control">
{% for setting in available_settings %}
<option value="{{ setting.id }}">{{ setting.name|safe }}</option>
{% endfor %}
</select>
<p></p>
</div>
</div>
{% with step_offset=1 %}
{% include "templates/modals/collections/new_setting_modal_body.html" %}
{% endwith %}
<div class="modal-footer">
<button
type="button"
class="btn btn-default js-btn-step pull-left"
data-orientation="cancel"
onclick="reset()"
data-dismiss="modal"
></button>
<button
type="button"
class="btn btn-default js-btn-step"
data-orientation="previous"
id="backbutton"
></button>
<button
type="button"
class="btn btn-default js-btn-step"
data-orientation="next"
id="nextbutton"
></button>
<a id="modal-form-submit" class="btn btn-primary" href="#">Submit</a>
</div>
</div>
</div>
</div>
<script>
s = new Setting(
"settingName",
"package_multi_select",
"modelSelect",
"cutoff",
"evalType",
"availableTS",
"forms",
"truncatorTable",
"summaryTable",
);
$(function () {
// hide all forms
$("#forms").children().hide();
$("#render-button").on("click", function () {
syncKetcherAndTextInput("text", "ifKetcher", "smilesinput");
});
// If theres a change in the in '#smilesinput' sync the value to ketcher
$("#smilesinput").on("input", function () {
syncKetcherAndTextInput("text", "ifKetcher", "smilesinput");
});
// If theres an update in ketcher sync it to textinput
setInterval(function () {
syncKetcherAndTextInput("ketcher", "ifKetcher", "smilesinput");
}, 250);
$("#smilesinput").on("blur", function () {
syncKetcherAndTextInput("text", "ifKetcher", "smilesinput");
});
$("#smilesinput").on("keypress", function (event) {
if (event.keyCode == 13) {
syncKetcherAndTextInput("text", "ifKetcher", "smilesinput");
}
});
// Show forms depending on the selected TS
$("#availableTS").on("change", function (e) {
e.preventDefault();
var type = $(this).val();
// hide current content
$("#forms").children().hide();
if (type === "") {
return;
}
$("#" + type + "_form").show();
});
$("#modelSelect").on("change", function () {
setCutoff = function (thresh) {
$("#cutoff").val(thresh);
};
var modelUri = $("#modelSelect :selected").val();
fillPRCurve(modelUri, setCutoff);
});
// Add a TS to the setting
$("#add-ts-button").on("click", function (e) {
e.preventDefault();
s.addTruncator();
});
$("input[type=radio][name=predict]").change(function () {
if (this.id == "radioBuild") {
$("#nextbutton").prop("disabled", true);
} else {
$("#nextbutton").prop("disabled", false);
}
});
$("input[type=radio][name=existing]").change(function () {
if (this.id == "radioDefault" || this.id == "radioExists") {
if (this.id == "radioDefault") {
$("#settingSelect").prop("disabled", true);
} else {
$("#settingSelect").prop("disabled", false);
}
$("#nextbutton").prop("disabled", true);
} else {
// build...
$("#settingSelect").prop("disabled", true);
$("#nextbutton").prop("disabled", false);
}
});
var pwStep1 = function () {
console.log("pw step 1");
// Make "Next" to "Advanced"
$("#nextbutton").val("Advanced");
};
var pwStep2 = function () {
console.log("pw step 2");
// Make "Advanced" to "Next"
$("#nextbutton").val("Next");
// As "Use default is preselected" disable "Next" button
$("#nextbutton").prop("disabled", true);
// Disable setting dropdown as long as the correspndonding radio isnt checked
$("#settingSelect").prop("disabled", true);
// Show submit button
$("#modal-form-submit").show();
};
var settingStep1 = function () {
// First step sets name and packages
s.extractName();
s.extractSelectedPackages();
};
var settingStep2 = function () {
// Seconds step gathers relative reasoning params
s.extractRelativeReasoning();
s.extractCutoff();
s.extractEvaluationType();
};
var settingStep3 = function () {
s.updateTable();
s.updateSummaryTable();
// hide duplicate submit...
$("#nextbutton").hide();
};
var postPathway = function () {
console.log("Complete!");
console.log(s.tsParams);
console.log("Getting SMILES");
};
function dummy() {
console.log("dummy");
}
$("#new_pathway_modal").modalSteps({
btnCancelHtml: "Cancel",
btnPreviousHtml: "Back",
btnNextHtml: "Next",
btnLastStepHtml: "Submit",
disableNextButton: false,
completeCallback: postPathway,
callbacks: {
1: pwStep1,
2: pwStep2,
3: dummy,
4: settingStep1,
5: settingStep2,
6: settingStep3,
},
});
$("#modal-form-submit").on("click", function () {
e.preventDefault();
postPathway();
});
});
</script>

View File

@ -1,185 +1,260 @@
{% load static %}
<div id="new_prediction_setting_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Create a Prediction Setting</h5>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>
To create a Prediction Setting fill the form below and click "Create"
</p>
<form
id="new-prediction-setting-modal-form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<dialog
id="new_prediction_setting_modal"
class="modal"
x-data="{
isSubmitting: false,
tpMethod: '',
<label for="prediction-setting-name">Name</label>
reset() {
this.isSubmitting = false;
this.tpMethod = '';
},
async submit() {
const form = document.getElementById('new-prediction-setting-modal-form');
if (!form.checkValidity()) {
form.reportValidity();
return;
}
this.isSubmitting = true;
const formData = new FormData(form);
try {
const response = await fetch('/setting', {
method: 'POST',
body: new URLSearchParams(formData)
});
if (response.ok) {
location.reload();
}
} catch (error) {
console.error('Error creating setting:', error);
} finally {
this.isSubmitting = false;
}
}
}"
@close="reset()"
>
<div class="modal-box max-w-2xl">
<!-- Header -->
<h3 class="text-lg font-bold">Create a Prediction Setting</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<p class="mb-4">
To create a Prediction Setting fill the form below and click "Create"
</p>
<form
id="new-prediction-setting-modal-form"
accept-charset="UTF-8"
action=""
method="post"
>
{% csrf_token %}
<div class="form-control mb-3">
<label class="label" for="prediction-setting-name">
<span class="label-text">Name</span>
</label>
<input
id="prediction-setting-name"
name="prediction-setting-name"
class="form-control"
class="input input-bordered w-full"
placeholder="Name"
required
/>
<label for="prediction-setting-description">Description</label>
</div>
<div class="form-control mb-3">
<label class="label" for="prediction-setting-description">
<span class="label-text">Description</span>
</label>
<input
id="prediction-setting-description"
name="prediction-setting-description"
class="form-control"
class="input input-bordered w-full"
placeholder="Description"
/>
</div>
<label for="prediction-setting-max-nodes">Max #Nodes</label>
<div class="form-control mb-3">
<label class="label" for="prediction-setting-max-nodes">
<span class="label-text">Max #Nodes</span>
</label>
<input
id="prediction-setting-max-nodes"
type="number"
class="form-control"
class="input input-bordered w-full"
name="prediction-setting-max-nodes"
value="30"
min="1"
max="50"
step="1"
/>
<label for="prediction-setting-max-depth">Max Depth</label>
</div>
<div class="form-control mb-3">
<label class="label" for="prediction-setting-max-depth">
<span class="label-text">Max Depth</span>
</label>
<input
id="prediction-setting-max-depth"
type="number"
class="form-control"
class="input input-bordered w-full"
name="prediction-setting-max-depth"
value="5"
min="1"
max="8"
step="1"
/>
</div>
<label for="tp-generation-method">TP Generation Method</label>
<div class="form-control mb-3">
<label class="label" for="tp-generation-method">
<span class="label-text">TP Generation Method</span>
</label>
<select
id="tp-generation-method"
name="tp-generation-method"
class="form-control"
data-width="100%"
class="select select-bordered w-full"
x-model="tpMethod"
required
>
<option disabled selected>Select how TPs are generated</option>
<option value="" disabled selected>
Select how TPs are generated
</option>
<option value="rule-based-prediction-setting">Rule Based</option>
<option value="model-based-prediction-setting">Model Based</option>
</select>
<div id="rule-based-prediction-setting-specific-form">
<!-- Rule Packages -->
<label>Rule Packages</label><br />
</div>
<!-- Rule Based Settings -->
<div x-show="tpMethod === 'rule-based-prediction-setting'" x-cloak>
<div class="form-control mb-3">
<label class="label">
<span class="label-text">Rule Packages</span>
</label>
<select
id="rule-based-prediction-setting-packages"
name="rule-based-prediction-setting-packages"
data-actions-box="true"
class="form-control"
class="select select-bordered w-full h-32"
multiple
data-width="100%"
>
<option disabled>Reviewed Packages</option>
{% for obj in meta.readable_packages %}
{% if obj.reviewed %}
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
{% endif %}
{% endfor %}
<option disabled>Unreviewed Packages</option>
{% for obj in meta.readable_packages %}
{% if not obj.reviewed %}
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
{% endif %}
{% endfor %}
<optgroup label="Reviewed Packages">
{% for obj in meta.readable_packages %}
{% if obj.reviewed %}
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
{% endif %}
{% endfor %}
</optgroup>
<optgroup label="Unreviewed Packages">
{% for obj in meta.readable_packages %}
{% if not obj.reviewed %}
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
{% endif %}
{% endfor %}
</optgroup>
</select>
<label class="label">
<span class="label-text-alt"
>Hold Ctrl/Cmd to select multiple</span
>
</label>
</div>
<div id="model-based-prediction-setting-specific-form">
<label>Select Model</label><br />
</div>
<!-- Model Based Settings -->
<div x-show="tpMethod === 'model-based-prediction-setting'" x-cloak>
<div class="form-control mb-3">
<label class="label" for="model-based-prediction-setting-model">
<span class="label-text">Select Model</span>
</label>
<select
id="model-based-prediction-setting-model"
name="model-based-prediction-setting-model"
class="form-control"
data-width="100%"
class="select select-bordered w-full"
>
<option disabled selected>Select the model</option>
<option value="" disabled selected>Select the model</option>
{% for m in models %}
<option value="{{ m.url }}">{{ m.name|safe }}</option>
{% endfor %}
</select>
<label for="model-based-prediction-setting-threshold"
>Threshold</label
>
</div>
<div class="form-control mb-3">
<label class="label" for="model-based-prediction-setting-threshold">
<span class="label-text">Threshold</span>
</label>
<input
id="model-based-prediction-setting-threshold"
name="model-based-prediction-setting-threshold"
class="form-control"
class="input input-bordered w-full"
placeholder="0.25"
type="number"
min="0"
max="1"
step="0.05"
/>
</div>
</div>
<input
class="form-check-input"
type="checkbox"
value="on"
id="prediction-setting-new-default"
name="prediction-setting-new-default"
/>
<label class="form-check-label" for="prediction-setting-new-default"
>Set this setting as new default</label
>
</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="new-prediction-setting-modal-submit"
>
Create
</button>
</div>
<div class="form-control">
<label class="label cursor-pointer justify-start gap-3">
<input
type="checkbox"
class="checkbox"
value="on"
id="prediction-setting-new-default"
name="prediction-setting-new-default"
/>
<span class="label-text">Set this setting as new default</span>
</label>
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit()"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Create</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Creating...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
// Initially hide all "specific" forms
$("div[id$='-specific-form']").each(function () {
$(this).hide();
});
$("#rule-based-prediction-setting-packages").selectpicker();
// On change hide all and show only selected
$("#tp-generation-method").change(function () {
$("div[id$='-specific-form']").each(function () {
$(this).hide();
});
val = $("option:selected", this).val();
$("#" + val + "-specific-form").show();
});
$("#new-prediction-setting-modal-submit").click(function (e) {
e.preventDefault();
// $('#new-prediction-setting-modal-form').submit();
const formData = $("#new-prediction-setting-modal-form").serialize();
$.post("/setting", formData, function (response) {
location.reload();
});
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,91 +1,105 @@
{% load static %}
<div
class="modal fade bs-modal-lg"
<dialog
id="new_reaction_modal"
tabindex="-1"
aria-labelledby="new_reaction_modal"
aria-modal="true"
role="dialog"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<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">Create a new Reaction</h4>
</div>
<div class="modal-body">
<form
id="new_reaction_modal_form"
accept-charset="UTF-8"
action="{% url 'package reaction list' meta.current_package.uuid %}"
data-remote="true"
method="post"
>
{% csrf_token %}
<label for="reaction-name">Name</label>
<div class="modal-box max-w-3xl">
<!-- Header -->
<h3 class="font-bold text-lg">Create a new Reaction</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="new-reaction-modal-form"
accept-charset="UTF-8"
action="{% url 'package reaction list' meta.current_package.uuid %}"
method="post"
>
{% csrf_token %}
<div class="form-control mb-3">
<label class="label" for="reaction-name">
<span class="label-text">Name</span>
</label>
<input
id="reaction-name"
class="form-control"
class="input input-bordered w-full"
name="reaction-name"
placeholder="Name"
required
/>
<label for="reaction-description">Description</label>
</div>
<div class="form-control mb-3">
<label class="label" for="reaction-description">
<span class="label-text">Description</span>
</label>
<input
id="reaction-description"
class="form-control"
class="input input-bordered w-full"
name="reaction-description"
placeholder="Description"
/>
<p></p>
<div>
<iframe
id="new_reaction_ketcher"
src="{% static '/js/ketcher2/ketcher.html' %}"
width="100%"
height="510"
></iframe>
</div>
<input type="hidden" name="reaction-smirks" id="reaction-smirks" />
<p></p>
</form>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary pull-left"
data-dismiss="modal"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
id="new_reaction_modal_form_submit"
>
Submit
</button>
</div>
</div>
<div class="mb-3">
<iframe
id="new_reaction_ketcher"
src="{% static '/js/ketcher2/ketcher.html' %}"
width="100%"
height="510"
></iframe>
</div>
<input type="hidden" name="reaction-smirks" id="reaction-smirks" />
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="
const k = getKetcher('new_reaction_ketcher');
document.getElementById('reaction-smirks').value = k.getSmiles();
submit('new-reaction-modal-form');
"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Submit</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Creating...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#new_reaction_modal_form_submit").on("click", function (e) {
e.preventDefault();
$(this).prop("disabled", true);
k = getKetcher("new_reaction_ketcher");
$("#reaction-smirks").val(k.getSmiles());
// submit form
$("#new_reaction_modal_form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,120 +1,140 @@
{% load static %}
<div
class="modal fade bs-modal-lg"
<dialog
id="new_rule_modal"
tabindex="-1"
aria-labelledby="new_rule_modal"
aria-modal="true"
role="dialog"
class="modal"
x-data="{
...modalForm(),
smirksVizHtml: '',
updateSmirksViz() {
const smirks = document.getElementById('rule-smirks').value;
if (!smirks) {
this.smirksVizHtml = '';
return;
}
const img = new Image();
img.src = '{% url 'depict' %}?is_query_smirks=true&smirks=' + encodeURIComponent(smirks);
img.style.width = '100%';
img.style.height = '100%';
img.style.objectFit = 'cover';
img.onload = () => {
this.smirksVizHtml = img.outerHTML;
};
img.onerror = () => {
this.smirksVizHtml = `
<div class='alert alert-error' role='alert'>
<h4 class='alert-heading'>Could not render SMIRKS!</h4>
<p>Could not render SMIRKS - Have you entered a valid SMIRKS?</p>
</div>`;
};
}
}"
@close="reset(); smirksVizHtml = ''"
>
<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">Create a new Rule</h4>
</div>
<div class="modal-body">
<form
id="new_rule_modal_form"
accept-charset="UTF-8"
action="{% url 'package rule list' meta.current_package.uuid %}"
data-remote="true"
method="post"
>
{% csrf_token %}
<label for="rule-name">Name</label>
<div class="modal-box max-w-3xl">
<!-- Header -->
<h3 class="font-bold text-lg">Create a new Rule</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="new-rule-modal-form"
accept-charset="UTF-8"
action="{% url 'package rule list' meta.current_package.uuid %}"
method="post"
>
{% csrf_token %}
<div class="form-control mb-3">
<label class="label" for="rule-name">
<span class="label-text">Name</span>
</label>
<input
id="rule-name"
class="form-control"
class="input input-bordered w-full"
name="rule-name"
placeholder="Name"
required
/>
<label for="rule-description">Description</label>
</div>
<div class="form-control mb-3">
<label class="label" for="rule-description">
<span class="label-text">Description</span>
</label>
<input
id="rule-description"
class="form-control"
class="input input-bordered w-full"
name="rule-description"
placeholder="Description"
/>
<label for="rule-smirks">SMIRKS</label>
</div>
<div class="form-control mb-3">
<label class="label" for="rule-smirks">
<span class="label-text">SMIRKS</span>
</label>
<input
id="rule-smirks"
class="form-control"
class="input input-bordered w-full"
name="rule-smirks"
placeholder="SMIRKS"
@input="updateSmirksViz()"
/>
<p></p>
<div id="rule-smirks-viz"></div>
<input
type="hidden"
name="rule-type"
id="rule-type"
value="SimpleAmbitRule"
/>
<p></p>
</form>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary pull-left"
data-dismiss="modal"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
id="new_rule_modal_form_submit"
>
Submit
</button>
</div>
</div>
<div id="rule-smirks-viz" class="mb-3" x-html="smirksVizHtml"></div>
<input
type="hidden"
name="rule-type"
id="rule-type"
value="SimpleAmbitRule"
/>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('new-rule-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Submit</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Creating...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#rule-smirks").on("input", function (e) {
$("#rule-smirks-viz").empty();
smirks = $("#rule-smirks").val();
const img = new Image();
img.src =
"{% url 'depict' %}?is_query_smirks=true&smirks=" +
encodeURIComponent(smirks);
img.style.width = "100%";
img.style.height = "100%";
img.style.objectFit = "cover";
img.onload = function () {
$("#rule-smirks-viz").append(img);
};
img.onerror = function () {
error_tpl = `
<div class="alert alert-error" role="alert">
<h4 class="alert-heading">Could not render SMIRKS!</h4>
<p>Could not render SMIRKS - Have you entered a valid SMIRKS?</a>
</p>
</div>`;
$("#rule-smirks-viz").append(error_tpl);
};
});
$("#new_rule_modal_form_submit").on("click", function (e) {
e.preventDefault();
$(this).prop("disabled", true);
// submit form
$("#new_rule_modal_form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,30 +1,45 @@
<div
class="modal fade"
tabindex="-1"
{% load static %}
<dialog
id="new_scenario_modal"
role="dialog"
aria-labelledby="new_scenario_modal"
aria-hidden="true"
class="modal"
x-data="{
...modalForm(),
scenarioType: 'empty',
validateYear(el) {
if (el.value && el.value.length < 4) {
el.value = new Date().getFullYear();
}
}
}"
@close="reset()"
>
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
<h4 class="modal-title">New Scenario</h4>
</div>
<div class="modal-body">
<form
id="new_scenario_form"
accept-charset="UTF-8"
action="{{ meta.current_package.url }}/scenario"
data-remote="true"
method="post"
>
{% csrf_token %}
<div class="jumbotron">
<div class="modal-box max-w-3xl">
<!-- Header -->
<h3 class="font-bold text-lg">New Scenario</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="new-scenario-modal-form"
accept-charset="UTF-8"
action="{{ meta.current_package.url }}/scenario"
method="post"
>
{% csrf_token %}
<div class="alert alert-info mb-4">
<span>
Please enter name, description, and date of scenario. Date should be
associated to the data, not the current date. For example, this
could reflect the publishing date of a study. You can leave all
@ -32,122 +47,131 @@
<a
target="_blank"
href="https://wiki.envipath.org/index.php/scenario"
role="button"
class="link"
>wiki &gt;&gt;</a
>
</div>
<label for="scenario-name">Name</label>
</span>
</div>
<div class="form-control mb-3">
<label class="label" for="scenario-name">
<span class="label-text">Name</span>
</label>
<input
id="scenario-name"
name="scenario-name"
class="form-control"
class="input input-bordered w-full"
placeholder="Name"
required
/>
<label for="scenario-description">Description</label>
</div>
<div class="form-control mb-3">
<label class="label" for="scenario-description">
<span class="label-text">Description</span>
</label>
<input
id="scenario-description"
name="scenario-description"
class="form-control"
class="input input-bordered w-full"
placeholder="Description"
/>
<label id="dateField" for="dateYear">Date</label>
<table>
<tr>
<th>
<input
type="number"
id="dateYear"
name="scenario-date-year"
class="form-control"
placeholder="YYYY"
max="{% now "Y" %}"
/>
</th>
<th>
<input
type="number"
id="dateMonth"
name="scenario-date-month"
min="1"
max="12"
class="form-control"
placeholder="MM"
/>
</th>
<th>
<input
type="number"
id="dateDay"
name="scenario-date-day"
min="1"
max="31"
class="form-control"
placeholder="DD"
/>
</th>
</tr>
</table>
<label for="scenario-type">Scenario Type</label>
</div>
<div class="form-control mb-3">
<label class="label">
<span class="label-text">Date</span>
</label>
<div class="flex gap-2">
<input
type="number"
id="dateYear"
name="scenario-date-year"
class="input input-bordered w-24"
placeholder="YYYY"
max="{% now 'Y' %}"
@blur="validateYear($el)"
/>
<input
type="number"
id="dateMonth"
name="scenario-date-month"
min="1"
max="12"
class="input input-bordered w-20"
placeholder="MM"
/>
<input
type="number"
id="dateDay"
name="scenario-date-day"
min="1"
max="31"
class="input input-bordered w-20"
placeholder="DD"
/>
</div>
</div>
<div class="form-control mb-3">
<label class="label" for="scenario-type">
<span class="label-text">Scenario Type</span>
</label>
<select
id="scenario-type"
name="scenario-type"
class="form-control"
data-width="100%"
class="select select-bordered w-full"
x-model="scenarioType"
>
<option value="empty" selected>Empty Scenario</option>
{% for k, v in scenario_types.items %}
<option value="{{ v.name }}">{{ k }}</option>
{% endfor %}
</select>
</div>
{% for type in scenario_types.values %}
<div id="{{ type.name }}-specific-inputs">
{% for widget in type.widgets %}
{{ widget|safe }}
{% endfor %}
</div>
{% endfor %}
</form>
</div>
<div class="modal-footer">
<a id="new_scenario_modal_form_submit" class="btn btn-primary" href="#"
>Submit</a
>
<button type="button" class="btn btn-default" data-dismiss="modal">
Cancel
</button>
</div>
{% for type in scenario_types.values %}
<div
id="{{ type.name }}-specific-inputs"
x-show="scenarioType === '{{ type.name }}'"
x-cloak
>
{% for widget in type.widgets %}
{{ widget|safe }}
{% endfor %}
</div>
{% endfor %}
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Cancel
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('new-scenario-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Submit</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Creating...</span>
</button>
</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();
});
$("#new_scenario_modal_form_submit").on("click", function (e) {
e.preventDefault();
$("#new_scenario_form").submit();
});
var dateYear = document.getElementById("dateYear");
dateYear.addEventListener("change", () => {
console.log("Final value after editing:", dateYear.value);
if (dateYear.value.length < 4) {
dateYear.value = new Date().getFullYear();
}
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,92 +1,117 @@
{% 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">&times;</span>
</button>
<h3 class="modal-title">Add Additional Information</h3>
</div>
<div class="modal-body">
<!-- Add Additional Information -->
<dialog
id="add_additional_information_modal"
class="modal"
x-data="{
isSubmitting: false,
selectedType: '',
reset() {
this.isSubmitting = false;
this.selectedType = '';
},
submit() {
if (!this.selectedType) return;
const form = document.getElementById('add_' + this.selectedType + '_add-additional-information-modal-form');
if (form && form.checkValidity()) {
this.isSubmitting = true;
form.submit();
} else if (form) {
form.reportValidity();
}
}
}"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="text-lg font-bold">Add Additional Information</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<div class="form-control">
<label class="label" for="select-additional-information-type">
<span class="label-text">Select the type to add</span>
</label>
<select
id="select-additional-information-type"
data-actions-box="true"
class="form-control"
data-width="100%"
class="select select-bordered w-full"
x-model="selectedType"
>
<option selected disabled>Select the type to add</option>
<option value="" 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"
{% for add_inf in available_additional_information %}
<div
class="mt-4"
x-show="selectedType === '{{ add_inf.name }}'"
x-cloak
>
Add
</button>
</div>
<form
id="add_{{ add_inf.name }}_add-additional-information-modal-form"
accept-charset="UTF-8"
action=""
method="post"
>
{% csrf_token %}
{{ add_inf.widget|safe }}
<input
type="hidden"
name="hidden"
value="add-additional-information"
/>
</form>
</div>
{% endfor %}
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit()"
:disabled="isSubmitting || !selectedType"
>
<span x-show="!isSubmitting">Add</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Adding...</span>
</button>
</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>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,67 +1,107 @@
{% load static %}
<div
class="modal fade bs-modal-lg"
<dialog
id="add_pathway_edge_modal"
tabindex="-1"
aria-labelledby="add_pathway_edge_modal"
aria-modal="true"
role="dialog"
class="modal"
x-data="{
isSubmitting: false,
reactionImageUrl: '',
reset() {
this.isSubmitting = false;
this.reactionImageUrl = '';
},
updateReactionImage() {
const substratesSelect = document.getElementById('add_pathway_edge_substrates');
const productsSelect = document.getElementById('add_pathway_edge_products');
const substrates = [];
for (const option of substratesSelect.selectedOptions) {
substrates.push(option.dataset.smiles);
}
const products = [];
for (const option of productsSelect.selectedOptions) {
products.push(option.dataset.smiles);
}
if (substrates.length > 0 && products.length > 0) {
const reaction = substrates.join('.') + '>>' + products.join('.');
this.reactionImageUrl = '{% url "depict" %}?smirks=' + encodeURIComponent(reaction);
} else {
this.reactionImageUrl = '';
}
},
submit() {
this.isSubmitting = true;
document.getElementById('add_pathway_edge_modal_form').submit();
}
}"
@close="reset()"
>
<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">&times;</span>
</button>
<h4 class="modal-title">Add a Reaction</h4>
</div>
<div class="modal-body">
<form
id="add_pathway_edge_modal_form"
accept-charset="UTF-8"
action="{% url 'package pathway edge list' meta.current_package.uuid pathway.uuid %}"
data-remote="true"
method="post"
>
{% csrf_token %}
<label for="edge-name">Name</label>
<div class="modal-box max-w-4xl">
<!-- Header -->
<h3 class="text-lg font-bold">Add a Reaction</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="add_pathway_edge_modal_form"
accept-charset="UTF-8"
action="{% url 'package pathway edge list' meta.current_package.uuid pathway.uuid %}"
data-remote="true"
method="post"
>
{% csrf_token %}
<div class="form-control mb-3">
<label class="label" for="edge-name">
<span class="label-text">Name</span>
</label>
<input
id="edge-name"
class="form-control"
type="text"
class="input input-bordered w-full"
name="edge-name"
placeholder="Name"
/>
<label for="edge-description">Description</label>
</div>
<div class="form-control mb-3">
<label class="label" for="edge-description">
<span class="label-text">Description</span>
</label>
<input
id="edge-description"
class="form-control"
type="text"
class="input input-bordered w-full"
name="edge-description"
placeholder="Description"
/>
<p></p>
<div class="row">
<div class="col-xs-5">
<legend>Substrate(s)</legend>
</div>
<div class="col-xs-2"></div>
<div class="col-xs-5">
<legend>Product(s)</legend>
</div>
</div>
<div class="row">
<div class="col-xs-5">
</div>
<div class="mb-3 grid grid-cols-11 gap-2">
<div class="col-span-5">
<div class="form-control">
<label class="label">
<span class="label-text font-semibold">Substrate(s)</span>
</label>
<select
id="add_pathway_edge_substrates"
name="edge-substrates"
data-actions-box="true"
class="form-control"
class="select select-bordered h-32 w-full"
multiple
data-width="100%"
@change="updateReactionImage()"
>
{% for n in pathway.nodes %}
<option
@ -73,20 +113,21 @@
{% endfor %}
</select>
</div>
<div
class="col-xs-2"
style="display: flex; justify-content: center; align-items: center;"
>
<i class="glyphicon glyphicon-arrow-right"></i>
</div>
<div class="col-xs-5">
</div>
<div class="col-span-1 flex items-center justify-center">
<span class="text-2xl"></span>
</div>
<div class="col-span-5">
<div class="form-control">
<label class="label">
<span class="label-text font-semibold">Product(s)</span>
</label>
<select
id="add_pathway_edge_products"
name="edge-products"
data-actions-box="true"
class="form-control"
class="select select-bordered h-32 w-full"
multiple
data-width="100%"
@change="updateReactionImage()"
>
{% for n in pathway.nodes %}
<option
@ -99,76 +140,42 @@
</select>
</div>
</div>
<div class="row">
<p></p>
<div class="col-xs-12" id="reaction_image"></div>
</div>
</form>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary pull-left"
data-dismiss="modal"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
id="add_pathway_edge_modal_form_submit"
>
Submit
</button>
</div>
</div>
<div class="mb-3" x-show="reactionImageUrl" x-cloak>
<img :src="reactionImageUrl" class="w-full" alt="Reaction preview" />
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit()"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Submit</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Submitting...</span>
</button>
</div>
</div>
</div>
<script>
function reactionImage() {
var substrates = [];
$("#add_pathway_edge_substrates option:selected").each(function () {
var smiles = $(this).data("smiles"); // read data-smiles attribute
substrates.push(smiles);
});
var products = [];
$("#add_pathway_edge_products option:selected").each(function () {
var smiles = $(this).data("smiles"); // read data-smiles attribute
products.push(smiles);
});
if (substrates.length > 0 && products.length > 0) {
reaction = substrates.join(".") + ">>" + products.join(".");
$("#reaction_image").empty();
$("#reaction_image").append(
"<img width='100%' src='{% url 'depict' %}?smirks=" +
encodeURIComponent(reaction) +
"'>",
);
}
}
$(function () {
$("#add_pathway_edge_substrates").selectpicker();
$("#add_pathway_edge_products").selectpicker();
$("#add_pathway_edge_substrates").on("change", function (e) {
reactionImage();
});
$("#add_pathway_edge_products").on("change", function (e) {
reactionImage();
});
$(function () {
$("#add_pathway_edge_modal_form_submit").on("click", function (e) {
e.preventDefault();
$(this).prop("disabled", true);
// submit form
$("#add_pathway_edge_modal_form").submit();
});
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,119 +1,137 @@
{% load static %}
<div
class="modal fade bs-modal-lg"
<dialog
id="add_pathway_node_modal"
tabindex="-1"
aria-labelledby="add_pathway_node_modal"
aria-modal="true"
role="dialog"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<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">&times;</span>
</button>
<h4 class="modal-title">Add a Node</h4>
</div>
<div class="modal-body">
<form
id="add_pathway_node_modal_form"
accept-charset="UTF-8"
action="{% url 'package pathway node list' meta.current_package.uuid pathway.uuid %}"
data-remote="true"
method="post"
>
{% csrf_token %}
<label for="node-name">Name</label>
<div class="modal-box max-w-4xl">
<!-- Header -->
<h3 class="text-lg font-bold">Add a Node</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="add_pathway_node_modal_form"
accept-charset="UTF-8"
action="{% url 'package pathway node list' meta.current_package.uuid pathway.uuid %}"
data-remote="true"
method="post"
>
{% csrf_token %}
<div class="form-control mb-3">
<label class="label" for="node-name">
<span class="label-text">Name</span>
</label>
<input
id="node-name"
class="form-control"
type="text"
class="input input-bordered w-full"
name="node-name"
placeholder="Name"
/>
<label for="node-description">Description</label>
</div>
<div class="form-control mb-3">
<label class="label" for="node-description">
<span class="label-text">Description</span>
</label>
<input
id="node-description"
class="form-control"
type="text"
class="input input-bordered w-full"
name="node-description"
placeholder="Description"
/>
<label for="node-smiles">SMILES</label>
</div>
<div class="form-control mb-3">
<label class="label" for="node-smiles">
<span class="label-text">SMILES</span>
</label>
<input
type="text"
class="form-control"
class="input input-bordered w-full"
name="node-smiles"
placeholder="SMILES"
id="node-smiles"
/>
<p></p>
<div>
<iframe
id="add_node_ketcher"
src="{% static '/js/ketcher2/ketcher.html' %}"
width="100%"
height="510"
></iframe>
</div>
<p></p>
</form>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary pull-left"
data-dismiss="modal"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
id="add_pathway_node_modal_form_submit"
>
Submit
</button>
</div>
</div>
<div class="mb-3">
<iframe
id="add_node_ketcher"
src="{% static '/js/ketcher2/ketcher.html' %}"
width="100%"
height="510"
></iframe>
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('add_pathway_node_modal_form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Submit</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Submitting...</span>
</button>
</div>
</div>
</div>
<script>
function newStructureModalketcherToNewStructureModalTextInput() {
$("#node-smiles").val(this.ketcher.getSmiles());
}
$(function () {
$("#add_node_ketcher").on("load", function () {
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>
<script>
document
.getElementById("add_node_ketcher")
.addEventListener("load", function () {
const iframe = this;
const checkKetcherReady = () => {
win = this.contentWindow;
const win = iframe.contentWindow;
if (win.ketcher && "editor" in win.ketcher) {
win.ketcher.editor.event.change.handlers.push({
once: false,
priority: 0,
f: newStructureModalketcherToNewStructureModalTextInput,
f: function () {
document.getElementById("node-smiles").value =
this.ketcher.getSmiles();
},
ketcher: win.ketcher,
});
} else {
setTimeout(checkKetcherReady, 100);
}
};
checkKetcherReady();
});
$(function () {
$("#add_pathway_node_modal_form_submit").on("click", function (e) {
e.preventDefault();
$(this).prop("disabled", true);
// submit form
$("#add_pathway_node_modal_form").submit();
});
});
});
</script>

View File

@ -1,119 +1,137 @@
{% load static %}
<div
class="modal fade bs-modal-lg"
<dialog
id="add_structure_modal"
tabindex="-1"
aria-labelledby="add_structure_modal"
aria-modal="true"
role="dialog"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<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">Create a new Structure</h4>
</div>
<div class="modal-body">
<form
id="add_structure_modal_form"
accept-charset="UTF-8"
action="{% url 'package compound structure list' meta.current_package.uuid compound.uuid %}"
data-remote="true"
method="post"
>
{% csrf_token %}
<label for="structure-name">Name</label>
<div class="modal-box max-w-4xl">
<!-- Header -->
<h3 class="text-lg font-bold">Create a new Structure</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="add_structure_modal_form"
accept-charset="UTF-8"
action="{% url 'package compound structure list' meta.current_package.uuid compound.uuid %}"
data-remote="true"
method="post"
>
{% csrf_token %}
<div class="form-control mb-3">
<label class="label" for="structure-name">
<span class="label-text">Name</span>
</label>
<input
id="structure-name"
class="form-control"
type="text"
class="input input-bordered w-full"
name="structure-name"
placeholder="Name"
/>
<label for="structure-description">Description</label>
</div>
<div class="form-control mb-3">
<label class="label" for="structure-description">
<span class="label-text">Description</span>
</label>
<input
id="structure-description"
class="form-control"
type="text"
class="input input-bordered w-full"
name="structure-description"
placeholder="Description"
/>
<label for="structure-smiles">SMILES</label>
</div>
<div class="form-control mb-3">
<label class="label" for="structure-smiles">
<span class="label-text">SMILES</span>
</label>
<input
type="text"
class="form-control"
class="input input-bordered w-full"
name="structure-smiles"
placeholder="SMILES"
id="structure-smiles"
/>
<p></p>
<div>
<iframe
id="add_structure_ketcher"
src="{% static '/js/ketcher2/ketcher.html' %}"
width="100%"
height="510"
></iframe>
</div>
<p></p>
</form>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary pull-left"
data-dismiss="modal"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
id="add_structure_modal_form_submit"
>
Submit
</button>
</div>
</div>
<div class="mb-3">
<iframe
id="add_structure_ketcher"
src="{% static '/js/ketcher2/ketcher.html' %}"
width="100%"
height="510"
></iframe>
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('add_structure_modal_form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Submit</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Submitting...</span>
</button>
</div>
</div>
</div>
<script>
function newStructureModalketcherToNewStructureModalTextInput() {
$("#structure-smiles").val(this.ketcher.getSmiles());
}
$(function () {
$("#add_structure_ketcher").on("load", function () {
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>
<script>
document
.getElementById("add_structure_ketcher")
.addEventListener("load", function () {
const iframe = this;
const checkKetcherReady = () => {
win = this.contentWindow;
const win = iframe.contentWindow;
if (win.ketcher && "editor" in win.ketcher) {
win.ketcher.editor.event.change.handlers.push({
once: false,
priority: 0,
f: newStructureModalketcherToNewStructureModalTextInput,
f: function () {
document.getElementById("structure-smiles").value =
this.ketcher.getSmiles();
},
ketcher: win.ketcher,
});
} else {
setTimeout(checkKetcherReady, 100);
}
};
checkKetcherReady();
});
$(function () {
$("#add_structure_modal_form_submit").on("click", function (e) {
e.preventDefault();
$(this).prop("disabled", true);
// submit form
$("#add_structure_modal_form").submit();
});
});
});
</script>

View File

@ -1,36 +1,48 @@
{% load static %}
<!-- Delete Edge -->
<div id="delete_pathway_edge_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Delete Edge</h3>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<dialog
id="delete_pathway_edge_modal"
class="modal"
x-data="modalForm({ state: { selectedEdge: '', imageUrl: '' } })"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="text-lg font-bold">Delete Edge</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<p class="mb-4">
Deletes the Edge. Nodes referenced by this edge will remain.
<p></p>
<form
id="delete-pathway-edge-modal-form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
</p>
<form
id="delete-pathway-edge-modal-form"
accept-charset="UTF-8"
action=""
method="post"
>
{% csrf_token %}
<div class="form-control">
<label class="label" for="delete_pathway_edge_edges">
<span class="label-text">Select Reaction to delete</span>
</label>
<select
id="delete_pathway_edge_edges"
name="edge-url"
data-actions-box="true"
class="form-control"
data-width="100%"
class="select select-bordered w-full"
x-model="selectedEdge"
@change="imageUrl = selectedEdge ? selectedEdge + '?image=svg' : ''"
required
>
<option value="" disabled selected>
Select Reaction to delete
@ -39,51 +51,44 @@
<option value="{{ e.url }}">{{ e.edge_label.name|safe }}</option>
{% endfor %}
</select>
<input type="hidden" id="hidden" name="hidden" value="delete" />
</form>
<p></p>
<div id="delete_pathway_edge_image"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button
type="button"
class="btn btn-primary"
id="delete-pathway-edge-modal-submit"
>
Delete
</button>
</div>
<input type="hidden" id="hidden" name="hidden" value="delete" />
</form>
<!-- Image Preview -->
<div class="mt-4" x-show="imageUrl" x-cloak>
<img :src="imageUrl" class="w-full" alt="Edge preview" />
</div>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-error"
@click="setFormAction('delete-pathway-edge-modal-form', selectedEdge); submit('delete-pathway-edge-modal-form')"
:disabled="isSubmitting || !selectedEdge"
>
<span x-show="!isSubmitting">Delete</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Deleting...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#delete_pathway_edge_edges").selectpicker();
$("#delete_pathway_edge_edges").on("change", function (e) {
edge_url = $("#delete_pathway_edge_edges option:selected").val();
if (edge_url !== "") {
$("#delete_pathway_edge_image").empty();
$("#delete_pathway_edge_image").append(
"<img width='100%' src='" + edge_url + "?image=svg'>",
);
}
});
$("#delete-pathway-edge-modal-submit").click(function (e) {
e.preventDefault();
edge_url = $("#delete_pathway_edge_edges option:selected").val();
if (edge_url === "") {
return;
}
$("#delete-pathway-edge-modal-form").attr("action", edge_url);
$("#delete-pathway-edge-modal-form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,38 +1,49 @@
{% load static %}
<!-- Delete Node -->
<div id="delete_pathway_node_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Delete Node</h3>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<dialog
id="delete_pathway_node_modal"
class="modal"
x-data="modalForm({ state: { selectedNode: '', imageUrl: '' } })"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="text-lg font-bold">Delete Node</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<p class="mb-4">
Deletes the Node. Edges having this Node as Substrate or Product will be
removed as well.
<p></p>
<form
id="delete-pathway-node-modal-form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
</p>
<form
id="delete-pathway-node-modal-form"
accept-charset="UTF-8"
action=""
method="post"
>
{% csrf_token %}
<div class="form-control">
<label class="label" for="delete_pathway_node_nodes">
<span class="label-text">Select Compound to delete</span>
</label>
<select
id="delete_pathway_node_nodes"
name="node-url"
data-actions-box="true"
class="form-control"
data-width="100%"
class="select select-bordered w-full"
x-model="selectedNode"
@change="imageUrl = selectedNode ? selectedNode + '?image=svg' : ''"
required
>
<option value="" disabled selected>
Select Compound to delete
@ -43,51 +54,44 @@
</option>
{% endfor %}
</select>
<input type="hidden" id="hidden" name="hidden" value="delete" />
</form>
<p></p>
<div id="delete_pathway_node_image"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button
type="button"
class="btn btn-primary"
id="delete-pathway-node-modal-submit"
>
Delete
</button>
</div>
<input type="hidden" id="hidden" name="hidden" value="delete" />
</form>
<!-- Image Preview -->
<div class="mt-4" x-show="imageUrl" x-cloak>
<img :src="imageUrl" class="w-full" alt="Node preview" />
</div>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-error"
@click="setFormAction('delete-pathway-node-modal-form', selectedNode); submit('delete-pathway-node-modal-form')"
:disabled="isSubmitting || !selectedNode"
>
<span x-show="!isSubmitting">Delete</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Deleting...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#delete_pathway_node_nodes").selectpicker();
$("#delete_pathway_node_nodes").on("change", function (e) {
node_url = $("#delete_pathway_node_nodes option:selected").val();
if (node_url !== "") {
$("#delete_pathway_node_image").empty();
$("#delete_pathway_node_image").append(
"<img width='100%' src='" + node_url + "?image=svg'>",
);
}
});
$("#delete-pathway-node-modal-submit").click(function (e) {
e.preventDefault();
node_url = $("#delete_pathway_node_nodes option:selected").val();
if (node_url === "") {
return;
}
$("#delete-pathway-node-modal-form").attr("action", node_url);
$("#delete-pathway-node-modal-form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,53 +1,69 @@
{% load static %}
<!-- Download Pathway -->
<div id="download_pathway_csv_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Download Pathway as CSV</h3>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<dialog
id="download_pathway_csv_modal"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="font-bold text-lg">Download Pathway as CSV</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<p>
By clicking on Download the Pathway will be converted into a CSV and
directly downloaded.
<form
id="download-pathway-csv-modal-form"
accept-charset="UTF-8"
action="{{ pathway.url }}"
data-remote="true"
method="GET"
>
<input type="hidden" name="download" value="true" />
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button
type="button"
class="btn btn-primary"
id="download-pathway-csv-modal-submit"
>
Download
</button>
</div>
</p>
<form
id="download-pathway-csv-modal-form"
accept-charset="UTF-8"
action="{{ pathway.url }}"
method="GET"
>
<input type="hidden" name="download" value="true" />
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('download-pathway-csv-modal-form'); $el.closest('dialog').close();"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Download</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#download-pathway-csv-modal-submit").click(function (e) {
e.preventDefault();
$("#download-pathway-csv-modal-form").submit();
$("#download_pathway_csv_modal").modal("hide");
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,43 +1,57 @@
{% load static %}
<!-- Download Pathway -->
<div id="download_pathway_image_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Download Pathway as Image</h3>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
By clicking on Download the Pathway will be saved as SVG.
</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="download-pathway-image-modal-submit"
>
Download
</button>
</div>
<dialog
id="download_pathway_image_modal"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="font-bold text-lg">Download Pathway as Image</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<p>By clicking on Download the Pathway will be saved as SVG.</p>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="isSubmitting = true; downloadSVG(document.getElementById('pwsvg'), '{{ pathway.name.split|join:'_' }}.svg'); $el.closest('dialog').close();"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Download</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#download-pathway-image-modal-submit").click(function (e) {
e.preventDefault();
downloadSVG($("#pwsvg")[0], '{{ pathway.name.split|join:"_" }}.svg');
$("#download_pathway_image_modal").modal("hide");
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,70 +1,91 @@
{% load static %}
<!-- Edit Compound -->
<div id="edit_compound_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit Compound</h5>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>Edit Compound.</p>
<form
id="edit-compound-modal-form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<p>
<label for="compound-name">Name</label>
<input
id="compound-name"
class="form-control"
name="compound-name"
value="{{ compound.name|safe }}"
/>
</p>
<p>
<label for="compound-description">Description</label>
<input
id="compound-description"
type="text"
class="form-control"
value="{{ compound.description|safe }}"
name="compound-description"
/>
</p>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button
type="button"
class="btn btn-primary"
id="edit-compound-modal-submit"
>
Update
</button>
</div>
<dialog
id="edit_compound_modal"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="font-bold text-lg">Edit Compound</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="edit-compound-modal-form"
accept-charset="UTF-8"
action=""
method="post"
>
{% csrf_token %}
<div class="form-control mb-3">
<label class="label" for="compound-name">
<span class="label-text">Name</span>
</label>
<input
id="compound-name"
class="input input-bordered w-full"
name="compound-name"
value="{{ compound.name|safe }}"
required
/>
</div>
<div class="form-control mb-3">
<label class="label" for="compound-description">
<span class="label-text">Description</span>
</label>
<input
id="compound-description"
type="text"
class="input input-bordered w-full"
value="{{ compound.description|safe }}"
name="compound-description"
/>
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('edit-compound-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Update</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Updating...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#edit-compound-modal-submit").click(function (e) {
e.preventDefault();
$("#edit-compound-modal-form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,70 +1,91 @@
{% load static %}
<!-- Edit Compound -->
<div id="edit_compound_structure_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Create a Compound</h5>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>Edit a Compound Structure.</p>
<form
id="edit-compound-structure-modal-form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<p>
<label for="compound-structure-name">Name</label>
<input
id="compound-structure-name"
class="form-control"
name="compound-structure-name"
value="{{ compound_structure.name|safe }}"
/>
</p>
<p>
<label for="compound-structure-description">Description</label>
<input
id="compound-structure-description"
type="text"
class="form-control"
value="{{ compound_structure.description|safe }}"
name="compound-structure-description"
/>
</p>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button
type="button"
class="btn btn-primary"
id="edit-compound-structure-modal-submit"
>
Create
</button>
</div>
<dialog
id="edit_compound_structure_modal"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="font-bold text-lg">Edit Compound Structure</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="edit-compound-structure-modal-form"
accept-charset="UTF-8"
action=""
method="post"
>
{% csrf_token %}
<div class="form-control mb-3">
<label class="label" for="compound-structure-name">
<span class="label-text">Name</span>
</label>
<input
id="compound-structure-name"
class="input input-bordered w-full"
name="compound-structure-name"
value="{{ compound_structure.name|safe }}"
required
/>
</div>
<div class="form-control mb-3">
<label class="label" for="compound-structure-description">
<span class="label-text">Description</span>
</label>
<input
id="compound-structure-description"
type="text"
class="input input-bordered w-full"
value="{{ compound_structure.description|safe }}"
name="compound-structure-description"
/>
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('edit-compound-structure-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Update</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Updating...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#edit-compound-structure-modal-submit").click(function (e) {
e.preventDefault();
$("#edit-compound-structure-modal-form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,151 +1,150 @@
{% load static %}
<!-- Edit Package Permission -->
<div id="edit_group_member_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Add or Remove Group Member</h5>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>
To add member (either User or entire Groups) to this group select the
entity you want to add below and click the check mark.
<br />
To remove member simply click the <code>X</code> next to the member.
</p>
<!-- Edit Group Member -->
<dialog
id="edit_group_member_modal"
class="modal"
x-data="{
isSubmitting: false,
<div class="row">
<div class="col-xs-8">
<legend>User or Group</legend>
</div>
<div class="col-xs-4">
<legend>Add/Remove</legend>
</div>
</div>
reset() {
this.isSubmitting = false;
},
<div class="row">
<form
id="modal-form-group-member"
class="form-inline"
role="form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<div class="col-xs-8">
<select
id="select_member"
name="member"
data-actions-box="true"
class="selPackages"
data-width="100%"
>
<option disabled selected>User</option>
submitForm(form) {
if (form && form.checkValidity()) {
form.submit();
} else if (form) {
form.reportValidity();
}
}
}"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="text-lg font-bold">Add or Remove Group Member</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<p class="mb-4">
To add member (either User or entire Groups) to this group select the
entity you want to add below and click the check mark.
<br />
To remove member simply click the X button next to the member.
</p>
<!-- Add Member Form -->
<form
id="modal-form-group-member"
accept-charset="UTF-8"
action=""
method="post"
class="mb-4"
>
{% csrf_token %}
<div class="flex gap-2 items-end">
<div class="form-control flex-1">
<label class="label">
<span class="label-text">User or Group</span>
</label>
<select
id="select_member"
name="member"
class="select select-bordered w-full"
required
>
<optgroup label="Users">
{% for u in users %}
<option value="{{ u.url }}">{{ u.username }}</option>
{% endfor %}
<option disabled>Groups</option>
</optgroup>
<optgroup label="Groups">
{% for g in groups %}
<option value="{{ g.url }}">{{ g.name|safe }}</option>
{% endfor %}
</select>
<input type="hidden" name="action" value="add" />
</div>
<div class="col-xs-2"></div>
<div class="col-xs-2">
<button type="submit" style="width:60%;" class="btn col-xs-2">
<span class="glyphicon glyphicon-ok"></span>
</button>
</div>
</form>
</optgroup>
</select>
<input type="hidden" name="action" value="add" />
</div>
<button type="submit" class="btn btn-primary">Add</button>
</div>
<p></p>
{% for u in group.user_member.all %}
<div class="row">
</form>
<!-- User Members -->
{% if group.user_member.all %}
<div class="divider">User Members</div>
<div class="space-y-2">
{% for u in group.user_member.all %}
<form
id="modal-form-group-member_{{ u.uuid }}"
class="form-inline"
role="form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<div class="col-xs-8">
{{ u.username }}
<div class="flex items-center gap-2">
<span class="flex-1">{{ u.username }}</span>
<input type="hidden" name="member" value="{{ u.url }}" />
<input type="hidden" name="action" value="remove" />
</div>
<div class="col-xs-2"></div>
<div class="col-xs-2">
<button type="submit" style="width:60%;" class="btn col-xs-2">
<span class="glyphicon glyphicon-trash"></span>
<button type="submit" class="btn btn-error btn-sm">
Remove
</button>
</div>
</form>
</div>
{% endfor %}
<p></p>
{% for g in group.group_member.all %}
<div class="row">
{% endfor %}
</div>
{% endif %}
<!-- Group Members -->
{% if group.group_member.all %}
<div class="divider">Group Members</div>
<div class="space-y-2">
{% for g in group.group_member.all %}
<form
id="modal-form-group-member_{{ g.uuid }}"
class="form-inline"
role="form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<div class="col-xs-8">
{{ g.name|safe }}
<div class="flex items-center gap-2">
<span class="flex-1">{{ g.name|safe }}</span>
<input type="hidden" name="member" value="{{ g.url }}" />
<input type="hidden" name="action" value="remove" />
</div>
<div class="col-xs-2"></div>
<div class="col-xs-2">
<button type="submit" style="width:60%;" class="btn col-xs-2">
<span class="glyphicon glyphicon-trash"></span>
<button type="submit" class="btn btn-error btn-sm">
Remove
</button>
</div>
</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="edit-package-modal-submit"
>
Update
</button>
</div>
{% endfor %}
</div>
{% endif %}
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
>
Close
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#edit-package-modal-submit").click(function (e) {
e.preventDefault();
$("#edit-package-modal-form").submit();
});
$("#select_member").selectpicker();
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
</dialog>

View File

@ -1,71 +1,94 @@
{% load static %}
<!-- Edit Model -->
<div id="edit_model_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">&times;</span>
</button>
<h3 class="modal-title">Update Model</h3>
</div>
<div class="modal-body">
<p>Alter Name and Description of the Model.</p>
<form
id="edit-model-modal-form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<p>
<label for="model-name">Name</label>
<input
id="model-name"
type="text"
class="form-control"
name="model-name"
value="{{ model.name|safe }}"
/>
</p>
<p>
<label for="model-description">Description</label>
<input
id="model-description"
type="text"
class="form-control"
name="model-description"
value="{{ model.description|safe }}"
/>
</p>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button
type="button"
class="btn btn-primary"
id="edit-model-modal-submit"
>
Update
</button>
</div>
<dialog
id="edit_model_modal"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="font-bold text-lg">Update Model</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<p class="mb-4">Alter Name and Description of the Model.</p>
<form
id="edit-model-modal-form"
accept-charset="UTF-8"
action=""
method="post"
>
{% csrf_token %}
<div class="form-control mb-3">
<label class="label" for="model-name">
<span class="label-text">Name</span>
</label>
<input
id="model-name"
type="text"
class="input input-bordered w-full"
name="model-name"
value="{{ model.name|safe }}"
required
/>
</div>
<div class="form-control mb-3">
<label class="label" for="model-description">
<span class="label-text">Description</span>
</label>
<input
id="model-description"
type="text"
class="input input-bordered w-full"
name="model-description"
value="{{ model.description|safe }}"
/>
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('edit-model-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Update</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Updating...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#edit-model-modal-submit").click(function (e) {
e.preventDefault();
$("#edit-model-modal-form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,70 +1,91 @@
{% load static %}
<!-- Edit Node -->
<div id="edit_node_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit Node</h5>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>Edit Node.</p>
<form
id="edit-node-modal-form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<p>
<label for="node-name">Name</label>
<input
id="node-name"
class="form-control"
name="node-name"
value="{{ node.name|safe }}"
/>
</p>
<p>
<label for="node-description">Description</label>
<input
id="node-description"
type="text"
class="form-control"
value="{{ node.description|safe }}"
name="node-description"
/>
</p>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button
type="button"
class="btn btn-primary"
id="edit-node-modal-submit"
>
Create
</button>
</div>
<dialog
id="edit_node_modal"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="font-bold text-lg">Edit Node</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="edit-node-modal-form"
accept-charset="UTF-8"
action=""
method="post"
>
{% csrf_token %}
<div class="form-control mb-3">
<label class="label" for="node-name">
<span class="label-text">Name</span>
</label>
<input
id="node-name"
class="input input-bordered w-full"
name="node-name"
value="{{ node.name|safe }}"
required
/>
</div>
<div class="form-control mb-3">
<label class="label" for="node-description">
<span class="label-text">Description</span>
</label>
<input
id="node-description"
type="text"
class="input input-bordered w-full"
value="{{ node.description|safe }}"
name="node-description"
/>
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('edit-node-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Update</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Updating...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#edit-node-modal-submit").click(function (e) {
e.preventDefault();
$("#edit-node-modal-form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,70 +1,91 @@
{% load static %}
<!-- Edit Package -->
<div id="edit_package_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Update Package</h5>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>Edit a Package.</p>
<form
id="edit-package-modal-form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<p>
<label for="package-name">Name</label>
<input
id="package-name"
class="form-control"
name="package-name"
value="{{ package.name|safe }}"
/>
</p>
<p>
<label for="package-description">Description</label>
<input
id="package-description"
type="text"
class="form-control"
value="{{ package.description|safe }}"
name="package-description"
/>
</p>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button
type="button"
class="btn btn-primary"
id="edit-package-modal-submit"
>
Update
</button>
</div>
<dialog
id="edit_package_modal"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="font-bold text-lg">Update Package</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="edit-package-modal-form"
accept-charset="UTF-8"
action=""
method="post"
>
{% csrf_token %}
<div class="form-control mb-3">
<label class="label" for="package-name">
<span class="label-text">Name</span>
</label>
<input
id="package-name"
class="input input-bordered w-full"
name="package-name"
value="{{ package.name|safe }}"
required
/>
</div>
<div class="form-control mb-3">
<label class="label" for="package-description">
<span class="label-text">Description</span>
</label>
<input
id="package-description"
type="text"
class="input input-bordered w-full"
value="{{ package.description|safe }}"
name="package-description"
/>
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('edit-package-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Update</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Updating...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#edit-package-modal-submit").click(function (e) {
e.preventDefault();
$("#edit-package-modal-form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,264 +1,271 @@
{% load static %}
<!-- Edit Package Permission -->
<div id="edit_package_permissions_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Grant or Revoke Permissions</h5>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>
Modify permissions for this package. Note that if you give
<code>write</code> permissions to a user or group,
<code>read</code> permissions will be granted automatically.
<br />
To allow users to perform destructive actions, such as deleting the
package, <code>owner</code>
permissions must be granted.
</p>
<!-- Edit Package Permissions -->
<dialog
id="edit_package_permissions_modal"
class="modal"
x-data="{
updatePermissions(checkbox) {
const parts = checkbox.id.split('_');
const perm = parts[0];
const id = parts[1];
<div class="row">
<div class="col-xs-4">
<legend>User or Group</legend>
</div>
<div class="col-xs-2">
<legend>Read</legend>
</div>
<div class="col-xs-2">
<legend>Write</legend>
</div>
<div class="col-xs-2">
<legend>Owner</legend>
</div>
</div>
const readBox = document.getElementById('read_' + id);
const writeBox = document.getElementById('write_' + id);
const ownerBox = document.getElementById('owner_' + id);
<div class="row">
<form
id="modal-form-permissions"
class="form-inline"
role="form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<div class="col-xs-4">
<select
id="select_grantee"
name="grantee"
data-actions-box="true"
class="selPackages"
data-width="100%"
>
<option disabled selected>User</option>
if (perm === 'read' && !readBox.checked) {
writeBox.checked = false;
ownerBox.checked = false;
}
if (perm === 'write') {
if (writeBox.checked) {
readBox.checked = true;
} else {
ownerBox.checked = false;
}
}
if (perm === 'owner' && ownerBox.checked) {
readBox.checked = true;
writeBox.checked = true;
}
}
}"
>
<div class="modal-box max-w-2xl">
<!-- Header -->
<h3 class="text-lg font-bold">Grant or Revoke Permissions</h3>
<!-- Close button (X) -->
<form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2">
</button>
</form>
<!-- Body -->
<div class="py-4">
<p class="mb-4">
Modify permissions for this package. Note that if you give
<code class="badge badge-ghost">write</code> permissions to a user or
group, <code class="badge badge-ghost">read</code> permissions will be
granted automatically.
<br />
To allow users to perform destructive actions, such as deleting the
package, <code class="badge badge-ghost">owner</code> permissions must
be granted.
</p>
<!-- Add New Permission -->
<form
id="modal-form-permissions"
accept-charset="UTF-8"
action=""
method="post"
class="mb-4"
>
{% csrf_token %}
<div class="grid grid-cols-12 gap-2 items-end">
<div class="col-span-5">
<label class="label">
<span class="label-text">User or Group</span>
</label>
<select
id="select_grantee"
name="grantee"
class="select select-bordered w-full select-sm"
required
>
<optgroup label="Users">
{% for u in users %}
<option value="{{ u.url }}">{{ u.username }}</option>
{% endfor %}
<option disabled>Groups</option>
</optgroup>
<optgroup label="Groups">
{% for g in groups %}
<option value="{{ g.url }}">{{ g.name|safe }}</option>
{% endfor %}
</select>
</div>
<div class="col-xs-2">
<input type="checkbox" name="read" id="read_new" />
</div>
<div class="col-xs-2">
<input type="checkbox" name="write" id="write_new" />
</div>
<div class="col-xs-2">
<input type="checkbox" name="owner" id="owner_new" />
</div>
<div class="col-xs-2">
<button
type="submit"
style="width:60%;"
class="btn col-xs-2 modify-perm-button"
>
<span class="glyphicon glyphicon-plus"></span>
</button>
</div>
</form>
</optgroup>
</select>
</div>
<div class="col-span-2 text-center">
<label class="label justify-center">
<span class="label-text">Read</span>
</label>
<input
type="checkbox"
name="read"
id="read_new"
class="checkbox"
@click="updatePermissions($el)"
/>
</div>
<div class="col-span-2 text-center">
<label class="label justify-center">
<span class="label-text">Write</span>
</label>
<input
type="checkbox"
name="write"
id="write_new"
class="checkbox"
@click="updatePermissions($el)"
/>
</div>
<div class="col-span-2 text-center">
<label class="label justify-center">
<span class="label-text">Owner</span>
</label>
<input
type="checkbox"
name="owner"
id="owner_new"
class="checkbox"
@click="updatePermissions($el)"
/>
</div>
<div class="col-span-1">
<button type="submit" class="btn btn-primary btn-sm">+</button>
</div>
</div>
<p></p>
{% for up in user_permissions %}
<div class="row">
</form>
<!-- User Permissions -->
{% if user_permissions %}
<div class="divider">User Permissions</div>
<div class="space-y-2">
{% for up in user_permissions %}
<form
id="modal-form-permissions_{{ up.user.uuid }}"
class="form-inline"
role="form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<div class="col-xs-4">
{{ up.user.username }}
<input type="hidden" name="grantee" value="{{ up.user.url }}" />
</div>
<div class="col-xs-2">
<input
type="checkbox"
name="read"
id="read_{{ up.user.uuid }}"
{% if up.has_read %}checked{% endif %}
/>
</div>
<div class="col-xs-2">
<input
type="checkbox"
name="write"
id="write_{{ up.user.uuid }}"
{% if up.has_write %}checked{% endif %}
/>
</div>
<div class="col-xs-2">
<input
type="checkbox"
name="owner"
id="owner_{{ up.user.uuid }}"
{% if up.has_all %}checked{% endif %}
/>
</div>
<div class="col-xs-2">
<button
type="submit"
style="width:60%;"
class="btn col-xs-2 modify-perm-button"
>
<span class="glyphicon glyphicon-ok"></span>
</button>
<div class="grid grid-cols-12 gap-2 items-center">
<div class="col-span-5 truncate">
{{ up.user.username }}
<input
type="hidden"
name="grantee"
value="{{ up.user.url }}"
/>
</div>
<div class="col-span-2 text-center">
<input
type="checkbox"
name="read"
id="read_{{ up.user.uuid }}"
class="checkbox"
{% if up.has_read %}checked{% endif %}
@click="updatePermissions($el)"
/>
</div>
<div class="col-span-2 text-center">
<input
type="checkbox"
name="write"
id="write_{{ up.user.uuid }}"
class="checkbox"
{% if up.has_write %}checked{% endif %}
@click="updatePermissions($el)"
/>
</div>
<div class="col-span-2 text-center">
<input
type="checkbox"
name="owner"
id="owner_{{ up.user.uuid }}"
class="checkbox"
{% if up.has_all %}checked{% endif %}
@click="updatePermissions($el)"
/>
</div>
<div class="col-span-1">
<button type="submit" class="btn btn-sm btn-ghost"></button>
</div>
</div>
</form>
</div>
{% endfor %}
<p></p>
{% for gp in group_permissions %}
<div class="row">
{% endfor %}
</div>
{% endif %}
<!-- Group Permissions -->
{% if group_permissions %}
<div class="divider">Group Permissions</div>
<div class="space-y-2">
{% for gp in group_permissions %}
<form
id="modal-form-permissions_{{ gp.user.uuid }}"
class="form-inline"
role="form"
id="modal-form-permissions_{{ gp.group.uuid }}"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<div class="col-xs-4">
{{ gp.group.name|safe }}
<input
type="hidden"
name="grantee"
value="{{ gp.group.url }}"
/>
</div>
<div class="col-xs-2">
<input
type="checkbox"
name="read"
id="read_{{ gp.group.uuid }}"
{% if gp.has_read %}checked{% endif %}
/>
</div>
<div class="col-xs-2">
<input
type="checkbox"
name="write"
id="write_{{ gp.group.uuid }}"
{% if gp.has_write %}checked{% endif %}
/>
</div>
<div class="col-xs-2">
<input
type="checkbox"
name="owner"
id="owner_{{ gp.group.uuid }}"
{% if gp.has_all %}checked{% endif %}
/>
</div>
<div class="col-xs-2">
<button
type="submit"
style="width:60%;"
class="btn col-xs-2 modify-perm-button"
>
<span class="glyphicon glyphicon-ok"></span>
</button>
<div class="grid grid-cols-12 gap-2 items-center">
<div class="col-span-5 truncate">
{{ gp.group.name|safe }}
<input
type="hidden"
name="grantee"
value="{{ gp.group.url }}"
/>
</div>
<div class="col-span-2 text-center">
<input
type="checkbox"
name="read"
id="read_{{ gp.group.uuid }}"
class="checkbox"
{% if gp.has_read %}checked{% endif %}
@click="updatePermissions($el)"
/>
</div>
<div class="col-span-2 text-center">
<input
type="checkbox"
name="write"
id="write_{{ gp.group.uuid }}"
class="checkbox"
{% if gp.has_write %}checked{% endif %}
@click="updatePermissions($el)"
/>
</div>
<div class="col-span-2 text-center">
<input
type="checkbox"
name="owner"
id="owner_{{ gp.group.uuid }}"
class="checkbox"
{% if gp.has_all %}checked{% endif %}
@click="updatePermissions($el)"
/>
</div>
<div class="col-span-1">
<button type="submit" class="btn btn-sm btn-ghost"></button>
</div>
</div>
</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="edit-package-modal-submit"
>
Update
</button>
</div>
{% endfor %}
</div>
{% endif %}
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
>
Close
</button>
</div>
</div>
</div>
<script>
function checkboxClick() {
// id looks like read_3cadef24-220e-4587-9fa5-0e9a17aca2da
parts = this.id.split("_");
perm = parts[0];
id = parts[1];
readbox = "#read_" + id;
writebox = "#write_" + id;
ownerbox = "#owner_" + id;
if (perm == "read" && !$(readbox).prop("checked")) {
$(writebox).prop("checked", false);
$(ownerbox).prop("checked", false);
}
if (perm == "write") {
if ($(writebox).prop("checked")) {
$(readbox).prop("checked", true);
}
if (!$(writebox).prop("checked")) {
$(ownerbox).prop("checked", false);
}
}
if (perm == "owner") {
if ($(ownerbox).prop("checked")) {
$(readbox).prop("checked", true);
$(writebox).prop("checked", true);
}
}
}
$(function () {
$("#edit-package-modal-submit").click(function (e) {
e.preventDefault();
$("#edit-package-modal-form").submit();
});
$("#select_grantee").selectpicker();
// Add click functions to permission checkboxes
$('[id^="read_"]').on("click", checkboxClick);
$('[id^="write_"]').on("click", checkboxClick);
$('[id^="owner_"]').on("click", checkboxClick);
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
</dialog>

View File

@ -1,82 +1,119 @@
{% load static %}
<!-- Edit Package -->
<div id="edit_password_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Update your Password</h5>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>To change your password please fill out the following inputs</p>
<form
id="edit-password-modal-form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<p>
<label for="old-password">Old Password</label>
<input
id="old-password"
class="form-control"
name="old-password"
type="password"
autocomplete="current-password"
/>
</p>
<p>
<label for="new-password">New Password</label>
<input
id="new-password"
class="form-control"
name="new-password"
type="password"
,
autocomplete="new-password"
/>
</p>
<p>
<label for="new-password-repeat">Repeat New Password</label>
<input
id="new-password-repeat"
class="form-control"
name="new-password-repeat"
type="password"
autocomplete="new-password"
/>
</p>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button
type="button"
class="btn btn-primary"
id="edit-password-modal-submit"
>
Update
</button>
</div>
<dialog
id="edit_password_modal"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="font-bold text-lg">Update your Password</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<p class="mb-4">
To change your password please fill out the following inputs
</p>
<form
id="edit-password-modal-form"
accept-charset="UTF-8"
action=""
method="post"
>
{% csrf_token %}
<input type="hidden" name="hidden" value="update-password" />
<div class="form-control mb-3">
<label class="label" for="old-password">
<span class="label-text">Old Password</span>
</label>
<input
id="old-password"
class="input input-bordered w-full"
name="old-password"
type="password"
autocomplete="current-password"
required
/>
</div>
<div class="form-control mb-3">
<label class="label" for="new-password">
<span class="label-text">New Password</span>
</label>
<input
id="new-password"
class="input input-bordered w-full"
name="new-password"
type="password"
autocomplete="new-password"
required
/>
</div>
<div class="form-control mb-3">
<label class="label" for="new-password-repeat">
<span class="label-text">Repeat New Password</span>
</label>
<input
id="new-password-repeat"
class="input input-bordered w-full"
name="new-password-repeat"
type="password"
autocomplete="new-password"
required
@input="validatePasswordMatch('new-password', 'new-password-repeat')"
/>
<label class="label" x-show="errors['new-password-repeat']">
<span
class="label-text-alt text-error"
x-text="errors['new-password-repeat']"
></span>
</label>
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="if (validatePasswordMatch('new-password', 'new-password-repeat')) submit('edit-password-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Update</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Updating...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#edit-password-modal-submit").click(function (e) {
e.preventDefault();
$("#edit-password-modal-form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,72 +1,92 @@
{% load static %}
<!-- Edit Pathway -->
<div id="edit_pathway_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit Pathway</h5>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>Edit Pathway.</p>
<form
id="edit-pathway-modal-form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<p>
<label for="pathway-name">Name</label>
<input
id="pathway-name"
class="form-control"
name="pathway-name"
value="{{ pathway.name|safe }}"
/>
</p>
<p>
<label for="pathway-description">Description</label>
<textarea
id="pathway-description"
type="text"
class="form-control"
name="pathway-description"
rows="10"
>
<dialog
id="edit_pathway_modal"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="font-bold text-lg">Edit Pathway</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="edit-pathway-modal-form"
accept-charset="UTF-8"
action=""
method="post"
>
{% csrf_token %}
<div class="form-control mb-3">
<label class="label" for="pathway-name">
<span class="label-text">Name</span>
</label>
<input
id="pathway-name"
class="input input-bordered w-full"
name="pathway-name"
value="{{ pathway.name|safe }}"
required
/>
</div>
<div class="form-control mb-3">
<label class="label" for="pathway-description">
<span class="label-text">Description</span>
</label>
<textarea
id="pathway-description"
class="textarea textarea-bordered w-full"
name="pathway-description"
rows="10"
>
{{ pathway.description|safe }}</textarea
>
</p>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button
type="button"
class="btn btn-primary"
id="edit-pathway-modal-submit"
>
Update
</button>
</div>
>
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('edit-pathway-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Update</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Updating...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#edit-pathway-modal-submit").click(function (e) {
e.preventDefault();
$("#edit-pathway-modal-form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,163 +1,156 @@
{% load static %}
<!-- Edit Package -->
<div id="update_prediction_settings_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Update Prediction Setting</h5>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>
To update your prediction setting modify parameters in the form below
und click "Update"
</p>
<form
id="edit-prediction-setting-modal-form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<div id="prediction-setting" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<table class="table-bordered table-hover table">
<tr style="background-color: rgba(0, 0, 0, 0.08);">
<th scope="col" width="20%">Parameter</th>
<th scope="col" width="80%">Value</th>
<!-- Edit Prediction Setting -->
<dialog
id="update_prediction_settings_modal"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-box max-w-3xl">
<!-- Header -->
<h3 class="text-lg font-bold">Update Prediction Setting</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<p class="mb-4">
To update your prediction setting modify parameters in the form below
and click "Update"
</p>
<form
id="edit-prediction-setting-modal-form"
accept-charset="UTF-8"
action=""
method="post"
>
{% csrf_token %}
<div class="overflow-x-auto">
<table class="table table-zebra w-full">
<thead>
<tr>
<th class="w-1/5">Parameter</th>
<th class="w-4/5">Value</th>
</tr>
</thead>
<tbody>
{% if 'model' in user.prediction_settings %}
<tr>
<td>Model</td>
<td>
<div class="form-control">
<select
id="model"
name="model"
class="select select-bordered w-full"
>
{% for m in models %}
<option
value="{{ m.id }}"
{% if user.prediction_settings.model.url == m.url %}selected{% endif %}
>
{{ m.name|safe }}
</option>
{% endfor %}
</select>
</div>
{% for k, v in user.prediction_settings.model_parameters.items %}
{% if k == 'threshold' %}
<div class="form-control mt-2">
<label class="label">
<span class="label-text">Threshold</span>
</label>
<input
type="number"
class="input input-bordered w-full"
name="{{ k }}"
value="{{ v }}"
min="0"
max="1"
step="0.05"
/>
</div>
{% endif %}
{% endfor %}
</td>
</tr>
<tbody>
{% if 'model' in user.prediction_settings %}
<tr>
<td width="20%">Model</td>
<td width="80%">
<table
width="100%"
class="table-bordered table-hover table"
>
<tbody>
<tr>
<td colspan="2">
<select
id="model"
name="model"
class="form-control"
data-width="100%"
>
{% for m in models %}
<option
value="{{ m.id }}"
{% if user.prediction_settings.model.url == m.url %}selected{% endif %}
>
{{ m.name|safe }}
</option>
{% endfor %}
</select>
</td>
</tr>
{% for k, v in user.prediction_settings.model_parameters.items %}
<tr>
<th width="20%">Model Parameter</th>
<th width="80%">Parameter Value</th>
</tr>
<tr>
<td width="20%">
{% if k == 'threshold' %}
Threshold
{% endif %}
</td>
<td width="80%">
{% if k == 'threshold' %}
<input
type="number"
class="form-control"
name="{{ k }}"
value="{{ v }}"
min="0"
max="1"
step="0.05"
/>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</td>
</tr>
{% endif %}
{% for k, v in user.prediction_settings.truncator.items %}
<tr>
<td>
<p>
{% if k == 'max_nodes' %}
Max Nodes
{% elif k == 'max_depth' %}
Max Depth
{% endif %}
</p>
</td>
<td>
<p>
{% if k == 'max_nodes' %}
<input
type="number"
class="form-control"
name="{{ k }}"
value="{{ v }}"
min="1"
max="50"
step="1"
/>
{% elif k == 'max_depth' %}
<input
type="number"
class="form-control"
name="{{ k }}"
value="{{ v }}"
min="1"
max="8"
step="1"
/>
{% endif %}
</p>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</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-prediction-setting-modal-submit"
>
Update
</button>
</div>
{% endif %}
{% for k, v in user.prediction_settings.truncator.items %}
<tr>
<td>
{% if k == 'max_nodes' %}
Max Nodes
{% elif k == 'max_depth' %}
Max Depth
{% endif %}
</td>
<td>
{% if k == 'max_nodes' %}
<input
type="number"
class="input input-bordered w-full"
name="{{ k }}"
value="{{ v }}"
min="1"
max="50"
step="1"
/>
{% elif k == 'max_depth' %}
<input
type="number"
class="input input-bordered w-full"
name="{{ k }}"
value="{{ v }}"
min="1"
max="8"
step="1"
/>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('edit-prediction-setting-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Update</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Updating...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#edit-prediction-setting-modal-submit").click(function (e) {
e.preventDefault();
$("#edit-prediction-setting-modal-form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,69 +1,91 @@
{% load static %}
<!-- Edit Reaction -->
<div id="edit_reaction_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">&times;</span>
</button>
<h3 class="modal-title">Update Reaction</h3>
</div>
<div class="modal-body">
<form
id="edit-reaction-modal-form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<p>
<label for="reaction-name">Name</label>
<input
id="reaction-name"
class="form-control"
name="reaction-name"
value="{{ reaction.name|safe }}"
/>
</p>
<p>
<label for="reaction-description">Description</label>
<input
id="reaction-description"
type="text"
class="form-control"
value="{{ reaction.description|safe }}"
name="reaction-description"
/>
</p>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button
type="button"
class="btn btn-primary"
id="edit-reaction-modal-submit"
>
Update
</button>
</div>
<dialog
id="edit_reaction_modal"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="font-bold text-lg">Update Reaction</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="edit-reaction-modal-form"
accept-charset="UTF-8"
action=""
method="post"
>
{% csrf_token %}
<div class="form-control mb-3">
<label class="label" for="reaction-name">
<span class="label-text">Name</span>
</label>
<input
id="reaction-name"
class="input input-bordered w-full"
name="reaction-name"
value="{{ reaction.name|safe }}"
required
/>
</div>
<div class="form-control mb-3">
<label class="label" for="reaction-description">
<span class="label-text">Description</span>
</label>
<input
id="reaction-description"
type="text"
class="input input-bordered w-full"
value="{{ reaction.description|safe }}"
name="reaction-description"
/>
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('edit-reaction-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Update</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Updating...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#edit-reaction-modal-submit").click(function (e) {
e.preventDefault();
$("#edit-reaction-modal-form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,69 +1,91 @@
{% load static %}
<!-- Edit Rule -->
<div id="edit_rule_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
<h3 class="modal-title">Update Rule</h3>
</div>
<div class="modal-body">
<form
id="edit-rule-modal-form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<p>
<label for="rule-name">Name</label>
<input
id="rule-name"
class="form-control"
name="rule-name"
value="{{ rule.name|safe }}"
/>
</p>
<p>
<label for="rule-description">Description</label>
<input
id="rule-description"
type="text"
class="form-control"
value="{{ rule.description|safe }}"
name="rule-description"
/>
</p>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button
type="button"
class="btn btn-primary"
id="edit-rule-modal-submit"
>
Update
</button>
</div>
<dialog
id="edit_rule_modal"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="font-bold text-lg">Update Rule</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="edit-rule-modal-form"
accept-charset="UTF-8"
action=""
method="post"
>
{% csrf_token %}
<div class="form-control mb-3">
<label class="label" for="rule-name">
<span class="label-text">Name</span>
</label>
<input
id="rule-name"
class="input input-bordered w-full"
name="rule-name"
value="{{ rule.name|safe }}"
required
/>
</div>
<div class="form-control mb-3">
<label class="label" for="rule-description">
<span class="label-text">Description</span>
</label>
<input
id="rule-description"
type="text"
class="input input-bordered w-full"
value="{{ rule.description|safe }}"
name="rule-description"
/>
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('edit-rule-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Update</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Updating...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#edit-rule-modal-submit").click(function (e) {
e.preventDefault();
$("#edit-rule-modal-form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,110 +1,128 @@
{% load static %}
<!-- Edit User -->
<div id="edit_user_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Update User Defaults</h5>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>Edit User Defaults.</p>
<form
id="edit-user-modal-form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<p>
<label for="default-package">Default Package</label>
<select
id="default-package"
name="default-package"
class="form-control"
data-width="100%"
>
<option disabled>Select a Package</option>
{% for p in meta.writeable_packages %}
<option
value="{{ p.url }}"
{% if p.id == meta.user.default_package.id %}selected{% endif %}
>
{{ p.name|safe }}
</option>
{% endfor %}
</select>
</p>
<p>
<label for="default-group">Default Group</label>
<select
id="default-group"
name="default-group"
class="form-control"
data-width="100%"
>
<option disabled>Select a Group</option>
{% for g in meta.available_groups %}
<option
value="{{ g.url }}"
{% if g.id == meta.user.default_group.id %}selected{% endif %}
>
{{ g.name|safe }}
</option>
{% endfor %}
</select>
</p>
<p>
<label for="default-prediction-setting"
>Default Prediction Setting</label
>
<select
id="default-prediction-setting"
name="default-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|safe }}
</option>
{% endfor %}
</select>
</p>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button
type="button"
class="btn btn-primary"
id="edit-user-modal-submit"
>
Update
</button>
</div>
<dialog
id="edit_user_modal"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="font-bold text-lg">Update User Defaults</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="edit-user-modal-form"
accept-charset="UTF-8"
action=""
method="post"
>
{% csrf_token %}
<div class="form-control mb-3">
<label class="label" for="default-package">
<span class="label-text">Default Package</span>
</label>
<select
id="default-package"
name="default-package"
class="select select-bordered w-full"
>
<option disabled>Select a Package</option>
{% for p in meta.writeable_packages %}
<option
value="{{ p.url }}"
{% if p.id == meta.user.default_package.id %}selected{% endif %}
>
{{ p.name|safe }}
</option>
{% endfor %}
</select>
</div>
<div class="form-control mb-3">
<label class="label" for="default-group">
<span class="label-text">Default Group</span>
</label>
<select
id="default-group"
name="default-group"
class="select select-bordered w-full"
>
<option disabled>Select a Group</option>
{% for g in meta.available_groups %}
<option
value="{{ g.url }}"
{% if g.id == meta.user.default_group.id %}selected{% endif %}
>
{{ g.name|safe }}
</option>
{% endfor %}
</select>
</div>
<div class="form-control mb-3">
<label class="label" for="default-prediction-setting">
<span class="label-text">Default Prediction Setting</span>
</label>
<select
id="default-prediction-setting"
name="default-prediction-setting"
class="select select-bordered w-full"
>
<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|safe }}
</option>
{% endfor %}
</select>
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('edit-user-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Update</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Updating...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#edit-user-modal-submit").click(function (e) {
e.preventDefault();
$("#edit-user-modal-form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,93 +1,123 @@
<div
class="modal fade"
tabindex="-1"
<dialog
id="evaluate_model_modal"
role="dialog"
aria-labelledby="evaluate_model_modal"
aria-hidden="true"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
<h4 class="modal-title">Evaluate Model</h4>
</div>
<div class="modal-body">
<form
id="evaluate_model_form"
accept-charset="UTF-8"
action="{{ current_object.url }}"
data-remote="true"
method="post"
>
{% csrf_token %}
<div class="jumbotron">
<div class="modal-box max-w-3xl">
<!-- Header -->
<h3 class="text-lg font-bold">Evaluate Model</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="evaluate_model_form"
accept-charset="UTF-8"
action="{{ current_object.url }}"
method="post"
>
{% csrf_token %}
<div class="alert alert-info mb-4">
<span>
For evaluation, you need to select the packages you want to use.
While the model is evaluating, you can use the model for
predictions.
</div>
<!-- Evaluation Packages -->
<label for="model-evaluation-packages">Evaluation Packages</label>
</span>
</div>
<!-- Evaluation Packages -->
<div class="form-control">
<label class="label" for="model-evaluation-packages">
<span class="label-text">Evaluation Packages</span>
</label>
<select
id="model-evaluation-packages"
name="model-evaluation-packages"
data-actions-box="true"
class="form-control"
class="select select-bordered w-full h-48"
multiple
data-width="100%"
required
>
<option disabled>Reviewed Packages</option>
{% for obj in meta.readable_packages %}
{% if obj.reviewed %}
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
{% endif %}
{% endfor %}
<option disabled>Unreviewed Packages</option>
{% for obj in meta.readable_packages %}
{% if not obj.reviewed %}
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
{% endif %}
{% endfor %}
<optgroup label="Reviewed Packages">
{% for obj in meta.readable_packages %}
{% if obj.reviewed %}
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
{% endif %}
{% endfor %}
</optgroup>
<optgroup label="Unreviewed Packages">
{% for obj in meta.readable_packages %}
{% if not obj.reviewed %}
<option value="{{ obj.url }}">{{ obj.name|safe }}</option>
{% endif %}
{% endfor %}
</optgroup>
</select>
<label class="label">
<span class="label-text-alt"
>Hold Ctrl/Cmd to select multiple packages</span
>
</label>
</div>
<!-- Eval Type -->
<label for="model-evaluation-type">Evaluation Type</label>
<!-- Eval Type -->
<div class="form-control mt-4">
<label class="label" for="model-evaluation-type">
<span class="label-text">Evaluation Type</span>
</label>
<select
id="model-evaluation-type"
name="model-evaluation-type"
class="form-control"
class="select select-bordered w-full"
required
>
<option disabled selected>Select evaluation type</option>
<option value="" disabled selected>Select evaluation type</option>
<option value="sg">Single Generation</option>
<option value="mg">Multiple Generations</option>
</select>
</div>
<input type="hidden" name="hidden" value="evaluate" />
</form>
</div>
<div class="modal-footer">
<a id="evaluate_model_form_submit" class="btn btn-primary" href="#"
>Evaluate</a
>
<button type="button" class="btn btn-default" data-dismiss="modal">
Cancel
</button>
</div>
<input type="hidden" name="hidden" value="evaluate" />
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Cancel
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('evaluate_model_form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Evaluate</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Evaluating...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#model-evaluation-packages").selectpicker();
$("#evaluate_model_form_submit").on("click", function (e) {
e.preventDefault();
$("#evaluate_model_form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,53 +1,56 @@
{% load static %}
<!-- Export Package -->
<div id="export_package_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Export Package as JSON</h3>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<dialog
id="export_package_modal"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="font-bold text-lg">Export Package as JSON</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<p>
By clicking on Export the Package will be serialized into a JSON and
directly downloaded.
<form
id="export-package-modal-form"
accept-charset="UTF-8"
action="{{ package.url }}"
data-remote="true"
method="GET"
>
<input type="hidden" name="export" value="true" />
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button
type="button"
class="btn btn-primary"
id="export-package-modal-form-submit"
>
Export
</button>
</div>
opened in a new tab.
</p>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="window.open('{{ package.url }}?export=true', '_blank'); $el.closest('dialog').close();"
:disabled="isSubmitting"
>
Export
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#export-package-modal-form-submit").click(function (e) {
e.preventDefault();
$("#export-package-modal-form").submit();
$("#export_package_modal").modal("hide");
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,109 +1,142 @@
{% 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">&times;</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
>
<dialog
id="generic_copy_object_modal"
class="modal"
x-data="{
isSubmitting: false,
errorMessage: '',
targetPackage: '',
reset() {
this.isSubmitting = false;
this.errorMessage = '';
this.targetPackage = '';
},
async submit() {
if (!this.targetPackage) return;
this.isSubmitting = true;
this.errorMessage = '';
try {
const response = await fetch(this.targetPackage, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
},
body: new URLSearchParams({
hidden: 'copy',
object_to_copy: '{{ current_object.url }}'
})
});
const data = await response.json();
if (response.ok) {
window.location.href = data.success;
} else {
if (data.error && data.error.indexOf('to the same package') > -1) {
this.errorMessage = 'The target Package is the same as the source Package. Please select another target!';
} else {
this.errorMessage = data.error || 'An error occurred';
}
}
} catch (error) {
this.errorMessage = 'An error occurred while copying';
} finally {
this.isSubmitting = false;
}
}
}"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="text-lg font-bold">Copy {{ object_type|capfirst }}</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="generic-copy-object-modal-form"
accept-charset="UTF-8"
method="post"
>
{% csrf_token %}
<div class="form-control">
<label class="label" for="target-package">
<span class="label-text">
Select the Target Package you want to copy this {{ object_type }}
into
</span>
</label>
<select
id="target-package"
name="target-package"
data-actions-box="true"
class="form-control"
data-width="100%"
class="select select-bordered w-full"
x-model="targetPackage"
required
>
<option disabled selected>Select Target Package</option>
<option value="" disabled selected>Select Target Package</option>
{% for p in meta.writeable_packages %}
<option value="{{ p.url }}">{{ p.name|safe }}</option>
`
{% endfor %}
</select>
<input type="hidden" name="hidden" value="copy" />
</form>
<div
id="copy-object-error-message"
class="alert alert-danger"
role="alert"
style="display: none"
></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button
type="button"
class="btn btn-primary"
id="generic-copy-object-modal-form-submit"
>
Copy
</button>
</div>
<input type="hidden" name="hidden" value="copy" />
</form>
<!-- Error Message -->
<div
x-show="errorMessage"
x-cloak
class="alert alert-error mt-4"
role="alert"
>
<span x-text="errorMessage"></span>
</div>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit()"
:disabled="isSubmitting || !targetPackage"
>
<span x-show="!isSubmitting">Copy</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Copying...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#generic-copy-object-modal-form-submit").click(function (e) {
e.preventDefault();
$("#copy-object-error-message").hide();
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 }}",
};
$.ajax({
type: "post",
data: formData,
url: packageUrl,
success: function (data, textStatus) {
window.location.href = data.success;
},
error: function (jqXHR, textStatus, errorThrown) {
if (jqXHR.responseJSON.error.indexOf("to the same package") > -1) {
$("#copy-object-error-message").append(
"<p>The target Package is the same as the source Package. Please select another target!</p>",
);
} else {
$("#copy-object-error-message").append(
"<p>" + jqXHR.responseJSON.error + "</p>",
);
}
$("#copy-object-error-message").show();
},
});
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,58 +1,99 @@
{% 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"
<!--
Generic Delete Modal - Delete object with confirmation
Migrated from Bootstrap + jQuery to DaisyUI + Alpine.js
Uses native <dialog> element with .showModal() API
-->
<dialog
id="generic_delete_modal"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="font-bold text-lg">Delete {{ object_type|capfirst }}</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<!-- Warning message -->
<div class="alert alert-warning">
<svg
xmlns="http://www.w3.org/2000/svg"
class="stroke-current shrink-0 h-6 w-6"
fill="none"
viewBox="0 0 24 24"
>
<span aria-hidden="true">&times;</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>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<span>
{% 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 %}
</span>
</div>
<!-- Hidden form -->
<form
id="generic-delete-modal-form"
accept-charset="UTF-8"
action="{{ current_object.url }}"
method="post"
>
{% csrf_token %}
<input type="hidden" name="hidden" value="delete" />
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Cancel
</button>
<button
type="button"
class="btn btn-error"
@click="submit('generic-delete-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Delete</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Deleting...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#generic-delete-modal-form-submit").click(function (e) {
e.preventDefault();
$("#generic-delete-modal-form").submit();
});
});
</script>
<!-- Backdrop (click to close) -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,213 +1,173 @@
{% load static %}
<style>
.alias-container {
display: flex;
flex-wrap: wrap;
align-items: center;
border: 1px solid #ccc;
border-radius: 4px;
padding: 4px 6px;
cursor: text;
min-height: 38px;
background-color: #fff;
}
.alias {
display: inline-flex;
align-items: center;
background-color: #5bc0de;
color: white;
padding: 4px 8px;
margin: 3px 3px;
border-radius: 4px;
font-size: 13px;
line-height: 1.4;
}
.alias .remove {
margin-left: 6px;
cursor: pointer;
font-weight: bold;
line-height: 1;
}
.alias-input {
flex: 1;
min-width: 120px;
border: none;
outline: none;
margin: 3px 3px;
font-size: 14px;
}
.form-control.alias-container {
height: auto;
box-shadow: none;
}
</style>
<div
class="modal fade bs-modal-lg"
<dialog
id="set_aliases_modal"
tabindex="-1"
aria-labelledby="set_aliases_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 Aliases for {{ current_object.name|safe }}
</h4>
</div>
<div class="modal-body">
<form
id="set_aliases_modal_form"
accept-charset="UTF-8"
action="{{ current_object.url }}"
data-remote="true"
method="post"
>
{% csrf_token %}
<label for="alias-input">Aliases:</label>
<div class="form-control alias-container" id="alias-box">
{% for alias in current_object.aliases %}
<span class="alias"
>{{ alias|escape }}<span class="remove">&times;</span></span
>
{% endfor %}
<input
type="text"
id="alias-input"
class="alias-input"
placeholder="Add Alias..."
/>
</div>
</form>
<div
id="add-alias-error-message"
class="alert alert-danger"
role="alert"
style="display: none"
></div>
</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_aliases_modal_form_submit"
>
Submit
</button>
</div>
</div>
</div>
</div>
<script>
$(function () {
function addAlias(aliasText) {
aliasText = aliasText.trim();
if (aliasText === "") return;
class="modal"
x-data="{
isSubmitting: false,
aliases: [{% for alias in current_object.aliases %}'{{ alias|escapejs }}'{% if not forloop.last %},{% endif %}{% endfor %}],
newAlias: '',
errorMessage: '',
// Avoid duplicate aliass
var exists = false;
$("#alias-box .alias").each(function () {
if (
$(this).text().replace("×", "").trim().toLowerCase() ===
aliasText.toLowerCase()
) {
exists = true;
return false;
}
});
reset() {
this.isSubmitting = false;
this.errorMessage = '';
},
addAlias() {
const aliasText = this.newAlias.trim();
if (aliasText === '') return;
// Check for duplicates (case-insensitive)
const exists = this.aliases.some(
a => a.toLowerCase() === aliasText.toLowerCase()
);
if (!exists) {
var aliasHtml =
'<span class="alias">' +
$("<div>").text(aliasText).html() +
'<span class="remove">&times;</span></span>';
$(aliasHtml).insertBefore("#alias-input");
this.aliases.push(aliasText);
}
$("#alias-input").val("");
}
this.newAlias = '';
},
// Add alias when Enter is pressed
$("#alias-input").on("keypress", function (e) {
if (e.which === 13) {
removeAlias(index) {
this.aliases.splice(index, 1);
},
handleKeypress(e) {
if (e.key === 'Enter') {
e.preventDefault();
addAlias($(this).val());
this.addAlias();
}
});
},
// Add alias when input loses focus
$("#alias-input").on("blur", function () {
var val = $(this).val();
if (val.trim() !== "") {
addAlias(val);
handleBlur() {
if (this.newAlias.trim() !== '') {
this.addAlias();
}
});
},
// Remove alias when clicking ×
$("#alias-box").on("click", ".remove", function () {
$(this).closest(".alias").remove();
});
async submit() {
this.isSubmitting = true;
this.errorMessage = '';
// Focus input when clicking the container
$("#alias-box").on("click", function () {
$("#alias-input").focus();
});
$("#set_aliases_modal_form_submit").on("click", function (e) {
e.preventDefault();
let aliases = [];
$("#alias-box .alias").each(function () {
aliases.push($(this).text().replace("×", "").trim());
});
if (aliases.length === 0) {
// Set empty string for deletion of all aliases
// If empty list is sent, its gets removed entirely from post data
aliases = [""];
const formData = new URLSearchParams();
if (this.aliases.length === 0) {
formData.append('aliases', '');
} else {
this.aliases.forEach(alias => {
formData.append('aliases', alias);
});
}
formData = {
aliases: aliases,
};
try {
const response = await fetch('{{ current_object.url }}', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: formData
});
$.ajax({
type: "post",
data: formData,
url: "{{ current_object.url }}",
traditional: true,
success: function (data, textStatus) {
if (response.ok) {
const data = await response.json();
window.location.href = data.success;
},
error: function (jqXHR, textStatus, errorThrown) {
$("#add-alias-error-message").append(
"<p>Setting aliases failed!</p>",
);
$("#add-alias-error-message").show();
},
});
});
});
</script>
} else {
this.errorMessage = 'Setting aliases failed!';
}
} catch (error) {
this.errorMessage = 'Setting aliases failed!';
} finally {
this.isSubmitting = false;
}
}
}"
@close="reset()"
>
<div class="modal-box max-w-4xl">
<!-- Header -->
<h3 class="text-lg font-bold">
Set Aliases for {{ current_object.name|safe }}
</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<div class="form-control">
<label class="label">
<span class="label-text">Aliases:</span>
</label>
<div
class="flex flex-wrap items-center gap-1 p-2 border border-base-300 rounded-lg bg-base-100 min-h-[38px] cursor-text"
@click="$refs.aliasInput.focus()"
>
<template x-for="(alias, index) in aliases" :key="index">
<span class="badge badge-info gap-1 py-3">
<span x-text="alias"></span>
<button
type="button"
class="btn btn-ghost btn-xs p-0 h-auto min-h-0"
@click.stop="removeAlias(index)"
>
</button>
</span>
</template>
<input
type="text"
x-ref="aliasInput"
x-model="newAlias"
class="flex-1 min-w-[120px] border-none outline-none bg-transparent text-sm"
placeholder="Add Alias..."
@keypress="handleKeypress($event)"
@blur="handleBlur()"
/>
</div>
</div>
<!-- Error Message -->
<div x-show="errorMessage" x-cloak class="alert alert-error mt-4">
<span x-text="errorMessage"></span>
</div>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit()"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Submit</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Submitting...</span>
</button>
</div>
</div>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,97 +1,137 @@
{% load static %}
<!-- Delete Object -->
<div id="generic_set_external_reference_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Add External References</h3>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form
id="generic-set-external-reference-modal-form"
accept-charset="UTF-8"
action="{{ current_object.url }}"
data-remote="true"
method="post"
>
{% csrf_token %}
<label for="database-select"
>Select the Database you want to attach an External Reference
for</label
>
<!-- Set External Reference -->
<dialog
id="generic_set_external_reference_modal"
class="modal"
x-data="{
isSubmitting: false,
selectedDatabase: '',
placeholder: '',
reset() {
this.isSubmitting = false;
this.selectedDatabase = '';
this.placeholder = '';
},
updatePlaceholder() {
if (this.selectedDatabase) {
const option = document.querySelector('#database-select option[value=\'' + this.selectedDatabase + '\']');
if (option) {
this.placeholder = option.dataset.inputPlaceholder || '';
}
}
},
submit(formId) {
const form = document.getElementById(formId);
if (form && form.checkValidity()) {
this.isSubmitting = true;
form.submit();
} else if (form) {
form.reportValidity();
}
}
}"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="text-lg font-bold">Add External References</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="generic-set-external-reference-modal-form"
accept-charset="UTF-8"
action="{{ current_object.url }}"
method="post"
>
{% csrf_token %}
<div class="form-control">
<label class="label" for="database-select">
<span class="label-text">
Select the Database you want to attach an External Reference for
</span>
</label>
<select
id="database-select"
name="selected-database"
data-actions-box="true"
class="form-control"
data-width="100%"
class="select select-bordered w-full"
x-model="selectedDatabase"
@change="updatePlaceholder()"
required
>
<option disabled selected>Select Database</option>
<option value="" disabled selected>Select Database</option>
{% for entity, databases in meta.external_databases.items %}
{% if entity == object_type %}
{% for db in databases %}
<option
id="db-select-{{ db.database.pk }}"
data-input-placeholder="{{ db.placeholder }}"
value="{{ db.database.id }}"
data-input-placeholder="{{ db.placeholder }}"
>
{{ db.database.name|safe }}
</option>
`
{% endfor %}
{% endif %}
{% endfor %}
</select>
<p></p>
<div id="input-div" style="display: none">
<label for="identifier">The reference</label>
<input
type="text"
id="identifier"
name="identifier"
class="form-control"
placeholder=""
/>
</div>
</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-set-external-reference-modal-form-submit"
>
Submit
</button>
</div>
</div>
<div class="form-control mt-4" x-show="selectedDatabase" x-cloak>
<label class="label" for="identifier">
<span class="label-text">The reference</span>
</label>
<input
type="text"
id="identifier"
name="identifier"
class="input input-bordered w-full"
:placeholder="placeholder"
required
/>
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('generic-set-external-reference-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Submit</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Submitting...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#database-select").on("change", function () {
let selected = $(this).val();
$("#identifier").attr(
"placeholder",
$("#db-select-" + selected).data("input-placeholder"),
);
$("#input-div").show();
});
$("#generic-set-external-reference-modal-form-submit").click(function (e) {
e.preventDefault();
$("#generic-set-external-reference-modal-form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,112 +1,142 @@
{% load static %}
<div
class="modal fade bs-modal-lg"
<dialog
id="set_scenario_modal"
tabindex="-1"
aria-labelledby="set_scenario_modal"
aria-modal="true"
role="dialog"
class="modal"
x-data="{
isSubmitting: false,
isLoading: false,
loaded: false,
scenarios: [],
attachedScenarios: [{% for scen in current_object.scenarios.all %}'{{ scen.url }}'{% if not forloop.last %},{% endif %}{% endfor %}],
reset() {
this.isSubmitting = false;
},
async loadScenarios() {
if (this.loaded) return;
this.isLoading = true;
try {
const response = await fetch('{% url "package scenario list" meta.current_package.uuid %}');
const data = await response.json();
this.scenarios = data;
this.loaded = true;
} catch (error) {
console.error('Error loading scenarios:', error);
} finally {
this.isLoading = false;
}
},
isSelected(url) {
return this.attachedScenarios.includes(url);
},
submit() {
this.isSubmitting = true;
const select = document.getElementById('scenario-select');
if (select.selectedOptions.length === 0) {
// Add hidden empty option to indicate clearing all scenarios
const emptyOption = document.createElement('option');
emptyOption.value = '';
emptyOption.selected = true;
select.appendChild(emptyOption);
}
document.getElementById('set_scenario_modal_form').submit();
}
}"
@close="reset()"
x-init="$watch('$el.open', value => { if (value) loadScenarios(); })"
>
<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|safe }}
</h4>
<div class="modal-box max-w-4xl">
<!-- Header -->
<h3 class="text-lg font-bold">
Set Scenarios for {{ current_object.name|safe }}
</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<!-- Loading indicator -->
<div x-show="isLoading" x-cloak class="mb-4 text-center">
<span class="loading loading-spinner loading-lg"></span>
<div class="alert alert-info mt-2">Loading Scenarios...</div>
</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>
<form
id="set_scenario_modal_form"
accept-charset="UTF-8"
action="{{ current_object.url }}"
data-remote="true"
method="post"
x-show="!isLoading"
>
{% csrf_token %}
<div class="form-control">
<label class="label" for="scenario-select">
<span class="label-text">Scenarios</span>
</label>
<select
id="scenario-select"
name="selected-scenarios"
data-actions-box="true"
class="form-control"
class="select select-bordered h-48 w-full"
multiple
data-width="100%"
>
<option disabled>Select Scenarios</option>
<option value="" hidden></option>
<template x-for="scenario in scenarios" :key="scenario.url">
<option
:value="scenario.url"
:selected="isSelected(scenario.url)"
x-text="scenario.name"
></option>
</template>
</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>
<label class="label">
<span class="label-text-alt"
>Hold Ctrl/Cmd to select multiple scenarios</span
>
</label>
</div>
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit()"
:disabled="isSubmitting || isLoading"
>
<span x-show="!isSubmitting">Submit</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Submitting...</span>
</button>
</div>
</div>
</div>
{# prettier-ignore-start #}
<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();
if ($('#scenario-select').val().length == 0) {
$('#scenario-select').val("")
}
$('#set_scenario_modal_form').submit();
});
});
});
</script>
{# prettier-ignore-end #}
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,75 +1,89 @@
{% load static %}
<!-- Identify Missing Rules -->
<div id="identify_missing_rules_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Identify Missing Rules</h3>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<dialog
id="identify_missing_rules_modal"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="text-lg font-bold">Identify Missing Rules</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<p class="mb-4">
By clicking on Download we'll search the Pathway for Reactions that are
not backed by a Rule or which can be assembled by chaining two rules.
<form
id="identify-missing-rules-modal-form"
accept-charset="UTF-8"
action="{{ pathway.url }}"
data-remote="true"
method="GET"
>
<label for="rule-package">Select the Rule Package</label>
</p>
<form
id="identify-missing-rules-modal-form"
accept-charset="UTF-8"
action="{{ pathway.url }}"
method="GET"
>
<div class="form-control">
<label class="label" for="rule-package">
<span class="label-text">Select the Rule Package</span>
</label>
<select
id="rule-package"
name="rule-package"
data-actions-box="true"
class="form-control"
data-width="100%"
class="select select-bordered w-full"
required
>
<option disabled>Reviewed Packages</option>
{% for obj in meta.readable_packages %}
{% if obj.reviewed %}
<option value="{{ obj.url }}">{{ obj.name }}</option>
{% endif %}
{% endfor %}
<option disabled>Unreviewed Packages</option>
{% for obj in meta.readable_packages %}
{% if not obj.reviewed %}
<option value="{{ obj.url }}">{{ obj.name }}</option>
{% endif %}
{% endfor %}
<optgroup label="Reviewed Packages">
{% for obj in meta.readable_packages %}
{% if obj.reviewed %}
<option value="{{ obj.url }}">{{ obj.name }}</option>
{% endif %}
{% endfor %}
</optgroup>
<optgroup label="Unreviewed Packages">
{% for obj in meta.readable_packages %}
{% if not obj.reviewed %}
<option value="{{ obj.url }}">{{ obj.name }}</option>
{% endif %}
{% endfor %}
</optgroup>
</select>
<input type="hidden" name="identify-missing-rules" value="true" />
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button
type="button"
class="btn btn-primary"
id="identify-missing-rules-modal-submit"
>
Download
</button>
</div>
</div>
<input type="hidden" name="identify-missing-rules" value="true" />
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="document.getElementById('identify-missing-rules-modal-form').submit(); $el.closest('dialog').close()"
>
Download
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#identify-missing-rules-modal-submit").click(function (e) {
e.preventDefault();
$("#identify-missing-rules-modal-form").submit();
$("#identify_missing_rules_modal").modal("hide");
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,118 +1,174 @@
<div
class="modal fade"
tabindex="-1"
<dialog
id="manage_api_token_modal"
role="dialog"
aria-labelledby="manage_api_token_modal"
aria-hidden="true"
class="modal"
x-data="{
isSubmitting: false,
newToken: '',
tokens: [],
reset() {
this.isSubmitting = false;
this.newToken = '';
},
async requestToken() {
this.isSubmitting = true;
const form = document.getElementById('request_api_token_form');
const formData = new FormData(form);
try {
const response = await fetch('{{ meta.user.url }}', {
method: 'POST',
body: new URLSearchParams(formData)
});
const data = await response.json();
this.newToken = data.raw_token;
} catch (error) {
console.error('Error requesting token:', error);
} finally {
this.isSubmitting = false;
}
},
async deleteToken(form) {
const formData = new FormData(form);
try {
await fetch(form.action, {
method: 'POST',
body: new URLSearchParams(formData)
});
form.closest('.token-item').remove();
} catch (error) {
console.error('Error deleting token:', error);
}
}
}"
@close="reset()"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
<h3 class="modal-title">Manage API Token</h3>
</div>
<div class="modal-body">
<p>
Requesting a Token will invalidate all potential existing tokens. The
new token will only be shown once, so ensure to store it in secure
place
</p>
<form
id="request_api_token_form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<p>
<label for="token-name">Name</label>
<input
type="text"
id="token-name"
class="form-control"
name="name"
placeholder="Generic Token for {{ meta.user.username }}"
/>
<label for="valid-for">Valid for (in days)</label>
<input
type="number"
id="valid-for"
class="form-control"
name="valid-for"
value="90"
/>
<input type="hidden" name="hidden" value="request-api-token" />
</p>
</form>
<div id="new-token">
<pre id="new-token-pre"></pre>
<div class="modal-box">
<!-- Header -->
<h3 class="text-lg font-bold">Manage API Token</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<p class="mb-4">
Requesting a Token will invalidate all potential existing tokens. The
new token will only be shown once, so ensure to store it in a secure
place.
</p>
<form id="request_api_token_form" accept-charset="UTF-8" method="post">
{% csrf_token %}
<div class="form-control mb-3">
<label class="label" for="token-name">
<span class="label-text">Name</span>
</label>
<input
type="text"
id="token-name"
class="input input-bordered w-full"
name="name"
placeholder="Generic Token for {{ meta.user.username }}"
/>
</div>
<div id="existing-tokens">
{% for t in tokens %}
<div class="form-control mb-3">
<label class="label" for="valid-for">
<span class="label-text">Valid for (in days)</span>
</label>
<input
type="number"
id="valid-for"
class="input input-bordered w-full"
name="valid-for"
value="90"
/>
</div>
<input type="hidden" name="hidden" value="request-api-token" />
</form>
<!-- New Token Display -->
<div x-show="newToken" x-cloak class="alert alert-success mb-4">
<div class="w-full">
<span class="font-bold">New Token:</span>
<pre
class="mt-2 p-2 bg-base-200 rounded overflow-x-auto"
x-text="newToken"
></pre>
</div>
</div>
<!-- Existing Tokens -->
<div class="space-y-2">
{% for t in tokens %}
<div class="token-item">
<form
id="delete-token-{{ forloop.counter0 }}"
accept-charset="UTF-8"
action="{{ meta.user.url }}"
data-remote="true"
method="post"
>
{% csrf_token %}
<div class="input-group">
<div class="join w-full">
<input type="hidden" name="hidden" value="delete" />
<input type="hidden" name="token-id" value="{{ t.pk }}" />
<input
type="text"
class="form-control"
class="input input-bordered join-item w-full"
value="{{ t.name|safe }}"
disabled
/>
<span class="input-group-btn">
<button type="submit" class="btn btn-danger">Delete</button>
</span>
<button
type="button"
class="btn btn-error join-item"
@click="deleteToken($el.closest('form'))"
>
Delete
</button>
</div>
</form>
{% endfor %}
</div>
</div>
<div class="modal-footer">
<a id="manage_api_token_form_submit" class="btn btn-primary" href="#"
>Submit</a
>
<button type="button" class="btn btn-default" data-dismiss="modal">
Cancel
</button>
</div>
{% endfor %}
</div>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Cancel
</button>
<button
type="button"
class="btn btn-primary"
@click="requestToken()"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Request Token</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Requesting...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#new-token").hide();
$("#manage_api_token_form_submit").on("click", function (e) {
e.preventDefault();
const formData = $("#request_api_token_form").serialize();
$.post("", formData, function (response) {
$("#new-token-pre").text(response.raw_token);
$("#new-token").show();
});
});
// Select all elements that start with 'delete-token-'
$("[id^='delete-token-']").on("submit", function (e) {
e.preventDefault();
const formData = $(this).serialize();
const form = $(this);
$.post(this.action, formData, function (response) {
console.log(this);
form.remove();
});
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,52 +1,85 @@
{% load static %}
<!-- Publish a Package -->
<div id="publish_package_modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Publish Package</h5>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
<dialog
id="publish_package_modal"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-box">
<!-- Header -->
<h3 class="font-bold text-lg">Publish Package</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<div class="alert alert-info">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
class="h-6 w-6 shrink-0 stroke-current"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>Clicking on Publish will make this Package publicly available!</p>
<form
id="publish-package-modal-form"
accept-charset="UTF-8"
action="{{ current_package.url }}"
data-remote="true"
method="post"
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
></path>
</svg>
<span
>Clicking on Publish will make this Package publicly available!</span
>
{% csrf_token %}
<input type="hidden" name="hidden" value="publish-package" />
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button
type="button"
class="btn btn-primary"
id="publish-package-modal-form-submit"
>
Publish
</button>
</div>
<form
id="publish-package-modal-form"
accept-charset="UTF-8"
action="{{ current_package.url }}"
method="post"
>
{% csrf_token %}
<input type="hidden" name="hidden" value="publish-package" />
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('publish-package-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Publish</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Publishing...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#publish-package-modal-form-submit").click(function (e) {
e.preventDefault();
$("#publish-package-modal-form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,55 +1,72 @@
<div
class="modal fade"
tabindex="-1"
<dialog
id="retrain_model_modal"
role="dialog"
aria-labelledby="retrain_model_modal"
aria-hidden="true"
class="modal"
x-data="modalForm()"
@close="reset()"
>
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
<h4 class="modal-title">Retrain Model</h4>
</div>
<div class="modal-body">
<form
id="retrain_model_form"
accept-charset="UTF-8"
action="{{ meta.current_object.url }}"
data-remote="true"
method="post"
>
<div class="jumbotron">
<div class="modal-box max-w-3xl">
<!-- Header -->
<h3 class="text-lg font-bold">Retrain Model</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="retrain_model_form"
accept-charset="UTF-8"
action="{{ meta.current_object.url }}"
method="post"
>
{% csrf_token %}
<div class="alert alert-info mb-4">
<span>
To reflect changes in the rule or data packages, you can use the
"Retrain" button, to let the model reflect the changes without
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>
</span>
</div>
<input type="hidden" name="hidden" value="retrain" />
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Cancel
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('retrain_model_form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Retrain</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Retraining...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#retrain_model_form_submit").on("click", function (e) {
e.preventDefault();
$("#retrain_model_form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,198 +1,193 @@
<div
class="modal fade"
tabindex="-1"
<dialog
id="set_license_modal"
role="dialog"
aria-labelledby="set_license_modal"
aria-hidden="true"
class="modal"
x-data="{
isSubmitting: false,
licenseType: '',
ccRemix: false,
ccNoCom: false,
ccAlike: false,
reset() {
this.isSubmitting = false;
this.licenseType = '';
this.ccRemix = false;
this.ccNoCom = false;
this.ccAlike = false;
},
get licenseString() {
if (this.licenseType !== 'cc') return 'no-license';
let str = 'by';
if (this.ccNoCom) str += '-nc';
if (!this.ccRemix) {
str += '-nd';
} else if (this.ccAlike) {
str += '-sa';
}
return str;
},
get licenseUrl() {
if (this.licenseType !== 'cc') return '';
return 'https://creativecommons.org/licenses/' + this.licenseString + '/4.0/';
},
get licenseImageUrl() {
if (this.licenseType !== 'cc') return '';
return 'https://licensebuttons.net/l/' + this.licenseString + '/4.0/88x31.png';
},
submit(formId) {
const form = document.getElementById(formId);
document.getElementById('license').value = this.licenseString;
if (form && form.checkValidity()) {
this.isSubmitting = true;
form.submit();
} else if (form) {
form.reportValidity();
}
}
}"
@close="reset()"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
<h3 class="modal-title">Set License</h3>
<div class="modal-box">
<!-- Header -->
<h3 class="text-lg font-bold">Set License</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<div class="flex gap-4 mb-4">
<div class="flex-1">
<p>
Set the license for all data in this package:
<br /><br />
(For more information please visit our
<a
target="_blank"
href="https://wiki.envipath.org/index.php/License"
class="link link-primary"
>wiki</a
>.)
</p>
</div>
<div class="flex-none" x-show="licenseType === 'cc'" x-cloak>
<a :href="licenseUrl" target="_blank">
<img :src="licenseImageUrl" alt="CC License" />
</a>
</div>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-9">
<p>
Set the license for all data in this package:
<br />
<br />
(For more information please visit our
<a target="#" href="https://wiki.envipath.org/index.php/License"
>wiki</a
>.)
</p>
<form
id="set_license_form"
accept-charset="UTF-8"
action=""
method="post"
>
{% csrf_token %}
<div class="form-control">
<label class="label cursor-pointer justify-start gap-3">
<input
type="radio"
name="optlicense"
class="radio"
value="cc"
x-model="licenseType"
/>
<span class="label-text">Creative commons license</span>
</label>
</div>
<div class="ml-8 space-y-2" x-show="licenseType === 'cc'" x-cloak>
<div class="form-control">
<label class="label cursor-pointer justify-start gap-3">
<input type="checkbox" class="checkbox" x-model="ccRemix" />
<span class="label-text"
>Allow adaptations of your work to be shared</span
>
</label>
</div>
<div class="col-md-3">
<div id="ccfig"></div>
<div class="form-control">
<label class="label cursor-pointer justify-start gap-3">
<input type="checkbox" class="checkbox" x-model="ccNoCom" />
<span class="label-text">Prohibit commercial use</span>
</label>
</div>
<div class="form-control">
<label class="label cursor-pointer justify-start gap-3">
<input
type="checkbox"
class="checkbox"
x-model="ccAlike"
:disabled="!ccRemix"
/>
<span class="label-text">Share only if others share alike</span>
</label>
</div>
</div>
<form
id="set_license_form"
accept-charset="UTF-8"
action=""
data-remote="true"
method="post"
>
{% csrf_token %}
<div class="input-group">
<div class="radio">
<label
><input
type="radio"
name="optlicense"
onclick="cc()"
id="ccradio"
/>Creative commons license</label
>
</div>
<div>
<div class="checkbox">
<label
><input
type="checkbox"
value=""
onclick="cc()"
id="ccremix"
disabled
/>Allow adaptations of your work to be shared</label
>
</div>
<div class="checkbox">
<label
><input
type="checkbox"
value=""
onclick="cc()"
id="ccnocom"
disabled
/>Prohibit commercial use</label
>
</div>
<div class="checkbox">
<label
><input
type="checkbox"
value=""
onclick="cc()"
id="ccalike"
disabled
/>Share only if others share alike</label
>
</div>
</div>
<div class="radio">
<label
><input
type="radio"
name="optlicense"
onclick="cc()"
id="noccradio"
/>No creative commons license, contact package owner for license
information</label
>
</div>
</div>
<input type="hidden" id="license" name="license" />
</form>
</div>
<div class="modal-footer">
<button id="set_license_form_submit" class="btn btn-primary">
Submit
</button>
<button type="button" class="btn btn-default" data-dismiss="modal">
Cancel
</button>
</div>
<div class="form-control mt-2">
<label class="label cursor-pointer justify-start gap-3">
<input
type="radio"
name="optlicense"
class="radio"
value="nocc"
x-model="licenseType"
/>
<span class="label-text"
>No creative commons license, contact package owner for license
information</span
>
</label>
</div>
<input type="hidden" id="license" name="license" />
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Cancel
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('set_license_form')"
:disabled="isSubmitting || !licenseType"
>
<span x-show="!isSubmitting">Submit</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Submitting...</span>
</button>
</div>
</div>
</div>
<script>
function ccstring(ccremix, ccnocom, ccalike) {
var ccstring = "by";
if (ccnocom) {
ccstring += "-nc";
}
if (!ccremix) {
ccstring += "-nd";
} else {
if (ccalike) {
ccstring += "-sa";
}
}
return ccstring;
}
function cc() {
var nocc = $("#noccradio").prop("checked");
var iscc = $("#ccradio").prop("checked");
if (nocc) {
$("#ccradio").prop("checked", false);
$("#ccremix").prop("checked", false);
$("#ccnocom").prop("checked", false);
$("#ccalike").prop("checked", false);
$("#ccremix").prop("disabled", true);
$("#ccnocom").prop("disabled", true);
$("#ccalike").prop("disabled", true);
} else if (iscc) {
$("#ccremix").prop("disabled", false);
$("#ccnocom").prop("disabled", false);
if ($("#ccremix").prop("checked")) {
$("#ccalike").prop("disabled", false);
} else {
$("#ccalike").prop("disabled", true);
}
}
var remix = $("#ccremix").prop("checked");
var nocom = $("#ccnocom").prop("checked");
var alike = $("#ccalike").prop("checked");
if (nocc) {
$("#set_license_form_submit").prop("disabled", false);
$("#ccfig").empty();
$("#license").val("no-license");
} else if (iscc) {
$("#set_license_form_submit").prop("disabled", false);
$("#ccfig").empty();
var ccstr = ccstring(remix, nocom, alike);
var link = `https://creativecommons.org/licenses/${ccstr}/4.0/`;
var imageLink = `https://licensebuttons.net/l/${ccstr}/4.0/88x31.png`;
var img_tpl = `<a href='${link}' target="_blank">
<img src='${imageLink}'>
</a>`;
$("#ccfig").append(img_tpl);
$("#license").val(ccstr);
} else {
$("#ccfig").empty();
$("#set_license_form_submit").prop("disabled", true);
$("#license").val("no-license");
}
}
$(function () {
// Disable by default as nothing is selected
cc();
$("#set_license_form_submit").prop("disabled", true);
$("#set_license_form_submit").on("click", function (e) {
e.preventDefault();
$("#set_license_form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,62 +1,69 @@
{% load static %}
<!-- Update Scenario Additional Information-->
<div
<dialog
id="update_scenario_additional_information_modal"
class="modal"
tabindex="-1"
x-data="modalForm()"
@close="reset()"
>
<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">&times;</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 class="modal-box">
<!-- Header -->
<h3 class="text-lg font-bold">Update Additional Information</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<form
id="edit-scenario-additional-information-modal-form"
accept-charset="UTF-8"
action=""
method="post"
>
{% csrf_token %}
{% for widget in update_widgets %}
{{ widget|safe }}
{% endfor %}
<input type="hidden" name="hidden" value="set-additional-information" />
</form>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit('edit-scenario-additional-information-modal-form')"
:disabled="isSubmitting"
>
<span x-show="!isSubmitting">Update</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Updating...</span>
</button>
</div>
</div>
</div>
<script>
$(function () {
$("#edit-scenario-additional-information-modal-submit").click(function (e) {
e.preventDefault();
$("#edit-scenario-additional-information-modal-form").submit();
});
});
</script>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>

View File

@ -1,178 +0,0 @@
{% load static %}
<div
class="modal fade bs-modal-lg"
id="predict_modal"
tabindex="-1"
aria-labelledby="predict_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">Predict a Pathway</h4>
</div>
<div class="modal-body">
<form
id="predict_modal_form"
accept-charset="UTF-8"
action="{{ meta.current_package.url }}/pathway"
data-remote="true"
method="post"
>
{% csrf_token %}
<div class="row">
<div class="col-md-6">
<label for="name">Name</label>
<input
id="name"
class="form-control"
name="name"
placeholder="Name"
/>
<label for="description">Description</label>
<input
id="description"
class="form-control"
name="description"
placeholder="Description"
/>
</div>
<div class="col-md-6">
<label for="predict">Predict pathway or build yourself?</label>
<div class="radio" id="predict">
<p>
<label>
<input
type="radio"
name="predict"
id="radioPredict"
value="predict"
checked
/>Predict pathway
</label>
</p>
<p>
<label>
<input
type="radio"
name="predict"
id="radioIncremental"
value="incremental"
/>Incremental prediction
</label>
</p>
<p>
<label>
<input
type="radio"
name="predict"
id="radioBuild"
value="build"
/>Build pathway
</label>
</p>
</div>
</div>
</div>
<label for="predict-modal-smiles">SMILES</label>
<input
type="text"
class="form-control"
name="smiles"
placeholder="SMILES"
id="predict-modal-smiles"
/>
<p id="ketcher_container"></p>
<div>
<iframe
id="predict-modal-ketcher"
src="{% static '/js/ketcher2/ketcher.html' %}"
width="100%"
height="510"
></iframe>
</div>
<label for="prediction-setting">Default Prediction Setting</label>
<select
id="prediction-setting"
name="prediction-setting"
class="form-control"
data-width="100%"
>
<option disabled>Select a Setting</option>
{% for s in meta.available_settings %}
<option
value="{{ s.url }}"
{% if s.id == meta.user.default_setting.id %}selected{% endif %}
>
{{ s.name|safe }}{% if s.id == meta.user.default_setting.id %}
<i>(User default)</i>
{% endif %}
</option>
{% endfor %}
</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="predictButton">
Predict
</button>
</div>
</div>
</div>
</div>
<script>
function predictModalketcherToPredictModalTextInput() {
$("#predict-modal-smiles").val(this.ketcher.getSmiles());
}
$(function () {
$("#predict-modal-ketcher").on("load", function () {
const checkKetcherReady = () => {
win = this.contentWindow;
if (win.ketcher && "editor" in win.ketcher) {
win.ketcher.editor.event.change.handlers.push({
once: false,
priority: 0,
f: predictModalketcherToPredictModalTextInput,
ketcher: win.ketcher,
});
} else {
setTimeout(checkKetcherReady, 100);
}
};
checkKetcherReady();
});
$("#predictButton").on("click", function (e) {
e.preventDefault();
$(this).prop("disabled", true);
// loading button
// validation
// existing pws...
// submit form
$("#predict_modal_form").submit();
});
});
</script>

View File

@ -1,5 +1,10 @@
{% load static %}
<dialog id="search_modal" class="modal @max-sm:modal-top justify-center">
<dialog
id="search_modal"
class="modal @max-sm:modal-top justify-center"
x-data="searchModal()"
@close="reset()"
>
<div class="modal-box h-full w-lvw p-1 sm:h-8/12 sm:w-[85vw] sm:max-w-5xl">
<!-- Search Input and Mode Selector -->
<div class="form-control mb-4 w-full shrink-0">
@ -23,7 +28,9 @@
<input
type="text"
autofocus
id="modal_searchbar"
x-ref="searchbar"
x-model="query"
@keydown.enter="performSearch('{{ SERVER_BASE }}')"
placeholder="Benfuracarb"
class="grow"
aria-label="Search"
@ -35,12 +42,11 @@
<button
type="button"
tabindex="0"
id="modal_mode_button"
popovertarget="search_dropdown_menu"
style="anchor-name:--1"
style="anchor-name: --1"
class="btn join-item btn-ghost"
>
Text
<span x-text="searchModeLabel"></span>
<svg
class="ml-1 h-4 w-4"
fill="none"
@ -59,13 +65,14 @@
tabindex="0"
class="dropdown dropdown-end menu bg-base-200 rounded-box w-64 p-2 shadow-lg"
popover
x-ref="modeDropdown"
id="search_dropdown_menu"
style="position-anchor:--anchor-2"
style="position-anchor: --anchor-2"
>
<li class="menu-title">Text</li>
<li>
<a
id="modal_dropdown_text"
@click.prevent="setSearchMode('text', 'Text')"
class="tooltip tooltip-left"
data-tip="Search on object names and descriptions"
>
@ -75,7 +82,7 @@
<li class="menu-title">SMILES</li>
<li>
<a
id="modal_dropdown_smiles_default"
@click.prevent="setSearchMode('smiles_default', 'Default')"
class="tooltip tooltip-left"
data-tip="Ignores stereochemistry and charge"
>
@ -84,7 +91,7 @@
</li>
<li>
<a
id="modal_dropdown_smiles_canonical"
@click.prevent="setSearchMode('smiles_canonical', 'Canonical')"
class="tooltip tooltip-left"
data-tip="Ignores stereochemistry, preserves charge"
>
@ -93,7 +100,7 @@
</li>
<li>
<a
id="modal_dropdown_smiles_exact"
@click.prevent="setSearchMode('smiles_exact', 'Exact')"
class="tooltip tooltip-left"
data-tip="Exact match for stereochemistry and charge"
>
@ -103,7 +110,7 @@
<li class="menu-title">InChI</li>
<li>
<a
id="modal_dropdown_inchikey"
@click.prevent="setSearchMode('inchikey', 'InChIKey')"
class="tooltip tooltip-left"
data-tip="Search by InChIKey"
>
@ -115,7 +122,7 @@
<button
type="button"
id="modal_search_button"
@click="performSearch('{{ SERVER_BASE }}')"
class="btn btn-xs btn-ghost join-item"
>
<kbd class="kbd kbd-sm text-base-content/50 p-1">
@ -143,18 +150,62 @@
<div class="form-control mb-4 shrink-0">
<!-- Pills Container -->
<div
id="modal_package_pills_container"
class="border-base-300 m-3 flex min-h-12 flex-wrap items-center gap-2 rounded-lg border-2 border-dashed p-3"
>
<!-- Pills will be added here dynamically -->
<!-- Dynamic Pills -->
<template x-for="pkg in selectedPackages" :key="pkg.url">
<span class="badge badge-outline gap-2 max-w-xs">
<span class="truncate" :title="pkg.name" x-text="pkg.name"></span>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4 cursor-pointer hover:text-error shrink-0 rotate-45"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
@click="removePackage(pkg.url)"
>
<path d="M5 12h14" />
<path d="M12 5v14" />
</svg>
</span>
</template>
<!-- Add Package Button -->
<button
type="button"
popovertarget="package_dropdown_menu"
style="anchor-name: --anchor-packages"
class="btn btn-sm btn-ghost gap-2 text-base-content/50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-plus-icon lucide-plus"
>
<path d="M5 12h14" />
<path d="M12 5v14" />
</svg>
Add Package
</button>
</div>
<!-- Package Dropdown Menu -->
<ul
class="dropdown dropdown-center menu bg-base-200 rounded-box max-h-96 w-80 overflow-y-auto p-2 shadow-lg"
popover
x-ref="packageDropdown"
id="package_dropdown_menu"
style="position-anchor:--anchor-packages"
style="position-anchor: --anchor-packages"
>
{% if unreviewed_packages %}
<li class="menu-title">Reviewed Packages</li>
@ -164,11 +215,13 @@
class="package-option flex items-center justify-between"
data-package-url="{{ obj.url }}"
data-package-name="{{ obj.name }}"
@click.prevent.stop="togglePackage('{{ obj.url }}', '{{ obj.name }}')"
>
<span>{{ obj.name }}</span>
<svg
xmlns="http://www.w3.org/2000/svg"
class="package-checkmark hidden h-4 w-4"
class="h-4 w-4"
:class="isPackageSelected('{{ obj.url }}') ? '' : 'hidden'"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
@ -190,11 +243,13 @@
class="package-option flex items-center justify-between"
data-package-url="{{ obj.url }}"
data-package-name="{{ obj.name }}"
@click.prevent.stop="togglePackage('{{ obj.url }}', '{{ obj.name }}')"
>
<span>{{ obj.name }}</span>
<svg
xmlns="http://www.w3.org/2000/svg"
class="package-checkmark hidden h-4 w-4"
class="h-4 w-4"
:class="isPackageSelected('{{ obj.url }}') ? '' : 'hidden'"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
@ -217,11 +272,13 @@
class="package-option flex items-center justify-between"
data-package-url="{{ obj.url }}"
data-package-name="{{ obj.name }}"
@click.prevent.stop="togglePackage('{{ obj.url }}', '{{ obj.name }}')"
>
<span>{{ obj.name }}</span>
<svg
xmlns="http://www.w3.org/2000/svg"
class="package-checkmark hidden h-4 w-4"
class="h-4 w-4"
:class="isPackageSelected('{{ obj.url }}') ? '' : 'hidden'"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
@ -241,12 +298,201 @@
</div>
<!-- Loading Indicator -->
<div id="search_loading" class="hidden shrink-0 justify-center py-8">
<div x-show="isSearching" class="flex shrink-0 justify-center py-8">
<span class="loading loading-spinner loading-lg text-primary"></span>
</div>
<!-- Results Container - scrollable -->
<div id="search_results" class="min-h-0 flex-1 overflow-y-auto p-2"></div>
<!-- Results Container -->
<div class="min-h-0 flex-1 overflow-y-auto p-2">
<!-- No packages selected error -->
<template x-if="results && results.error === 'no_packages'">
<div class="alert alert-info">
<svg
class="w-6 h-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<span>Please select at least one package</span>
</div>
</template>
<!-- Search error -->
<template x-if="error">
<div class="alert alert-error">
<svg
class="w-6 h-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<span x-text="error"></span>
</div>
</template>
<!-- No results -->
<template x-if="results && !results.error && !hasResults()">
<div class="alert alert-warning">
<svg
class="w-6 h-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<span>No results found</span>
</div>
</template>
<!-- Results display -->
<template x-if="results && !results.error && hasResults()">
<div class="mb-2">
<div class="text-sm font-semibold text-base-content/70 mb-2">
Results
</div>
<!-- Compounds -->
<template x-if="results.Compounds && results.Compounds.length > 0">
<div class="collapse collapse-arrow bg-base-200 mb-2">
<input type="checkbox" checked />
<div class="collapse-title font-medium">
Compounds
<span
class="badge badge-neutral badge-sm ml-2"
x-text="results.Compounds.length"
></span>
</div>
<div class="collapse-content">
<template x-for="item in results.Compounds" :key="item.url">
<a
:href="item.url"
class="block px-4 py-2 hover:bg-base-300 rounded-lg transition-colors"
x-text="item.name"
></a>
</template>
</div>
</div>
</template>
<!-- Compound Structures -->
<template
x-if="results['Compound Structures'] && results['Compound Structures'].length > 0"
>
<div class="collapse collapse-arrow bg-base-200 mb-2">
<input type="checkbox" checked />
<div class="collapse-title font-medium">
Compound Structures
<span
class="badge badge-neutral badge-sm ml-2"
x-text="results['Compound Structures'].length"
></span>
</div>
<div class="collapse-content">
<template
x-for="item in results['Compound Structures']"
:key="item.url"
>
<a
:href="item.url"
class="block px-4 py-2 hover:bg-base-300 rounded-lg transition-colors"
x-text="item.name"
></a>
</template>
</div>
</div>
</template>
<!-- Rules -->
<template x-if="results.Rules && results.Rules.length > 0">
<div class="collapse collapse-arrow bg-base-200 mb-2">
<input type="checkbox" checked />
<div class="collapse-title font-medium">
Rules
<span
class="badge badge-neutral badge-sm ml-2"
x-text="results.Rules.length"
></span>
</div>
<div class="collapse-content">
<template x-for="item in results.Rules" :key="item.url">
<a
:href="item.url"
class="block px-4 py-2 hover:bg-base-300 rounded-lg transition-colors"
x-text="item.name"
></a>
</template>
</div>
</div>
</template>
<!-- Reactions -->
<template x-if="results.Reactions && results.Reactions.length > 0">
<div class="collapse collapse-arrow bg-base-200 mb-2">
<input type="checkbox" checked />
<div class="collapse-title font-medium">
Reactions
<span
class="badge badge-neutral badge-sm ml-2"
x-text="results.Reactions.length"
></span>
</div>
<div class="collapse-content">
<template x-for="item in results.Reactions" :key="item.url">
<a
:href="item.url"
class="block px-4 py-2 hover:bg-base-300 rounded-lg transition-colors"
x-text="item.name"
></a>
</template>
</div>
</div>
</template>
<!-- Pathways -->
<template x-if="results.Pathways && results.Pathways.length > 0">
<div class="collapse collapse-arrow bg-base-200 mb-2">
<input type="checkbox" checked />
<div class="collapse-title font-medium">
Pathways
<span
class="badge badge-neutral badge-sm ml-2"
x-text="results.Pathways.length"
></span>
</div>
<div class="collapse-content">
<template x-for="item in results.Pathways" :key="item.url">
<a
:href="item.url"
class="block px-4 py-2 hover:bg-base-300 rounded-lg transition-colors"
x-text="item.name"
></a>
</template>
</div>
</div>
</template>
</div>
</template>
</div>
</div>
<!-- Backdrop to close -->
@ -254,433 +500,3 @@
<button>close</button>
</form>
</dialog>
<script>
(function () {
// Package Selector Module - Data-driven multiselect package selection
const PackageSelector = {
// Single source of truth: array of selected packages
selectedPackages: [],
elements: {
pillsContainer: null,
packageDropdown: null,
packageOptions: null,
},
init() {
this.cacheElements();
this.loadInitialSelection();
this.attachEventListeners();
this.render();
},
cacheElements() {
this.elements.pillsContainer = document.getElementById(
"modal_package_pills_container",
);
this.elements.packageDropdown = document.getElementById(
"package_dropdown_menu",
);
this.elements.packageOptions =
document.querySelectorAll(".package-option");
},
loadInitialSelection() {
// Load pre-selected packages from server-rendered pills
const existingPills =
this.elements.pillsContainer.querySelectorAll(".badge");
existingPills.forEach((pill) => {
this.selectedPackages.push({
url: pill.dataset.packageUrl,
name: pill.dataset.packageName,
});
});
// If no pills found, select all reviewed packages by default
if (this.selectedPackages.length === 0) {
// Iterate through all menu items and collect reviewed packages
const menuItems =
this.elements.packageDropdown.querySelectorAll("li");
for (const item of menuItems) {
// Check if this is the "Unreviewed Packages" menu title
if (
item.classList.contains("menu-title") &&
item.textContent.trim() === "Unreviewed Packages"
) {
break; // Stop processing after this point
}
// Check for package options (only reviewed packages reach here)
const packageOption = item.querySelector(".package-option");
if (packageOption) {
this.selectedPackages.push({
url: packageOption.dataset.packageUrl,
name: packageOption.dataset.packageName,
});
}
}
}
},
attachEventListeners() {
// Toggle package selection on dropdown item click
this.elements.packageOptions.forEach((option) => {
option.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation(); // Prevent dropdown from closing
const packageUrl = option.dataset.packageUrl;
const packageName = option.dataset.packageName;
this.togglePackageSelection(packageUrl, packageName);
});
});
// Remove package when X is clicked (using event delegation)
this.elements.pillsContainer.addEventListener("click", (e) => {
if (
e.target.classList.contains("package-remove-btn") ||
e.target.closest(".package-remove-btn")
) {
const pill = e.target.closest(".badge");
if (pill) {
const packageUrl = pill.dataset.packageUrl;
this.removePackage(packageUrl);
}
}
});
},
togglePackageSelection(packageUrl, packageName) {
const index = this.selectedPackages.findIndex(
(pkg) => pkg.url === packageUrl,
);
if (index !== -1) {
// Remove from selection
this.selectedPackages.splice(index, 1);
} else {
// Add to selection
this.selectedPackages.push({ url: packageUrl, name: packageName });
}
this.render();
},
removePackage(packageUrl) {
const index = this.selectedPackages.findIndex(
(pkg) => pkg.url === packageUrl,
);
if (index !== -1) {
this.selectedPackages.splice(index, 1);
this.render();
}
},
render() {
this.renderPills();
this.renderAddButton();
this.renderCheckmarks();
},
renderPills() {
// Clear existing pills and button (except placeholder)
const pills = this.elements.pillsContainer.querySelectorAll(".badge");
pills.forEach((pill) => pill.remove());
const existingButton = this.elements.pillsContainer.querySelector(
"#modal_package_add_button",
);
if (existingButton) {
existingButton.remove();
}
// Create pills from data
this.selectedPackages.forEach((pkg) => {
const pill = this.createPillElement(pkg.url, pkg.name);
this.elements.pillsContainer.appendChild(pill);
});
},
renderAddButton() {
// Only render button if there are packages available
if (this.elements.packageOptions.length === 0) {
return;
}
const button = document.createElement("button");
button.type = "button";
button.id = "modal_package_add_button";
button.setAttribute("popovertarget", "package_dropdown_menu");
button.style.cssText = "anchor-name:--anchor-packages";
button.className = "btn btn-sm btn-ghost gap-2 text-base-content/50";
button.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-plus-icon lucide-plus"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
Add Package
`;
this.elements.pillsContainer.appendChild(button);
},
createPillElement(packageUrl, packageName) {
const pill = document.createElement("span");
pill.className = "badge badge-outline gap-2 max-w-xs";
pill.dataset.packageUrl = packageUrl;
pill.dataset.packageName = packageName;
pill.innerHTML = `
<span class="truncate" title="${packageName}">${packageName}</span>
<svg xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4 cursor-pointer hover:text-error package-remove-btn flex-shrink-0 rotate-45"
viewBox="0 0 24 24"
fill="none" stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M5 12h14"/><path d="M12 5v14"/>
</svg>
`;
return pill;
},
renderCheckmarks() {
// Update all checkmarks based on selected packages
this.elements.packageOptions.forEach((option) => {
const packageUrl = option.dataset.packageUrl;
const isSelected = this.selectedPackages.some(
(pkg) => pkg.url === packageUrl,
);
const checkmark = option.querySelector(".package-checkmark");
if (checkmark) {
checkmark.classList.toggle("hidden", !isSelected);
}
});
},
getSelectedPackages() {
return this.selectedPackages.map((pkg) => pkg.url);
},
};
// Modal and Search Management
const modal = document.getElementById("search_modal");
const searchbar = document.getElementById("modal_searchbar");
const searchButton = document.getElementById("modal_search_button");
const modeButton = document.getElementById("modal_mode_button");
const resultsDiv = document.getElementById("search_results");
const loadingDiv = document.getElementById("search_loading");
// MutationObserver to detect when modal opens
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === "open" && modal.open) {
PackageSelector.render();
// Delay focus to allow CSS transitions to complete (modal has 0.3s transition)
setTimeout(() => {
searchbar.focus();
}, 320);
}
});
});
observer.observe(modal, { attributes: true });
// Close modal when clicking outside (on the backdrop)
// According to DaisyUI docs: https://daisyui.com/components/modal/
// The backdrop form with method="dialog" should handle closing automatically when its button is clicked.
// We also handle clicks directly on the dialog element (backdrop area) or the backdrop form.
modal.addEventListener("click", function (event) {
const backdrop = modal.querySelector(".modal-backdrop");
const modalBox = modal.querySelector(".modal-box");
// Close if clicking directly on the dialog element (backdrop area)
// or on the backdrop form (but ensure we're not clicking on modal-box content)
if (
event.target === modal ||
(backdrop &&
(event.target === backdrop || backdrop.contains(event.target)) &&
!modalBox.contains(event.target))
) {
modal.close();
}
});
// Clear results when modal closes
modal.addEventListener("close", function () {
resultsDiv.innerHTML = "";
loadingDiv.classList.add("hidden");
searchbar.value = "";
});
// Mode dropdown handlers
const dropdownMenu = document.getElementById("search_dropdown_menu");
const modeButtons = [
{ id: "modal_dropdown_text", text: "Text" },
{ id: "modal_dropdown_smiles_default", text: "Default" },
{ id: "modal_dropdown_smiles_canonical", text: "Canonical" },
{ id: "modal_dropdown_smiles_exact", text: "Exact" },
{ id: "modal_dropdown_inchikey", text: "InChIKey" },
];
modeButtons.forEach(({ id, text }) => {
document.getElementById(id).addEventListener("click", function (e) {
e.preventDefault();
modeButton.innerHTML =
text +
` <svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg>`;
// Close dropdown using popover API
if (dropdownMenu && typeof dropdownMenu.hidePopover === "function") {
dropdownMenu.hidePopover();
}
});
});
// Initialize Package Selector
PackageSelector.init();
// Search Response Handler
function handleSearchResponse(data) {
resultsDiv.innerHTML = "";
function makeContent(objs) {
let links = "";
objs.forEach((obj) => {
links += `<a href="${obj.url}" class="block px-4 py-2 hover:bg-base-300 rounded-lg transition-colors">${obj.name}</a>`;
});
return links;
}
let allEmpty = true;
let content = "";
// Category order for better UX
const categoryOrder = [
"Compounds",
"Compound Structures",
"Rules",
"Reactions",
"Pathways",
];
categoryOrder.forEach((key) => {
if (!data[key] || data[key].length < 1) {
return;
}
allEmpty = false;
content += `
<div class="collapse collapse-arrow bg-base-200 mb-2">
<input type="checkbox" checked />
<div class="collapse-title font-medium">
${key} <span class="badge badge-neutral badge-sm ml-2">${data[key].length}</span>
</div>
<div class="collapse-content">
${makeContent(data[key])}
</div>
</div>
`;
});
if (allEmpty) {
resultsDiv.innerHTML = `
<div class="alert alert-warning">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
</svg>
<span>No results found</span>
</div>
`;
} else {
resultsDiv.innerHTML = `
<div class="mb-2">
<div class="text-sm font-semibold text-base-content/70 mb-2">Results</div>
${content}
</div>
`;
}
}
// Search Execution
function performSearch(e) {
e.preventDefault();
const query = searchbar.value.trim();
if (!query) {
console.log("Search phrase empty, won't do search");
return;
}
const selPacks = PackageSelector.getSelectedPackages();
if (selPacks.length < 1) {
console.log("No package selected, won't do search");
resultsDiv.innerHTML = `
<div class="alert alert-info">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<span>Please select at least one package</span>
</div>
`;
return;
}
const mode = modeButton.textContent.trim().toLowerCase();
const params = new URLSearchParams();
selPacks.forEach((pack) => params.append("packages", pack));
params.append("search", query);
params.append("mode", mode);
// Show loading
loadingDiv.classList.remove("hidden");
resultsDiv.innerHTML = "";
fetch(`{{ SERVER_BASE }}/search?${params.toString()}`, {
method: "GET",
headers: {
Accept: "application/json",
},
})
.then((response) => {
if (!response.ok) {
throw new Error("Search request failed");
}
return response.json();
})
.then((result) => {
loadingDiv.classList.add("hidden");
handleSearchResponse(result);
})
.catch((error) => {
loadingDiv.classList.add("hidden");
console.error("Search error:", error);
resultsDiv.innerHTML = `
<div class="alert alert-error">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<span>Search failed. Please try again.</span>
</div>
`;
});
}
// Event listeners for search
searchButton.addEventListener("click", performSearch);
searchbar.addEventListener("keydown", function (e) {
if (e.key === "Enter") {
performSearch(e);
}
});
})();
</script>

View File

@ -1,4 +1,4 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% block content %}
@ -10,171 +10,146 @@
{% include "modals/objects/generic_delete_modal.html" %}
{% endblock action_modals %}
<div class="panel-group" id="rule-detail">
<div class="panel panel-default">
<div
class="panel-heading"
id="headingPanel"
style="font-size:2rem;height: 46px"
>
{{ rule.name|safe }}
<div
id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"
>
<a
href="#"
class="dropdown-toggle"
data-toggle="dropdown"
role="button"
aria-haspopup="true"
aria-expanded="false"
><span class="glyphicon glyphicon-wrench"></span> Actions
<span class="caret"></span><span style="padding-right:1em"></span
></a>
<ul id="actionsList" class="dropdown-menu">
{% block actions %}
{% include "actions/objects/rule.html" %}
{% endblock %}
<div class="space-y-2 p-4">
<!-- Header Section -->
<div class="card bg-base-100">
<div class="card-body">
<div class="flex items-center justify-between">
<h2 class="card-title text-2xl">{{ rule.name }}</h2>
<div id="actionsButton" class="dropdown dropdown-end hidden">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-wrench"
>
<path
d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"
/>
</svg>
Actions
</div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2"
>
{% block actions %}
{% include "actions/objects/rule.html" %}
{% endblock %}
</ul>
</div>
</div>
<p class="mt-2">{{ rule.description|safe }}</p>
</div>
</div>
{% if rule.aliases %}
<!-- Aliases -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Aliases</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for alias in rule.aliases %}
<li><a class="hover:bg-base-200">{{ alias }}</a></li>
{% endfor %}
</ul>
</div>
</div>
<div class="panel-body">
<p>{{ rule.description|safe }}</p>
</div>
{% endif %}
{% if rule.aliases %}
<!-- Aliases -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="rule-aliases-link"
data-toggle="collapse"
data-parent="#rule-detail"
href="#rule-aliases"
>Aliases</a
>
</h4>
</div>
<div id="rule-aliases" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{% for alias in rule.aliases %}
<a class="list-group-item">{{ alias }}</a>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Reaction Patterns -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="rule-reaction-patterns-link"
data-toggle="collapse"
data-parent="#rule-detail"
href="#rule-reaction-patterns"
>Reaction Patterns</a
>
</h4>
</div>
<div id="rule-reaction-patterns" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<!-- Reaction Patterns -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Reaction Patterns</div>
<div class="collapse-content">
<div class="space-y-4">
{% for r in rule.srs %}
<a class="list-group-item" href="{{ r.url }}">{{ r.name|safe }}</a>
<div align="center">
<p>{{ r.as_svg|safe }}</p>
<div class="card bg-base-100">
<div class="card-body">
<a href="{{ r.url }}" class="link link-primary font-semibold"
>{{ r.name }}</a
>
<div class="mt-2 flex justify-center">{{ r.as_svg|safe }}</div>
</div>
</div>
{% endfor %}
</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 in collapse">
<div class="panel-body list-group-item">
<!-- Scenarios -->
{% if rule.scenarios.all %}
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Scenarios</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for s in rule.scenarios.all %}
<a class="list-group-item" href="{{ s.url }}"
>{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a
>
<li>
<a href="{{ s.url }}" class="hover:bg-base-200"
>{{ s.name }} <i>({{ s.package.name }})</i></a
>
</li>
{% endfor %}
</div>
</ul>
</div>
{% endif %}
</div>
{% endif %}
{% if rule.enzymelinks %}
<!-- EC Numbers -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="rule-ec-numbers-link"
data-toggle="collapse"
data-parent="#rule-detail"
href="#rule-ec-numbers"
>EC Numbers</a
>
</h4>
</div>
<div id="rule-ec-numbers" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{% if rule.enzymelinks %}
<!-- EC Numbers -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">EC Numbers</div>
<div class="collapse-content">
<div class="space-y-2">
{% for k, v in rule.get_grouped_enzymelinks.items %}
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="{{ k|slugify }}_Link"
data-toggle="collapse"
data-parent="#{{ k|slugify }}_Accordion"
href="#{{ k|slugify }}"
>
{{ k }}
</a>
</h4>
</div>
<div id="{{ k|slugify }}" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{% for enzyme in v %}
<a class="list-group-item" href="{{ enzyme.url }}">
{{ enzyme.ec_number }}
<div style="position:absolute;bottom:10px;left:100px;">
{{ enzyme.name }}
</div>
<div style="float:right;">
{{ enzyme.linking_method }}
</div>
</a>
{% endfor %}
<div class="collapse-arrow bg-base-100 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-lg font-medium">{{ k }}</div>
<div class="collapse-content">
<ul class="menu bg-base-200 rounded-box">
{% for enzyme in v %}
<li>
<a href="{{ enzyme.url }}" class="hover:bg-base-300">
<div class="flex w-full items-center justify-between">
<span>{{ enzyme.ec_number }}</span>
<span class="text-sm opacity-70"
>{{ enzyme.linking_method }}</span
>
</div>
<div class="text-sm opacity-60">
{{ enzyme.name }}
</div>
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
</div>
{% endif %}
</div>
<script>
// Show actions button if there are actions
document.addEventListener("DOMContentLoaded", function () {
const actionsButton = document.getElementById("actionsButton");
const actionsList = actionsButton?.querySelector("ul");
if (actionsList && actionsList.children.length > 0) {
actionsButton?.classList.remove("hidden");
}
});
</script>
{% endblock content %}

View File

@ -1,4 +1,4 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% block content %}
@ -12,360 +12,261 @@
{% include "modals/objects/generic_delete_modal.html" %}
{% endblock action_modals %}
<div class="panel-group" id="compound-detail">
<div class="panel panel-default">
<div
class="panel-heading"
id="headingPanel"
style="font-size:2rem;height: 46px"
>
{{ compound.name|safe }}
<div
id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"
>
<a
href="#"
class="dropdown-toggle"
data-toggle="dropdown"
role="button"
aria-haspopup="true"
aria-expanded="false"
><span class="glyphicon glyphicon-wrench"></span> Actions
<span class="caret"></span><span style="padding-right:1em"></span
></a>
<ul id="actionsList" class="dropdown-menu">
{% block actions %}
{% include "actions/objects/compound.html" %}
{% endblock %}
</ul>
<div class="space-y-2 p-4">
<!-- Header Section -->
<div class="card bg-base-100">
<div class="card-body">
<div class="flex items-center justify-between">
<h2 class="card-title text-2xl">{{ compound.name }}</h2>
<div id="actionsButton" class="dropdown dropdown-end hidden">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-wrench"
>
<path
d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"
/>
</svg>
Actions
</div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2"
>
{% block actions %}
{% include "actions/objects/compound.html" %}
{% endblock %}
</ul>
</div>
</div>
</div>
<div class="panel-body">
<p>
<p class="mt-2">
The structures stored in this compound can be found at
<a target="_blank" href="{{ compound.url }}/structure" role="button"
<a
target="_blank"
href="{{ compound.url }}/structure"
class="link link-primary"
>Compound structures &gt;&gt;</a
>
</p>
</div>
</div>
{% if compound.aliases %}
<!-- Aliases -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="compound-aliases-link"
data-toggle="collapse"
data-parent="#compound-detail"
href="#compound-aliases"
>Aliases</a
>
</h4>
</div>
<div id="compound-aliases" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{% if compound.aliases %}
<!-- Aliases -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Aliases</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for alias in compound.aliases %}
<a class="list-group-item">{{ alias }}</a>
<li><a class="hover:bg-base-200">{{ alias }}</a></li>
{% endfor %}
</div>
</ul>
</div>
{% endif %}
</div>
{% endif %}
<!-- Description -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="compound-desc-link"
data-toggle="collapse"
data-parent="#compound-detail"
href="#compound-desc"
>Description</a
>
</h4>
</div>
<div id="compound-desc" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{{ compound.description|safe }}
</div>
</div>
<!-- Description -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Description</div>
<div class="collapse-content">{{ compound.description }}</div>
</div>
<!-- Image -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="compound-image-link"
data-toggle="collapse"
data-parent="#compound-detail"
href="#compound-image"
>Image Representation</a
>
</h4>
</div>
<div id="compound-image" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<div id="image-div" align="center">
{{ compound.default_structure.as_svg|safe }}
</div>
<!-- Image Representation -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Image Representation</div>
<div class="collapse-content">
<div class="flex justify-center">
{{ compound.default_structure.as_svg|safe }}
</div>
</div>
</div>
<!-- SMILES -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="compound-smiles-link"
data-toggle="collapse"
data-parent="#compound-detail"
href="#compound-smiles"
>SMILES Representation</a
>
</h4>
<!-- SMILES Representation -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
SMILES Representation
</div>
<div id="compound-smiles" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{{ compound.default_structure.smiles }}
</div>
<div class="collapse-content">
{{ compound.default_structure.smiles }}
</div>
</div>
<!-- Canonical SMILES -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="compound-canonical-smiles-link"
data-toggle="collapse"
data-parent="#compound-detail"
href="#compound-canonical-smiles"
>Canonical SMILES Representation</a
>
</h4>
<!-- Canonical SMILES Representation -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
Canonical SMILES Representation
</div>
<div id="compound-canonical-smiles" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{{ compound.default_structure.canonical_smiles }}
</div>
<div class="collapse-content">
{{ compound.default_structure.canonical_smiles }}
</div>
</div>
<!-- InChiKey -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="compound-inchi-link"
data-toggle="collapse"
data-parent="#compound-detail"
href="#compound-inchi"
>InChIKey</a
>
</h4>
</div>
<div id="compound-inchi" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{{ compound.default_structure.inchikey }}
</div>
<!-- InChIKey -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">InChIKey</div>
<div class="collapse-content">
{{ compound.default_structure.inchikey }}
</div>
</div>
<!-- Reactions -->
{% if compound.related_reactions %}
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="compound-reaction-link"
data-toggle="collapse"
data-parent="#compound-detail"
href="#compound-reaction"
>Reactions</a
>
</h4>
</div>
<div id="compound-reaction" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<!-- Reactions -->
{% if compound.related_reactions %}
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Reactions</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for r in compound.related_reactions %}
<a class="list-group-item" href="{{ r.url }}"
>{{ r.name|safe }} <i>({{ r.package.name|safe }})</i></a
>
<li>
<a href="{{ r.url }}" class="hover:bg-base-200"
>{{ r.name }} <i>({{ r.package.name }})</i></a
>
</li>
{% endfor %}
</div>
</ul>
</div>
{% endif %}
</div>
{% endif %}
<!-- Pathways -->
{% if compound.related_pathways %}
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="compound-pathway-link"
data-toggle="collapse"
data-parent="#compound-detail"
href="#compound-pathway"
>Pathways</a
>
</h4>
</div>
<div id="compound-pathway" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<!-- Pathways -->
{% if compound.related_pathways %}
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Pathways</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for r in compound.related_pathways %}
<a class="list-group-item" href="{{ r.url }}"
>{{ r.name|safe }} <i>({{ r.package.name|safe }})</i></a
>
<li>
<a href="{{ r.url }}" class="hover:bg-base-200"
>{{ r.name }} <i>({{ r.package.name }})</i></a
>
</li>
{% endfor %}
</div>
</ul>
</div>
{% endif %}
</div>
{% endif %}
<!-- Scenarios -->
{% if compound.scenarios.all %}
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="compound-scenario-link"
data-toggle="collapse"
data-parent="#compound-detail"
href="#compound-scenario"
>Scenarios</a
>
</h4>
</div>
<div id="compound-scenario" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<!-- Scenarios -->
{% if compound.scenarios.all %}
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Scenarios</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for s in compound.scenarios.all %}
<a class="list-group-item" href="{{ s.url }}"
>{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a
>
<li>
<a href="{{ s.url }}" class="hover:bg-base-200"
>{{ s.name }} <i>({{ s.package.name }})</i></a
>
</li>
{% endfor %}
</div>
</ul>
</div>
{% endif %}
</div>
{% endif %}
<!-- External Identifiers -->
{% if compound.get_external_identifiers %}
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="compound-external-identifier-link"
data-toggle="collapse"
data-parent="#compound-detail"
href="#compound-external-identifier"
>External Identifier</a
>
</h4>
<!-- External Identifiers -->
{% if compound.get_external_identifiers %}
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
External Identifier
</div>
<div
id="compound-external-identifier"
class="panel-collapse in collapse"
>
<div class="panel-body list-group-item">
<div class="collapse-content">
<div class="space-y-2">
{% if compound.get_pubchem_compound_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 in collapse"
>
{% for eid in compound.get_pubchem_compound_identifiers %}
<a class="list-group-item" href="{{ eid.external_url }}"
>CID{{ eid.identifier_value }}</a
>
{% endfor %}
<div class="collapse-arrow bg-base-100 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-lg font-medium">
PubChem Compound Identifier
</div>
<div class="collapse-content">
<ul class="menu bg-base-200 rounded-box">
{% for eid in compound.get_pubchem_compound_identifiers %}
<li>
<a
href="{{ eid.external_url }}"
class="hover:bg-base-300"
>CID{{ eid.identifier_value }}</a
>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
{% if compound.get_pubchem_substance_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 Substance Identifier</a
>
</h4>
</div>
<div
id="compound-pubchem-identifier"
class="panel-collapse in collapse"
>
{% for eid in compound.get_pubchem_substance_identifiers %}
<a class="list-group-item" href="{{ eid.external_url }}"
>SID{{ eid.identifier_value }}</a
>
{% endfor %}
<div class="collapse-arrow bg-base-100 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-lg font-medium">
PubChem Substance Identifier
</div>
<div class="collapse-content">
<ul class="menu bg-base-200 rounded-box">
{% for eid in compound.get_pubchem_substance_identifiers %}
<li>
<a
href="{{ eid.external_url }}"
class="hover:bg-base-300"
>SID{{ eid.identifier_value }}</a
>
</li>
{% endfor %}
</ul>
</div>
</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 in collapse"
>
{% for eid in compound.get_chebi_identifiers %}
<a class="list-group-item" href="{{ eid.external_url }}"
>CHEBI:{{ eid.identifier_value }}</a
>
{% endfor %}
<div class="collapse-arrow bg-base-100 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-lg font-medium">
ChEBI Identifier
</div>
<div class="collapse-content">
<ul class="menu bg-base-200 rounded-box">
{% for eid in compound.get_chebi_identifiers %}
<li>
<a
href="{{ eid.external_url }}"
class="hover:bg-base-300"
>CHEBI:{{ eid.identifier_value }}</a
>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>
</div>
{% endif %}
</div>
</div>
{% endif %}
</div>
<script>
// Show actions button if there are actions
document.addEventListener("DOMContentLoaded", function () {
const actionsButton = document.getElementById("actionsButton");
const actionsList = actionsButton?.querySelector("ul");
if (actionsList && actionsList.children.length > 0) {
actionsButton?.classList.remove("hidden");
}
});
</script>
{% endblock content %}

View File

@ -1,4 +1,4 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% block content %}
@ -10,141 +10,109 @@
{% include "modals/objects/generic_delete_modal.html" %}
{% endblock action_modals %}
<div class="panel-group" id="compound-structure-detail">
<div class="panel panel-default">
<div
class="panel-heading"
id="headingPanel"
style="font-size:2rem;height: 46px"
>
{{ compound_structure.name|safe }}
<div
id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"
>
<a
href="#"
class="dropdown-toggle"
data-toggle="dropdown"
role="button"
aria-haspopup="true"
aria-expanded="false"
><span class="glyphicon glyphicon-wrench"></span> Actions
<span class="caret"></span><span style="padding-right:1em"></span
></a>
<ul id="actionsList" class="dropdown-menu">
{% block actions %}
{% include "actions/objects/compound_structure.html" %}
{% endblock %}
<div class="space-y-2 p-4">
<!-- Header Section -->
<div class="card bg-base-100">
<div class="card-body">
<div class="flex items-center justify-between">
<h2 class="card-title text-2xl">{{ compound_structure.name }}</h2>
<div id="actionsButton" class="dropdown dropdown-end hidden">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-wrench"
>
<path
d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"
/>
</svg>
Actions
</div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2"
>
{% block actions %}
{% include "actions/objects/compound_structure.html" %}
{% endblock %}
</ul>
</div>
</div>
<p class="mt-2">{{ compound_structure.description }}</p>
</div>
</div>
<!-- Image Representation -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Image Representation</div>
<div class="collapse-content">
<div class="flex justify-center">
{{ compound_structure.as_svg|safe }}
</div>
</div>
</div>
<!-- SMILES Representation -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
SMILES Representation
</div>
<div class="collapse-content">{{ compound_structure.smiles }}</div>
</div>
{% if compound_structure.aliases %}
<!-- Aliases -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Aliases</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for alias in compound_structure.aliases %}
<li><a class="hover:bg-base-200">{{ alias }}</a></li>
{% endfor %}
</ul>
</div>
</div>
<div class="panel-body">
<p>{{ compound_structure.description|safe }}</p>
</div>
{% endif %}
<!-- Image -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="compound-structure-image-link"
data-toggle="collapse"
data-parent="#compound-structure-detail"
href="#compound-structure-image"
>Image Representation</a
>
</h4>
</div>
<div id="compound-structure-image" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<div id="image-div" align="center">
{{ compound_structure.as_svg|safe }}
</div>
</div>
</div>
<!-- SMILES -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="compound-structure-smiles-link"
data-toggle="collapse"
data-parent="#compound-structure-detail"
href="#compound-structure-smiles"
>SMILES Representation</a
>
</h4>
</div>
<div id="compound-structure-smiles" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{{ compound_structure.smiles }}
</div>
</div>
{% if compound_structure.aliases %}
<!-- Aliases -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="compound-structure-aliases-link"
data-toggle="collapse"
data-parent="#compound-structure-detail"
href="#compound-structure-aliases"
>Aliases</a
>
</h4>
</div>
<div id="compound-structure-aliases" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{% for alias in compound_structure.aliases %}
<a class="list-group-item">{{ alias }}</a>
{% endfor %}
</div>
</div>
{% endif %}
{% 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 in collapse"
>
<div class="panel-body list-group-item">
{% if compound_structure.scenarios.all %}
<!-- Scenarios -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Scenarios</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for s in compound_structure.scenarios.all %}
<a class="list-group-item" href="{{ s.url }}"
>{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a
>
<li>
<a href="{{ s.url }}" class="hover:bg-base-200"
>{{ s.name }} <i>({{ s.package.name }})</i></a
>
</li>
{% endfor %}
</div>
</ul>
</div>
{% endif %}
<!-- Reactions -->
<!-- Pathways -->
</div>
</div>
{% endif %}
</div>
<script>
// Show actions button if there are actions
document.addEventListener("DOMContentLoaded", function () {
const actionsButton = document.getElementById("actionsButton");
const actionsList = actionsButton?.querySelector("ul");
if (actionsList && actionsList.children.length > 0) {
actionsButton?.classList.remove("hidden");
}
});
</script>
{% endblock content %}

View File

@ -1,4 +1,4 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% block content %}
@ -9,214 +9,165 @@
{% include "modals/objects/generic_delete_modal.html" %}
{% endblock action_modals %}
<div class="panel-group" id="edge-detail">
<div class="panel panel-default">
<div
class="panel-heading"
id="headingPanel"
style="font-size:2rem;height: 46px"
>
{{ edge.edge_label.name|safe }}
<div
id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"
>
<a
href="#"
class="dropdown-toggle"
data-toggle="dropdown"
role="button"
aria-haspopup="true"
aria-expanded="false"
><span class="glyphicon glyphicon-wrench"></span> Actions
<span class="caret"></span><span style="padding-right:1em"></span
></a>
<ul id="actionsList" class="dropdown-menu">
{% block actions %}
{% include "actions/objects/edge.html" %}
{% endblock %}
<div class="space-y-2 p-4">
<!-- Header Section -->
<div class="card bg-base-100">
<div class="card-body">
<div class="flex items-center justify-between">
<h2 class="card-title text-2xl">{{ edge.edge_label.name }}</h2>
<div id="actionsButton" class="dropdown dropdown-end hidden">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-wrench"
>
<path
d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"
/>
</svg>
Actions
</div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2"
>
{% block actions %}
{% include "actions/objects/edge.html" %}
{% endblock %}
</ul>
</div>
</div>
</div>
</div>
<!-- Description -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Description</div>
<div class="collapse-content">{{ edge.description }}</div>
</div>
{% if edge.aliases %}
<!-- Aliases -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Aliases</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for alias in edge.aliases %}
<li><a class="hover:bg-base-200">{{ alias }}</a></li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
<!-- Description -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="edge-desc-link"
data-toggle="collapse"
data-parent="#edge-detail"
href="#edge-desc"
>Description</a
>
</h4>
<!-- Image Representation -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Image Representation</div>
<div class="collapse-content">
<div class="flex justify-center">{{ edge.edge_label.as_svg|safe }}</div>
</div>
<div id="edge-desc" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{{ edge.description|safe }}
</div>
</div>
{% if edge.aliases %}
<!-- Aliases -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="edge-aliases-link"
data-toggle="collapse"
data-parent="#edge-detail"
href="#edge-aliases"
>Aliases</a
>
</h4>
</div>
<div id="edge-aliases" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{% for alias in edge.aliases %}
<a class="list-group-item">{{ alias }}</a>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Image -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="edge-image-link"
data-toggle="collapse"
data-parent="#edge-detail"
href="#edge-image"
>Image Representation</a
>
</h4>
</div>
<div id="edge-image" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<div id="image-div" align="center">
{{ edge.edge_label.as_svg|safe }}
</div>
</div>
</div>
<!-- Reaction Description -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="edge-description-link"
data-toggle="collapse"
data-parent="#edge-description-detail"
href="#edge-description-smiles"
>Reaction Description</a
>
</h4>
</div>
<div id="edge-description-smiles" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{% for educt in edge.start_nodes.all %}
<a class="btn btn-default" href="{{ educt.url }}"
>{{ educt.name|safe }}</a
>
{% endfor %}
<span
class="glyphicon glyphicon-arrow-right"
style="margin-left:5em;margin-right:5em;"
aria-hidden="true"
></span>
{% for product in edge.end_nodes.all %}
<a class="btn btn-default" href="{{ product.url }}"
>{{ product.name|safe }}</a
>
{% endfor %}
</div>
</div>
<!-- SMIRKS -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="edge-smirks-link"
data-toggle="collapse"
data-parent="#edge-detail"
href="#edge-smirks"
>SMIRKS Representation</a
>
</h4>
</div>
<div id="edge-smirks" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{{ edge.edge_label.smirks }}
</div>
</div>
{% if edge.edge_label.rules.all %}
<!-- Rules -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="edge-rules-link"
data-toggle="collapse"
data-parent="#edge-detail"
href="#edge-rules"
>Rules</a
>
</h4>
</div>
<div id="edge-rules" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{% for r in edge.edge_label.rules.all %}
<a class="list-group-item" href="{{ r.url }}"
>{{ r.name|safe }}</a
>
{% endfor %}
</div>
</div>
{% endif %}
{% if edge.scenarios.all %}
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="edge-scenario-link"
data-toggle="collapse"
data-parent="#edge-detail"
href="#edge-scenario"
>Scenarios</a
>
</h4>
</div>
<div id="edge-scenario" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{% for s in edge.scenarios.all %}
<a class="list-group-item" href="{{ s.url }}"
>{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a
>
{% endfor %}
</div>
</div>
{% endif %}
</div>
<!-- Reaction Description -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Reaction Description</div>
<div class="collapse-content">
<div class="flex flex-wrap items-center justify-center gap-4">
{% for educt in edge.start_nodes.all %}
<a href="{{ educt.url }}" class="btn btn-outline btn-sm"
>{{ educt.name }}</a
>
{% endfor %}
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-arrow-right"
>
<path d="M5 12h14" />
<path d="m12 5 7 7-7 7" />
</svg>
{% for product in edge.end_nodes.all %}
<a href="{{ product.url }}" class="btn btn-outline btn-sm"
>{{ product.name }}</a
>
{% endfor %}
</div>
</div>
</div>
<!-- SMIRKS Representation -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
SMIRKS Representation
</div>
<div class="collapse-content">{{ edge.edge_label.smirks }}</div>
</div>
{% if edge.edge_label.rules.all %}
<!-- Rules -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Rules</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for r in edge.edge_label.rules.all %}
<li>
<a href="{{ r.url }}" class="hover:bg-base-200">{{ r.name }}</a>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
{% if edge.scenarios.all %}
<!-- Scenarios -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Scenarios</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for s in edge.scenarios.all %}
<li>
<a href="{{ s.url }}" class="hover:bg-base-200"
>{{ s.name }} <i>({{ s.package.name }})</i></a
>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>
<script>
// Show actions button if there are actions
document.addEventListener("DOMContentLoaded", function () {
const actionsButton = document.getElementById("actionsButton");
const actionsList = actionsButton?.querySelector("ul");
if (actionsList && actionsList.children.length > 0) {
actionsButton?.classList.remove("hidden");
}
});
</script>
{% endblock content %}

View File

@ -1,163 +1,109 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% block content %}
<div class="panel-group" id="enzyme-detail">
<div class="panel panel-default">
<div
class="panel-heading"
id="headingPanel"
style="font-size:2rem;height: 46px"
>
{{ enzymelink.ec_number }}
<div class="space-y-2 p-4">
<!-- Header Section -->
<div class="card bg-base-100">
<div class="card-body">
<h2 class="card-title text-2xl">{{ enzymelink.ec_number }}</h2>
</div>
</div>
<!-- Name -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="enzyme-name-link"
data-toggle="collapse"
data-parent="#enzyme-detail"
href="#enzyme-name"
>Enzyme Name</a
>
</h4>
</div>
<div id="enzyme-name" class="panel-collapse in collapse">
<div class="panel-body list-group-item">{{ enzymelink.name }}</div>
</div>
<!-- Enzyme Name -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Enzyme Name</div>
<div class="collapse-content">{{ enzymelink.name }}</div>
</div>
<!-- Linking Method -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="enzyme-linking-link"
data-toggle="collapse"
data-parent="#enzyme-detail"
href="#enzyme-linking"
>Linking Method</a
>
</h4>
</div>
<div id="enzyme-linking" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{{ enzymelink.linking_method }}. &nbsp;<a
href="https://wiki.envipath.org/index.php/Rules#EnzymeLinks"
target="#"
>Learn more &gt;&gt;</a
>
</div>
</div>
{% if enzymelink.kegg_reaction_links %}
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
<!-- Linking Method -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Linking Method</div>
<div class="collapse-content">
{{ enzymelink.linking_method }}. &nbsp;<a
href="https://wiki.envipath.org/index.php/Rules#EnzymeLinks"
target="_blank"
class="link link-primary"
>Learn more &gt;&gt;</a
>
<h4 class="panel-title">
<a
id="enzyme-evidence-link"
data-toggle="collapse"
data-parent="#enzyme-detail"
href="#enzyme-evidence"
>Linking Evidence</a
>
</h4>
</div>
<div id="enzyme-evidence" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
</div>
</div>
{% if enzymelink.kegg_reaction_links %}
<!-- Linking Evidence -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Linking Evidence</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for kl in enzymelink.kegg_reaction_links %}
<a class="list-group-item" href="{{ kl.external_url }}"
>{{ kl.identifier_value }}</a
>
<li>
<a href="{{ kl.external_url }}" class="hover:bg-base-200"
>{{ kl.identifier_value }}</a
>
</li>
{% endfor %}
</div>
</ul>
</div>
{% endif %}
{% if enzymelink.reaction_evidence.all %}
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="enzyme-reaction-evidence-link"
data-toggle="collapse"
data-parent="#enzyme-detail"
href="#enzyme-reaction-evidence"
>Linking Evidence - enviPath Reactions</a
>
</h4>
</div>
<div id="enzyme-reaction-evidence" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{% for r in enzymelink.reaction_evidence.all %}
<a class="list-group-item" href="{{ r.url }}"
>{{ r.name }} <i>({{ r.package.name }})</i></a
>
{% endfor %}
</div>
</div>
{% endif %}
{% if enzymelink.edge_evidence.all %}
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="enzyme-edge-evidence-link"
data-toggle="collapse"
data-parent="#enzyme-detail"
href="#enzyme-edge-evidence"
>Linking Evidence - enviPath Pathways</a
>
</h4>
</div>
<div id="enzyme-edge-evidence" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{% for e in enzymelink.edge_evidence.all %}
<a class="list-group-item" href="{{ e.pathway.url }}"
>{{ e.pathway.name }} <i>({{ r.package.name }})</i></a
>
{% endfor %}
</div>
</div>
{% endif %}
<!-- External DB Reference -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="enzyme-external-identifier-link"
data-toggle="collapse"
data-parent="#enzyme-detail"
href="#enzyme-external-identifier"
>External DB References</a
>
</h4>
</div>
<div id="enzyme-external-identifier" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<a
class="list-group-item"
href="http://www.brenda-enzymes.org/enzyme.php?ecno={{ enzymelink.ec_number }}"
target="_blank"
>
Brenda entry for {{ enzymelink.ec_number }}</a
>
{% endif %}
{% if enzymelink.reaction_evidence.all %}
<!-- Linking Evidence - enviPath Reactions -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
Linking Evidence - enviPath Reactions
</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for r in enzymelink.reaction_evidence.all %}
<li>
<a href="{{ r.url }}" class="hover:bg-base-200"
>{{ r.name }} <i>({{ r.package.name }})</i></a
>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
{% if enzymelink.edge_evidence.all %}
<!-- Linking Evidence - enviPath Pathways -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
Linking Evidence - enviPath Pathways
</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for e in enzymelink.edge_evidence.all %}
<li>
<a href="{{ e.pathway.url }}" class="hover:bg-base-200"
>{{ e.pathway.name }} <i>({{ e.pathway.package.name }})</i></a
>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
<!-- External DB References -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
External DB References
</div>
<div class="collapse-content">
<a
href="http://www.brenda-enzymes.org/enzyme.php?ecno={{ enzymelink.ec_number }}"
target="_blank"
class="link link-primary"
>Brenda entry for {{ enzymelink.ec_number }}</a
>
</div>
</div>
</div>

View File

@ -1,4 +1,4 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% block content %}
@ -8,84 +8,92 @@
{% include "modals/objects/generic_delete_modal.html" %}
{% endblock action_modals %}
<div class="panel-group" id="package-detail">
<div class="panel panel-default">
<div
class="panel-heading"
id="headingPanel"
style="font-size:2rem;height: 46px"
>
{{ group.name|safe }}
<div
id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"
>
<a
href="#"
class="dropdown-toggle"
data-toggle="dropdown"
role="button"
aria-haspopup="true"
aria-expanded="false"
><span class="glyphicon glyphicon-wrench"></span> Actions
<span class="caret"></span><span style="padding-right:1em"></span
></a>
<ul id="actionsList" class="dropdown-menu">
{% block actions %}
{% include "actions/objects/group.html" %}
{% endblock %}
</ul>
<div class="space-y-2 p-4">
<!-- Header Section -->
<div class="card bg-base-100">
<div class="card-body">
<div class="flex items-center justify-between">
<h2 class="card-title text-2xl">{{ group.name }}</h2>
<div id="actionsButton" class="dropdown dropdown-end hidden">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-wrench"
>
<path
d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"
/>
</svg>
Actions
</div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2"
>
{% block actions %}
{% include "actions/objects/group.html" %}
{% endblock %}
</ul>
</div>
</div>
</div>
<div class="panel-body">
<p>{{ group.description|safe }}</p>
<p class="mt-2">{{ group.description }}</p>
</div>
</div>
<p></p>
<div class="panel panel-default">
<div
id="member-panel"
style="font-size:2rem;height: 46px"
class="panel-heading"
>
Members
<!-- Members -->
<div class="card bg-base-100">
<div class="card-body">
<h3 class="card-title mb-4 text-xl">Members</h3>
<p class="mb-4">List of members of this group</p>
<ul class="menu bg-base-200 rounded-box">
{% for um in group.user_member.all %}
<li>
<a href="{{ um.url }}" class="hover:bg-base-300"
>{{ um.username }}</a
>
</li>
{% endfor %}
{% for gm in group.group_member.all %}
<li>
<a href="{{ gm.url }}" class="hover:bg-base-300">{{ gm.name }}</a>
</li>
{% endfor %}
</ul>
</div>
<div class="panel-body">
<p>List of members of this group</p>
</div>
<ul class="list-group">
{% for um in group.user_member.all %}
<a class="list-group-item" href="{{ um.url }}"
>{{ um.username|safe }}</a
>
{% endfor %}
{% for gm in group.group_member.all %}
<a class="list-group-item" href="{{ gm.url }}">{{ gm.name|safe }}</a>
{% endfor %}
</ul>
</div>
<p></p>
<div class="panel panel-default">
<div
id="package-panel"
style="font-size:2rem;height: 46px"
class="panel-heading"
>
Packages
<!-- Packages -->
<div class="card bg-base-100">
<div class="card-body">
<h3 class="card-title mb-4 text-xl">Packages</h3>
<p class="mb-4">Packages where this group has access to</p>
<ul class="menu bg-base-200 rounded-box">
{% for p in packages %}
<li>
<a href="{{ p.url }}" class="hover:bg-base-300">{{ p.name }}</a>
</li>
{% endfor %}
</ul>
</div>
<div class="panel-body">
<p>Packages where this group has access to</p>
</div>
<ul class="list-group">
{% for p in packages %}
<a class="list-group-item" href="{{ p.url }}">{{ p.name|safe }}</a>
{% endfor %}
</ul>
</div>
</div>
<script>
// Show actions button if there are actions
document.addEventListener("DOMContentLoaded", function () {
const actionsButton = document.getElementById("actionsButton");
const actionsList = actionsButton?.querySelector("ul");
if (actionsList && actionsList.children.length > 0) {
actionsButton?.classList.remove("hidden");
}
});
</script>
{% endblock content %}

View File

@ -1,4 +1,4 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% load static %}
{% load envipytags %}
{% block content %}
@ -18,509 +18,458 @@
rel="stylesheet"
/>
<div class="panel-group" id="model-detail">
<div class="panel panel-default">
<div
class="panel-heading"
id="headingPanel"
style="font-size:2rem;height: 46px"
>
{{ model.name|safe }}
<div
id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"
>
<a
href="#"
class="dropdown-toggle"
data-toggle="dropdown"
role="button"
aria-haspopup="true"
aria-expanded="false"
><span class="glyphicon glyphicon-wrench"></span> Actions
<span class="caret"></span><span style="padding-right:1em"></span
></a>
<ul id="actionsList" class="dropdown-menu">
{% block actions %}
{% include "actions/objects/model.html" %}
{% endblock %}
<div class="space-y-2 p-4">
<!-- Header Section -->
<div class="card bg-base-100">
<div class="card-body">
<div class="flex items-center justify-between">
<h2 class="card-title text-2xl">{{ model.name }}</h2>
<div id="actionsButton" class="dropdown dropdown-end hidden">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-wrench"
>
<path
d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"
/>
</svg>
Actions
</div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2"
>
{% block actions %}
{% include "actions/objects/model.html" %}
{% endblock %}
</ul>
</div>
</div>
<p class="mt-2">{{ model.description }}</p>
</div>
</div>
{% if model|classname == 'MLRelativeReasoning' or model|classname == 'RuleBasedRelativeReasoning' %}
<!-- Rule Packages -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Rule Packages</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box w-full">
{% for p in model.rule_packages.all %}
<li>
<a href="{{ p.url }}" class="hover:bg-base-200">{{ p.name }}</a>
</li>
{% endfor %}
</ul>
</div>
</div>
<div class="panel-body">
<p>{{ model.description|safe }}</p>
</div>
{% if model|classname == 'MLRelativeReasoning' or model|classname == 'RuleBasedRelativeReasoning' %}
<!-- Rule Packages -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="rule-package-link"
data-toggle="collapse"
data-parent="#model-detail"
href="#rule-package"
>Rule Packages</a
>
</h4>
</div>
<div id="rule-package" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{% for p in model.rule_packages.all %}
<a class="list-group-item" href="{{ p.url }}"
>{{ p.name|safe }}</a
>
{% endfor %}
</div>
</div>
<!-- Reaction Packages -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="reaction-package-link"
data-toggle="collapse"
data-parent="#model-detail"
href="#reaction-package"
>Data Packages</a
>
</h4>
</div>
<div id="reaction-package" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<!-- Reaction Packages -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Reaction Packages</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box w-full">
{% for p in model.data_packages.all %}
<a class="list-group-item" href="{{ p.url }}"
>{{ p.name|safe }}</a
>
<li>
<a href="{{ p.url }}" class="hover:bg-base-200">{{ p.name }}</a>
</li>
{% endfor %}
</div>
</ul>
</div>
{% if model.eval_packages.all|length > 0 %}
<!-- Eval Packages -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="eval-package-link"
data-toggle="collapse"
data-parent="#model-detail"
href="#eval-package"
>Evaluation Packages</a
>
</h4>
</div>
<div id="eval-package" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
</div>
{% if model.eval_packages.all|length > 0 %}
<!-- Eval Packages -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Eval Packages</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box w-full">
{% for p in model.eval_packages.all %}
<a class="list-group-item" href="{{ p.url }}"
>{{ p.name|safe }}</a
>
<li>
<a href="{{ p.url }}" class="hover:bg-base-200"
>{{ p.name }}</a
>
</li>
{% endfor %}
</div>
</ul>
</div>
{% endif %}
<!-- Model Status -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="model-status-link"
data-toggle="collapse"
data-parent="#model-detail"
href="#model-status"
>Model Status</a
>
</h4>
</div>
<div id="model-status" class="panel-collapse in collapse">
<div class="panel-body list-group-item">{{ model.status }}</div>
</div>
{% endif %}
{% if model.ready_for_prediction %}
<!-- Predict Panel -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="predict-smiles-link"
data-toggle="collapse"
data-parent="#model-detail"
href="#predict-smiles"
>Predict</a
>
</h4>
</div>
<div id="predict-smiles" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<div class="input-group">
<!-- Model Status -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Model Status</div>
<div class="collapse-content">{{ model.status }}</div>
</div>
{% endif %}
{% if model.ready_for_prediction %}
<!-- Predict Panel -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Predict</div>
<div class="collapse-content">
<div class="form-control">
<div class="join w-full">
<input
id="smiles-to-predict"
type="text"
class="form-control"
class="input input-bordered join-item grow"
placeholder="CCN(CC)C(=O)C1=CC(=CC=C1)C"
/>
<span class="input-group-btn">
<button
class="btn btn-default"
type="submit"
id="predict-button"
>
Predict!
</button>
</span>
<button
class="btn btn-primary join-item"
type="button"
id="predict-button"
>
Predict!
</button>
</div>
<div id="predictLoading"></div>
<div id="predictResultTable"></div>
</div>
<div id="predictLoading" class="mt-2"></div>
<div id="predictResultTable" class="mt-4"></div>
</div>
<!-- End Predict Panel -->
{% endif %}
</div>
{% endif %}
{% if model.ready_for_prediction and 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>
{% if model.ready_for_prediction and model.app_domain %}
<!-- App Domain -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
Applicability Domain Assessment
</div>
<div id="app-domain-assessment" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<div class="input-group">
<div class="collapse-content">
<div class="form-control">
<div class="join w-full">
<input
id="smiles-to-assess"
type="text"
class="form-control"
class="input input-bordered join-item grow"
placeholder="CCN(CC)C(=O)C1=CC(=CC=C1)C"
/>
<span class="input-group-btn">
<button
class="btn btn-default"
type="submit"
id="assess-button"
>
Assess!
</button>
</span>
</div>
<div id="appDomainLoading"></div>
<div id="appDomainAssessmentResultTable"></div>
</div>
</div>
<!-- End App Domain -->
{% endif %}
{% if model.model_status == 'FINISHED' %}
<!-- Single Gen Curve Panel -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="sg-curve-link"
data-toggle="collapse"
data-parent="#model-detail"
href="#sg-curve"
>Precision Recall Curve</a
>
</h4>
</div>
<div id="sg-curve" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<!-- Center container contents -->
<div
class="container"
style="display: flex;justify-content: center;"
>
<div id="sg-curve-plotdiv" class="chart">
<div id="sg-chart"></div>
</div>
<button
class="btn btn-primary join-item"
type="button"
id="assess-button"
>
Assess!
</button>
</div>
</div>
<div id="appDomainLoading" class="mt-2"></div>
<div id="appDomainAssessmentResultTable" class="mt-4"></div>
</div>
{# prettier-ignore-start #}
<script>
$(function () {
if (!($('#sg-chart').length > 0)) {
return;
}
</div>
{% endif %}
var x = ['Recall'];
var y = ['Precision'];
var thres = ['threshold'];
// Compare function for the given array
function compare(a, b) {
if (a.threshold < b.threshold)
return -1;
else if (a.threshold > b.threshold)
return 1;
else
return 0;
}
function getIndexForValue(data, val, val_name) {
for (var idx in data) {
if (data[idx][val_name] == val) {
return idx;
}
}
return -1;
}
var data = {{ model.pr_curve|safe }}
var dataLength = Object.keys(data).length;
data.sort(compare);
for (var idx in data) {
var d = data[idx];
x.push(d.recall);
y.push(d.precision);
thres.push(d.threshold);
}
var chart = c3.generate({
bindto: '#sg-chart',
data: {
onclick: function (d, e) {
var idx = d.index;
var thresh = data[dataLength - idx - 1].threshold;
//onclick(thresh)
},
x: 'Recall',
y: 'Precision',
columns: [
x,
y,
//thres
]
},
size: {
height: 400, // TODO: Make variable to current modal width
width: 480
},
axis: {
x: {
max: 1,
min: 0,
label: 'Recall',
padding: 0,
tick: {
fit: true,
values: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
}
},
y: {
max: 1,
min: 0,
label: 'Precision',
padding: 0,
tick: {
fit: true,
values: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
}
}
},
point: {
r: 4
},
tooltip: {
format: {
title: function (recall) {
idx = getIndexForValue(data, recall, "recall");
if (idx != -1) {
return "Threshold: " + data[idx].threshold;
}
return "";
},
value: function (precision, ratio, id) {
return undefined;
}
}
},
zoom: {
enabled: true
}
});
});
</script>
{# prettier-ignore-end #}
<!-- End Single Gen Curve Panel -->
{% endif %}
</div>
{% if model.model_status == 'FINISHED' %}
<!-- Single Gen Curve Panel -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
Precision Recall Curve
</div>
<div class="collapse-content">
<div class="flex justify-center">
<div id="sg-chart"></div>
</div>
</div>
</div>
{% endif %}
</div>
<script>
function handlePredictionResponse(data) {
res = "<table class='table table-striped'>";
res += "<thead>";
res += "<th scope='col'>#</th>";
{# prettier-ignore-start #}
{# FIXME: This is a hack to get the precision recall curve data into the JavaScript code. #}
<script>
function handlePredictionResponse(data) {
let res = "<table class='table table-zebra'>"
res += "<thead>"
res += "<th scope='col'>#</th>"
columns = ["products", "image", "probability", "btrule"];
const columns = ['products', 'image', 'probability', 'btrule']
for (col in columns) {
res += "<th scope='col'>" + columns[col] + "</th>";
}
res += "</thead>";
res += "<tbody>";
var cnt = 1;
for (transformation in data) {
res += "<tr>";
res += "<th scope='row'>" + cnt + "</th>";
res +=
"<th scope='row'>" +
data[transformation]["products"][0].join(", ") +
"</th>";
res +=
"<th scope='row'>" +
"<img width='400' src='{% url 'depict' %}?smiles=" +
encodeURIComponent(data[transformation]["products"][0].join(".")) +
"'></th>";
res +=
"<th scope='row'>" +
data[transformation]["probability"].toFixed(3) +
"</th>";
if (data[transformation]["btrule"] != null) {
res +=
"<th scope='row'>" +
"<a href='" +
data[transformation]["btrule"]["url"] +
"'>" +
data[transformation]["btrule"]["name"] +
"</a>" +
"</th>";
} else {
res += "<th scope='row'>N/A</th>";
}
res += "</tr>";
cnt += 1;
}
res += "</tbody>";
res += "</table>";
$("#predictResultTable").append(res);
}
function clear(divid) {
$("#" + divid).removeClass("alert alert-danger");
$("#" + divid).empty();
}
if ($("#predict-button").length > 0) {
$("#predict-button").on("click", function (e) {
e.preventDefault();
clear("predictResultTable");
data = {
smiles: $("#smiles-to-predict").val(),
classify: "ILikeCats!",
};
if (data["smiles"].trim() === "") {
$("#predictResultTable").addClass("alert alert-danger");
$("#predictResultTable").append(
"Please enter a SMILES string to predict!",
);
return;
}
makeLoadingGif("#predictLoading", "{% static '/images/wait.gif' %}");
$.ajax({
type: "get",
data: data,
url: "",
success: function (data, textStatus) {
try {
$("#predictLoading").empty();
handlePredictionResponse(data);
} catch (error) {
$("#predictLoading").empty();
$("#predictResultTable").addClass("alert alert-danger");
$("#predictResultTable").append(
"Error while processing response :/",
);
for (const col of columns) {
res += "<th scope='col'>" + col + "</th>"
}
res += "</thead>"
res += "<tbody>"
let cnt = 1;
for (const transformation in data) {
res += "<tr>"
res += "<th scope='row'>" + cnt + "</th>"
res += "<th scope='row'>" + data[transformation]['products'][0].join(', ') + "</th>"
res += "<th scope='row'>" + "<img width='400' src='{% url 'depict' %}?smiles=" + encodeURIComponent(data[transformation]['products'][0].join('.')) + "'></th>"
res += "<th scope='row'>" + data[transformation]['probability'].toFixed(3) + "</th>"
if (data[transformation]['btrule'] != null) {
res += "<th scope='row'>" + "<a href='" + data[transformation]['btrule']['url'] + "' class='link link-primary'>" + data[transformation]['btrule']['name'] + "</a>" + "</th>"
} else {
res += "<th scope='row'>N/A</th>"
}
res += "</tr>"
cnt += 1;
}
},
error: function (jqXHR, textStatus, errorThrown, x) {
$("#predictLoading").empty();
$("#predictResultTable").addClass("alert alert-danger");
$("#predictResultTable").append(jqXHR.responseJSON.error);
},
});
});
}
if ($("#assess-button").length > 0) {
$("#assess-button").on("click", function (e) {
e.preventDefault();
clear("appDomainAssessmentResultTable");
data = {
smiles: $("#smiles-to-assess").val(),
"app-domain-assessment": "ILikeCats!",
};
if (data["smiles"].trim() === "") {
$("#appDomainAssessmentResultTable").addClass("alert alert-danger");
$("#appDomainAssessmentResultTable").append(
"Please enter a SMILES string to predict!",
);
return;
res += "</tbody>"
res += "</table>"
const resultTable = document.getElementById("predictResultTable");
if (resultTable) {
resultTable.innerHTML = res;
}
}
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) {
$("#appDomainLoading").empty();
$("#appDomainAssessmentResultTable").addClass(
"alert alert-danger",
);
$("#appDomainAssessmentResultTable").append(
"Error while processing response :/",
);
function clear(divid) {
const element = document.getElementById(divid);
if (element) {
element.classList.remove("alert", "alert-error");
element.innerHTML = "";
}
}
function makeLoadingGif(selector, gifPath) {
const element = document.querySelector(selector);
if (element) {
element.innerHTML = '<img src="' + gifPath + '" alt="Loading...">';
}
}
document.addEventListener('DOMContentLoaded', function() {
// Show actions button if there are actions
const actionsButton = document.getElementById('actionsButton');
const actionsList = actionsButton?.querySelector('ul');
if (actionsList && actionsList.children.length > 0) {
actionsButton?.classList.remove('hidden');
}
{% if model.model_status == 'FINISHED' %}
// Precision Recall Curve
const sgChart = document.getElementById('sg-chart');
if (sgChart) {
const x = ['Recall'];
const y = ['Precision'];
const thres = ['threshold'];
function compare(a, b) {
if (a.threshold < b.threshold)
return -1;
else if (a.threshold > b.threshold)
return 1;
else
return 0;
}
function getIndexForValue(data, val, val_name) {
for (const idx in data) {
if (data[idx][val_name] == val) {
return idx;
}
}
return -1;
}
var data = {{ model.pr_curve|safe }};
if (!data || data.length === 0) {
console.warn('PR curve data is empty');
return;
}
const dataLength = data.length;
data.sort(compare);
for (const idx in data) {
const d = data[idx];
x.push(d.recall);
y.push(d.precision);
thres.push(d.threshold);
}
const chart = c3.generate({
bindto: '#sg-chart',
data: {
onclick: function (d, e) {
const idx = d.index;
const thresh = data[dataLength - idx - 1].threshold;
},
x: 'Recall',
y: 'Precision',
columns: [
x,
y,
]
},
size: {
height: 400,
width: 480
},
axis: {
x: {
max: 1,
min: 0,
label: 'Recall',
padding: 0,
tick: {
fit: true,
values: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
}
},
y: {
max: 1,
min: 0,
label: 'Precision',
padding: 0,
tick: {
fit: true,
values: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
}
}
},
point: {
r: 4
},
tooltip: {
format: {
title: function (recall) {
const idx = getIndexForValue(data, recall, "recall");
if (idx != -1) {
return "Threshold: " + data[idx].threshold;
}
return "";
},
value: function (precision, ratio, id) {
return undefined;
}
}
},
zoom: {
enabled: true
}
});
}
{% endif %}
// Predict button handler
const predictButton = document.getElementById('predict-button');
if (predictButton) {
predictButton.addEventListener('click', function(e) {
e.preventDefault();
clear("predictResultTable");
const smilesInput = document.getElementById('smiles-to-predict');
const smiles = smilesInput ? smilesInput.value.trim() : '';
if (smiles === "") {
const resultTable = document.getElementById("predictResultTable");
if (resultTable) {
resultTable.classList.add("alert", "alert-error");
resultTable.innerHTML = "Please enter a SMILES string to predict!";
}
return;
}
makeLoadingGif("#predictLoading", "{% static '/images/wait.gif' %}");
const params = new URLSearchParams({
smiles: smiles,
classify: "ILikeCats!"
});
fetch('?' + params.toString(), {
method: 'GET',
headers: {
'X-CSRFToken': document.querySelector('[name=csrf-token]').content
}
})
.then(response => {
if (!response.ok) {
return response.json().then(err => { throw err; });
}
return response.json();
})
.then(data => {
const loadingEl = document.getElementById("predictLoading");
if (loadingEl) loadingEl.innerHTML = "";
handlePredictionResponse(data);
})
.catch(error => {
const loadingEl = document.getElementById("predictLoading");
if (loadingEl) loadingEl.innerHTML = "";
const resultTable = document.getElementById("predictResultTable");
if (resultTable) {
resultTable.classList.add("alert", "alert-error");
resultTable.innerHTML = error.error || "Error while processing response :/";
}
});
});
}
// Assess button handler
const assessButton = document.getElementById('assess-button');
if (assessButton) {
assessButton.addEventListener('click', function(e) {
e.preventDefault();
clear("appDomainAssessmentResultTable");
const smilesInput = document.getElementById('smiles-to-assess');
const smiles = smilesInput ? smilesInput.value.trim() : '';
if (smiles === "") {
const resultTable = document.getElementById("appDomainAssessmentResultTable");
if (resultTable) {
resultTable.classList.add("alert", "alert-error");
resultTable.innerHTML = "Please enter a SMILES string to predict!";
}
return;
}
makeLoadingGif("#appDomainLoading", "{% static '/images/wait.gif' %}");
const params = new URLSearchParams({
smiles: smiles,
"app-domain-assessment": "ILikeCats!"
});
fetch('?' + params.toString(), {
method: 'GET',
headers: {
'X-CSRFToken': document.querySelector('[name=csrf-token]').content
}
})
.then(response => {
if (!response.ok) {
return response.json().then(err => { throw err; });
}
return response.json();
})
.then(data => {
const loadingEl = document.getElementById("appDomainLoading");
if (loadingEl) loadingEl.innerHTML = "";
if (typeof handleAssessmentResponse === 'function') {
handleAssessmentResponse("{% url 'depict' %}", data);
}
console.log(data);
})
.catch(error => {
const loadingEl = document.getElementById("appDomainLoading");
if (loadingEl) loadingEl.innerHTML = "";
const resultTable = document.getElementById("appDomainAssessmentResultTable");
if (resultTable) {
resultTable.classList.add("alert", "alert-error");
resultTable.innerHTML = error.error || "Error while processing response :/";
}
});
});
}
},
error: function (jqXHR, textStatus, errorThrown) {
$("#appDomainLoading").empty();
$("#appDomainAssessmentResultTable").addClass("alert alert-danger");
$("#appDomainAssessmentResultTable").append(
jqXHR.responseJSON.error,
);
},
});
});
}
</script>
</script>
{# prettier-ignore-end #}
{% endblock content %}

View File

@ -1,4 +1,4 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% block content %}
@ -9,167 +9,135 @@
{% include "modals/objects/generic_delete_modal.html" %}
{% endblock action_modals %}
<div class="panel-group" id="node-detail">
<div class="panel panel-default">
<div
class="panel-heading"
id="headingPanel"
style="font-size:2rem;height: 46px"
>
{{ node.name|safe }}
<div
id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"
>
<a
href="#"
class="dropdown-toggle"
data-toggle="dropdown"
role="button"
aria-haspopup="true"
aria-expanded="false"
><span class="glyphicon glyphicon-wrench"></span> Actions
<span class="caret"></span><span style="padding-right:1em"></span
></a>
<ul id="actionsList" class="dropdown-menu">
{% block actions %}
{% include "actions/objects/node.html" %}
{% endblock %}
<div class="space-y-2 p-4">
<!-- Header Section -->
<div class="card bg-base-100">
<div class="card-body">
<div class="flex items-center justify-between">
<h2 class="card-title text-2xl">{{ node.name }}</h2>
<div id="actionsButton" class="dropdown dropdown-end hidden">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-wrench"
>
<path
d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"
/>
</svg>
Actions
</div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2"
>
{% block actions %}
{% include "actions/objects/node.html" %}
{% endblock %}
</ul>
</div>
</div>
<p class="mt-2">
The underlying structure can be found
<a href="{{ node.default_node_label.url }}" class="link link-primary"
>here</a
>.
</p>
</div>
</div>
<!-- Description -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Description</div>
<div class="collapse-content">{{ node.description }}</div>
</div>
{% if node.aliases %}
<!-- Aliases -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Aliases</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for alias in node.aliases %}
<li><a class="hover:bg-base-200">{{ alias }}</a></li>
{% endfor %}
</ul>
</div>
</div>
<div class="panel-body">
The underlying structure can be found
<a href="{{ node.default_node_label.url }}">here</a>.
</div>
{% endif %}
<!-- Description -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="node-desc-link"
data-toggle="collapse"
data-parent="#node-detail"
href="#node-desc"
>Description</a
>
</h4>
</div>
<div id="node-desc" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{{ node.description|safe }}
<!-- Image Representation -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Image Representation</div>
<div class="collapse-content">
<div class="flex justify-center">
{{ node.default_node_label.as_svg|safe }}
</div>
</div>
{% if node.aliases %}
<!-- Aliases -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="node-aliases-link"
data-toggle="collapse"
data-parent="#node-detail"
href="#node-aliases"
>Aliases</a
>
</h4>
</div>
<div id="node-aliases" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{% for alias in node.aliases %}
<a class="list-group-item">{{ alias }}</a>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Image -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="node-image-link"
data-toggle="collapse"
data-parent="#node-detail"
href="#node-image"
>Image Representation</a
>
</h4>
</div>
<div id="node-image" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<div id="image-div" align="center">
{{ node.default_node_label.as_svg|safe }}
</div>
</div>
</div>
<!-- SMILES -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="node-smiles-link"
data-toggle="collapse"
data-parent="#node-detail"
href="#node-smiles"
>SMILES Representation</a
>
</h4>
</div>
<div id="node-smiles" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{{ node.default_node_label.smiles }}
</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 in collapse">
<div class="panel-body list-group-item">
{% for s in node.scenarios.all %}
<a class="list-group-item" href="{{ s.url }}"
>{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a
>
{% endfor %}
</div>
</div>
{% endif %}
{% if app_domain_assessment_data %}
<div id="appDomainAssessmentResultTable"></div>
{# prettier-ignore-start #}
<script>
$(document).ready(function () {
handleAssessmentResponse("{% url 'depict' %}", {{ app_domain_assessment_data|safe }})
})
</script>
{# prettier-ignore-end #}
{% endif %}
</div>
<!-- SMILES Representation -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
SMILES Representation
</div>
<div class="collapse-content">{{ node.default_node_label.smiles }}</div>
</div>
{% if node.scenarios.all %}
<!-- Scenarios -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Scenarios</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for s in node.scenarios.all %}
<li>
<a href="{{ s.url }}" class="hover:bg-base-200"
>{{ s.name }} <i>({{ s.package.name }})</i></a
>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
{% if app_domain_assessment_data %}
<div id="appDomainAssessmentResultTable"></div>
{# prettier-ignore-start #}
{# FIXME: This is a hack to get the app domain assessment data into the JavaScript code. #}
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof handleAssessmentResponse === 'function') {
handleAssessmentResponse("{% url 'depict' %}", {{ app_domain_assessment_data|safe }});
}
});
</script>
{# prettier-ignore-end #}
{% endif %}
</div>
<script>
// Show actions button if there are actions
document.addEventListener("DOMContentLoaded", function () {
const actionsButton = document.getElementById("actionsButton");
const actionsList = actionsButton?.querySelector("ul");
if (actionsList && actionsList.children.length > 0) {
actionsButton?.classList.remove("hidden");
}
});
</script>
{% endblock content %}

View File

@ -1,4 +1,4 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% block content %}
@ -11,69 +11,87 @@
{% include "modals/objects/generic_delete_modal.html" %}
{% endblock action_modals %}
<div class="panel-group" id="package-detail">
<div class="panel panel-default">
<div
class="panel-heading"
id="headingPanel"
style="font-size:2rem;height: 46px"
>
{{ package.name|safe }}
<div
id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"
>
<a
href="#"
class="dropdown-toggle"
data-toggle="dropdown"
role="button"
aria-haspopup="true"
aria-expanded="false"
><span class="glyphicon glyphicon-wrench"></span> Actions
<span class="caret"></span><span style="padding-right:1em"></span
></a>
<ul id="actionsList" class="dropdown-menu">
{% block actions %}
{% include "actions/objects/package.html" %}
{% endblock %}
</ul>
<div class="space-y-2 p-4">
<!-- Header Section -->
<div class="card bg-base-100">
<div class="card-body">
<div class="flex items-center justify-between">
<h2 class="card-title text-2xl">{{ package.name }}</h2>
<div id="actionsButton" class="dropdown dropdown-end hidden">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-wrench"
>
<path
d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"
/>
</svg>
Actions
</div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2"
>
{% block actions %}
{% include "actions/objects/package.html" %}
{% endblock %}
</ul>
</div>
</div>
<p class="mt-2">{{ package.description|safe }}</p>
<ul class="menu bg-base-200 rounded-box mt-4 w-full">
<li>
<a href="{{ package.url }}/pathway" class="hover:bg-base-300"
>Pathways ({{ package.pathways.count }})</a
>
</li>
<li>
<a href="{{ package.url }}/rule" class="hover:bg-base-300"
>Rules ({{ package.rules.count }})</a
>
</li>
<li>
<a href="{{ package.url }}/compound" class="hover:bg-base-300"
>Compounds ({{ package.compounds.count }})</a
>
</li>
<li>
<a href="{{ package.url }}/reaction" class="hover:bg-base-300"
>Reactions ({{ package.reactions.count }})</a
>
</li>
<li>
<a href="{{ package.url }}/model" class="hover:bg-base-300"
>Models ({{ package.models.count }})</a
>
</li>
<li>
<a href="{{ package.url }}/scenario" class="hover:bg-base-300"
>Scenarios ({{ package.scenarios.count }})</a
>
</li>
</ul>
</div>
<div class="panel-body">
<p>{{ package.description|safe }}</p>
</div>
<ul class="list-group">
<li class="list-group-item">
<a href="{{ package.url }}/pathway"
>Pathways ({{ package.pathways.count }})</a
>
</li>
<li class="list-group-item">
<a href="{{ package.url }}/rule">Rules ({{ package.rules.count }})</a>
</li>
<li class="list-group-item">
<a href="{{ package.url }}/compound"
>Compounds ({{ package.compounds.count }})</a
>
</li>
<li class="list-group-item">
<a href="{{ package.url }}/reaction"
>Reactions ({{ package.reactions.count }})</a
>
</li>
<li class="list-group-item">
<a href="{{ package.url }}/model"
>Models ({{ package.models.count }})</a
>
</li>
<li class="list-group-item">
<a href="{{ package.url }}/scenario"
>Scenarios ({{ package.scenarios.count }})</a
>
</li>
</ul>
</div>
</div>
<script>
// Show actions button if there are actions
document.addEventListener("DOMContentLoaded", function () {
const actionsButton = document.getElementById("actionsButton");
const actionsList = actionsButton?.querySelector("ul");
if (actionsList && actionsList.children.length > 0) {
actionsButton?.classList.remove("hidden");
}
});
</script>
{% endblock content %}

View File

@ -1,4 +1,4 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% load static %}
{% block content %}
<script src="https://d3js.org/d3.v7.min.js"></script>
@ -7,6 +7,7 @@
width: 100%;
height: 600px;
background: white;
position: relative;
}
#pwsvg {
@ -18,6 +19,7 @@
.link {
stroke: #999;
stroke-opacity: 0.6;
/* marker-end: url(#arrow); */
}
.link_no_arrow {
@ -63,15 +65,6 @@
stroke: red;
stroke-width: 3px;
}
.tooltip {
position: absolute;
background: lightgrey;
padding: 5px;
border-radius: 5px;
visibility: hidden;
opacity: 1;
}
</style>
<script src="{% static 'js/pw.js' %}"></script>
@ -90,141 +83,217 @@
{% include "modals/objects/generic_delete_modal.html" %}
{% endblock action_modals %}
<p></p>
<div id="pwcontent">
<div class="panel-group" id="pwAccordion">
<div class="panel panel-default">
<div
class="panel-heading"
id="headingPanel"
style="font-size:2rem;height: 46px"
>
{{ pathway.name|safe }}
</div>
</div>
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="vizLink"
data-toggle="collapse"
data-parent="#pwAccordion"
href="#viz"
>
Graphical Representation
</a>
</h4>
</div>
<div id="viz" class="panel-collapse in collapse">
<nav role="navigation" class="navbar navbar-default" style="margin: 0;">
<div class="navbar-header"></div>
<div id="editbarCollapse" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown requiresWritePerm">
<a
href="#"
class="dropdown-toggle"
data-toggle="dropdown"
role="button"
aria-haspopup="true"
aria-expanded="false"
>
<span class="glyphicon glyphicon-edit"></span>
Edit
<span class="caret"></span
></a>
<ul id="editingList" class="dropdown-menu">
{% block actions %}
{% include "actions/objects/pathway.html" %}
{% endblock %}
</ul>
</li>
{% if pathway.setting.model.app_domain %}
<li class="dropdown">
<a
href="#"
class="dropdown-toggle"
data-toggle="dropdown"
role="button"
aria-haspopup="true"
aria-expanded="false"
>
<span class="glyphicon glyphicon-eye-open"></span>
View
<span class="caret"></span
></a>
<ul id="editingList" class="dropdown-menu">
<li>
<a class="button" id="app-domain-toggle-button">
<i
id="app-domain-toggle-button"
class="glyphicon glyphicon-eye-open"
></i>
App Domain View</a
>
</li>
</ul>
</li>
{% endif %}
</ul>
<ul class="nav navbar-nav navbar-right">
<li>
<a
role="button"
data-toggle="modal"
onclick="goFullscreen('vizdiv')"
>
<span class="glyphicon glyphicon-fullscreen"></span>
Fullscreen
</a>
</li>
<li>
{% if pathway.completed %}
<button
type="button"
class="btn btn-default navbar-btn"
data-toggle="popover"
id="status"
data-original-title=""
title=""
data-content="Pathway prediction complete."
>
<span class="glyphicon glyphicon-ok"></span>
</button>
{% elif pathway.failed %}
<button
type="button"
class="btn btn-default navbar-btn"
data-toggle="popover"
id="status"
data-original-title=""
title=""
data-content="Pathway prediction failed."
>
<span class="glyphicon glyphicon-remove"></span>
</button>
{% else %}
<button
type="button"
class="btn btn-default navbar-btn"
data-toggle="popover"
id="status"
data-original-title=""
title=""
data-content="Pathway prediction running."
>
<img height="20" src="{% static '/images/wait.gif' %}" />
</button>
{% endif %}
&nbsp;
</li>
<div class="space-y-2 p-4">
<!-- Header Section -->
<div class="card bg-base-100">
<div class="card-body">
<div class="flex items-center justify-between">
<h2 class="card-title text-2xl">{{ pathway.name }}</h2>
<div id="actionsButton" class="dropdown dropdown-end hidden">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-wrench"
>
<path
d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"
/>
</svg>
Actions
</div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2"
>
{% block actions %}
{% include "actions/objects/pathway.html" %}
{% endblock %}
</ul>
</div>
</nav>
</div>
</div>
</div>
<!-- Graphical Representation -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
Graphical Representation
</div>
<div class="collapse-content">
<div class="bg-base-100 mb-2 rounded-lg p-2">
<div class="navbar bg-base-100 rounded-lg">
<div class="flex-1">
{% if meta.can_edit %}
<div class="dropdown">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-edit"
>
<path
d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"
/>
<path
d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"
/>
</svg>
Edit
</div>
<ul
tabindex="0"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2"
>
{% include "actions/objects/pathway.html" %}
</ul>
</div>
{% endif %}
{% if pathway.setting.model.app_domain %}
<div class="dropdown">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-eye"
>
<path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" />
<circle cx="12" cy="12" r="3" />
</svg>
View
</div>
<ul
tabindex="0"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2"
>
<li>
<a id="app-domain-toggle-button" class="cursor-pointer">
<svg
id="app-domain-icon"
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-eye"
>
<path
d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"
/>
<circle cx="12" cy="12" r="3" />
</svg>
App Domain View
</a>
</li>
</ul>
</div>
{% endif %}
</div>
<div class="flex-none gap-2">
<button
class="btn btn-ghost btn-sm"
onclick="goFullscreen('vizdiv')"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-maximize"
>
<path
d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"
/>
</svg>
Fullscreen
</button>
</div>
</div>
</div>
<div id="vizdiv">
{% if pathway.completed %}
<div class="tooltip tooltip-bottom absolute top-4 right-4 z-10">
<div class="tooltip-content">Pathway prediction complete.</div>
<div id="status" class="flex items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-check"
>
<path d="M20 6 9 17l-5-5" />
</svg>
</div>
</div>
{% elif pathway.failed %}
<div class="tooltip tooltip-bottom absolute top-4 right-4 z-10">
<div class="tooltip-content">Pathway prediction failed.</div>
<div id="status" class="flex items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-x"
>
<path d="M18 6 6 18" />
<path d="M6 6l12 12" />
</svg>
</div>
</div>
{% else %}
<div class="tooltip tooltip-bottom absolute top-4 right-4 z-10">
<div class="tooltip-content">Pathway prediction running.</div>
<div id="status" class="flex items-center">
<div
id="status-loading-spinner"
style="width: 20px; height: 20px;"
></div>
</div>
</div>
{% endif %}
<svg id="pwsvg">
<defs>
<marker
@ -266,184 +335,137 @@
</defs>
<g id="zoomable"></g>
</svg>
<div id="tooltip" class="tooltip"></div>
</div>
</div>
<!-- Description -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="DescriptionLink"
data-toggle="collapse"
data-parent="#pathwayAccordion"
href="#Description"
>Description</a
>
</h4>
</div>
<div id="Description" class="panel-collapse in collapse">
<div class="panel-body list-group-item" id="DescriptionContent">
{{ pathway.description | safe }}
<div id="tooltip" class="tooltip-content"></div>
</div>
</div>
</div>
{% if pathway.aliases %}
<!-- Aliases -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="pathway-aliases-link"
data-toggle="collapse"
data-parent="#pathway-detail"
href="#pathway-aliases"
>Aliases</a
>
</h4>
</div>
<div id="pathway-aliases" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<!-- Description -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Description</div>
<div class="collapse-content">
<div id="DescriptionContent">{{ pathway.description | safe }}</div>
</div>
</div>
{% if pathway.aliases %}
<!-- Aliases -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Aliases</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for alias in pathway.aliases %}
<a class="list-group-item">{{ alias }}</a>
<li><a class="hover:bg-base-200">{{ alias }}</a></li>
{% endfor %}
</div>
</ul>
</div>
{% endif %}
</div>
{% endif %}
{% 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 in collapse">
<div class="panel-body list-group-item">
{% if pathway.scenarios.all %}
<!-- Scenarios -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Scenarios</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for s in pathway.scenarios.all %}
<a class="list-group-item" href="{{ s.url }}"
>{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a
>
<li>
<a href="{{ s.url }}" class="hover:bg-base-200">
{{ s.name }}
<span class="text-sm opacity-70">({{ s.package.name }})</span>
</a>
</li>
{% endfor %}
</div>
</ul>
</div>
{% endif %}
</div>
{% endif %}
{% if pathway.setting %}
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="pathwaySettingLink"
data-toggle="collapse"
data-parent="#pathwayAccordion"
href="#pathwaySetting"
>Setting</a
>
</h4>
</div>
<div id="pathwaySetting" class="panel-collapse collapse">
<div class="panel-body list-group-item" id="pathwaySettingContent">
<table class="table-bordered table-hover table">
<tr style="background-color: rgba(0, 0, 0, 0.08);">
<th scope="col" width="20%">Parameter</th>
<th scope="col" width="80%">Value</th>
</tr>
{% if pathway.setting %}
<!-- Setting -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" />
<div class="collapse-title text-xl font-medium">Setting</div>
<div class="collapse-content">
<div class="overflow-x-auto">
<table class="table-zebra table">
<thead>
<tr>
<th>Parameter</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{% if pathway.setting.model %}
<tr>
<td width="20%">Model</td>
<td width="80%">
<table
width="100%"
class="table-bordered table-hover table"
>
<tbody>
<tr>
<td colspan="2">
<li class="list-group-item">
<a href="{{ pathway.setting.model.url }}">
{{ pathway.setting.model.name|safe }}
</a>
</li>
</td>
</tr>
<tr>
<th width="20%">Model Parameter</th>
<th width="80%">Parameter Value</th>
</tr>
<tr>
<td width="20%">Threshold</td>
<td width="80%">
{{ pathway.setting.model_threshold }}
</td>
</tr>
</tbody>
</table>
<td>Model</td>
<td>
<div class="space-y-2">
<div>
<a
href="{{ pathway.setting.model.url }}"
class="link link-primary"
>
{{ pathway.setting.model.name }}
</a>
</div>
<div class="overflow-x-auto">
<table class="table-xs table">
<thead>
<tr>
<th>Model Parameter</th>
<th>Parameter Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Threshold</td>
<td>{{ pathway.setting.model_threshold }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</td>
</tr>
{% endif %}
{% if pathway.setting.rule_packages.all %}
<tr>
<td width="20%">Rule Packages</td>
<td width="80%">
<table
width="100%"
class="table-bordered table-hover table"
>
<tbody>
<tr>
<td colspan="2">
{% for p in pathway.setting.rule_packages.all %}
<li class="list-group-item">
<a href="{{ p.url }}"> {{ p.name|safe }} </a>
</li>
{% endfor %}
</td>
</tr>
</tbody>
</table>
<td>Rule Packages</td>
<td>
<ul class="menu bg-base-100 rounded-box">
{% for p in pathway.setting.rule_packages.all %}
<li>
<a href="{{ p.url }}" class="hover:bg-base-200"
>{{ p.name }}</a
>
</li>
{% endfor %}
</ul>
</td>
</tr>
{% endif %}
<tr>
<td>
<p>Max Nodes</p>
</td>
<td>
<p>{{ pathway.setting.max_nodes }}</p>
</td>
<td>Max Nodes</td>
<td>{{ pathway.setting.max_nodes }}</td>
</tr>
<tr>
<td>
<p>Max Depth</p>
</td>
<td>
<p>{{ pathway.setting.max_depth }}</p>
</td>
<td>Max Depth</td>
<td>{{ pathway.setting.max_depth }}</td>
</tr>
</tbody>
</table>
</div>
</div>
{% endif %}
</div>
</div>
{% endif %}
</div>
{# prettier-ignore-start #}
{# FIXME: This is a hack to get the pathway data into the JavaScript code. #}
<script>
// Global switch for app domain view
var appDomainViewEnabled = false;
@ -461,15 +483,17 @@
return text.replace(/\[\s*(http[^\]|]+)\s*\|\s*([^\]]+)\s*\]/g, '<a target="parent" href="$1">$2</a>');
}
pathway = {{ pathway.d3_json | safe }};
$(function () {
var pathway = {{ pathway.d3_json | safe }};
$('#status').popover({
trigger: 'manual',
placement: 'bottom',
html: true
});
document.addEventListener('DOMContentLoaded', function() {
// Initialize loading spinner if pathway is running
if (pathway.status === 'running') {
const spinnerContainer = document.getElementById('status-loading-spinner');
if (spinnerContainer) {
showLoadingSpinner(spinnerContainer);
}
}
// If prediction is still running, regularly check status
if (pathway.status === 'running') {
@ -481,29 +505,32 @@
const data = await response.json();
if (data.modified > last_modified) {
var msg = 'Prediction '
var btn = '<button type="button" onclick="location.reload()" class="btn btn-primary" id="reloadBtn">Reload page</button>'
var msg = 'Prediction ';
var btn = '<button type="button" onclick="location.reload()" class="btn btn-primary btn-sm mt-2" id="reloadBtn">Reload page</button>';
if (data.status === "running") {
msg += 'is still running. But the Pathway was updated.<br>' + btn;
} else if (data.status === "completed") {
msg += 'is completed. Reload the page to see the updated Pathway.<br>' + btn;
} else if (data.status === "failed") {
msg += 'failed. Reload the page to see the current shape<br>' + btn;
msg += 'failed. Reload the page to see the current shape.<br>' + btn;
}
$('#status').attr(
'data-content', msg
).popover('show');
showStatusPopover(msg);
}
if (data.status === "completed" || data.status === "failed") {
$('#status img').remove();
const statusBtn = document.getElementById('status');
const tooltipContent = statusBtn.parentElement.querySelector('.tooltip-content');
const spinner = statusBtn.querySelector('#status-loading-spinner');
if (spinner) spinner.remove();
if (data.status === "completed") {
$('#status').append('<span class="glyphicon glyphicon-ok"></span>')
statusBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check"><path d="M20 6 9 17l-5-5"/></svg>`;
tooltipContent.textContent = 'Pathway prediction complete.';
} else {
$('#status').append('<span class="glyphicon glyphicon-remove"></span>')
statusBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x"><path d="M18 6 6 18"/><path d="M6 6l12 12"/></svg>`;
tooltipContent.textContent = 'Pathway prediction failed.';
}
clearInterval(pollInterval);
}
@ -515,54 +542,68 @@
}
draw(pathway, 'vizdiv');
// TODO fix somewhere else...
var newDesc = transformReferences($('#DescriptionContent')[0].innerText);
$('#DescriptionContent').html(newDesc);
// Transform references in description
const descContent = document.getElementById('DescriptionContent');
if (descContent) {
const newDesc = transformReferences(descContent.innerText);
descContent.innerHTML = newDesc;
}
$('#app-domain-toggle-button').on('click', function () {
// glyphicon glyphicon-eye-close
// glyphicon glyphicon-eye-open
appDomainViewEnabled = !appDomainViewEnabled;
// App domain toggle
const appDomainBtn = document.getElementById('app-domain-toggle-button');
if (appDomainBtn) {
appDomainBtn.addEventListener('click', function() {
appDomainViewEnabled = !appDomainViewEnabled;
const icon = document.getElementById('app-domain-icon');
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);
if (appDomainViewEnabled) {
// Change to eye-off icon
icon.innerHTML = '<path d="M9.88 9.88a3 3 0 1 0 4.24 4.24"/><path d="M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68"/><path d="M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61"/><line x1="2" x2="22" y1="2" y2="22"/>';
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);
});
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);
});
}
})
});
} else {
// Change back to eye icon
icon.innerHTML = '<path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/>';
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);
});
}
});
}
// Show actions button if there are actions
const actionsButton = document.getElementById("actionsButton");
const actionsList = actionsButton?.querySelector("ul");
if (actionsList && actionsList.children.length > 0) {
actionsButton?.classList.remove("hidden");
}
});
</script>

View File

@ -1,4 +1,4 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% block content %}
@ -11,343 +11,257 @@
{% include "modals/objects/generic_delete_modal.html" %}
{% endblock action_modals %}
<div class="panel-group" id="reaction-detail">
<div class="panel panel-default">
<div
class="panel-heading"
id="headingPanel"
style="font-size:2rem;height: 46px"
>
{{ reaction.name|safe }}
<div
id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"
>
<a
href="#"
class="dropdown-toggle"
data-toggle="dropdown"
role="button"
aria-haspopup="true"
aria-expanded="false"
><span class="glyphicon glyphicon-wrench"></span> Actions
<span class="caret"></span><span style="padding-right:1em"></span
></a>
<ul id="actionsList" class="dropdown-menu">
{% block actions %}
{% include "actions/objects/reaction.html" %}
{% endblock %}
<div class="space-y-2 p-4">
<!-- Header Section -->
<div class="card bg-base-100">
<div class="card-body">
<div class="flex items-center justify-between">
<h2 class="card-title text-2xl">{{ reaction.name }}</h2>
<div id="actionsButton" class="dropdown dropdown-end hidden">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-wrench"
>
<path
d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"
/>
</svg>
Actions
</div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2"
>
{% block actions %}
{% include "actions/objects/reaction.html" %}
{% endblock %}
</ul>
</div>
</div>
</div>
</div>
<!-- Description -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Description</div>
<div class="collapse-content">{{ reaction.description }}</div>
</div>
{% if reaction.aliases %}
<!-- Aliases -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Aliases</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for alias in reaction.aliases %}
<li><a class="hover:bg-base-200">{{ alias }}</a></li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
<!-- Description -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="reaction-desc-link"
data-toggle="collapse"
data-parent="#reaction-detail"
href="#reaction-desc"
>Description</a
>
</h4>
</div>
<div id="reaction-desc" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{{ reaction.description|safe }}
</div>
<!-- Image Representation -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Image Representation</div>
<div class="collapse-content">
<div class="flex justify-center">{{ reaction.as_svg|safe }}</div>
</div>
</div>
{% if reaction.aliases %}
<!-- Aliases -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="reaction-aliases-link"
data-toggle="collapse"
data-parent="#reaction-detail"
href="#reaction-aliases"
>Aliases</a
>
</h4>
</div>
<div id="reaction-aliases" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{% for alias in reaction.aliases %}
<a class="list-group-item">{{ alias }}</a>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Image -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="reaction-image-link"
data-toggle="collapse"
data-parent="#reaction-detail"
href="#reaction-image"
>Image Representation</a
>
</h4>
</div>
<div id="reaction-image" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<div id="image-div" align="center">{{ reaction.as_svg|safe }}</div>
</div>
</div>
<!-- Reaction Description -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="reaction-description-link"
data-toggle="collapse"
data-parent="#reaction-description-detail"
href="#reaction-description-smiles"
>Reaction Description</a
>
</h4>
</div>
<div id="reaction-description-smiles" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<!-- Reaction Description -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Reaction Description</div>
<div class="collapse-content">
<div class="flex flex-wrap items-center justify-center gap-4">
{% for educt in reaction.educts.all %}
<a class="btn btn-default" href="{{ educt.url }}"
>{{ educt.name|safe }}</a
<a href="{{ educt.url }}" class="btn btn-outline btn-sm"
>{{ educt.name }}</a
>
{% endfor %}
<span
class="glyphicon glyphicon-arrow-right"
style="margin-left:5em;margin-right:5em;"
aria-hidden="true"
></span>
{% for product in reaction.products.all %}
<a class="btn btn-default" href="{{ product.url }}"
>{{ product.name|safe }}</a
>
{% endfor %}
</div>
</div>
<!-- SMIRKS -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="reaction-smirks-link"
data-toggle="collapse"
data-parent="#reaction-detail"
href="#reaction-smirks"
>SMIRKS Representation</a
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-arrow-right"
>
</h4>
</div>
<div id="reaction-smirks" class="panel-collapse in collapse">
<div class="panel-body list-group-item">{{ reaction.smirks }}</div>
</div>
{% if reaction.rules.all %}
<!-- Rules -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="reaction-rules-link"
data-toggle="collapse"
data-parent="#reaction-detail"
href="#reaction-rules"
>Rules</a
<path d="M5 12h14" />
<path d="m12 5 7 7-7 7" />
</svg>
{% for product in reaction.products.all %}
<a href="{{ product.url }}" class="btn btn-outline btn-sm"
>{{ product.name }}</a
>
</h4>
{% endfor %}
</div>
<div id="reaction-rules" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
</div>
</div>
<!-- SMIRKS Representation -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
SMIRKS Representation
</div>
<div class="collapse-content">{{ reaction.smirks }}</div>
</div>
<!-- Rules -->
{% if reaction.rules.all %}
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Rules</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for r in reaction.rules.all %}
<a class="list-group-item" href="{{ r.url }}"
>{{ r.name|safe }}</a
>
<li>
<a href="{{ r.url }}" class="hover:bg-base-200">{{ r.name }}</a>
</li>
{% endfor %}
</div>
</ul>
</div>
{% endif %}
</div>
{% endif %}
{% if reaction.get_related_enzymes %}
<!-- EC Numbers -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="rule-ec-numbers-link"
data-toggle="collapse"
data-parent="#rule-detail"
href="#rule-ec-numbers"
>EC Numbers</a
>
</h4>
</div>
<div id="rule-ec-numbers" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<!-- EC Numbers -->
{% if reaction.get_related_enzymes %}
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">EC Numbers</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for e in reaction.get_related_enzymes %}
<a
class="list-group-item"
href="http://www.brenda-enzymes.org/enzyme.php?ecno={{ e.ec_number }}"
>{{ e.name }}</a
>
<li>
<a
href="http://www.brenda-enzymes.org/enzyme.php?ecno={{ e.ec_number }}"
class="hover:bg-base-200"
>{{ e.name }}</a
>
</li>
{% endfor %}
</div>
</ul>
</div>
{% endif %}
</div>
{% endif %}
{% if reaction.related_pathways %}
<!-- Pathways -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="reaction-pathway-link"
data-toggle="collapse"
data-parent="#reaction-detail"
href="#reaction-pathway"
>Pathways</a
>
</h4>
</div>
<div id="reaction-pathway" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<!-- Pathways -->
{% if reaction.related_pathways %}
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Pathways</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for r in reaction.related_pathways %}
<a class="list-group-item" href="{{ r.url }}"
>{{ r.name|safe }}</a
>
<li>
<a href="{{ r.url }}" class="hover:bg-base-200"
>{{ r.name }} <i>({{ r.package.name }})</i></a
>
</li>
{% endfor %}
</div>
</ul>
</div>
{% endif %}
</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 in collapse">
<div class="panel-body list-group-item">
<!-- Scenarios -->
{% if reaction.scenarios.all %}
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Scenarios</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for s in reaction.scenarios.all %}
<a class="list-group-item" href="{{ s.url }}"
>{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a
>
<li>
<a href="{{ s.url }}" class="hover:bg-base-200"
>{{ s.name }} <i>({{ s.package.name }})</i></a
>
</li>
{% endfor %}
</div>
</ul>
</div>
{% endif %}
</div>
{% endif %}
<!-- External Identifiers -->
{% if reaction.get_external_identifiers %}
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="reaction-external-identifier-link"
data-toggle="collapse"
data-parent="#reaction-detail"
href="#reaction-external-identifier"
>External Identifier</a
>
</h4>
<!-- External Identifiers -->
{% if reaction.get_external_identifiers %}
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
External Identifier
</div>
<div
id="reaction-external-identifier"
class="panel-collapse in collapse"
>
<div class="panel-body list-group-item">
<div class="collapse-content">
<div class="space-y-2">
{% 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 in collapse"
>
{% for eid in reaction.get_rhea_identifiers %}
<a class="list-group-item" href="{{ eid.external_url }}"
>{{ eid.identifier_value }}</a
>
{% endfor %}
<div class="collapse-arrow bg-base-100 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-lg font-medium">Rhea</div>
<div class="collapse-content">
<ul class="menu bg-base-200 rounded-box">
{% for eid in reaction.get_rhea_identifiers %}
<li>
<a
href="{{ eid.external_url }}"
class="hover:bg-base-300"
>{{ eid.identifier_value }}</a
>
</li>
{% endfor %}
</ul>
</div>
</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 in collapse"
>
{% 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 class="collapse-arrow bg-base-100 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-lg font-medium">UniProt</div>
<div class="collapse-content">
<ul class="menu bg-base-200 rounded-box">
{% for eid in reaction.get_uniprot_identifiers %}
<li>
<a
href="{{ eid.external_url }}"
class="hover:bg-base-300"
>10 SwissProt entries ({{ eid.identifier_value }})</a
>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>
</div>
{% endif %}
</div>
</div>
{% endif %}
</div>
<script>
// Show actions button if there are actions
document.addEventListener("DOMContentLoaded", function () {
const actionsButton = document.getElementById("actionsButton");
const actionsList = actionsButton?.querySelector("ul");
if (actionsList && actionsList.children.length > 0) {
actionsButton?.classList.remove("hidden");
}
});
</script>
{% endblock content %}

View File

@ -1,4 +1,4 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% block content %}
@ -7,115 +7,169 @@
{% include "modals/objects/update_scenario_additional_information_modal.html" %}
{% include "modals/objects/generic_delete_modal.html" %}
{% endblock action_modals %}
<div class="panel-group" id="scenario-detail">
<div class="panel panel-default">
<div
class="panel-heading"
id="headingPanel"
style="font-size:2rem;height: 46px"
>
{{ scenario.name|safe }}
<div
id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"
>
<a
href="#"
class="dropdown-toggle"
data-toggle="dropdown"
role="button"
aria-haspopup="true"
aria-expanded="false"
><span class="glyphicon glyphicon-wrench"></span> Actions
<span class="caret"></span><span style="padding-right:1em"></span
></a>
<ul id="actionsList" class="dropdown-menu">
{% block actions %}
{% include "actions/objects/scenario.html" %}
{% endblock %}
</ul>
<div class="space-y-2 p-4">
<!-- Header Section -->
<div class="card bg-base-100">
<div class="card-body">
<div class="flex items-center justify-between">
<h2 class="card-title text-2xl">{{ scenario.name }}</h2>
<div id="actionsButton" class="dropdown dropdown-end hidden">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-wrench"
>
<path
d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"
/>
</svg>
Actions
</div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2"
>
{% block actions %}
{% include "actions/objects/scenario.html" %}
{% endblock %}
</ul>
</div>
</div>
</div>
</div>
<!-- Description -->
<div class="card bg-base-100">
<div class="card-body">
<h3 class="card-title mb-2 text-lg">Description</h3>
<p>{{ scenario.description }}</p>
<p class="mt-2"><strong>Type:</strong> {{ scenario.scenario_type }}</p>
<p><strong>Reported:</strong> {{ scenario.scenario_date }}</p>
</div>
</div>
<!-- Additional Information Table -->
<div class="card bg-base-100">
<div class="card-body">
<h3 class="card-title mb-4 text-lg">Additional Information</h3>
<div class="overflow-x-auto">
<table class="table-zebra table">
<thead>
<tr>
<th>Property</th>
<th>Value</th>
<th>Unit</th>
{% if meta.can_edit %}
<th>Remove</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for ai in scenario.get_additional_information %}
<tr>
<td>{{ ai.property_name|safe }}</td>
<td>{{ ai.property_data|safe }}</td>
<td>{{ ai.property_unit|safe }}</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 btn-sm btn-ghost">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-minus"
>
<path d="M5 12h14" />
</svg>
</button>
</form>
</td>
{% endif %}
</tr>
{% 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 btn-sm btn-ghost">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-trash"
>
<path d="M3 6h18" />
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" />
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" />
</svg>
</button>
</form>
</td>
</tr>
{% endif %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">Description</div>
<div class="panel-body">
{{ scenario.description|safe }}
<br />
{{ scenario.scenario_type }}
<br />
Reported {{ scenario.scenario_date }}
</div>
</div>
<div class="table-responsive">
<table
id="scenario-table"
class="table-bordered table-striped table-hover table"
>
<tbody>
<tr>
<th>Property</th>
<th>Value</th>
<th>Unit</th>
{% if meta.can_edit %}
<th>Remove</th>
{% endif %}
</tr>
{% for ai in scenario.get_additional_information %}
<tr>
<td>{{ ai.property_name|safe }}</td>
<td>{{ ai.property_data|safe }}</td>
<td>{{ ai.property_unit|safe }}</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>
{% 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>
</table>
</div>
<script>
// Show actions button if there are actions
document.addEventListener("DOMContentLoaded", function () {
const actionsButton = document.getElementById("actionsButton");
const actionsList = actionsButton?.querySelector("ul");
if (actionsList && actionsList.children.length > 0) {
actionsButton?.classList.remove("hidden");
}
});
</script>
{% endblock content %}

View File

@ -1,4 +1,4 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% block content %}
@ -10,359 +10,243 @@
{% include "modals/objects/generic_delete_modal.html" %}
{% endblock action_modals %}
<div class="panel-group" id="rule-detail">
<div class="panel panel-default">
<div
class="panel-heading"
id="headingPanel"
style="font-size:2rem;height: 46px"
>
{{ rule.name|safe }}
<div
id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"
>
<a
href="#"
class="dropdown-toggle"
data-toggle="dropdown"
role="button"
aria-haspopup="true"
aria-expanded="false"
><span class="glyphicon glyphicon-wrench"></span> Actions
<span class="caret"></span><span style="padding-right:1em"></span
></a>
<ul id="actionsList" class="dropdown-menu">
{% block actions %}
{% include "actions/objects/rule.html" %}
{% endblock %}
<div class="space-y-2 p-4">
<!-- Header Section -->
<div class="card bg-base-100">
<div class="card-body">
<div class="flex items-center justify-between">
<h2 class="card-title text-2xl">{{ rule.name }}</h2>
<div id="actionsButton" class="dropdown dropdown-end hidden">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-wrench"
>
<path
d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"
/>
</svg>
Actions
</div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2"
>
{% block actions %}
{% include "actions/objects/rule.html" %}
{% endblock %}
</ul>
</div>
</div>
<p class="mt-2">{{ rule.description }}</p>
</div>
</div>
{% if rule.aliases %}
<!-- Aliases -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Aliases</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for alias in rule.aliases %}
<li><a class="hover:bg-base-200">{{ alias }}</a></li>
{% endfor %}
</ul>
</div>
</div>
<div class="panel-body">
<p>{{ rule.description|safe }}</p>
</div>
{% endif %}
{% if rule.aliases %}
<!-- Aliases -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="rule-aliases-link"
data-toggle="collapse"
data-parent="#rule-detail"
href="#rule-aliases"
>Aliases</a
>
</h4>
</div>
<div id="rule-aliases" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{% for alias in rule.aliases %}
<a class="list-group-item">{{ alias }}</a>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Image Representation -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Image Representation</div>
<div class="collapse-content">
<div class="flex justify-center">{{ rule.as_svg|safe }}</div>
</div>
</div>
<!-- Representation -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="rule-image-link"
data-toggle="collapse"
data-parent="#rule-detail"
href="#rule-image"
>Image Representation</a
>
</h4>
<!-- SMIRKS -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
SMIRKS Representation
</div>
<div id="rule-image" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<div id="image-div" align="center">{{ rule.as_svg|safe }}</div>
</div>
<div class="collapse-content">
<p>{{ rule.smirks }}</p>
</div>
</div>
<!-- SMIRKS -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="rule-smirks-link"
data-toggle="collapse"
data-parent="#rule-detail"
href="#rule-smirks"
>SMIRKS Representation</a
>
</h4>
</div>
<div id="rule-smirks" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<p>{{ rule.smirks }}</p>
</div>
<!-- Reactants SMARTS -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Reactant SMARTS</div>
<div class="collapse-content">
<p>{{ rule.reactants_smarts }}</p>
</div>
</div>
<!-- Reactants SMARTS -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="rule-reactants-smarts-link"
data-toggle="collapse"
data-parent="#rule-detail"
href="#rule-reactants-smarts"
>Reactant SMARTS</a
>
</h4>
</div>
<div id="rule-reactants-smarts" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<p>{{ rule.reactants_smarts }}</p>
<!-- Reactant Filter SMARTS -->
{% if rule.reactant_filter_smarts %}
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
Reactant Filter SMARTS
</div>
<div class="collapse-content">
<p>{{ rule.reactant_filter_smarts }}</p>
</div>
</div>
{% endif %}
<!-- Reactant Filter SMARTS -->
{% if rule.reactant_filter_smarts %}
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="rule-reactant-filter-smarts-link"
data-toggle="collapse"
data-parent="#rule-detail"
href="#rule-reactant-filter-smarts"
>Reactant Filter SMARTS</a
>
</h4>
</div>
<div
id="rule-reactant-filter-smarts"
class="panel-collapse in collapse"
>
<div class="panel-body list-group-item">
<p>{{ rule.reactant_filter_smarts }}</p>
</div>
</div>
{% endif %}
<!-- Products SMARTS -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="rule-products-smarts-link"
data-toggle="collapse"
data-parent="#rule-detail"
href="#rule-products-smarts"
>Reactant SMARTS</a
>
</h4>
<!-- Products SMARTS -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Products SMARTS</div>
<div class="collapse-content">
<p>{{ rule.products_smarts }}</p>
</div>
<div id="rule-products-smarts" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<p>{{ rule.products_smarts }}</p>
</div>
<!-- Product Filter SMARTS -->
{% if rule.product_filter_smarts %}
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
Product Filter SMARTS
</div>
<div class="collapse-content">
<p>{{ rule.product_filter_smarts }}</p>
</div>
</div>
{% endif %}
<!-- Product Filter SMARTS -->
{% if rule.product_filter_smarts %}
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="rule-product-filter-smarts-link"
data-toggle="collapse"
data-parent="#rule-detail"
href="#rule-product-filter-smarts"
>Product Filter SMARTS</a
>
</h4>
<!-- Included in Composite Rules -->
{% if rule.parallelrule_set.all %}
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
Included in Composite Rules
</div>
<div id="rule-product-filter-smarts" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<p>{{ rule.product_filter_smarts }}</p>
</div>
</div>
{% endif %}
<!-- Included in Composite Rules -->
{% if rule.parallelrule_set.all %}
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="rule-composite-rule-link"
data-toggle="collapse"
data-parent="#rule-detail"
href="#rule-composite-rule"
>Included in Composite Rules</a
>
</h4>
</div>
<div id="rule-composite-rule" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for cr in rule.parallelrule_set.all %}
<a class="list-group-item" href="{{ cr.url }}"
>{{ cr.name|safe }}</a
>
<li>
<a href="{{ cr.url }}" class="hover:bg-base-200"
>{{ cr.name }}</a
>
</li>
{% endfor %}
</div>
</ul>
</div>
{% endif %}
</div>
{% endif %}
<!-- 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 in collapse">
<div class="panel-body list-group-item">
<!-- Scenarios -->
{% if rule.scenarios.all %}
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Scenarios</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for s in rule.scenarios.all %}
<a class="list-group-item" href="{{ s.url }}"
>{{ s.name|safe }}</a
>
<li>
<a href="{{ s.url }}" class="hover:bg-base-200">{{ s.name }}</a>
</li>
{% endfor %}
</div>
</ul>
</div>
{% endif %}
</div>
{% endif %}
<!-- Reactions -->
{% if rule.related_reactions %}
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="rule-reaction-link"
data-toggle="collapse"
data-parent="#rule-detail"
href="#rule-reaction"
>Reactions</a
>
</h4>
</div>
<div id="rule-reaction" class="panel-collapse collapse">
<div class="panel-body list-group-item">
<!-- Reactions -->
{% if rule.related_reactions %}
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" />
<div class="collapse-title text-xl font-medium">Reactions</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for r in rule.related_reactions %}
<a class="list-group-item" href="{{ r.url }}"
>{{ r.name|safe }}</a
>
<li>
<a href="{{ r.url }}" class="hover:bg-base-200">{{ r.name }}</a>
</li>
{% endfor %}
</div>
</ul>
</div>
{% endif %}
</div>
{% endif %}
<!-- Pathways -->
{% if rule.related_pathways %}
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="rule-pathway-link"
data-toggle="collapse"
data-parent="#rule-detail"
href="#rule-pathway"
>Pathways</a
>
</h4>
</div>
<div id="rule-pathway" class="panel-collapse collapse">
<div class="panel-body list-group-item">
<!-- Pathways -->
{% if rule.related_pathways %}
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" />
<div class="collapse-title text-xl font-medium">Pathways</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for r in rule.related_pathways %}
<a class="list-group-item" href="{{ r.url }}"
>{{ r.name|safe }}</a
>
<li>
<a href="{{ r.url }}" class="hover:bg-base-200">{{ r.name }}</a>
</li>
{% endfor %}
</div>
</ul>
</div>
{% endif %}
</div>
{% endif %}
{% if rule.enzymelinks %}
<!-- EC Numbers -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="rule-ec-numbers-link"
data-toggle="collapse"
data-parent="#rule-detail"
href="#rule-ec-numbers"
>EC Numbers</a
>
</h4>
</div>
<div id="rule-ec-numbers" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{% if rule.enzymelinks %}
<!-- EC Numbers -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">EC Numbers</div>
<div class="collapse-content">
<div class="space-y-2">
{% for k, v in rule.get_grouped_enzymelinks.items %}
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="{{ k|slugify }}_Link"
data-toggle="collapse"
data-parent="#{{ k|slugify }}_Accordion"
href="#{{ k|slugify }}"
>
{{ k }}
</a>
</h4>
</div>
<div id="{{ k|slugify }}" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{% for enzyme in v %}
<a class="list-group-item" href="{{ enzyme.url }}">
{{ enzyme.ec_number }}
<div style="position:absolute;bottom:10px;left:100px;">
{{ enzyme.name }}
</div>
<div style="float:right;">
{{ enzyme.linking_method }}
</div>
</a>
{% endfor %}
<div class="collapse-arrow bg-base-100 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-lg font-medium">{{ k }}</div>
<div class="collapse-content">
<ul class="menu bg-base-200 rounded-box">
{% for enzyme in v %}
<li>
<a href="{{ enzyme.url }}" class="hover:bg-base-300">
<div class="flex w-full items-center justify-between">
<span>{{ enzyme.ec_number }}</span>
<span class="text-sm opacity-70"
>{{ enzyme.linking_method }}</span
>
</div>
<div class="text-sm opacity-60">
{{ enzyme.name }}
</div>
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
</div>
{% endif %}
</div>
<script>
// Show actions button if there are actions
document.addEventListener("DOMContentLoaded", function () {
const actionsButton = document.getElementById("actionsButton");
const actionsList = actionsButton?.querySelector("ul");
if (actionsList && actionsList.children.length > 0) {
actionsButton?.classList.remove("hidden");
}
});
</script>
{% endblock content %}

View File

@ -1,4 +1,4 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% block content %}
@ -10,187 +10,161 @@
{% include "modals/objects/generic_delete_modal.html" %}
{% endblock action_modals %}
<div class="panel-group" id="user-detail">
<div class="panel panel-default">
<div
class="panel-heading"
id="headingPanel"
style="font-size:2rem;height: 46px"
>
{{ user.username }}
<div
id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"
>
<a
href="#"
class="dropdown-toggle"
data-toggle="dropdown"
role="button"
aria-haspopup="true"
aria-expanded="false"
><span class="glyphicon glyphicon-wrench"></span> Actions
<span class="caret"></span><span style="padding-right:1em"></span
></a>
<ul id="actionsList" class="dropdown-menu">
{% block actions %}
{% include "actions/objects/user.html" %}
{% endblock %}
</ul>
<div class="space-y-2 p-4">
<!-- Header Section -->
<div class="card bg-base-100">
<div class="card-body">
<div class="flex items-center justify-between">
<h2 class="card-title text-2xl">{{ user.username }}</h2>
<div id="actionsButton" class="dropdown dropdown-end hidden">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-wrench"
>
<path
d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"
/>
</svg>
Actions
</div>
<ul
tabindex="-1"
class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2"
>
{% block actions %}
{% include "actions/objects/user.html" %}
{% endblock %}
</ul>
</div>
</div>
</div>
<div class="panel-body">
<p>
<p class="mt-2">
On this page you can modify your account or set preferences such as
prediction settings.
</p>
</div>
</div>
<!-- Default Package -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="default-package-link"
data-toggle="collapse"
data-parent="#user-detail"
href="#default-package"
>Default Package</a
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Default Package</div>
<div class="collapse-content">
<a href="{{ user.default_package.url }}" class="link link-primary"
>{{ user.default_package.name }}</a
>
</h4>
</div>
<div id="default-package" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<li class="list-group-item">
<a href="{{ user.default_package.url }}">
{{ user.default_package.name|safe }}</a
>
</li>
</div>
</div>
<!-- Groups -->
{% if meta.available_groups|length > 0 %}
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="group-links"
data-toggle="collapse"
data-parent="#user-detail"
href="#groups"
>Groups</a
>
</h4>
</div>
<div id="groups" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
{% for g in meta.available_groups %}
<li class="list-group-item">
<a href="{{ g.url }}"> {{ g.name|safe }}</a>
</li>
{% endfor %}
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">Groups</div>
<div class="collapse-content">
<ul class="menu bg-base-100 rounded-box">
{% for g in meta.available_groups %}
<li>
<a href="{{ g.url }}" class="hover:bg-base-200">{{ g.name }}</a>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
<!-- Current Prediction Settings -->
<div
class="panel panel-default panel-heading list-group-item"
style="background-color:silver"
>
<h4 class="panel-title">
<a
id="current-prediction-setting-links"
data-toggle="collapse"
data-parent="#user-detail"
href="#current-prediction-setting"
>Current Prediction Setting</a
>
</h4>
</div>
<div id="current-prediction-setting" class="panel-collapse in collapse">
<div class="panel-body list-group-item">
<table class="table-bordered table-hover table">
<tr style="background-color: rgba(0, 0, 0, 0.08);">
<th scope="col" width="20%">Parameter</th>
<th scope="col" width="80%">Value</th>
</tr>
<tbody>
{% if user.default_setting.model %}
<!-- Current Prediction Setting -->
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
Current Prediction Setting
</div>
<div class="collapse-content">
<div class="overflow-x-auto">
<table class="table-zebra table">
<thead>
<tr>
<td width="20%">Model</td>
<td width="80%">
<table width="100%" class="table-bordered table-hover table">
<tbody>
<tr>
<td colspan="2">
<li class="list-group-item">
<a href="{{ user.default_setting.model.url }}">
{{ user.default_setting.model.name|safe }}
</a>
</li>
</td>
</tr>
<tr>
<th width="20%">Model Parameter</th>
<th width="80%">Parameter Value</th>
</tr>
<tr>
<td width="20%">Threshold</td>
<td width="80%">
{{ user.default_setting.model_threshold }}
</td>
</tr>
</tbody>
</table>
</td>
<th>Parameter</th>
<th>Value</th>
</tr>
{% endif %}
{% if user.default_setting.rule_packages.all %}
</thead>
<tbody>
{% if user.default_setting.model %}
<tr>
<td>Model</td>
<td>
<div class="space-y-2">
<a
href="{{ user.default_setting.model.url }}"
class="link link-primary"
>
{{ user.default_setting.model.name }}
</a>
<table class="table-xs table">
<thead>
<tr>
<th>Model Parameter</th>
<th>Parameter Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Threshold</td>
<td>{{ user.default_setting.model_threshold }}</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
{% endif %}
{% if user.default_setting.rule_packages.all %}
<tr>
<td>Rule Packages</td>
<td>
<ul class="menu bg-base-200 rounded-box">
{% for p in user.default_setting.rule_packages.all %}
<li>
<a href="{{ p.url }}" class="hover:bg-base-300"
>{{ p.name }}</a
>
</li>
{% endfor %}
</ul>
</td>
</tr>
{% endif %}
<tr>
<td width="20%">Rule Packages</td>
<td width="80%">
<table width="100%" class="table-bordered table-hover table">
<tbody>
<tr>
<td colspan="2">
{% for p in user.default_setting.rule_packages.all %}
<li class="list-group-item">
<a href="{{ p.url }}"> {{ p.name|safe }} </a>
</li>
{% endfor %}
</td>
</tr>
</tbody>
</table>
</td>
<td>Max Nodes</td>
<td>{{ user.default_setting.max_nodes }}</td>
</tr>
{% endif %}
<tr>
<td>
<p>Max Nodes</p>
</td>
<td>
<p>{{ user.default_setting.max_nodes }}</p>
</td>
</tr>
<tr>
<td>
<p>Max Depth</p>
</td>
<td>
<p>{{ user.default_setting.max_depth }}</p>
</td>
</tr>
</tbody>
</table>
<tr>
<td>Max Depth</td>
<td>{{ user.default_setting.max_depth }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
// Show actions button if there are actions
document.addEventListener("DOMContentLoaded", function () {
const actionsButton = document.getElementById("actionsButton");
const actionsList = actionsButton?.querySelector("ul");
if (actionsList && actionsList.children.length > 0) {
actionsButton?.classList.remove("hidden");
}
});
</script>
{% endblock content %}

View File

@ -158,11 +158,12 @@
}
function predictKetcherToTextInput() {
$("#predict-smiles").val(this.ketcher.getSmiles());
document.getElementById("predict-smiles").value = this.ketcher.getSmiles();
}
$(function () {
$("#predict-ketcher").on("load", function () {
document.addEventListener("DOMContentLoaded", function () {
const predictKetcher = document.getElementById("predict-ketcher");
predictKetcher.addEventListener("load", function () {
const checkKetcherReady = () => {
const win = this.contentWindow;
if (win.ketcher && "editor" in win.ketcher) {
@ -181,14 +182,16 @@
checkKetcherReady();
});
$("#predict-submit-button").on("click", function (e) {
const submitButton = document.getElementById("predict-submit-button");
submitButton.addEventListener("click", function (e) {
e.preventDefault();
const button = $(this);
button.prop("disabled", true);
button.text("Predicting...");
const button = this;
button.disabled = true;
button.textContent = "Predicting...";
// Get SMILES from either input or Ketcher
let smiles = $("#predict-smiles").val().trim();
const smilesInput = document.getElementById("predict-smiles");
let smiles = smilesInput.value.trim();
// If SMILES input is empty, try to get from Ketcher
if (!smiles) {
@ -197,13 +200,13 @@
try {
smiles = ketcher.getSmiles().trim();
if (smiles) {
$("#predict-smiles").val(smiles);
smilesInput.value = smiles;
}
} catch (err) {
console.error("Failed to get SMILES from Ketcher:", err);
alert("Unable to extract structure from the drawing editor. Please enter a SMILES string instead.");
button.prop("disabled", false);
button.text("Predict");
button.disabled = false;
button.textContent = "Predict";
return;
}
}
@ -212,13 +215,13 @@
// Basic validation
if (!smiles) {
alert("Please enter a SMILES string or draw a structure.");
button.prop("disabled", false);
button.text("Predict");
button.disabled = false;
button.textContent = "Predict";
return;
}
// Submit form
$("#predict_form").submit();
document.getElementById("predict_form").submit();
});
});
</script>