From 97f223a89097712eaaf55590877012cf5a2463b3 Mon Sep 17 00:00:00 2001 From: marya Date: Wed, 11 Dec 2024 16:28:55 +0000 Subject: [PATCH 1/7] added new urls for task workflow task create --- tasks/urls.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tasks/urls.py b/tasks/urls.py index ad00db22f..33e44933f 100644 --- a/tasks/urls.py +++ b/tasks/urls.py @@ -111,6 +111,16 @@ views.TaskWorkflowConfirmDeleteView.as_view(), name="task-workflow-ui-confirm-delete", ), + path( + "/task/create/", + views.TaskWorkflowTaskCreateView.as_view(), + name="task-workflow-task-ui-create", + ), + path( + "/task/confirm-create/", + views.TaskWorkflowTaskConfirmCreateView.as_view(), + name="task-workflow-task-ui-confirm-create", + ), ] task_and_workflow_ui_patterns = [ From d2feb1bdc9f46a06094f3a45204f18bbb787ae2b Mon Sep 17 00:00:00 2001 From: marya Date: Wed, 11 Dec 2024 16:29:45 +0000 Subject: [PATCH 2/7] added 2 views for task workflow task create and confirm create --- tasks/views.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tasks/views.py b/tasks/views.py index 261491b6b..da1dd50dc 100644 --- a/tasks/views.py +++ b/tasks/views.py @@ -537,6 +537,14 @@ def get_context_data(self, **kwargs): return context_data +class TaskWorkflowTaskCreateView(CreateView): + pass + + +class TaskWorkflowTaskConfirmCreateView(DetailView): + pass + + class TaskWorkflowTemplateDetailView( PermissionRequiredMixin, QueuedItemManagementMixin, From 0bf3effa84ce9551b866eec3294becfe25ce7c77 Mon Sep 17 00:00:00 2001 From: marya Date: Mon, 16 Dec 2024 17:22:03 +0000 Subject: [PATCH 3/7] added URL and views for workflow task create and confirm create --- tasks/jinja2/tasks/workflows/detail.jinja | 2 +- tasks/urls.py | 4 +- tasks/views.py | 47 +++++++++++++++++++++-- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/tasks/jinja2/tasks/workflows/detail.jinja b/tasks/jinja2/tasks/workflows/detail.jinja index db9be5e80..b9f395a0a 100644 --- a/tasks/jinja2/tasks/workflows/detail.jinja +++ b/tasks/jinja2/tasks/workflows/detail.jinja @@ -8,7 +8,7 @@ {% if object_name == "workflow" %} {% set task_object_name = "task" %} - {% set task_object_create_url = "#TODO" %} + {% set task_object_create_url = url("workflow:task-workflow-task-ui-create", kwargs={"task_workflow_pk": object.pk}) %} {% elif object_name == "workflow template" %} {% set task_object_name = "task template" %} {% set task_object_create_url = url("workflow:task-template-ui-create", kwargs={"workflow_template_pk": object.pk}) %} diff --git a/tasks/urls.py b/tasks/urls.py index 33e44933f..6f6d856f6 100644 --- a/tasks/urls.py +++ b/tasks/urls.py @@ -112,12 +112,12 @@ name="task-workflow-ui-confirm-delete", ), path( - "/task/create/", + "/task/create/", views.TaskWorkflowTaskCreateView.as_view(), name="task-workflow-task-ui-create", ), path( - "/task/confirm-create/", + "/task/confirm-create/", views.TaskWorkflowTaskConfirmCreateView.as_view(), name="task-workflow-task-ui-confirm-create", ), diff --git a/tasks/views.py b/tasks/views.py index da1dd50dc..a6cdbf76a 100644 --- a/tasks/views.py +++ b/tasks/views.py @@ -537,12 +537,51 @@ def get_context_data(self, **kwargs): return context_data -class TaskWorkflowTaskCreateView(CreateView): - pass +class TaskWorkflowTaskCreateView(PermissionRequiredMixin, CreateView): + model = Task + template_name = "tasks/workflows/workflow_task_create.jinja" + permission_required = "tasks.add_task" + form_class = TaskCreateForm + + def get_task_workflow(self): + """Get the associated TaskWorkflow via its pk in the URL.""" + return TaskWorkflow.objects.get( + pk=self.kwargs["task_workflow_pk"], + ) + + def get_context_data(self, **kwargs) -> dict: + context = super().get_context_data(**kwargs) + + context["page_title"] = "Create a task" + context["task_workflow"] = self.get_task_workflow() + return context + + def form_valid(self, form) -> HttpResponseRedirect: + with transaction.atomic(): + self.object = form.save(user=self.request.user) + TaskItem.objects.create( + workflow=self.get_task_workflow(), + task=self.object, + ) + return HttpResponseRedirect(self.get_success_url(), self.object.pk) + + def get_success_url(self): + return reverse( + "workflow:task-workflow-task-ui-confirm-create", + kwargs={"pk": self.object.pk}, + ) -class TaskWorkflowTaskConfirmCreateView(DetailView): - pass +class TaskWorkflowTaskConfirmCreateView(PermissionRequiredMixin, DetailView): + model = Task + template_name = "tasks/workflows/workflow_task_confirm_create.jinja" + permission_required = "tasks.add_task" + + def get_context_data(self, **kwargs) -> dict: + context = super().get_context_data(**kwargs) + + context["task_workflow"] = self.get_object().taskitem.workflow + return context class TaskWorkflowTemplateDetailView( From cae9f2cea2663e66beed9baaabad8cf9ba6db288 Mon Sep 17 00:00:00 2001 From: marya Date: Tue, 17 Dec 2024 16:47:58 +0000 Subject: [PATCH 4/7] failing unit test --- tasks/tests/test_views.py | 34 ++++++++++++++++++++++++++++++++++ tasks/views.py | 10 +++++----- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/tasks/tests/test_views.py b/tasks/tests/test_views.py index ac179e65e..ae6fb4041 100644 --- a/tasks/tests/test_views.py +++ b/tasks/tests/test_views.py @@ -811,3 +811,37 @@ def test_task_and_workflow_list_view(valid_user_client, task, task_workflow): assert table.select("tr:nth-child(2) > td:nth-child(1) > a:nth-child(1)")[ 0 ].text == str(task_workflow.summary_task.pk) + + +def test_create_workflow_task_view(valid_user_client, task_workflow): + """Test the view for creating new Tasks for an existing workflow and the + confirmation view that a successful creation redirects to.""" + + assert task_workflow.get_tasks().count() == 0 + + create_url = reverse( + "workflow:task-workflow-task-ui-create", + kwargs={"task_workflow_pk": task_workflow.pk}, + ) + + form_data = { + "title": factory.Faker("sentence"), + "description": factory.Faker("sentence"), + } + create_response = valid_user_client.post(create_url, form_data) + created_workflow_task = task_workflow.get_tasks().get() + confirmation_url = reverse( + "workflow:task-workflow-task-ui-confirm-create", + kwargs={"pk": created_workflow_task.pk}, + ) + + assert create_response.status_code == 302 + assert task_workflow.get_tasks().count() == 1 + 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_task.title in soup.select("h1.govuk-panel__title")[0].text diff --git a/tasks/views.py b/tasks/views.py index a6cdbf76a..43ef4711b 100644 --- a/tasks/views.py +++ b/tasks/views.py @@ -539,7 +539,7 @@ def get_context_data(self, **kwargs): class TaskWorkflowTaskCreateView(PermissionRequiredMixin, CreateView): model = Task - template_name = "tasks/workflows/workflow_task_create.jinja" + template_name = "layouts/create.jinja" permission_required = "tasks.add_task" form_class = TaskCreateForm @@ -553,7 +553,7 @@ def get_context_data(self, **kwargs) -> dict: context = super().get_context_data(**kwargs) context["page_title"] = "Create a task" - context["task_workflow"] = self.get_task_workflow() + # context["task_workflow"] = self.get_task_workflow() return context def form_valid(self, form) -> HttpResponseRedirect: @@ -574,13 +574,13 @@ def get_success_url(self): class TaskWorkflowTaskConfirmCreateView(PermissionRequiredMixin, DetailView): model = Task - template_name = "tasks/workflows/workflow_task_confirm_create.jinja" + template_name = "tasks/confirm_create.jinja" permission_required = "tasks.add_task" def get_context_data(self, **kwargs) -> dict: context = super().get_context_data(**kwargs) - - context["task_workflow"] = self.get_object().taskitem.workflow + context["verbose_name"] = "task" + # context["task_workflow"] = self.get_object().taskitem.workflow return context From cc2ff3aa06c50d652559bcf72f6c806342c7bd53 Mon Sep 17 00:00:00 2001 From: marya Date: Tue, 17 Dec 2024 19:33:03 +0000 Subject: [PATCH 5/7] fixed failing test for workflow task create and confirm create view --- tasks/forms.py | 7 +++++++ tasks/tests/test_views.py | 41 +++++++++++++++++++++++++++++++++++++++ tasks/urls.py | 2 +- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/tasks/forms.py b/tasks/forms.py index 5fab477ca..80d029a36 100644 --- a/tasks/forms.py +++ b/tasks/forms.py @@ -25,6 +25,13 @@ class TaskBaseForm(ModelForm): class Meta: model = Task + include = [ + "title", + "description", + "progress_state", + "category", + "workbasket", + ] exclude = ["parent_task", "creator"] error_messages = { diff --git a/tasks/tests/test_views.py b/tasks/tests/test_views.py index ae6fb4041..63bbcf296 100644 --- a/tasks/tests/test_views.py +++ b/tasks/tests/test_views.py @@ -819,6 +819,8 @@ def test_create_workflow_task_view(valid_user_client, task_workflow): assert task_workflow.get_tasks().count() == 0 + progress_state = ProgressStateFactory.create() + create_url = reverse( "workflow:task-workflow-task-ui-create", kwargs={"task_workflow_pk": task_workflow.pk}, @@ -827,6 +829,7 @@ def test_create_workflow_task_view(valid_user_client, task_workflow): form_data = { "title": factory.Faker("sentence"), "description": factory.Faker("sentence"), + "progress_state": progress_state.pk, } create_response = valid_user_client.post(create_url, form_data) created_workflow_task = task_workflow.get_tasks().get() @@ -845,3 +848,41 @@ def test_create_workflow_task_view(valid_user_client, task_workflow): assert confirmation_response.status_code == 200 assert created_workflow_task.title in soup.select("h1.govuk-panel__title")[0].text + + +def test_workflow_delete_view_deletes_related_tasks( + valid_user_client, + task_workflow_single_task_item, +): + """Tests that a workflow can be deleted (along with related Task and + TaskItem objects) and that the corresponding confirmation view returns a + HTTP 200 response.""" + + task_workflow_pk = task_workflow_single_task_item.pk + task_pk = task_workflow_single_task_item.get_tasks().get().pk + + delete_url = task_workflow_single_task_item.get_url("delete") + delete_response = valid_user_client.post(delete_url) + assert delete_response.status_code == 302 + + assert not TaskWorkflow.objects.filter( + pk=task_workflow_pk, + ).exists() + assert not TaskItem.objects.filter( + workflow_id=task_workflow_pk, + ).exists() + assert not Task.objects.filter(pk=task_pk).exists() + + confirmation_url = reverse( + "workflow:task-workflow-ui-confirm-delete", + kwargs={"pk": task_workflow_pk}, + ) + assert delete_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 ( + f"Workflow ID: {task_workflow_pk}" in soup.select(".govuk-panel__title")[0].text + ) diff --git a/tasks/urls.py b/tasks/urls.py index 6f6d856f6..e2fb46a5c 100644 --- a/tasks/urls.py +++ b/tasks/urls.py @@ -117,7 +117,7 @@ name="task-workflow-task-ui-create", ), path( - "/task/confirm-create/", + "task/confirm-create/", views.TaskWorkflowTaskConfirmCreateView.as_view(), name="task-workflow-task-ui-confirm-create", ), From b85075192550c3ef91791cf01f4dde6aac81d251 Mon Sep 17 00:00:00 2001 From: marya Date: Thu, 19 Dec 2024 13:47:47 +0000 Subject: [PATCH 6/7] modified TaskBaseForm to explicitly add 'fields' attribute as per Django documentation guidelines --- tasks/forms.py | 3 +-- tasks/views.py | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/tasks/forms.py b/tasks/forms.py index 80d029a36..5108463b3 100644 --- a/tasks/forms.py +++ b/tasks/forms.py @@ -25,14 +25,13 @@ class TaskBaseForm(ModelForm): class Meta: model = Task - include = [ + fields = [ "title", "description", "progress_state", "category", "workbasket", ] - exclude = ["parent_task", "creator"] error_messages = { "title": { diff --git a/tasks/views.py b/tasks/views.py index 9dfcb4fe2..47b0a5807 100644 --- a/tasks/views.py +++ b/tasks/views.py @@ -569,7 +569,6 @@ def get_context_data(self, **kwargs) -> dict: context = super().get_context_data(**kwargs) context["page_title"] = "Create a task" - # context["task_workflow"] = self.get_task_workflow() return context def form_valid(self, form) -> HttpResponseRedirect: From 8546b9bde62e80f4adf4807f80b8f2fc0ba6017f Mon Sep 17 00:00:00 2001 From: marya Date: Thu, 19 Dec 2024 13:58:37 +0000 Subject: [PATCH 7/7] removed redundant comment --- tasks/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tasks/views.py b/tasks/views.py index 47b0a5807..0bae2e1cc 100644 --- a/tasks/views.py +++ b/tasks/views.py @@ -595,7 +595,6 @@ class TaskWorkflowTaskConfirmCreateView(PermissionRequiredMixin, DetailView): def get_context_data(self, **kwargs) -> dict: context = super().get_context_data(**kwargs) context["verbose_name"] = "task" - # context["task_workflow"] = self.get_object().taskitem.workflow return context