зеркало из https://github.com/mozilla/pontoon.git
Add Pretranslated status and drop Fuzzy as status (#2463)
* Fix Time Range layout in local environment * Remove duplicate CSS rule * Fix typo in the spec * Move Fuzzy filter from Status to Extra * Show Fuzzy strings as Missing and support Pretranslated in the string list * Set fuzzy stats to 0 * Link to Missing strings only in new string notifications (which now includes Fuzzy) * Model changes: - Rename fuzzy_strings to pretranslated_strings in AggregatedStats models - Add Locale.pretranslated field - Male relevant logic changes (for stats/insights calculation, active translation selection, etc.) * Replace fuzzy with pretranslated on dashboards and charts * Add missing fuzzy property on EntityTranslation type * Show failed checks for pretranslated * Add Pretranslated status * Remove Fuzzy from Contributors and Profile pages * Render Pretranslated in History panel * Last bit of translate app changes * Batch actions: set Pretranslated flag to false * Do not send suggestion notifications for Pretranslated * More robust tests * Do not approve pretranslated strings on sync * Disable Pretranslated in Dashboards, Filter and Translate view progress chart * Attempt to fix pytest DB error * Add comments explaining why Pretranslated status and filter are disabled * Q objects don't need a wrapping call when they're combined via | or & * Rebase migrations
This commit is contained in:
Родитель
9e77a9404e
Коммит
17138c608c
|
@ -37,7 +37,7 @@ class ProjectLocale(DjangoObjectType, Stats):
|
|||
"locale",
|
||||
"total_strings",
|
||||
"approved_strings",
|
||||
"fuzzy_strings",
|
||||
"pretranslated_strings",
|
||||
"strings_with_errors",
|
||||
"strings_with_warnings",
|
||||
"unreviewed_strings",
|
||||
|
@ -62,7 +62,7 @@ class Project(DjangoObjectType, Stats):
|
|||
"contact",
|
||||
"total_strings",
|
||||
"approved_strings",
|
||||
"fuzzy_strings",
|
||||
"pretranslated_strings",
|
||||
"strings_with_errors",
|
||||
"strings_with_warnings",
|
||||
"unreviewed_strings",
|
||||
|
@ -91,7 +91,7 @@ class Locale(DjangoObjectType, Stats):
|
|||
"population",
|
||||
"total_strings",
|
||||
"approved_strings",
|
||||
"fuzzy_strings",
|
||||
"pretranslated_strings",
|
||||
"strings_with_errors",
|
||||
"strings_with_warnings",
|
||||
"unreviewed_strings",
|
||||
|
|
|
@ -20,7 +20,7 @@ from pontoon.teams.utils import log_user_groups
|
|||
AGGREGATED_STATS_FIELDS = (
|
||||
"total_strings",
|
||||
"approved_strings",
|
||||
"fuzzy_strings",
|
||||
"pretranslated_strings",
|
||||
"strings_with_errors",
|
||||
"strings_with_warnings",
|
||||
"unreviewed_strings",
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# Generated by Django 3.2.10 on 2022-03-23 22:54
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def adjust_stats(model):
|
||||
model.fuzzy_strings = 0
|
||||
model.save(update_fields=["fuzzy_strings"])
|
||||
|
||||
|
||||
def fuzzy_to_missing(apps, schema_editor):
|
||||
ProjectLocale = apps.get_model("base", "ProjectLocale")
|
||||
TranslatedResource = apps.get_model("base", "TranslatedResource")
|
||||
|
||||
for tr in TranslatedResource.objects.filter(fuzzy_strings__gt=0):
|
||||
project = tr.resource.project
|
||||
locale = tr.locale
|
||||
|
||||
adjust_stats(tr)
|
||||
adjust_stats(project)
|
||||
adjust_stats(locale)
|
||||
|
||||
try:
|
||||
project_locale = ProjectLocale.objects.get(project=project, locale=locale)
|
||||
adjust_stats(project_locale)
|
||||
except ProjectLocale.DoesNotExist:
|
||||
return None
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("base", "0022_reviewed_suggestions"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=fuzzy_to_missing,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
),
|
||||
]
|
|
@ -0,0 +1,51 @@
|
|||
# Generated by Django 3.2.10 on 2022-03-24 01:12
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
("base", "0023_fuzzy_to_missing"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name="locale",
|
||||
old_name="fuzzy_strings",
|
||||
new_name="pretranslated_strings",
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="project",
|
||||
old_name="fuzzy_strings",
|
||||
new_name="pretranslated_strings",
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="projectlocale",
|
||||
old_name="fuzzy_strings",
|
||||
new_name="pretranslated_strings",
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="translatedresource",
|
||||
old_name="fuzzy_strings",
|
||||
new_name="pretranslated_strings",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="translation",
|
||||
name="pretranslated",
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AlterIndexTogether(
|
||||
name="translation",
|
||||
index_together={
|
||||
("entity", "user", "approved", "pretranslated"),
|
||||
("locale", "user", "entity"),
|
||||
("date", "locale"),
|
||||
("entity", "locale", "approved"),
|
||||
("entity", "locale", "fuzzy"),
|
||||
("entity", "locale", "pretranslated"),
|
||||
},
|
||||
),
|
||||
]
|
|
@ -300,7 +300,7 @@ def serialized_notifications(self):
|
|||
"part": "all-resources",
|
||||
},
|
||||
)
|
||||
+ "?status=missing,fuzzy",
|
||||
+ "?status=missing",
|
||||
}
|
||||
else:
|
||||
actor = {
|
||||
|
@ -443,7 +443,7 @@ class PermissionChangelog(models.Model):
|
|||
class AggregatedStats(models.Model):
|
||||
total_strings = models.PositiveIntegerField(default=0)
|
||||
approved_strings = models.PositiveIntegerField(default=0)
|
||||
fuzzy_strings = models.PositiveIntegerField(default=0)
|
||||
pretranslated_strings = models.PositiveIntegerField(default=0)
|
||||
strings_with_errors = models.PositiveIntegerField(default=0)
|
||||
strings_with_warnings = models.PositiveIntegerField(default=0)
|
||||
unreviewed_strings = models.PositiveIntegerField(default=0)
|
||||
|
@ -459,7 +459,7 @@ class AggregatedStats(models.Model):
|
|||
return {
|
||||
"total_strings": sum(x.total_strings for x in qs),
|
||||
"approved_strings": sum(x.approved_strings for x in qs),
|
||||
"fuzzy_strings": sum(x.fuzzy_strings for x in qs),
|
||||
"pretranslated_strings": sum(x.pretranslated_strings for x in qs),
|
||||
"strings_with_errors": sum(x.strings_with_errors for x in qs),
|
||||
"strings_with_warnings": sum(x.strings_with_warnings for x in qs),
|
||||
"unreviewed_strings": sum(x.unreviewed_strings for x in qs),
|
||||
|
@ -481,14 +481,16 @@ class AggregatedStats(models.Model):
|
|||
self,
|
||||
total_strings_diff,
|
||||
approved_strings_diff,
|
||||
fuzzy_strings_diff,
|
||||
pretranslated_strings_diff,
|
||||
strings_with_errors_diff,
|
||||
strings_with_warnings_diff,
|
||||
unreviewed_strings_diff,
|
||||
):
|
||||
self.total_strings = F("total_strings") + total_strings_diff
|
||||
self.approved_strings = F("approved_strings") + approved_strings_diff
|
||||
self.fuzzy_strings = F("fuzzy_strings") + fuzzy_strings_diff
|
||||
self.pretranslated_strings = (
|
||||
F("pretranslated_strings") + pretranslated_strings_diff
|
||||
)
|
||||
self.strings_with_errors = F("strings_with_errors") + strings_with_errors_diff
|
||||
self.strings_with_warnings = (
|
||||
F("strings_with_warnings") + strings_with_warnings_diff
|
||||
|
@ -499,7 +501,7 @@ class AggregatedStats(models.Model):
|
|||
update_fields=[
|
||||
"total_strings",
|
||||
"approved_strings",
|
||||
"fuzzy_strings",
|
||||
"pretranslated_strings",
|
||||
"strings_with_errors",
|
||||
"strings_with_warnings",
|
||||
"unreviewed_strings",
|
||||
|
@ -511,7 +513,7 @@ class AggregatedStats(models.Model):
|
|||
return (
|
||||
self.total_strings
|
||||
- self.approved_strings
|
||||
- self.fuzzy_strings
|
||||
- self.pretranslated_strings
|
||||
- self.strings_with_errors
|
||||
- self.strings_with_warnings
|
||||
)
|
||||
|
@ -533,8 +535,8 @@ class AggregatedStats(models.Model):
|
|||
return self.percent_of_total(self.approved_strings)
|
||||
|
||||
@property
|
||||
def fuzzy_percent(self):
|
||||
return self.percent_of_total(self.fuzzy_strings)
|
||||
def pretranslated_percent(self):
|
||||
return self.percent_of_total(self.pretranslated_strings)
|
||||
|
||||
@property
|
||||
def errors_percent(self):
|
||||
|
@ -973,7 +975,7 @@ class Locale(AggregatedStats):
|
|||
"title": "all-resources",
|
||||
"resource__path": [],
|
||||
"resource__total_strings": self.total_strings,
|
||||
"fuzzy_strings": self.fuzzy_strings,
|
||||
"pretranslated_strings": self.pretranslated_strings,
|
||||
"strings_with_errors": self.strings_with_errors,
|
||||
"strings_with_warnings": self.strings_with_warnings,
|
||||
"unreviewed_strings": self.unreviewed_strings,
|
||||
|
@ -991,7 +993,7 @@ class Locale(AggregatedStats):
|
|||
"resource__path",
|
||||
"resource__deadline",
|
||||
"resource__total_strings",
|
||||
"fuzzy_strings",
|
||||
"pretranslated_strings",
|
||||
"strings_with_errors",
|
||||
"strings_with_warnings",
|
||||
"unreviewed_strings",
|
||||
|
@ -1028,8 +1030,8 @@ class Locale(AggregatedStats):
|
|||
resource__path=F("resources__path"),
|
||||
resource__deadline=F("resources__deadline"),
|
||||
resource__total_strings=F("resources__total_strings"),
|
||||
fuzzy_strings=F(
|
||||
"resources__translatedresources__fuzzy_strings"
|
||||
pretranslated_strings=F(
|
||||
"resources__translatedresources__pretranslated_strings"
|
||||
),
|
||||
strings_with_errors=F(
|
||||
"resources__translatedresources__strings_with_errors"
|
||||
|
@ -1056,8 +1058,8 @@ class Locale(AggregatedStats):
|
|||
resource__path=F("project__resources__path"),
|
||||
resource__deadline=F("project__resources__deadline"),
|
||||
resource__total_strings=F("project__resources__total_strings"),
|
||||
fuzzy_strings=F(
|
||||
"project__resources__translatedresources__fuzzy_strings"
|
||||
pretranslated_strings=F(
|
||||
"project__resources__translatedresources__pretranslated_strings"
|
||||
),
|
||||
strings_with_errors=F(
|
||||
"project__resources__translatedresources__strings_with_errors"
|
||||
|
@ -1092,7 +1094,7 @@ class Locale(AggregatedStats):
|
|||
"resource__path": [],
|
||||
"resource__deadline": [],
|
||||
"resource__total_strings": all_resources.total_strings,
|
||||
"fuzzy_strings": all_resources.fuzzy_strings,
|
||||
"pretranslated_strings": all_resources.pretranslated_strings,
|
||||
"strings_with_errors": all_resources.strings_with_errors,
|
||||
"strings_with_warnings": all_resources.strings_with_warnings,
|
||||
"unreviewed_strings": all_resources.unreviewed_strings,
|
||||
|
@ -1637,7 +1639,7 @@ class ProjectLocaleQuerySet(models.QuerySet):
|
|||
return self.aggregate(
|
||||
total_strings=Sum("total_strings"),
|
||||
approved_strings=Sum("approved_strings"),
|
||||
fuzzy_strings=Sum("fuzzy_strings"),
|
||||
pretranslated_strings=Sum("pretranslated_strings"),
|
||||
strings_with_errors=Sum("strings_with_errors"),
|
||||
strings_with_warnings=Sum("strings_with_warnings"),
|
||||
unreviewed_strings=Sum("unreviewed_strings"),
|
||||
|
@ -1776,12 +1778,12 @@ class ProjectLocale(AggregatedStats):
|
|||
return {
|
||||
"total_strings": obj.total_strings,
|
||||
"approved_strings": obj.approved_strings,
|
||||
"fuzzy_strings": obj.fuzzy_strings,
|
||||
"pretranslated_strings": obj.pretranslated_strings,
|
||||
"strings_with_errors": obj.strings_with_errors,
|
||||
"strings_with_warnings": obj.strings_with_warnings,
|
||||
"unreviewed_strings": obj.unreviewed_strings,
|
||||
"approved_share": round(obj.approved_percent),
|
||||
"fuzzy_share": round(obj.fuzzy_percent),
|
||||
"pretranslated_share": round(obj.pretranslated_percent),
|
||||
"errors_share": round(obj.errors_percent),
|
||||
"warnings_share": round(obj.warnings_percent),
|
||||
"unreviewed_share": round(obj.unreviewed_percent),
|
||||
|
@ -2252,7 +2254,7 @@ class EntityQuerySet(models.QuerySet):
|
|||
"""Return a filter to be used to select entities marked as "missing".
|
||||
|
||||
An entity is marked as "missing" if at least one of its plural forms
|
||||
has no approved or fuzzy translations.
|
||||
has no approved or pretranslated translations.
|
||||
|
||||
:arg Locale locale: a Locale object to get translations for
|
||||
|
||||
|
@ -2262,28 +2264,8 @@ class EntityQuerySet(models.QuerySet):
|
|||
return ~Q(
|
||||
pk__in=self.get_filtered_entities(
|
||||
locale,
|
||||
Q(approved=True) | Q(fuzzy=True),
|
||||
lambda x: x.approved or x.fuzzy,
|
||||
project=project,
|
||||
)
|
||||
)
|
||||
|
||||
def fuzzy(self, locale, project=None):
|
||||
"""Return a filter to be used to select entities marked as "fuzzy".
|
||||
|
||||
An entity is marked as "fuzzy" if all of its plural forms have a fuzzy
|
||||
translation.
|
||||
|
||||
:arg Locale locale: a Locale object to get translations for
|
||||
|
||||
:returns: a django ORM Q object to use as a filter
|
||||
|
||||
"""
|
||||
return Q(
|
||||
pk__in=self.get_filtered_entities(
|
||||
locale,
|
||||
Q(fuzzy=True, warnings__isnull=True, errors__isnull=True),
|
||||
lambda x: x.fuzzy,
|
||||
Q(approved=True) | Q(pretranslated=True),
|
||||
lambda x: x.approved or x.pretranslated,
|
||||
project=project,
|
||||
)
|
||||
)
|
||||
|
@ -2292,7 +2274,7 @@ class EntityQuerySet(models.QuerySet):
|
|||
"""Return a filter to be used to select entities with translations with warnings.
|
||||
|
||||
This filter will return an entity if at least one of its plural forms
|
||||
has an approved or fuzzy translation with a warning.
|
||||
has an approved, pretranslated or fuzzy translation with a warning.
|
||||
|
||||
:arg Locale locale: a Locale object to get translations for
|
||||
|
||||
|
@ -2302,8 +2284,10 @@ class EntityQuerySet(models.QuerySet):
|
|||
return Q(
|
||||
pk__in=self.get_filtered_entities(
|
||||
locale,
|
||||
Q(Q(Q(approved=True) | Q(fuzzy=True)) & Q(warnings__isnull=False)),
|
||||
lambda x: (x.approved or x.fuzzy) and x.warnings.count(),
|
||||
(Q(approved=True) | Q(pretranslated=True) | Q(fuzzy=True))
|
||||
& Q(warnings__isnull=False),
|
||||
lambda x: (x.approved or x.pretranslated or x.fuzzy)
|
||||
and x.warnings.count(),
|
||||
match_all=False,
|
||||
prefetch=Prefetch("warnings"),
|
||||
project=project,
|
||||
|
@ -2314,7 +2298,7 @@ class EntityQuerySet(models.QuerySet):
|
|||
"""Return a filter to be used to select entities with translations with errors.
|
||||
|
||||
This filter will return an entity if at least one of its plural forms
|
||||
has an approved or fuzzy translation with an error.
|
||||
has an approved, pretranslated or fuzzy translation with an error.
|
||||
|
||||
:arg Locale locale: a Locale object to get translations for
|
||||
|
||||
|
@ -2324,19 +2308,39 @@ class EntityQuerySet(models.QuerySet):
|
|||
return Q(
|
||||
pk__in=self.get_filtered_entities(
|
||||
locale,
|
||||
Q(Q(Q(approved=True) | Q(fuzzy=True)) & Q(errors__isnull=False)),
|
||||
lambda x: (x.approved or x.fuzzy) and x.errors.count(),
|
||||
(Q(approved=True) | Q(pretranslated=True) | Q(fuzzy=True))
|
||||
& Q(errors__isnull=False),
|
||||
lambda x: (x.approved or x.pretranslated or x.fuzzy)
|
||||
and x.errors.count(),
|
||||
match_all=False,
|
||||
prefetch=Prefetch("errors"),
|
||||
project=project,
|
||||
)
|
||||
)
|
||||
|
||||
def pretranslated(self, locale, project=None):
|
||||
"""Return a filter to be used to select entities marked as "pretranslated".
|
||||
|
||||
An entity is marked as "pretranslated" if all of its plural forms have a pretranslated translation.
|
||||
|
||||
:arg Locale locale: a Locale object to get translations for
|
||||
|
||||
:returns: a django ORM Q object to use as a filter
|
||||
|
||||
"""
|
||||
return Q(
|
||||
pk__in=self.get_filtered_entities(
|
||||
locale,
|
||||
Q(pretranslated=True, warnings__isnull=True, errors__isnull=True),
|
||||
lambda x: x.pretranslated,
|
||||
project=project,
|
||||
)
|
||||
)
|
||||
|
||||
def translated(self, locale, project):
|
||||
"""Return a filter to be used to select entities marked as "approved".
|
||||
|
||||
An entity is marked as "approved" if all of its plural forms have an approved
|
||||
translation.
|
||||
An entity is marked as "approved" if all of its plural forms have an approved translation.
|
||||
|
||||
:arg Locale locale: a Locale object to get translations for
|
||||
|
||||
|
@ -2356,7 +2360,7 @@ class EntityQuerySet(models.QuerySet):
|
|||
"""Return a filter to be used to select entities with suggested translations.
|
||||
|
||||
An entity is said to have suggestions if at least one of its plural forms
|
||||
has at least one unreviewed suggestion (not fuzzy, not approved, not rejected).
|
||||
has at least one unreviewed suggestion (not approved, not rejected, not pretranslated, not fuzzy).
|
||||
|
||||
:arg Locale locale: a Locale object to get translations for
|
||||
|
||||
|
@ -2366,8 +2370,11 @@ class EntityQuerySet(models.QuerySet):
|
|||
return Q(
|
||||
pk__in=self.get_filtered_entities(
|
||||
locale,
|
||||
Q(approved=False, rejected=False, fuzzy=False),
|
||||
lambda x: not x.approved and not x.rejected and not x.fuzzy,
|
||||
Q(approved=False, rejected=False, pretranslated=False, fuzzy=False),
|
||||
lambda x: not x.approved
|
||||
and not x.rejected
|
||||
and not x.pretranslated
|
||||
and not x.fuzzy,
|
||||
match_all=False,
|
||||
project=project,
|
||||
)
|
||||
|
@ -2376,8 +2383,7 @@ class EntityQuerySet(models.QuerySet):
|
|||
def rejected(self, locale, project=None):
|
||||
"""Return a filter to be used to select entities with rejected translations.
|
||||
|
||||
This filter will return all entities that have a rejected translation, whether
|
||||
they have approved or fuzzy translations or not.
|
||||
This filter will return all entities that have a rejected translation.
|
||||
|
||||
:arg Locale locale: a Locale object to get translations for
|
||||
|
||||
|
@ -2406,8 +2412,28 @@ class EntityQuerySet(models.QuerySet):
|
|||
return ~Q(
|
||||
pk__in=self.get_filtered_entities(
|
||||
locale,
|
||||
Q(approved=True) | Q(fuzzy=True) | Q(rejected=False),
|
||||
lambda x: x.approved or x.fuzzy or not x.rejected,
|
||||
Q(approved=True) | Q(pretranslated=True) | Q(rejected=False),
|
||||
lambda x: x.approved or x.pretranslated or not x.rejected,
|
||||
project=project,
|
||||
)
|
||||
)
|
||||
|
||||
def fuzzy(self, locale, project=None):
|
||||
"""Return a filter to be used to select entities marked as "fuzzy".
|
||||
|
||||
An entity is marked as "fuzzy" if all of its plural forms have a fuzzy
|
||||
translation.
|
||||
|
||||
:arg Locale locale: a Locale object to get translations for
|
||||
|
||||
:returns: a django ORM Q object to use as a filter
|
||||
|
||||
"""
|
||||
return Q(
|
||||
pk__in=self.get_filtered_entities(
|
||||
locale,
|
||||
Q(fuzzy=True, warnings__isnull=True, errors__isnull=True),
|
||||
lambda x: x.fuzzy,
|
||||
project=project,
|
||||
)
|
||||
)
|
||||
|
@ -2444,12 +2470,9 @@ class EntityQuerySet(models.QuerySet):
|
|||
return Q(
|
||||
pk__in=self.get_filtered_entities(
|
||||
locale,
|
||||
Q(
|
||||
Q(active=True)
|
||||
& Q(
|
||||
Q(string=F("entity__string"))
|
||||
| Q(string=F("entity__string_plural"))
|
||||
)
|
||||
Q(active=True)
|
||||
& (
|
||||
Q(string=F("entity__string")) | Q(string=F("entity__string_plural"))
|
||||
),
|
||||
lambda x: x.active
|
||||
and (x.string == x.entity.string or x.string == x.entity.string_plural),
|
||||
|
@ -2529,14 +2552,17 @@ class EntityQuerySet(models.QuerySet):
|
|||
# First, deactivate all translations
|
||||
translations.update(active=False)
|
||||
|
||||
# Mark all approved and fuzzy translations as active.
|
||||
translations.filter(Q(approved=True) | Q(fuzzy=True)).update(active=True)
|
||||
# Mark all approved, pretranslated and fuzzy translations as active.
|
||||
translations.filter(
|
||||
Q(approved=True) | Q(pretranslated=True) | Q(fuzzy=True)
|
||||
).update(active=True)
|
||||
|
||||
# Mark most recent unreviewed suggestions without active siblings
|
||||
# for any given combination of (locale, entity, plural_form) as active.
|
||||
unreviewed_pks = set()
|
||||
unreviewed = translations.filter(
|
||||
approved=False,
|
||||
pretranslated=False,
|
||||
fuzzy=False,
|
||||
rejected=False,
|
||||
).values_list("entity", "plural_form")
|
||||
|
@ -2641,35 +2667,36 @@ class Entity(DirtyFieldsMixin, models.Model):
|
|||
]
|
||||
)
|
||||
|
||||
fuzzy_strings_count = len(
|
||||
pretranslated_strings_count = len(
|
||||
[
|
||||
t
|
||||
for t in translations
|
||||
if t.fuzzy and not (t.errors.exists() or t.warnings.exists())
|
||||
if t.pretranslated and not (t.errors.exists() or t.warnings.exists())
|
||||
]
|
||||
)
|
||||
|
||||
if self.string_plural:
|
||||
approved = int(approved_strings_count == locale.nplurals)
|
||||
fuzzy = int(fuzzy_strings_count == locale.nplurals)
|
||||
pretranslated = int(pretranslated_strings_count == locale.nplurals)
|
||||
|
||||
else:
|
||||
approved = int(approved_strings_count > 0)
|
||||
fuzzy = int(fuzzy_strings_count > 0)
|
||||
pretranslated = int(pretranslated_strings_count > 0)
|
||||
|
||||
if not (approved or fuzzy):
|
||||
if not (approved or pretranslated):
|
||||
has_errors = bool(
|
||||
[
|
||||
t
|
||||
for t in translations
|
||||
if (t.approved or t.fuzzy) and t.errors.exists()
|
||||
if (t.approved or t.pretranslated or t.fuzzy) and t.errors.exists()
|
||||
]
|
||||
)
|
||||
has_warnings = bool(
|
||||
[
|
||||
t
|
||||
for t in translations
|
||||
if (t.approved or t.fuzzy) and t.warnings.exists()
|
||||
if (t.approved or t.pretranslated or t.fuzzy)
|
||||
and t.warnings.exists()
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -2681,13 +2708,17 @@ class Entity(DirtyFieldsMixin, models.Model):
|
|||
warnings = 0
|
||||
|
||||
unreviewed_count = len(
|
||||
[t for t in translations if not (t.approved or t.fuzzy or t.rejected)]
|
||||
[
|
||||
t
|
||||
for t in translations
|
||||
if not (t.approved or t.pretranslated or t.fuzzy or t.rejected)
|
||||
]
|
||||
)
|
||||
|
||||
return {
|
||||
"total_strings_diff": 0,
|
||||
"approved_strings_diff": approved,
|
||||
"fuzzy_strings_diff": fuzzy,
|
||||
"pretranslated_strings_diff": pretranslated,
|
||||
"strings_with_errors_diff": errors,
|
||||
"strings_with_warnings_diff": warnings,
|
||||
"unreviewed_strings_diff": unreviewed_count,
|
||||
|
@ -2735,7 +2766,7 @@ class Entity(DirtyFieldsMixin, models.Model):
|
|||
|
||||
def reset_active_translation(self, locale, plural_form=None):
|
||||
"""
|
||||
Reset active translation for given entity, locale and plural for.
|
||||
Reset active translation for given entity, locale and plural form.
|
||||
Return active translation if exists or empty Translation instance.
|
||||
"""
|
||||
translations = self.translation_set.filter(locale=locale)
|
||||
|
@ -2746,7 +2777,7 @@ class Entity(DirtyFieldsMixin, models.Model):
|
|||
translations.update(active=False)
|
||||
|
||||
candidates = translations.filter(rejected=False).order_by(
|
||||
"-approved", "-fuzzy", "-date"
|
||||
"-approved", "-pretranslated", "-fuzzy", "-date"
|
||||
)
|
||||
|
||||
if candidates:
|
||||
|
@ -2847,9 +2878,9 @@ class Entity(DirtyFieldsMixin, models.Model):
|
|||
# Apply a combination of filters based on the list of statuses the user sent.
|
||||
status_filter_choices = (
|
||||
"missing",
|
||||
"fuzzy",
|
||||
"warnings",
|
||||
"errors",
|
||||
"pretranslated",
|
||||
"translated",
|
||||
"unreviewed",
|
||||
)
|
||||
|
@ -2865,6 +2896,7 @@ class Entity(DirtyFieldsMixin, models.Model):
|
|||
"rejected",
|
||||
"unchanged",
|
||||
"empty",
|
||||
"fuzzy",
|
||||
"missing-without-unreviewed",
|
||||
)
|
||||
post_filters.append(
|
||||
|
@ -3094,6 +3126,7 @@ class Translation(DirtyFieldsMixin, models.Model):
|
|||
# each (entity, locale, plural_form) combination. See bug 1481175.
|
||||
active = models.BooleanField(default=False)
|
||||
|
||||
pretranslated = models.BooleanField(default=False)
|
||||
fuzzy = models.BooleanField(default=False)
|
||||
|
||||
approved = models.BooleanField(default=False)
|
||||
|
@ -3158,8 +3191,9 @@ class Translation(DirtyFieldsMixin, models.Model):
|
|||
|
||||
class Meta:
|
||||
index_together = (
|
||||
("entity", "user", "approved", "fuzzy"),
|
||||
("entity", "user", "approved", "pretranslated"),
|
||||
("entity", "locale", "approved"),
|
||||
("entity", "locale", "pretranslated"),
|
||||
("entity", "locale", "fuzzy"),
|
||||
("locale", "user", "entity"),
|
||||
("date", "locale"),
|
||||
|
@ -3279,6 +3313,7 @@ class Translation(DirtyFieldsMixin, models.Model):
|
|||
rejected=True,
|
||||
rejected_user=self.approved_user,
|
||||
rejected_date=self.approved_date,
|
||||
pretranslated=False,
|
||||
fuzzy=False,
|
||||
)
|
||||
|
||||
|
@ -3357,6 +3392,7 @@ class Translation(DirtyFieldsMixin, models.Model):
|
|||
self.approved_user = user
|
||||
self.approved_date = timezone.now()
|
||||
|
||||
self.pretranslated = False
|
||||
self.fuzzy = False
|
||||
|
||||
self.unapproved_user = None
|
||||
|
@ -3396,9 +3432,9 @@ class Translation(DirtyFieldsMixin, models.Model):
|
|||
"""
|
||||
Reject translation.
|
||||
"""
|
||||
# Check if translation was approved or fuzzy.
|
||||
# We must do this before unapproving/unfuzzying it.
|
||||
if self.approved or self.fuzzy:
|
||||
# Check if translation was approved or pretranslated or fuzzy.
|
||||
# We must do this before rejecting it.
|
||||
if self.approved or self.pretranslated or self.fuzzy:
|
||||
TranslationMemoryEntry.objects.filter(translation=self).delete()
|
||||
self.entity.mark_changed(self.locale)
|
||||
|
||||
|
@ -3408,6 +3444,7 @@ class Translation(DirtyFieldsMixin, models.Model):
|
|||
self.approved = False
|
||||
self.approved_user = None
|
||||
self.approved_date = None
|
||||
self.pretranslated = False
|
||||
self.fuzzy = False
|
||||
self.save()
|
||||
|
||||
|
@ -3426,6 +3463,7 @@ class Translation(DirtyFieldsMixin, models.Model):
|
|||
"string": self.string,
|
||||
"approved": self.approved,
|
||||
"rejected": self.rejected,
|
||||
"pretranslated": self.pretranslated,
|
||||
"fuzzy": self.fuzzy,
|
||||
"errors": [error.message for error in self.errors.all()],
|
||||
"warnings": [warning.message for warning in self.warnings.all()],
|
||||
|
@ -3585,7 +3623,7 @@ class TranslatedResourceQuerySet(models.QuerySet):
|
|||
return self.aggregate(
|
||||
total=Sum("resource__total_strings"),
|
||||
approved=Sum("approved_strings"),
|
||||
fuzzy=Sum("fuzzy_strings"),
|
||||
pretranslated=Sum("pretranslated_strings"),
|
||||
errors=Sum("strings_with_errors"),
|
||||
warnings=Sum("strings_with_warnings"),
|
||||
unreviewed=Sum("unreviewed_strings"),
|
||||
|
@ -3596,7 +3634,7 @@ class TranslatedResourceQuerySet(models.QuerySet):
|
|||
|
||||
instance.total_strings = aggregated_stats["total"] or 0
|
||||
instance.approved_strings = aggregated_stats["approved"] or 0
|
||||
instance.fuzzy_strings = aggregated_stats["fuzzy"] or 0
|
||||
instance.pretranslated_strings = aggregated_stats["pretranslated"] or 0
|
||||
instance.strings_with_errors = aggregated_stats["errors"] or 0
|
||||
instance.strings_with_warnings = aggregated_stats["warnings"] or 0
|
||||
instance.unreviewed_strings = aggregated_stats["unreviewed"] or 0
|
||||
|
@ -3605,7 +3643,7 @@ class TranslatedResourceQuerySet(models.QuerySet):
|
|||
update_fields=[
|
||||
"total_strings",
|
||||
"approved_strings",
|
||||
"fuzzy_strings",
|
||||
"pretranslated_strings",
|
||||
"strings_with_errors",
|
||||
"strings_with_warnings",
|
||||
"unreviewed_strings",
|
||||
|
@ -3665,7 +3703,7 @@ class TranslatedResourceQuerySet(models.QuerySet):
|
|||
fields=[
|
||||
"total_strings",
|
||||
"approved_strings",
|
||||
"fuzzy_strings",
|
||||
"pretranslated_strings",
|
||||
"strings_with_errors",
|
||||
"strings_with_warnings",
|
||||
"unreviewed_strings",
|
||||
|
@ -3750,15 +3788,18 @@ class TranslatedResource(AggregatedStats):
|
|||
warnings__isnull=True,
|
||||
).count()
|
||||
|
||||
fuzzy = translations.filter(
|
||||
fuzzy=True,
|
||||
pretranslated = translations.filter(
|
||||
pretranslated=True,
|
||||
errors__isnull=True,
|
||||
warnings__isnull=True,
|
||||
).count()
|
||||
|
||||
errors = (
|
||||
translations.filter(
|
||||
Q(Q(Q(approved=True) | Q(fuzzy=True)) & Q(errors__isnull=False)),
|
||||
Q(
|
||||
Q(Q(approved=True) | Q(pretranslated=True) | Q(fuzzy=True))
|
||||
& Q(errors__isnull=False)
|
||||
),
|
||||
)
|
||||
.distinct()
|
||||
.count()
|
||||
|
@ -3766,7 +3807,10 @@ class TranslatedResource(AggregatedStats):
|
|||
|
||||
warnings = (
|
||||
translations.filter(
|
||||
Q(Q(Q(approved=True) | Q(fuzzy=True)) & Q(warnings__isnull=False)),
|
||||
Q(
|
||||
Q(Q(approved=True) | Q(pretranslated=True) | Q(fuzzy=True))
|
||||
& Q(warnings__isnull=False)
|
||||
),
|
||||
)
|
||||
.distinct()
|
||||
.count()
|
||||
|
@ -3774,8 +3818,9 @@ class TranslatedResource(AggregatedStats):
|
|||
|
||||
unreviewed = translations.filter(
|
||||
approved=False,
|
||||
fuzzy=False,
|
||||
rejected=False,
|
||||
pretranslated=False,
|
||||
fuzzy=False,
|
||||
).count()
|
||||
|
||||
# Plural
|
||||
|
@ -3792,21 +3837,21 @@ class TranslatedResource(AggregatedStats):
|
|||
warnings__isnull=True,
|
||||
).count()
|
||||
|
||||
plural_fuzzy_count = translations.filter(
|
||||
fuzzy=True,
|
||||
plural_pretranslated_count = translations.filter(
|
||||
pretranslated=True,
|
||||
errors__isnull=True,
|
||||
warnings__isnull=True,
|
||||
).count()
|
||||
|
||||
if plural_approved_count == nplurals:
|
||||
approved += 1
|
||||
elif plural_fuzzy_count == nplurals:
|
||||
fuzzy += 1
|
||||
elif plural_pretranslated_count == nplurals:
|
||||
pretranslated += 1
|
||||
else:
|
||||
plural_errors_count = (
|
||||
translations.filter(
|
||||
Q(
|
||||
Q(Q(approved=True) | Q(fuzzy=True))
|
||||
Q(Q(approved=True) | Q(pretranslated=True) | Q(fuzzy=True))
|
||||
& Q(errors__isnull=False)
|
||||
),
|
||||
)
|
||||
|
@ -3817,7 +3862,7 @@ class TranslatedResource(AggregatedStats):
|
|||
plural_warnings_count = (
|
||||
translations.filter(
|
||||
Q(
|
||||
Q(Q(approved=True) | Q(fuzzy=True))
|
||||
Q(Q(approved=True) | Q(pretranslated=True) | Q(fuzzy=True))
|
||||
& Q(warnings__isnull=False)
|
||||
),
|
||||
)
|
||||
|
@ -3831,7 +3876,7 @@ class TranslatedResource(AggregatedStats):
|
|||
warnings += 1
|
||||
|
||||
plural_unreviewed_count = translations.filter(
|
||||
approved=False, fuzzy=False, rejected=False
|
||||
approved=False, pretranslated=False, fuzzy=False, rejected=False
|
||||
).count()
|
||||
if plural_unreviewed_count:
|
||||
unreviewed += plural_unreviewed_count
|
||||
|
@ -3839,7 +3884,7 @@ class TranslatedResource(AggregatedStats):
|
|||
if not save:
|
||||
self.total_strings = resource.total_strings
|
||||
self.approved_strings = approved
|
||||
self.fuzzy_strings = fuzzy
|
||||
self.pretranslated_strings = pretranslated
|
||||
self.strings_with_errors = errors
|
||||
self.strings_with_warnings = warnings
|
||||
self.unreviewed_strings = unreviewed
|
||||
|
@ -3849,7 +3894,7 @@ class TranslatedResource(AggregatedStats):
|
|||
# Calculate diffs to reduce DB queries
|
||||
total_strings_diff = resource.total_strings - self.total_strings
|
||||
approved_strings_diff = approved - self.approved_strings
|
||||
fuzzy_strings_diff = fuzzy - self.fuzzy_strings
|
||||
pretranslated_strings_diff = pretranslated - self.pretranslated_strings
|
||||
strings_with_errors_diff = errors - self.strings_with_errors
|
||||
strings_with_warnings_diff = warnings - self.strings_with_warnings
|
||||
unreviewed_strings_diff = unreviewed - self.unreviewed_strings
|
||||
|
@ -3857,7 +3902,7 @@ class TranslatedResource(AggregatedStats):
|
|||
self.adjust_all_stats(
|
||||
total_strings_diff,
|
||||
approved_strings_diff,
|
||||
fuzzy_strings_diff,
|
||||
pretranslated_strings_diff,
|
||||
strings_with_errors_diff,
|
||||
strings_with_warnings_diff,
|
||||
unreviewed_strings_diff,
|
||||
|
|
|
@ -25,8 +25,8 @@
|
|||
color: #7bc876;
|
||||
}
|
||||
|
||||
.fuzzy .status.fa:before {
|
||||
color: #fed271;
|
||||
.pretranslated .status.fa:before {
|
||||
color: #c0ff00;
|
||||
}
|
||||
|
||||
.warnings .status.fa:before {
|
||||
|
@ -461,8 +461,8 @@ tfoot td a {
|
|||
background: #7bc876;
|
||||
}
|
||||
|
||||
.select .menu ul li .chart span.fuzzy {
|
||||
background: #fed271;
|
||||
.select .menu ul li .chart span.pretranslated {
|
||||
background: #c0ff00;
|
||||
}
|
||||
|
||||
.select .menu ul li .chart span.missing {
|
||||
|
@ -621,55 +621,6 @@ img.rounded {
|
|||
border: 2px solid #4d5967;
|
||||
}
|
||||
|
||||
.details div {
|
||||
border-top: 5px solid;
|
||||
color: #aaaaaa;
|
||||
display: inline-block;
|
||||
margin-right: 1px;
|
||||
width: 89px;
|
||||
}
|
||||
|
||||
.details div:last-child {
|
||||
margin-right: 0;
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
.details div.translated {
|
||||
border-color: #7bc876;
|
||||
}
|
||||
|
||||
.details div.fuzzy {
|
||||
border-color: #fed271;
|
||||
}
|
||||
|
||||
.details div.warnings {
|
||||
border-color: #ffa10f;
|
||||
}
|
||||
|
||||
.details div.errors {
|
||||
border-color: #f36;
|
||||
}
|
||||
|
||||
.details div.missing {
|
||||
border-color: #5f7285;
|
||||
}
|
||||
|
||||
.details div.unreviewed {
|
||||
border-color: #4fc4f6;
|
||||
}
|
||||
|
||||
.details div span {
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.details div p {
|
||||
color: #ffffff;
|
||||
font-size: 28px;
|
||||
padding: 10px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#error {
|
||||
background: #333941;
|
||||
bottom: 0;
|
||||
|
|
|
@ -287,8 +287,8 @@ table.table.project-list.hidden {
|
|||
background: #7bc876;
|
||||
}
|
||||
|
||||
.table .progress .chart-wrapper .fuzzy {
|
||||
background: #fed271;
|
||||
.table .progress .chart-wrapper .pretranslated {
|
||||
background: #c0ff00;
|
||||
}
|
||||
|
||||
.table .progress .chart-wrapper .warnings {
|
||||
|
@ -325,11 +325,11 @@ table.table.project-list.hidden {
|
|||
display: inline-block;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
width: 51px;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.table .progress .legend li:last-child {
|
||||
width: 54px;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.table .progress .legend li a {
|
||||
|
@ -349,8 +349,8 @@ table.table.project-list.hidden {
|
|||
color: #7bc876;
|
||||
}
|
||||
|
||||
.table .progress .legend li.fuzzy .title {
|
||||
color: #fed271;
|
||||
.table .progress .legend li.pretranslated .title {
|
||||
color: #c0ff00;
|
||||
}
|
||||
|
||||
.table .progress .legend li.warnings .title {
|
||||
|
|
|
@ -21,7 +21,7 @@ $(function () {
|
|||
|
||||
var fraction = {
|
||||
translated: stats.all ? stats.translated / stats.all : 0,
|
||||
fuzzy: stats.all ? stats.fuzzy / stats.all : 0,
|
||||
pretranslated: stats.all ? stats.pretranslated / stats.all : 0,
|
||||
warnings: stats.all ? stats.warnings / stats.all : 0,
|
||||
errors: stats.all ? stats.errors / stats.all : 0,
|
||||
missing: stats.all
|
||||
|
|
|
@ -124,7 +124,8 @@ var Pontoon = (function (my) {
|
|||
all = legend.find('.all .value').data('value') || 0,
|
||||
translated =
|
||||
legend.find('.translated .value').data('value') / all || 0,
|
||||
fuzzy = legend.find('.fuzzy .value').data('value') / all || 0;
|
||||
pretranslated =
|
||||
legend.find('.pretranslated .value').data('value') / all || 0;
|
||||
|
||||
if ($(el).find('.progress .not-ready').length) {
|
||||
return 'not-ready';
|
||||
|
@ -132,7 +133,7 @@ var Pontoon = (function (my) {
|
|||
|
||||
return {
|
||||
translated: translated,
|
||||
fuzzy: fuzzy,
|
||||
pretranslated: pretranslated,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -188,7 +189,7 @@ var Pontoon = (function (my) {
|
|||
node.addClass(cls);
|
||||
|
||||
items.sort(function (a, b) {
|
||||
// Sort by translated, then by fuzzy percentage
|
||||
// Sort by translated, then by pretranslated percentage
|
||||
if (node.is('.progress')) {
|
||||
var chartA = getProgress(a),
|
||||
chartB = getProgress(b);
|
||||
|
@ -206,7 +207,7 @@ var Pontoon = (function (my) {
|
|||
|
||||
return (
|
||||
(chartA.translated - chartB.translated) * dir ||
|
||||
(chartA.fuzzy - chartB.fuzzy) * dir
|
||||
(chartA.pretranslated - chartB.pretranslated) * dir
|
||||
);
|
||||
|
||||
// Sort by unreviewed state
|
||||
|
|
|
@ -87,12 +87,15 @@
|
|||
value=obj.approved_strings,
|
||||
link=(link + '?status=translated') if link else None)
|
||||
}}
|
||||
<!-- Pretranslation feature is not ready yet, so we're disabling the Pretranslated status
|
||||
in dashboards to avoid showing unnecessary zeros.
|
||||
{{ legend_item(
|
||||
title='Fuzzy',
|
||||
class='fuzzy',
|
||||
value=obj.fuzzy_strings,
|
||||
link=(link + '?status=fuzzy') if link else None)
|
||||
title='Pretranslated',
|
||||
class='pretranslated',
|
||||
value=obj.pretranslated_strings,
|
||||
link=(link + '?status=pretranslated') if link else None)
|
||||
}}
|
||||
-->
|
||||
{{ legend_item(
|
||||
title='Warnings',
|
||||
class='warnings',
|
||||
|
@ -108,7 +111,7 @@
|
|||
{{ legend_item(
|
||||
title='Missing',
|
||||
class='missing',
|
||||
value=(obj.total_strings - obj.approved_strings - obj.fuzzy_strings - obj.strings_with_warnings - obj.strings_with_errors),
|
||||
value=(obj.total_strings - obj.approved_strings - obj.pretranslated_strings - obj.strings_with_warnings - obj.strings_with_errors),
|
||||
link=(link + '?status=missing') if link else None)
|
||||
}}
|
||||
</ul>
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
<span class="percent">{{ chart.completion_percent }}%</span>
|
||||
<span class="chart">
|
||||
<span class="translated" style="width:{{ chart.approved_share }}%;"></span>
|
||||
<span class="fuzzy" style="width:{{ chart.fuzzy_share }}%;"></span>
|
||||
<span class="pretranslated" style="width:{{ chart.pretranslated_share }}%;"></span>
|
||||
<span class="warnings" style="width:{{ chart.warnings_share }}%;"></span>
|
||||
<span class="errors" style="width:{{ chart.errors_share }}%;"></span>
|
||||
<span class="missing" style="width:{{ 100 - chart.approved_share - chart.fuzzy_share - chart.warnings_share - chart.errors_share }}%;"></span>
|
||||
<span class="missing" style="width:{{ 100 - chart.approved_share - chart.pretranslated_share - chart.warnings_share - chart.errors_share }}%;"></span>
|
||||
</span>
|
||||
<span class="unreviewed-status fa fa-lightbulb{% if chart.unreviewed_strings > 0 %} pending{% endif %}"></span>
|
||||
</div>
|
||||
|
@ -25,12 +25,15 @@
|
|||
<div class="value" data-value="{{ chart.approved_strings }}">{{ chart.approved_strings|comma_or_prefix }}</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="fuzzy">
|
||||
<a href="{{ link }}{% if link_parameter %}{% if not has_params %}?{% else %}&{% endif %}status=fuzzy{% endif %}">
|
||||
<div class="title">Fuzzy</div>
|
||||
<div class="value" data-value="{{ chart.fuzzy_strings }}">{{ chart.fuzzy_strings|comma_or_prefix }}</div>
|
||||
<!-- Pretranslation feature is not ready yet, so we're disabling the Pretranslated status
|
||||
in dashboards to avoid showing unnecessary zeros.
|
||||
<li class="pretranslated">
|
||||
<a href="{{ link }}{% if link_parameter %}{% if not has_params %}?{% else %}&{% endif %}status=pretranslated{% endif %}">
|
||||
<div class="title">Pre</div>
|
||||
<div class="value" data-value="{{ chart.pretranslated_strings }}">{{ chart.pretranslated_strings|comma_or_prefix }}</div>
|
||||
</a>
|
||||
</li>
|
||||
-->
|
||||
<li class="warnings">
|
||||
<a href="{{ link }}{% if link_parameter %}{% if not has_params %}?{% else %}&{% endif %}status=warnings{% endif %}">
|
||||
<div class="title">Wrngs</div>
|
||||
|
@ -46,7 +49,7 @@
|
|||
<li class="missing">
|
||||
<a href="{{ link }}{% if link_parameter %}{% if not has_params %}?{% else %}&{% endif %}status=missing{% endif %}">
|
||||
<div class="title">Missing</div>
|
||||
{% set missing_strings = chart.total_strings - chart.approved_strings - chart.fuzzy_strings - chart.strings_with_warnings - chart.strings_with_errors %}
|
||||
{% set missing_strings = chart.total_strings - chart.approved_strings - chart.pretranslated_strings - chart.strings_with_warnings - chart.strings_with_errors %}
|
||||
<div class="value" data-value="{{ missing_strings }}">{{ missing_strings|comma_or_prefix }}</div>
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
@ -301,19 +301,19 @@ def test_mgr_entity_filter_warnings_plural(resource_a, locale_a):
|
|||
locale=locale_a,
|
||||
entity=entities[0],
|
||||
plural_form=1,
|
||||
fuzzy=True,
|
||||
pretranslated=True,
|
||||
)
|
||||
translation20 = TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[2],
|
||||
plural_form=0,
|
||||
fuzzy=True,
|
||||
pretranslated=True,
|
||||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[2],
|
||||
plural_form=1,
|
||||
fuzzy=True,
|
||||
pretranslated=True,
|
||||
)
|
||||
|
||||
WarningFactory.create(translation=translation20)
|
||||
|
@ -402,6 +402,85 @@ def test_mgr_entity_filter_fuzzy_plurals(resource_a, locale_a):
|
|||
}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_mgr_entity_filter_pretranslated(resource_a, locale_a):
|
||||
entities = [
|
||||
EntityFactory.create(
|
||||
resource=resource_a,
|
||||
string="testentity%s" % i,
|
||||
)
|
||||
for i in range(0, 3)
|
||||
]
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[0],
|
||||
pretranslated=True,
|
||||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[1],
|
||||
approved=True,
|
||||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[2],
|
||||
pretranslated=True,
|
||||
)
|
||||
assert set(Entity.objects.filter(Entity.objects.pretranslated(locale_a))) == {
|
||||
entities[0],
|
||||
entities[2],
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_mgr_entity_filter_pretranslated_plurals(resource_a, locale_a):
|
||||
locale_a.cldr_plurals = "1,5"
|
||||
locale_a.save()
|
||||
entities = [
|
||||
EntityFactory.create(
|
||||
resource=resource_a,
|
||||
string="testentity%s" % i,
|
||||
string_plural="testpluralentity%s" % i,
|
||||
)
|
||||
for i in range(0, 3)
|
||||
]
|
||||
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[0],
|
||||
plural_form=0,
|
||||
pretranslated=True,
|
||||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[0],
|
||||
plural_form=1,
|
||||
pretranslated=True,
|
||||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[1],
|
||||
plural_form=0,
|
||||
pretranslated=True,
|
||||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[2],
|
||||
plural_form=0,
|
||||
pretranslated=True,
|
||||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[2],
|
||||
plural_form=1,
|
||||
pretranslated=True,
|
||||
)
|
||||
assert set(Entity.objects.filter(Entity.objects.pretranslated(locale_a))) == {
|
||||
entities[0],
|
||||
entities[2],
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_mgr_entity_filter_missing(resource_a, locale_a):
|
||||
entities = [
|
||||
|
@ -420,7 +499,7 @@ def test_mgr_entity_filter_missing(resource_a, locale_a):
|
|||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[2],
|
||||
fuzzy=True,
|
||||
pretranslated=True,
|
||||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
|
@ -492,6 +571,11 @@ def test_mgr_entity_filter_unreviewed(resource_a, locale_a):
|
|||
entity=entities[1],
|
||||
string="translation for 1",
|
||||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[2],
|
||||
pretranslated=True,
|
||||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[2],
|
||||
|
@ -510,7 +594,7 @@ def test_mgr_entity_filter_unchanged(resource_a, locale_a):
|
|||
resource=resource_a,
|
||||
string="Unchanged string",
|
||||
)
|
||||
for i in range(0, 3)
|
||||
for i in range(0, 4)
|
||||
]
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
|
@ -519,6 +603,13 @@ def test_mgr_entity_filter_unchanged(resource_a, locale_a):
|
|||
approved=True,
|
||||
string="Unchanged string",
|
||||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[1],
|
||||
active=False,
|
||||
approved=True,
|
||||
string="Unchanged string",
|
||||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[2],
|
||||
|
@ -528,14 +619,15 @@ def test_mgr_entity_filter_unchanged(resource_a, locale_a):
|
|||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[1],
|
||||
active=False,
|
||||
approved=True,
|
||||
entity=entities[3],
|
||||
active=True,
|
||||
pretranslated=True,
|
||||
string="Unchanged string",
|
||||
)
|
||||
assert set(Entity.objects.filter(Entity.objects.unchanged(locale_a))) == {
|
||||
entities[0],
|
||||
entities[2],
|
||||
entities[3],
|
||||
}
|
||||
|
||||
|
||||
|
@ -555,13 +647,13 @@ def test_mgr_entity_filter_missing_plural(resource_a, locale_a):
|
|||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[0],
|
||||
fuzzy=True,
|
||||
pretranslated=True,
|
||||
plural_form=0,
|
||||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[0],
|
||||
fuzzy=True,
|
||||
pretranslated=True,
|
||||
plural_form=1,
|
||||
)
|
||||
TranslationFactory.create(
|
||||
|
@ -598,28 +690,28 @@ def test_mgr_entity_filter_unreviewed_plural(resource_a, locale_a):
|
|||
locale=locale_a,
|
||||
entity=entities[0],
|
||||
approved=False,
|
||||
fuzzy=False,
|
||||
pretranslated=False,
|
||||
plural_form=0,
|
||||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[0],
|
||||
approved=False,
|
||||
fuzzy=False,
|
||||
pretranslated=False,
|
||||
plural_form=1,
|
||||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[2],
|
||||
approved=False,
|
||||
fuzzy=False,
|
||||
pretranslated=False,
|
||||
plural_form=0,
|
||||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[2],
|
||||
approved=False,
|
||||
fuzzy=False,
|
||||
pretranslated=False,
|
||||
plural_form=1,
|
||||
)
|
||||
assert set(Entity.objects.filter(Entity.objects.unreviewed(locale_a))) == {
|
||||
|
@ -661,7 +753,7 @@ def test_mgr_entity_filter_unchanged_plural(resource_a, locale_a):
|
|||
locale=locale_a,
|
||||
entity=entities[1],
|
||||
active=False,
|
||||
fuzzy=True,
|
||||
pretranslated=True,
|
||||
plural_form=0,
|
||||
string="Unchanged string",
|
||||
)
|
||||
|
@ -669,7 +761,7 @@ def test_mgr_entity_filter_unchanged_plural(resource_a, locale_a):
|
|||
locale=locale_a,
|
||||
entity=entities[1],
|
||||
active=False,
|
||||
fuzzy=True,
|
||||
pretranslated=True,
|
||||
plural_form=1,
|
||||
string="Unchanged plural string",
|
||||
)
|
||||
|
@ -677,7 +769,7 @@ def test_mgr_entity_filter_unchanged_plural(resource_a, locale_a):
|
|||
locale=locale_a,
|
||||
entity=entities[2],
|
||||
active=True,
|
||||
fuzzy=True,
|
||||
pretranslated=True,
|
||||
plural_form=0,
|
||||
string="Unchanged string",
|
||||
)
|
||||
|
@ -685,7 +777,7 @@ def test_mgr_entity_filter_unchanged_plural(resource_a, locale_a):
|
|||
locale=locale_a,
|
||||
entity=entities[2],
|
||||
active=True,
|
||||
fuzzy=True,
|
||||
pretranslated=True,
|
||||
plural_form=1,
|
||||
string="Unchanged plural string",
|
||||
)
|
||||
|
@ -1131,7 +1223,7 @@ def test_mgr_entity_reset_active_translations(resource_a, locale_a):
|
|||
resource=resource_a,
|
||||
string="testentity%s" % i,
|
||||
)
|
||||
for i in range(0, 4)
|
||||
for i in range(0, 5)
|
||||
] + [
|
||||
EntityFactory(
|
||||
resource=resource_a,
|
||||
|
@ -1186,27 +1278,41 @@ def test_mgr_entity_reset_active_translations(resource_a, locale_a):
|
|||
fuzzy=True,
|
||||
)
|
||||
|
||||
# Translations for Entity 4 - pluralized:
|
||||
# Translations for Entity 4:
|
||||
# Pretranslated and unreviewed translation
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[4],
|
||||
string=entities[4].string + " translation1",
|
||||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[4],
|
||||
string=entities[4].string + " translation2",
|
||||
pretranslated=True,
|
||||
)
|
||||
|
||||
# Translations for Entity 5 - pluralized:
|
||||
# Approved and unreviewed translation for first form,
|
||||
# a single unreviewed translation for second form
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[4],
|
||||
entity=entities[5],
|
||||
plural_form=0,
|
||||
string=entities[4].string + " translation1",
|
||||
string=entities[5].string + " translation1",
|
||||
approved=True,
|
||||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[4],
|
||||
entity=entities[5],
|
||||
plural_form=0,
|
||||
string=entities[4].string + " translation2",
|
||||
string=entities[5].string + " translation2",
|
||||
)
|
||||
TranslationFactory.create(
|
||||
locale=locale_a,
|
||||
entity=entities[4],
|
||||
entity=entities[5],
|
||||
plural_form=1,
|
||||
string=entities[4].string_plural + " translation1plural",
|
||||
string=entities[5].string_plural + " translation1plural",
|
||||
)
|
||||
|
||||
entities_qs.reset_active_translations(locale=locale_a)
|
||||
|
@ -1236,12 +1342,19 @@ def test_mgr_entity_reset_active_translations(resource_a, locale_a):
|
|||
== entities[3].string + " translation2"
|
||||
)
|
||||
|
||||
# Active translations for Entity 4 - pluralized:
|
||||
# Active translations for Entity 4:
|
||||
# pretranslated translation is active
|
||||
assert (
|
||||
entities[4].translation_set.get(active=True).string
|
||||
== entities[4].string + " translation2"
|
||||
)
|
||||
|
||||
# Active translations for Entity 5 - pluralized:
|
||||
# Approved translation for first form,
|
||||
# a single unreviewed translation for second form
|
||||
active = entities[4].translation_set.filter(active=True)
|
||||
assert active[0].string == entities[4].string + " translation1"
|
||||
assert active[1].string == entities[4].string_plural + " translation1plural"
|
||||
active = entities[5].translation_set.filter(active=True)
|
||||
assert active[0].string == entities[5].string + " translation1"
|
||||
assert active[1].string == entities[5].string_plural + " translation1plural"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
|
@ -125,20 +125,17 @@ def test_mgr_user_translation_counts(
|
|||
assert top_contribs[0].translations_count == 16
|
||||
assert top_contribs[0].translations_approved_count == 5
|
||||
assert top_contribs[0].translations_rejected_count == 9
|
||||
assert top_contribs[0].translations_unapproved_count == 0
|
||||
assert top_contribs[0].translations_needs_work_count == 2
|
||||
assert top_contribs[0].translations_unapproved_count == 2
|
||||
|
||||
assert top_contribs[1].translations_count == 12
|
||||
assert top_contribs[1].translations_approved_count == 7
|
||||
assert top_contribs[1].translations_rejected_count == 3
|
||||
assert top_contribs[1].translations_unapproved_count == 0
|
||||
assert top_contribs[1].translations_needs_work_count == 2
|
||||
assert top_contribs[1].translations_unapproved_count == 2
|
||||
|
||||
assert top_contribs[2].translations_count == 8
|
||||
assert top_contribs[2].translations_approved_count == 1
|
||||
assert top_contribs[2].translations_rejected_count == 0
|
||||
assert top_contribs[2].translations_unapproved_count == 2
|
||||
assert top_contribs[2].translations_needs_work_count == 5
|
||||
assert top_contribs[2].translations_unapproved_count == 7
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
@ -235,33 +232,28 @@ def test_mgr_user_period_filters(
|
|||
assert top_contribs[0].translations_approved_count == 5
|
||||
assert top_contribs[0].translations_rejected_count == 0
|
||||
assert top_contribs[0].translations_unapproved_count == 0
|
||||
assert top_contribs[0].translations_needs_work_count == 0
|
||||
|
||||
top_contribs = users_with_translations_counts(aware_datetime(2015, 5, 10))
|
||||
assert len(top_contribs) == 2
|
||||
assert top_contribs[0].translations_count == 15
|
||||
assert top_contribs[0].translations_approved_count == 2
|
||||
assert top_contribs[0].translations_rejected_count == 0
|
||||
assert top_contribs[0].translations_unapproved_count == 11
|
||||
assert top_contribs[0].translations_needs_work_count == 2
|
||||
assert top_contribs[0].translations_unapproved_count == 13
|
||||
assert top_contribs[1].translations_count == 5
|
||||
assert top_contribs[1].translations_approved_count == 5
|
||||
assert top_contribs[1].translations_rejected_count == 0
|
||||
assert top_contribs[1].translations_unapproved_count == 0
|
||||
assert top_contribs[1].translations_needs_work_count == 0
|
||||
|
||||
top_contribs = users_with_translations_counts(aware_datetime(2015, 1, 10))
|
||||
assert len(top_contribs) == 2
|
||||
assert top_contribs[0].translations_count == 20
|
||||
assert top_contribs[0].translations_approved_count == 17
|
||||
assert top_contribs[0].translations_rejected_count == 0
|
||||
assert top_contribs[0].translations_unapproved_count == 1
|
||||
assert top_contribs[0].translations_needs_work_count == 2
|
||||
assert top_contribs[0].translations_unapproved_count == 3
|
||||
assert top_contribs[1].translations_count == 15
|
||||
assert top_contribs[1].translations_approved_count == 2
|
||||
assert top_contribs[1].translations_rejected_count == 0
|
||||
assert top_contribs[1].translations_unapproved_count == 11
|
||||
assert top_contribs[1].translations_needs_work_count == 2
|
||||
assert top_contribs[1].translations_unapproved_count == 13
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
@ -314,14 +306,12 @@ def test_mgr_user_query_args_filtering(
|
|||
assert top_contribs[0].translations_count == 24
|
||||
assert top_contribs[0].translations_approved_count == 10
|
||||
assert top_contribs[0].translations_rejected_count == 0
|
||||
assert top_contribs[0].translations_unapproved_count == 12
|
||||
assert top_contribs[0].translations_needs_work_count == 2
|
||||
assert top_contribs[0].translations_unapproved_count == 14
|
||||
assert top_contribs[1] == contributors[0]
|
||||
assert top_contribs[1].translations_count == 15
|
||||
assert top_contribs[1].translations_approved_count == 12
|
||||
assert top_contribs[1].translations_rejected_count == 0
|
||||
assert top_contribs[1].translations_unapproved_count == 1
|
||||
assert top_contribs[1].translations_needs_work_count == 2
|
||||
assert top_contribs[1].translations_unapproved_count == 3
|
||||
|
||||
top_contribs = users_with_translations_counts(
|
||||
aware_datetime(2015, 1, 1),
|
||||
|
@ -332,5 +322,4 @@ def test_mgr_user_query_args_filtering(
|
|||
assert top_contribs[0].translations_count == 14
|
||||
assert top_contribs[0].translations_approved_count == 11
|
||||
assert top_contribs[0].translations_rejected_count == 0
|
||||
assert top_contribs[0].translations_unapproved_count == 1
|
||||
assert top_contribs[0].translations_needs_work_count == 2
|
||||
assert top_contribs[0].translations_unapproved_count == 3
|
||||
|
|
|
@ -108,6 +108,21 @@ def test_reset_active_translation_single_approved(translation_a):
|
|||
assert entity.reset_active_translation(locale) == translation_a
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_reset_active_translation_single_pretranslated(translation_a):
|
||||
"""
|
||||
Test if active translations gets set properly for an entity
|
||||
with a single pretranslated translation.
|
||||
"""
|
||||
entity = translation_a.entity
|
||||
locale = translation_a.locale
|
||||
|
||||
translation_a.pretranslated = True
|
||||
translation_a.save()
|
||||
|
||||
assert entity.reset_active_translation(locale) == translation_a
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_reset_active_translation_single_fuzzy(translation_a):
|
||||
"""
|
||||
|
@ -292,6 +307,7 @@ def test_entity_project_locale_no_paths(
|
|||
"translation": [
|
||||
{
|
||||
"pk": tr0.pk,
|
||||
"pretranslated": False,
|
||||
"fuzzy": False,
|
||||
"string": str(tr0.string),
|
||||
"approved": False,
|
||||
|
@ -301,6 +317,7 @@ def test_entity_project_locale_no_paths(
|
|||
},
|
||||
{
|
||||
"pk": tr0pl.pk,
|
||||
"pretranslated": False,
|
||||
"fuzzy": False,
|
||||
"string": str(tr0pl.string),
|
||||
"approved": False,
|
||||
|
|
|
@ -78,7 +78,7 @@ def test_translation_approved(stats_update, get_stats, translation_a):
|
|||
assert get_stats(translation_a) == {
|
||||
"total": 1,
|
||||
"approved": 1,
|
||||
"fuzzy": 0,
|
||||
"pretranslated": 0,
|
||||
"unreviewed": 0,
|
||||
"warnings": 0,
|
||||
"errors": 0,
|
||||
|
@ -90,34 +90,34 @@ def test_translation_approved(stats_update, get_stats, translation_a):
|
|||
assert get_stats(translation_a) == {
|
||||
"total": 1,
|
||||
"approved": 0,
|
||||
"fuzzy": 0,
|
||||
"pretranslated": 0,
|
||||
"unreviewed": 1,
|
||||
"warnings": 0,
|
||||
"errors": 0,
|
||||
}
|
||||
|
||||
|
||||
def test_translation_fuzzy(stats_update, get_stats, translation_a):
|
||||
translation_a.fuzzy = True
|
||||
def test_translation_pretranslated(stats_update, get_stats, translation_a):
|
||||
translation_a.pretranslated = True
|
||||
stats_update(translation_a)
|
||||
|
||||
assert get_stats(translation_a) == {
|
||||
"total": 1,
|
||||
"approved": 0,
|
||||
"fuzzy": 1,
|
||||
"pretranslated": 1,
|
||||
"unreviewed": 0,
|
||||
"warnings": 0,
|
||||
"errors": 0,
|
||||
}
|
||||
|
||||
translation_a.fuzzy = False
|
||||
translation_a.pretranslated = False
|
||||
translation_a.rejected = True
|
||||
stats_update(translation_a)
|
||||
|
||||
assert get_stats(translation_a) == {
|
||||
"total": 1,
|
||||
"approved": 0,
|
||||
"fuzzy": 0,
|
||||
"pretranslated": 0,
|
||||
"unreviewed": 0,
|
||||
"warnings": 0,
|
||||
"errors": 0,
|
||||
|
@ -131,32 +131,32 @@ def test_translation_with_error(stats_update, get_stats, translation_with_error)
|
|||
assert get_stats(translation_with_error) == {
|
||||
"total": 1,
|
||||
"approved": 0,
|
||||
"fuzzy": 0,
|
||||
"pretranslated": 0,
|
||||
"unreviewed": 0,
|
||||
"warnings": 0,
|
||||
"errors": 1,
|
||||
}
|
||||
|
||||
translation_with_error.approved = False
|
||||
translation_with_error.fuzzy = True
|
||||
translation_with_error.pretranslated = True
|
||||
stats_update(translation_with_error)
|
||||
|
||||
assert get_stats(translation_with_error) == {
|
||||
"total": 1,
|
||||
"approved": 0,
|
||||
"fuzzy": 0,
|
||||
"pretranslated": 0,
|
||||
"unreviewed": 0,
|
||||
"warnings": 0,
|
||||
"errors": 1,
|
||||
}
|
||||
|
||||
translation_with_error.fuzzy = False
|
||||
translation_with_error.pretranslated = False
|
||||
stats_update(translation_with_error)
|
||||
|
||||
assert get_stats(translation_with_error) == {
|
||||
"total": 1,
|
||||
"approved": 0,
|
||||
"fuzzy": 0,
|
||||
"pretranslated": 0,
|
||||
"unreviewed": 1,
|
||||
"warnings": 0,
|
||||
"errors": 0,
|
||||
|
@ -170,32 +170,32 @@ def test_translation_with_warning(stats_update, get_stats, translation_with_warn
|
|||
assert get_stats(translation_with_warning) == {
|
||||
"total": 1,
|
||||
"approved": 0,
|
||||
"fuzzy": 0,
|
||||
"pretranslated": 0,
|
||||
"unreviewed": 0,
|
||||
"warnings": 1,
|
||||
"errors": 0,
|
||||
}
|
||||
|
||||
translation_with_warning.approved = False
|
||||
translation_with_warning.fuzzy = True
|
||||
translation_with_warning.pretranslated = True
|
||||
stats_update(translation_with_warning)
|
||||
|
||||
assert get_stats(translation_with_warning) == {
|
||||
"total": 1,
|
||||
"approved": 0,
|
||||
"fuzzy": 0,
|
||||
"pretranslated": 0,
|
||||
"unreviewed": 0,
|
||||
"warnings": 1,
|
||||
"errors": 0,
|
||||
}
|
||||
|
||||
translation_with_warning.fuzzy = False
|
||||
translation_with_warning.pretranslated = False
|
||||
stats_update(translation_with_warning)
|
||||
|
||||
assert get_stats(translation_with_warning) == {
|
||||
"total": 1,
|
||||
"approved": 0,
|
||||
"fuzzy": 0,
|
||||
"pretranslated": 0,
|
||||
"unreviewed": 1,
|
||||
"warnings": 0,
|
||||
"errors": 0,
|
||||
|
|
|
@ -88,6 +88,7 @@ def approve_translations(form, user, translations, locale):
|
|||
rejected=False,
|
||||
rejected_user=None,
|
||||
rejected_date=None,
|
||||
pretranslated=False,
|
||||
fuzzy=False,
|
||||
)
|
||||
|
||||
|
@ -143,6 +144,7 @@ def reject_translations(form, user, translations, locale):
|
|||
approved=False,
|
||||
approved_user=None,
|
||||
approved_date=None,
|
||||
pretranslated=False,
|
||||
fuzzy=False,
|
||||
)
|
||||
|
||||
|
@ -200,6 +202,7 @@ def replace_translations(form, user, translations, locale):
|
|||
rejected=True,
|
||||
rejected_user=user,
|
||||
rejected_date=timezone.now(),
|
||||
pretranslated=False,
|
||||
fuzzy=False,
|
||||
)
|
||||
|
||||
|
|
|
@ -115,6 +115,7 @@ def find_and_replace(translations, find, replace, user):
|
|||
new_translation.rejected = False
|
||||
new_translation.rejected_date = None
|
||||
new_translation.rejected_user = None
|
||||
new_translation.pretranslated = False
|
||||
new_translation.fuzzy = False
|
||||
|
||||
if new_translation.entity.resource.format in DB_FORMATS:
|
||||
|
|
|
@ -26,7 +26,7 @@ body.top-contributors #heading .legend li .status.fa {
|
|||
.contributor {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 580px;
|
||||
width: 670px;
|
||||
}
|
||||
|
||||
.contributor img {
|
||||
|
@ -57,10 +57,24 @@ body.top-contributors #heading .legend li .status.fa {
|
|||
top: 42px;
|
||||
}
|
||||
|
||||
.details div {
|
||||
display: inline-block;
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
.details div span {
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.details div p {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
th:last-child,
|
||||
.stats .details {
|
||||
text-align: center;
|
||||
width: 360px;
|
||||
width: 270px;
|
||||
}
|
||||
|
||||
th:last-child {
|
||||
|
@ -80,10 +94,6 @@ th:last-child sup {
|
|||
color: #7bc876;
|
||||
}
|
||||
|
||||
.stats .details div.fuzzy {
|
||||
color: #fed271;
|
||||
}
|
||||
|
||||
.stats .details div.unreviewed {
|
||||
color: #4fc4f6;
|
||||
}
|
||||
|
|
|
@ -2,16 +2,37 @@
|
|||
padding: 1em 0;
|
||||
}
|
||||
|
||||
#middle .container p {
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
#middle .container div {
|
||||
width: 244px;
|
||||
width: 326px;
|
||||
border-top: 5px solid;
|
||||
color: #aaaaaa;
|
||||
display: inline-block;
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
#middle .container div:last-child {
|
||||
width: 245px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
#middle .container div.translated {
|
||||
border-color: #7bc876;
|
||||
}
|
||||
|
||||
#middle .container div.unreviewed {
|
||||
border-color: #4fc4f6;
|
||||
}
|
||||
|
||||
#middle .container div span {
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
#middle .container div p {
|
||||
color: #ffffff;
|
||||
font-size: 28px;
|
||||
line-height: 18px;
|
||||
padding: 10px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#main {
|
||||
|
|
|
@ -70,11 +70,6 @@
|
|||
class='translated',
|
||||
value=top_contributor.translations_approved_count)
|
||||
}}
|
||||
{{ HeadingInfo.legend_item(
|
||||
title='Fuzzy strings',
|
||||
class='fuzzy',
|
||||
value=top_contributor.translations_needs_work_count)
|
||||
}}
|
||||
{{ HeadingInfo.legend_item(
|
||||
title='Unreviewed suggestions',
|
||||
class='unreviewed',
|
||||
|
|
|
@ -62,12 +62,9 @@
|
|||
</div><div class="translated">
|
||||
<span>Translated</span>
|
||||
<p>{{ translations.filter(approved=True).count()|intcomma }}</p>
|
||||
</div><div class="fuzzy">
|
||||
<span>Fuzzy</span>
|
||||
<p>{{ translations.filter(fuzzy=True).count()|intcomma }}</p>
|
||||
</div><div class="unreviewed">
|
||||
<span>Unreviewed</span>
|
||||
<p>{{ translations.exclude(approved=True).exclude(fuzzy=True).exclude(rejected=True).count()|intcomma }}</p>
|
||||
<p>{{ translations.exclude(approved=True).exclude(rejected=True).count()|intcomma }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -38,9 +38,6 @@
|
|||
</div><div class="approved">
|
||||
<span>Translated</span>
|
||||
<p>{{ contributor.translations_approved_count|intcomma }}</p>
|
||||
</div><div class="fuzzy">
|
||||
<span>Fuzzy</span>
|
||||
<p>{{ contributor.translations_needs_work_count|intcomma }}</p>
|
||||
</div><div class="unreviewed">
|
||||
<span>Unreviewed</span>
|
||||
<p>{{ contributor.translations_unapproved_count|intcomma }}</p>
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
{% if notification.actor.slug %}
|
||||
{% set actor_anchor = notification.actor %}
|
||||
{% if "new string" in notification.verb and user.profile.custom_homepage %}
|
||||
{% set actor_url = url('pontoon.translate.locale.agnostic', notification.actor.slug, "all-resources") + '?status=missing,fuzzy' %}
|
||||
{% set actor_url = url('pontoon.translate.locale.agnostic', notification.actor.slug, "all-resources") + '?status=missing' %}
|
||||
{% else %}
|
||||
{% set actor_url = url('pontoon.projects.project', notification.actor.slug) %}
|
||||
{% endif %}
|
||||
|
|
|
@ -46,7 +46,6 @@ def users_with_translations_counts(start_date=None, query_filters=None, limit=10
|
|||
* translations_approved_count
|
||||
* translations_rejected_count
|
||||
* translations_unapproved_count
|
||||
* translations_needs_work_count
|
||||
* user_role
|
||||
|
||||
All counts will be returned from start_date to now().
|
||||
|
@ -66,9 +65,9 @@ def users_with_translations_counts(start_date=None, query_filters=None, limit=10
|
|||
|
||||
# Count('user') returns 0 if the user is None.
|
||||
# See https://docs.djangoproject.com/en/1.11/topics/db/aggregation/#values.
|
||||
translations = translations.values(
|
||||
"user", "approved", "fuzzy", "rejected"
|
||||
).annotate(count=Count("approved"))
|
||||
translations = translations.values("user", "approved", "rejected").annotate(
|
||||
count=Count("approved")
|
||||
)
|
||||
|
||||
for translation in translations:
|
||||
count = translation["count"]
|
||||
|
@ -76,8 +75,6 @@ def users_with_translations_counts(start_date=None, query_filters=None, limit=10
|
|||
|
||||
if translation["approved"]:
|
||||
status = "approved"
|
||||
elif translation["fuzzy"]:
|
||||
status = "fuzzy"
|
||||
elif translation["rejected"]:
|
||||
status = "rejected"
|
||||
else:
|
||||
|
@ -88,7 +85,6 @@ def users_with_translations_counts(start_date=None, query_filters=None, limit=10
|
|||
"total": 0,
|
||||
"approved": 0,
|
||||
"unreviewed": 0,
|
||||
"fuzzy": 0,
|
||||
"rejected": 0,
|
||||
}
|
||||
|
||||
|
@ -128,7 +124,6 @@ def users_with_translations_counts(start_date=None, query_filters=None, limit=10
|
|||
contributor.translations_approved_count = user["approved"]
|
||||
contributor.translations_rejected_count = user["rejected"]
|
||||
contributor.translations_unapproved_count = user["unreviewed"]
|
||||
contributor.translations_needs_work_count = user["fuzzy"]
|
||||
|
||||
if contributor.pk is None:
|
||||
contributor.user_role = "System User"
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# Generated by Django 3.2.10 on 2022-03-24 14:23
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("base", "0024_fuzzy_to_pretranslated_strings"),
|
||||
("insights", "0009_fix_projectlocale_insights_data"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name="localeinsightssnapshot",
|
||||
old_name="fuzzy_strings",
|
||||
new_name="pretranslated_strings",
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="projectinsightssnapshot",
|
||||
old_name="fuzzy_strings",
|
||||
new_name="pretranslated_strings",
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="projectlocaleinsightssnapshot",
|
||||
old_name="fuzzy_strings",
|
||||
new_name="pretranslated_strings",
|
||||
),
|
||||
]
|
|
@ -154,6 +154,7 @@ def get_suggestions():
|
|||
# Make sure TranslatedResource is still enabled for the locale
|
||||
locale=F("entity__resource__translatedresources__locale"),
|
||||
approved=False,
|
||||
pretranslated=False,
|
||||
fuzzy=False,
|
||||
rejected=False,
|
||||
entity__obsolete=False,
|
||||
|
@ -270,7 +271,7 @@ def get_locale_insights_snapshot(
|
|||
# AggregatedStats
|
||||
total_strings=locale.total_strings,
|
||||
approved_strings=locale.approved_strings,
|
||||
fuzzy_strings=locale.fuzzy_strings,
|
||||
pretranslated_strings=locale.pretranslated_strings,
|
||||
strings_with_errors=locale.strings_with_errors,
|
||||
strings_with_warnings=locale.strings_with_warnings,
|
||||
unreviewed_strings=locale.unreviewed_strings,
|
||||
|
@ -323,7 +324,7 @@ def get_project_locale_insights_snapshot(
|
|||
# AggregatedStats
|
||||
total_strings=project_locale.total_strings,
|
||||
approved_strings=project_locale.approved_strings,
|
||||
fuzzy_strings=project_locale.fuzzy_strings,
|
||||
pretranslated_strings=project_locale.pretranslated_strings,
|
||||
strings_with_errors=project_locale.strings_with_errors,
|
||||
strings_with_warnings=project_locale.strings_with_warnings,
|
||||
unreviewed_strings=project_locale.unreviewed_strings,
|
||||
|
|
|
@ -33,7 +33,7 @@ def test_latest_activity(mock_parts_stats, mock_render, client, project_a, local
|
|||
"resource__total_strings": 1,
|
||||
"approved_strings": 0,
|
||||
"unreviewed_strings": 1,
|
||||
"fuzzy_strings": 0,
|
||||
"pretranslated_strings": 0,
|
||||
"strings_with_warnings": 0,
|
||||
"strings_with_errors": 0,
|
||||
"resource__deadline": None,
|
||||
|
@ -45,7 +45,7 @@ def test_latest_activity(mock_parts_stats, mock_render, client, project_a, local
|
|||
"resource__total_strings": 1,
|
||||
"approved_strings": 0,
|
||||
"unreviewed_strings": 0,
|
||||
"fuzzy_strings": 0,
|
||||
"pretranslated_strings": 0,
|
||||
"strings_with_warnings": 0,
|
||||
"strings_with_errors": 0,
|
||||
"resource__deadline": None,
|
||||
|
@ -73,11 +73,11 @@ def test_latest_activity(mock_parts_stats, mock_render, client, project_a, local
|
|||
"resource__total_strings": 1,
|
||||
"approved_strings": 0,
|
||||
"unreviewed_strings": 1,
|
||||
"fuzzy_strings": 0,
|
||||
"pretranslated_strings": 0,
|
||||
"strings_with_errors": 0,
|
||||
"strings_with_warnings": 0,
|
||||
"chart": {
|
||||
"fuzzy_strings": 0,
|
||||
"pretranslated_strings": 0,
|
||||
"total_strings": 1,
|
||||
"approved_strings": 0,
|
||||
"unreviewed_strings": 1,
|
||||
|
@ -85,7 +85,7 @@ def test_latest_activity(mock_parts_stats, mock_render, client, project_a, local
|
|||
"strings_with_warnings": 0,
|
||||
"approved_share": 0.0,
|
||||
"unreviewed_share": 100.0,
|
||||
"fuzzy_share": 0.0,
|
||||
"pretranslated_share": 0.0,
|
||||
"warnings_share": 0.0,
|
||||
"errors_share": 0.0,
|
||||
"completion_percent": 0,
|
||||
|
@ -100,11 +100,11 @@ def test_latest_activity(mock_parts_stats, mock_render, client, project_a, local
|
|||
"resource__total_strings": 1,
|
||||
"approved_strings": 0,
|
||||
"unreviewed_strings": 0,
|
||||
"fuzzy_strings": 0,
|
||||
"pretranslated_strings": 0,
|
||||
"strings_with_errors": 0,
|
||||
"strings_with_warnings": 0,
|
||||
"chart": {
|
||||
"fuzzy_strings": 0,
|
||||
"pretranslated_strings": 0,
|
||||
"total_strings": 1,
|
||||
"approved_strings": 0,
|
||||
"unreviewed_strings": 0,
|
||||
|
@ -112,7 +112,7 @@ def test_latest_activity(mock_parts_stats, mock_render, client, project_a, local
|
|||
"strings_with_warnings": 0,
|
||||
"approved_share": 0.0,
|
||||
"unreviewed_share": 0.0,
|
||||
"fuzzy_share": 0.0,
|
||||
"pretranslated_share": 0.0,
|
||||
"warnings_share": 0.0,
|
||||
"errors_share": 0.0,
|
||||
"completion_percent": 0,
|
||||
|
|
|
@ -109,7 +109,7 @@ def ajax_resources(request, code, slug):
|
|||
|
||||
part["chart"] = {
|
||||
"unreviewed_strings": part["unreviewed_strings"],
|
||||
"fuzzy_strings": part["fuzzy_strings"],
|
||||
"pretranslated_strings": part["pretranslated_strings"],
|
||||
"strings_with_errors": part["strings_with_errors"],
|
||||
"strings_with_warnings": part["strings_with_warnings"],
|
||||
"total_strings": part["resource__total_strings"],
|
||||
|
@ -120,8 +120,8 @@ def ajax_resources(request, code, slug):
|
|||
"unreviewed_share": round(
|
||||
part["unreviewed_strings"] / part["resource__total_strings"] * 100
|
||||
),
|
||||
"fuzzy_share": round(
|
||||
part["fuzzy_strings"] / part["resource__total_strings"] * 100
|
||||
"pretranslated_share": round(
|
||||
part["pretranslated_strings"] / part["resource__total_strings"] * 100
|
||||
),
|
||||
"errors_share": round(
|
||||
part["strings_with_errors"] / part["resource__total_strings"] * 100
|
||||
|
|
|
@ -116,7 +116,7 @@ def pretranslate(self, project_pk, locales=None, entities=None):
|
|||
string=string,
|
||||
user=user,
|
||||
approved=False,
|
||||
fuzzy=True,
|
||||
pretranslated=True,
|
||||
active=True,
|
||||
plural_form=plural_form,
|
||||
)
|
||||
|
|
|
@ -49,8 +49,8 @@ def test_pretranslate(gt_mock, project_a, locale_a, resource_a, locale_b):
|
|||
# Total pretranslations = 2(tr_ax) + 2(tr_bx) + 2(tr_ay)
|
||||
assert len(translations) == 6
|
||||
|
||||
# fuzzy count == total pretranslations.
|
||||
assert project_a.fuzzy_strings == 6
|
||||
# pretranslated count == total pretranslations.
|
||||
assert project_a.pretranslated_strings == 6
|
||||
|
||||
# latest_translation belongs to pretranslations.
|
||||
assert project_a.latest_translation in translations
|
||||
|
|
|
@ -75,7 +75,7 @@ class Command(BaseCommand):
|
|||
start = timezone.now() - timedelta(days=7)
|
||||
|
||||
return Translation.objects.filter(
|
||||
approved=False, rejected=False, fuzzy=False
|
||||
approved=False, pretranslated=False, rejected=False, fuzzy=False
|
||||
).filter(
|
||||
Q(date__gt=start)
|
||||
| Q(unapproved_date__gt=start)
|
||||
|
|
|
@ -280,7 +280,11 @@ class ChangeSet:
|
|||
# Modify existing translation.
|
||||
if db_translation:
|
||||
new_action = None
|
||||
if not db_translation.approved and not vcs_translation.fuzzy:
|
||||
if (
|
||||
not db_translation.approved
|
||||
and not db_translation.pretranslated
|
||||
and not vcs_translation.fuzzy
|
||||
):
|
||||
new_action = ActionLog(
|
||||
action_type=ActionLog.ActionType.TRANSLATION_APPROVED,
|
||||
performed_by=user or self.sync_user,
|
||||
|
@ -299,7 +303,7 @@ class ChangeSet:
|
|||
self.actions_to_log.append(new_action)
|
||||
if db_translation.fuzzy:
|
||||
fuzzy_translations.append(db_translation)
|
||||
else:
|
||||
elif not db_translation.pretranslated:
|
||||
approved_translations.append(db_translation)
|
||||
|
||||
# Create new translation.
|
||||
|
|
|
@ -90,7 +90,7 @@ class SyncLog(BaseLog):
|
|||
for pl in ProjectLocale.objects.filter(project=p):
|
||||
pl.aggregate_stats()
|
||||
|
||||
# approved + fuzzy + errors + warnings > total in TranslatedResource
|
||||
# approved + pretranslated + errors + warnings > total in TranslatedResource
|
||||
for t in (
|
||||
TranslatedResource.objects.filter(
|
||||
resource__project__disabled=False,
|
||||
|
@ -99,7 +99,7 @@ class SyncLog(BaseLog):
|
|||
.annotate(
|
||||
total=Sum(
|
||||
F("approved_strings")
|
||||
+ F("fuzzy_strings")
|
||||
+ F("pretranslated_strings")
|
||||
+ F("strings_with_errors")
|
||||
+ F("strings_with_warnings")
|
||||
)
|
||||
|
|
|
@ -124,7 +124,7 @@ def _get_translated_resources(locales, project, resources):
|
|||
"locale": locale,
|
||||
"approved_strings": (i + 2) ** 3,
|
||||
"unreviewed_strings": (i + 1) ** 3,
|
||||
"fuzzy_strings": i ** 3,
|
||||
"pretranslated_strings": i ** 3,
|
||||
}
|
||||
)
|
||||
return _kwargs
|
||||
|
|
|
@ -32,7 +32,7 @@ def _assert_tags(expected, data):
|
|||
"total_strings",
|
||||
"approved_strings",
|
||||
"unreviewed_strings",
|
||||
"fuzzy_strings",
|
||||
"pretranslated_strings",
|
||||
]
|
||||
for slug, stats in results.items():
|
||||
_exp = expected[slug]
|
||||
|
@ -105,7 +105,7 @@ def _calculate_tags(**kwargs):
|
|||
attrs = [
|
||||
"approved_strings",
|
||||
"unreviewed_strings",
|
||||
"fuzzy_strings",
|
||||
"pretranslated_strings",
|
||||
]
|
||||
totals = {}
|
||||
resource_attrs = [
|
||||
|
|
|
@ -191,5 +191,5 @@ def test_util_tags_stats_tool_groupby_locale(
|
|||
for locale in data:
|
||||
locale_exp = exp[locale["locale"]]
|
||||
assert locale_exp["total_strings"] == locale["total_strings"]
|
||||
assert locale_exp["fuzzy_strings"] == locale["fuzzy_strings"]
|
||||
assert locale_exp["pretranslated_strings"] == locale["pretranslated_strings"]
|
||||
assert locale_exp["approved_strings"] == locale["approved_strings"]
|
||||
|
|
|
@ -173,7 +173,7 @@ def test_util_tag_chart():
|
|||
|
||||
chart = TagChart()
|
||||
assert chart.approved_strings is None
|
||||
assert chart.fuzzy_strings is None
|
||||
assert chart.pretranslated_strings is None
|
||||
assert chart.total_strings is None
|
||||
assert chart.unreviewed_strings is None
|
||||
|
||||
|
@ -187,12 +187,12 @@ def test_util_tag_chart():
|
|||
|
||||
chart = TagChart(
|
||||
total_strings=73,
|
||||
fuzzy_strings=7,
|
||||
pretranslated_strings=7,
|
||||
approved_strings=13,
|
||||
strings_with_warnings=0,
|
||||
unreviewed_strings=23,
|
||||
)
|
||||
assert chart.approved_share == 18.0
|
||||
assert chart.fuzzy_share == 10.0
|
||||
assert chart.pretranslated_share == 10.0
|
||||
assert chart.unreviewed_share == 32.0
|
||||
assert chart.completion_percent == 17
|
||||
|
|
|
@ -5,7 +5,7 @@ class TagChart:
|
|||
def __init__(self, **kwargs):
|
||||
self.kwargs = kwargs
|
||||
self.approved_strings = kwargs.get("approved_strings")
|
||||
self.fuzzy_strings = kwargs.get("fuzzy_strings")
|
||||
self.pretranslated_strings = kwargs.get("pretranslated_strings")
|
||||
self.strings_with_warnings = kwargs.get("strings_with_warnings")
|
||||
self.strings_with_errors = kwargs.get("strings_with_errors")
|
||||
self.total_strings = kwargs.get("total_strings")
|
||||
|
@ -26,8 +26,8 @@ class TagChart:
|
|||
return self._share(self.approved_strings)
|
||||
|
||||
@property
|
||||
def fuzzy_share(self):
|
||||
return self._share(self.fuzzy_strings)
|
||||
def pretranslated_share(self):
|
||||
return self._share(self.pretranslated_strings)
|
||||
|
||||
@property
|
||||
def warnings_share(self):
|
||||
|
|
|
@ -20,7 +20,7 @@ class TagsStatsTool(TagsTRTool):
|
|||
# from the perspective of translated resources
|
||||
_default_annotations = (
|
||||
("total_strings", Coalesce(Sum("resource__total_strings"), Value(0))),
|
||||
("fuzzy_strings", Coalesce(Sum("fuzzy_strings"), Value(0))),
|
||||
("pretranslated_strings", Coalesce(Sum("pretranslated_strings"), Value(0))),
|
||||
("strings_with_warnings", Coalesce(Sum("strings_with_warnings"), Value(0))),
|
||||
("strings_with_errors", Coalesce(Sum("strings_with_errors"), Value(0))),
|
||||
("approved_strings", Coalesce(Sum("approved_strings"), Value(0))),
|
||||
|
|
|
@ -10,7 +10,7 @@ class Tagged:
|
|||
def __init__(self, **kwargs):
|
||||
self._latest_translation = kwargs.pop("latest_translation", None)
|
||||
self.approved_strings = kwargs.get("approved_strings")
|
||||
self.fuzzy_strings = kwargs.get("fuzzy_strings")
|
||||
self.pretranslated_strings = kwargs.get("pretranslated_strings")
|
||||
self.strings_with_warnings = kwargs.get("strings_with_warnings")
|
||||
self.strings_with_errors = kwargs.get("strings_with_errors")
|
||||
self.total_strings = kwargs.get("total_strings")
|
||||
|
|
|
@ -22,7 +22,7 @@ def update_terminology_project_stats():
|
|||
[
|
||||
"total_strings",
|
||||
"approved_strings",
|
||||
"fuzzy_strings",
|
||||
"pretranslated_strings",
|
||||
"strings_with_errors",
|
||||
"strings_with_warnings",
|
||||
"unreviewed_strings",
|
||||
|
|
|
@ -34,7 +34,7 @@ Let's have a closer look at each of them.
|
|||
|
||||
We treat Gettext Fuzzy strings as Missing instead of Pretranslated on dashboards, in string list, in progress chart, i.e. everywhere.
|
||||
|
||||
Internally, we keep using the fuzzy=True flag for Fuzzy strings, which allows us to distinct them from Missing and:
|
||||
Internally, we keep using the fuzzy=True flag for Fuzzy strings, which allows us to distinguish them from Missing and:
|
||||
1. Sync them with version control system.
|
||||
2. Run quality checks on them.
|
||||
3. Set fuzzy flag accordingly in the .po file.
|
||||
|
|
|
@ -84,7 +84,7 @@ batchactions-ReplaceAll--error = OOPS, SOMETHING WENT WRONG
|
|||
resourceprogress-ResourceProgress--all-strings = ALL STRINGS
|
||||
resourceprogress-ResourceProgress--unreviewed = UNREVIEWED
|
||||
resourceprogress-ResourceProgress--translated = TRANSLATED
|
||||
resourceprogress-ResourceProgress--fuzzy = FUZZY
|
||||
resourceprogress-ResourceProgress--pretranslated = PRETRANSLATED
|
||||
resourceprogress-ResourceProgress--warnings = WARNINGS
|
||||
resourceprogress-ResourceProgress--errors = ERRORS
|
||||
resourceprogress-ResourceProgress--missing = MISSING
|
||||
|
@ -621,13 +621,13 @@ search-FiltersPanel--heading-extra = EXTRA FILTERS
|
|||
search-FiltersPanel--heading-authors = TRANSLATION AUTHORS
|
||||
search-FiltersPanel--status-name-all = All
|
||||
search-FiltersPanel--status-name-translated = Translated
|
||||
search-FiltersPanel--status-name-fuzzy = Fuzzy
|
||||
search-FiltersPanel--status-name-warnings = Warnings
|
||||
search-FiltersPanel--status-name-errors = Errors
|
||||
search-FiltersPanel--status-name-missing = Missing
|
||||
search-FiltersPanel--status-name-unreviewed = Unreviewed
|
||||
search-FiltersPanel--extra-name-unchanged = Unchanged
|
||||
search-FiltersPanel--extra-name-empty = Empty
|
||||
search-FiltersPanel--extra-name-fuzzy = Fuzzy
|
||||
search-FiltersPanel--extra-name-rejected = Rejected
|
||||
|
||||
search-FiltersPanel--clear-selection = <glyph></glyph>CLEAR
|
||||
|
|
|
@ -5,6 +5,7 @@ export type EntityTranslation = {
|
|||
readonly pk: number;
|
||||
readonly string: string | null | undefined;
|
||||
readonly approved: boolean;
|
||||
readonly pretranslated: boolean;
|
||||
readonly fuzzy: boolean;
|
||||
readonly rejected: boolean;
|
||||
readonly errors: Array<string>;
|
||||
|
|
|
@ -43,7 +43,7 @@ export function _getTranslationForSelectedEntity(
|
|||
* Return the active translation for the currently selected entity
|
||||
* and plural form.
|
||||
*
|
||||
* The active translation is either the approved one, the fuzzy one, or the
|
||||
* The active translation is either the approved one, the pretranslated one, the fuzzy one, or the
|
||||
* most recent non-rejected one.
|
||||
*/
|
||||
export const getTranslationForSelectedEntity = createSelector(
|
||||
|
@ -64,7 +64,7 @@ export function _getTranslationStringForSelectedEntity(
|
|||
* Return the active translation *string* for the currently selected entity
|
||||
* and plural form.
|
||||
*
|
||||
* The active translation is either the approved one, the fuzzy one, or the
|
||||
* The active translation is either the approved one, the pretranslated one, the fuzzy one, or the
|
||||
* most recent non-rejected one.
|
||||
*/
|
||||
export const getTranslationStringForSelectedEntity = createSelector(
|
||||
|
|
|
@ -2,7 +2,7 @@ export const UPDATE: 'stats/UPDATE' = 'stats/UPDATE';
|
|||
|
||||
export type APIStats = {
|
||||
approved: number;
|
||||
fuzzy: number;
|
||||
pretranslated: number;
|
||||
warnings: number;
|
||||
errors: number;
|
||||
unreviewed: number;
|
||||
|
@ -23,7 +23,7 @@ export function update(stats: APIStats): UpdateAction {
|
|||
missing:
|
||||
stats.total -
|
||||
stats.approved -
|
||||
stats.fuzzy -
|
||||
stats.pretranslated -
|
||||
stats.errors -
|
||||
stats.warnings,
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@ type Action = UpdateAction;
|
|||
|
||||
const initial: Stats = {
|
||||
approved: 0,
|
||||
fuzzy: 0,
|
||||
pretranslated: 0,
|
||||
warnings: 0,
|
||||
errors: 0,
|
||||
missing: 0,
|
||||
|
|
|
@ -92,8 +92,8 @@
|
|||
color: #7bc876;
|
||||
}
|
||||
|
||||
.entity.fuzzy .status:before {
|
||||
color: #fed271;
|
||||
.entity.pretranslated .status:before {
|
||||
color: #c0ff00;
|
||||
}
|
||||
|
||||
.entity.errors .status:before {
|
||||
|
|
|
@ -27,7 +27,7 @@ describe('<Entity>', () => {
|
|||
translation: [
|
||||
{
|
||||
string: 'chaine b',
|
||||
fuzzy: true,
|
||||
pretranslated: true,
|
||||
errors: [],
|
||||
warnings: [],
|
||||
},
|
||||
|
@ -62,7 +62,7 @@ describe('<Entity>', () => {
|
|||
translation: [
|
||||
{
|
||||
string: 'chaine e',
|
||||
fuzzy: true,
|
||||
pretranslated: true,
|
||||
errors: [],
|
||||
warnings: ['warning'],
|
||||
},
|
||||
|
@ -80,7 +80,7 @@ describe('<Entity>', () => {
|
|||
},
|
||||
{
|
||||
string: 'chaine f2',
|
||||
fuzzy: true,
|
||||
pretranslated: true,
|
||||
errors: [],
|
||||
warnings: [],
|
||||
},
|
||||
|
@ -102,7 +102,7 @@ describe('<Entity>', () => {
|
|||
expect(wrapper.find('.approved')).toHaveLength(1);
|
||||
|
||||
wrapper = shallow(<Entity entity={ENTITY_B} parameters={{}} />);
|
||||
expect(wrapper.find('.fuzzy')).toHaveLength(1);
|
||||
expect(wrapper.find('.pretranslated')).toHaveLength(1);
|
||||
|
||||
wrapper = shallow(<Entity entity={ENTITY_C} parameters={{}} />);
|
||||
expect(wrapper.find('.missing')).toHaveLength(1);
|
||||
|
|
|
@ -27,17 +27,17 @@ type Props = {
|
|||
* The format of this element is: "[Status] Source (Translation)"
|
||||
*
|
||||
* "Status" is the current status of the translation. Can be:
|
||||
* - "errors": one of the plural forms has errors and is approved or fuzzy
|
||||
* - "warnings": one of the plural forms has warnings and is approved or fuzzy
|
||||
* - "errors": one of the plural forms has errors and is approved, pretranslated or fuzzy
|
||||
* - "warnings": one of the plural forms has warnings and is approved, pretranslated or fuzzy
|
||||
* - "approved": all plural forms are approved and don't have errors or warnings
|
||||
* - "fuzzy": all plural forms are fuzzy and don't have errors or warnings
|
||||
* - "partial": some plural forms have either approved or fuzzy translations, but not all
|
||||
* - "missing": none of the plural forms have an approved or fuzzy translation
|
||||
* - "pretranslated": all plural forms are pretranslated and don't have errors or warnings
|
||||
* - "partial": some plural forms have either approved or pretranslated translations, but not all
|
||||
* - "missing": none of the plural forms have an approved or pretranslated translation
|
||||
*
|
||||
* "Source" is the original string from the project. Usually it's the en-US string.
|
||||
*
|
||||
* "Translation" is the current "best" translation. It shows either the approved
|
||||
* translation, or the fuzzy translation, or the last suggested translation.
|
||||
* "Translation" is the current "best" translation. It shows either the approved,
|
||||
* pretranslated or fuzzy translation, or the last suggested translation.
|
||||
*/
|
||||
export function Entity({
|
||||
checkedForBatchEditing,
|
||||
|
@ -152,19 +152,24 @@ function translationStatus(translations: EntityTranslation[]): string {
|
|||
let errors = false;
|
||||
let warnings = false;
|
||||
let approved = 0;
|
||||
let fuzzy = 0;
|
||||
let pretranslated = 0;
|
||||
|
||||
for (const tx of translations) {
|
||||
if (tx.errors.length && (tx.approved || tx.fuzzy)) errors = true;
|
||||
else if (tx.warnings.length && (tx.approved || tx.fuzzy)) warnings = true;
|
||||
if (tx.errors.length && (tx.approved || tx.pretranslated || tx.fuzzy))
|
||||
errors = true;
|
||||
else if (
|
||||
tx.warnings.length &&
|
||||
(tx.approved || tx.pretranslated || tx.fuzzy)
|
||||
)
|
||||
warnings = true;
|
||||
else if (tx.approved) approved += 1;
|
||||
else if (tx.fuzzy) fuzzy += 1;
|
||||
else if (tx.pretranslated) pretranslated += 1;
|
||||
}
|
||||
|
||||
if (errors) return 'errors';
|
||||
if (warnings) return 'warnings';
|
||||
if (approved === translations.length) return 'approved';
|
||||
if (fuzzy === translations.length) return 'fuzzy';
|
||||
if (approved > 0 || fuzzy > 0) return 'partial';
|
||||
if (pretranslated === translations.length) return 'pretranslated';
|
||||
if (approved > 0 || pretranslated > 0) return 'partial';
|
||||
return 'missing';
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ describe('<EntityDetailsBase>', () => {
|
|||
|
||||
// enzyme shallow rendering doesn't support useEffect()
|
||||
// https://github.com/enzymejs/enzyme/issues/2086
|
||||
it.skip('shows failed checks for approved (or fuzzy) translations with errors or warnings', () => {
|
||||
it.skip('shows failed checks for approved translations with errors or warnings', () => {
|
||||
const wrapper = createShallowEntityDetails();
|
||||
|
||||
// componentDidMount(): reset failed checks
|
||||
|
@ -170,7 +170,7 @@ describe('<EntityDetailsBase>', () => {
|
|||
|
||||
// enzyme shallow rendering doesn't support useEffect()
|
||||
// https://github.com/enzymejs/enzyme/issues/2086
|
||||
it.skip('hides failed checks for approved (or fuzzy) translations without errors or warnings', () => {
|
||||
it.skip('hides failed checks for pretranslated translations without errors or warnings', () => {
|
||||
const wrapper = createShallowEntityDetails();
|
||||
|
||||
// componentDidMount(): reset failed checks
|
||||
|
@ -184,7 +184,7 @@ describe('<EntityDetailsBase>', () => {
|
|||
original: 'something',
|
||||
translation: [
|
||||
{
|
||||
approved: true,
|
||||
pretranslated: true,
|
||||
string: 'quelque chose',
|
||||
errors: [],
|
||||
warnings: [],
|
||||
|
|
|
@ -136,12 +136,13 @@ export function EntityDetailsBase({
|
|||
const plural = pluralForm === -1 ? 0 : pluralForm;
|
||||
const translation = selectedEntity.translation[plural];
|
||||
|
||||
// Only show failed checks for active translations that are approved or fuzzy,
|
||||
// i.e. when their status icon is colored as error/warning in the string list
|
||||
// Only show failed checks for active translations that are approved,
|
||||
// pretranslated or fuzzy, i.e. when their status icon is colored as
|
||||
// error/warning in the string list
|
||||
if (
|
||||
translation &&
|
||||
(translation.errors.length || translation.warnings.length) &&
|
||||
(translation.approved || translation.fuzzy)
|
||||
(translation.approved || translation.pretranslated || translation.fuzzy)
|
||||
) {
|
||||
const failedChecks: FailedChecks = {
|
||||
clErrors: translation.errors,
|
||||
|
|
|
@ -17,6 +17,7 @@ export const UPDATE: 'history/UPDATE' = 'history/UPDATE';
|
|||
export type HistoryTranslation = {
|
||||
readonly approved: boolean;
|
||||
readonly approvedUser: string;
|
||||
readonly pretranslated: boolean;
|
||||
readonly date: string;
|
||||
readonly dateIso: string;
|
||||
readonly fuzzy: boolean;
|
||||
|
|
|
@ -135,10 +135,14 @@
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
.history .translation.fuzzy .content > header button.approve:before {
|
||||
.history .translation.pretranslated .content > header button.approve:before {
|
||||
color: #fed271;
|
||||
}
|
||||
|
||||
.history .translation.fuzzy .content > header button.approve:before {
|
||||
color: #c0ff00;
|
||||
}
|
||||
|
||||
.history .translation .content > header button.approve:before,
|
||||
.history .translation .content > header button.unapprove:before {
|
||||
content: '';
|
||||
|
|
|
@ -13,6 +13,7 @@ describe('<TranslationBase>', () => {
|
|||
const DEFAULT_TRANSLATION = {
|
||||
approved: false,
|
||||
approvedUser: '',
|
||||
pretranslated: false,
|
||||
date: '',
|
||||
dateIso: '',
|
||||
fuzzy: false,
|
||||
|
@ -67,6 +68,22 @@ describe('<TranslationBase>', () => {
|
|||
expect(wrapper.find('.rejected')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('returns the correct status for pretranslated translations', () => {
|
||||
const translation = {
|
||||
...DEFAULT_TRANSLATION,
|
||||
...{ pretranslated: true },
|
||||
};
|
||||
const wrapper = shallow(
|
||||
<TranslationBase
|
||||
translation={translation}
|
||||
entity={DEFAULT_ENTITY}
|
||||
user={DEFAULT_USER}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('.pretranslated')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('returns the correct status for fuzzy translations', () => {
|
||||
const translation = {
|
||||
...DEFAULT_TRANSLATION,
|
||||
|
|
|
@ -209,6 +209,8 @@ export function TranslationBase({
|
|||
'translation',
|
||||
translation.approved
|
||||
? 'approved'
|
||||
: translation.pretranslated
|
||||
? 'pretranslated'
|
||||
: translation.fuzzy
|
||||
? 'fuzzy'
|
||||
: translation.rejected
|
||||
|
|
|
@ -45,7 +45,7 @@ export default class ProgressChart extends React.Component<Props> {
|
|||
}
|
||||
|
||||
drawCanvas() {
|
||||
const { approved, fuzzy, warnings, errors, missing, total } =
|
||||
const { approved, pretranslated, warnings, errors, missing, total } =
|
||||
this.props.stats;
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
const canvas = this.canvas.current;
|
||||
|
@ -60,8 +60,8 @@ export default class ProgressChart extends React.Component<Props> {
|
|||
color: '#7BC876',
|
||||
},
|
||||
{
|
||||
type: total ? fuzzy / total : 0,
|
||||
color: '#FED271',
|
||||
type: total ? pretranslated / total : 0,
|
||||
color: '#C0FF00',
|
||||
},
|
||||
{
|
||||
type: total ? warnings / total : 0,
|
||||
|
|
|
@ -96,20 +96,20 @@
|
|||
color: #aaaaaa;
|
||||
display: inline-block;
|
||||
margin-right: 1px;
|
||||
width: 79px;
|
||||
width: 99px;
|
||||
}
|
||||
|
||||
.progress-chart .menu .details div:last-child {
|
||||
margin-right: 0;
|
||||
width: 80px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.progress-chart .menu .details div.approved {
|
||||
border-color: #7bc876;
|
||||
}
|
||||
|
||||
.progress-chart .menu .details div.fuzzy {
|
||||
border-color: #fed271;
|
||||
.progress-chart .menu .details div.pretranslated {
|
||||
border-color: #c0ff00;
|
||||
}
|
||||
|
||||
.progress-chart .menu .details div.warnings {
|
||||
|
|
|
@ -6,7 +6,7 @@ import ResourceProgressBase from './ResourceProgress';
|
|||
describe('<ResourceProgressBase>', () => {
|
||||
const STATS = {
|
||||
approved: 5,
|
||||
fuzzy: 4,
|
||||
pretranslated: 4,
|
||||
unreviewed: 5,
|
||||
warnings: 3,
|
||||
errors: 2,
|
||||
|
|
|
@ -33,8 +33,15 @@ function ResourceProgress({
|
|||
stats,
|
||||
onDiscard,
|
||||
}: ResourceProgressProps) {
|
||||
const { approved, fuzzy, warnings, errors, missing, unreviewed, total } =
|
||||
stats;
|
||||
const {
|
||||
approved,
|
||||
pretranslated,
|
||||
warnings,
|
||||
errors,
|
||||
missing,
|
||||
unreviewed,
|
||||
total,
|
||||
} = stats;
|
||||
|
||||
const ref = React.useRef(null);
|
||||
useOnDiscard(ref, onDiscard);
|
||||
|
@ -77,23 +84,26 @@ function ResourceProgress({
|
|||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
<div className='fuzzy'>
|
||||
{/* Pretranslation feature is not ready yet, so we're disabling the
|
||||
Pretranslated filter, which wouldn't catch anything.
|
||||
<div className='pretranslated'>
|
||||
<span className='title'>
|
||||
<Localized id='resourceprogress-ResourceProgress--fuzzy'>
|
||||
FUZZY
|
||||
<Localized id='resourceprogress-ResourceProgress--pretranslated'>
|
||||
PRETRANSLATED
|
||||
</Localized>
|
||||
</span>
|
||||
<p className='value' onClick={onDiscard}>
|
||||
<Link
|
||||
to={{
|
||||
pathname: currentPath,
|
||||
search: '?status=fuzzy',
|
||||
search: '?status=pretranslated',
|
||||
}}
|
||||
>
|
||||
{asLocaleString(fuzzy)}
|
||||
{asLocaleString(pretranslated)}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
*/}
|
||||
<div className='warnings'>
|
||||
<span className='title'>
|
||||
<Localized id='resourceprogress-ResourceProgress--warnings'>
|
||||
|
|
|
@ -71,11 +71,6 @@
|
|||
background-color: #3f4752;
|
||||
}
|
||||
|
||||
.filters-panel .menu li:not(.horizontal-separator) {
|
||||
padding: 4px 0 4px 30px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.filters-panel .menu li .status {
|
||||
font-size: 16px;
|
||||
margin: -3px -13px -3px -16px;
|
||||
|
|
|
@ -66,8 +66,8 @@
|
|||
color: #7bc876;
|
||||
}
|
||||
|
||||
.search-box .fuzzy .status:before {
|
||||
color: #fed271;
|
||||
.search-box .pretranslated .status:before {
|
||||
color: #c0ff00;
|
||||
}
|
||||
|
||||
.search-box .warnings .status:before {
|
||||
|
@ -102,6 +102,10 @@
|
|||
font-weight: 100;
|
||||
}
|
||||
|
||||
.search-box .fuzzy .status:before {
|
||||
content: '\f059';
|
||||
}
|
||||
|
||||
.search-box .menu .empty.selected .status:before,
|
||||
.search-box .menu .empty .status:hover:before {
|
||||
font-weight: 900;
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.filters-panel .menu li.time-range {
|
||||
.filters-panel .menu li:not(.horizontal-separator).time-range {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,10 +11,6 @@ export const FILTERS_STATUS = [
|
|||
slug: 'translated',
|
||||
stat: 'approved',
|
||||
},
|
||||
{
|
||||
name: 'Fuzzy',
|
||||
slug: 'fuzzy',
|
||||
},
|
||||
{
|
||||
name: 'Warnings',
|
||||
slug: 'warnings',
|
||||
|
@ -44,6 +40,10 @@ export const FILTERS_EXTRA = [
|
|||
name: 'Empty',
|
||||
slug: 'empty',
|
||||
},
|
||||
{
|
||||
name: 'Fuzzy',
|
||||
slug: 'fuzzy',
|
||||
},
|
||||
{
|
||||
name: 'Rejected',
|
||||
slug: 'rejected',
|
||||
|
|
Загрузка…
Ссылка в новой задаче