[Feature] Server pagination implementation (#243)

## Major Changes
- Implement a REST style API app in epapi
- Currently implements a GET method for all entity types in the browse menu (both package level and global)
- Provides paginated results per default with query style filtering for reviewed vs unreviewed.
- Provides new paginated templates with thin wrappers per entity types for easier maintainability
- Implements e2e tests for the API

## Minor changes
- Added more comprehensive gitignore to cover coverage reports and other test/node.js etc. data.
- Add additional CI file for API tests that only gets triggered on API relevant changes.

## ⚠️ Currently only works with session-based authentication. Token based will be added in new PR.

Co-authored-by: Tim Lorsbach <tim@lorsba.ch>
Co-authored-by: jebus <lorsbach@envipath.com>
Reviewed-on: enviPath/enviPy#243
Co-authored-by: Tobias O <tobias.olenyi@envipath.com>
Co-committed-by: Tobias O <tobias.olenyi@envipath.com>
This commit is contained in:
2025-12-15 11:34:53 +13:00
committed by jebus
parent d2d475b990
commit 8adb93012a
59 changed files with 3101 additions and 620 deletions

View File

@ -4,7 +4,8 @@
{# Serialize objects data for Alpine pagination #}
{# prettier-ignore-start #}
{# FIXME: This is a hack to get the objects data into the JavaScript code. #}
{# FIXME: This is a hack to get the objects data into the JavaScript code. #}
{% if object_type != 'scenario' %}
<script>
window.reviewedObjects = [
{% for obj in reviewed_objects %}
@ -17,46 +18,23 @@
{% endfor %}
];
</script>
{# prettier-ignore-end #}
{% endif %}
{# prettier-ignore-end #}
{% if object_type != 'package' %}
<div class="px-8 py-4">
<input
type="text"
id="object-search"
class="input input-bordered hidden w-full max-w-xs"
placeholder="Search by name"
/>
</div>
{% endif %}
<div class="px-8 py-4">
<input
type="text"
id="object-search"
class="input input-bordered hidden w-full max-w-xs"
placeholder="Search by name"
/>
</div>
{% block action_modals %}
{% if object_type == 'package' %}
{% include "modals/collections/new_package_modal.html" %}
{% include "modals/collections/import_package_modal.html" %}
{% include "modals/collections/import_legacy_package_modal.html" %}
{% elif object_type == 'compound' %}
{% include "modals/collections/new_compound_modal.html" %}
{% elif object_type == 'rule' %}
{% include "modals/collections/new_rule_modal.html" %}
{% elif object_type == 'reaction' %}
{% include "modals/collections/new_reaction_modal.html" %}
{% elif object_type == 'pathway' %}
{# {% include "modals/collections/new_pathway_modal.html" %} #}
{% elif object_type == 'node' %}
{% if object_type == 'node' %}
{% include "modals/collections/new_node_modal.html" %}
{% elif object_type == 'edge' %}
{% include "modals/collections/new_edge_modal.html" %}
{% elif object_type == 'scenario' %}
{% include "modals/collections/new_scenario_modal.html" %}
{% elif object_type == 'model' %}
{% include "modals/collections/new_model_modal.html" %}
{% elif object_type == 'setting' %}
{#{% include "modals/collections/new_setting_modal.html" %}#}
{% elif object_type == 'user' %}
<div></div>
{% elif object_type == 'group' %}
{% include "modals/collections/new_group_modal.html" %}
{% endif %}
{% endblock action_modals %}
@ -66,32 +44,10 @@
<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' %}
{% if 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">
@ -119,103 +75,17 @@
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' %}
{% if 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 class="mt-2">
<!-- 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"
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' %}
{% if object_type == 'node' %}
<p>
Nodes represent the (predicted) compounds in a graph.
<a
@ -227,7 +97,7 @@
</p>
{% elif object_type == 'edge' %}
<p>
Edges represent the links between Nodes in a graph
Edges represent the links between nodes in a graph.
<a
target="_blank"
href="https://wiki.envipath.org/index.php/edges"
@ -235,70 +105,15 @@
>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 %}
<!-- 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.
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 ensure you have at least reading permissions.
</p>
{% endif %}
{% endif %}
@ -306,7 +121,7 @@
</div>
</div>
<!-- Lists Container - Full Width with Reviewed on Right -->
<!-- Lists Container -->
<div class="w-full">
{% if reviewed_objects %}
{% if reviewed_objects|length > 0 %}
@ -404,7 +219,7 @@
>
<input
type="checkbox"
{% if reviewed_objects|length == 0 or object_type == 'package' %}checked{% endif %}
{% if reviewed_objects|length == 0 %}checked{% endif %}
/>
<div class="collapse-title text-xl font-medium">
Unreviewed
@ -466,31 +281,6 @@
</div>
{% endif %}
</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 w-full">
{% 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>
<script>