[damned-lies] chore: flake8 fixes on stats
- From: Claude Paroz <claudep src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [damned-lies] chore: flake8 fixes on stats
- Date: Sat, 8 May 2021 10:08:30 +0000 (UTC)
commit 0926bca33e3e33a6dd9fdeb77f3163f045517450
Author: Guillaume Bernard <associations guillaume-bernard fr>
Date: Sat May 1 16:53:14 2021 +0200
chore: flake8 fixes on stats
stats/admin.py | 23 +-
stats/forms.py | 9 +-
stats/management/commands/run-maintenance.py | 1 +
stats/management/commands/update-from-doap.py | 1 +
stats/management/commands/update-stats.py | 26 +-
stats/management/commands/update-trans.py | 7 +-
stats/models.py | 354 +++++++++++++++-----------
stats/potdiff.py | 36 ++-
stats/repos.py | 6 +-
stats/templatetags/stats_extras.py | 4 +-
stats/tests/fixture_factory.py | 192 +++++++++-----
stats/tests/tests.py | 110 ++++----
stats/tests/utils.py | 10 +-
stats/utils.py | 123 +++++----
stats/views.py | 51 ++--
vertimus/tests/tests.py | 4 +-
16 files changed, 586 insertions(+), 371 deletions(-)
---
diff --git a/stats/admin.py b/stats/admin.py
index 0f334446..466766fb 100644
--- a/stats/admin.py
+++ b/stats/admin.py
@@ -63,11 +63,11 @@ class DomainInline(admin.StackedInline):
def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name == 'description':
- kwargs['widget'] = forms.Textarea(attrs={'rows':'1', 'cols':'20'})
+ kwargs['widget'] = forms.Textarea(attrs={'rows': '1', 'cols': '20'})
elif db_field.name in ('name', 'layout'):
- kwargs['widget'] = forms.TextInput(attrs={'size':'20'})
+ kwargs['widget'] = forms.TextInput(attrs={'size': '20'})
elif db_field.name in ('red_filter', 'extra_its_dirs'):
- kwargs['widget'] = forms.Textarea(attrs={'rows':'1', 'cols':'40'})
+ kwargs['widget'] = forms.Textarea(attrs={'rows': '1', 'cols': '40'})
return super().formfield_for_dbfield(db_field, **kwargs)
def formfield_for_foreignkey(self, db_field, request, **kwargs):
@@ -100,14 +100,14 @@ class ModuleAdmin(admin.ModelAdmin):
form = ModuleForm
fieldsets = (
(None, {
- 'fields': (('name','description'),
- 'homepage', 'comment', 'archived',
+ 'fields': (('name', 'description'),
+ 'homepage', 'comment', 'archived',
('bugs_base',),
('vcs_type', 'vcs_root', 'vcs_web'),
'ext_platform', 'maintainers')
}),
)
- inlines = [ BranchInline, DomainInline ]
+ inlines = [BranchInline, DomainInline]
search_fields = ('name', 'homepage', 'comment', 'vcs_web')
list_filter = ('archived',)
@@ -136,18 +136,21 @@ class DomainAdmin(admin.ModelAdmin):
list_filter = ('dtype', 'pot_method')
search_fields = ('name', 'module__name', 'layout', 'pot_method')
+
class CategoryInline(admin.TabularInline):
model = Category
- raw_id_fields = ('branch',) # Too costly otherwise
+ raw_id_fields = ('branch',) # Too costly otherwise
extra = 1
+
class CategoryAdmin(admin.ModelAdmin):
search_fields = ('name__name', 'branch__module__name')
+
class ReleaseAdmin(admin.ModelAdmin):
list_display = ('name', 'status', 'weight', 'string_frozen')
list_editable = ('weight',)
- inlines = [ CategoryInline ]
+ inlines = [CategoryInline]
actions = ['copy_release', 'delete_release']
def copy_release(self, request, queryset):
@@ -178,6 +181,7 @@ class ReleaseAdmin(admin.ModelAdmin):
branch_seen.add(branch)
messages.success(request, "New release '%s' created" % new_rel.name)
return HttpResponseRedirect(request.path)
+
copy_release.short_description = "Copy release (and associated branches)"
def delete_release(self, request, queryset):
@@ -210,6 +214,7 @@ class ReleaseAdmin(admin.ModelAdmin):
"action_checkbox_name": helpers.ACTION_CHECKBOX_NAME,
}
return render(request, 'admin/delete_release_confirmation.html', context)
+
delete_release.short_description = "Delete release (and associated branches)"
@@ -221,7 +226,7 @@ class InformationInline(admin.TabularInline):
class StatisticsAdmin(admin.ModelAdmin):
search_fields = ('language__name', 'branch__module__name')
raw_id_fields = ('branch', 'domain', 'language', 'full_po', 'part_po')
- inlines = [ InformationInline ]
+ inlines = [InformationInline]
class PoFileAdmin(admin.ModelAdmin):
diff --git a/stats/forms.py b/stats/forms.py
index fbdac4e0..d0190ef6 100644
--- a/stats/forms.py
+++ b/stats/forms.py
@@ -26,7 +26,7 @@ class ModuleBranchForm(forms.Form):
self.fields[str(category.id)] = ReleaseField(
queryset=Release.objects.all(), label=branch.name, initial=category.release.pk
)
- self.fields[str(category.id)+'_cat'] = forms.ModelChoiceField(
+ self.fields[str(category.id) + '_cat'] = forms.ModelChoiceField(
queryset=CategoryName.objects.all(),
required=False,
initial=category.name
@@ -58,8 +58,11 @@ class ModuleBranchForm(forms.Form):
def clean(self):
cleaned_data = super().clean()
for field_name in list(cleaned_data.keys()):
- if (field_name.endswith('_cat') and cleaned_data[field_name] is None
- and cleaned_data[field_name[:-4]] is not None):
+ if (
+ field_name.endswith('_cat')
+ and cleaned_data[field_name] is None
+ and cleaned_data[field_name[:-4]] is not None
+ ):
self.add_error(
field_name,
ValidationError(_("You have to provide a category when a version is specified."))
diff --git a/stats/management/commands/run-maintenance.py b/stats/management/commands/run-maintenance.py
index 994a47bb..24825ac9 100644
--- a/stats/management/commands/run-maintenance.py
+++ b/stats/management/commands/run-maintenance.py
@@ -5,6 +5,7 @@ from teams.models import Role
from vertimus.models import ActionArchived
from languages.views import clean_tar_files
+
class Command(BaseCommand):
help = "Run maintenance tasks"
diff --git a/stats/management/commands/update-from-doap.py b/stats/management/commands/update-from-doap.py
index 8f3e2984..00a47097 100644
--- a/stats/management/commands/update-from-doap.py
+++ b/stats/management/commands/update-from-doap.py
@@ -4,6 +4,7 @@ from django.core.management.base import BaseCommand
from stats.models import Module, ModuleLock
from stats.doap import update_doap_infos
+
class Command(BaseCommand):
help = "Update module information from doap file"
diff --git a/stats/management/commands/update-stats.py b/stats/management/commands/update-stats.py
index b5ae9969..fcd241c1 100644
--- a/stats/management/commands/update-stats.py
+++ b/stats/management/commands/update-stats.py
@@ -13,18 +13,26 @@ class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument('module', nargs='?', default=None)
parser.add_argument('branch', nargs='*')
- parser.add_argument('--release',
- help="update all modules and branches linked to the specified release name")
- parser.add_argument('--force', action='store_true', default=False,
- help="force statistics generation, even if files didn't change")
- parser.add_argument('--non-gnome', action='store_true', default=False,
- help="generate statistics for non-gnome modules (externally hosted)")
- parser.add_argument('--debug', action='store_true', default=False,
- help="activate interactive debug mode")
+ parser.add_argument(
+ '--release',
+ help="update all modules and branches linked to the specified release name"
+ )
+ parser.add_argument(
+ '--force', action='store_true', default=False,
+ help="force statistics generation, even if files didn't change"
+ )
+ parser.add_argument(
+ '--non-gnome', action='store_true', default=False,
+ help="generate statistics for non-gnome modules (externally hosted)"
+ )
+ parser.add_argument(
+ '--debug', action='store_true', default=False,
+ help="activate interactive debug mode"
+ )
def handle(self, **options):
if options['debug']:
- import pdb; pdb.set_trace()
+ breakpoint()
self.force_stats = options['force']
if options['module'] and options['branch']:
# Update the specific branch(es) of a module
diff --git a/stats/management/commands/update-trans.py b/stats/management/commands/update-trans.py
index 2f92c7d0..7ec9f941 100644
--- a/stats/management/commands/update-trans.py
+++ b/stats/management/commands/update-trans.py
@@ -1,16 +1,16 @@
+import itertools
import os
import re
import shutil
-import itertools
from django.conf import settings
from django.core.management import call_command
from django.core.management.base import BaseCommand
from django.db.models import F
-from teams.models import Team
from languages.models import Language
from stats.models import Module, Domain, Release, CategoryName
+from teams.models import Team
class Command(BaseCommand):
@@ -46,7 +46,8 @@ class Command(BaseCommand):
Module.objects.exclude(name__exact=F('description')).values_list('description', flat=True),
Module.objects.exclude(comment='').values_list('comment', flat=True),
Release.objects.values_list('description', flat=True),
- CategoryName.objects.values_list('name', flat=True)):
+ CategoryName.objects.values_list('name', flat=True)
+ ):
if value:
value = re.sub(r'\r\n|\r|\n', '\n', value)
fh.write("_(u\"\"\"%s\"\"\")\n" % value)
diff --git a/stats/models.py b/stats/models.py
index 8505b5de..54612d7a 100644
--- a/stats/models.py
+++ b/stats/models.py
@@ -56,7 +56,6 @@ GLIB_PRESET = (
'--flag=g_set_error:4:c-format',
)
-
# Standard Django slug validation but also accept '+' (for gtk+)
slug_re = re.compile(r'^[-\+a-zA-Z0-9_]+\Z')
validate_slug = RegexValidator(
@@ -87,8 +86,10 @@ class UnableToCommit(Exception):
class Module(models.Model):
name = models.CharField(max_length=50, unique=True, validators=[validate_slug])
- homepage = models.URLField(blank=True,
- help_text="Automatically updated if the module contains a doap file.")
+ homepage = models.URLField(
+ blank=True,
+ help_text="Automatically updated if the module contains a doap file."
+ )
description = models.TextField(blank=True)
comment = models.TextField(blank=True)
bugs_base = models.CharField(max_length=250, blank=True)
@@ -96,13 +97,19 @@ class Module(models.Model):
# URLField is too restrictive for vcs_root
vcs_root = models.CharField(max_length=200)
vcs_web = models.URLField()
- ext_platform = models.URLField(blank=True,
- help_text="URL to external translation platform, if any")
+ ext_platform = models.URLField(
+ blank=True,
+ help_text="URL to external translation platform, if any"
+ )
archived = models.BooleanField(default=False)
- maintainers = models.ManyToManyField(Person, db_table='module_maintainer',
- related_name='maintains_modules', blank=True,
- help_text="Automatically updated if the module contains a doap file.")
+ maintainers = models.ManyToManyField(
+ Person,
+ db_table='module_maintainer',
+ related_name='maintains_modules',
+ blank=True,
+ help_text="Automatically updated if the module contains a doap file."
+ )
class Meta:
db_table = 'module'
@@ -129,9 +136,10 @@ class Module(models.Model):
comment += "<br/>"
comment = "%s<em>%s</em>" % (
comment,
- _("""Translations for this module are externally hosted. Please go to the <a
href="%(link)s">external platform</a> to see how you can submit your translation.""") % {
- 'link': self.ext_platform
- }
+ _(
+ 'Translations for this module are externally hosted. Please go to the <a
href="%(link)s">'
+ 'external platform</a> to see how you can submit your translation.'
+ ) % {'link': self.ext_platform}
)
return comment
@@ -166,7 +174,7 @@ class Module(models.Model):
def can_edit_branches(self, user):
""" Returns True for superusers, users with adequate permissions or maintainers of the module """
- if is_site_admin(user) or user.username in [ p.username for p in self.maintainers.all() ]:
+ if is_site_admin(user) or user.username in [p.username for p in self.maintainers.all()]:
return True
return False
@@ -185,6 +193,7 @@ class ModuleLock:
""" Weird things happen when multiple updates run in parallel for the same module
We use filesystem directories creation/deletion to act as global lock mecanism
"""
+
def __init__(self, mod):
self.module = mod
self.dirpath = settings.LOCK_DIR / f"updating-{self.module.name}"
@@ -213,11 +222,10 @@ class ModuleLock:
@total_ordering
class Branch(models.Model):
""" Branch of a module """
- name = models.CharField(max_length=50)
- #description = models.TextField(null=True)
+ name = models.CharField(max_length=50)
vcs_subpath = models.CharField(max_length=50, blank=True)
- module = models.ForeignKey(Module, on_delete=models.CASCADE)
- weight = models.IntegerField(default=0, help_text="Smaller weight is displayed first")
+ module = models.ForeignKey(Module, on_delete=models.CASCADE)
+ weight = models.IntegerField(default=0, help_text="Smaller weight is displayed first")
file_hashes = models.JSONField(blank=True, null=True, editable=False)
# 'releases' is the backward relation name from Release model
@@ -254,13 +262,15 @@ class Branch(models.Model):
with ModuleLock(self.module):
self.checkout()
except Exception:
- raise ValidationError("Branch not valid: error while checking out the branch (%s)." %
sys.exc_info()[1])
+ raise ValidationError(
+ "Branch not valid: error while checking out the branch (%s)." % sys.exc_info()[1]
+ )
def save(self, update_statistics=True, **kwargs):
super().save(**kwargs)
if update_statistics and not self.module.archived:
# The update command is launched asynchronously in a separate thread
- upd_thread = threading.Thread(target=self.update_stats, kwargs={'force':True})
+ upd_thread = threading.Thread(target=self.update_stats, kwargs={'force': True})
upd_thread.start()
def delete(self):
@@ -312,7 +322,7 @@ class Branch(models.Model):
"""
full_path = self.co_path / rel_path
if not full_path.exists():
- return False # Raise exception?
+ return False # FIXME: Raise exception?
new_hash = utils.compute_md5(full_path)
if self.file_hashes and self.file_hashes.get(rel_path, None) == new_hash:
return False
@@ -371,7 +381,7 @@ class Branch(models.Model):
def has_domain(self, domain):
# generate query only if branch_from/branch_to are defined.
- if (domain.branch_from_id or domain.branch_to_id):
+ if domain.branch_from_id or domain.branch_to_id:
if (domain.branch_from and self > domain.branch_from) or (domain.branch_to and self <
domain.branch_to):
return False
return True
@@ -387,22 +397,28 @@ class Branch(models.Model):
return dirname
def get_stats(self, typ, mandatory_langs=()):
- """ Get statistics list of type typ ('ui' or 'doc'), in a dict of lists, key is domain.name (POT in
1st position)
- stats = {'po': [potstat, polang1, polang2, ...],
- 'po-tips': [potstat, polang1, polang2, ...]}
- mandatory_langs is an iterable of language objects whose stats should be added even if no
translation exists.
+ """
+ Get statistics list of type typ ('ui' or 'doc'), in a dict of lists, key is domain.name (POT in 1st
position)
+ stats = {'po': [potstat, polang1, polang2, ...],
+ 'po-tips': [potstat, polang1, polang2, ...]}
+ mandatory_langs is an iterable of language objects whose stats should be added even if no
translation exists.
"""
stats = OrderedDict()
stats_langs = {}
domain_pks = [d.pk for d in self.get_domains().values() if d.dtype == typ]
- pot_stats = Statistics.objects.select_related("language", "domain", "branch", "full_po"
- ).filter(branch=self, language__isnull=True, domain__pk__in=domain_pks
- ).order_by('domain__name')
+ pot_stats = Statistics.objects.select_related(
+ "language", "domain", "branch", "full_po"
+ ).filter(
+ branch=self, language__isnull=True, domain__pk__in=domain_pks
+ ).order_by('domain__name')
for stat in pot_stats.all():
- stats[stat.domain.name] = [stat,]
+ stats[stat.domain.name] = [stat]
stats_langs[stat.domain.name] = []
- tr_stats = Statistics.objects.select_related("language", "domain", "branch", "full_po"
- ).filter(branch=self, language__isnull=False, domain__pk__in=domain_pks)
+ tr_stats = Statistics.objects.select_related(
+ "language", "domain", "branch", "full_po"
+ ).filter(
+ branch=self, language__isnull=False, domain__pk__in=domain_pks
+ )
for stat in tr_stats.all():
stats[stat.domain.name].append(stat)
stats_langs[stat.domain.name].append(stat.language)
@@ -414,7 +430,7 @@ class Branch(models.Model):
# Sort
for doms in stats.values():
# Sort stats, pot first, then translated (desc), then language name
- doms.sort(key=lambda st:(int(st.language is not None), -st.translated(), st.get_lang()))
+ doms.sort(key=lambda st: (int(st.language is not None), -st.translated(), st.get_lang()))
return stats
def get_doc_stats(self, mandatory_langs=()):
@@ -653,14 +669,16 @@ class Domain(models.Model):
)
linguas_location = models.CharField(
max_length=50, blank=True,
- help_text=f"Use 'no' for no LINGUAS check, or path/to/file#variable for a non-standard location.\n"
- f"Leave blank for standard location (ALL_LINGUAS in LINGUAS/configure.ac/.in for UI and
DOC_LINGUAS "
- f"in Makefile.am for DOC)"
+ help_text=(
+ "Use 'no' for no LINGUAS check, or path/to/file#variable for a non-standard location.\n"
+ "Leave blank for standard location (ALL_LINGUAS in LINGUAS/configure.ac/.in for UI and
DOC_LINGUAS "
+ "in Makefile.am for DOC)"
+ )
)
red_filter = models.TextField(
blank=True,
- help_text=f"pogrep filter to strip po file from unprioritized strings (format: location|string,"
- f" “-” for no filter)"
+ help_text="pogrep filter to strip po file from unprioritized strings (format: location|string,"
+ " “-” for no filter)"
)
# Allow to specify the branches to which this domain applies
branch_from = models.ForeignKey(Branch, null=True, blank=True, on_delete=models.PROTECT,
related_name='+')
@@ -804,10 +822,10 @@ class Domain(models.Model):
return "", (
("error",
gettext_noop("Error regenerating POT file for
%(file)s:\n<pre>%(cmd)s\n%(output)s</pre>") % {
- 'file': self.potbase(),
- 'cmd': ' '.join(pot_command) if isinstance(pot_command, list) else pot_command,
- 'output': force_str(errs)
- }),
+ 'file': self.potbase(),
+ 'cmd': ' '.join(pot_command) if isinstance(pot_command, list) else pot_command,
+ 'output': force_str(errs)
+ }),
)
if not potfile.exists():
# Try to get POT file from command output, with path relative to checkout root
@@ -831,13 +849,14 @@ class Domain(models.Model):
return potfile, ()
def get_xgettext_command(self, branch):
- pot_command = ['xgettext',
- '--files-from', 'POTFILES.in',
- '--directory', str(branch.co_path),
- '--from-code', 'utf-8',
- '--add-comments',
- '--output', '%s.pot' % self.potbase(),
- ]
+ pot_command = [
+ 'xgettext',
+ '--files-from', 'POTFILES.in',
+ '--directory', str(branch.co_path),
+ '--from-code', 'utf-8',
+ '--add-comments',
+ '--output', '%s.pot' % self.potbase(),
+ ]
if not os.path.exists(utils.ITS_DATA_DIR):
utils.collect_its_data()
env = {'GETTEXTDATADIRS': os.path.dirname(utils.ITS_DATA_DIR)}
@@ -941,7 +960,7 @@ class Release(models.Model):
string_frozen = models.BooleanField(default=False)
status = models.CharField(max_length=12, choices=RELEASE_STATUS_CHOICES)
# weight is used to sort releases, higher on top, below 0 in archives
- weight = models.IntegerField(default=0)
+ weight = models.IntegerField(default=0)
branches = models.ManyToManyField(Branch, through='Category', related_name='releases')
class Meta:
@@ -983,12 +1002,16 @@ class Release(models.Model):
totals = [0] * len(releases)
lang_dict = dict((lang.locale, lang) for lang in Language.objects.all())
for rel in releases:
- query = Statistics.objects.filter(domain__dtype=dtype, branch__releases=rel
- ).exclude(domain__in=rel.excluded_domains
- ).values('language__locale'
- ).annotate(trans=models.Sum('full_po__translated'), fuzzy=models.Sum('full_po__fuzzy'),
- untrans=models.Sum('full_po__untranslated')
- ).order_by('language__name')
+ query = Statistics.objects.filter(
+ domain__dtype=dtype, branch__releases=rel
+ ).exclude(
+ domain__in=rel.excluded_domains
+ ).values(
+ 'language__locale'
+ ).annotate(
+ trans=models.Sum('full_po__translated'), fuzzy=models.Sum('full_po__fuzzy'),
+ untrans=models.Sum('full_po__untranslated')
+ ).order_by('language__name')
for line in query:
locale = line['language__locale']
if locale and locale not in stats:
@@ -1042,8 +1065,9 @@ class Release(models.Model):
).values('branch_id', 'domain_id', 'language__locale', 'part_po__translated',
'part_po__fuzzy', 'part_po__untranslated')
stats_d = {
- "%d-%d-%s" % (st['branch_id'], st['domain_id'], st['language__locale']
- ): (st['part_po__translated'] or 0) + (st['part_po__fuzzy'] or 0) + (st['part_po__untranslated']
or 0)
+ f"{st['branch_id']}-{st['domain_id']}-{st['language__locale']}": sum([
+ st['part_po__translated'] or 0, st['part_po__fuzzy'] or 0, st['part_po__untranslated'] or 0
+ ])
for st in all_ui_stats
}
for lang in Language.objects.all():
@@ -1087,26 +1111,33 @@ class Release(models.Model):
total_doc, total_ui = self.total_strings()
total_ui_part = self.total_part_for_lang(lang)
- query = Statistics.objects.filter(language=lang, branch__releases=self
- ).exclude(domain__in=self.excluded_domains
- ).values('domain__dtype'
- ).annotate(
- trans=Coalesce(models.Sum('full_po__translated'), models.Value(0)),
- fuzzy=Coalesce(models.Sum('full_po__fuzzy'), models.Value(0)),
- trans_p=Coalesce(models.Sum('part_po__translated'), models.Value(0)),
- fuzzy_p=Coalesce(models.Sum('part_po__fuzzy'), models.Value(0))
- )
- stats = {'id': self.id, 'name': self.name, 'description': _(self.description),
- 'ui': {'translated': 0, 'fuzzy': 0, 'total': total_ui,
- 'translated_perc': 0, 'fuzzy_perc': 0, 'untranslated_perc': 0,
- },
- 'ui_part': {'translated': 0, 'fuzzy': 0, 'total': total_ui_part,
- 'translated_perc': 0, 'fuzzy_perc': 0, 'untranslated_perc': 0,
- },
- 'doc': {'translated': 0, 'fuzzy': 0, 'total': total_doc,
- 'translated_perc': 0, 'fuzzy_perc': 0, 'untranslated_perc': 0
- },
- }
+ query = Statistics.objects.filter(
+ language=lang, branch__releases=self
+ ).exclude(
+ domain__in=self.excluded_domains
+ ).values(
+ 'domain__dtype'
+ ).annotate(
+ trans=Coalesce(models.Sum('full_po__translated'), models.Value(0)),
+ fuzzy=Coalesce(models.Sum('full_po__fuzzy'), models.Value(0)),
+ trans_p=Coalesce(models.Sum('part_po__translated'), models.Value(0)),
+ fuzzy_p=Coalesce(models.Sum('part_po__fuzzy'), models.Value(0))
+ )
+ stats = {
+ 'id': self.id, 'name': self.name, 'description': _(self.description),
+ 'ui': {
+ 'translated': 0, 'fuzzy': 0, 'total': total_ui,
+ 'translated_perc': 0, 'fuzzy_perc': 0, 'untranslated_perc': 0,
+ },
+ 'ui_part': {
+ 'translated': 0, 'fuzzy': 0, 'total': total_ui_part,
+ 'translated_perc': 0, 'fuzzy_perc': 0, 'untranslated_perc': 0,
+ },
+ 'doc': {
+ 'translated': 0, 'fuzzy': 0, 'total': total_doc,
+ 'translated_perc': 0, 'fuzzy_perc': 0, 'untranslated_perc': 0
+ },
+ }
for res in query:
if res['domain__dtype'] == 'ui':
stats['ui']['translated'] = res['trans']
@@ -1119,34 +1150,37 @@ class Release(models.Model):
stats['ui']['untranslated'] = total_ui - (stats['ui']['translated'] + stats['ui']['fuzzy'])
stats['ui_part']['untranslated'] = total_ui_part - (stats['ui_part']['translated'] +
stats['ui_part']['fuzzy'])
if total_ui > 0:
- stats['ui']['translated_perc'] = int(100*stats['ui']['translated']/total_ui)
- stats['ui']['fuzzy_perc'] = int(100*stats['ui']['fuzzy']/total_ui)
- stats['ui']['untranslated_perc'] = int(100*stats['ui']['untranslated']/total_ui)
+ stats['ui']['translated_perc'] = int(100 * stats['ui']['translated'] / total_ui)
+ stats['ui']['fuzzy_perc'] = int(100 * stats['ui']['fuzzy'] / total_ui)
+ stats['ui']['untranslated_perc'] = int(100 * stats['ui']['untranslated'] / total_ui)
if total_ui_part > 0:
- stats['ui_part']['translated_perc'] = int(100*stats['ui_part']['translated']/total_ui_part)
- stats['ui_part']['fuzzy_perc'] = int(100*stats['ui_part']['fuzzy']/total_ui_part)
- stats['ui_part']['untranslated_perc'] = int(100*stats['ui_part']['untranslated']/total_ui_part)
+ stats['ui_part']['translated_perc'] = int(100 * stats['ui_part']['translated'] / total_ui_part)
+ stats['ui_part']['fuzzy_perc'] = int(100 * stats['ui_part']['fuzzy'] / total_ui_part)
+ stats['ui_part']['untranslated_perc'] = int(100 * stats['ui_part']['untranslated'] /
total_ui_part)
stats['doc']['untranslated'] = total_doc - (stats['doc']['translated'] + stats['doc']['fuzzy'])
if total_doc > 0:
- stats['doc']['translated_perc'] = int(100*stats['doc']['translated']/total_doc)
- stats['doc']['fuzzy_perc'] = int(100*stats['doc']['fuzzy']/total_doc)
- stats['doc']['untranslated_perc'] = int(100*stats['doc']['untranslated']/total_doc)
+ stats['doc']['translated_perc'] = int(100 * stats['doc']['translated'] / total_doc)
+ stats['doc']['fuzzy_perc'] = int(100 * stats['doc']['fuzzy'] / total_doc)
+ stats['doc']['untranslated_perc'] = int(100 * stats['doc']['untranslated'] / total_doc)
return stats
def get_global_stats(self):
""" Get statistics for all languages in a release, grouped by language
Returns a sorted list: (language name and locale, ui, ui-part and doc stats dictionaries) """
- query = Statistics.objects.filter(language__isnull=False, branch__releases=self
- ).exclude(domain__in=self.excluded_domains
- ).values('domain__dtype', 'language'
- ).annotate(
- trans=Coalesce(models.Sum('full_po__translated'), models.Value(0)),
- fuzzy=Coalesce(models.Sum('full_po__fuzzy'), models.Value(0)),
- trans_p=Coalesce(models.Sum('part_po__translated'), models.Value(0)),
- fuzzy_p=Coalesce(models.Sum('part_po__fuzzy'), models.Value(0)),
- locale=models.Min('language__locale'), lang_name=models.Min('language__name')
- ).order_by('domain__dtype', 'trans')
+ query = Statistics.objects.filter(
+ language__isnull=False, branch__releases=self
+ ).exclude(
+ domain__in=self.excluded_domains
+ ).values(
+ 'domain__dtype', 'language'
+ ).annotate(
+ trans=Coalesce(models.Sum('full_po__translated'), models.Value(0)),
+ fuzzy=Coalesce(models.Sum('full_po__fuzzy'), models.Value(0)),
+ trans_p=Coalesce(models.Sum('part_po__translated'), models.Value(0)),
+ fuzzy_p=Coalesce(models.Sum('part_po__fuzzy'), models.Value(0)),
+ locale=models.Min('language__locale'), lang_name=models.Min('language__name')
+ ).order_by('domain__dtype', 'trans')
stats = {}
total_docstrings, total_uistrings = self.total_strings()
total_uistrings_part = self.total_part_for_all_langs()
@@ -1156,12 +1190,18 @@ class Release(models.Model):
# Initialize stats dict
stats[locale] = {
'lang_name': row['lang_name'], 'lang_locale': locale,
- 'ui' : {'translated': 0, 'fuzzy': 0, 'untranslated': total_uistrings,
- 'translated_perc': 0, 'fuzzy_perc': 0, 'untranslated_perc': 100},
- 'ui_part' : {'translated': 0, 'fuzzy': 0, 'untranslated': total_uistrings_part[locale],
- 'translated_perc': 0, 'fuzzy_perc': 0, 'untranslated_perc': 100},
- 'doc': {'translated': 0, 'fuzzy': 0, 'untranslated': total_docstrings,
- 'translated_perc': 0, 'fuzzy_perc': 0, 'untranslated_perc': 100,},
+ 'ui': {
+ 'translated': 0, 'fuzzy': 0, 'untranslated': total_uistrings,
+ 'translated_perc': 0, 'fuzzy_perc': 0, 'untranslated_perc': 100
+ },
+ 'ui_part': {
+ 'translated': 0, 'fuzzy': 0, 'untranslated': total_uistrings_part[locale],
+ 'translated_perc': 0, 'fuzzy_perc': 0, 'untranslated_perc': 100
+ },
+ 'doc': {
+ 'translated': 0, 'fuzzy': 0, 'untranslated': total_docstrings,
+ 'translated_perc': 0, 'fuzzy_perc': 0, 'untranslated_perc': 100
+ },
}
if row['domain__dtype'] == 'doc':
stats[locale]['doc']['translated'] = row['trans']
@@ -1180,7 +1220,7 @@ class Release(models.Model):
stats[locale]['ui_part']['translated'] = row['trans_p']
stats[locale]['ui_part']['fuzzy'] = row['fuzzy_p']
stats[locale]['ui_part']['untranslated'] = total_uistrings_part[locale] - (
- row['trans_p'] + row['fuzzy_p']
+ row['trans_p'] + row['fuzzy_p']
)
if total_uistrings > 0:
stats[locale]['ui']['translated_perc'] = int(100 * row['trans'] / total_uistrings)
@@ -1207,9 +1247,10 @@ class Release(models.Model):
""" Get statistics for a specific language, producing the stats data structure
Used for displaying the language-release template """
- stats = {'doc': Statistics.get_lang_stats_by_type(lang, 'doc', self),
- 'ui': Statistics.get_lang_stats_by_type(lang, 'ui', self),
- }
+ stats = {
+ 'doc': Statistics.get_lang_stats_by_type(lang, 'doc', self),
+ 'ui': Statistics.get_lang_stats_by_type(lang, 'ui', self),
+ }
return stats
def get_lang_files(self, lang, dtype):
@@ -1263,16 +1304,16 @@ class Category(models.Model):
class PoFile(models.Model):
# File type fields of Django may not be flexible enough for our use case
- path = models.CharField(max_length=255, blank=True)
- updated = models.DateTimeField(auto_now_add=True)
- translated = models.IntegerField(default=0)
- fuzzy = models.IntegerField(default=0)
+ path = models.CharField(max_length=255, blank=True)
+ updated = models.DateTimeField(auto_now_add=True)
+ translated = models.IntegerField(default=0)
+ fuzzy = models.IntegerField(default=0)
untranslated = models.IntegerField(default=0)
# List of figure dict
figures = models.JSONField(blank=True, null=True)
# words statistics
- translated_words = models.IntegerField(default=0)
- fuzzy_words = models.IntegerField(default=0)
+ translated_words = models.IntegerField(default=0)
+ fuzzy_words = models.IntegerField(default=0)
untranslated_words = models.IntegerField(default=0)
class Meta:
@@ -1343,12 +1384,12 @@ class PoFile(models.Model):
def update_stats(self):
stats = utils.po_file_stats(Path(self.full_path))
- self.translated = stats['translated']
- self.fuzzy = stats['fuzzy']
+ self.translated = stats['translated']
+ self.fuzzy = stats['fuzzy']
self.untranslated = stats['untranslated']
- self.translated_words = stats['translated_words']
- self.fuzzy_words = stats['fuzzy_words']
+ self.translated_words = stats['translated_words']
+ self.fuzzy_words = stats['fuzzy_words']
self.untranslated_words = stats['untranslated_words']
self.save()
@@ -1370,7 +1411,7 @@ class Statistics(models.Model):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- self.partial_po = False # True if part of a multiple po module
+ self.partial_po = False # True if part of a multiple po module
self.info_list = []
def __str__(self):
@@ -1379,22 +1420,22 @@ class Statistics(models.Model):
self.branch.name, self.get_lang())
def translated(self, scope='full'):
- return getattr(scope=='part' and self.part_po or self.full_po, 'translated', 0)
+ return getattr(self.part_po if scope == 'part' else self.full_po, 'translated', 0)
def translated_words(self, scope='full'):
- return getattr(scope=='part' and self.part_po or self.full_po, 'translated_words', 0)
+ return getattr(self.part_po if scope == 'part' else self.full_po, 'translated_words', 0)
def fuzzy(self, scope='full'):
- return getattr(scope=='part' and self.part_po or self.full_po, 'fuzzy', 0)
+ return getattr(self.part_po if scope == 'part' else self.full_po, 'fuzzy', 0)
def fuzzy_words(self, scope='full'):
- return getattr(scope=='part' and self.part_po or self.full_po, 'fuzzy_words', 0)
+ return getattr(self.part_po if scope == 'part' else self.full_po, 'fuzzy_words', 0)
def untranslated(self, scope='full'):
- return getattr(scope=='part' and self.part_po or self.full_po, 'untranslated', 0)
+ return getattr(self.part_po if scope == 'part' else self.full_po, 'untranslated', 0)
def untranslated_words(self, scope='full'):
- return getattr(scope=='part' and self.part_po or self.full_po, 'untranslated_words', 0)
+ return getattr(self.part_po if scope == 'part' else self.full_po, 'untranslated_words', 0)
def is_fake(self):
return False
@@ -1485,17 +1526,19 @@ class Statistics(models.Model):
# Return stat table header: 'POT file (n messages) - updated on ??-??-???? tz'
msg_text = ngettext("%(count)s message", "%(count)s messages", pot_size) % {'count': pot_size}
upd_text = _("updated on %(date)s") % {
- # Date format syntax is similar to PHP http://www.php.net/date
- 'date': dateformat.format(self.full_po.updated, _("Y-m-d g:i a O"))
- }
+ # Date format syntax is similar to PHP http://www.php.net/date
+ 'date': dateformat.format(self.full_po.updated, _("Y-m-d g:i a O"))
+ }
words_text = ngettext("%(count)s word", "%(count)s words", pot_words_size) % {'count':
pot_words_size}
if fig_count:
fig_text = ngettext("%(count)s figure", "%(count)s figures", fig_count) % {'count': fig_count}
- text = _("POT file (%(messages)s — %(words)s, %(figures)s) — %(updated)s") % \
- {'messages': msg_text, 'figures': fig_text, 'updated': upd_text, 'words':
words_text }
+ text = _("POT file (%(messages)s — %(words)s, %(figures)s) — %(updated)s") % {
+ 'messages': msg_text, 'figures': fig_text, 'updated': upd_text, 'words': words_text
+ }
else:
- text = _("POT file (%(messages)s — %(words)s) — %(updated)s") % \
- {'messages': msg_text, 'updated': upd_text, 'words': words_text }
+ text = _("POT file (%(messages)s — %(words)s) — %(updated)s") % {
+ 'messages': msg_text, 'updated': upd_text, 'words': words_text
+ }
return text
def has_figures(self):
@@ -1525,16 +1568,18 @@ class Statistics(models.Model):
return figures
def fig_stats(self):
- stats = {'fuzzy':0, 'translated':0, 'total':0, 'prc':0}
+ stats = {'fuzzy': 0, 'translated': 0, 'total': 0, 'prc': 0}
if self.full_po and self.full_po.figures:
for fig in self.full_po.figures:
stats['total'] += 1
- if fig.get('fuzzy', 0): stats['fuzzy'] += 1
+ if fig.get('fuzzy', 0):
+ stats['fuzzy'] += 1
else:
- if fig.get('translated', 0): stats['translated'] += 1
+ if fig.get('translated', 0):
+ stats['translated'] += 1
stats['untranslated'] = stats['total'] - (stats['translated'] + stats['fuzzy'])
if stats['total'] > 0:
- stats['prc'] = 100*stats['translated']/stats['total']
+ stats['prc'] = 100 * stats['translated'] / stats['total']
return stats
def vcs_web_path(self):
@@ -1559,8 +1604,9 @@ class Statistics(models.Model):
subdir = ""
if self.domain.dtype == "doc":
subdir = "docs/"
- return utils.url_join("/POT/", "%s.%s" % (self.module_name, self.branch.name_escaped),
- subdir, self.filename(potfile, reduced))
+ return utils.url_join(
+ "/POT/", "%s.%s" % (self.module_name, self.branch.name_escaped), subdir, self.filename(potfile,
reduced)
+ )
def pot_url(self):
return self.po_url(potfile=True)
@@ -1719,9 +1765,11 @@ class Statistics(models.Model):
dtype = dtype[:-5]
scope = "part"
- stats = {'dtype':dtype, 'totaltrans':0, 'totalfuzzy':0, 'totaluntrans':0,
- 'totaltransperc': 0, 'totalfuzzyperc': 0, 'totaluntransperc': 0,
- 'categs': OrderedDict(), 'all_errors': []}
+ stats = {
+ 'dtype': dtype, 'totaltrans': 0, 'totalfuzzy': 0, 'totaluntrans': 0,
+ 'totaltransperc': 0, 'totalfuzzyperc': 0, 'totaluntransperc': 0,
+ 'categs': OrderedDict(), 'all_errors': [],
+ }
# Sorted by module to allow grouping ('fake' stats)
pot_stats = Statistics.objects.select_related('domain', 'branch__module', 'full_po', 'part_po')
if release:
@@ -1747,7 +1795,7 @@ class Statistics(models.Model):
infos_dict = Information.get_info_dict(lang)
# Prepare State objects in a dict (with "branch_id-domain_id" as key), to save database queries later
- vt_states = State.objects.select_related('branch','domain')
+ vt_states = State.objects.select_related('branch', 'domain')
if release:
vt_states = vt_states.filter(language=lang, branch__releases=release, domain__dtype=dtype)
else:
@@ -1801,7 +1849,7 @@ class Statistics(models.Model):
stats['categs'][categ_key]['catuntrans'] += stat.untranslated(scope)
if module not in stats['categs'][categ_key]['modules']:
# first element is a placeholder for a fake stat
- stats['categs'][categ_key]['modules'][module] = {branchname:[[' fake', None], [domname,
stat]]}
+ stats['categs'][categ_key]['modules'][module] = {branchname: [[' fake', None], [domname,
stat]]}
elif branchname not in stats['categs'][categ_key]['modules'][module]:
# first element is a placeholder for a fake stat
stats['categs'][categ_key]['modules'][module][branchname] = [[' fake', None], [domname,
stat]]
@@ -1821,14 +1869,14 @@ class Statistics(models.Model):
# Compute percentages and sorting
stats['total'] = stats['totaltrans'] + stats['totalfuzzy'] + stats['totaluntrans']
if stats['total'] > 0:
- stats['totaltransperc'] = int(100*stats['totaltrans']/stats['total'])
- stats['totalfuzzyperc'] = int(100*stats['totalfuzzy']/stats['total'])
- stats['totaluntransperc'] = int(100*stats['totaluntrans']/stats['total'])
+ stats['totaltransperc'] = int(100 * stats['totaltrans'] / stats['total'])
+ stats['totalfuzzyperc'] = int(100 * stats['totalfuzzy'] / stats['total'])
+ stats['totaluntransperc'] = int(100 * stats['totaluntrans'] / stats['total'])
stats['categs'] = OrderedDict(sorted(stats['categs'].items(), key=lambda t: t[0]))
for categ in stats['categs'].values():
categ['cattotal'] = categ['cattrans'] + categ['catfuzzy'] + categ['catuntrans']
if categ['cattotal'] > 0:
- categ['cattransperc'] = int(100*categ['cattrans']/categ['cattotal'])
+ categ['cattransperc'] = int(100 * categ['cattrans'] / categ['cattotal'])
# Sort domains
for mod in categ['modules'].values():
for doms in mod.values():
@@ -1841,6 +1889,7 @@ class Statistics(models.Model):
class FakeLangStatistics:
""" Statistics class for a non existing lang stats """
is_fake = True
+
def __init__(self, pot_stat, lang):
self.stat = pot_stat
self.language = lang
@@ -1871,16 +1920,17 @@ class FakeLangStatistics:
class FakeSummaryStatistics:
""" Statistics class that sums up an entire module stats """
is_fake = True
+
def __init__(self, module, branch, dtype):
self.module = module
self.branch = branch
self.domain = module.domain_set.filter(dtype=dtype)[0]
self._translated = 0
- self._fuzzy = 0
+ self._fuzzy = 0
self._untranslated = 0
self.partial_po = False
self._translated_words = 0
- self._fuzzy_words = 0
+ self._fuzzy_words = 0
self._untranslated_words = 0
def translated(self, scope=None):
@@ -1902,8 +1952,8 @@ class FakeSummaryStatistics:
return self._untranslated_words
def trans(self, stat):
- self._translated += stat.translated()
- self._fuzzy += stat.fuzzy()
+ self._translated += stat.translated()
+ self._fuzzy += stat.fuzzy()
self._untranslated += stat.untranslated()
stat.partial_po = True
@@ -1923,7 +1973,7 @@ class FakeSummaryStatistics:
def un_percentage(self, scope='full'):
if self.pot_size() == 0:
return 0
- return int(100*self._untranslated/self.pot_size())
+ return int(100 * self._untranslated / self.pot_size())
class StatisticsArchived(models.Model):
@@ -1981,7 +2031,7 @@ class Information(models.Model):
def get_description(self):
text = self.description
- matches = re.findall('###([^#]*)###',text)
+ matches = re.findall('###([^#]*)###', text)
if matches:
text = re.sub('###([^#]*)###', '%s', text)
@@ -1989,14 +2039,14 @@ class Information(models.Model):
# FIXME: if multiple substitutions, works only if order of %s is unchanged in translated string
for match in matches:
- text = text.replace('%s',match,1)
+ text = text.replace('%s', match, 1)
return text
def report_bug_url(self):
link = self.statistics.branch.module.get_bugs_enter_url()
link += "&short_desc=%(short)s&content=%(short)s&comment=%(long)s" % {
'short': "Error regenerating POT file",
- 'long' : utils.ellipsize(utils.stripHTML(self.get_description()), 1600),
+ 'long': utils.ellipsize(utils.stripHTML(self.get_description()), 1600),
}
return link
diff --git a/stats/potdiff.py b/stats/potdiff.py
index 613f8a8f..63875b92 100644
--- a/stats/potdiff.py
+++ b/stats/potdiff.py
@@ -4,6 +4,7 @@
USE_DIFFLIB = 0
+
def diff(pota, potb):
"""Returns a list of differing lines between two text files."""
with open(pota, "r", encoding="utf-8") as f1:
@@ -24,14 +25,15 @@ def diff(pota, potb):
result_all = []
while i < len(res1) and j < len(res2):
if res1[i] == res2[j]:
- i+=1; j+=1
+ i += 1
+ j += 1
elif res1[i] < res2[j]:
result_all.append("- " + res1[i])
- i+=1
+ i += 1
elif res1[i] > res2[j]:
result_all.append("+ " + res2[j])
result_add_only.append("+ " + res2[j])
- j+=1
+ j += 1
return result_all, result_add_only
else:
import difflib
@@ -40,9 +42,8 @@ def diff(pota, potb):
onlydiffs = []
for line in result:
- if line[0]!=" ":
+ if line[0] != " ":
onlydiffs.append(line)
- #print line
return onlydiffs
@@ -54,10 +55,13 @@ def _parse_contents(contents):
[msgctxt::]msgid[/msgid_plural]"""
- if len(contents) and contents[-1] != "\n": contents += "\n"
+ if len(contents) and contents[-1] != "\n":
+ contents += "\n"
# state machine for parsing PO files
- msgid = ""; msgctxt = ""; plural = "";
+ msgid = ""
+ msgctxt = ""
+ plural = ""
in_msgid = in_msgstr = in_msgctxt = in_msgid_plural = 0
result = []
@@ -71,9 +75,11 @@ def _parse_contents(contents):
if in_msgstr and msgid != "":
onemsg = ""
- if msgctxt: onemsg += ('"' + msgctxt + '"::')
+ if msgctxt:
+ onemsg += ('"' + msgctxt + '"::')
onemsg += ('"' + msgid + '"')
- if plural: onemsg += ('/"' + plural + '"')
+ if plural:
+ onemsg += ('/"' + plural + '"')
result.append(onemsg)
@@ -81,9 +87,13 @@ def _parse_contents(contents):
# Ignore PO header
pass
- msgid = ""; msgctxt = ""
- in_msgid = 0; in_msgstr = 0; in_msgctxt = 0
- plural = ""; in_msgid_plural = 0
+ msgid = ""
+ msgctxt = ""
+ in_msgid = 0
+ in_msgstr = 0
+ in_msgctxt = 0
+ plural = ""
+ in_msgid_plural = 0
elif line[0] == "\"" and line[-1] == "\"":
if in_msgid:
@@ -124,6 +134,8 @@ def _parse_contents(contents):
pass
return result
+
if __name__ == "__main__":
import sys
+
print("\n".join(diff(sys.argv[1], sys.argv[2])))
diff --git a/stats/repos.py b/stats/repos.py
index 94217859..f7fc2bf9 100644
--- a/stats/repos.py
+++ b/stats/repos.py
@@ -152,8 +152,8 @@ class CVSRepo(RepoBase):
def init_checkout(self):
run_shell_command([
'cvs', '-d%s' % self.branch.module.vcs_root, '-z4', 'co',
- '-d%s' % self.branch.module.name + "." + self.branch.name,
- '-r%s' % self.branch.name, self.module.name
+ '-d%s' % self.branch.module.name + "." + self.branch.name,
+ '-r%s' % self.branch.name, self.module.name
], cwd=settings.SCRATCHDIR / self.branch.module.vcs_type)
def update(self):
@@ -166,7 +166,7 @@ class CVSRepo(RepoBase):
class MercurialRepo(RepoBase):
def exists(self):
- return self.branch.id != None and os.access(str(self.branch.co_path), os.X_OK | os.W_OK)
+ return self.branch.id is not None and os.access(str(self.branch.co_path), os.X_OK | os.W_OK)
def init_checkout(self):
base_path = self.branch.co_path
diff --git a/stats/templatetags/stats_extras.py b/stats/templatetags/stats_extras.py
index 9e884088..9e07044a 100644
--- a/stats/templatetags/stats_extras.py
+++ b/stats/templatetags/stats_extras.py
@@ -223,7 +223,9 @@ def markdown(value, arg=''):
import markdown
except ImportError:
if settings.DEBUG:
- raise template.TemplateSyntaxError("Error in 'markdown' filter: The Python markdown library
isn't installed.")
+ raise template.TemplateSyntaxError(
+ "Error in 'markdown' filter: The Python markdown library isn't installed."
+ )
return value
else:
extensions = [e for e in arg.split(",") if e]
diff --git a/stats/tests/fixture_factory.py b/stats/tests/fixture_factory.py
index 80ce1ddb..0827de38 100644
--- a/stats/tests/fixture_factory.py
+++ b/stats/tests/fixture_factory.py
@@ -7,79 +7,100 @@ from languages.models import Language
from teams.models import Team, Role
from people.models import Person
from stats.models import (
- Module, Domain, Branch, Release, Category, CategoryName, Statistics,
- Information, PoFile,
+ Branch, Category, CategoryName, Domain, Information, Module, PoFile,
+ Release, Statistics,
)
class FixtureFactory(TestCase):
- """ Fake Test case to create fixture.
- To create a JSON fixture, run:
- python manage.py test stats.tests.FixtureFactory.make_fixture
+ """
+ Fake Test case to create fixture.
+ To create a JSON fixture, run:
+ python manage.py test stats.tests.FixtureFactory.make_fixture
"""
def make_fixture(self):
# Creating models: Teams
- t1 = Team.objects.create(name='fr', description="French",
+ t1 = Team.objects.create(
+ name='fr',
+ description="French",
webpage_url="http://gnomefr.traduc.org/",
mailing_list="gnomefr traduc org",
mailing_list_subscribe="http://www.traduc.org/mailman/listinfo/gnomefr",
- presentation="Here can come any custom description for a team")
- t2 = Team.objects.create(name='it', description="Italian",
- webpage_url="http://www.it.gnome.org/")
+ presentation="Here can come any custom description for a team",
+ )
+ t2 = Team.objects.create(name='it', description="Italian", webpage_url="http://www.it.gnome.org/")
# Creating models: Languages
Language.objects.create(name='en', locale='en', plurals="nplurals=2; plural=(n != 1)")
- l_fr = Language.objects.create(name='French', locale='fr', plurals="nplurals=2; plural=(n > 1)",
- team=t1)
- l_it = Language.objects.create(name='Italian', locale='it', plurals="nplurals=2; plural=(n != 1)",
- team=t2)
+ l_fr = Language.objects.create(name='French', locale='fr', plurals="nplurals=2; plural=(n > 1)",
team=t1)
+ l_it = Language.objects.create(name='Italian', locale='it', plurals="nplurals=2; plural=(n != 1)",
team=t2)
# Lang with no team and no stats
Language.objects.create(name='Bemba', locale='bem')
# Creating models: Persons/Roles
- p0 = Person.objects.create(username='admin1') # Fake person (deleted below), just not to use pk=1
for user
- p1 = Person.objects.create(first_name='Robert', last_name='Translator',
- email='bob example org', username='bob', irc_nick='bobby',
- svn_account='bob1')
+ p0 = Person.objects.create(username='admin1') # Fake person (deleted below), just not to use pk=1
for user
+ p1 = Person.objects.create(
+ first_name='Robert',
+ last_name='Translator',
+ email='bob example org',
+ username='bob',
+ irc_nick='bobby',
+ svn_account='bob1',
+ )
p1.set_password('bob')
Role.objects.create(team=t1, person=p1, role='translator')
- p2 = Person.objects.create(first_name='John', last_name='Coordinator',
- email='coord example org', username='coord', svn_account='coord_fr')
+ p2 = Person.objects.create(
+ first_name='John',
+ last_name='Coordinator',
+ email='coord example org',
+ username='coord',
+ svn_account='coord_fr',
+ )
p2.set_password('coord')
Role.objects.create(team=t1, person=p2, role='coordinator')
- p3 = Person.objects.create(first_name='Alessio', last_name='Reviewer',
- email='alessio example org', username='alessio')
+ p3 = Person.objects.create(
+ first_name='Alessio', last_name='Reviewer', email='alessio example org', username='alessio'
+ )
p1.set_password('alessio')
Role.objects.create(team=t2, person=p3, role='reviewer')
p0.delete()
# Creating models: Modules
gnome_hello = Module.objects.create(
- name="gnome-hello", vcs_type="git",
+ name="gnome-hello",
+ vcs_type="git",
vcs_root="https://gitlab.gnome.org/GNOME/gnome-hello.git",
vcs_web="https://gitlab.gnome.org/GNOME/gnome-hello/",
bugs_base="https://gitlab.gnome.org/GNOME/gnome-hello/issues",
)
zenity = Module.objects.create(
- name="zenity", vcs_type="git",
+ name="zenity",
+ vcs_type="git",
vcs_root="https://gitlab.gnome.org/GNOME/zenity.git",
vcs_web="https://gitlab.gnome.org/GNOME/zenity/",
bugs_base="https://gitlab.gnome.org/GNOME/zenity/issues",
)
- s_m_i = Module.objects.create(name="shared-mime-info", vcs_type="git",
+ s_m_i = Module.objects.create(
+ name="shared-mime-info",
+ vcs_type="git",
description="Shared MIME Info",
vcs_root="https://gitlab.freedesktop.org/xdg/shared-mime-info.git",
vcs_web="https://gitlab.freedesktop.org/xdg/shared-mime-info",
bugs_base="https://gitlab.freedesktop.org/xdg/shared-mime-info/issues",
- comment="This is not a GNOME-specific module. Please submit your translation " \
- "through the <a href=\"https://www.transifex.com/freedesktop/shared-mime-info/\">Transifex
platform</a>.")
+ comment="This is not a GNOME-specific module. Please submit your translation "
+ "through the <a href=\"https://www.transifex.com/freedesktop/shared-mime-info/\">Transifex
platform</a>.",
+ )
# Creating models: Domains
dom = {}
for mod in (gnome_hello, zenity, s_m_i):
- dom['%s-ui' % mod.name] = Domain.objects.create(module=mod, name='po', description='UI
Translations', dtype='ui', layout='po/{lang}.po')
- dom['%s-doc' % mod.name] = Domain.objects.create(module=mod, name='help', description='User
Guide', dtype='doc', layout='help/{lang}/{lang}.po')
+ dom['%s-ui' % mod.name] = Domain.objects.create(
+ module=mod, name='po', description='UI Translations', dtype='ui', layout='po/{lang}.po'
+ )
+ dom['%s-doc' % mod.name] = Domain.objects.create(
+ module=mod, name='help', description='User Guide', dtype='doc',
layout='help/{lang}/{lang}.po'
+ )
# Creating models: Branches
Branch.checkout_on_creation = False
@@ -93,15 +114,15 @@ class FixtureFactory(TestCase):
b4.save(update_statistics=False)
# Creating models: Releases/Categories
- rel1 = Release.objects.create(name='gnome-3-8', status='official',
- description='GNOME 3.8 (stable)',
- string_frozen=True)
- rel2 = Release.objects.create(name='gnome-dev', status='official',
- description='GNOME in Development',
- string_frozen=False)
- rel3 = Release.objects.create(name='freedesktop-org', status='xternal',
- description='freedesktop.org (non-GNOME)',
- string_frozen=False)
+ rel1 = Release.objects.create(
+ name='gnome-3-8', status='official', description='GNOME 3.8 (stable)', string_frozen=True
+ )
+ rel2 = Release.objects.create(
+ name='gnome-dev', status='official', description='GNOME in Development', string_frozen=False
+ )
+ rel3 = Release.objects.create(
+ name='freedesktop-org', status='xternal', description='freedesktop.org (non-GNOME)',
string_frozen=False
+ )
cat_name = CategoryName.objects.create(name='Desktop')
Category.objects.create(release=rel1, branch=b1, name=cat_name)
@@ -112,28 +133,64 @@ class FixtureFactory(TestCase):
# Creating models: Statistics
# gnome-hello ui, gnome-hello doc (POT, fr, it)
pofile = PoFile.objects.create(untranslated=47)
- Statistics.objects.create(branch=b1, domain=dom['gnome-hello-ui'], language=None, full_po=pofile,
part_po=pofile)
+ Statistics.objects.create(
+ branch=b1, domain=dom['gnome-hello-ui'], language=None, full_po=pofile, part_po=pofile
+ )
pofile = PoFile.objects.create(translated=47)
- Statistics.objects.create(branch=b1, domain=dom['gnome-hello-ui'], language=l_fr, full_po=pofile,
part_po=pofile)
+ Statistics.objects.create(
+ branch=b1, domain=dom['gnome-hello-ui'], language=l_fr, full_po=pofile, part_po=pofile
+ )
pofile = PoFile.objects.create(translated=30, fuzzy=10, untranslated=7)
- Statistics.objects.create(branch=b1, domain=dom['gnome-hello-ui'], language=l_it, full_po=pofile,
part_po=pofile)
- pofile = PoFile.objects.create(untranslated=20,
- figures = [{"path": "figures/gnome-hello-new.png", "hash": "8a1fcc6f46a22a1f500cfef9ca51b481"},
{"path": "figures/gnome-hello-logo.png", "hash": "1ae47b7a7c4fbeb1f6bb72c0cf18d389"}])
- Statistics.objects.create(branch=b1, domain=dom['gnome-hello-doc'], language=None, full_po=pofile,
part_po=pofile)
- pofile = PoFile.objects.create(translated=20,
- figures = [{"translated": False, "path": "figures/gnome-hello-new.png", "hash":
"8a1fcc6f46a22a1f500cfef9ca51b481", "fuzzy": True}, {"translated": False, "path":
"figures/gnome-hello-logo.png", "hash": "1ae47b7a7c4fbeb1f6bb72c0cf18d389", "fuzzy": False}])
- Statistics.objects.create(branch=b1, domain=dom['gnome-hello-doc'], language=l_fr, full_po=pofile,
part_po=pofile)
+ Statistics.objects.create(
+ branch=b1, domain=dom['gnome-hello-ui'], language=l_it, full_po=pofile, part_po=pofile
+ )
+ pofile = PoFile.objects.create(
+ untranslated=20,
+ figures=[
+ {"path": "figures/gnome-hello-new.png", "hash": "8a1fcc6f46a22a1f500cfef9ca51b481"},
+ {"path": "figures/gnome-hello-logo.png", "hash": "1ae47b7a7c4fbeb1f6bb72c0cf18d389"},
+ ],
+ )
+ Statistics.objects.create(
+ branch=b1, domain=dom['gnome-hello-doc'], language=None, full_po=pofile, part_po=pofile
+ )
+ pofile = PoFile.objects.create(
+ translated=20,
+ figures=[
+ {
+ "translated": False,
+ "path": "figures/gnome-hello-new.png",
+ "hash": "8a1fcc6f46a22a1f500cfef9ca51b481",
+ "fuzzy": True,
+ },
+ {
+ "translated": False,
+ "path": "figures/gnome-hello-logo.png",
+ "hash": "1ae47b7a7c4fbeb1f6bb72c0cf18d389",
+ "fuzzy": False,
+ },
+ ],
+ )
+ Statistics.objects.create(
+ branch=b1, domain=dom['gnome-hello-doc'], language=l_fr, full_po=pofile, part_po=pofile
+ )
pofile = PoFile.objects.create(translated=20)
- Statistics.objects.create(branch=b1, domain=dom['gnome-hello-doc'], language=l_it, full_po=pofile,
part_po=pofile)
+ Statistics.objects.create(
+ branch=b1, domain=dom['gnome-hello-doc'], language=l_it, full_po=pofile, part_po=pofile
+ )
# zenity ui 3.8, zenity doc 3.8, zenity ui master, zenity doc master (POT, fr, it)
pofile = PoFile.objects.create(untranslated=136)
part_pofile = PoFile.objects.create(untranslated=128)
- Statistics.objects.create(branch=b2, domain=dom['zenity-ui'], language=None, full_po=pofile,
part_po=part_pofile)
+ Statistics.objects.create(
+ branch=b2, domain=dom['zenity-ui'], language=None, full_po=pofile, part_po=part_pofile
+ )
pofile = PoFile.objects.create(translated=136)
Statistics.objects.create(branch=b2, domain=dom['zenity-ui'], language=l_fr, full_po=pofile,
part_po=pofile)
pofile = PoFile.objects.create(translated=130, untranslated=6)
part_pofile = PoFile.objects.create(translated=100, untranslated=28)
- Statistics.objects.create(branch=b2, domain=dom['zenity-ui'], language=l_it, full_po=pofile,
part_po=part_pofile)
+ Statistics.objects.create(
+ branch=b2, domain=dom['zenity-ui'], language=l_it, full_po=pofile, part_po=part_pofile
+ )
pofile = PoFile.objects.create(untranslated=259)
Statistics.objects.create(branch=b2, domain=dom['zenity-doc'], language=None, full_po=pofile,
part_po=pofile)
pofile = PoFile.objects.create(untranslated=259)
@@ -141,7 +198,9 @@ class FixtureFactory(TestCase):
pofile = PoFile.objects.create(translated=259)
Statistics.objects.create(branch=b2, domain=dom['zenity-doc'], language=l_it, full_po=pofile,
part_po=pofile)
pofile = PoFile.objects.create(untranslated=149)
- stat1 = Statistics.objects.create(branch=b3, domain=dom['zenity-ui'], language=None, full_po=pofile,
part_po=pofile)
+ stat1 = Statistics.objects.create(
+ branch=b3, domain=dom['zenity-ui'], language=None, full_po=pofile, part_po=pofile
+ )
pofile = PoFile.objects.create(translated=255, fuzzy=4)
Statistics.objects.create(branch=b3, domain=dom['zenity-ui'], language=l_fr, full_po=pofile,
part_po=pofile)
pofile = PoFile.objects.create(translated=259)
@@ -154,20 +213,37 @@ class FixtureFactory(TestCase):
Statistics.objects.create(branch=b3, domain=dom['zenity-doc'], language=l_it, full_po=pofile,
part_po=pofile)
# shared-mime-info ui (POT, fr, it)
pofile = PoFile.objects.create(untranslated=626)
- Statistics.objects.create(branch=b4, domain=dom['shared-mime-info-ui'], language=None,
full_po=pofile, part_po=pofile)
+ Statistics.objects.create(
+ branch=b4, domain=dom['shared-mime-info-ui'], language=None, full_po=pofile, part_po=pofile
+ )
pofile = PoFile.objects.create(translated=598, fuzzy=20, untranslated=2)
- Statistics.objects.create(branch=b4, domain=dom['shared-mime-info-ui'], language=l_fr,
full_po=pofile, part_po=pofile)
+ Statistics.objects.create(
+ branch=b4, domain=dom['shared-mime-info-ui'], language=l_fr, full_po=pofile, part_po=pofile
+ )
pofile = PoFile.objects.create(translated=620, fuzzy=6)
- Statistics.objects.create(branch=b4, domain=dom['shared-mime-info-ui'], language=l_it,
full_po=pofile, part_po=pofile)
+ Statistics.objects.create(
+ branch=b4, domain=dom['shared-mime-info-ui'], language=l_it, full_po=pofile, part_po=pofile
+ )
# Example of error
- stat1.information_set.add(Information(
- type='error',
- description="Error regenerating POT file for zenity:\n<pre>intltool-update -g 'zenity'
-p\nERROR: xgettext failed to generate PO template file.</pre>"))
+ stat1.information_set.add(
+ Information(
+ type='error',
+ description=(
+ "Error regenerating POT file for zenity:\n<pre>intltool-update -g 'zenity' -p\nERROR:"
+ " xgettext failed to generate PO template file.</pre>"
+ ),
+ )
+ )
# Output fixture
out_file = NamedTemporaryFile(suffix=".json", dir=".", delete=False)
- call_command('dumpdata', *['auth.User', 'people', 'teams', 'languages', 'stats'],
- indent=1, format='json', stdout=out_file)
+ call_command(
+ 'dumpdata',
+ *['auth.User', 'people', 'teams', 'languages', 'stats'],
+ indent=1,
+ format='json',
+ stdout=out_file
+ )
out_file.close()
print("Fixture created in the file %s" % out_file.name)
diff --git a/stats/tests/tests.py b/stats/tests/tests.py
index 12d72f2f..f7a77c77 100644
--- a/stats/tests/tests.py
+++ b/stats/tests/tests.py
@@ -11,22 +11,23 @@ from django.conf import settings
from django.contrib.auth.models import User
from django.core import mail
from django.core.exceptions import ValidationError
-from django.urls import reverse
from django.test import TestCase
from django.test.utils import override_settings
+from django.urls import reverse
from common.utils import run_shell_command
+from languages.models import Language
+from people.models import Person
+from stats import utils
from stats.models import (
GLIB_PRESET, Module, Domain, Branch, Release, CategoryName, PoFile, Statistics,
FakeLangStatistics, Information, UnableToCommit
)
-from stats import utils
-from languages.models import Language
-
-from .utils import patch_shell_command, test_scratchdir
+from .utils import PatchShellCommand, test_scratchdir
try:
from translate.storage import subtitles # NOQA
+
has_translate_subtitle_support = True
except ImportError:
has_translate_subtitle_support = False
@@ -89,7 +90,8 @@ class ModuleTestCase(TestCase):
)
self.assertEqual(
self.mod.get_bugs_i18n_url('pot error'),
-
'https://gitlab.gnome.org/GNOME/gnome-hello/issues/?state=opened&label_name[]=8.%20Translation&search=pot+error'
+ 'https://gitlab.gnome.org/GNOME/gnome-hello/issues/?state=opened&label_name[]'
+ '=8.%20Translation&search=pot+error'
)
response = self.client.get(reverse('module', args=[self.mod.name]))
self.assertContains(
@@ -106,7 +108,7 @@ class ModuleTestCase(TestCase):
def test_first_branch_creation(self):
mod = Module.objects.create(name='eog', vcs_type='git',
vcs_root='https://gitlab.gnome.org/GNOME/eog.git')
br = Branch(module=mod, name='master')
- with patch_shell_command() as cmds:
+ with PatchShellCommand() as cmds:
br.checkout()
self.assertTrue(
'git clone https://gitlab.gnome.org/GNOME/eog.git %s/git/eog' % settings.SCRATCHDIR in cmds,
@@ -158,9 +160,11 @@ class ModuleTestCase(TestCase):
@test_scratchdir
def test_branch_stats(self):
lang = Language.objects.create(name='xxx', locale='xxx')
- ghost_stat = Statistics.objects.create(branch=self.branch,
domain=self.mod.domain_set.get(name='po'), language=lang)
+ ghost_stat = Statistics.objects.create(
+ branch=self.branch, domain=self.mod.domain_set.get(name='po'), language=lang
+ )
# Check stats
- with patch_shell_command(only=['git ']):
+ with PatchShellCommand(only=['git ']):
self.branch.update_stats(force=True)
fr_po_stat = Statistics.objects.get(branch=self.branch, domain__name='po', language__locale='fr')
self.assertEqual(fr_po_stat.translated(), 44)
@@ -212,7 +216,7 @@ class ModuleTestCase(TestCase):
def test_delete_branch(self):
""" Deleting the master branch of a git module deletes the checkout dir """
checkout_path = self.branch.co_path
- branch = Branch.objects.create(name="gnome-hello-1-4", module = self.mod)
+ branch = Branch.objects.create(name="gnome-hello-1-4", module=self.mod)
branch.delete()
self.assertTrue(checkout_path.exists())
self.branch.delete()
@@ -237,10 +241,12 @@ class ModuleTestCase(TestCase):
coord.save()
# Duplicate data
cat_name = CategoryName.objects.get(name='Desktop')
- response = self.client.post(reverse('module_edit_branches', args=[self.mod]), {
- '1': '1', '1_cat': str(cat_name.pk),
- '2': '2', '2_cat': str(cat_name.pk),
- 'new_branch': 'master', 'new_branch_release': '2', 'new_branch_category': str(cat_name.pk)}
+ response = self.client.post(
+ reverse('module_edit_branches', args=[self.mod]), {
+ '1': '1', '1_cat': str(cat_name.pk),
+ '2': '2', '2_cat': str(cat_name.pk),
+ 'new_branch': 'master', 'new_branch_release': '2', 'new_branch_category': str(cat_name.pk)
+ }
)
self.assertContains(response, "the form is not valid")
@@ -257,7 +263,7 @@ class ModuleTestCase(TestCase):
b1.save(update_statistics=False)
self.assertEqual(
[b.name for b in sorted(self.mod.branch_set.all())],
- ['master', 'a-branch', 'p-branch',]
+ ['master', 'a-branch', 'p-branch']
)
self.assertEqual(
[b.name for b in self.mod.get_branches(reverse=True)],
@@ -279,7 +285,7 @@ class ModuleTestCase(TestCase):
def test_string_frozen_mail(self):
""" String change for a module of a string_frozen release should generate a message """
mail.outbox = []
- with patch_shell_command(only=['git ']):
+ with PatchShellCommand(only=['git ']):
self.branch.update_stats(force=False)
# Create a new file with translation
@@ -302,8 +308,8 @@ class ModuleTestCase(TestCase):
def test_dynamic_po(self):
""" Test the creation of a blank po file for a new language """
tamil = Language.objects.create(name="Tamil", locale="ta")
- with patch_shell_command(only=['git ']):
- self.branch.update_stats(force=False) # At least POT stats needed
+ with PatchShellCommand(only=['git ']):
+ self.branch.update_stats(force=False) # At least POT stats needed
pot_stats = Statistics.objects.get(
branch=self.branch, domain__name='po', language__isnull=True
)
@@ -363,7 +369,7 @@ class ModuleTestCase(TestCase):
po_file = Path(__file__).parent / 'test.po'
domain = self.mod.domain_set.get(name='po')
fr_lang = Language.objects.get(locale='fr')
- with patch_shell_command():
+ with PatchShellCommand():
with self.assertRaisesRegex(UnableToCommit, 'read only'):
with self.assertLogs('stats.models', level='ERROR'):
branch.commit_po(po_file, domain, fr_lang, 'Author <someone example org>')
@@ -382,7 +388,7 @@ class ModuleTestCase(TestCase):
'git commit -m Update French translation --author Author <someone example org>',
'git push origin master', 'git log -n1 --format=oneline',
)
- with patch_shell_command() as cmds:
+ with PatchShellCommand() as cmds:
branch.commit_po(po_file, domain, fr_lang, 'Author <someone example org>')
for idx, cmd in enumerate(git_ops):
self.assertIn(cmd, cmds[idx])
@@ -394,7 +400,7 @@ class ModuleTestCase(TestCase):
'git commit -m Add Bemba translation --author Author <someone example org>',
'git push origin master'
)
- with patch_shell_command() as cmds:
+ with PatchShellCommand() as cmds:
with self.assertLogs('stats.models', level='ERROR'):
branch.commit_po(po_file, domain, bem_lang, 'Author <someone example org>')
for idx, cmd in enumerate(git_ops):
@@ -411,7 +417,7 @@ class ModuleTestCase(TestCase):
'git commit -m Update French translation --author Author <someone example org>',
'git push origin master'
)
- with patch_shell_command() as cmds:
+ with PatchShellCommand() as cmds:
branch.commit_po(po_file, domain, fr_lang, 'Author <someone example org>')
for idx, cmd in enumerate(git_ops):
self.assertIn(cmd, cmds[idx])
@@ -447,7 +453,7 @@ class ModuleTestCase(TestCase):
self.mod.vcs_root = self.mod.vcs_root.replace('git://', 'ssh://')
self.mod.save()
- with patch_shell_command(only=['git push', 'git fetch', 'git reset']) as cmds:
+ with PatchShellCommand(only=['git push', 'git fetch', 'git reset']) as cmds:
commit_hash = branch.commit_po(po_file, domain, fr_lang, 'Author <someone example org>')
update_repo_sequence = (
'git checkout -f master', 'git fetch', 'git reset --hard origin/master',
@@ -457,7 +463,7 @@ class ModuleTestCase(TestCase):
'git cherry-pick -x',
'git push origin master',
)
- with patch_shell_command(only=['git push', 'git fetch', 'git reset']) as cmds:
+ with PatchShellCommand(only=['git push', 'git fetch', 'git reset']) as cmds:
self.branch.cherrypick_commit(commit_hash, domain)
for idx, cmd in enumerate(git_ops):
self.assertIn(cmd, cmds[idx])
@@ -551,14 +557,20 @@ class DomainTests(TestModuleBase):
)
self.assertEqual(
domain.get_xgettext_command(self.branch),
- (['xgettext', '--files-from', 'POTFILES.in', '--directory',
- str(settings.SCRATCHDIR / 'git' / 'testmodule'),
- '--from-code', 'utf-8',
- '--add-comments', '--output', 'testmodule.pot'] + list(GLIB_PRESET) +
- ['--keyword=Description',
- '--msgid-bugs-address',
- 'https://gitlab.gnome.org/GNOME/testmodule/issues'],
- {'GETTEXTDATADIRS': os.path.dirname(utils.ITS_DATA_DIR)}
+ (
+ [
+ 'xgettext', '--files-from', 'POTFILES.in', '--directory',
+ str(settings.SCRATCHDIR / 'git' / 'testmodule'),
+ '--from-code', 'utf-8',
+ '--add-comments', '--output', 'testmodule.pot'
+ ]
+ + list(GLIB_PRESET) +
+ [
+ '--keyword=Description',
+ '--msgid-bugs-address',
+ 'https://gitlab.gnome.org/GNOME/testmodule/issues'
+ ],
+ {'GETTEXTDATADIRS': os.path.dirname(utils.ITS_DATA_DIR)}
)
)
@@ -670,7 +682,7 @@ class StatisticsTests(TestCase):
self.assertEqual(total_for_lang['ui']['total'], total_for_lang['ui_part']['total'])
self.assertTrue(total_for_lang['ui']['untranslated'] == total_for_lang['ui_part']['untranslated'] ==
0)
total_for_lang = rel.total_for_lang(Language.objects.get(locale='bem'))
- self.assertEqual(total_for_lang['ui']['total']-8, total_for_lang['ui_part']['total'])
+ self.assertEqual(total_for_lang['ui']['total'] - 8, total_for_lang['ui_part']['total'])
self.assertEqual(total_for_lang['ui']['untranslated'], 183)
self.assertEqual(total_for_lang['ui_part']['untranslated'], 175)
# Test that excluded domains are taken into account
@@ -734,22 +746,26 @@ class StatisticsTests(TestCase):
lang, 'ui', Release.objects.get(name="gnome-3-8")
)
for key, value in {
- 'total': 183, 'totaltrans': 183, 'totalfuzzy': 0, 'totaluntrans': 0,
- 'totaltransperc': 100, 'totalfuzzyperc': 0, 'totaluntransperc': 0,
- 'dtype': 'ui', 'all_errors': []
- }.items():
+ 'total': 183, 'totaltrans': 183, 'totalfuzzy': 0, 'totaluntrans': 0,
+ 'totaltransperc': 100, 'totalfuzzyperc': 0, 'totaluntransperc': 0,
+ 'dtype': 'ui', 'all_errors': []
+ }.items():
self.assertEqual(stats[key], value)
def _test_update_statistics(self):
# Temporarily deactivated, since update_stats cannot receive stats any more
from vertimus.models import State, StateTranslating
# Get a stat that has full_po and part_po
- stat = Statistics.objects.get(branch__module__name='zenity', branch__name='gnome-2-30',
language__locale='it', domain__dtype='ui')
+ stat = Statistics.objects.get(
+ branch__module__name='zenity', branch__name='gnome-2-30', language__locale='it',
domain__dtype='ui'
+ )
pers = Person.objects.create(username="toto")
- state = StateTranslating.objects.create(branch=stat.branch, domain=stat.domain,
language=stat.language, person=pers)
+ state = StateTranslating.objects.create(
+ branch=stat.branch, domain=stat.domain, language=stat.language, person=pers
+ )
# Mark file completely translated
self.assertNotEqual(stat.part_po, stat.full_po)
- #stat.set_translation_stats('dummy', 136, 0, 0)
+ # stat.set_translation_stats('dummy', 136, 0, 0)
self.assertEqual(stat.part_po, stat.full_po)
# Check state is still translating
state = State.objects.filter(branch=stat.branch, domain=stat.domain, language=stat.language)
@@ -790,6 +806,7 @@ class StatisticsTests(TestCase):
class FigureTests(TestCase):
fixtures = ['sample_data.json']
+
def test_figure_view(self):
url = reverse('docimages', args=['gnome-hello', 'help', 'master', 'fr'])
response = self.client.get(url)
@@ -800,11 +817,16 @@ class FigureTests(TestCase):
response = self.client.get(url)
self.assertContains(response, "gnome-hello-new.png")
- def test_figure_URLs(self):
+ def test_figure_urls(self):
""" Test if figure urls are properly constructed """
- stat = Statistics.objects.get(branch__module__name='gnome-hello', branch__name='master',
domain__dtype='doc', language__locale='fr')
+ stat = Statistics.objects.get(
+ branch__module__name='gnome-hello', branch__name='master', domain__dtype='doc',
language__locale='fr'
+ )
figs = stat.get_figures()
- self.assertEqual(figs[0]['orig_remote_url'],
'https://gitlab.gnome.org/GNOME/gnome-hello/raw/master/help/C/figures/gnome-hello-new.png')
+ self.assertEqual(
+ figs[0]['orig_remote_url'],
+ 'https://gitlab.gnome.org/GNOME/gnome-hello/raw/master/help/C/figures/gnome-hello-new.png'
+ )
self.assertFalse('trans_remote_url' in figs[0])
@test_scratchdir
@@ -952,7 +974,7 @@ class OtherTests(TestCase):
name='webkit', vcs_type='svn', vcs_root='https://svn.webkit.org/repository'
)
repo = SVNRepo(Branch(name='HEAD', module=mod, vcs_subpath='trunk'))
- with patch_shell_command() as cmds:
+ with PatchShellCommand() as cmds:
repo.init_checkout()
self.assertEqual(cmds, [
'svn co --non-interactive https://svn.webkit.org/repository/webkit/trunk '
diff --git a/stats/tests/utils.py b/stats/tests/utils.py
index c4b8a88b..f028f5c5 100644
--- a/stats/tests/utils.py
+++ b/stats/tests/utils.py
@@ -9,7 +9,7 @@ from unittest.mock import patch
from django.conf import settings
-class patch_shell_command:
+class PatchShellCommand:
"""
Mock common.utils.run_shell_commands and gather all passed commands.
`only` is an optional list of commands to limit mocking to (empty -> all).
@@ -47,8 +47,8 @@ def test_scratchdir(test_func):
""" Decorator to temporarily use the scratchdir inside the test directory """
@wraps(test_func)
def decorator(self):
- old_SCRATCHDIR = settings.SCRATCHDIR
- old_POTDIR = settings.POTDIR
+ old_scratchdir = settings.SCRATCHDIR
+ old_potdir = settings.POTDIR
settings.SCRATCHDIR = Path(tempfile.mkdtemp()) / 'scratch'
settings.POTDIR = settings.SCRATCHDIR / "POT"
settings.POTDIR.mkdir(parents=True)
@@ -59,6 +59,6 @@ def test_scratchdir(test_func):
test_func(self)
finally:
shutil.rmtree(settings.SCRATCHDIR)
- settings.SCRATCHDIR = old_SCRATCHDIR
- settings.POTDIR = old_POTDIR
+ settings.SCRATCHDIR = old_scratchdir
+ settings.POTDIR = old_potdir
return decorator
diff --git a/stats/utils.py b/stats/utils.py
index 9550daf0..c76fcd4d 100644
--- a/stats/utils.py
+++ b/stats/utils.py
@@ -1,5 +1,4 @@
import hashlib
-import logging
import os
import re
import shutil
@@ -8,14 +7,15 @@ from itertools import islice
from pathlib import Path
from unittest.mock import MagicMock
-from translate.tools import pogrep, pocount
-
from django.conf import settings
from django.core.files.base import File
from django.template.loader import get_template
from django.utils.functional import cached_property
from django.utils.translation import gettext as _, gettext_noop
+from translate.storage.base import TranslationStore
+from translate.tools import pogrep, pocount
+
from common.utils import run_shell_command, send_mail
from . import potdiff
@@ -24,17 +24,17 @@ C_ENV = {"LC_ALL": "C", "LANG": "C", "LANGUAGE": "C"}
NOT_CHANGED = 0
CHANGED_ONLY_FORMATTING = 1
-CHANGED_WITH_ADDITIONS = 2
-CHANGED_NO_ADDITIONS = 3
+CHANGED_WITH_ADDITIONS = 2
+CHANGED_NO_ADDITIONS = 3
ITSTOOL_PATH = getattr(settings, 'ITSTOOL_PATH', '')
ITS_DATA_DIR = settings.SCRATCHDIR / 'data' / 'its'
# monkey-patch ttk (https://github.com/translate/translate/issues/2129)
-from translate.storage.base import TranslationStore
orig_addunit = TranslationStore.addunit
-def patchedAddunit(self, unit):
+
+def patched_add_unit(self, unit):
# Prevent two header units in the same store
if unit.isheader() and len(self.units) and self.units[0].isheader():
unit._store = self
@@ -42,7 +42,8 @@ def patchedAddunit(self, unit):
else:
orig_addunit(self, unit)
-TranslationStore.addunit = patchedAddunit
+
+TranslationStore.addunit = patched_add_unit
class UndetectableDocFormat(Exception):
@@ -233,7 +234,7 @@ class MakefileWrapper:
class MesonfileWrapper(MakefileWrapper):
i18n_gettext_kwargs = {'po_dir', 'data_dirs', 'type', 'languages', 'args', 'preset'}
- gnome_yelp_kwargs= {'sources', 'media', 'symlink_media', 'languages'}
+ gnome_yelp_kwargs = {'sources', 'media', 'symlink_media', 'languages'}
ignorable_kwargs = {'install_dir'}
readable = True
@@ -249,9 +250,15 @@ class MesonfileWrapper(MakefileWrapper):
)
# ignore if/endif sections
content = re.sub(r"^if .*endif$", '', content, flags=re.M | re.S)
- content = content.replace('true', 'True').replace('false', 'False'
- ).replace("i18n = import('i18n')", ''
- ).replace("gnome = import('gnome')", '')
+ content = content.replace(
+ 'true', 'True'
+ ).replace(
+ 'false', 'False'
+ ).replace(
+ "i18n = import('i18n')", ''
+ ).replace(
+ "gnome = import('gnome')", ''
+ )
return content
@cached_property
@@ -356,14 +363,17 @@ def sort_object_list(lst, sort_meth):
templist.sort()
return [obj_ for (key1, obj_) in templist]
+
def multiple_replace(dct, text):
regex = re.compile("(%s)" % "|".join(map(re.escape, dct.keys())))
return regex.sub(lambda mo: dct[mo.string[mo.start():mo.end()]], text)
+
def stripHTML(string):
replacements = {"<ul>": "\n", "</ul>": "\n", "<li>": " * ", "\n</li>": "", "</li>": ""}
return multiple_replace(replacements, string)
+
def ellipsize(val, length):
if len(val) > length:
val = "%s..." % val[:length]
@@ -375,6 +385,7 @@ def check_program_presence(prog_name):
status, output, err = run_shell_command(['which', prog_name])
return status == 0
+
def po_grep(in_file, out_file, filter_):
if filter_ == "-":
return
@@ -394,11 +405,14 @@ def po_grep(in_file, out_file, filter_):
pass
# command-line variant:
"""
- cmd = "pogrep --invert-match --header --search=locations --regexp \"gschema\\.xml\\.in|schemas\\.in\"
%(full_po)s %(part_po)s" % {
+ cmd = 'pogrep --invert-match --header --search=locations --regexp ' \
+ '"gschema\\.xml\\.in|schemas\\.in\" %(full_po)s %(part_po)s' % {
'full_po': in_file,
'part_po': out_file,
}
- run_shell_command(cmd)"""
+ run_shell_command(cmd)
+ """
+
def check_potfiles(po_path):
"""Check if there were any problems regenerating a POT file (intltool-update -m).
@@ -409,25 +423,30 @@ def check_potfiles(po_path):
status, output, errs = run_shell_command(['intltool-update', '-m'], cwd=po_path)
if status != STATUS_OK:
- errors.append( ("error", gettext_noop("Errors while running “intltool-update -m” check.")) )
+ errors.append(("error", gettext_noop("Errors while running “intltool-update -m” check.")))
missing = po_path / "missing"
if missing.exists():
with missing.open("r") as fh:
errors.append(
- ("warn",
- gettext_noop("There are some missing files from POTFILES.in: %s") % (
- "<ul><li>" + "</li>\n<li>".join(fh.readlines()) + "</li>\n</ul>")
- )
+ (
+ "warn",
+ gettext_noop(
+ "There are some missing files from POTFILES.in: %s"
+ ) % ("<ul><li>" + "</li>\n<li>".join(fh.readlines()) + "</li>\n</ul>")
+ )
)
notexist = po_path / "notexist"
if notexist.exists():
with notexist.open("r") as fh:
errors.append(
- ("error",
- gettext_noop("Following files are referenced in either POTFILES.in or POTFILES.skip, yet
they don’t exist: %s") % (
- "<ul><li>" + "</li>\n<li>".join(fh.readlines()) + "</li>\n</ul>")
+ (
+ "error",
+ gettext_noop(
+ "Following files are referenced in either POTFILES.in or POTFILES.skip, "
+ "yet they don’t exist: %s"
+ ) % ("<ul><li>" + "</li>\n<li>".join(fh.readlines()) + "</li>\n</ul>")
)
)
return errors
@@ -512,7 +531,7 @@ def check_po_conformity(pofile):
if status != STATUS_OK:
errors.append((
"warn",
- gettext_noop("PO file “%s” is not UTF-8 encoded.") % pofile.name
+ gettext_noop("PO file “%s” is not UTF-8 encoded.") % pofile.name
))
return errors
@@ -538,13 +557,13 @@ def check_po_quality(pofile, filters):
def po_file_stats(pofile):
"""Compute pofile translation statistics."""
res = {
- 'translated' : 0,
- 'fuzzy' : 0,
- 'untranslated' : 0,
+ 'translated': 0,
+ 'fuzzy': 0,
+ 'untranslated': 0,
'translated_words': 0,
'fuzzy_words': 0,
'untranslated_words': 0,
- 'errors' : [],
+ 'errors': [],
}
if not pofile.exists():
@@ -585,6 +604,7 @@ def read_linguas_file(full_path):
'error': gettext_noop("Entry for this language is not present in LINGUAS file."),
}
+
def insert_locale_in_linguas(linguas_path, locale):
temp_linguas = linguas_path.parent / (linguas_path.name + "~")
with linguas_path.open('r') as fin, temp_linguas.open('w') as fout:
@@ -602,13 +622,13 @@ def insert_locale_in_linguas(linguas_path, locale):
def get_ui_linguas(branch, subdir):
"""Get language list in one of po/LINGUAS, configure.ac or configure.in"""
- LINGUAShere = branch.co_path / subdir/ "LINGUAS"
- LINGUASpo = branch.co_path / "po" / "LINGUAS" # if we're in eg. po-locations/
+ linguas_here = branch.co_path / subdir / "LINGUAS"
+ lingua_po = branch.co_path / "po" / "LINGUAS" # if we're in eg. po-locations/
# is "lang" listed in either of po/LINGUAS, ./configure.ac(ALL_LINGUAS) or ./configure.in(ALL_LINGUAS)
- for LINGUAS in [LINGUAShere, LINGUASpo]:
- if LINGUAS.exists():
- return read_linguas_file(LINGUAS)
+ for linguas in [linguas_here, lingua_po]:
+ if linguas.exists():
+ return read_linguas_file(linguas)
# AS_ALL_LINGUAS is a macro that takes all po files by default
status, output, errs = run_shell_command("grep -qs AS_ALL_LINGUAS %s%sconfigure.*" % (branch.co_path,
os.sep))
if status == 0:
@@ -621,9 +641,9 @@ def get_ui_linguas(branch, subdir):
found = MakefileWrapper(branch, configure).read_variable('ALL_LINGUAS')
if found is not None:
return {'langs': found.split(),
- 'error': gettext_noop("Entry for this language is not present in ALL_LINGUAS in
configure file.") }
- return {'langs':None,
- 'error': gettext_noop("Don’t know where to look for the LINGUAS variable, ask the module
maintainer.") }
+ 'error': gettext_noop("Entry for this language is not present in ALL_LINGUAS in
configure file.")}
+ return {'langs': None,
+ 'error': gettext_noop("Don’t know where to look for the LINGUAS variable, ask the module
maintainer.")}
def get_doc_linguas(branch, subdir):
@@ -632,18 +652,19 @@ def get_doc_linguas(branch, subdir):
po_path = branch.co_path / subdir
# Prioritize LINGUAS files when it exists.
- LINGUAS_path = po_path / "LINGUAS"
- if LINGUAS_path.exists():
- return read_linguas_file(LINGUAS_path)
+ linguas_path = po_path / "LINGUAS"
+ if linguas_path.exists():
+ return read_linguas_file(linguas_path)
linguas_file = MakefileWrapper.find_file(branch, [po_path, branch.co_path])
if linguas_file:
linguas = linguas_file.read_variable("DOC_LINGUAS", "HELP_LINGUAS", "gettext.languages")
if linguas is None:
- return {'langs':None,
- 'error': gettext_noop("Don’t know where to look for the DOC_LINGUAS variable, ask the module
maintainer.") }
+ return {'langs': None,
+ 'error': gettext_noop(
+ "Don’t know where to look for the DOC_LINGUAS variable, ask the module maintainer.")}
return {'langs': linguas.split() if isinstance(linguas, str) else linguas,
- 'error': gettext_noop("DOC_LINGUAS list doesn’t include this language.") }
+ 'error': gettext_noop("DOC_LINGUAS list doesn’t include this language.")}
def collect_its_data():
@@ -683,21 +704,22 @@ def get_fig_stats(pofile, doc_format):
return []
lines = output.split('\n')
while lines[0][0] != "#":
- lines = lines[1:] # skip warning messages at the top of the output
+ lines = lines[1:] # skip warning messages at the top of the output
figures = []
- for i, line in islice(enumerate(lines), 0, None, 3+before_lines):
+ for i, line in islice(enumerate(lines), 0, None, 3 + before_lines):
# TODO: add image size
fig = {"path": '', "hash": ''}
- m = doc_format.img_regex.match(lines[i+before_lines])
+ m = doc_format.img_regex.match(lines[i + before_lines])
if m:
fig["path"] = m.group('path')
fig["hash"] = m.group('hash')
- fig["fuzzy"] = (line=='#, fuzzy' or line[:8]=='#| msgid')
- fig["translated"] = len(lines[i+before_lines+1])>9 and not fig['fuzzy']
+ fig["fuzzy"] = (line == '#, fuzzy' or line[:8] == '#| msgid')
+ fig["translated"] = len(lines[i + before_lines + 1]) > 9 and not fig['fuzzy']
figures.append(fig)
return figures
+
def check_identical_figures(fig_stats, base_path, lang):
errors = []
for fig in fig_stats:
@@ -705,7 +727,9 @@ def check_identical_figures(fig_stats, base_path, lang):
if trans_path.exists():
trans_hash = compute_md5(trans_path)
if fig['hash'] == trans_hash:
- errors.append(("warn-ext", "Figures should not be copied when identical to original (%s)." %
trans_path))
+ errors.append(
+ ("warn-ext", "Figures should not be copied when identical to original (%s)." %
trans_path)
+ )
return errors
@@ -739,7 +763,7 @@ def exclude_untrans_messages(potfile):
fh.seek(0)
skip_unit = False
for line in lines:
- if not exclude_message in line.lower() and not skip_unit:
+ if exclude_message not in line.lower() and not skip_unit:
fh.write(line)
else:
# A blank line is resetting skip_unit
@@ -754,7 +778,7 @@ def is_po_reduced(file_path):
def compute_md5(full_path):
m = hashlib.md5()
- block_size=2**13
+ block_size = 2 ** 13
with full_path.open('rb') as fh:
while True:
data = fh.read(block_size)
@@ -789,6 +813,7 @@ def url_join(base, *args):
url += arg
return url
+
class Profiler:
def __init__(self):
self.start = time.clock()
diff --git a/stats/views.py b/stats/views.py
index 67173f86..1954226f 100644
--- a/stats/views.py
+++ b/stats/views.py
@@ -24,7 +24,7 @@ def modules(request, format='html'):
data = serializers.serialize(format, all_modules)
return HttpResponse(data, content_type=MIME_TYPES[format])
context = {
- 'pageSection': "module",
+ 'pageSection': "module",
'modules': sort_object_list(all_modules, 'get_description'),
}
return render(request, 'module_list.html', context)
@@ -42,10 +42,10 @@ def module(request, module_name):
branch.get_doc_stats(mandatory_langs=langs)
context = {
- 'pageSection': "module",
+ 'pageSection': "module",
'module': mod,
'branches': branches,
- 'non_standard_repo_msg' : _(settings.VCS_HOME_WARNING),
+ 'non_standard_repo_msg': _(settings.VCS_HOME_WARNING),
'can_edit_branches': mod.can_edit_branches(request.user),
'can_refresh': can_refresh_branch(request.user),
'user_language': get_user_locale(request)
@@ -102,7 +102,7 @@ def module_edit_branches(request, module_name):
else:
rel = Release.objects.get(pk=form.cleaned_data[key].pk)
branch = Branch.objects.get(module=mod, name=key)
- cat = Category(release=rel, branch=branch, name=form.cleaned_data[key+'_cat'])
+ cat = Category(release=rel, branch=branch, name=form.cleaned_data[key + '_cat'])
cat.save()
updated = True
# New branch (or new category)
@@ -163,27 +163,32 @@ def branch_refresh(request, branch_id):
def docimages(request, module_name, potbase, branch_name, langcode):
mod = get_object_or_404(Module, name=module_name)
try:
- stat = Statistics.objects.get(branch__module=mod.id,
- branch__name=branch_name,
- domain__name=potbase,
- language__locale=langcode)
+ stat = Statistics.objects.get(
+ branch__module=mod.id,
+ branch__name=branch_name,
+ domain__name=potbase,
+ language__locale=langcode
+ )
except Statistics.DoesNotExist:
- pot_stat = get_object_or_404(Statistics,
+ pot_stat = get_object_or_404(
+ Statistics,
branch__module=mod.id,
branch__name=branch_name,
domain__name=potbase,
- language__isnull=True)
+ language__isnull=True
+ )
lang = get_object_or_404(Language, locale=langcode)
stat = FakeLangStatistics(pot_stat, lang)
context = {
'pageSection': "module",
- 'module': mod,
- 'stat': stat,
- 'locale': stat.language.locale,
+ 'module': mod,
+ 'stat': stat,
+ 'locale': stat.language.locale,
'figstats': stat.fig_stats(),
}
return render(request, 'module_images.html', context)
+
def dynamic_po(request, module_name, branch_name, domain_name, filename):
""" Generates a dynamic po file from the POT file of a branch """
try:
@@ -217,9 +222,9 @@ def dynamic_po(request, module_name, branch_name, domain_name, filename):
if request.user.is_authenticated:
person = Person.get_by_user(request.user)
dyn_content += "# %(name)s <%(email)s>, %(year)s.\n#\n" % {
- 'name' : person.name,
+ 'name': person.name,
'email': person.email,
- 'year' : date.today().year,
+ 'year': date.today().year,
}
else:
dyn_content += "# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\n"
@@ -248,32 +253,35 @@ def dynamic_po(request, module_name, branch_name, domain_name, filename):
dyn_content += new_line + "\n"
if line == "":
# Output the remaining part of the file untouched
- dyn_content += "\n".join(lines[i+1:])
+ dyn_content += "\n".join(lines[i + 1:])
break
response = HttpResponse(dyn_content, 'text/plain')
response['Content-Disposition'] = 'inline; filename=%s' % (
".".join([domain.potbase(), branch.name_escaped, filename]))
return response
+
def releases(request, format='html'):
active_releases = Release.objects.filter(weight__gte=0).order_by('status', '-weight', '-name')
- old_releases = Release.objects.filter(weight__lt=0).order_by('status', '-weight', '-name')
+ old_releases = Release.objects.filter(weight__lt=0).order_by('status', '-weight', '-name')
if format in ('json', 'xml'):
from itertools import chain
data = serializers.serialize(format, chain(active_releases, old_releases))
return HttpResponse(data, content_type=MIME_TYPES[format])
context = {
- 'pageSection' : "releases",
+ 'pageSection': "releases",
'active_releases': active_releases,
- 'old_releases' : old_releases,
+ 'old_releases': old_releases,
}
return render(request, 'release_list.html', context)
+
def release(request, release_name, format='html'):
release = get_object_or_404(Release, name=release_name)
if format == 'xml':
- return render(request, 'release_detail.xml', { 'release' : release },
- content_type=MIME_TYPES[format])
+ return render(
+ request, 'release_detail.xml', {'release': release}, content_type=MIME_TYPES[format]
+ )
context = {
'pageSection': "releases",
'user_language': get_user_locale(request),
@@ -281,6 +289,7 @@ def release(request, release_name, format='html'):
}
return render(request, 'release_detail.html', context)
+
def compare_by_releases(request, dtype, rels_to_compare):
releases = []
try:
diff --git a/vertimus/tests/tests.py b/vertimus/tests/tests.py
index 82bc7cd9..362df2cb 100644
--- a/vertimus/tests/tests.py
+++ b/vertimus/tests/tests.py
@@ -22,7 +22,7 @@ from teams.models import Role, Team
from teams.tests import TeamsAndRolesMixin
from stats.models import Module, Branch, Release, Category, CategoryName, Domain, Statistics
from stats.tests.tests import TestModuleBase
-from stats.tests.utils import patch_shell_command, test_scratchdir
+from stats.tests.utils import PatchShellCommand, test_scratchdir
from vertimus.models import (
Action, ActionArchived, ActionCI, ActionUNDO, ActionWC, State, StateCommitted,
StateCommitting, StateNone, StateProofread, StateProofreading, StateToCommit,
@@ -466,7 +466,7 @@ class VertimusTest(TeamsAndRolesMixin, TestCase):
self.assertTrue(form.is_valid())
# path needed when copying file to commit
(self.b.co_path / 'po').mkdir(parents=True, exist_ok=True)
- with patch_shell_command(only=['git ']) as cmds:
+ with PatchShellCommand(only=['git ']) as cmds:
action = Action.new_by_name('CI', person=self.pcoo)
msg = action.apply_on(state, form.cleaned_data)
self.assertIn(
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]