internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: zestedesavoir/zds-site

Issue:
La pagination article est inversée
Dans le bas des articles on trouve une barre de pagination pour aller a l'article précédent/suivant. Il me semble que cette barre est inversé puisque l'article précédent est a droite et le suivant est a gauche. Dans la logique de la lecture et du fil des pages occidental, ce qui est avant est a gauche (le passé) et a droite on trouve ce qui vient ensuite (le futur). Voici une capture d'écran téléphone du dernier article paru :

![screenshot_2015-11-02-12-13-22](https://cloud.githubusercontent.com/assets/761168/10880453/9818fea0-815b-11e5-8402-0f4077d80b6d.png)



Discussion:

Resolving patch:
diff --git a/zds/tutorialv2/views/views_published.py b/zds/tutorialv2/views/views_published.py
--- a/zds/tutorialv2/views/views_published.py
+++ b/zds/tutorialv2/views/views_published.py
@@ -82,7 +82,7 @@ def get_context_data(self, **kwargs):
             all_articles = \
                 [a for a in PublishedContent.objects
                     .filter(content_type="ARTICLE", must_redirect=False)
-                    .order_by('-publication_date')
+                    .order_by('publication_date')
                     .all()]
             articles_count = len(all_articles)
 


Test patch:
diff --git a/zds/tutorialv2/tests/tests_views.py b/zds/tutorialv2/tests/tests_views.py
--- a/zds/tutorialv2/tests/tests_views.py
+++ b/zds/tutorialv2/tests/tests_views.py
@@ -5073,6 +5073,32 @@ def test_republish_with_different_slug(self):
         tuto = PublishableContent.objects.get(pk=self.tuto.pk)
         self.assertEqual(tuto.public_version.pk, new_published.pk)
 
+    def test_logical_article_pagination(self):
+        """Test that the past is "left" and the future is "right", or in other word that the good article
+        are given to pagination and not the opposite"""
+
+        article1 = PublishedContentFactory(type="ARTICLE")
+
+        # force "article1" to be the first one by setting a publication date equals to one hour before the test
+        article1.public_version.publication_date = datetime.datetime.now() - datetime.timedelta(0, 1)
+        article1.public_version.save()
+
+        article2 = PublishedContentFactory(type="ARTICLE")
+
+        self.assertEqual(PublishedContent.objects.count(), 3)  # both articles have been published
+
+        # visit article 1 (so article2 is next)
+        result = self.client.get(reverse('article:view', kwargs={'pk': article1.pk, 'slug': article1.slug}))
+        self.assertEqual(result.status_code, 200)
+        self.assertEqual(result.context['next_article'].pk, article2.public_version.pk)
+        self.assertIsNone(result.context['previous_article'])
+
+        # visit article 2 (so article1 is previous)
+        result = self.client.get(reverse('article:view', kwargs={'pk': article2.pk, 'slug': article2.slug}))
+        self.assertEqual(result.status_code, 200)
+        self.assertEqual(result.context['previous_article'].pk, article1.public_version.pk)
+        self.assertIsNone(result.context['next_article'])
+
     def tearDown(self):
 
         if os.path.isdir(settings.ZDS_APP['content']['repo_private_path']):
Repository: zestedesavoir/zds-site

Issue:
Mauvais redirection sur des tutoriels qui n'existent plus
# Issue à fermer quand v16 en prod et que les url foireuses sur sentry auront été testées.

Via Sentry (URL + /zeste-de-savoir/backend/group/276/).

En gros c'est cette ligne qui plante :  https://github.com/zestedesavoir/zds-site/blob/dev/zds/tutorialv2/views/views_published.py#L36. Je vais fixer ça de suite.

Les logs : 

```
Stacktrace (most recent call last):

  File "django/core/handlers/base.py", line 111, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "django/views/generic/base.py", line 69, in view
    return self.dispatch(request, *args, **kwargs)
  File "django/views/generic/base.py", line 87, in dispatch
    return handler(request, *args, **kwargs)
  File "django/views/generic/base.py", line 189, in get
    url = self.get_redirect_url(*args, **kwargs)
  File "zds/tutorialv2/views/views_published.py", line 36, in get_redirect_url
    obj = PublishableContent.objects.get(old_pk=int(kwargs["pk"]), type="TUTORIAL")
  File "django/db/models/manager.py", line 92, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "django/db/models/query.py", line 357, in get
    self.model._meta.object_name)
```



Discussion:

Resolving patch:
diff --git a/zds/tutorialv2/views/views_published.py b/zds/tutorialv2/views/views_published.py
--- a/zds/tutorialv2/views/views_published.py
+++ b/zds/tutorialv2/views/views_published.py
@@ -33,8 +33,8 @@ class RedirectContentSEO(RedirectView):
 
     def get_redirect_url(self, **kwargs):
         """Redirects the user to the new url"""
-        obj = PublishableContent.objects.get(old_pk=int(kwargs["pk"]), type="TUTORIAL")
-        if obj is None or not obj.in_public():
+        obj = get_object_or_404(PublishableContent, old_pk=int(kwargs.get('pk')), type='TUTORIAL')
+        if not obj.in_public():
             raise Http404(_(u"Aucun contenu public n'est disponible avec cet identifiant."))
         kwargs["parent_container_slug"] = str(kwargs["p2"]) + "_" + kwargs["parent_container_slug"]
         kwargs["container_slug"] = str(kwargs["p3"]) + "_" + kwargs["container_slug"]


Test patch:
Repository: zestedesavoir/zds-site

Issue:
Un template qui n'est pas variabilisé
Comme on peut le constater dans le template [opensearch.xml](https://github.com/zestedesavoir/zds-site/blob/3e91b083ee882396abf4dc0d508595d9bdb101d7/templates/search/opensearch.xml#L13) il y'a une valeur en dur qui y traine.

Il faut remplacer la ligne : 

``` python
<Contact>http://zestedesavoir.com/pages/contact/</Contact>
```

par 

``` python
<Contact>{% url "zds.pages.views.contact" %}</Contact>
```



Discussion:
Je précise que `{% URL %}` génère une URL relative et pas absolue. Est-ce gênant ?

En lisant la [spécification officielle](http://www.opensearch.org/Specifications/OpenSearch/1.1/Draft_5) je me rend
compte que cette balise attend surtout une adresse mail.

Le mer. 2 sept. 2015 12:14, Situphen notifications@github.com a écrit :

> Je précise que {% URL %} génère une URL relative et pas absolue. Est-ce
> gênant ?
> 
> —
> Reply to this email directly or view it on GitHub
> https://github.com/zestedesavoir/zds-site/issues/2977#issuecomment-137013882
> .

Je vais m'occuper de ça.


Resolving patch:
diff --git a/zds/search/views.py b/zds/search/views.py
--- a/zds/search/views.py
+++ b/zds/search/views.py
@@ -56,6 +56,7 @@ def opensearch(request):
     return render(request, 'search/opensearch.xml', {
         'site_name': settings.ZDS_APP['site']['litteral_name'],
         'site_url': settings.ZDS_APP['site']['url'],
+        'email_contact': settings.ZDS_APP['site']['email_contact'],
         'language': settings.LANGUAGE_CODE,
         'search_url': settings.ZDS_APP['site']['url'] + reverse('haystack_search')
     }, content_type='application/opensearchdescription+xml')


Test patch:
Repository: zestedesavoir/zds-site

Issue:
Ne pas afficher les bannis dans l'autocomplete
Lorsque l'on rédige un MP (ou qu'on utilise l'autocomplete en général) il serait bon de ne pas afficher les membres bannis, ou alors de les afficher avec une mention précisant qu'ils sont bannis et ne pas pouvoir les sélectionner.

Source : https://zestedesavoir.com/forums/sujet/3157/envoyer-un-mp-a-un-membre-banni/



Discussion:
C'est lié à l'API membres, qui retourne _tous_ les membres par défaut, même les bannis, et, à ce que sache, il y a pas moyen de filtrer... ping @GerardPaligot 

Je confirme, l'API des membres ne permet pas de filtrer mais c'est une fonctionnalité qui peut-être développé par qui veut.

Je tag API puisque l'autocompletion est directement cablé à l'API.

Je m'en suis chargé, la PR arrive bientot


Resolving patch:
diff --git a/zds/member/api/views.py b/zds/member/api/views.py
--- a/zds/member/api/views.py
+++ b/zds/member/api/views.py
@@ -48,11 +48,18 @@ class MemberListAPI(ListCreateAPIView, ProfileCreate, TokenGenerator):
     Profile resource to list and register.
     """
 
-    queryset = Profile.objects.all_members_ordered_by_date_joined()
     filter_backends = (filters.SearchFilter,)
     search_fields = ('user__username',)
     list_key_func = PagingSearchListKeyConstructor()
 
+    def get_queryset(self):
+        contactable = self.request.query_params.get('contactable', None)
+        if contactable is not None:
+            queryset = Profile.objects.contactable_members()
+        else:
+            queryset = Profile.objects.all_members_ordered_by_date_joined()
+        return queryset
+
     @etag(list_key_func)
     @cache_response(key_func=list_key_func)
     def get(self, request, *args, **kwargs):
diff --git a/zds/mp/forms.py b/zds/mp/forms.py
--- a/zds/mp/forms.py
+++ b/zds/mp/forms.py
@@ -21,7 +21,7 @@ class PrivateTopicForm(forms.Form, ParticipantsStringValidator, TitleValidator,
                 'placeholder': _(u'Les participants doivent '
                                  u'être séparés par une virgule.'),
                 'required': 'required',
-                'data-autocomplete': '{ "type": "multiple" }'}))
+                'data-autocomplete': '{ "type": "multiple", "url": "/api/membres/?contactable&search=%s" }'}))
 
     title = forms.CharField(
         label=_('Titre'),


Test patch:
Repository: zestedesavoir/zds-site

Issue:
[v18] Filtrer les contenus par catégories/tags ne donne aucune indication sur le filtre par la suite
Version 18
Navigateur Firefox
OS Windows

Lorsque je vais sur les url suivante : 
- filtrer tous les contenus de catégorie "bureautique et rédaction" : https://beta.zestedesavoir.com/tutoriels/?category=bureautique-et-redaction
- filtrer tous les contenus de tag "latex" : https://beta.zestedesavoir.com/contenus/?tag=latex

Je n'ai aucune information dans la page qui me permet de savoir quel filtre a été appliqué. Je considère ceci comme une régression car en prod actuellement on a deux façon d'identifier ceci : le fil d'arianne et le header.

Voici quelques captures pour comprendre mieux.
### En production (là ou c'est OK)

![contextualize](https://cloud.githubusercontent.com/assets/6066015/14284135/886a7a78-fb46-11e5-9305-a4cf29562d17.PNG)
### En beta (le problème)

![contextualize2](https://cloud.githubusercontent.com/assets/6066015/14284148/9259456e-fb46-11e5-9122-ff691ccb48c0.PNG)



Discussion:

Resolving patch:
diff --git a/zds/tutorialv2/views/views_published.py b/zds/tutorialv2/views/views_published.py
--- a/zds/tutorialv2/views/views_published.py
+++ b/zds/tutorialv2/views/views_published.py
@@ -310,6 +310,7 @@ def get_context_data(self, **kwargs):
                 public_content.content.public_version = public_content
                 public_content.content.count_note = public_content.count_note
         context['category'] = self.category
+        context['tag'] = self.tag
         context['top_categories'] = top_categories_content(self.current_content_type)
 
         return context


Test patch:
Repository: zestedesavoir/zds-site

Issue:
Erreur si le forumaire « Ajouter un auteur » sur un contenu est vide
Tout est dans le titre.



Discussion:

Resolving patch:
diff --git a/zds/tutorialv2/forms.py b/zds/tutorialv2/forms.py
--- a/zds/tutorialv2/forms.py
+++ b/zds/tutorialv2/forms.py
@@ -67,19 +67,21 @@ def __init__(self, *args, **kwargs):
             )
         )
 
-    def clean(self):
+    def clean_username(self):
         """Check every username and send it to the cleaned_data["user"] list
 
         :return: a dictionary of all treated data with the users key added
         """
         cleaned_data = super(AuthorForm, self).clean()
         users = []
-        for username in cleaned_data.get('username').split(","):
-            user = Profile.objects.contactable_members().filter(user__username__iexact=username.strip().lower()).first()
-            if user is not None:
-                users.append(user.user)
-        if len(users) > 0:
-            cleaned_data["users"] = users
+        if cleaned_data.get('username'):
+            for username in cleaned_data.get('username').split(","):
+                user = Profile.objects.contactable_members().filter(user__username__iexact=username.strip().lower())\
+                    .first()
+                if user is not None:
+                    users.append(user.user)
+            if len(users) > 0:
+                cleaned_data["users"] = users
         return cleaned_data
 
     def is_valid(self):
@@ -88,7 +90,7 @@ def is_valid(self):
 
 class RemoveAuthorForm(AuthorForm):
 
-    def clean(self):
+    def clean_username(self):
         """Check every username and send it to the cleaned_data["user"] list
 
         :return: a dictionary of all treated data with the users key added


Test patch:
Repository: zestedesavoir/zds-site

Issue:
Requirement de dev nécessaires en run
Ce if https://github.com/zestedesavoir/zds-site/blob/dev/zds/settings.py#L287-L290 ne fonctionne pas : par défaut on est en debug, donc le système s'attends à avoir `debug_toolbar` d'installée.



Discussion:
Deux choses : 
- c'est le cas car un serveur de prod ne doit en aucun cas être en `DEBUG=True`
- si jamais on décide qu'on peut avoir `DEBUG=True` en cas de tests sur une version de prod alors il faut l'ajouter dans `requirements.txt`.

En fait je pense que les propos de @SpaceFox ici c'était pour dire que le code du fichier `settings.py` rend la `debug_toolbar` necessaire pour installer une prod alors que c'est une dépendance qui est par nature une dépendance de dev.

Une solution correcte pour cette issue serait de simplement déplacer [le fameux bloc](https://github.com/zestedesavoir/zds-site/blob/dev/zds/settings.py#L287-L290) à la fin du fichier ([après cette ligne](https://github.com/zestedesavoir/zds-site/blob/dev/zds/settings.py#L583) donc).

On reste sur un mode par défaut en `DEBUG` pour la dev, mais dès lors qu'on a un `settings_prod.py` qui surcharge le `settings.py` pour une conf de prod, la `debug_toolbar` devient facultative. ça devrais donc permettre d'arriver à ce qu'on veut.


Resolving patch:
diff --git a/zds/settings.py b/zds/settings.py
--- a/zds/settings.py
+++ b/zds/settings.py
@@ -284,11 +284,6 @@
     'link'
 )
 
-if DEBUG:
-    INSTALLED_APPS += (
-        'debug_toolbar',
-    )
-
 # A sample logging configuration. The only tangible logging
 # performed by this configuration is to send an email to
 # the site admins on every HTTP 500 error when DEBUG=False.
@@ -581,3 +576,9 @@
     from settings_prod import *  # noqa
 except ImportError:
     pass
+
+# MUST BE after settings_prog import
+if DEBUG:
+    INSTALLED_APPS += (
+        'debug_toolbar',
+    )


Test patch:
Repository: zestedesavoir/zds-site

Issue:
[v18][RC5] Impossible de lire un sujet
En allant sur la beta, il m'est impossible de lire un sujet, ou tout simplement d'aller sur un forum. Je tombe sur une erreur 500 de nginx (et pas de django) comme si le serveur attendais une reponse pendant trop longtemps.

Les pages incriminées me font penser que le probleme se situe dans la liste des derniers sujets suivis que l'on affiche dans la sidebar.

Voila.



Discussion:
@sandhose @gustavi Il y a eu des problèmes dans la migration des notifications ? oO

Je précise que ça n'arrive qu'en mode connecté donc le problème vient bien des sujets suivis


Resolving patch:
diff --git a/zds/notification/migrations/0008_auto_20160507_1504.py b/zds/notification/migrations/0008_auto_20160507_1504.py
new file mode 100644
--- /dev/null
+++ b/zds/notification/migrations/0008_auto_20160507_1504.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('notification', '0007_auto_20160121_2343'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='notification',
+            name='object_id',
+            field=models.PositiveIntegerField(db_index=True),
+        ),
+        migrations.AlterField(
+            model_name='subscription',
+            name='object_id',
+            field=models.PositiveIntegerField(db_index=True),
+        ),
+    ]
diff --git a/zds/notification/models.py b/zds/notification/models.py
--- a/zds/notification/models.py
+++ b/zds/notification/models.py
@@ -30,7 +30,7 @@ class Meta:
     is_active = models.BooleanField(_(u'Actif'), default=True, db_index=True)
     by_email = models.BooleanField(_(u'Recevoir un email'), default=False)
     content_type = models.ForeignKey(ContentType)
-    object_id = models.PositiveIntegerField()
+    object_id = models.PositiveIntegerField(db_index=True)
     content_object = GenericForeignKey('content_type', 'object_id')
     last_notification = models.ForeignKey(u'Notification', related_name="last_notification", null=True, default=None)
 
@@ -267,7 +267,7 @@ class Meta:
     subscription = models.ForeignKey(Subscription, related_name='subscription', db_index=True)
     pubdate = models.DateTimeField(_(u'Date de création'), auto_now_add=True, db_index=True)
     content_type = models.ForeignKey(ContentType)
-    object_id = models.PositiveIntegerField()
+    object_id = models.PositiveIntegerField(db_index=True)
     content_object = GenericForeignKey('content_type', 'object_id')
     is_read = models.BooleanField(_(u'Lue'), default=False, db_index=True)
     url = models.CharField('URL', max_length=255)


Test patch:
Repository: zestedesavoir/zds-site

Issue:
[beta v18] L'URL mes tuto/articles dans les MP de migration ZEP-25 est invalide
Via https://zestedesavoir.com/forums/sujet/6064/on-ne-lattendait-plus-voici-la-v18/?page=1#p110019

Même problème que pour le `Bonjour {0},`, l'URL de ces deux liens utilise le PK de l'utilisateur, qui est donc mal substitué [ici](https://github.com/zestedesavoir/zds-site/blob/release-v18/zds/tutorialv2/management/commands/migrate_to_zep25.py#L156)

Question à part, une raison de pourquoi ce message est pas dans un fichier à part ? Même si c'est juste pour une migration, c'est plus propre, nan ? :o 

ping @gustavi 



Discussion:
Parce que ça ne va servir qu'une fois donc osef.


Resolving patch:
diff --git a/zds/tutorialv2/management/commands/migrate_to_zep25.py b/zds/tutorialv2/management/commands/migrate_to_zep25.py
--- a/zds/tutorialv2/management/commands/migrate_to_zep25.py
+++ b/zds/tutorialv2/management/commands/migrate_to_zep25.py
@@ -151,10 +151,9 @@ def alert_authors(self):
                   's** ont fait leur apparition et les catégories ont été revues afin de faciliter et d\'aléger cette' \
                   ' classification.\n\nLes anciennes catégories ont été transformées en tags et de nouvelles catégori' \
                   'es plus générales ont été ajoutés. L\'équipe de Zeste de Savoir va ou a déjà changé les catégories' \
-                  ' des contenus publiés.\n\nNous vous invitons à vérifier la catégorie de vos [articles](https://zes' \
-                  'tedesavoir.com/contenus/articles/{1}/) et [tutoriels](https://zestedesavoir.com/contenus/tutoriels' \
-                  '/{1}/) mais également la pertinence des tags et en ajouter si besoin.\n\n\nNous restons à votre di' \
-                  'sposition et votre écoute pour toutes suggestions ou remarques,\n\nL\'équipe de Zeste de Savoir'
+                  ' des contenus publiés.\n\nNous vous invitons à vérifier les catégories de vos articles et tutoriel' \
+                  's mais également la pertinence des tags et en ajouter si besoin.\n\n\nNous restons à votre disposi' \
+                  'tion et votre écoute pour toutes suggestions ou remarques,\n\nL\'équipe de Zeste de Savoir'
             send_mp(
                 bot,
                 [user],


Test patch:
Repository: zestedesavoir/zds-site

Issue:
Problème d'espace dans les tags
« foo, bar » est mal parsé mais pas « for,bar ». Je bosse dessus de suite.



Discussion:

Resolving patch:
diff --git a/zds/tutorialv2/models/models_database.py b/zds/tutorialv2/models/models_database.py
--- a/zds/tutorialv2/models/models_database.py
+++ b/zds/tutorialv2/models/models_database.py
@@ -527,7 +527,7 @@ def add_tags(self, tag_collection):
         """
         for tag in tag_collection:
             try:
-                current_tag, created = Tag.objects.get_or_create(title=tag.lower())
+                current_tag, created = Tag.objects.get_or_create(title=tag.lower().strip())
                 self.tags.add(current_tag)
             except ValueError as e:
                 logging.getLogger("zds.tutorialv2").warn(e)


Test patch:
diff --git a/zds/tutorialv2/tests/tests_models.py b/zds/tutorialv2/tests/tests_models.py
--- a/zds/tutorialv2/tests/tests_models.py
+++ b/zds/tutorialv2/tests/tests_models.py
@@ -503,6 +503,18 @@ def test_add_tags(self):
                          'all tags are "{}"'.format('","'.join([str(t) for t in Tag.objects.all()])))
         self.assertEqual(tuto_tags_len, len(tuto.tags.all()))
 
+        # test space in tags (first and last space deleted)
+        tags = ['foo bar', ' azerty', 'qwerty ', ' another tag ']
+        tuto.add_tags(tags)
+        tuto_tags_list = [tag['title'] for tag in tuto.tags.values('title')]
+        self.assertIn('foo bar', tuto_tags_list)
+        self.assertNotIn(' azerty', tuto_tags_list)
+        self.assertIn('azerty', tuto_tags_list)
+        self.assertNotIn('qwerty ', tuto_tags_list)
+        self.assertIn('qwerty', tuto_tags_list)
+        self.assertNotIn(' another tag', tuto_tags_list)
+        self.assertIn('another tag', tuto_tags_list)
+
     def tearDown(self):
         if os.path.isdir(settings.ZDS_APP['content']['repo_private_path']):
             shutil.rmtree(settings.ZDS_APP['content']['repo_private_path'])
Repository: zestedesavoir/zds-site

Issue:
Le suivi des topics par email n'a pas été migré
Les topics que je suivais par email n'ont pas été migrés correctement. En allant sur un topic que je suivais par email avant la MEP, on me propose de le re-suivre par email, alors que je le suivais déjà par email.

Je suis obligé de me rendre sur chaque topic que je suivais par email pour refaire le suivi par email moi-même.



Discussion:
Pour cette issue, il faut :
- Utiliser l'attribut de `TopicFollowed` qui dispose toujours de toutes les données dans sa table.
- D'utiliser son attribut `email` : https://github.com/zestedesavoir/zds-site/blob/dev/zds/notification/models.py#L318
- De créer un nouveau script de migration (on va pas ré-appliquer le gros script, il est inutile maintenant).
- D'itérer sur tous les sujets suivis grâce à `TopicAnswerSubscription` (check la méthode dans son manager) et de récupérer son penchant dans `TopicFollowed`.
- Puis d'appliquer la modification.

Je le fais si j'ai du temps sur mon week-end. SInon, n'hésitez pas à corriger.


Resolving patch:
diff --git a/zds/notification/management/commands/migrate_email_subscription.py b/zds/notification/management/commands/migrate_email_subscription.py
new file mode 100644
--- /dev/null
+++ b/zds/notification/management/commands/migrate_email_subscription.py
@@ -0,0 +1,24 @@
+# coding: utf-8
+from django.core.management import BaseCommand
+
+from zds.member.models import Profile
+from zds.notification.models import TopicFollowed, TopicAnswerSubscription
+
+
+class Command(BaseCommand):
+    help = 'Migrate old email subscriptions to new models.'
+
+    def handle(self, *args, **options):
+        for profile in Profile.objects.all():
+            self.stdout.write(u'Starting migration for {}'.format(profile.user.username))
+
+            # Get alls topic followed by the user.
+            topics_followed = TopicFollowed.objects \
+                .filter(user=profile.user, email=True) \
+                .values("topic").distinct().all()
+
+            # And update email attribute in the TopicAnswerSubscription model.
+            for topic in topics_followed:
+                active = TopicAnswerSubscription.objects.get_or_create_active(user=profile.user, content_object=topic)
+                active.email = True
+                active.save()


Test patch: