예제 #1
0
    def fetch_members(self, board, trello_board):
        trello_members = trello_board.all_members()

        # For each member, check if he/she doesn't exist. In that case, create him/her
        for trello_member in trello_members:

            # If the member does not exist, create it with empty Trello credentials
            try:
                member = Member.objects.get(
                    trello_member_profile__trello_id=trello_member.id)
                if self.debug:
                    print(u"Member {0} already existed ".format(
                        member.external_username))
            except Member.DoesNotExist:
                member = Member(creator=self.member)
                member.save()
                trello_member_profile = TrelloMemberProfile(
                    trello_id=trello_member.id,
                    member=member,
                    username=trello_member.username,
                    initials=trello_member.initials)
                trello_member_profile.save()

                if self.debug:
                    print(u"Member {0} created".format(
                        member.external_username))

            # Only add the board to the member if he/she has not it yet
            if not member.boards.filter(uuid=board.uuid).exists():
                member.boards.add(board)

            if self.debug:
                print(u"Member {0} has role {1}".format(
                    member.external_username, trello_member.member_type))
            # If this member has no role in this board, add the role to the member
            if not member.roles.filter(board=board).exists():
                if self.debug:
                    print("Creating role {0} for {1}".format(
                        trello_member.member_type, board.name))
                member_role, created = MemberRole.objects.get_or_create(
                    board=board, type=trello_member.member_type)
                member_role.members.add(member)

            # If this member has a role but is different from the role he/she has in Trello,
            # update his/her role
            elif member.roles.get(
                    board=board).type != trello_member.member_type:
                if self.debug:
                    print("Updating {0}'s role from {1} to {2} in {3}".format(
                        member.external_username,
                        member.roles.get(board=board).type,
                        trello_member.member_type, board.name))
                member.roles.clear()
                member_role, created = MemberRole.objects.get_or_create(
                    board=board, type=trello_member.member_type)
                member_role.members.add(member)
예제 #2
0
    def handle(self, *args, **options):
        if 'username' not in options or not options['username'] or len(
                options['username']) != 1:
            self.stdout.write(self.style.ERROR("username is mandatory"))
            return False

        if 'password' not in options or not options['password'] or len(
                options['password']) != 1:
            self.stdout.write(self.style.ERROR("password is mandatory"))
            return False

        username = options['username'][0]
        if User.objects.filter(username=username).exists():
            self.stdout.write(self.style.ERROR("This username already exists"))
            return False

        password = options['password'][0]

        try:
            with transaction.atomic():
                # Creation of user
                user = User(username=username,
                            is_superuser=True,
                            is_active=True)
                user.set_password(password)
                user.save()

                # Assignment of member
                member = Member(user=user)
                member.save()

                # Assignment of user to group administrators
                administrators = Group.objects.get(
                    name=settings.ADMINISTRATOR_GROUP)
                administrators.user_set.add(user)

        except Exception as e:
            self.stdout.write(
                self.style.ERROR(
                    "Supermember couldn't be created successfully because of exception {0}"
                    .format(e)))

        # Everything went well
        self.stdout.write(
            self.style.SUCCESS(
                "Supermember with username {0} has been created successfully".
                format(username)))
예제 #3
0
def task_movements_by_member(request, movement_type="forward", board=None):
    if movement_type != "forward" and movement_type != "backward":
        raise ValueError(
            "{0} is not recognized as a valid movement type".format(
                movement_type))

    # Caching
    chart_uuid = "members.task_movements_by_member-{0}-{1}".format(
        movement_type,
        board.id if board else "user-{0}".format(request.user.id))
    chart = CachedChart.get(board=board, uuid=chart_uuid)
    if chart:
        return chart

    chart_title = u"Task {0} movements as of {1}".format(
        movement_type, timezone.now())
    if board:
        chart_title += u" for board {0}".format(board.name)

    member_chart = pygal.HorizontalBar(title=chart_title,
                                       legend_at_bottom=True,
                                       print_values=True,
                                       print_zeroes=False,
                                       human_readable=True)

    card_movement_filter = {"type": movement_type}
    if board:
        card_movement_filter["board_id"] = board.id

    # If this user is a member, include it in the developers list at the front
    if user_is_member(request.user):
        members = [request.user.member] + list(
            Member.get_user_team_mates(request.user))
    else:
        members = Member.get_user_team_mates(request.user)

    for member_i in members:
        member_name = member_i.external_username
        num_card_movements = member_i.card_movements.filter(
            **card_movement_filter).count()
        if num_card_movements > 0:
            member_chart.add(u"{0}".format(member_name), num_card_movements)

    chart = CachedChart.make(board=board,
                             uuid=chart_uuid,
                             svg=member_chart.render(is_unicode=True))
    return chart.render_django_response()
예제 #4
0
def noise_level_per_weekday(current_user):
    # Caching
    chart_uuid = "noise_measurements.noise_level_per_weekday-{0}".format(
        current_user.id)
    chart = CachedChart.get(board=None, uuid=chart_uuid)
    if chart:
        return chart

    chart_title = u"Noise levels per weekday in db as of {0}".format(
        timezone.now())

    noise_measurement_filter = {
        "member__in": Member.get_user_team_members(current_user)
    }

    noise_measurements = NoiseMeasurement.objects.filter(
        **noise_measurement_filter).order_by("datetime")

    noise_chart = pygal.Line(title=chart_title,
                             legend_at_bottom=True,
                             print_values=True,
                             print_zeroes=False,
                             x_label_rotation=0,
                             human_readable=True)

    noise_values = {"avg": [], "min": [], "max": []}
    weekday_dict = {
        1: "Sunday",
        2: "Monday",
        3: "Tuesday",
        4: "Wednesday",
        5: "Thursday",
        6: "Friday",
        7: "Saturday"
    }
    weekdays = []
    weekday_i = 1
    while weekday_i < 7:
        noise_level_in_hour_i = noise_measurements. \
            filter(datetime__week_day=weekday_i). \
            aggregate(avg=Avg("noise_level"), max=Max("noise_level"), min=Min("noise_level"))

        if noise_level_in_hour_i["avg"] is not None:
            noise_values["avg"].append(noise_level_in_hour_i["avg"])
            noise_values["min"].append(noise_level_in_hour_i["min"])
            noise_values["max"].append(noise_level_in_hour_i["max"])

            weekdays.append(weekday_dict[weekday_i])
        weekday_i += 1

    noise_chart.add("Avg noise level", noise_values["avg"])
    noise_chart.add("Min noise level", noise_values["min"])
    noise_chart.add("Max noise level", noise_values["max"])
    noise_chart.x_labels = weekdays

    chart = CachedChart.make(board=None,
                             uuid=chart_uuid,
                             svg=noise_chart.render(is_unicode=True))
    return chart.render_django_response()
예제 #5
0
 def get_all_from_member(member):
     user = member.user
     boards = get_user_boards(user)
     teammates = Member.get_user_team_members(user)
     forecasters = Forecaster.objects.filter(
         Q(last_updater=member) | Q(board__in=boards)
         | Q(member__in=list(teammates) + [member]))
     return forecasters
예제 #6
0
def noise_level(current_user):

    # Caching
    chart_uuid = "noise_measurements.noise_level-{0}".format(current_user.id)
    chart = CachedChart.get(board=None, uuid=chart_uuid)
    if chart:
        return chart

    chart_title = u"Average noise levels per day in db as of {0}".format(
        timezone.now())

    noise_measurement_filter = {
        "member__in": Member.get_user_team_members(current_user)
    }

    noise_measurements = NoiseMeasurement.objects.filter(
        **noise_measurement_filter).order_by("datetime")

    noise_chart = pygal.Line(title=chart_title,
                             legend_at_bottom=True,
                             print_values=True,
                             print_zeroes=False,
                             x_label_rotation=65,
                             human_readable=True)

    start_datetime = noise_measurements.aggregate(
        min_date=Min("datetime"))["min_date"]
    end_datetime = noise_measurements.aggregate(
        max_date=Max("datetime"))["max_date"]
    if start_datetime is None or end_datetime is None:
        return noise_chart.render_django_response()

    end_date = end_datetime.date()

    noise_values = []
    days = []

    date_i = copy.deepcopy(start_datetime).date()
    while date_i <= end_date:
        date_noise_measurements = noise_measurements.filter(
            datetime__date=date_i)
        if date_noise_measurements.exists():
            noise_values.append(
                numpy.mean([
                    noise_measurement.noise_level
                    for noise_measurement in date_noise_measurements
                ]))
            days.append(date_i.strftime("%Y-%m-%d"))

        date_i += timedelta(days=1)

    noise_chart.add("Average noise level by day", noise_values)
    noise_chart.x_labels = days

    chart = CachedChart.make(board=None,
                             uuid=chart_uuid,
                             svg=noise_chart.render(is_unicode=True))
    return chart.render_django_response()
예제 #7
0
def noise_level_per_hour(current_user):
    # Caching
    chart_uuid = "noise_measurements.noise_level_per_hour-{0}".format(
        current_user.id)
    chart = CachedChart.get(board=None, uuid=chart_uuid)
    if chart:
        return chart

    chart_title = u"Noise levels per hour in db as of {0}".format(
        timezone.now())

    noise_measurement_filter = {
        "member__in": Member.get_user_team_members(current_user)
    }

    noise_measurements = NoiseMeasurement.objects.filter(
        **noise_measurement_filter).order_by("datetime")

    noise_chart = pygal.Line(title=chart_title,
                             legend_at_bottom=True,
                             print_values=True,
                             print_zeroes=False,
                             x_label_rotation=0,
                             human_readable=True)

    noise_values = {"avg": [], "min": [], "max": []}
    hours = []
    hour_i = 0
    while hour_i < 24:
        noise_level_in_hour_i = noise_measurements.\
            filter(datetime__hour=hour_i).\
            aggregate(avg=Avg("noise_level"), max=Max("noise_level"), min=Min("noise_level"))

        if noise_level_in_hour_i["avg"] is not None:
            noise_values["avg"].append(noise_level_in_hour_i["avg"])
            noise_values["min"].append(noise_level_in_hour_i["min"])
            noise_values["max"].append(noise_level_in_hour_i["max"])

            hours.append(hour_i)
        hour_i += 1

    noise_chart.add("Avg noise level", noise_values["avg"])
    noise_chart.add("Min noise level", noise_values["min"])
    noise_chart.add("Max noise level", noise_values["max"])
    noise_chart.x_labels = hours

    chart = CachedChart.make(board=None,
                             uuid=chart_uuid,
                             svg=noise_chart.render(is_unicode=True))
    return chart.render_django_response()
예제 #8
0
 def __init__(self, *args, **kwarsg):
     super(FilterForecastersForm, self).__init__(*args, **kwarsg)
     # Regression model choices
     self.fields["model"].choices = REGRESSION_MODELS
     # Board and members
     current_request = CrequestMiddleware.get_request()
     current_user = current_request.user
     boards = get_user_boards(current_user)
     members = Member.get_user_team_members(current_user)
     self.fields["board"].choices = [("", "All")] + [(board.id, board.name)
                                                     for board in boards]
     self.fields["member"].choices = [("", "None")] + [
         (member.id, member.external_username) for member in members
     ]
예제 #9
0
def new(request):
    user = request.user
    current_member = user.member

    member = Member(creator=current_member, is_public=False)
    if request.method == "POST":

        form = NewMemberForm(request.POST, instance=member)
        if form.is_valid():
            with transaction.atomic():
                form.save(commit=True)
                member_password = form.cleaned_data.get("password")
                send_new_member_email(member, member_password)
            return HttpResponseRedirect(reverse("members:view_members"))

    else:
        form = NewMemberForm(instance=member)

    replacements = {"member": member, "form": form}
    return render(request, "members/new.html", replacements)
예제 #10
0
def subjective_noise_level(current_user, month=None, year=None):

    # Caching
    chart_uuid = "noise_measurements.subjective_noise_level-{0}-{1}-{2}".format(
        current_user.id, month if month else "None", year if year else "None")
    chart = CachedChart.get(board=None, uuid=chart_uuid)
    if chart:
        return chart

    chart_title = u"Subjective noise levels as of {0}".format(timezone.now())

    noise_measurement_filter = {
        "member__in": Member.get_user_team_members(current_user)
    }
    noise_measurements = NoiseMeasurement.objects.filter(
        **noise_measurement_filter).order_by("datetime")

    if month and year and 1 <= month <= 12:
        noise_measurements = noise_measurements.filter(datetime__month=month,
                                                       datetime__year=year)

    noise_chart = pygal.Bar(title=chart_title,
                            legend_at_bottom=True,
                            print_values=True,
                            print_zeroes=False,
                            human_readable=True,
                            x_label_rotation=45)

    subjective_noise_levels = dict(NoiseMeasurement.SUBJECTIVE_NOISE_LEVELS)
    for level_key, level_name in subjective_noise_levels.items():
        noise_chart.add(
            u"{0}".format(level_name),
            noise_measurements.filter(
                subjective_noise_level=level_key).count())

    chart = CachedChart.make(board=None,
                             uuid=chart_uuid,
                             svg=noise_chart.render(is_unicode=True))
    return chart.render_django_response()
예제 #11
0
    def save(self, commit=False):
        member = Member()

        user = User(
            first_name=self.cleaned_data.get("first_name"),
            last_name=self.cleaned_data.get("last_name"),
            username=self.cleaned_data.get("username"),
            email=self.cleaned_data.get("email"),
        )

        user.set_password(self.cleaned_data["password1"])

        if commit:
            user.save()
            member.user = user
            member.max_number_of_boards = Member.DEFAULT_MAX_NUMBER_OF_BOARDS
            member.save()

        return member
예제 #12
0
def _number_of_interruptions(current_user,
                             board,
                             chart_title,
                             interruption_measurement,
                             incremental=False):

    # Caching
    chart_uuid = "interruptions.{0}".format(
        hashlib.sha256("_number_of_interruptions-{0}-{1}-{2}-{3}-{4}".format(
            current_user.id,
            board.id if board else "user-{0}".format(current_user.id),
            inspect.getsource(interruption_measurement),
            "incremental" if incremental else "absolute",
            chart_title)).hexdigest())
    chart = CachedChart.get(board=board, uuid=chart_uuid)
    if chart:
        return chart

    if board:
        chart_title += u" for board {0} as of {1}".format(
            board.name, board.get_human_fetch_datetime())

    interruptions_chart = pygal.Line(title=chart_title,
                                     legend_at_bottom=True,
                                     print_values=True,
                                     print_zeroes=False,
                                     x_label_rotation=65,
                                     human_readable=True)

    if incremental:
        datetime_filter = "datetime__date__lte"
        interruptions_chart.print_values = False
    else:
        datetime_filter = "datetime__date"

    interruptions_filter = {}
    if board:
        interruptions_filter["board"] = board
        boards = [board]
    else:
        boards = get_user_boards(current_user)
        interruptions_filter["member__in"] = Member.get_user_team_members(
            current_user)

    board_values = {board.id: [] for board in boards}

    interruptions = Interruption.objects.filter(
        **interruptions_filter).order_by("datetime")
    if not interruptions.exists():
        return interruptions_chart.render_django_response()

    min_datetime = interruptions.aggregate(
        min_datetime=Min("datetime"))["min_datetime"]
    max_datetime = interruptions.aggregate(
        max_datetime=Max("datetime"))["max_datetime"]

    date_i = copy.deepcopy(min_datetime.date())
    max_date = max_datetime.date() + timedelta(days=2)
    days = []
    num_interruptions = []
    while date_i <= max_date:
        interruptions_filter = {datetime_filter: date_i}
        interruptions_on_date = interruptions.filter(**interruptions_filter)
        interruptions_on_date_value = interruption_measurement(
            interruptions_on_date)

        # Add only values when there is some interruption in any project
        if interruptions_on_date_value > 0:
            days.append(date_i.strftime("%Y-%m-%d"))
            num_interruptions.append(interruptions_on_date_value)

            if board is None:
                for board_i in boards:
                    board_i_interruptions_value = interruption_measurement(
                        interruptions_on_date.filter(board=board_i))
                    board_values[board_i.id].append(
                        board_i_interruptions_value)

        date_i += timedelta(days=1)

    interruptions_chart.add(u"All interruptions", num_interruptions)
    for board_i in boards:
        if sum(board_values[board_i.id]) > 0:
            interruptions_chart.add(board_i.name, board_values[board_i.id])

    interruptions_chart.x_labels = days

    chart = CachedChart.make(board=None,
                             uuid=chart_uuid,
                             svg=interruptions_chart.render(is_unicode=True))
    return chart.render_django_response()
예제 #13
0
def _interruption_measurement_by_month(current_user,
                                       chart_title,
                                       interruption_measurement,
                                       board=None):

    chart_uuid = "interruptions.{0}".format(
        hashlib.sha256("_interruption_measurement_by_month-{0}-{1}-{2}".format(
            current_user.id, inspect.getsource(interruption_measurement),
            board.id
            if board else "username-{0}".format(current_user.id))).hexdigest())
    chart = CachedChart.get(board=board, uuid=chart_uuid)
    if chart:
        return chart

    if board:
        chart_title += u" for board {0} as of {1}".format(
            board.name, board.get_human_fetch_datetime())

    interruptions_filter = {}
    if board:
        interruptions_filter["board"] = board
    else:
        interruptions_filter["member__in"] = Member.get_user_team_members(
            current_user)

    interruptions = Interruption.objects.filter(
        **interruptions_filter).order_by("datetime")

    interruptions_chart = pygal.Line(title=chart_title,
                                     legend_at_bottom=True,
                                     print_values=True,
                                     print_zeroes=False,
                                     human_readable=True)

    min_datetime = interruptions.aggregate(
        min_datetime=Min("datetime"))["min_datetime"]
    max_datetime = interruptions.aggregate(
        max_datetime=Min("datetime"))["max_datetime"]
    if min_datetime is None or max_datetime is None:
        return interruptions_chart.render_django_response()

    datetime_i = copy.deepcopy(min_datetime)

    date_i = datetime_i.date()
    month_i = date_i.month
    year_i = date_i.year

    last_month = max_datetime.month
    last_year = max_datetime.year

    if board is None:
        boards = get_user_boards(current_user)
    else:
        boards = [board]

    months = []
    values = []
    board_values = {board.id: [] for board in boards}
    has_board_values = {board.id: False for board in boards}

    while year_i < last_year or year_i == last_year and month_i <= last_month:
        monthly_interruptions = interruptions.filter(datetime__month=month_i,
                                                     datetime__year=year_i)
        monthly_measurement = interruption_measurement(monthly_interruptions)
        # For each month that have some data, add it to the chart
        if monthly_measurement > 0:
            months.append(u"{0}-{1}".format(year_i, month_i))
            values.append(monthly_measurement)
            for board in boards:
                monthly_interruption_measurement = interruption_measurement(
                    monthly_interruptions.filter(board=board))
                board_values[board.id].append(monthly_interruption_measurement)
                if monthly_interruption_measurement > 0:
                    has_board_values[board.id] = True

        month_i += 1
        if month_i > 12:
            month_i = 1
            year_i += 1

    interruptions_chart.x_labels = months
    interruptions_chart.add(u"All interruptions", values)
    for board in boards:
        if has_board_values[board.id]:
            interruptions_chart.add(board.name, board_values[board.id])

    chart = CachedChart.make(board=None,
                             uuid=chart_uuid,
                             svg=interruptions_chart.render(is_unicode=True))
    return chart.render_django_response()
예제 #14
0
def index(request):
    member = None
    boards = []
    member_multiboards = []
    team_mates = []
    current_user = request.user
    if user_is_member(current_user):
        member = current_user.member
        member_multiboards = member.multiboards. \
            filter(show_in_index=True, is_archived=False). \
            order_by("order", "name")

    lists = []
    if current_user.is_authenticated():
        team_mates = Member.get_user_team_mates(request.user)
        boards = get_user_boards(current_user).filter(
            is_archived=False).order_by("name")

    now = timezone.now()
    today = now.date()
    week_of_year = get_week_of_year(today)

    weeks_of_year = get_weeks_of_year_since_one_year_ago()

    replacements = {
        "any_card_has_value":
        Card.objects.filter(board__in=boards, value__isnull=False).exists(),
        "has_noise_measurements":
        NoiseMeasurement.objects.filter(
            Q(member__in=team_mates) | Q(member=member)).exists(),
        "weeks_of_year":
        weeks_of_year,
        "lists":
        lists,
        "boards":
        boards,
        "week_of_year":
        week_of_year,
        "member":
        member,
        "multiboards":
        member_multiboards,
        "developers":
        [member] + (list(member.team_mates.filter(
            is_developer=True)) if member else list(team_mates)),
        "downtime_developers": ([
            dev for dev in member.team_members.filter(is_developer=True)
            if dev.is_in_downtime
        ]) if member else [],
        "pending_red_cards":
        Card.objects.filter(board__in=boards,
                            list__type="ready_to_develop",
                            is_closed=False,
                            labels__color="red").order_by(
                                "board__name", "name"),
        "pending_orange_cards":
        Card.objects.filter(board__in=boards,
                            list__type="ready_to_develop",
                            is_closed=False,
                            labels__color="orange").order_by(
                                "board__name", "name"),
        "pending_yellow_cards":
        Card.objects.filter(board__in=boards,
                            list__type="ready_to_develop",
                            is_closed=False,
                            labels__color="yellow").order_by(
                                "board__name", "name")
    }
    return render(request, "index/index.html", replacements)
예제 #15
0
    def _create_comments(card):
        card_comments = []

        # {u'type': u'commentCard', u'idMemberCreator': u'56e2ac8e14e4eda06ac6b8fd',
        #  u'memberCreator': {u'username': u'diegoj5', u'fullName': u'Diego J.', u'initials': u'DJ',
        #                     u'id': u'56e2ac8e14e4eda06ac6b8fd', u'avatarHash': u'a3086f12908905354b15972cd67b64f8'},
        #  u'date': u'2016-04-20T23:06:38.279Z',
        #  u'data': {u'text': u'Un comentario', u'list': {u'name': u'En desarrollo', u'id': u'5717fb3fde6bdaed40201667'},
        #            u'board': {u'id': u'5717fb368199521a139712f0', u'name': u'Test', u'shortLink': u'2CGPEnM2'},
        #            u'card': {u'idShort': 6, u'id': u'57180ae1ed24b1cff7f8da7c', u'name': u'Por todas',
        #                      u'shortLink': u'bnK3c1jF'}}, u'id': u'57180b7e25abc60313461aaf'}
        member_dict = {}

        local_timezone = pytz.timezone(settings.TIME_ZONE)

        card_deleted_comments = {
            comment.uuid: comment
            for comment in card.comments.all()
        }

        # Create each one of the comments
        for comment in card.trello_card.comments:

            # Author of the comment loaded using memoization
            trello_member_id = comment["idMemberCreator"]
            if trello_member_id not in member_dict:
                try:
                    member_dict[trello_member_id] = Member.objects.get(
                        trello_member_profile__trello_id=comment[
                            "idMemberCreator"])
                # If the member doesn't exist, create it
                except Member.DoesNotExist as e:
                    deleted_member = Member()
                    deleted_member.save()
                    try:
                        trello_member_profile = TrelloMemberProfile.objects.get(
                            trello_id=comment["idMemberCreator"],
                            username=comment["memberCreator"]["username"],
                            initials=comment["memberCreator"]["initials"])
                    except TrelloMemberProfile.DoesNotExist:
                        trello_member_profile = TrelloMemberProfile(
                            member=deleted_member,
                            trello_id=comment["idMemberCreator"],
                            username=comment["memberCreator"]["username"],
                            initials=comment["memberCreator"]["initials"])

                    trello_member_profile.member = deleted_member
                    trello_member_profile.save()

                    member_dict[trello_member_id] = deleted_member

            author = member_dict[trello_member_id]

            # Comment uuid
            uuid = comment["id"]

            # Comment content
            content = comment["data"]["text"]

            # Comment creation datetime
            comment_naive_creation_datetime = datetime.strptime(
                comment["date"], '%Y-%m-%dT%H:%M:%S.%fZ')
            comment_creation_datetime = local_timezone.localize(
                comment_naive_creation_datetime)

            # Creation of the card comment
            try:
                card_comment = card.comments.get(uuid=uuid)
                card_comment.content = content
                card_comment.blocking_card = None
                del card_deleted_comments[uuid]
            except CardComment.DoesNotExist:
                card_comment = CardComment(
                    uuid=uuid,
                    card=card,
                    board=card.board,
                    author=author,
                    creation_datetime=comment_creation_datetime,
                    content=content)

            #print "{0} {1} {2}".format(card.name, card_comment.content, card_comment.creation_datetime)

            # Check if comment has a blocking card
            blocking_card = card_comment.blocking_card_from_content
            if blocking_card:
                card_comment.blocking_card = blocking_card

            card_comment.save()

            # Create card comment list
            card_comments.append(card_comment)

        # Delete all card comments that are not present in trello.com
        for comment_uuid, comment in card_deleted_comments.items():
            comment.delete()

        for trello_member_id, member in member_dict.items():
            if not card.members.filter(
                    trello_member_profile__trello_id=trello_member_id).exists(
                    ):
                card.members.add(member)

        return card_comments