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)
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)
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)
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)
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)
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)
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)
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)
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()
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()
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)
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)
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)
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
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)
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)