Пример #1
0
    def duplicate(self, request, pk=None):
        project = self.get_object()
        self.check_permissions(request, "duplicate", project)
        if project.blocked_code is not None:
            raise exc.Blocked(_("Blocked element"))

        validator = validators.DuplicateProjectValidator(data=request.DATA)
        if not validator.is_valid():
            return response.BadRequest(validator.errors)

        data = validator.data

        # Validate if the project can be imported
        is_private = data.get('is_private', False)
        total_memberships = len(data.get("users", [])) + 1
        (enough_slots,
         error_message) = users_services.has_available_slot_for_new_project(
             self.request.user, is_private, total_memberships)
        if not enough_slots:
            raise exc.NotEnoughSlotsForProject(is_private, total_memberships,
                                               error_message)

        new_project = services.duplicate_project(
            project=project,
            owner=request.user,
            name=data["name"],
            description=data["description"],
            is_private=data["is_private"],
            users=data["users"])
        new_project = get_object_or_404(self.get_queryset(), id=new_project.id)
        serializer = self.get_serializer(new_project)
        return response.Created(serializer.data)
Пример #2
0
 def _check_if_new_members_can_be_created(self, project, memberships):
     (can_add_memberships, error_type,
      total_memberships) = services.check_if_new_members_can_be_created(
          project, memberships)
     if not can_add_memberships:
         raise exc.NotEnoughSlotsForProject(project.is_private,
                                            total_memberships, error_type)
Пример #3
0
 def _check_if_project_can_have_more_memberships(self, project,
                                                 total_new_memberships):
     (can_add_memberships,
      error_type) = services.check_if_project_can_have_more_memberships(
          project, total_new_memberships)
     if not can_add_memberships:
         raise exc.NotEnoughSlotsForProject(project.is_private,
                                            total_new_memberships,
                                            error_type)
Пример #4
0
    def pre_save(self, obj):
        user = self.request.user
        (enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(user, project=obj)
        members = max(obj.memberships.count(), 1)
        if not enough_slots:
            raise exc.NotEnoughSlotsForProject(obj.is_private, members, not_enough_slots_error)

        if not obj.id:
            obj.owner = user
            obj.template = self.request.QUERY_PARAMS.get('template', None)

        self._set_base_permissions(obj)
        super().pre_save(obj)
Пример #5
0
    def pre_save(self, obj):
        if not obj.id:
            obj.owner = self.request.user
            obj.template = self.request.QUERY_PARAMS.get('template', None)

        # Validate if the owner have enought slots to create or update the project
        # TODO: Move to the ProjectAdminSerializer
        (enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(obj.owner, obj)
        if not enough_slots:
            members = max(obj.memberships.count(), 1)
            raise exc.NotEnoughSlotsForProject(obj.is_private, members, not_enough_slots_error)

        self._set_base_permissions(obj)
        super().pre_save(obj)
Пример #6
0
    def pre_save(self, obj):
        if not obj.id:
            obj.owner = self.request.user
            obj.template = self.request.QUERY_PARAMS.get('template', None)

        if not obj.id or self.get_object().is_private != obj.is_private:
            # Validate if the owner have enought slots to create the project
            # or if you are changing the privacity
            (can_create_or_update, error_message) = services.check_if_project_can_be_created_or_updated(obj)
            if not can_create_or_update:
                members = max(obj.memberships.count(), 1)
                raise exc.NotEnoughSlotsForProject(obj.is_private, members, error_message)

        self._set_base_permissions(obj)
        super().pre_save(obj)
Пример #7
0
    def load_dump(self, request):
        throttle = throttling.ImportDumpModeRateThrottle()

        if not throttle.allow_request(request, self):
            self.throttled(request, throttle.wait())

        self.check_permissions(request, "load_dump", None)

        dump = request.FILES.get('dump', None)

        if not dump:
            raise exc.WrongArguments(_("Needed dump file"))

        reader = codecs.getreader("utf-8")

        try:
            dump = json.load(reader(dump))
        except Exception:
            raise exc.WrongArguments(_("Invalid dump format"))

        slug = dump.get('slug', None)
        if slug is not None and Project.objects.filter(slug=slug).exists():
            del dump['slug']

        user = request.user
        dump['owner'] = user.email

        # Validate if the project can be imported
        is_private = dump.get("is_private", False)
        total_memberships = len([
            m for m in dump.get("memberships", [])
            if m.get("email", None) != dump["owner"]
        ])
        total_memberships = total_memberships + 1  # 1 is the owner
        (enough_slots, error_message
         ) = users_service.has_available_slot_for_import_new_project(
             user, is_private, total_memberships)
        if not enough_slots:
            raise exc.NotEnoughSlotsForProject(is_private, total_memberships,
                                               error_message)

        if settings.CELERY_ENABLED:
            task = tasks.load_project_dump.delay(user, dump)
            return response.Accepted({"import_id": task.id})

        project = dump_service.dict_to_project(dump, request.user)
        response_data = ProjectSerializer(project).data
        return response.Created(response_data)
Пример #8
0
    def pre_save(self, obj):
        if not obj.id:
            members = 1
            (enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(
                self.request.user,
                obj.project,
                members
            )
            if not enough_slots:
                raise exc.NotEnoughSlotsForProject(obj.project.is_private, members, not_enough_slots_error)

        if not obj.token:
            obj.token = str(uuid.uuid1())

        obj.invited_by = self.request.user
        obj.user = services.find_invited_user(obj.email, default=obj.user)
        super().pre_save(obj)
Пример #9
0
    def transfer_accept(self, request, pk=None):
        token = request.DATA.get('token', None)
        if token is None:
            raise exc.WrongArguments(_("Invalid token"))

        project = self.get_object()
        self.check_permissions(request, "transfer_accept", project)

        (can_transfer, error_message) = services.check_if_project_can_be_transfered(
            project,
            request.user,
        )
        if not can_transfer:
            members = project.memberships.count()
            raise exc.NotEnoughSlotsForProject(project.is_private, members, error_message)

        reason = request.DATA.get('reason', None)
        services.accept_project_transfer(project, request.user, token, reason)
        return response.Ok()
Пример #10
0
    def transfer_accept(self, request, pk=None):
        token = request.DATA.get('token', None)
        if token is None:
            raise exc.WrongArguments(_("Invalid token"))

        project = self.get_object()
        self.check_permissions(request, "transfer_accept", project)

        (enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(
            request.user,
            project,
        )
        if not enough_slots:
            members = project.memberships.count()
            raise exc.NotEnoughSlotsForProject(project.is_private, members, not_enough_slots_error)

        reason = request.DATA.get('reason', None)
        services.accept_project_transfer(project, request.user, token, reason)
        return response.Ok()
Пример #11
0
    def load_dump(self, request):
        throttle = throttling.ImportDumpModeRateThrottle()

        if not throttle.allow_request(request, self):
            self.throttled(request, throttle.wait())

        self.check_permissions(request, "load_dump", None)

        dump = request.FILES.get('dump', None)

        if not dump:
            raise exc.WrongArguments(_("Needed dump file"))

        reader = codecs.getreader("utf-8")

        try:
            dump = json.load(reader(dump))
            is_private = dump.get("is_private", False)
        except Exception:
            raise exc.WrongArguments(_("Invalid dump format"))

        user = request.user
        slug = dump.get('slug', None)
        if slug is not None and Project.objects.filter(slug=slug).exists():
            del dump['slug']

        members = len(dump.get("memberships", []))
        (enough_slots, not_enough_slots_error
         ) = users_service.has_available_slot_for_project(
             user,
             project=Project(is_private=is_private, id=None),
             members=max(members, 1))
        if not enough_slots:
            raise exc.NotEnoughSlotsForProject(is_private, max(members, 1),
                                               not_enough_slots_error)

        if settings.CELERY_ENABLED:
            task = tasks.load_project_dump.delay(user, dump)
            return response.Accepted({"import_id": task.id})

        project = dump_service.dict_to_project(dump, request.user)
        response_data = ProjectSerializer(project).data
        return response.Created(response_data)
Пример #12
0
    def duplicate(self, request, pk=None):
        project = self.get_object()
        self.check_permissions(request, "duplicate", project)
        if project.blocked_code is not None:
            raise exc.Blocked(_("Blocked element"))

        validator = validators.DuplicateProjectValidator(data=request.DATA)
        if not validator.is_valid():
            return response.BadRequest(validator.errors)

        data = validator.data
        new_name = data.get("name", "")
        new_description = data.get("description", "")
        new_owner = self.request.user
        new_is_private = data.get('is_private', False)
        new_members = data.get("users", [])

        # Validate if the project can be imported
        (enough_slots, error_message,
         total_members) = services.check_if_project_can_be_duplicate(
             project=project,
             new_owner=new_owner,
             new_is_private=new_is_private,
             new_user_id_members=[m["id"] for m in new_members])
        if not enough_slots:
            raise exc.NotEnoughSlotsForProject(new_is_private, total_members,
                                               error_message)

        new_project = services.duplicate_project(project=project,
                                                 name=new_name,
                                                 description=new_description,
                                                 owner=new_owner,
                                                 is_private=new_is_private,
                                                 users=new_members)
        new_project = get_object_or_error(self.get_queryset(),
                                          request.user,
                                          id=new_project.id)
        serializer = self.get_serializer(new_project)
        return response.Created(serializer.data)
Пример #13
0
    def bulk_create(self, request, **kwargs):
        serializer = serializers.MembersBulkSerializer(data=request.DATA)
        if not serializer.is_valid():
            return response.BadRequest(serializer.errors)

        data = serializer.data
        project = models.Project.objects.get(id=data["project_id"])
        invitation_extra_text = data.get("invitation_extra_text", None)
        self.check_permissions(request, 'bulk_create', project)
        if project.blocked_code is not None:
            raise exc.Blocked(_("Blocked element"))

        # TODO: this should be moved to main exception handler instead
        # of handling explicit exception catchin here.

        if "bulk_memberships" in data and isinstance(data["bulk_memberships"], list):
            members = len(data["bulk_memberships"])
            (enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(
                project.owner,
                project,
                members
            )
            if not enough_slots:
                raise exc.NotEnoughSlotsForProject(project.is_private, members, not_enough_slots_error)

        try:
            members = services.create_members_in_bulk(data["bulk_memberships"],
                                                      project=project,
                                                      invitation_extra_text=invitation_extra_text,
                                                      callback=self.post_save,
                                                      precall=self.pre_save)
        except ValidationError as err:
            return response.BadRequest(err.message_dict)

        members_serialized = self.admin_serializer_class(members, many=True)
        return response.Ok(members_serialized.data)
Пример #14
0
    def _import_project_data(self, data, options):
        board = data
        labels = board['labels']
        statuses = board['lists']
        project_template = ProjectTemplate.objects.get(
            slug=options.get('template', "kanban"))
        project_template.us_statuses = []
        counter = 0
        for us_status in statuses:
            if counter == 0:
                project_template.default_options["us_status"] = us_status[
                    'name']

            counter += 1
            if us_status['name'] not in [
                    s['name'] for s in project_template.us_statuses
            ]:
                project_template.us_statuses.append({
                    "name":
                    us_status['name'],
                    "slug":
                    slugify(us_status['name']),
                    "is_closed":
                    False,
                    "is_archived":
                    True if us_status['closed'] else False,
                    "color":
                    "#999999",
                    "wip_limit":
                    None,
                    "order":
                    us_status['pos'],
                })

        project_template.task_statuses = []
        project_template.task_statuses.append({
            "name": "Incomplete",
            "slug": "incomplete",
            "is_closed": False,
            "color": "#ff8a84",
            "order": 1,
        })
        project_template.task_statuses.append({
            "name": "Complete",
            "slug": "complete",
            "is_closed": True,
            "color": "#669900",
            "order": 2,
        })
        project_template.default_options["task_status"] = "Incomplete"
        project_template.roles.append({
            "name":
            "Trello",
            "slug":
            "trello",
            "computable":
            False,
            "permissions":
            project_template.roles[0]['permissions'],
            "order":
            70,
        })

        tags_colors = []
        for label in labels:
            name = label['name']
            if not name:
                name = label['color']
            name = name.lower()
            color = self._ensure_hex_color(label['color'])
            tags_colors.append([name, color])

        project = Project(
            name=options.get('name', None) or board['name'],
            description=options.get('description', None) or board['desc'],
            owner=self._user,
            tags_colors=tags_colors,
            creation_template=project_template,
            is_private=options.get('is_private', False),
        )
        (can_create, error_message
         ) = projects_service.check_if_project_can_be_created_or_updated(
             project)
        if not can_create:
            raise exceptions.NotEnoughSlotsForProject(project.is_private, 1,
                                                      error_message)
        project.save()

        if board.get('organization', None):
            trello_avatar_template = "https://trello-logos.s3.amazonaws.com/{}/170.png"
            project_logo_url = trello_avatar_template.format(
                board['organization']['logoHash'])
            data = requests.get(project_logo_url)
            project.logo.save("logo.png", ContentFile(data.content), save=True)

        UserStoryCustomAttribute.objects.create(name="Due",
                                                description="Due date",
                                                type="date",
                                                order=1,
                                                project=project)
        import_service.create_memberships(options.get('users_bindings', {}),
                                          project, self._user, "trello")
        return project
Пример #15
0
    def create(self, request, *args, **kwargs):
        self.check_permissions(request, 'import_project', None)

        data = request.DATA.copy()
        data['owner'] = data.get('owner', request.user.email)

        # Validate if the project can be imported
        is_private = data.get('is_private', False)
        total_memberships = len([
            m for m in data.get("memberships", [])
            if m.get("email", None) != data["owner"]
        ])
        total_memberships = total_memberships + 1  # 1 is the owner
        (enough_slots, error_message
         ) = users_services.has_available_slot_for_import_new_project(
             self.request.user, is_private, total_memberships)
        if not enough_slots:
            raise exc.NotEnoughSlotsForProject(is_private, total_memberships,
                                               error_message)

        # Create Project
        project_serialized = services.store.store_project(data)

        if not project_serialized:
            raise exc.BadRequest(services.store.get_errors())

        # Create roles
        roles_serialized = None
        if "roles" in data:
            roles_serialized = services.store.store_roles(
                project_serialized.object, data)

        if not roles_serialized:
            raise exc.BadRequest(_("We needed at least one role"))

        # Create memberships
        if "memberships" in data:
            services.store.store_memberships(project_serialized.object, data)

        try:
            owner_membership = project_serialized.object.memberships.get(
                user=project_serialized.object.owner)
            owner_membership.is_admin = True
            owner_membership.save()
        except Membership.DoesNotExist:
            Membership.objects.create(
                project=project_serialized.object,
                email=project_serialized.object.owner.email,
                user=project_serialized.object.owner,
                role=project_serialized.object.roles.all().first(),
                is_admin=True)

        # Create project values choicess
        if "points" in data:
            services.store.store_project_attributes_values(
                project_serialized.object, data, "points",
                serializers.PointsExportSerializer)
        if "issue_types" in data:
            services.store.store_project_attributes_values(
                project_serialized.object, data, "issue_types",
                serializers.IssueTypeExportSerializer)
        if "issue_statuses" in data:
            services.store.store_project_attributes_values(
                project_serialized.object,
                data,
                "issue_statuses",
                serializers.IssueStatusExportSerializer,
            )
        if "us_statuses" in data:
            services.store.store_project_attributes_values(
                project_serialized.object,
                data,
                "us_statuses",
                serializers.UserStoryStatusExportSerializer,
            )
        if "task_statuses" in data:
            services.store.store_project_attributes_values(
                project_serialized.object, data, "task_statuses",
                serializers.TaskStatusExportSerializer)
        if "priorities" in data:
            services.store.store_project_attributes_values(
                project_serialized.object, data, "priorities",
                serializers.PriorityExportSerializer)
        if "severities" in data:
            services.store.store_project_attributes_values(
                project_serialized.object, data, "severities",
                serializers.SeverityExportSerializer)

        if ("points" in data or "issues_types" in data
                or "issues_statuses" in data or "us_statuses" in data
                or "task_statuses" in data or "priorities" in data
                or "severities" in data):
            services.store.store_default_project_attributes_values(
                project_serialized.object, data)

        # Created custom attributes
        if "userstorycustomattributes" in data:
            services.store.store_custom_attributes(
                project_serialized.object, data, "userstorycustomattributes",
                serializers.UserStoryCustomAttributeExportSerializer)

        if "taskcustomattributes" in data:
            services.store.store_custom_attributes(
                project_serialized.object, data, "taskcustomattributes",
                serializers.TaskCustomAttributeExportSerializer)

        if "issuecustomattributes" in data:
            services.store.store_custom_attributes(
                project_serialized.object, data, "issuecustomattributes",
                serializers.IssueCustomAttributeExportSerializer)

        # Is there any error?
        errors = services.store.get_errors()
        if errors:
            raise exc.BadRequest(errors)

        # Importer process is OK
        response_data = project_serialized.data
        response_data['id'] = project_serialized.object.id
        headers = self.get_success_headers(response_data)
        return response.Created(response_data, headers=headers)
Пример #16
0
    def load_dump(self, request):
        throttle = throttling.ImportDumpModeRateThrottle()

        if not throttle.allow_request(request, self):
            self.throttled(request, throttle.wait())

        self.check_permissions(request, "load_dump", None)

        dump = request.FILES.get('dump', None)

        if not dump:
            raise exc.WrongArguments(_("Needed dump file"))

        if dump.content_type == "application/gzip":
            dump = gzip.GzipFile(fileobj=dump)

        reader = codecs.getreader("utf-8")

        try:
            dump = json.load(reader(dump))
        except Exception:
            raise exc.WrongArguments(_("Invalid dump format"))

        slug = dump.get('slug', None)
        if slug is not None and Project.objects.filter(slug=slug).exists():
            del dump['slug']

        user = request.user
        dump['owner'] = user.email

        # Validate if the project can be imported
        is_private = dump.get("is_private", False)
        total_memberships = len([
            m for m in dump.get("memberships", [])
            if m.get("email", None) != dump["owner"]
        ])
        total_memberships = total_memberships + 1  # 1 is the owner
        (enough_slots,
         error_message) = users_services.has_available_slot_for_new_project(
             user, is_private, total_memberships)
        if not enough_slots:
            raise exc.NotEnoughSlotsForProject(is_private, total_memberships,
                                               error_message)

        # Async mode
        if settings.CELERY_ENABLED:
            task = tasks.load_project_dump.delay(user, dump)
            return response.Accepted({"import_id": task.id})

        # Sync mode
        try:
            project = services.store_project_from_dict(dump, request.user)
        except err.TaigaImportError as e:
            # On Error
            ## remove project
            if e.project:
                e.project.delete_related_content()
                e.project.delete()

            return response.BadRequest({
                "error": e.message,
                "details": e.errors
            })
        else:
            # On Success
            project_from_qs = project_utils.attach_extra_info(
                Project.objects.all()).get(id=project.id)
            response_data = ProjectSerializer(project_from_qs).data

            return response.Created(response_data)