From fe9f2e196d1c0e14fde055b407ca720069aa7f33 Mon Sep 17 00:00:00 2001 From: Achintya-Chatterjee Date: Tue, 22 Jul 2025 02:51:03 +0530 Subject: [PATCH 1/3] fix: exclude DONE tasks from get tasks API - Updated TaskRepository queries to filter out tasks with status DONE - Ensured count and list methods do not include DONE tasks - Updated related unit tests to assert correct filtering --- todo/repositories/task_repository.py | 24 ++++++++++++------- .../unit/repositories/test_task_repository.py | 2 +- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/todo/repositories/task_repository.py b/todo/repositories/task_repository.py index 75ec0f30..39d71002 100644 --- a/todo/repositories/task_repository.py +++ b/todo/repositories/task_repository.py @@ -9,7 +9,7 @@ from todo.repositories.common.mongo_repository import MongoRepository from todo.repositories.task_assignment_repository import TaskAssignmentRepository from todo.constants.messages import ApiErrors, RepositoryErrors -from todo.constants.task import SORT_FIELD_PRIORITY, SORT_FIELD_ASSIGNEE, SORT_ORDER_DESC +from todo.constants.task import SORT_FIELD_PRIORITY, SORT_FIELD_ASSIGNEE, SORT_ORDER_DESC, TaskStatus class TaskRepository(MongoRepository): @@ -22,18 +22,20 @@ def list( tasks_collection = cls.get_collection() logger = logging.getLogger(__name__) + base_filter = {"status": {"$ne": TaskStatus.DONE.value}} + if team_id: logger.debug(f"TaskRepository.list: team_id={team_id}") team_assignments = TaskAssignmentRepository.get_by_assignee_id(team_id, "team") team_task_ids = [assignment.task_id for assignment in team_assignments] logger.debug(f"TaskRepository.list: team_task_ids={team_task_ids}") - query_filter = {"_id": {"$in": team_task_ids}} + query_filter = {"$and": [base_filter, {"_id": {"$in": team_task_ids}}]} logger.debug(f"TaskRepository.list: query_filter={query_filter}") elif user_id: assigned_task_ids = cls._get_assigned_task_ids_for_user(user_id) - query_filter = {"_id": {"$in": assigned_task_ids}} + query_filter = {"$and": [base_filter, {"_id": {"$in": assigned_task_ids}}]} else: - query_filter = {} + query_filter = base_filter if sort_by == SORT_FIELD_PRIORITY: sort_direction = 1 if order == SORT_ORDER_DESC else -1 @@ -72,15 +74,20 @@ def _get_assigned_task_ids_for_user(cls, user_id: str) -> List[ObjectId]: @classmethod def count(cls, user_id: str = None, team_id: str = None) -> int: tasks_collection = cls.get_collection() + + base_filter = {"status": {"$ne": TaskStatus.DONE.value}} + if team_id: team_assignments = TaskAssignmentRepository.get_by_assignee_id(team_id, "team") team_task_ids = [assignment.task_id for assignment in team_assignments] - query_filter = {"_id": {"$in": team_task_ids}} + query_filter = {"$and": [base_filter, {"_id": {"$in": team_task_ids}}]} elif user_id: assigned_task_ids = cls._get_assigned_task_ids_for_user(user_id) - query_filter = {"$or": [{"createdBy": user_id}, {"_id": {"$in": assigned_task_ids}}]} + query_filter = { + "$and": [base_filter, {"$or": [{"createdBy": user_id}, {"_id": {"$in": assigned_task_ids}}]}] + } else: - query_filter = {} + query_filter = base_filter return tasks_collection.count_documents(query_filter) @classmethod @@ -211,7 +218,8 @@ def update(cls, task_id: str, update_data: dict) -> TaskModel | None: def get_tasks_for_user(cls, user_id: str, page: int, limit: int) -> List[TaskModel]: tasks_collection = cls.get_collection() assigned_task_ids = cls._get_assigned_task_ids_for_user(user_id) - query = {"_id": {"$in": assigned_task_ids}} + + query = {"$and": [{"status": {"$ne": TaskStatus.DONE.value}}, {"_id": {"$in": assigned_task_ids}}]} tasks_cursor = tasks_collection.find(query).skip((page - 1) * limit).limit(limit) return [TaskModel(**task) for task in tasks_cursor] diff --git a/todo/tests/unit/repositories/test_task_repository.py b/todo/tests/unit/repositories/test_task_repository.py index ec97adb9..6a3e4fcb 100644 --- a/todo/tests/unit/repositories/test_task_repository.py +++ b/todo/tests/unit/repositories/test_task_repository.py @@ -97,7 +97,7 @@ def test_count_returns_total_task_count(self): result = TaskRepository.count() self.assertEqual(result, 42) - self.mock_collection.count_documents.assert_called_once_with({}) + self.mock_collection.count_documents.assert_called_once_with({"status": {"$ne": "DONE"}}) def test_get_all_returns_all_tasks(self): self.mock_collection.find.return_value = self.task_data From 89d0a3f81f418c3aed46b52bb608f5a53626c4c8 Mon Sep 17 00:00:00 2001 From: Achintya-Chatterjee Date: Wed, 23 Jul 2025 01:54:02 +0530 Subject: [PATCH 2/3] refactor: add status filtering to tasks API with DONE exclusion by default - Exclude DONE tasks from GET /v1/tasks by default for cleaner active task view - Add ?status=DONE query parameter to specifically retrieve DONE tasks - Implement status filtering across all task endpoints: - GET /v1/tasks - general tasks with status filtering - GET /v1/tasks?profile=true - user's tasks with status filtering - GET /v1/tasks?teamId={id} - team tasks with status filtering - Fix get_tasks_for_user to include both created AND assigned tasks - Update repository layer (list, count, get_tasks_for_user) with status_filter parameter - Update service and view layers to pass status filter from query params - Update serializer to accept status query parameter - Fix all test assertions to match new method signatures --- todo/repositories/task_repository.py | 30 +++++-- todo/serializers/get_tasks_serializer.py | 2 + todo/services/task_service.py | 10 ++- .../test_task_sorting_integration.py | 24 ++++-- .../integration/test_tasks_pagination.py | 16 +++- todo/tests/unit/services/test_task_service.py | 30 +++++-- todo/tests/unit/views/test_task.py | 81 ++++++++++++++++--- todo/views/task.py | 22 ++--- 8 files changed, 169 insertions(+), 46 deletions(-) diff --git a/todo/repositories/task_repository.py b/todo/repositories/task_repository.py index 39d71002..2dffec6d 100644 --- a/todo/repositories/task_repository.py +++ b/todo/repositories/task_repository.py @@ -17,12 +17,22 @@ class TaskRepository(MongoRepository): @classmethod def list( - cls, page: int, limit: int, sort_by: str, order: str, user_id: str = None, team_id: str = None + cls, + page: int, + limit: int, + sort_by: str, + order: str, + user_id: str = None, + team_id: str = None, + status_filter: str = None, ) -> List[TaskModel]: tasks_collection = cls.get_collection() logger = logging.getLogger(__name__) - base_filter = {"status": {"$ne": TaskStatus.DONE.value}} + if status_filter: + base_filter = {"status": status_filter} + else: + base_filter = {"status": {"$ne": TaskStatus.DONE.value}} if team_id: logger.debug(f"TaskRepository.list: team_id={team_id}") @@ -72,10 +82,13 @@ def _get_assigned_task_ids_for_user(cls, user_id: str) -> List[ObjectId]: return direct_task_ids + team_task_ids @classmethod - def count(cls, user_id: str = None, team_id: str = None) -> int: + def count(cls, user_id: str = None, team_id: str = None, status_filter: str = None) -> int: tasks_collection = cls.get_collection() - base_filter = {"status": {"$ne": TaskStatus.DONE.value}} + if status_filter: + base_filter = {"status": status_filter} + else: + base_filter = {"status": {"$ne": TaskStatus.DONE.value}} if team_id: team_assignments = TaskAssignmentRepository.get_by_assignee_id(team_id, "team") @@ -215,11 +228,16 @@ def update(cls, task_id: str, update_data: dict) -> TaskModel | None: return None @classmethod - def get_tasks_for_user(cls, user_id: str, page: int, limit: int) -> List[TaskModel]: + def get_tasks_for_user(cls, user_id: str, page: int, limit: int, status_filter: str = None) -> List[TaskModel]: tasks_collection = cls.get_collection() assigned_task_ids = cls._get_assigned_task_ids_for_user(user_id) - query = {"$and": [{"status": {"$ne": TaskStatus.DONE.value}}, {"_id": {"$in": assigned_task_ids}}]} + if status_filter: + base_filter = {"status": status_filter} + else: + base_filter = {"status": {"$ne": TaskStatus.DONE.value}} + + query = {"$and": [base_filter, {"$or": [{"createdBy": user_id}, {"_id": {"$in": assigned_task_ids}}]}]} tasks_cursor = tasks_collection.find(query).skip((page - 1) * limit).limit(limit) return [TaskModel(**task) for task in tasks_cursor] diff --git a/todo/serializers/get_tasks_serializer.py b/todo/serializers/get_tasks_serializer.py index 6b2e41ae..2483e204 100644 --- a/todo/serializers/get_tasks_serializer.py +++ b/todo/serializers/get_tasks_serializer.py @@ -37,6 +37,8 @@ class GetTaskQueryParamsSerializer(serializers.Serializer): teamId = serializers.CharField(required=False, allow_blank=False, allow_null=True) + status = serializers.CharField(required=False, allow_blank=False, allow_null=True) + def validate(self, attrs): validated_data = super().validate(attrs) diff --git a/todo/services/task_service.py b/todo/services/task_service.py index f37538af..3c79f6b4 100644 --- a/todo/services/task_service.py +++ b/todo/services/task_service.py @@ -71,6 +71,7 @@ def get_tasks( order: str, user_id: str, team_id: str = None, + status_filter: str = None, ) -> GetTasksResponse: try: cls._validate_pagination_params(page, limit) @@ -89,8 +90,10 @@ def get_tasks( }, ) - tasks = TaskRepository.list(page, limit, sort_by, order, user_id, team_id=team_id) - total_count = TaskRepository.count(user_id, team_id=team_id) + tasks = TaskRepository.list( + page, limit, sort_by, order, user_id, team_id=team_id, status_filter=status_filter + ) + total_count = TaskRepository.count(user_id, team_id=team_id, status_filter=status_filter) if not tasks: return GetTasksResponse(tasks=[], links=None) @@ -665,9 +668,10 @@ def get_tasks_for_user( user_id: str, page: int = PaginationConfig.DEFAULT_PAGE, limit: int = PaginationConfig.DEFAULT_LIMIT, + status_filter: str = None, ) -> GetTasksResponse: cls._validate_pagination_params(page, limit) - tasks = TaskRepository.get_tasks_for_user(user_id, page, limit) + tasks = TaskRepository.get_tasks_for_user(user_id, page, limit, status_filter=status_filter) if not tasks: return GetTasksResponse(tasks=[], links=None) diff --git a/todo/tests/integration/test_task_sorting_integration.py b/todo/tests/integration/test_task_sorting_integration.py index 57fe3799..34a00d0d 100644 --- a/todo/tests/integration/test_task_sorting_integration.py +++ b/todo/tests/integration/test_task_sorting_integration.py @@ -24,7 +24,9 @@ def test_priority_sorting_integration(self, mock_list, mock_count): response = self.client.get("/v1/tasks", {"sort_by": "priority", "order": "desc"}) self.assertEqual(response.status_code, status.HTTP_200_OK) - mock_list.assert_called_with(1, 20, SORT_FIELD_PRIORITY, SORT_ORDER_DESC, str(self.user_id), team_id=None) + mock_list.assert_called_with( + 1, 20, SORT_FIELD_PRIORITY, SORT_ORDER_DESC, str(self.user_id), team_id=None, status_filter=None + ) @patch("todo.repositories.task_repository.TaskRepository.count") @patch("todo.repositories.task_repository.TaskRepository.list") @@ -36,7 +38,9 @@ def test_due_at_default_order_integration(self, mock_list, mock_count): self.assertEqual(response.status_code, status.HTTP_200_OK) - mock_list.assert_called_with(1, 20, SORT_FIELD_DUE_AT, SORT_ORDER_ASC, str(self.user_id), team_id=None) + mock_list.assert_called_with( + 1, 20, SORT_FIELD_DUE_AT, SORT_ORDER_ASC, str(self.user_id), team_id=None, status_filter=None + ) @patch("todo.repositories.task_repository.TaskRepository.count") @patch("todo.repositories.task_repository.TaskRepository.list") @@ -49,7 +53,9 @@ def test_assignee_sorting_uses_aggregation(self, mock_list, mock_count): self.assertEqual(response.status_code, status.HTTP_200_OK) # Assignee sorting now falls back to createdAt sorting - mock_list.assert_called_once_with(1, 20, SORT_FIELD_ASSIGNEE, SORT_ORDER_ASC, str(self.user_id), team_id=None) + mock_list.assert_called_once_with( + 1, 20, SORT_FIELD_ASSIGNEE, SORT_ORDER_ASC, str(self.user_id), team_id=None, status_filter=None + ) @patch("todo.repositories.task_repository.TaskRepository.count") @patch("todo.repositories.task_repository.TaskRepository.list") @@ -72,7 +78,9 @@ def test_field_specific_defaults_integration(self, mock_list, mock_count): response = self.client.get("/v1/tasks", {"sort_by": sort_field}) self.assertEqual(response.status_code, status.HTTP_200_OK) - mock_list.assert_called_with(1, 20, sort_field, expected_order, str(self.user_id), team_id=None) + mock_list.assert_called_with( + 1, 20, sort_field, expected_order, str(self.user_id), team_id=None, status_filter=None + ) @patch("todo.repositories.task_repository.TaskRepository.count") @patch("todo.repositories.task_repository.TaskRepository.list") @@ -84,7 +92,9 @@ def test_pagination_with_sorting_integration(self, mock_list, mock_count): self.assertEqual(response.status_code, status.HTTP_200_OK) - mock_list.assert_called_with(3, 5, SORT_FIELD_CREATED_AT, SORT_ORDER_ASC, str(self.user_id), team_id=None) + mock_list.assert_called_with( + 3, 5, SORT_FIELD_CREATED_AT, SORT_ORDER_ASC, str(self.user_id), team_id=None, status_filter=None + ) def test_invalid_sort_parameters_integration(self): response = self.client.get("/v1/tasks", {"sort_by": "invalid_field"}) @@ -103,7 +113,9 @@ def test_default_behavior_integration(self, mock_list, mock_count): self.assertEqual(response.status_code, status.HTTP_200_OK) - mock_list.assert_called_with(1, 20, SORT_FIELD_CREATED_AT, SORT_ORDER_DESC, str(self.user_id), team_id=None) + mock_list.assert_called_with( + 1, 20, SORT_FIELD_CREATED_AT, SORT_ORDER_DESC, str(self.user_id), team_id=None, status_filter=None + ) @patch("todo.services.task_service.reverse_lazy", return_value="/v1/tasks") @patch("todo.repositories.task_repository.TaskRepository.count") diff --git a/todo/tests/integration/test_tasks_pagination.py b/todo/tests/integration/test_tasks_pagination.py index 5423cc8c..6d0b2b6d 100644 --- a/todo/tests/integration/test_tasks_pagination.py +++ b/todo/tests/integration/test_tasks_pagination.py @@ -21,7 +21,13 @@ def test_pagination_settings_integration(self, mock_get_tasks): self.assertEqual(response.status_code, 200) mock_get_tasks.assert_called_with( - page=1, limit=default_limit, sort_by="createdAt", order="desc", user_id=str(self.user_id), team_id=None + page=1, + limit=default_limit, + sort_by="createdAt", + order="desc", + user_id=str(self.user_id), + team_id=None, + status_filter=None, ) mock_get_tasks.reset_mock() @@ -30,7 +36,13 @@ def test_pagination_settings_integration(self, mock_get_tasks): self.assertEqual(response.status_code, 200) mock_get_tasks.assert_called_with( - page=1, limit=10, sort_by="createdAt", order="desc", user_id=str(self.user_id), team_id=None + page=1, + limit=10, + sort_by="createdAt", + order="desc", + user_id=str(self.user_id), + team_id=None, + status_filter=None, ) # Verify API rejects values above max limit diff --git a/todo/tests/unit/services/test_task_service.py b/todo/tests/unit/services/test_task_service.py index 9750e34e..603929ab 100644 --- a/todo/tests/unit/services/test_task_service.py +++ b/todo/tests/unit/services/test_task_service.py @@ -71,7 +71,9 @@ def test_get_tasks_returns_paginated_response( response.links.prev, f"{self.mock_reverse_lazy('tasks')}?page=1&limit=1&sort_by=createdAt&order=desc" ) - mock_list.assert_called_once_with(2, 1, "createdAt", "desc", str(self.user_id), team_id=None) + mock_list.assert_called_once_with( + 2, 1, "createdAt", "desc", str(self.user_id), team_id=None, status_filter=None + ) mock_count.assert_called_once() @patch("todo.services.task_service.UserRepository.get_by_id") @@ -111,7 +113,7 @@ def test_get_tasks_returns_empty_response_if_no_tasks_present(self, mock_list: M self.assertEqual(len(response.tasks), 0) self.assertIsNone(response.links) - mock_list.assert_called_once_with(1, 10, "createdAt", "desc", "test_user", team_id=None) + mock_list.assert_called_once_with(1, 10, "createdAt", "desc", "test_user", team_id=None, status_filter=None) mock_count.assert_called_once() @patch("todo.services.task_service.TaskRepository.count") @@ -294,7 +296,9 @@ def test_get_tasks_default_sorting(self, mock_list, mock_count): TaskService.get_tasks(page=1, limit=20, sort_by="createdAt", order="desc", user_id="test_user") - mock_list.assert_called_once_with(1, 20, SORT_FIELD_CREATED_AT, SORT_ORDER_DESC, "test_user", team_id=None) + mock_list.assert_called_once_with( + 1, 20, SORT_FIELD_CREATED_AT, SORT_ORDER_DESC, "test_user", team_id=None, status_filter=None + ) @patch("todo.services.task_service.TaskRepository.count") @patch("todo.services.task_service.TaskRepository.list") @@ -304,7 +308,9 @@ def test_get_tasks_explicit_sort_by_priority(self, mock_list, mock_count): TaskService.get_tasks(page=1, limit=20, sort_by=SORT_FIELD_PRIORITY, order=SORT_ORDER_DESC, user_id="test_user") - mock_list.assert_called_once_with(1, 20, SORT_FIELD_PRIORITY, SORT_ORDER_DESC, "test_user", team_id=None) + mock_list.assert_called_once_with( + 1, 20, SORT_FIELD_PRIORITY, SORT_ORDER_DESC, "test_user", team_id=None, status_filter=None + ) @patch("todo.services.task_service.TaskRepository.count") @patch("todo.services.task_service.TaskRepository.list") @@ -314,7 +320,9 @@ def test_get_tasks_sort_by_due_at_default_order(self, mock_list, mock_count): TaskService.get_tasks(page=1, limit=20, sort_by=SORT_FIELD_DUE_AT, order="asc", user_id="test_user") - mock_list.assert_called_once_with(1, 20, SORT_FIELD_DUE_AT, SORT_ORDER_ASC, "test_user", team_id=None) + mock_list.assert_called_once_with( + 1, 20, SORT_FIELD_DUE_AT, SORT_ORDER_ASC, "test_user", team_id=None, status_filter=None + ) @patch("todo.services.task_service.TaskRepository.count") @patch("todo.services.task_service.TaskRepository.list") @@ -324,7 +332,9 @@ def test_get_tasks_sort_by_priority_default_order(self, mock_list, mock_count): TaskService.get_tasks(page=1, limit=20, sort_by=SORT_FIELD_PRIORITY, order="desc", user_id="test_user") - mock_list.assert_called_once_with(1, 20, SORT_FIELD_PRIORITY, SORT_ORDER_DESC, "test_user", team_id=None) + mock_list.assert_called_once_with( + 1, 20, SORT_FIELD_PRIORITY, SORT_ORDER_DESC, "test_user", team_id=None, status_filter=None + ) @patch("todo.services.task_service.TaskRepository.count") @patch("todo.services.task_service.TaskRepository.list") @@ -334,7 +344,9 @@ def test_get_tasks_sort_by_assignee_default_order(self, mock_list, mock_count): TaskService.get_tasks(page=1, limit=20, sort_by=SORT_FIELD_ASSIGNEE, order="asc", user_id="test_user") - mock_list.assert_called_once_with(1, 20, SORT_FIELD_ASSIGNEE, SORT_ORDER_ASC, "test_user", team_id=None) + mock_list.assert_called_once_with( + 1, 20, SORT_FIELD_ASSIGNEE, SORT_ORDER_ASC, "test_user", team_id=None, status_filter=None + ) @patch("todo.services.task_service.TaskRepository.count") @patch("todo.services.task_service.TaskRepository.list") @@ -344,7 +356,9 @@ def test_get_tasks_sort_by_created_at_default_order(self, mock_list, mock_count) TaskService.get_tasks(page=1, limit=20, sort_by=SORT_FIELD_CREATED_AT, order="desc", user_id="test_user") - mock_list.assert_called_once_with(1, 20, SORT_FIELD_CREATED_AT, SORT_ORDER_DESC, "test_user", team_id=None) + mock_list.assert_called_once_with( + 1, 20, SORT_FIELD_CREATED_AT, SORT_ORDER_DESC, "test_user", team_id=None, status_filter=None + ) @patch("todo.services.task_service.reverse_lazy", return_value="/v1/tasks") def test_build_page_url_includes_sort_parameters(self, mock_reverse): diff --git a/todo/tests/unit/views/test_task.py b/todo/tests/unit/views/test_task.py index 26c77cd5..f0a630c6 100644 --- a/todo/tests/unit/views/test_task.py +++ b/todo/tests/unit/views/test_task.py @@ -45,7 +45,13 @@ def test_get_tasks_returns_200_for_valid_params(self, mock_get_tasks: Mock): response: Response = self.client.get(self.url, self.valid_params) mock_get_tasks.assert_called_once_with( - page=1, limit=10, sort_by="createdAt", order="desc", user_id=str(self.user_id), team_id=None + page=1, + limit=10, + sort_by="createdAt", + order="desc", + user_id=str(self.user_id), + team_id=None, + status_filter=None, ) self.assertEqual(response.status_code, status.HTTP_200_OK) expected_response = mock_get_tasks.return_value.model_dump(mode="json") @@ -58,7 +64,13 @@ def test_get_tasks_returns_200_without_params(self, mock_get_tasks: Mock): response: Response = self.client.get(self.url) default_limit = settings.REST_FRAMEWORK["DEFAULT_PAGINATION_SETTINGS"]["DEFAULT_PAGE_LIMIT"] mock_get_tasks.assert_called_once_with( - page=1, limit=default_limit, sort_by="createdAt", order="desc", user_id=str(self.user_id), team_id=None + page=1, + limit=default_limit, + sort_by="createdAt", + order="desc", + user_id=str(self.user_id), + team_id=None, + status_filter=None, ) self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -163,7 +175,13 @@ def test_get_tasks_with_default_pagination(self, mock_get_tasks): self.assertEqual(response.status_code, status.HTTP_200_OK) default_limit = settings.REST_FRAMEWORK["DEFAULT_PAGINATION_SETTINGS"]["DEFAULT_PAGE_LIMIT"] mock_get_tasks.assert_called_once_with( - page=1, limit=default_limit, sort_by="createdAt", order="desc", user_id=str(self.user_id), team_id=None + page=1, + limit=default_limit, + sort_by="createdAt", + order="desc", + user_id=str(self.user_id), + team_id=None, + status_filter=None, ) @patch("todo.services.task_service.TaskService.get_tasks") @@ -175,7 +193,13 @@ def test_get_tasks_with_valid_pagination(self, mock_get_tasks): self.assertEqual(response.status_code, status.HTTP_200_OK) mock_get_tasks.assert_called_once_with( - page=2, limit=15, sort_by="createdAt", order="desc", user_id=str(self.user_id), team_id=None + page=2, + limit=15, + sort_by="createdAt", + order="desc", + user_id=str(self.user_id), + team_id=None, + status_filter=None, ) def test_get_tasks_with_invalid_page(self): @@ -217,7 +241,13 @@ def test_get_tasks_with_sort_by_priority(self, mock_get_tasks): self.assertEqual(response.status_code, status.HTTP_200_OK) mock_get_tasks.assert_called_once_with( - page=1, limit=20, sort_by=SORT_FIELD_PRIORITY, order="desc", user_id=str(self.user_id), team_id=None + page=1, + limit=20, + sort_by=SORT_FIELD_PRIORITY, + order="desc", + user_id=str(self.user_id), + team_id=None, + status_filter=None, ) @patch("todo.services.task_service.TaskService.get_tasks") @@ -228,7 +258,13 @@ def test_get_tasks_with_sort_by_and_order(self, mock_get_tasks): self.assertEqual(response.status_code, status.HTTP_200_OK) mock_get_tasks.assert_called_once_with( - page=1, limit=20, sort_by=SORT_FIELD_DUE_AT, order=SORT_ORDER_DESC, user_id=str(self.user_id), team_id=None + page=1, + limit=20, + sort_by=SORT_FIELD_DUE_AT, + order=SORT_ORDER_DESC, + user_id=str(self.user_id), + team_id=None, + status_filter=None, ) @patch("todo.services.task_service.TaskService.get_tasks") @@ -250,7 +286,13 @@ def test_get_tasks_with_all_sort_fields(self, mock_get_tasks): self.assertEqual(response.status_code, status.HTTP_200_OK) mock_get_tasks.assert_called_once_with( - page=1, limit=20, sort_by=sort_field, order=expected_order, user_id=str(self.user_id), team_id=None + page=1, + limit=20, + sort_by=sort_field, + order=expected_order, + user_id=str(self.user_id), + team_id=None, + status_filter=None, ) @patch("todo.services.task_service.TaskService.get_tasks") @@ -267,7 +309,13 @@ def test_get_tasks_with_all_order_values(self, mock_get_tasks): self.assertEqual(response.status_code, status.HTTP_200_OK) mock_get_tasks.assert_called_once_with( - page=1, limit=20, sort_by=SORT_FIELD_PRIORITY, order=order, user_id=str(self.user_id), team_id=None + page=1, + limit=20, + sort_by=SORT_FIELD_PRIORITY, + order=order, + user_id=str(self.user_id), + team_id=None, + status_filter=None, ) def test_get_tasks_with_invalid_sort_by(self): @@ -296,7 +344,13 @@ def test_get_tasks_sorting_with_pagination(self, mock_get_tasks): self.assertEqual(response.status_code, status.HTTP_200_OK) mock_get_tasks.assert_called_once_with( - page=2, limit=15, sort_by=SORT_FIELD_DUE_AT, order=SORT_ORDER_ASC, user_id=str(self.user_id), team_id=None + page=2, + limit=15, + sort_by=SORT_FIELD_DUE_AT, + order=SORT_ORDER_ASC, + user_id=str(self.user_id), + team_id=None, + status_filter=None, ) @patch("todo.services.task_service.TaskService.get_tasks") @@ -307,7 +361,13 @@ def test_get_tasks_default_behavior_unchanged(self, mock_get_tasks): self.assertEqual(response.status_code, status.HTTP_200_OK) mock_get_tasks.assert_called_once_with( - page=1, limit=20, sort_by=SORT_FIELD_CREATED_AT, order="desc", user_id=str(self.user_id), team_id=None + page=1, + limit=20, + sort_by=SORT_FIELD_CREATED_AT, + order="desc", + user_id=str(self.user_id), + team_id=None, + status_filter=None, ) def test_get_tasks_edge_case_combinations(self): @@ -324,6 +384,7 @@ def test_get_tasks_edge_case_combinations(self): order=SORT_ORDER_ASC, user_id=str(self.user_id), team_id=None, + status_filter=None, ) diff --git a/todo/views/task.py b/todo/views/task.py index 4c8467ed..4520cbd9 100644 --- a/todo/views/task.py +++ b/todo/views/task.py @@ -57,6 +57,13 @@ class TaskListView(APIView): description="If provided, filters tasks assigned to this team.", required=False, ), + OpenApiParameter( + name="status", + type=OpenApiTypes.STR, + location=OpenApiParameter.QUERY, + description="If provided, filters tasks by status (e.g., 'DONE', 'IN_PROGRESS', 'TODO', 'BLOCKED', 'DEFERRED').", + required=False, + ), ], responses={ 200: OpenApiResponse(response=GetTasksResponse, description="Successful response"), @@ -74,26 +81,18 @@ def get(self, request: Request): user = get_current_user_info(request) if not user: raise AuthenticationFailed(ApiErrors.AUTHENTICATION_FAILED) + status_filter = query.validated_data.get("status") response = TaskService.get_tasks_for_user( user_id=user["user_id"], page=query.validated_data["page"], limit=query.validated_data["limit"], + status_filter=status_filter, ) return Response(data=response.model_dump(mode="json"), status=status.HTTP_200_OK) user = get_current_user_info(request) - if query.validated_data["profile"]: - response = TaskService.get_tasks_for_user( - user_id=user["user_id"], - page=query.validated_data["page"], - limit=query.validated_data["limit"], - ) - return Response( - data=response.model_dump(mode="json", exclude_none=True), - status=status.HTTP_200_OK, - ) - team_id = query.validated_data.get("teamId") + status_filter = query.validated_data.get("status") response = TaskService.get_tasks( page=query.validated_data["page"], limit=query.validated_data["limit"], @@ -101,6 +100,7 @@ def get(self, request: Request): order=query.validated_data.get("order"), user_id=user["user_id"], team_id=team_id, + status_filter=status_filter, ) return Response(data=response.model_dump(mode="json"), status=status.HTTP_200_OK) From 39cdb03f29a0b8b4fb5d86469e57644f2e28e911 Mon Sep 17 00:00:00 2001 From: Achintya-Chatterjee Date: Wed, 23 Jul 2025 14:09:27 +0530 Subject: [PATCH 3/3] chore: remove the createdBy query from get get_tasks_for_user method --- todo/repositories/task_repository.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/todo/repositories/task_repository.py b/todo/repositories/task_repository.py index 2dffec6d..2089e98e 100644 --- a/todo/repositories/task_repository.py +++ b/todo/repositories/task_repository.py @@ -237,7 +237,7 @@ def get_tasks_for_user(cls, user_id: str, page: int, limit: int, status_filter: else: base_filter = {"status": {"$ne": TaskStatus.DONE.value}} - query = {"$and": [base_filter, {"$or": [{"createdBy": user_id}, {"_id": {"$in": assigned_task_ids}}]}]} + query = {"$and": [base_filter, {"_id": {"$in": assigned_task_ids}}]} tasks_cursor = tasks_collection.find(query).skip((page - 1) * limit).limit(limit) return [TaskModel(**task) for task in tasks_cursor]