From 64c63d2560503931555a0a8e3f28934421a8b0ad Mon Sep 17 00:00:00 2001 From: allegroai <> Date: Tue, 5 Jan 2021 18:49:25 +0200 Subject: [PATCH] Add projects.get_task_parents --- apiserver/apimodels/projects.py | 4 ++ apiserver/bll/organization/__init__.py | 24 ++++++++++++ apiserver/schema/services/projects.conf | 37 +++++++++++++++++++ apiserver/services/projects.py | 20 +++++++++- .../tests/automated/test_project_tags.py | 29 ++++++++++++++- 5 files changed, 112 insertions(+), 2 deletions(-) diff --git a/apiserver/apimodels/projects.py b/apiserver/apimodels/projects.py index 751217b..1175dd4 100644 --- a/apiserver/apimodels/projects.py +++ b/apiserver/apimodels/projects.py @@ -15,3 +15,7 @@ class GetHyperParamReq(ProjectReq): class ProjectTagsRequest(TagsRequest): projects = ListField(str) + + +class ProjectTaskParentsRequest(ProjectReq): + projects = ListField(str) diff --git a/apiserver/bll/organization/__init__.py b/apiserver/bll/organization/__init__.py index 9fd64e2..0d0426a 100644 --- a/apiserver/bll/organization/__init__.py +++ b/apiserver/bll/organization/__init__.py @@ -1,7 +1,10 @@ from collections import defaultdict from enum import Enum +from operator import itemgetter from typing import Sequence, Dict +from mongoengine import Q + from apiserver.config_repo import config from apiserver.database.model.model import Model from apiserver.database.model.task.task import Task @@ -61,3 +64,24 @@ class OrgBLL: def _get_tags_cache_for_entity(self, entity: Tags) -> _TagsCache: return self._task_tags if entity == Tags.Task else self._model_tags + + @classmethod + def get_parent_tasks( + cls, company_id: str, projects: Sequence[str] + ) -> Sequence[dict]: + """ + Get list of unique parent tasks sorted by task name for the passed company projects + If projects is None or empty then get parents for all the company tasks + """ + query = Q(company=company_id) + if projects: + query &= Q(project__in=projects) + parent_ids = set(Task.objects(query).distinct("parent")) + if not parent_ids: + return [] + + parents = [ + {"id": task.id, "name": task.name} + for task in Task.objects(id__in=parent_ids).only("id", "name") + ] + return sorted(parents, key=itemgetter("name")) diff --git a/apiserver/schema/services/projects.conf b/apiserver/schema/services/projects.conf index 7219251..0f4874d 100644 --- a/apiserver/schema/services/projects.conf +++ b/apiserver/schema/services/projects.conf @@ -638,4 +638,41 @@ make_private { } } } +} +get_task_parents { + "2.12" { + description: "Get unique parent tasks for the tasks in the specified pprojects" + request { + type: object + properties { + projects { + description: "The list of projects which task parents are retieved. If not passed or empty then all the projects are searched" + type: array + items { type: string } + } + } + } + response { + type: object + properties { + parents { + description: "The list of unique task parents sorted by their names" + type: array + items { + type: object + properties { + id { + description: "The ID of the parent task" + type: string + } + name { + description: "The name of the parent task" + type: string + } + } + } + } + } + } + } } \ No newline at end of file diff --git a/apiserver/services/projects.py b/apiserver/services/projects.py index 4e5f8c7..49a76ac 100644 --- a/apiserver/services/projects.py +++ b/apiserver/services/projects.py @@ -13,6 +13,7 @@ from apiserver.apimodels.projects import ( GetHyperParamReq, ProjectReq, ProjectTagsRequest, + ProjectTaskParentsRequest, ) from apiserver.bll.organization import OrgBLL, Tags from apiserver.bll.project import ProjectBLL @@ -22,7 +23,11 @@ from apiserver.database.model import EntityVisibility from apiserver.database.model.model import Model from apiserver.database.model.project import Project from apiserver.database.model.task.task import Task, TaskStatus -from apiserver.database.utils import parse_from_call, get_options, get_company_or_none_constraint +from apiserver.database.utils import ( + parse_from_call, + get_options, + get_company_or_none_constraint, +) from apiserver.service_repo import APICall, endpoint from apiserver.services.utils import ( conform_tag_fields, @@ -438,3 +443,16 @@ def make_public(call: APICall, company_id, request: MakePublicRequest): call.result.data = Project.set_public( company_id, ids=request.ids, invalid_cls=InvalidProjectId, enabled=False ) + + +@endpoint( + "projects.get_task_parents", + min_version="2.12", + request_data_model=ProjectTaskParentsRequest, +) +def get_task_parents( + call: APICall, company_id: str, request: ProjectTaskParentsRequest +): + call.result.data = { + "parents": org_bll.get_parent_tasks(company_id, projects=request.projects) + } diff --git a/apiserver/tests/automated/test_project_tags.py b/apiserver/tests/automated/test_project_tags.py index 614ea7c..abce67f 100644 --- a/apiserver/tests/automated/test_project_tags.py +++ b/apiserver/tests/automated/test_project_tags.py @@ -4,9 +4,36 @@ from apiserver.tests.automated import TestService class TestProjectTags(TestService): - def setUp(self, version="2.8"): + def setUp(self, version="2.12"): super().setUp(version=version) + def test_task_parent(self): + # stand alone task + parent_sa_name = "Test parent parent standalone" + parent_sa = self.new_task(name=parent_sa_name) + self.new_task(name="Test parent task standalone", parent=parent_sa) + + # tasks in projects + parent_name = "Test parent parent" + parent = self.new_task(name=parent_name) + p1 = self.create_temp("projects", name="Test parents1", description="test") + self.new_task(project=p1, name="Test parent task1", parent=parent) + p2 = self.create_temp("projects", name="Test parents2", description="test") + self.new_task(project=p1, name="Test parent task2", parent=parent) + + parents = self.api.projects.get_task_parents(projects=[p1, p2]).parents + self.assertEqual([{"id": parent, "name": parent_name}], parents) + + res = self.api.projects.get_task_parents() + parents = [p for p in res.parents if p.id in (parent, parent_sa)] + self.assertEqual( + [ + {"id": parent, "name": parent_name}, + {"id": parent_sa, "name": parent_sa_name}, + ], + parents, + ) + def test_project_tags(self): tags_1 = ["Test tag 1", "Test tag 2"] tags_2 = ["Test tag 3", "Test tag 4"]