Ejemplo n.º 1
0
def modify_karma(request):
    """Add a Karma note to a user profile."""

    try:
        profile_pk = int(request.POST['profile_pk'])
    except (KeyError, ValueError):
        raise Http404

    profile = get_object_or_404(Profile, pk=profile_pk)
    if profile.is_private():
        raise PermissionDenied

    note = KarmaNote(
        user=profile.user,
        moderator=request.user,
        note=request.POST.get('note', '').strip())

    try:
        note.karma = int(request.POST['karma'])
    except (KeyError, ValueError):
        note.karma = 0

    try:
        if not note.note:
            raise ValueError('note cannot be empty')
        elif note.karma > 100 or note.karma < -100:
            raise ValueError('Max karma amount has to be between -100 and 100, you entered {}'.format(note.karma))
        else:
            note.save()
            profile.karma += note.karma
            profile.save()
    except ValueError as e:
        logging.getLogger(__name__).warn('ValueError: modifying karma failed because {}'.format(e))

    return redirect(reverse('member-detail', args=[profile.user.username]))
Ejemplo n.º 2
0
def modify_karma(request):
    """ Add a Karma note to the user profile """

    if not request.user.has_perm("member.change_profile"):
        raise PermissionDenied

    try:
        profile_pk = request.POST["profile_pk"]
    except (KeyError, ValueError):
        raise Http404

    profile = get_object_or_404(Profile, pk=profile_pk)
    if profile.is_private():
        raise PermissionDenied

    note = KarmaNote()
    note.user = profile.user
    note.staff = request.user
    note.comment = request.POST["warning"]
    try:
        note.value = int(request.POST["points"])
    except (KeyError, ValueError):
        note.value = 0

    note.save()

    profile.karma += note.value
    profile.save()

    return redirect(reverse("member-detail", args=[profile.user.username]))
Ejemplo n.º 3
0
 def update_profile(self, profile, form):
     profile.show_email = "show_email" in form.cleaned_data.get("options")
     new_username = form.cleaned_data.get("username")
     previous_username = form.cleaned_data.get("previous_username")
     new_email = form.cleaned_data.get("email")
     previous_email = form.cleaned_data.get("previous_email")
     if new_username and new_username != previous_username:
         # Add a karma message for the staff
         bot = get_object_or_404(
             User, username=settings.ZDS_APP["member"]["bot_account"])
         KarmaNote(
             user=profile.user,
             moderator=bot,
             note=_("{} s'est renommé {}").format(profile.user.username,
                                                  new_username),
             karma=0,
         ).save()
         # Change the username
         profile.user.username = new_username
         # update skeleton
         profile.username_skeleton = Profile.find_username_skeleton(
             new_username)
     if new_email and new_email != previous_email:
         profile.user.email = new_email
         # Create an alert for the staff if it's a new provider
         provider = provider = new_email.split("@")[-1].lower()
         if (not NewEmailProvider.objects.filter(
                 provider=provider).exists() and not User.objects.filter(
                     email__iendswith=f"@{provider}").exclude(
                         pk=profile.user.pk).exists()):
             NewEmailProvider.objects.create(user=profile.user,
                                             provider=provider,
                                             use=EMAIL_EDIT)
Ejemplo n.º 4
0
def modify_karma(request):
    """Add a Karma note to a user profile."""

    try:
        profile_pk = int(request.POST['profile_pk'])
    except (KeyError, ValueError):
        raise Http404

    profile = get_object_or_404(Profile, pk=profile_pk)
    if profile.is_private():
        raise PermissionDenied

    note = KarmaNote(
        user=profile.user,
        moderator=request.user,
        note=request.POST.get('note', '').strip())

    try:
        note.karma = int(request.POST['karma'])
    except (KeyError, ValueError):
        note.karma = 0

    try:
        if not note.note:
            raise ValueError('note cannot be empty')
        elif note.karma > 100 or note.karma < -100:
            raise ValueError('Max karma amount has to be between -100 and 100, you entered {}'.format(note.karma))
        else:
            note.save()
            profile.karma += note.karma
            profile.save()
    except ValueError as e:
        logging.getLogger('zds.member').warn('ValueError: modifying karma failed because {}'.format(e))

    return redirect(reverse('member-detail', args=[profile.user.username]))
Ejemplo n.º 5
0
    def test_moderation_history(self):
        user = ProfileFactory().user

        ban = Ban(
            user=user,
            moderator=self.staff,
            type="Lecture Seule Temporaire",
            note="Test de LS",
            pubdate=datetime.now(),
        )
        ban.save()

        note = KarmaNote(
            user=user,
            moderator=self.staff,
            karma=5,
            note="Test de karma",
            pubdate=datetime.now(),
        )
        note.save()

        # staff rights are required to view the history, check that
        self.client.logout()
        self.client.force_login(user)
        result = self.client.get(user.profile.get_absolute_url(), follow=False)
        self.assertNotContains(result, "Historique de modération")

        self.client.logout()
        self.client.force_login(self.staff)
        result = self.client.get(user.profile.get_absolute_url(), follow=False)
        self.assertContains(result, "Historique de modération")

        # check that the note and the sanction are in the context
        self.assertIn(ban, result.context["actions"])
        self.assertIn(note, result.context["actions"])

        # and are displayed
        self.assertContains(result, "Test de LS")
        self.assertContains(result, "Test de karma")
Ejemplo n.º 6
0
 def update_profile(self, profile, form):
     if form.data['username']:
         # Add a karma message for the staff
         bot = get_object_or_404(User, username=settings.ZDS_APP['member']['bot_account'])
         KarmaNote(user=profile.user,
                   staff=bot,
                   comment=_(u"{} s'est renommé {}").format(profile.user.username, form.data['username']),
                   value=0).save()
         # Change the pseudo
         profile.user.username = form.data['username']
     if form.data['email']:
         if form.data['email'].strip() != '':
             profile.user.email = form.data['email']
Ejemplo n.º 7
0
def modify_karma(request):
    """ Add a Karma note to the user profile """

    if not request.user.has_perm("member.change_profile"):
        raise PermissionDenied

    try:
        profile_pk = int(request.POST["profile_pk"])
    except (KeyError, ValueError):
        raise Http404

    profile = get_object_or_404(Profile, pk=profile_pk)
    if profile.is_private():
        raise PermissionDenied

    note = KarmaNote()
    note.user = profile.user
    note.staff = request.user
    note.comment = request.POST.get("warning", "")

    try:
        note.value = int(request.POST["points"])
    except (KeyError, ValueError):
        note.value = 0

    try:
        if note.comment == "":
            raise ValueError("note.comment must not be empty")
        elif note.value > 100 or note.value < -100:
            raise ValueError(
                "note.value must be between -100 and 100 {} given".format(
                    note.value))
        else:
            note.save()
            profile.karma += note.value
            profile.save()
    except ValueError as e:
        logging.getLogger("zds.member").warn(
            "ValueError: modifying karma failed because {}".format(e))

    return redirect(reverse("member-detail", args=[profile.user.username]))
Ejemplo n.º 8
0
 def update_profile(self, profile, form):
     profile.show_email = 'show_email' in form.cleaned_data.get('options')
     new_username = form.cleaned_data.get('username')
     previous_username = form.cleaned_data.get('previous_username')
     new_email = form.cleaned_data.get('email')
     previous_email = form.cleaned_data.get('previous_email')
     if new_username and new_username != previous_username:
         # Add a karma message for the staff
         bot = get_object_or_404(
             User, username=settings.ZDS_APP['member']['bot_account'])
         KarmaNote(user=profile.user,
                   moderator=bot,
                   note=_(u"{} s'est renommé {}").format(
                       profile.user.username, new_username),
                   karma=0).save()
         # Change the pseudo
         profile.user.username = new_username
     if new_email and new_email != previous_email:
         profile.user.email = new_email
Ejemplo n.º 9
0
 def update_profile(self, profile, form):
     profile.show_email = 'show_email' in form.cleaned_data.get('options')
     new_username = form.cleaned_data.get('username')
     previous_username = form.cleaned_data.get('previous_username')
     new_email = form.cleaned_data.get('email')
     previous_email = form.cleaned_data.get('previous_email')
     if new_username and new_username != previous_username:
         # Add a karma message for the staff
         bot = get_object_or_404(User, username=settings.ZDS_APP['member']['bot_account'])
         KarmaNote(user=profile.user,
                   moderator=bot,
                   note=_("{} s'est renommé {}").format(profile.user.username, new_username),
                   karma=0).save()
         # Change the username
         profile.user.username = new_username
     if new_email and new_email != previous_email:
         profile.user.email = new_email
         # Create an alert for the staff if it's a new provider
         provider = provider = new_email.split('@')[-1].lower()
         if not NewEmailProvider.objects.filter(provider=provider).exists() \
                 and not User.objects.filter(email__iendswith='@{}'.format(provider)) \
                 .exclude(pk=profile.user.pk).exists():
             NewEmailProvider.objects.create(user=profile.user, provider=provider, use=EMAIL_EDIT)
Ejemplo n.º 10
0
def modify_karma(request):
    """Add a Karma note to a user profile."""

    try:
        profile_pk = int(request.POST["profile_pk"])
    except (KeyError, ValueError):
        raise Http404

    profile = get_object_or_404(Profile, pk=profile_pk)
    if profile.is_private():
        raise PermissionDenied

    note = KarmaNote(user=profile.user,
                     moderator=request.user,
                     note=request.POST.get("note", "").strip())

    try:
        note.karma = int(request.POST["karma"])
    except (KeyError, ValueError):
        note.karma = 0

    try:
        if not note.note:
            raise ValueError("note cannot be empty")
        elif note.karma > 100 or note.karma < -100:
            raise ValueError(
                f"Max karma amount has to be between -100 and 100, you entered {note.karma}"
            )
        else:
            note.save()
            profile.karma += note.karma
            profile.save()
    except ValueError as e:
        logging.getLogger(__name__).warning(
            f"ValueError: modifying karma failed because {e}")

    return redirect(reverse("member-detail", args=[profile.user.username]))
Ejemplo n.º 11
0
    def test_unregister(self):
        """
        To test that unregistering user is working.
        """

        # test not logged user can't unregister.
        self.client.logout()
        result = self.client.post(reverse("member-unregister"), follow=False)
        self.assertEqual(result.status_code, 302)

        # test logged user can unregister.
        user = ProfileFactory()
        self.client.force_login(user.user)
        result = self.client.post(reverse("member-unregister"), follow=False)
        self.assertEqual(result.status_code, 302)
        self.assertEqual(User.objects.filter(username=user.user.username).count(), 0)

        # Attach a user at tutorials, articles, topics and private topics. After that,
        # unregister this user and check that he is well removed in all contents.
        user = ProfileFactory()
        user2 = ProfileFactory()
        alone_gallery = GalleryFactory()
        UserGalleryFactory(gallery=alone_gallery, user=user.user)
        shared_gallery = GalleryFactory()
        UserGalleryFactory(gallery=shared_gallery, user=user.user)
        UserGalleryFactory(gallery=shared_gallery, user=user2.user)
        # first case : a published tutorial with only one author
        published_tutorial_alone = PublishedContentFactory(type="TUTORIAL")
        published_tutorial_alone.authors.add(user.user)
        published_tutorial_alone.save()
        # second case : a published tutorial with two authors
        published_tutorial_2 = PublishedContentFactory(type="TUTORIAL")
        published_tutorial_2.authors.add(user.user)
        published_tutorial_2.authors.add(user2.user)
        published_tutorial_2.save()
        # third case : a private tutorial with only one author
        writing_tutorial_alone = PublishableContentFactory(type="TUTORIAL")
        writing_tutorial_alone.authors.add(user.user)
        writing_tutorial_alone.save()
        writing_tutorial_alone_galler_path = writing_tutorial_alone.gallery.get_gallery_path()
        # fourth case : a private tutorial with at least two authors
        writing_tutorial_2 = PublishableContentFactory(type="TUTORIAL")
        writing_tutorial_2.authors.add(user.user)
        writing_tutorial_2.authors.add(user2.user)
        writing_tutorial_2.save()
        self.client.force_login(self.staff)
        # same thing for articles
        published_article_alone = PublishedContentFactory(type="ARTICLE")
        published_article_alone.authors.add(user.user)
        published_article_alone.save()
        published_article_2 = PublishedContentFactory(type="ARTICLE")
        published_article_2.authors.add(user.user)
        published_article_2.authors.add(user2.user)
        published_article_2.save()
        writing_article_alone = PublishableContentFactory(type="ARTICLE")
        writing_article_alone.authors.add(user.user)
        writing_article_alone.save()
        writing_article_2 = PublishableContentFactory(type="ARTICLE")
        writing_article_2.authors.add(user.user)
        writing_article_2.authors.add(user2.user)
        writing_article_2.save()
        # beta content
        beta_forum = ForumFactory(category=ForumCategoryFactory())
        beta_content = BetaContentFactory(author_list=[user.user], forum=beta_forum)
        beta_content_2 = BetaContentFactory(author_list=[user.user, user2.user], forum=beta_forum)
        # about posts and topics
        authored_topic = TopicFactory(author=user.user, forum=self.forum11, solved_by=user.user)
        answered_topic = TopicFactory(author=user2.user, forum=self.forum11)
        PostFactory(topic=answered_topic, author=user.user, position=2)
        edited_answer = PostFactory(topic=answered_topic, author=user.user, position=3)
        edited_answer.editor = user.user
        edited_answer.save()

        upvoted_answer = PostFactory(topic=answered_topic, author=user2.user, position=4)
        upvoted_answer.like += 1
        upvoted_answer.save()
        CommentVote.objects.create(user=user.user, comment=upvoted_answer, positive=True)

        private_topic = PrivateTopicFactory(author=user.user)
        private_topic.participants.add(user2.user)
        private_topic.save()
        PrivatePostFactory(author=user.user, privatetopic=private_topic, position_in_topic=1)

        # add API key
        self.assertEqual(Application.objects.count(), 0)
        self.assertEqual(AccessToken.objects.count(), 0)
        api_application = Application()
        api_application.client_id = "foobar"
        api_application.user = user.user
        api_application.client_type = "confidential"
        api_application.authorization_grant_type = "password"
        api_application.client_secret = "42"
        api_application.save()
        token = AccessToken()
        token.user = user.user
        token.token = "r@d0m"
        token.application = api_application
        token.expires = datetime.now()
        token.save()
        self.assertEqual(Application.objects.count(), 1)
        self.assertEqual(AccessToken.objects.count(), 1)

        # add a karma note and a sanction with this user
        note = KarmaNote(moderator=user.user, user=user2.user, note="Good!", karma=5)
        note.save()
        ban = Ban(moderator=user.user, user=user2.user, type="Ban définitif", note="Test")
        ban.save()

        # login and unregister:
        self.client.force_login(user.user)
        result = self.client.post(reverse("member-unregister"), follow=False)
        self.assertEqual(result.status_code, 302)

        # check that the bot have taken authorship of tutorial:
        self.assertEqual(published_tutorial_alone.authors.count(), 1)
        self.assertEqual(
            published_tutorial_alone.authors.first().username, settings.ZDS_APP["member"]["external_account"]
        )
        self.assertFalse(os.path.exists(writing_tutorial_alone_galler_path))
        self.assertEqual(published_tutorial_2.authors.count(), 1)
        self.assertEqual(
            published_tutorial_2.authors.filter(username=settings.ZDS_APP["member"]["external_account"]).count(), 0
        )

        # check that published tutorials remain published and accessible
        self.assertIsNotNone(published_tutorial_2.public_version.get_prod_path())
        self.assertTrue(os.path.exists(published_tutorial_2.public_version.get_prod_path()))
        self.assertIsNotNone(published_tutorial_alone.public_version.get_prod_path())
        self.assertTrue(os.path.exists(published_tutorial_alone.public_version.get_prod_path()))
        self.assertEqual(
            self.client.get(
                reverse("tutorial:view", args=[published_tutorial_alone.pk, published_tutorial_alone.slug]),
                follow=False,
            ).status_code,
            200,
        )
        self.assertEqual(
            self.client.get(
                reverse("tutorial:view", args=[published_tutorial_2.pk, published_tutorial_2.slug]), follow=False
            ).status_code,
            200,
        )

        # test that published articles remain accessible
        self.assertTrue(os.path.exists(published_article_alone.public_version.get_prod_path()))
        self.assertEqual(
            self.client.get(
                reverse("article:view", args=[published_article_alone.pk, published_article_alone.slug]), follow=True
            ).status_code,
            200,
        )
        self.assertEqual(
            self.client.get(
                reverse("article:view", args=[published_article_2.pk, published_article_2.slug]), follow=True
            ).status_code,
            200,
        )

        # check that the tutorial for which the author was alone does not exists anymore
        self.assertEqual(PublishableContent.objects.filter(pk=writing_tutorial_alone.pk).count(), 0)
        self.assertFalse(os.path.exists(writing_tutorial_alone.get_repo_path()))

        # check that bot haven't take the authorship of the tuto with more than one author
        self.assertEqual(writing_tutorial_2.authors.count(), 1)
        self.assertEqual(
            writing_tutorial_2.authors.filter(username=settings.ZDS_APP["member"]["external_account"]).count(), 0
        )

        # authorship for the article for which user was the only author
        self.assertEqual(published_article_alone.authors.count(), 1)
        self.assertEqual(
            published_article_alone.authors.first().username, settings.ZDS_APP["member"]["external_account"]
        )
        self.assertEqual(published_article_2.authors.count(), 1)

        self.assertEqual(PublishableContent.objects.filter(pk=writing_article_alone.pk).count(), 0)
        self.assertFalse(os.path.exists(writing_article_alone.get_repo_path()))

        # not bot if another author:
        self.assertEqual(
            published_article_2.authors.filter(username=settings.ZDS_APP["member"]["external_account"]).count(), 0
        )
        self.assertEqual(writing_article_2.authors.count(), 1)
        self.assertEqual(
            writing_article_2.authors.filter(username=settings.ZDS_APP["member"]["external_account"]).count(), 0
        )

        # topics, gallery and PMs:
        self.assertEqual(Topic.objects.filter(author__username=user.user.username).count(), 0)
        self.assertEqual(Topic.objects.filter(solved_by=user.user).count(), 0)
        self.assertEqual(Topic.objects.filter(solved_by=self.anonymous).count(), 1)
        self.assertEqual(Post.objects.filter(author__username=user.user.username).count(), 0)
        self.assertEqual(Post.objects.filter(editor__username=user.user.username).count(), 0)
        self.assertEqual(PrivatePost.objects.filter(author__username=user.user.username).count(), 0)
        self.assertEqual(PrivateTopic.objects.filter(author__username=user.user.username).count(), 0)

        self.assertIsNotNone(Topic.objects.get(pk=authored_topic.pk))
        self.assertIsNotNone(PrivateTopic.objects.get(pk=private_topic.pk))
        self.assertIsNotNone(Gallery.objects.get(pk=alone_gallery.pk))
        self.assertEqual(alone_gallery.get_linked_users().count(), 1)
        self.assertEqual(shared_gallery.get_linked_users().count(), 1)
        self.assertEqual(UserGallery.objects.filter(user=user.user).count(), 0)
        self.assertEqual(CommentVote.objects.filter(user=user.user, positive=True).count(), 0)
        self.assertEqual(Post.objects.filter(pk=upvoted_answer.id).first().like, 0)

        # zep 12, published contents and beta
        self.assertIsNotNone(PublishedContent.objects.filter(content__pk=published_tutorial_alone.pk).first())
        self.assertIsNotNone(PublishedContent.objects.filter(content__pk=published_tutorial_2.pk).first())
        self.assertTrue(Topic.objects.get(pk=beta_content.beta_topic.pk).is_locked)
        self.assertFalse(Topic.objects.get(pk=beta_content_2.beta_topic.pk).is_locked)

        # check API
        self.assertEqual(Application.objects.count(), 0)
        self.assertEqual(AccessToken.objects.count(), 0)

        # check that the karma note and the sanction were kept
        self.assertTrue(KarmaNote.objects.filter(pk=note.pk).exists())
        self.assertTrue(Ban.objects.filter(pk=ban.pk).exists())
Ejemplo n.º 12
0
    def import_users(self):
        print("\nImporting users...")

        with self.connection.cursor(MySQLdb.cursors.DictCursor) as cursor:
            cursor.execute("SELECT COUNT(*) AS users_count FROM fluxbb_users")
            users_count = cursor.fetchone()['users_count']

            print("{} users to be imported".format(users_count))

            # TODO add location support into the new site
            cursor.execute('SELECT '
                           'fluxbb_users.id AS user_id, '
                           'fluxbb_users.username AS username, '
                           'fluxbb_users.email AS email, '
                           'fluxbb_users.url AS website, '
                           'fluxbb_users.signature AS signature, '
                           'fluxbb_users.show_sig AS show_signature, '
                           'fluxbb_users.registered AS registration_date, '
                           'fluxbb_users.last_visit AS last_visit, '
                           'fluxbb_users.admin_note AS admin_note, '
                           'fluxbb_users.title AS title, '
                           'fluxbb_groups.g_title AS group_name, '
                           '('
                           '  SELECT fluxbb_users.username'
                           '  FROM fluxbb_users'
                           '  WHERE fluxbb_users.id = fluxbb_bans.ban_creator'
                           ') AS ban_creator_username, '
                           'fluxbb_bans.expire AS ban_expiration, '
                           'fluxbb_bans.message AS ban_message '
                           'FROM fluxbb_users '
                           'INNER JOIN fluxbb_groups ON fluxbb_groups.g_id = fluxbb_users.group_id '
                           'LEFT OUTER JOIN fluxbb_bans ON fluxbb_bans.username = fluxbb_users.username '
                           'ORDER BY fluxbb_users.id')

            imported = 0
            for row in cursor.fetchall():
                if self.decode(row['username']) is 'Guest':
                    continue

                user = self.create_user(self.decode(row['username']))
                profile = user.profile

                # User data
                user.email = self.decode(row['email'])
                user.date_joined = datetime.datetime.fromtimestamp(row['registration_date'])
                user.last_login = datetime.datetime.fromtimestamp(row['last_visit'])

                profile.last_visit = user.last_login

                # Groups
                user.groups.add(self.member_group)

                group_name = unicode(row['group_name'], self.encoding)
                if group_name == 'Administrateurs':
                    user.groups.add(self.admin_group)
                    user.is_superuser = True
                elif group_name == 'Gardiens' or group_name == 'Modérateur':
                    user.groups.add(self.staff_group)
                elif group_name == 'Animateur':
                    user.groups.add(self.animator_group)

                # Profile
                profile.title = self.decode(row['title']) if row['title'] is not None else ''
                profile.site = self.decode(row['website']) if row['website'] is not None else ''

                # TODO Markdown conversion
                profile.sign = self.decode(row['signature']) if row['signature'] is not None else ''
                profile.show_sign = bool(row['show_signature'])

                # Avatar
                # FluxBB doesn't store the avatar URL, but retries each time to find the file type,
                # checking if the file exists for the three allowed extensions. Yes.
                avatar_url_pattern = 'https://forum.zcraft.fr/img/avatars/{}.{}'
                extensions = ['jpg', 'png', 'gif']
                for extension in extensions:
                    avatar_url = avatar_url_pattern.format(row['user_id'], extension)
                    r = requests.get(avatar_url)
                    if r.status_code == 200:
                        profile.set_avatar_from_file(StringIO(r.content), filename='avatar.{}'.format(extension))

                user.save()
                profile.save()

                # Administrative note imported as a karma note with score = 0
                if row['admin_note']:
                    KarmaNote(user=user, staff=user, comment=self.decode(row['admin_note']), value=0).save()

                # Bans
                if row['ban_creator_username']:
                    if row['ban_message']:
                        ban_message = self.decode(row['ban_message'])
                    else:
                        ban_message = 'Bannissement : aucun motif donné'

                    # A ban with an expiration date
                    if row['ban_expiration']:
                        days = (datetime.datetime.fromtimestamp(row['ban_expiration']) - datetime.datetime.now()).days
                        if days > 0:
                            state = TemporaryBanSanction({
                                'ban-text': ban_message,
                                'ban-jrs': days
                            })
                            karma_score = -10
                        else:
                            state = None
                            karma_score = 0
                    else:
                        state = BanSanction({
                            'ban-text': ban_message
                        })
                        karma_score = -20

                    if state:
                        try:
                            moderator = User.objects.filter(username=self.decode(row['ban_creator_username'])).first()
                            if not moderator:
                                moderator = User.objects.filter(username=ZDS_APP['member']['bot_account']).first()

                            ban = state.get_sanction(moderator, user)
                            state.apply_sanction(profile, ban)

                            KarmaNote(user=user, staff=moderator, comment=ban_message,
                                      value=karma_score).save()

                            profile.karma += karma_score
                            profile.save()

                        except ValueError as e:
                            self.stderr.write(' Unable to import ban for {}: error: {}'.format(user.username, e))

                imported += 1
                percentage = int((float(imported) / float(users_count)) * 100)
                self.print_progress("[{}% - {}/{}] Importing user {}...".format(percentage, imported, users_count,
                                                                                user.username))

        print("\nDone.")