internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: translate/pootle

Issue:
Clarify re submitting/suggestion empty plural form in tranlations 
![screen shot 2017-03-20 at 18 24 39](https://cloud.githubusercontent.com/assets/682845/24140553/889c6be2-0e31-11e7-8186-07c3192e83ea.png)

Is it allowed to post translations with empty plural forms?
- submit - `no`
- suggest - `yes`
- accept suggestion - `yes`
- accept/submit suggestion with/without alterations - `no`

we need to be consistent here.


Discussion:

Resolving patch:
diff --git a/pootle/apps/pootle_store/forms.py b/pootle/apps/pootle_store/forms.py
--- a/pootle/apps/pootle_store/forms.py
+++ b/pootle/apps/pootle_store/forms.py
@@ -566,7 +566,7 @@ def __init__(self, *args, **kwargs):
         self.fields[
             "target_f"].hidden_widget = HiddenMultiStringWidget(nplurals=nplurals)
         self.fields["target_f"].fields = [
-            forms.CharField(strip=False) for i in range(nplurals)]
+            forms.CharField(strip=False, required=False) for i in range(nplurals)]
         for k in ["user", "name", "email"]:
             if k in self.fields:
                 self.fields[k].required = False
@@ -574,7 +574,7 @@ def __init__(self, *args, **kwargs):
 
 class SuggestionSubmitForm(SubmitFormMixin, BaseSuggestionForm):
 
-    target_f = MultiStringFormField(required=False)
+    target_f = MultiStringFormField(required=False, require_all_fields=False)
 
     def save_unit(self):
         updated = []
@@ -623,7 +623,7 @@ class SubmitForm(SubmitFormMixin, forms.Form):
     is_fuzzy = forms.BooleanField(
         initial=False,
         label=_("Needs work"))
-    target_f = MultiStringFormField(required=False)
+    target_f = MultiStringFormField(required=False, require_all_fields=False)
 
     def clean_is_fuzzy(self):
         return self.data["is_fuzzy"] != "0"


Test patch:
Repository: translate/pootle

Issue:
Wrong user is recorded as submitter for a comment
Steps to reproduce:
- Log in
- Open any unit in the editor
- Click on "add comment" icon
- Enter any comment and click submit

It will create submission where submitter is `system` user.


Discussion:

Resolving patch:
diff --git a/pootle/apps/pootle_store/forms.py b/pootle/apps/pootle_store/forms.py
--- a/pootle/apps/pootle_store/forms.py
+++ b/pootle/apps/pootle_store/forms.py
@@ -318,6 +318,10 @@ def clean_translator_comment(self):
                 return ''
 
             return self.cleaned_data['translator_comment']
+
+        def save(self, *args, **kwargs):
+            self.instance.save(user=self.request.user)
+
     return UnitCommentForm
 
 
diff --git a/pootle/apps/pootle_store/views.py b/pootle/apps/pootle_store/views.py
--- a/pootle/apps/pootle_store/views.py
+++ b/pootle/apps/pootle_store/views.py
@@ -18,7 +18,6 @@
 from django.http import Http404, QueryDict
 from django.shortcuts import get_object_or_404, redirect
 from django.template import loader
-from django.utils import timezone
 from django.utils.functional import cached_property
 from django.utils.lru_cache import lru_cache
 from django.utils.translation import to_locale
@@ -242,9 +241,6 @@ def save_comment(request, unit):
     :return: If the form validates, the cleaned comment is returned.
              An error message is returned otherwise.
     """
-    # Update current unit instance's attributes
-    unit.change.commented_by = request.user
-    unit.change.commented_on = timezone.now().replace(microsecond=0)
 
     language = request.translation_project.language
     form = unit_comment_form_factory(language)(request.POST, instance=unit,


Test patch:
Repository: translate/pootle

Issue:
units not passed through to store checker in update_tp context
...


Discussion:

Resolving patch:
diff --git a/pootle/apps/pootle_checks/utils.py b/pootle/apps/pootle_checks/utils.py
--- a/pootle/apps/pootle_checks/utils.py
+++ b/pootle/apps/pootle_checks/utils.py
@@ -402,7 +402,6 @@ def update_translated(self):
             unit["store__translation_project__language__code"] = lang_code
             if self.update_translated_unit(unit, checker=checker):
                 updated_count += 1
-        # clear the cache of the remaining Store
         return updated_count
 
 
diff --git a/pootle/apps/pootle_translationproject/contextmanagers.py b/pootle/apps/pootle_translationproject/contextmanagers.py
--- a/pootle/apps/pootle_translationproject/contextmanagers.py
+++ b/pootle/apps/pootle_translationproject/contextmanagers.py
@@ -59,7 +59,7 @@ def extra_update_data_handler_(**kwargs):
                 for to_check in updated.checks.values():
                     store = to_check["store"]
                     units = (
-                        [unit.id for unit in to_check["units"]]
+                        [unit for unit in to_check["units"]]
                         if to_check["units"]
                         else None)
                     update_checks.send(
@@ -151,9 +151,10 @@ def update_check_handler(**kwargs):
             units = None
             if isinstance(kwargs.get("instance"), Store):
                 store = kwargs["instance"]
+                units = set(kwargs.get("units") or [])
             else:
                 store = kwargs["instance"].store
-                units = set([kwargs["instance"]])
+                units = set([kwargs["instance"].id])
             updated.checks[store.id] = updated.checks.get(
                 store.id,
                 dict(store=store, units=set()))


Test patch:
Repository: translate/pootle

Issue:
Fulah locale exposes _XXX_LAST_SUBMISSION_
When in the Fulah (ff) locale, all pages expose the `_XXX_LAST_SUBMISSION_` text used to mangle timesince messages.

![screen shot 2017-05-11 at 19 58 59](https://cloud.githubusercontent.com/assets/647438/25966915/58d16d2e-3684-11e7-9eac-28fbe147a7ab.png)



Discussion:

Resolving patch:
diff --git a/pootle/i18n/dates.py b/pootle/i18n/dates.py
--- a/pootle/i18n/dates.py
+++ b/pootle/i18n/dates.py
@@ -8,10 +8,15 @@
 
 from datetime import datetime
 
+from django.conf import settings
 
 from .formatter import get_locale_formats
 
 
 def timesince(timestamp, locale=None):
     timedelta = datetime.now() - datetime.fromtimestamp(timestamp)
-    return get_locale_formats(locale).timedelta(timedelta, format='long')
+    formatted = get_locale_formats(locale).timedelta(timedelta, format='long')
+    if formatted:
+        return formatted
+    return get_locale_formats(
+        settings.LANGUAGE_CODE).timedelta(timedelta, format='long')


Test patch:
diff --git a/tests/i18n/dates.py b/tests/i18n/dates.py
--- a/tests/i18n/dates.py
+++ b/tests/i18n/dates.py
@@ -21,6 +21,9 @@ def test_local_date_timesince(settings):
     timedelta = datetime.now() - datetime.fromtimestamp(timestamp)
     language = str(get_locale_formats().locale)
     assert timesince(timestamp) == format_timedelta(timedelta, locale=language)
+    assert (
+        timesince(timestamp, locale="ff")
+        == timesince(timestamp, locale=settings.LANGUAGE_CODE))
 
 
 @pytest.mark.parametrize('language,fallback', [
Repository: translate/pootle

Issue:
Terminology is not updated when term units are updated
the terminology stemmer listens to `submission.post_save` - but submissions are always `bulk_created` so it doesn't seem to get triggered


Discussion:

Resolving patch:
diff --git a/pootle/apps/pootle_terminology/receivers.py b/pootle/apps/pootle_terminology/receivers.py
--- a/pootle/apps/pootle_terminology/receivers.py
+++ b/pootle/apps/pootle_terminology/receivers.py
@@ -34,7 +34,7 @@ def handle_unit_save(**kwargs):
 @receiver(post_save, sender=Submission)
 def handle_submission_save(**kwargs):
     sub = kwargs["instance"]
-    if sub.type != SubmissionFields.TARGET:
+    if sub.field != SubmissionFields.TARGET:
         return
     unit = sub.unit
     if unit.state != TRANSLATED:


Test patch:
Repository: translate/pootle

Issue:
AttributeError: 'QuerySet' object has no attribute 'keys'
The following appears when you first go to `/` and Pootle tries to redirect you to your language based on your Accept-Lang headers.  Note this will set a cookie so you need to have cookies reset to replicate.

```python
AttributeError: 'QuerySet' object has no attribute 'keys'
  File "pootle_app/views/index/index.py", line 94, in dispatch
    else self.languages).values_list('code', flat=True))
  File "pootle/i18n/override.py", line 81, in get_lang_from_http_header
    for lang in supported.keys():

AttributeError: 'QuerySet' object has no attribute 'keys'
```


Discussion:

Resolving patch:
diff --git a/pootle/apps/pootle_app/views/index/index.py b/pootle/apps/pootle_app/views/index/index.py
--- a/pootle/apps/pootle_app/views/index/index.py
+++ b/pootle/apps/pootle_app/views/index/index.py
@@ -89,9 +89,9 @@ def dispatch(self, request, *args, **kwargs):
         if lang is None:
             lang = get_lang_from_http_header(
                 request,
-                (self.all_languages
-                 if request.user.is_superuser
-                 else self.languages).values_list('code', flat=True))
+                dict((self.all_languages
+                      if request.user.is_superuser
+                      else self.languages).values_list('code', 'fullname')))
         if lang is not None and lang not in ('projects', ''):
             url = reverse('pootle-language-browse', args=[lang])
         else:


Test patch:
Repository: translate/pootle

Issue:
Languages in languages drop down menu are messed
Hi,

the languages in the languages drop down menu are in a pretty mess now. It seems that they are not sorted anymore now, neither by language name nor by locale.

Regards,
Michael


Discussion:
In addition to this: It is true for mozilla.locamotion.org and translations.documentfoundation.org only. On pootle.locamotion.org the issue doesn't occur.
I've just seen that pootle.locamotion.org uses an older Pootle version. I think therefore the issue doesn't occur here.

Resolving patch:
diff --git a/pootle/core/views/base.py b/pootle/core/views/base.py
--- a/pootle/core/views/base.py
+++ b/pootle/core/views/base.py
@@ -6,6 +6,8 @@
 # or later license. See the LICENSE file for a copy of the license and the
 # AUTHORS file for copyright and authorship information.
 
+from collections import OrderedDict
+
 from django.urls import reverse
 from django.utils.decorators import method_decorator
 from django.utils.functional import cached_property
@@ -91,10 +93,18 @@ def dispatch(self, request, *args, **kwargs):
     @property
     def languages(self):
         languages = site_languages.get()
-        return (
+        languages = (
             languages.all_languages
             if self.has_admin_access
             else languages.languages)
+        lang_map = {
+            v: k
+            for k, v
+            in languages.items()}
+        return OrderedDict(
+            (lang_map[v], v)
+            for v
+            in sorted(languages.values()))
 
     def get_context_data(self, *args, **kwargs):
         return {


Test patch:
Repository: translate/pootle

Issue:
No critical checks are displayed after sending translation
If you send a translation that has critical checks the editor correctly keeps you in the unit and displays a red background, but the critical errors are not displayed, so the translator has to reload to know which critical errors were reported or to mute them:

![erro](https://user-images.githubusercontent.com/939880/26923193-a420d6ca-4c41-11e7-8793-a852a538b0e2.png)


No critical checks are displayed after sending translation
If you send a translation that has critical checks the editor correctly keeps you in the unit and displays a red background, but the critical errors are not displayed, so the translator has to reload to know which critical errors were reported or to mute them:

![erro](https://user-images.githubusercontent.com/939880/26923193-a420d6ca-4c41-11e7-8793-a852a538b0e2.png)




Discussion:

Resolving patch:
diff --git a/pootle/apps/pootle_store/views.py b/pootle/apps/pootle_store/views.py
--- a/pootle/apps/pootle_store/views.py
+++ b/pootle/apps/pootle_store/views.py
@@ -146,6 +146,8 @@ def _get_critical_checks_snippet(request, unit):
     ctx = {
         'canreview': can_review,
         'unit': unit,
+        'critical_checks': list(unit.get_critical_qualitychecks()),
+        'warning_checks': list(unit.get_warning_qualitychecks()),
     }
     template = loader.get_template('editor/units/xhr_checks.html')
     return template.render(context=ctx, request=request)


Test patch:
Repository: translate/pootle

Issue:
Top scorers list includes zero score users
The top scorer list in e.g. `/af/?details` includes a number of users with zero score.

I'm doubtful that these contributed in last 30 days.  So they shouldn't be on the list at all.

![screen shot 2017-05-17 at 10 37 08](https://cloud.githubusercontent.com/assets/647438/26147888/d6e7c214-3aec-11e7-8db8-e847a4e8335a.png)


Discussion:
we should make minimum for any score to 1
Or round up all scores.

Resolving patch:
diff --git a/pootle/apps/pootle_score/utils.py b/pootle/apps/pootle_score/utils.py
--- a/pootle/apps/pootle_score/utils.py
+++ b/pootle/apps/pootle_score/utils.py
@@ -78,7 +78,8 @@ def get_top_scorers(self, days=30):
                 Sum("score"),
                 Sum("suggested"),
                 Sum("reviewed"),
-                Sum("translated")).order_by("-score__sum")
+                Sum("translated")).filter(
+                    score__sum__gt=0).order_by("-score__sum")
 
     def filter_scores(self, qs):
         return qs


Test patch:
diff --git a/tests/pootle_score/utils.py b/tests/pootle_score/utils.py
--- a/tests/pootle_score/utils.py
+++ b/tests/pootle_score/utils.py
@@ -55,7 +55,8 @@ def _test_scores(ns, context, score_data):
                     Sum("score"),
                     Sum("suggested"),
                     Sum("reviewed"),
-                    Sum("translated")).order_by("-score__sum")))
+                    Sum("translated")).filter(
+                        score__sum__gt=0).order_by("-score__sum")))
     assert (
         tuple(score_data.top_scorers)
         == tuple(score_data.get_top_scorers(30)))
Repository: translate/pootle

Issue:
Broken PO files are partially loaded without error
I mistakenly made a PO file with duplicate units.  The files are mostly like:

N/M.po where N=1 to 3 and M=1 to 20

A bug in my script meant that my files had more than 100 units and they where duplicated.

1. The files loaded but when checking only the first 100 units are actually loaded
2. No error was reported and update_stores did not abort or report any issue

Expected:
1. PO fil with duplicate units should throw and error
2. update_stores should report that error and abort update or that specific store.


Discussion:

Resolving patch:
diff --git a/pootle/apps/pootle_store/diff.py b/pootle/apps/pootle_store/diff.py
--- a/pootle/apps/pootle_store/diff.py
+++ b/pootle/apps/pootle_store/diff.py
@@ -7,6 +7,7 @@
 # AUTHORS file for copyright and authorship information.
 
 import difflib
+import logging
 from collections import OrderedDict
 
 from django.db import models
@@ -19,6 +20,9 @@
 from .unit import UnitProxy
 
 
+logger = logging.getLogger(__name__)
+
+
 class UnitDiffProxy(UnitProxy):
     """Wraps File/DB Unit dicts used by StoreDiff for equality comparison"""
 
@@ -125,6 +129,14 @@ def get_file_units(self, units):
         for unit in units:
             if unit.isheader():
                 continue
+            if unit.getid() in diff_units:
+                unitid = unit.getid()
+                logger.warning(
+                    "[diff] Duplicate unit found: %s %s",
+                    self.target_store.name,
+                    (unitid
+                     if len(unitid) <= 20
+                     else "%s..." % unitid[:17]))
             diff_units[unit.getid()] = self.get_file_unit(unit)
         return diff_units
 


Test patch:
diff --git a/tests/pootle_store/update.py b/tests/pootle_store/update.py
--- a/tests/pootle_store/update.py
+++ b/tests/pootle_store/update.py
@@ -6,6 +6,7 @@
 # or later license. See the LICENSE file for a copy of the license and the
 # AUTHORS file for copyright and authorship information.
 
+import logging
 import pytest
 
 from pytest_pootle.utils import create_store
@@ -38,3 +39,18 @@ def test_store_update_with_obsolete(store_po):
     store_po.update(file_store)
     unit = store_po.UnitClass.objects.get(id=unit.id)
     assert unit.isobsolete()
+
+
+@pytest.mark.django_db
+def test_store_update_with_duplicate(store_po, caplog):
+    units = [
+        ('source1', 'target1', False),
+        ('source2', 'target2', False),
+        ('source2', 'target2', False)]
+    file_store = create_store(store_po.pootle_path, units=units)
+    caplog.set_level(logging.WARN)
+    store_po.update(file_store)
+    assert (
+        caplog.records[0].message
+        == ('[diff] Duplicate unit found: %s source2'
+            % store_po.name))
Repository: translate/pootle

Issue:
Changing source (with xliff) can create submissions with no submitter
# Steps to reproduce:

- change source in file of existing unit
- run update_stores

# Results

- submissions have no submitter
- stuff breaks

## Expected result:

- nothing breaks


this only happens in xliff afaict - as i dont think its possible to change source of units in this way with po



Discussion:
```diff
       <trans-unit id="SomeId">
-        <source>Old source</source>
-        <note>Old note</note>
+        <source>New source</source>
+        <note>New note</note>
       </trans-unit>
```

Resolving patch:
diff --git a/pootle/apps/pootle_store/receivers.py b/pootle/apps/pootle_store/receivers.py
--- a/pootle/apps/pootle_store/receivers.py
+++ b/pootle/apps/pootle_store/receivers.py
@@ -98,17 +98,6 @@ def handle_unit_pre_save(**kwargs):
         unit.setid(unitid.getid())
 
 
-@receiver(pre_save, sender=UnitChange)
-def handle_unit_pre_change(**kwargs):
-    unit_change = kwargs["instance"]
-    unit = unit_change.unit
-    if unit.state == UNTRANSLATED:
-        # clear reviewer and translator data if translation
-        # has been deleted
-        unit_change.submitted_by = None
-        unit_change.submitted_on = None
-
-
 @receiver(post_save, sender=UnitChange)
 def handle_unit_change(**kwargs):
     unit_change = kwargs["instance"]


Test patch:
diff --git a/tests/pootle_store/models.py b/tests/pootle_store/models.py
--- a/tests/pootle_store/models.py
+++ b/tests/pootle_store/models.py
@@ -8,6 +8,8 @@
 
 import pytest
 
+from pootle_store.constants import UNTRANSLATED
+
 
 @pytest.mark.django_db
 def test_store_update_new_unit_revision(store0):
@@ -28,3 +30,16 @@ def test_store_update_new_unit_revision(store0):
         pk=new_unit.unit_source.pk)
     assert new_unit.revision > unit_source.creation_revision
     assert unit_source.creation_revision == creation_revision
+
+
+@pytest.mark.django_db
+def test_store_update_source_change_subs(store0, member, system):
+    unit = store0.units.filter(state=UNTRANSLATED).first()
+    unit.source = "NEW SOURCE"
+    unit.save()
+    created_sub = unit.submission_set.latest()
+    assert created_sub.submitter == system
+    unit.source = "SOURCE CHANGED BY USER"
+    unit.save(user=member)
+    created_sub = unit.submission_set.latest()
+    assert created_sub.submitter == member
Repository: translate/pootle

Issue:
Deleting a user does not remove cached data
# Steps to reproduce:

1. Create a user and provide some translations
2. View a Browse page to see users stats
3. Delete user - stdout shows user merged into -> nobody
4. View browser page - user's score still exists and they are still attributed
5. Remove redis cache
6. View browser page - user's score is removed, last activity is attributed to an anonymous user.

# Results

## Expected result:

Deleting a user flushes all their data.

## Actual result:

User's data is still in cache

# Environment:

* Pootle version: b76ea22caea60b1c1be2943fe1d60214809ae3d4



Discussion:

Resolving patch:
diff --git a/pootle/apps/accounts/utils.py b/pootle/apps/accounts/utils.py
--- a/pootle/apps/accounts/utils.py
+++ b/pootle/apps/accounts/utils.py
@@ -20,7 +20,8 @@
 from pootle.core.contextmanagers import keep_data
 from pootle.core.delegate import score_updater
 from pootle.core.models import Revision
-from pootle.core.signals import update_data
+from pootle.core.signals import update_data, update_revisions
+from pootle_app.models import Directory
 from pootle_statistics.models import SubmissionFields
 from pootle_store.constants import FUZZY, UNTRANSLATED
 from pootle_store.models import SuggestionState
@@ -169,6 +170,7 @@ def purge(self):
         - Revert unit comments by user.
         - Revert unit state changes by user.
         - Delete any remaining submissions and suggestions.
+        - Expire caches for relevant directories
         """
 
         stores = set()
@@ -188,6 +190,10 @@ def purge(self):
             self.user.suggestions.all().delete()
         for store in stores:
             update_data.send(store.__class__, instance=store)
+        update_revisions.send(
+            Directory,
+            object_list=Directory.objects.filter(
+                id__in=set(store.parent.id for store in stores)))
 
     @write_stdout(" * Removing units created by: %(user)s... ")
     def remove_units_created(self):


Test patch:
diff --git a/tests/models/user.py b/tests/models/user.py
--- a/tests/models/user.py
+++ b/tests/models/user.py
@@ -129,6 +129,8 @@ def _test_user_purging(store, member, evil_member, purge):
     assert latest_revision > first_revision
 
     unit = store.units[0]
+    original_revision = unit.store.parent.revisions.get(
+        key="stats").value
 
     # Test submitted/commented/reviewed times on the unit.  This is an
     # unreliable test on MySQL due to datetime precision
@@ -155,6 +157,11 @@ def _test_user_purging(store, member, evil_member, purge):
     assert unit.change.commented_on == initial_comment_time
     assert unit.change.reviewed_on == initial_review_time
 
+    # Revision has been expired for unit's directory
+    assert (
+        unit.store.parent.revisions.get(key="stats").value
+        != original_revision)
+
     # State is be back to how it was before evil user updated.
     _test_before_evil_user_updated(store, member)