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

Issue:
Ajouter les unités temporelles sur les graphs des stats
Cete amélioration est demandée par un auteur :  

>  Ce serait bien, aussi, de mettre les unités à côté du temps. Parce que là, j’ai 03:51, mais je ne sais pas si c’est min:sec ou sec:mil


Discussion:
Du js :heart: par contre je ne sais pas où ça se passe. Apparemment ici : https://github.com/zestedesavoir/zds-site/blob/ebd52c4be3e48d536cf87c9790b15bbe4e6e328e/assets/js/charts.js

https://www.chartjs.org/ Find. Je regarderais la doc.

Oh je vais m'amuser :D Je pense ajouter 2-3 trucs supplémentaires si ça ne me prend pas trop de temps


Resolving patch:
diff --git a/zds/utils/templatetags/seconds_to_duration.py b/zds/utils/templatetags/seconds_to_duration.py
--- a/zds/utils/templatetags/seconds_to_duration.py
+++ b/zds/utils/templatetags/seconds_to_duration.py
@@ -4,6 +4,14 @@
 register = template.Library()
 
 
+# https://stackoverflow.com/a/8907269/2226755
+def strfdelta(tdelta, fmt):
+    d = {'days': tdelta.days}
+    d['hours'], rem = divmod(tdelta.seconds, 3600)
+    d['minutes'], d['seconds'] = divmod(rem, 60)
+    return fmt.format(**d)
+
+
 # TODO add unit test
 @register.filter('seconds_to_duration')
 def seconds_to_duration(value):
@@ -15,4 +23,7 @@ def seconds_to_duration(value):
         return ''
 
     duration = datetime.timedelta(seconds=value)
-    return str(duration)
+    if duration < 3600
+        return strfdelta(duration, '{minutes}m{seconds}s')
+    else
+        return strfdelta(duration, '{hours}h{minutes}m{seconds}s') 


Test patch:
Repository: zestedesavoir/zds-site

Issue:
Paramètre GET pour activer la miniature twitter en `summary_large_image`
Ajouter une vérification `?twittercard=large` pour activer la twittercard en large dans le front.

Minimum : 300 x 157 idéal : 600 x 314

https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/summary-card-with-large-image.html


Discussion:
J'ai eu un peu de mal à comprendre ce que tu voulais avoir... Si j'ai bien compris: tu souhaiterais qu'en ajoutant `?twittercard=large` à l'URL, la meta `twitter:card` passe de `summary` à `summary_large_image`, avec en plus le lien de `twitter:image` qui contienne un lien vers l'image aux dimensions que tu as indiquées ?

Si c'est ça, je pense pouvoir le faire, ça ne me paraît pas trop difficile ! Si j'ai bien compris, il faut rajouter une ligne dans ce code: https://github.com/zestedesavoir/zds-site/blob/cb0eb357dc0d768f117512e41bbedf79dc3d7e65/zds/settings/abstract_base/zds.py#L35-L52
Mais comment les miniatures des images déjà présentes dans les galleries vont se générer ? Lors d'une migration ? Il faut le préciser quelque part ? (j'ai vu dans les anciens commit qu'apparemment non, mais je préfère demander, à la fois pour être sûr et comprendre comme ça fonctionne !)


Oui c'est bien ça

> Mais comment les miniatures des images déjà présentes dans les galleries vont se générer ? Lors d'une migration ? Il faut le préciser quelque part ? (j'ai vu dans les anciens commit qu'apparemment non, mais je préfère demander, à la fois pour être sûr et comprendre comme ça fonctionne !)

Aucun soucis, j'ai rajouté : `'social_network': {'size': (144, 144), 'crop': True},` sans trop me poser de question. ;)


Mais les dimensions de `social_network` ne correspondent pas à celles que tu as précisées !

Et ça ne répond pas vraiment à ma question: si on a besoin de rajouter une dimension dans la liste des miniatures, comment (quand) sont générées les nouvelles miniatures correspondant à cette dimension ?

Resolving patch:
diff --git a/zds/settings/abstract_base/zds.py b/zds/settings/abstract_base/zds.py
--- a/zds/settings/abstract_base/zds.py
+++ b/zds/settings/abstract_base/zds.py
@@ -48,6 +48,7 @@
         'social_network': {'size': (144, 144), 'crop': True},
         #                           ^^^  ^^^ -> minimum dimensions of 144x144
         # https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/summary
+        'social_network_large': {'size': (600, 314), 'crop': True}
     },
 }
 


Test patch:
Repository: zestedesavoir/zds-site

Issue:
Enlever les espaces en trop dans la balise title (avec `spaceless`)
On a un petit problème avec les espaces notamment sur discord : 

![image](https://user-images.githubusercontent.com/18501150/53085742-d9659d00-3503-11e9-858c-3020e18dbd2f.png)

Il faudrait ajouter un block spaceless pour régler le soucis comme ceci : 
```
{% spaceless %}
   my title
{% endspaceless %}
```

https://docs.djangoproject.com/fr/2.1/ref/templates/builtins/#spaceless


Discussion:

Resolving patch:
diff --git a/zds/utils/templatetags/trail.py b/zds/utils/templatetags/trail.py
new file mode 100644
--- /dev/null
+++ b/zds/utils/templatetags/trail.py
@@ -0,0 +1,43 @@
+import re
+from django import template
+
+register = template.Library()
+
+tag_regexp = re.compile(r'>\s+<')
+breaks_regexp = re.compile(r'[\n\r\f\v]+')
+spaces_line_begin_regexp = re.compile(r'^[ \t]+', flags=re.MULTILINE)
+spaces_line_end_regexp = re.compile(r'[ \t]+$', flags=re.MULTILINE)
+
+"""
+Define a tag to remove trailing whitespaces
+"""
+
+
+@register.tag(name='trail')
+def do_trail(parser, token):
+    """
+    Define a tag to remove trailing whitespaces between tags and between tags and text
+
+    :param parser: The django template parser
+    :param token: tag token (tag_name + variable_name)
+    :return: Template node.
+    """
+
+    nodelist = parser.parse(('endtrail',))
+    parser.delete_first_token()
+    return TrailNode(nodelist)
+
+
+class TrailNode(template.Node):
+    """
+    Remove spaces between tags, before and after newlines and newlines
+    """
+    def __init__(self, nodelist):
+        self.nodelist = nodelist
+
+    def render(self, context):
+        new_text = tag_regexp.sub('><', self.nodelist.render(context))
+        new_text = spaces_line_begin_regexp.sub('', new_text)
+        new_text = spaces_line_end_regexp.sub('', new_text)
+        new_text = breaks_regexp.sub('', new_text)
+        return new_text


Test patch:
Repository: zestedesavoir/zds-site

Issue:
L'export à la demande ne fonctionne pas
Plus précisement, l'export Markdown ne fonctionne pas quand on clique sur le bouton "Exporter le contenu" alors qu'il fonctionne lors de la publication.


Discussion:
ou plus précisément encore il met les titres et pas les textes.
J'ai regardé le code pour voir d’où ça peut venir et il est possible que ce soit un effet de bord de #5381 qui ajoute `replace('\\', '/')` dans les fonctions `get-introduction`, `get-conclusion` et `get-text` lors de l'appel a `get_blob`, lequel renvoie `None` en cas de soucis. Ça peut aussi être un problème de chemin relatif ou absolu car avec le bouton `Exporter le contenu` on lance l'export du markdown depuis le fichier `zds/tutorialv2/api/views.py`. J'essaierai de continuer mes investigations dans la semaine.

Resolving patch:
diff --git a/zds/tutorialv2/api/views.py b/zds/tutorialv2/api/views.py
--- a/zds/tutorialv2/api/views.py
+++ b/zds/tutorialv2/api/views.py
@@ -88,7 +88,7 @@ def create(self, request, *args, **kwargs):
             if not publishable_content.public_version:
                 raise Http404('Not public content')
             tmp_dir, _ = self.ensure_directories(publishable_content)
-            versioned = publishable_content.public_version.load_public_version()
+            versioned = publishable_content.load_version(public=True)
             base_name = str(Path(tmp_dir, versioned.slug))
             md_file_path = str(Path(tmp_dir, versioned.slug + '.md'))
 


Test patch:
Repository: zestedesavoir/zds-site

Issue:
Les descriptions vides sont affichées pour les contenus
Les contenus ayant une description vide se voient afficher `None` sur la page d’accueil et dans la bibliothèque.

C'est sans doute une conversion depuis la base de données qui a changé.

Sujet : https://zestedesavoir.com/forums/sujet/13013/les-descriptions-vides-sont-affichees-pour-les-contenus/
*Envoyé depuis Zeste de Savoir*


Discussion:
Très probablement une régression de #5364 qui a modifié la fonction `description(self)` de la classe `PublishedContent` dans le fichier `zds/tutorialv2/models/database.py`, fonction qui est utilisée pour afficher le sous-titre dans les vignettes des contenus.
~~Vous pouvez vérifier si le logo s'affiche correctement sur les tweets aussi ? J'ai testé sur mon portable appartement, ils n'ont pas d'icône.~~ EDIT: OK
La PR #5364 fonctionne pour les sujets sur le forum mais pas pour les contenus. :(

Resolving patch:
diff --git a/zds/tutorialv2/models/database.py b/zds/tutorialv2/models/database.py
--- a/zds/tutorialv2/models/database.py
+++ b/zds/tutorialv2/models/database.py
@@ -627,7 +627,7 @@ class Meta:
 
     # sizes contain a python dict (as a string in database) with all information about file sizes
     sizes = models.CharField('Tailles des fichiers téléchargeables', max_length=512, default='{}')
-    _description = None
+    _meta_description = None
 
     @staticmethod
     def get_slug_from_file_path(file_path):
@@ -643,14 +643,21 @@ def title(self):
             return self.load_public_version().title
 
     def description(self):
-        if self._description:
-            return self._description
+        if self.versioned_model:
+            return self.versioned_model.description
+        else:
+            return self.load_public_version().description
+
+    def meta_description(self):
+        """Generate a description for the HTML tag <meta name='description'>"""
+        if self._meta_description:
+            return self._meta_description
         if not self.versioned_model:
             self.load_public_version()
-        self._description = self.versioned_model.description or self.versioned_model.get_introduction()
-        if self._description:
-            self._description = self._description[:settings.ZDS_APP['forum']['description_size']]
-        return self._description
+        self._meta_description = self.versioned_model.description or self.versioned_model.get_introduction() or ''
+        if self._meta_description:
+            self._meta_description = self._meta_description[:settings.ZDS_APP['forum']['description_size']]
+        return self._meta_description
 
     def get_prod_path(self, relative=False):
         if not relative:


Test patch:
Repository: zestedesavoir/zds-site

Issue:
[PDF] Problème lien de la licence
Sur la première page le lien de la licence peut être cassé : https://zestedesavoir.com/articles/pdf/2905/conferences-et-seminaires-scientifiques-a-paris-et-ailleurs.pdf (CC 0)

https://zestedesavoir.com/articles/pdf/2652/recap-communautaire-5.pdf ici aussi ? Ou c'est parce que c'est un vieux pdf ?


Discussion:
CC-0 n'a pas de lien apparament, et l'autre semble en droits d'auteur normal.
Le premier pdf, @artragis a réglé le bug. Quand au second, en effet, c'est étonnant, la CC-0 n'est "que" en version 1.0 (et pas 4.0, comme les autres) : https://creativecommons.org/publicdomain/zero/1.0/legalcode

Resolving patch:
diff --git a/zds/tutorialv2/publication_utils.py b/zds/tutorialv2/publication_utils.py
--- a/zds/tutorialv2/publication_utils.py
+++ b/zds/tutorialv2/publication_utils.py
@@ -373,6 +373,9 @@ def publish(self, md_file_path, base_name, **kwargs):
         licence_logo = licences.get(licence_short, False)
         if licence_logo:
             licence_url = 'https://creativecommons.org/licenses/{}/4.0/legalcode'.format(licence_short)
+            # we need a specific case for CC-0 as it is "public-domain"
+            if licence_logo == licences['0']:
+                licence_url = 'https://creativecommons.org/publicdomain/zero/1.0/legalcode.fr'
         else:
             licence = str(_('Tous droits réservés'))
             licence_logo = licences['copyright']


Test patch:
Repository: zestedesavoir/zds-site

Issue:
"None" est affiché quand une introduction ou une conclusion est vide
> Salut,
> 
> J'ai l'impression que si une partie d'un tutoriel n'a pas d'introduction, alors `None` est affiché. Voir par exemple la [partie 1 du tutoriel sur les collisions](https://zestedesavoir.com/tutoriels/2835/theorie-des-collisions/collisions-en-2d/). Et il y a la même chose pour la conclusion.
> 
Sujet : https://zestedesavoir.com/forums/sujet/13381/partie-sans-introductionconclusion/
*Envoyé depuis Zeste de Savoir*

Le problème a l'air d'avoir été réglé sur la page principale des contenus grâce à #4879, mais apparemment il est toujours présent pour les parties et peut-être les chapites.


Discussion:

Resolving patch:
diff --git a/zds/tutorialv2/models/versioned.py b/zds/tutorialv2/models/versioned.py
--- a/zds/tutorialv2/models/versioned.py
+++ b/zds/tutorialv2/models/versioned.py
@@ -427,7 +427,8 @@ def get_introduction(self):
         """
         if self.introduction:
             return get_blob(self.top_container().repository.commit(self.top_container().current_version).tree,
-                            self.introduction.replace('\\', '/'))
+                            self.introduction.replace('\\', '/')) or ''
+        return ''
 
     def get_conclusion(self):
         """
@@ -436,29 +437,36 @@ def get_conclusion(self):
         """
         if self.conclusion:
             return get_blob(self.top_container().repository.commit(self.top_container().current_version).tree,
-                            self.conclusion.replace('\\', '/'))
+                            self.conclusion.replace('\\', '/')) or ''
+        return ''
 
     def get_introduction_online(self):
         """The introduction content for online version.
 
-        :return: the full text if introduction exists ``None`` otherwise
+        This method should be only used in templates
+
+        :return: the full text if introduction exists, empty string otherwise
         :rtype: str
         """
         if self.introduction:
             path = os.path.join(self.top_container().get_prod_path(), self.introduction)
             if os.path.isfile(path):
                 return codecs.open(path, 'r', encoding='utf-8').read()
+        return ''
 
     def get_conclusion_online(self):
         """The conclusion content for online version.
 
-        :return: the full text if introduction exists ``None`` otherwise
+        This method should be only used in templates
+
+        :return: the full text if introduction exists, empty string otherwise
         :rtype: str
         """
         if self.conclusion:
             path = os.path.join(self.top_container().get_prod_path(), self.conclusion)
             if os.path.isfile(path):
                 return codecs.open(path, 'r', encoding='utf-8').read()
+        return ''
 
     def get_content_online(self):
         if os.path.isfile(self.get_prod_path()):


Test patch:
Repository: zestedesavoir/zds-site

Issue:
SEO et signature : <a rel="nofollow" />
Dans la signature il faudrait voir si on peut facilement ajouter un attribut `rel="nofollow"` pour préserver notre SEO. https://github.com/zestedesavoir/zmarkdown/blob/1dded309a2670689a4a3353f9e38b80624c6df1a/packages/zmarkdown/server/handlers.js#L139

> limitez les liens en signatures à des no follow or lien interne.
c’est pas mal (:evil) de partager un lien, mais si A-312 répond 4 fois dans la même page, il renvoie 4 fois du jus sur son compte twitter, 4 coding game, … ca a plusieurs effet négatifs
Source: https://zestedesavoir.com/forums/sujet/12099/seo-et-spam/?page=1#p199005




Discussion:
il y aura probablement un truc à faire sur zmd

Resolving patch:
diff --git a/zds/utils/templatetags/emarkdown.py b/zds/utils/templatetags/emarkdown.py
--- a/zds/utils/templatetags/emarkdown.py
+++ b/zds/utils/templatetags/emarkdown.py
@@ -178,7 +178,7 @@ def emarkdown_inline(text):
     :rtype: str
     """
     rendered = emarkdown(text, inline=True)
-    return rendered
+    return mark_safe(rendered.replace('<a href=', '<a rel="nofollow" href='))
 
 
 def sub_hd(match, count):


Test patch:
diff --git a/zds/utils/tests/tests_emarkdown.py b/zds/utils/tests/tests_emarkdown.py
--- a/zds/utils/tests/tests_emarkdown.py
+++ b/zds/utils/tests/tests_emarkdown.py
@@ -40,7 +40,13 @@ def test_emarkdown_inline(self):
 
         self.assertEqual(tr, expected)
 
-        # Todo: Find a way to force parsing crash or simulate it.
+    def test_emarkdown_inline_with_link(self):
+        # The goal is not to test zmarkdown but test that template tag correctly call it
+        self.context['content'] = '[zds](zestedesavoir.com)'
+        tr = Template('{% load emarkdown %}{{ content | emarkdown_inline}}').render(self.context)
+
+        expected = '<p><a rel="nofollow" href="zestedesavoir.com">zds</a></p>'
+        self.assertEqual(tr, expected)
 
     def test_shift_heading(self):
         tr = Template('{% load emarkdown %}{{ content | shift_heading_1}}').render(self.context)
Repository: zestedesavoir/zds-site

Issue:
Ajouter un validateur comme auteur l'ajoute deux fois au MP
Bonjour,

J'ai ajouté par accident mon validateur comme auteur de mon article. Ceci a eu pour effet de l'ajouter une deuxième fois au MP de validation (avec une notification et un affichage double).

Le comportement souhaité est de n'avoir chaque membre qu'une seule fois et pas de notification s'il est déjà présent dans le MP.

ID du MP en question si ça peut aider : 26210.

L'affichage en question : 
![](https://user-images.githubusercontent.com/35631001/72759997-be9a1d80-3bd7-11ea-90a5-bf9bf02b14d4.png)



Discussion:

Resolving patch:
diff --git a/zds/tutorialv2/views/contents.py b/zds/tutorialv2/views/contents.py
--- a/zds/tutorialv2/views/contents.py
+++ b/zds/tutorialv2/views/contents.py
@@ -1900,7 +1900,8 @@ def form_valid(self, form):
         for user in form.cleaned_data['users']:
             if user.pk not in all_authors_pk and user != self.request.user:
                 self.object.authors.add(user)
-                if self.object.validation_private_message:
+                if self.object.validation_private_message\
+                   and not self.object.validation_private_message.is_participant(user):
                     self.object.validation_private_message.participants.add(user)
                 all_authors_pk.append(user.pk)
                 url_index = reverse(self.object.type.lower() + ':find-' + self.object.type.lower(), args=[user.pk])


Test patch:
Repository: zestedesavoir/zds-site

Issue:
Améliorer l'intitulé du bouton « Convertir en article »
Sur les billets, le staff dispose d'un bouton « Convertir en article », qui fait une copie du billet en article et l'envoie en validation. Cette dernière étape n'est pas évidente étant donné le nom du bouton, et on pourrait croire que ça crée directement un article publié à partir du billet.

C'est une amélioration mineure, mais je propose de le renommer pour quelque chose du type « Proposer comme article », pour que ce soit plus clair.

En plus, c'est très facile à faire. :)


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
@@ -1323,7 +1323,9 @@ def __init__(self, content, *args, **kwargs):
         self.helper.form_id = 'convert-opinion'
 
         self.helper.layout = Layout(
-            HTML('<p>Êtes-vous certain(e) de vouloir promouvoir ce billet en article ?</p>'),
+            HTML("""<p>Avez-vous la certitude de vouloir proposer ce billet comme article ?
+                    Cela copiera le billet pour en faire un article,
+                    puis créera une demande de validation pour ce dernier.</p>"""),
             Field('version'),
             StrictButton(
                 _('Valider'),
diff --git a/zds/tutorialv2/views/validations.py b/zds/tutorialv2/views/validations.py
--- a/zds/tutorialv2/views/validations.py
+++ b/zds/tutorialv2/views/validations.py
@@ -1045,7 +1045,7 @@ def form_valid(self, form):
         article.validation_private_message = send_mp(
             bot,
             article.authors.all(),
-            _('Billet promu en article'),
+            _('Billet proposé comme article'),
             versionned_article.title,
             msg,
             True,
@@ -1056,6 +1056,7 @@ def form_valid(self, form):
 
         self.success_url = db_object.get_absolute_url()
 
-        messages.success(self.request, _('Le billet a bien été promu en article et est en attente de validation.'))
+        messages.success(self.request, _("""Le billet a bien été copié sous forme d’article
+                                            et est en attente de validation."""))
 
         return super(PromoteOpinionToArticle, self).form_valid(form)


Test patch:
Repository: zestedesavoir/zds-site

Issue:
Le message de proposition de correction contient du makdown trop chargé
Lorsque quelqu'un me propose des corrections sur mon article, je reçois un MP avec un message des correctifs.

Le problème c'est que ce message possède beaucoup de retour à la ligne, et donc quand on cite le fameux message on se retrouve avec plein de retour à supprimer.

Un problème vraiment mineur, mais ça fait toujours du bien quand le message est bon.



Discussion:
Je pense qu'on est sur une évolution, à ce niveau là, non ?

Je pense aussi, ça serait bien qu'on enlève le tage "beta 15.9" pour que ça nous facilite le travail de débug.

Fait :)


Resolving patch:
diff --git a/zds/tutorialv2/views/contents.py b/zds/tutorialv2/views/contents.py
--- a/zds/tutorialv2/views/contents.py
+++ b/zds/tutorialv2/views/contents.py
@@ -1574,10 +1574,7 @@ def form_valid(self, form):
             if form.content.is_opinion:
                 _type = _('le billet')
 
-            if form.content.get_tree_depth() == 0:
-                pm_title = _('J\'ai trouvé une faute dans {} « {} ».').format(_type, form.content.title)
-            else:
-                pm_title = _('J\'ai trouvé une faute dans le chapitre « {} ».').format(form.content.title)
+            pm_title = _('J\'ai trouvé une faute dans {} « {} ».').format(_type, form.content.title)
 
             msg = render_to_string(
                 'tutorialv2/messages/warn_typo.md',


Test patch: