SciPost Code Repository

Skip to content
Snippets Groups Projects
Commit 10ff9ff9 authored by George Katsikas's avatar George Katsikas :goat:
Browse files

add task table searching through htmx partial

parent 35aa9154
No related branches found
No related tags found
No related merge requests found
__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
__license__ = "AGPL v3"
from collections.abc import Collection
from itertools import chain
from typing import Dict
from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Div, Field
from tasks.tasks.task import Task
from tasks.tasks.task_kinds import get_all_task_kinds
class TaskListSearchForm(forms.Form):
search = forms.CharField(label="Search", required=False)
orderby = forms.ChoiceField(
label="Order by",
choices=[
("", "-----"),
("kind__name", "Type"),
("title", "Title"),
("due_date", "Due date"),
],
initial="",
required=False,
)
ordering = forms.ChoiceField(
label="Ordering",
choices=[
("-", "Descending"),
("+", "Ascending"),
],
required=False,
)
def __init__(self, *args, **kwargs):
self.user = kwargs.pop("user")
self.task_kinds = get_all_task_kinds(self.user)
super().__init__(*args, **kwargs)
self.helper = FormHelper()
div_block_ordering = Div(
Div(Field("orderby"), css_class="col-6"),
Div(Field("ordering"), css_class="col-6"),
css_class="row mb-0",
)
self.helper.layout = Div(
Div(Field("search"), css_class="col-12"),
div_block_ordering,
)
def apply_filter_set(self, filters: Dict, none_on_empty: bool = False):
# Apply the filter set to the form
for key in self.fields:
if key in filters:
self.fields[key].initial = filters[key]
elif none_on_empty:
if isinstance(self.fields[key], forms.MultipleChoiceField):
self.fields[key].initial = []
else:
self.fields[key].initial = None
def search_results(self) -> Collection[Task]:
search_text = self.cleaned_data.get("search", "")
orderby = self.cleaned_data.get("orderby", "")
ordering = self.cleaned_data.get("ordering", "-")
tasks = [
task
for task_kind in self.task_kinds
for task in task_kind.get_tasks(search_text)
]
return tasks
......@@ -90,6 +90,12 @@ class ScheduleSubsidyPayments(TaskKind):
.prefetch_related("organization")
)
@staticmethod
def search_query(text: str) -> Q:
return Q(organization__name__icontains=text) | Q(
organization__acronym__icontains=text
)
class ScheduleSubsidyCollectivePayments(TaskKind):
name = "Schedule Subsidy Collective Payments"
......@@ -135,6 +141,12 @@ class ScheduleSubsidyCollectivePayments(TaskKind):
.prefetch_related("coordinator")
)
@staticmethod
def search_query(text: str) -> Q:
return Q(coordinator__name__icontains=text) | Q(
coordinator__acronym__icontains=text
)
class SendSubsidyInvoiceTask(TaskKind):
name = "Send Invoice"
......@@ -178,6 +190,12 @@ class SendSubsidyInvoiceTask(TaskKind):
.prefetch_related("organization")
)
@staticmethod
def search_query(text: str) -> Q:
return Q(organization__name__icontains=text) | Q(
organization__acronym__icontains=text
)
class CheckSubsidyPaymentTask(TaskKind):
name = "Check Payment"
......@@ -222,6 +240,12 @@ class CheckSubsidyPaymentTask(TaskKind):
.prefetch_related("organization")
)
@staticmethod
def search_query(text: str) -> Q:
return Q(organization__name__icontains=text) | Q(
organization__acronym__icontains=text
)
#####################
## Fellow Tasks
......@@ -264,6 +288,14 @@ class TreatOngoingAssignmentsTask(TaskKind):
.prefetch_related("submission")
)
@staticmethod
def search_query(text: str) -> Q:
return (
Q(submission__title__icontains=text)
| Q(submission__preprint__identifier_w_vn_nr__icontains=text)
| Q(submission__author_list__unaccent__icontains=text)
)
class VetCommentTask(TaskKind):
name = "Vet Comment"
......@@ -293,6 +325,14 @@ class VetCommentTask(TaskKind):
.prefetch_related("author__user")
)
@staticmethod
def search_query(text: str) -> Q:
return (
Q(author__profile__last_name__unaccent__icontains=text)
| Q(author__profile__first_name__unaccent__icontains=text)
| Q(comment_text__icontains=text)
)
class VetReportTask(TaskKind):
name = "Vet Report"
......@@ -336,6 +376,14 @@ class VetReportTask(TaskKind):
return qs.awaiting_vetting().prefetch_related("submission")
@staticmethod
def search_query(text: str) -> Q:
return (
Q(submission__title__icontains=text)
| Q(submission__preprint__identifier_w_vn_nr__icontains=text)
| Q(submission__author_list__unaccent__icontains=text)
)
class SelectRefereeingCycleTask(TaskKind):
name = "Select Refereeing Cycle"
......@@ -370,3 +418,11 @@ class SelectRefereeingCycleTask(TaskKind):
editor_in_charge=cls.user.contributor,
refereeing_cycle__isnull=False,
)
@staticmethod
def search_query(text: str) -> Q:
return (
Q(title__icontains=text)
| Q(preprint__identifier_w_vn_nr__icontains=text)
| Q(author_list__unaccent__icontains=text)
)
<table class="table">
<thead>
<tr>
<th scope="col">Type</th>
<th scope="col">Name</th>
<th scope="col">Due</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{% for task in tasks %}
<tr>
<td>{{ task.kind.name }}</td>
<td>{{ task.title }}</td>
<td>{{ task.due_date }}</td>
<td>
{% for action in task.actions|slice:":2" %}{{ action.as_html|safe }}{% endfor %}
{% if task.actions|length > 2 %}
<div class="btn-group" role="group">
<button class="btn btn-sm btn-light dropdown-toggle"
type="button"
data-bs-toggle="dropdown"
aria-expanded="false">
<span>More</span>
</button>
<ul class="dropdown-menu dropdown-menu-end">
{% for action in task.actions|slice:"2:" %}
<li class="dropdown-item">{{ action.element|safe }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% extends "scipost/base.html" %}
{% load crispy_forms_tags %}
{% block pagetitle %}: Tasklist{% endblock %}
{% block content %}
......@@ -13,57 +15,19 @@
<h1>Tasklist</h1>
</div>
<div class="d-flex flex-column gap-3">
<table class="table">
<thead>
<tr>
<th scope="col">Type</th>
<th scope="col">Name</th>
<th scope="col">Due</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{% for task_type, tasks in kinds_with_tasks.items %}
{% for task in tasks %}
<tr>
<td>{{ task.kind.name }}</td>
<td>{{ task.title }}</td>
<td>{{ task.due_date }}</td>
<td>
{% for action in task.actions|slice:":2" %}{{ action.as_html|safe }}{% endfor %}
{% if task.actions|length > 2 %}
<div class="btn-group" role="group">
<button class="btn btn-sm btn-light dropdown-toggle"
type="button"
data-bs-toggle="dropdown"
aria-expanded="false">
<span>More</span>
</button>
<ul class="dropdown-menu dropdown-menu-end">
{% for action in task.actions|slice:"2:" %}
<li class="dropdown-item">{{ action.element|safe }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
</td>
</tr>
{% endfor %}
{% endfor %}
<section aria-label="Search and filter tasks">
<form hx-get="{% url 'tasks:tasklist_new' %}"
hx-target="#tasklist-table"
hx-push-url="true"
hx-params="not csrfmiddlewaretoken"
hx-trigger="change delay:500ms">
{% crispy form %}
</form>
</section>
</tbody>
<div id="tasklist-table" class="d-flex flex-column gap-3">
</table>
{% include "tasks/_hx_task_table.html" %}
</div>
{% endblock %}
......@@ -8,6 +8,7 @@ from django.shortcuts import render
from colleges.permissions import is_edadmin_or_active_fellow
from submissions.models.assignment import EditorialAssignment
from submissions.models.recommendation import EICRecommendation
from tasks.forms import TaskListSearchForm
from tasks.tasks.task_kinds import get_all_task_kinds
......@@ -45,10 +46,19 @@ def tasklist_new_grouped(request):
@login_required
@user_passes_test(is_edadmin_or_active_fellow)
def tasklist_new(request):
form = TaskListSearchForm(request.GET, user=request.user)
tasks = []
if form.is_valid():
tasks = form.search_results()
context = {
"kinds_with_tasks": {
task_type: task_type.get_tasks()
for task_type in get_all_task_kinds(request.user)
}
"form": form,
"tasks": tasks,
}
# If htmx request, return only the task list
if request.headers.get("HX-Request") == "true":
return render(request, "tasks/_hx_task_table.html", context)
return render(request, "tasks/tasklist_new.html", context)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment