def merge_project(cls, company, source_id: str, destination_id: str) -> Tuple[int, int, Set[str]]: """ Move all the tasks and sub projects from the source project to the destination Remove the source project Return the amounts of moved entities and subprojects + set of all the affected project ids """ with TimingContext("mongo", "move_project"): if source_id == destination_id: raise errors.bad_request.ProjectSourceAndDestinationAreTheSame( source=source_id) source = Project.get(company, source_id) destination = Project.get(company, destination_id) if source_id in destination.path: raise errors.bad_request.ProjectCannotBeMergedIntoItsChild( source=source_id, destination=destination_id) children = _get_sub_projects([source.id], _only=("id", "name", "parent", "path"))[source.id] cls.validate_projects_depth( projects=children, old_parent_depth=len(source.path) + 1, new_parent_depth=len(destination.path) + 1, ) moved_entities = 0 for entity_type in (Task, Model): moved_entities += entity_type.objects( company=company, project=source_id, system_tags__nin=[EntityVisibility.archived.value], ).update(upsert=False, project=destination_id) moved_sub_projects = 0 for child in Project.objects(company=company, parent=source_id): _reposition_project_with_children( project=child, children=[c for c in children if c.parent == child.id], parent=destination, ) moved_sub_projects += 1 affected = {source.id, *(source.path or [])} source.delete() if destination: destination.update(last_update=datetime.utcnow()) affected.update({destination.id, *(destination.path or [])}) return moved_entities, moved_sub_projects, affected
def move_project(cls, company: str, user: str, project_id: str, new_location: str) -> Tuple[int, Set[str]]: """ Move project with its sub projects from its current location to the target one. If the target location does not exist then it will be created. If it exists then it should be writable. The source location should be writable too. Return the number of moved projects + set of all the affected project ids """ with TimingContext("mongo", "move_project"): project = Project.get(company, project_id) old_parent_id = project.parent old_parent = (Project.get_for_writing(company=project.company, id=old_parent_id) if old_parent_id else None) children = _get_sub_projects([project.id], _only=("id", "name", "path"))[project.id] cls.validate_projects_depth( projects=[project, *children], old_parent_depth=len(project.path), new_parent_depth=_get_project_depth(new_location), ) new_parent = _ensure_project(company=company, user=user, name=new_location) new_parent_id = new_parent.id if new_parent else None if old_parent_id == new_parent_id: raise errors.bad_request.ProjectSourceAndDestinationAreTheSame( location=new_parent.name if new_parent else "") if (new_parent and project_id == new_parent.id or project_id in new_parent.path): raise errors.bad_request.ProjectCannotBeMovedUnderItself( project=project_id, parent=new_parent.id) moved = _reposition_project_with_children(project, children=children, parent=new_parent) now = datetime.utcnow() affected = set() for p in filter(None, (old_parent, new_parent)): p.update(last_update=now) affected.update({p.id, *(p.path or [])}) return moved, affected