예제 #1
0
    def update(cls, company: str, project_id: str, **fields):
        with TimingContext("mongo", "projects_update"):
            project = Project.get_for_writing(company=company, id=project_id)
            if not project:
                raise errors.bad_request.InvalidProjectId(id=project_id)

            new_name = fields.pop("name", None)
            if new_name:
                new_name, new_location = _validate_project_name(new_name)
                old_name, old_location = _validate_project_name(project.name)
                if new_location != old_location:
                    raise errors.bad_request.CannotUpdateProjectLocation(
                        name=new_name)
                fields["name"] = new_name

            fields["last_update"] = datetime.utcnow()
            updated = project.update(upsert=False, **fields)

            if new_name:
                old_name = project.name
                project.name = new_name
                children = _get_sub_projects([project.id],
                                             _only=("id", "name",
                                                    "path"))[project.id]
                _update_subproject_names(project=project,
                                         children=children,
                                         old_name=old_name)

            return updated
예제 #2
0
    def validate(
        cls,
        task: Task,
        validate_models=True,
        validate_parent=True,
        validate_project=True,
    ):
        """
        Validate task properties according to the flag
        Task project is always checked for being writable
        in order to disable the modification of public projects
        """
        if (validate_parent and task.parent
                and not task.parent.startswith(deleted_prefix)
                and not Task.get(company=task.company,
                                 id=task.parent,
                                 _only=("id", ),
                                 include_public=True)):
            raise errors.bad_request.InvalidTaskId("invalid parent",
                                                   parent=task.parent)

        if task.project:
            project = Project.get_for_writing(company=task.company,
                                              id=task.project)
            if validate_project and not project:
                raise errors.bad_request.InvalidProjectId(id=task.project)

        if validate_models:
            cls.validate_input_models(task)
예제 #3
0
def delete_project(
    company: str, project_id: str, force: bool, delete_contents: bool
) -> Tuple[DeleteProjectResult, Set[str]]:
    project = Project.get_for_writing(
        company=company, id=project_id, _only=("id", "path")
    )
    if not project:
        raise errors.bad_request.InvalidProjectId(id=project_id)

    project_ids = _ids_with_children([project_id])
    if not force:
        for cls, error in (
            (Task, errors.bad_request.ProjectHasTasks),
            (Model, errors.bad_request.ProjectHasModels),
        ):
            non_archived = cls.objects(
                project__in=project_ids,
                system_tags__nin=[EntityVisibility.archived.value],
            ).only("id")
            if non_archived:
                raise error("use force=true to delete", id=project_id)

    if not delete_contents:
        with TimingContext("mongo", "update_children"):
            for cls in (Model, Task):
                updated_count = cls.objects(project__in=project_ids).update(
                    project=None
                )
        res = DeleteProjectResult(disassociated_tasks=updated_count)
    else:
        deleted_models, model_urls = _delete_models(projects=project_ids)
        deleted_tasks, event_urls, artifact_urls = _delete_tasks(
            company=company, projects=project_ids
        )
        res = DeleteProjectResult(
            deleted_tasks=deleted_tasks,
            deleted_models=deleted_models,
            urls=TaskUrls(
                model_urls=list(model_urls),
                event_urls=list(event_urls),
                artifact_urls=list(artifact_urls),
            ),
        )

    affected = {*project_ids, *(project.path or [])}
    res.deleted = Project.objects(id__in=project_ids).delete()

    return res, affected
예제 #4
0
    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
예제 #5
0
def validate_project_delete(company: str, project_id: str):
    project = Project.get_for_writing(
        company=company, id=project_id, _only=("id", "path")
    )
    if not project:
        raise errors.bad_request.InvalidProjectId(id=project_id)

    project_ids = _ids_with_children([project_id])
    ret = {}
    for cls in (Task, Model):
        ret[f"{cls.__name__.lower()}s"] = cls.objects(
            project__in=project_ids,
        ).count()
    for cls in (Task, Model):
        ret[f"non_archived_{cls.__name__.lower()}s"] = cls.objects(
            project__in=project_ids,
            system_tags__nin=[EntityVisibility.archived.value],
        ).count()

    return ret