From 266e62cf5c5f7e252831a5370cfc7145b10d0bc1 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 21 Oct 2024 16:36:00 +0800 Subject: [PATCH 1/5] Add terminate selected tasks in action --- django_celery_results/admin.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/django_celery_results/admin.py b/django_celery_results/admin.py index de5172af..72e5e886 100644 --- a/django_celery_results/admin.py +++ b/django_celery_results/admin.py @@ -1,7 +1,8 @@ """Result Task Admin interface.""" +from celery import current_app as celery_app from django.conf import settings -from django.contrib import admin +from django.contrib import admin, messages from django.utils.translation import gettext_lazy as _ try: @@ -58,6 +59,7 @@ class TaskResultAdmin(admin.ModelAdmin): 'classes': ('extrapretty', 'wide') }), ) + actions = ['terminate_task'] def get_readonly_fields(self, request, obj=None): if ALLOW_EDITS: @@ -67,6 +69,25 @@ def get_readonly_fields(self, request, obj=None): field.name for field in self.opts.local_fields }) + def terminate_task(self, request, queryset): + """Terminate selected tasks.""" + task_ids = list(queryset.values_list('task_id', flat=True)) + try: + celery_app.control.terminate(task_ids) + self.message_user( + request, + f"{len(task_ids)} Task was terminated successfully.", + messages.SUCCESS, + ) + except Exception as e: + self.message_user( + request, + f"Error while terminating tasks: {e}", + messages.ERROR, + ) + + terminate_task.short_description = "Terminate selected tasks" + admin.site.register(TaskResult, TaskResultAdmin) From 0a183d4e0529634981d7e1c0af240dc2d9752696 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Sat, 12 Apr 2025 22:32:13 +0800 Subject: [PATCH 2/5] Add test for terminate selected tasks --- t/unit/test_admin.py | 84 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 t/unit/test_admin.py diff --git a/t/unit/test_admin.py b/t/unit/test_admin.py new file mode 100644 index 00000000..8eac466c --- /dev/null +++ b/t/unit/test_admin.py @@ -0,0 +1,84 @@ +from unittest.mock import patch, MagicMock +import pytest +from celery import uuid +from django.test import TestCase +from django.test import RequestFactory +from django.contrib.messages import get_messages, constants +from django.contrib.messages.middleware import MessageMiddleware +from django.contrib.sessions.middleware import SessionMiddleware +from django_celery_results.admin import TaskResultAdmin +from django_celery_results.models import TaskResult + +@pytest.mark.usefixtures('depends_on_current_app') +class test_Admin(TestCase): + + def setUp(self): + self.task_admin = TaskResultAdmin(model=TaskResult, admin_site=None) + self.factory = RequestFactory() + + def _apply_middleware(self, request): + SessionMiddleware(lambda req: None).process_request(request) + MessageMiddleware(lambda req: None).process_request(request) + request.session.save() + + def create_task_result(self): + id = uuid() + taskmeta, created = TaskResult.objects.get_or_create(task_id=id) + return taskmeta + + @patch('celery.current_app.control.terminate') + def test_terminate_task_success(self, mock_terminate): + # Create mock request + request = self.factory.post('/') + request.user = MagicMock() + self._apply_middleware(request) + + # Create mock queryset + tr1 = self.create_task_result() + tr2 = self.create_task_result() + task_id_list = [tr1.task_id, tr2.task_id] + + mock_queryset = MagicMock() + mock_queryset.values_list.return_value = task_id_list + + # Call the terminate_task method + self.task_admin.terminate_task(request, mock_queryset) + + # Verify terminate was called with the correct task IDs + mock_terminate.assert_called_once_with(task_id_list) + + # Verify message_user was called with the success message + messages = list(get_messages(request)) + self.assertEqual(len(messages), 1) + self.assertEqual(str(messages[0]), "2 Task was terminated successfully.") + self.assertEqual(messages[0].level, constants.SUCCESS) + + @patch('celery.current_app.control.terminate') + def test_terminate_task_failure(self, mock_terminate): + # Create mock request + request = self.factory.post('/') + request.user = MagicMock() + self._apply_middleware(request) + + # Create mock queryset + tr1 = self.create_task_result() + tr2 = self.create_task_result() + task_id_list = [tr1.task_id, tr2.task_id] + + mock_queryset = MagicMock() + mock_queryset.values_list.return_value = task_id_list + + # Simulate an exception in terminate + mock_terminate.side_effect = Exception("Termination failed") + + # Call the terminate_task method + self.task_admin.terminate_task(request, mock_queryset) + + # Verify terminate was called with the correct task IDs + mock_terminate.assert_called_once_with(task_id_list) + + # Verify message_user was called with the error message + messages = list(get_messages(request)) + self.assertEqual(len(messages), 1) + self.assertIn("Error while terminating tasks: Termination failed", str(messages[0])) + self.assertEqual(messages[0].level, constants.ERROR) From 7a19716f7627cb55ec3acd9f8ece1910013234ff Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 12 Apr 2025 14:32:26 +0000 Subject: [PATCH 3/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- t/unit/test_admin.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/t/unit/test_admin.py b/t/unit/test_admin.py index 8eac466c..96bb9b90 100644 --- a/t/unit/test_admin.py +++ b/t/unit/test_admin.py @@ -1,14 +1,16 @@ -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch + import pytest from celery import uuid -from django.test import TestCase -from django.test import RequestFactory -from django.contrib.messages import get_messages, constants +from django.contrib.messages import constants, get_messages from django.contrib.messages.middleware import MessageMiddleware from django.contrib.sessions.middleware import SessionMiddleware +from django.test import RequestFactory, TestCase + from django_celery_results.admin import TaskResultAdmin from django_celery_results.models import TaskResult + @pytest.mark.usefixtures('depends_on_current_app') class test_Admin(TestCase): From 8555e3b267e0df4dbf14249e08321364ac55e82f Mon Sep 17 00:00:00 2001 From: caoqianming Date: Sat, 12 Apr 2025 22:48:59 +0800 Subject: [PATCH 4/5] fix flake8 check --- t/unit/test_admin.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/t/unit/test_admin.py b/t/unit/test_admin.py index 96bb9b90..037ff331 100644 --- a/t/unit/test_admin.py +++ b/t/unit/test_admin.py @@ -33,7 +33,7 @@ def test_terminate_task_success(self, mock_terminate): # Create mock request request = self.factory.post('/') request.user = MagicMock() - self._apply_middleware(request) + self._apply_middleware(request) # Create mock queryset tr1 = self.create_task_result() @@ -52,7 +52,8 @@ def test_terminate_task_success(self, mock_terminate): # Verify message_user was called with the success message messages = list(get_messages(request)) self.assertEqual(len(messages), 1) - self.assertEqual(str(messages[0]), "2 Task was terminated successfully.") + self.assertEqual(str(messages[0]), + "2 Task was terminated successfully.") self.assertEqual(messages[0].level, constants.SUCCESS) @patch('celery.current_app.control.terminate') @@ -60,7 +61,7 @@ def test_terminate_task_failure(self, mock_terminate): # Create mock request request = self.factory.post('/') request.user = MagicMock() - self._apply_middleware(request) + self._apply_middleware(request) # Create mock queryset tr1 = self.create_task_result() @@ -82,5 +83,6 @@ def test_terminate_task_failure(self, mock_terminate): # Verify message_user was called with the error message messages = list(get_messages(request)) self.assertEqual(len(messages), 1) - self.assertIn("Error while terminating tasks: Termination failed", str(messages[0])) + self.assertIn(str(messages[0]), + "Error while terminating tasks: Termination failed") self.assertEqual(messages[0].level, constants.ERROR) From f645f4c7190e9ce7465713df8f1bddef4f223a61 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Sat, 12 Apr 2025 23:01:44 +0800 Subject: [PATCH 5/5] fix flake8 check2 --- t/unit/test_admin.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/t/unit/test_admin.py b/t/unit/test_admin.py index 037ff331..f55160b4 100644 --- a/t/unit/test_admin.py +++ b/t/unit/test_admin.py @@ -52,7 +52,8 @@ def test_terminate_task_success(self, mock_terminate): # Verify message_user was called with the success message messages = list(get_messages(request)) self.assertEqual(len(messages), 1) - self.assertEqual(str(messages[0]), + self.assertEqual( + str(messages[0]), "2 Task was terminated successfully.") self.assertEqual(messages[0].level, constants.SUCCESS) @@ -83,6 +84,7 @@ def test_terminate_task_failure(self, mock_terminate): # Verify message_user was called with the error message messages = list(get_messages(request)) self.assertEqual(len(messages), 1) - self.assertIn(str(messages[0]), + self.assertIn( + str(messages[0]), "Error while terminating tasks: Termination failed") self.assertEqual(messages[0].level, constants.ERROR)