Skip to content

Commit

Permalink
TP2000-1544 Prototype workflow template create & update views (#1328)
Browse files Browse the repository at this point in the history
* Don't show actions for a single item in queue list

* Filter queue items by their own queue when reordering

* Support task workflow template creation

* Support workflow template editing
  • Loading branch information
dalecannon authored Nov 18, 2024
1 parent baf7f61 commit f4f351c
Show file tree
Hide file tree
Showing 13 changed files with 334 additions and 4 deletions.
3 changes: 3 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,9 @@ def policy_group(db) -> Group:
("tasks", "view_comment"),
("tasks", "change_comment"),
("tasks", "delete_comment"),
("tasks", "add_taskworkflowtemplate"),
("tasks", "change_taskworkflowtemplate"),
("tasks", "delete_taskworkflowtemplate"),
("tasks", "view_taskworkflowtemplate"),
("tasks", "add_tasktemplate"),
("tasks", "change_tasktemplate"),
Expand Down
43 changes: 43 additions & 0 deletions tasks/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from common.forms import delete_form_for
from tasks.models import Task
from tasks.models import TaskTemplate
from tasks.models import TaskWorkflowTemplate
from workbaskets.models import WorkBasket


Expand Down Expand Up @@ -81,6 +82,48 @@ def save(self, parent_task, user, commit=True):
TaskDeleteForm = delete_form_for(Task)


class TaskWorkflowTemplateBaseForm(ModelForm):
class Meta:
model = TaskWorkflowTemplate
fields = ("title", "description")

error_messages = {
"title": {
"required": "Enter a title",
},
"description": {
"required": "Enter a description",
},
}

def __init__(self, *args, submit_title, **kwargs):
super().__init__(*args, **kwargs)

self.helper = FormHelper(self)
self.helper.label_size = Size.SMALL
self.helper.legend_size = Size.SMALL
self.helper.layout = Layout(
"title",
"description",
Submit(
"submit",
submit_title,
data_module="govuk-button",
data_prevent_double_click="true",
),
)


class TaskWorkflowTemplateCreateForm(TaskWorkflowTemplateBaseForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, submit_title="Create", **kwargs)


class TaskWorkflowTemplateUpdateForm(TaskWorkflowTemplateBaseForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, submit_title="Update", **kwargs)


class TaskTemplateFormBase(ModelForm):
class Meta:
model = TaskTemplate
Expand Down
8 changes: 6 additions & 2 deletions tasks/jinja2/tasks/includes/task_queue.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

{%- for obj in object_list %}
{%- set up_down_cell_content %}
{% if loop.index == 1 %}
{% if object_list|length == 1 %}
{# No buttons needed for a single item #}
{% elif loop.index == 1 %}
{{ render_item_button(obj, "demote", use_icon=True) }}
{% elif loop.index == 2 %}
{{ render_item_button(obj, "promote", use_icon=True) }}
Expand All @@ -31,7 +33,9 @@
{% endset -%}

{%- set order_cell_content %}
{% if loop.index == 1%}
{% if object_list|length == 1 %}
{# No buttons needed for a single item #}
{% elif loop.index == 1 %}
{{ render_item_button(obj, "demote_to_last") }}
{% elif loop.index == object_list | length %}
{{ render_item_button(obj, "promote_to_first") }}
Expand Down
47 changes: 47 additions & 0 deletions tasks/jinja2/tasks/workflows/template_confirm_create.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{% extends "common/confirm_create.jinja" %}

{% from "components/breadcrumbs.jinja" import breadcrumbs %}
{% from "components/panel/macro.njk" import govukPanel %}
{% from "components/button/macro.njk" import govukButton %}

{% set page_title = "Workflow template created" %}

{% block breadcrumb %}
{{ breadcrumbs(
request,
[
{"text": "Find and view workflow templates", "href": "#TODO"},
{
"text": "Create a workflow template",
"href": url("workflow:task-workflow-template-ui-create"),
},
{"text": page_title}
],
False,
) }}
{% endblock %}

{% block panel %}
{{ govukPanel({
"titleText": "Workflow template: " ~ object.title,
"text": "You have created a new workflow template",
"classes": "govuk-!-margin-bottom-7"
}) }}
{% endblock %}

{% block button_group %}
{{ govukButton({
"text": "View workflow template",
"href": url("workflow:task-workflow-template-ui-detail", kwargs={"pk": object.pk}),
"classes": "govuk-button"
}) }}
{{ govukButton({
"text": "Create a task template",
"href": url("workflow:task-template-ui-create", kwargs={"workflow_template_pk": object.pk}),
"classes": "govuk-button--secondary"
}) }}
{% endblock %}

{% block actions %}
<li><a href="TODO" class="govuk-link">Find and view workflow templates</a></li>
{% endblock %}
49 changes: 49 additions & 0 deletions tasks/jinja2/tasks/workflows/template_confirm_update.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{% extends "common/confirm_create.jinja" %}

{% from "components/breadcrumbs.jinja" import breadcrumbs %}
{% from "components/panel/macro.njk" import govukPanel %}
{% from "components/button/macro.njk" import govukButton %}

{% set page_title = "Workflow template updated" %}

{% block breadcrumb %}
{{ breadcrumbs(
request,
[
{"text": "Find and view workflow templates", "href": "#TODO"},
{
"text": "Workflow template: " ~ object.title,
"href": url(
"workflow:task-workflow-template-ui-detail",
kwargs={"pk": object.pk}),
},
{"text": page_title}
],
False,
) }}
{% endblock %}

{% block panel %}
{{ govukPanel({
"titleText": "Workflow template: " ~ object.title,
"text": "You have updated the workflow template",
"classes": "govuk-!-margin-bottom-7"
}) }}
{% endblock %}

{% block button_group %}
{{ govukButton({
"text": "View workflow template",
"href": url("workflow:task-workflow-template-ui-detail", kwargs={"pk": object.pk}),
"classes": "govuk-button"
}) }}
{{ govukButton({
"text": "Create a task template",
"href": url("workflow:task-template-ui-create", kwargs={"workflow_template_pk": object.pk}),
"classes": "govuk-button--secondary"
}) }}
{% endblock %}

{% block actions %}
<li><a href="TODO" class="govuk-link">Find and view workflow templates</a></li>
{% endblock %}
16 changes: 16 additions & 0 deletions tasks/jinja2/tasks/workflows/template_create.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{% extends "layouts/create.jinja" %}

{% from "components/breadcrumbs.jinja" import breadcrumbs %}

{% set page_title = "Create a workflow template" %}

{% block breadcrumb %}
{{ breadcrumbs(
request,
[
{"text": "Find and view workflow templates", "href": "#TODO"},
{"text": page_title}
],
False,
) }}
{% endblock %}
2 changes: 1 addition & 1 deletion tasks/jinja2/tasks/workflows/template_detail.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
{% set page_title = "Workflow template: " ~ object.title %}
{% set list_include = "tasks/includes/task_queue.jinja" %}

{% set edit_url = "#TODO" %}
{% set edit_url = object.get_url("edit") %}

{% block breadcrumb %}
{{ breadcrumbs(request, [
Expand Down
19 changes: 19 additions & 0 deletions tasks/jinja2/tasks/workflows/template_edit.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{% extends "layouts/form.jinja" %}
{% from "components/breadcrumbs.jinja" import breadcrumbs %}

{% set page_title = "Edit workflow template details" %}

{% block breadcrumb %}
{{ breadcrumbs(request, [
{"text": "Find and view tasks", "href": url("workflow:task-ui-list")},
{"text": "Workflow template: " ~ object.title, "href": object.get_url("detail")},
{"text": page_title}
])
}}
{% endblock %}

{% block form %}
{% call django_form() %}
{{ crispy(form) }}
{% endcall %}
{% endblock %}
7 changes: 6 additions & 1 deletion tasks/models/queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ def delete(self):

self.__class__.objects.select_for_update(nowait=True).filter(
position__gt=instance.position,
queue=instance.queue,
).update(position=models.F("position") - 1)

return super().delete()
Expand All @@ -145,6 +146,7 @@ def promote(self) -> Self:

item_to_demote = self.__class__.objects.select_for_update(nowait=True).get(
position=instance.position - 1,
queue=instance.queue,
)
item_to_demote.position += 1
instance.position -= 1
Expand All @@ -169,6 +171,7 @@ def demote(self) -> Self:

item_to_promote = self.__class__.objects.select_for_update(nowait=True).get(
position=instance.position + 1,
queue=instance.queue,
)
item_to_promote.position -= 1
instance.position += 1
Expand All @@ -194,7 +197,8 @@ def promote_to_first(self) -> Self:
return instance

self.__class__.objects.select_for_update(nowait=True).filter(
models.Q(position__lt=instance.position),
position__lt=instance.position,
queue=instance.queue,
).update(position=models.F("position") + 1)

instance.position = 1
Expand All @@ -221,6 +225,7 @@ def demote_to_last(self) -> Self:

self.__class__.objects.select_for_update(nowait=True).filter(
position__gt=instance.position,
queue=instance.queue,
).update(position=models.F("position") - 1)

instance.position = last_place
Expand Down
14 changes: 14 additions & 0 deletions tasks/models/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,20 @@ def create_task_workflow(self) -> "TaskWorkflow":

return task_workflow

def get_url(self, action: str = "detail"):
if action == "detail":
return reverse(
"workflow:task-workflow-template-ui-detail",
kwargs={"pk": self.pk},
)
elif action == "edit":
return reverse(
"workflow:task-workflow-template-ui-update",
kwargs={"pk": self.pk},
)

return "#NOT-IMPLEMENTED"


class TaskItemTemplate(QueueItem):
"""Queue item management for TaskTemplate instances."""
Expand Down
70 changes: 70 additions & 0 deletions tasks/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,76 @@ def convert_to_index(position: int) -> int:
assert reordered_item.id == expected_item.id


def test_workflow_template_create_view(valid_user_client):
"""Tests that a new workflow template can be created and that the
corresponding confirmation view returns a HTTP 200 response."""

assert not TaskWorkflowTemplate.objects.exists()

create_url = reverse("workflow:task-workflow-template-ui-create")
form_data = {
"title": "Test workflow template",
"description": "Test description",
}
create_response = valid_user_client.post(create_url, form_data)

created_workflow_template = TaskWorkflowTemplate.objects.get(
title=form_data["title"],
description=form_data["description"],
)
confirmation_url = reverse(
"workflow:task-workflow-template-ui-confirm-create",
kwargs={"pk": created_workflow_template.pk},
)
assert create_response.status_code == 302
assert create_response.url == confirmation_url

confirmation_response = valid_user_client.get(confirmation_url)

soup = BeautifulSoup(str(confirmation_response.content), "html.parser")

assert confirmation_response.status_code == 200
assert (
created_workflow_template.title in soup.select("h1.govuk-panel__title")[0].text
)


def test_workflow_template_update_view(
valid_user_client,
task_workflow_template,
):
"""Tests that a workflow template can be updated and that the corresponding
confirmation view returns a HTTP 200 response."""

update_url = reverse(
"workflow:task-workflow-template-ui-update",
kwargs={"pk": task_workflow_template.pk},
)
form_data = {
"title": "Updated test title",
"description": "Updated test title",
}

update_response = valid_user_client.post(update_url, form_data)
assert update_response.status_code == 302

task_workflow_template.refresh_from_db()
assert task_workflow_template.title == form_data["title"]
assert task_workflow_template.description == form_data["description"]

confirmation_url = reverse(
"workflow:task-workflow-template-ui-confirm-update",
kwargs={"pk": task_workflow_template.pk},
)
assert update_response.url == confirmation_url

confirmation_response = valid_user_client.get(confirmation_url)
assert confirmation_response.status_code == 200

soup = BeautifulSoup(str(confirmation_response.content), "html.parser")
assert task_workflow_template.title in soup.select("h1.govuk-panel__title")[0].text


def test_create_task_template_view(valid_user_client, task_workflow_template):
"""Test the view for creating new TaskTemplates and the confirmation view
that a successful creation redirects to."""
Expand Down
20 changes: 20 additions & 0 deletions tasks/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,26 @@
views.TaskWorkflowTemplateDetailView.as_view(),
name="task-workflow-template-ui-detail",
),
path(
"create/",
views.TaskWorkflowTemplateCreateView.as_view(),
name="task-workflow-template-ui-create",
),
path(
"<int:pk>/confirm-create/",
views.TaskWorkflowTemplateConfirmCreateView.as_view(),
name="task-workflow-template-ui-confirm-create",
),
path(
"<int:pk>/update/",
views.TaskWorkflowTemplateUpdateView.as_view(),
name="task-workflow-template-ui-update",
),
path(
"<int:pk>/confirm-update/",
views.TaskWorkflowTemplateConfirmUpdateView.as_view(),
name="task-workflow-template-ui-confirm-update",
),
path(
"task-templates/<int:pk>/",
views.TaskTemplateDetailView.as_view(),
Expand Down
Loading

0 comments on commit f4f351c

Please sign in to comment.