[damned-lies] refactor: fix few linter issues in stats
- From: Claude Paroz <claudep src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [damned-lies] refactor: fix few linter issues in stats
- Date: Sat, 24 Apr 2021 08:42:17 +0000 (UTC)
commit d53265a6d54ba1703c28eb1faaa57248489d92d0
Author: Guillaume Bernard <associations guillaume-bernard fr>
Date: Sun Apr 18 11:29:54 2021 +0200
refactor: fix few linter issues in stats
stats/admin.py | 9 +-
stats/doap.py | 23 ++--
stats/forms.py | 24 ++--
stats/models.py | 364 +++++++++++++++++++++++++++++----------------------
stats/tests/tests.py | 8 ++
5 files changed, 248 insertions(+), 180 deletions(-)
---
diff --git a/stats/admin.py b/stats/admin.py
index 1bcd679b..0f334446 100644
--- a/stats/admin.py
+++ b/stats/admin.py
@@ -174,9 +174,8 @@ class ReleaseAdmin(admin.ModelAdmin):
branch = cat.branch
if branch in branch_seen:
continue
- else:
- Category.objects.create(release=new_rel, branch=branch, name=cat.name)
- branch_seen.add(branch)
+ Category.objects.create(release=new_rel, branch=branch, name=cat.name)
+ 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)"
@@ -213,18 +212,22 @@ class ReleaseAdmin(admin.ModelAdmin):
return render(request, 'admin/delete_release_confirmation.html', context)
delete_release.short_description = "Delete release (and associated branches)"
+
class InformationInline(admin.TabularInline):
model = Information
extra = 0
+
class StatisticsAdmin(admin.ModelAdmin):
search_fields = ('language__name', 'branch__module__name')
raw_id_fields = ('branch', 'domain', 'language', 'full_po', 'part_po')
inlines = [ InformationInline ]
+
class PoFileAdmin(admin.ModelAdmin):
search_fields = ('path',)
+
admin.site.register(Statistics, StatisticsAdmin)
admin.site.register(PoFile, PoFileAdmin)
admin.site.register(Branch, BranchAdmin)
diff --git a/stats/doap.py b/stats/doap.py
index 182aab40..af20ca68 100644
--- a/stats/doap.py
+++ b/stats/doap.py
@@ -45,6 +45,7 @@ class DoapParser:
return homepage_tag.attrib.get('{http://www.w3.org/1999/02/22-rdf-syntax-ns#}resource')
return None
+
def update_doap_infos(module):
""" Should only be called inside an "update-stats" context of a master branch,
so there is no need for any extra checkout/locking strategy """
@@ -64,24 +65,26 @@ def update_doap_infos(module):
current_maintainers = dict([(m.email or m.username, m) for m in module.maintainers.all()])
# Using email or username as unique identifier
- for maint in doap_maintainers:
- maint_key = maint['email'] or maint['account'] or slugify(maint['name'])
- if maint_key in current_maintainers.keys():
- del current_maintainers[maint_key]
+ for maintainer in doap_maintainers:
+ maintainer_key = maintainer['email'] or maintainer['account'] or slugify(maintainer['name'])
+ if maintainer_key in current_maintainers.keys():
+ del current_maintainers[maintainer_key]
else:
# Add new maintainer
- pers = Person.get_by_attr('email', maint['email'])
+ pers = Person.get_by_attr('email', maintainer['email'])
if not pers:
- pers = Person.get_by_attr('username', maint['account'] or slugify(maint['name']))
+ pers = Person.get_by_attr('username', maintainer['account'] or slugify(maintainer['name']))
if not pers:
- pers = Person(username=maint['account'] or slugify(maint['name']), email=maint['email'] or
'',
- password='!', svn_account=maint['account'], last_name=maint['name'])
+ pers = Person(
+ username=maintainer['account'] or slugify(maintainer['name']), email=maintainer['email']
or '',
+ password='!', svn_account=maintainer['account'], last_name=maintainer['name']
+ )
pers.save()
module.maintainers.add(pers)
- for key, maint in current_maintainers.items():
+ for maintainer in current_maintainers.values():
# Drop maintainers not in doap file
- module.maintainers.remove(maint)
+ module.maintainers.remove(maintainer)
# *********** Update homepage
home = tree.parse_homepage()
diff --git a/stats/forms.py b/stats/forms.py
index 23788e01..fbdac4e0 100644
--- a/stats/forms.py
+++ b/stats/forms.py
@@ -20,19 +20,19 @@ class ModuleBranchForm(forms.Form):
self.branch_fields = []
default_cat_name = None
for branch in module.get_branches(reverse=True):
- categs = branch.category_set.order_by('name', 'release__name')
- if len(categs):
- for cat in categs:
- self.fields[str(cat.id)] = ReleaseField(queryset=Release.objects.all(),
- label=branch.name,
- initial=cat.release.pk)
- self.fields[str(cat.id)+'_cat'] = forms.ModelChoiceField(
+ categories = list(branch.category_set.order_by('name', 'release__name'))
+ if len(categories) > 0:
+ for category in categories:
+ 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(
queryset=CategoryName.objects.all(),
required=False,
- initial=cat.name
+ initial=category.name
)
- self.branch_fields.append((str(cat.id), str(cat.id)+'_cat'))
- default_cat_name = cat.name
+ self.branch_fields.append((str(category.id), f'{category.id}_cat'))
+ default_cat_name = categories[-1].name
else:
# Branch is not linked to any release
self.fields[branch.name] = ReleaseField(queryset=Release.objects.all(),
@@ -42,7 +42,7 @@ class ModuleBranchForm(forms.Form):
required=False,
initial=default_cat_name
)
- self.branch_fields.append((branch.name, branch.name+'_cat'))
+ self.branch_fields.append((branch.name, branch.name + '_cat'))
self.fields['new_branch'] = forms.CharField(required=False)
self.fields['new_branch_release'] = ReleaseField(queryset=Release.objects.all())
@@ -53,7 +53,7 @@ class ModuleBranchForm(forms.Form):
def get_branches(self):
for rel_field, cat_field in self.branch_fields:
- yield (self[rel_field], self[cat_field])
+ yield self[rel_field], self[cat_field]
def clean(self):
cleaned_data = super().clean()
diff --git a/stats/models.py b/stats/models.py
index eea291d9..8505b5de 100644
--- a/stats/models.py
+++ b/stats/models.py
@@ -1,5 +1,7 @@
import logging
-import os, sys, re
+import os
+import sys
+import re
import shutil
import threading
from collections import Counter, OrderedDict
@@ -121,7 +123,7 @@ class Module(models.Model):
return self.name
def get_comment(self):
- comment = self.comment and _(self.comment) or ""
+ comment = _(self.comment) if self.comment else ""
if self.ext_platform:
if comment:
comment += "<br/>"
@@ -139,18 +141,16 @@ class Module(models.Model):
def get_bugs_i18n_url(self, content=None):
if 'gitlab' in self.bugs_base:
- return utils.url_join(self.bugs_base, '?state=opened&label_name[]=8.%20Translation')
+ link = utils.url_join(self.bugs_base, '?state=opened&label_name[]=8.%20Translation')
if content:
- link += "&search=%s" % content
+ link += "&search=%s" % content.replace(' ', '+')
return link
- else:
- return None
+ return None
def get_bugs_enter_url(self):
if 'gitlab' in self.bugs_base:
return utils.url_join(self.bugs_base, 'new')
- else:
- return self.bugs_base
+ return self.bugs_base
def get_branches(self, reverse=False):
""" Return module branches, in ascending order by default (descending order if reverse == True) """
@@ -158,12 +158,11 @@ class Module(models.Model):
def get_head_branch(self):
""" Returns the HEAD (trunk, master, ...) branch of the module """
- branch = self.branch_set.filter(name__in = BRANCH_HEAD_NAMES)
+ branch = self.branch_set.filter(name__in=BRANCH_HEAD_NAMES)
if branch:
# First one (if many something is wrong :-/)
return branch[0]
- else:
- return None
+ return None
def can_edit_branches(self, user):
""" Returns True for superusers, users with adequate permissions or maintainers of the module """
@@ -187,7 +186,6 @@ class ModuleLock:
We use filesystem directories creation/deletion to act as global lock mecanism
"""
def __init__(self, mod):
- assert isinstance(mod, Module)
self.module = mod
self.dirpath = settings.LOCK_DIR / f"updating-{self.module.name}"
@@ -287,7 +285,7 @@ class Branch(models.Model):
def __lt__(self, other):
if self.name in BRANCH_HEAD_NAMES:
return True
- elif other.name in BRANCH_HEAD_NAMES:
+ if other.name in BRANCH_HEAD_NAMES:
return False
# Splitting so gnome-3-2 < gnome-3-10
return ((-self.weight,) + split_name(self.name)) > ((-other.weight,) + split_name(other.name))
@@ -298,7 +296,7 @@ class Branch(models.Model):
@property
def img_url_prefix(self):
- return self.module.vcs_type == 'git' and "raw" or ""
+ return 'raw' if self.module.vcs_type == 'git' else ''
def is_head(self):
return self.name in BRANCH_HEAD_NAMES
@@ -318,12 +316,12 @@ class Branch(models.Model):
new_hash = utils.compute_md5(full_path)
if self.file_hashes and self.file_hashes.get(rel_path, None) == new_hash:
return False
- else:
- if not self.file_hashes:
- self.file_hashes = {}
- self.file_hashes[rel_path] = new_hash
- self.save(update_statistics=False)
- return True
+
+ if not self.file_hashes:
+ self.file_hashes = {}
+ self.file_hashes[rel_path] = new_hash
+ self.save(update_statistics=False)
+ return True
def has_string_frozen(self):
""" Returns true if the branch is contained in at least one string frozen release """
@@ -339,20 +337,18 @@ class Branch(models.Model):
def get_vcs_web_url(self):
if self.module.vcs_type in ('hg', 'git'):
return self.module.vcs_web
- elif self.vcs_subpath:
+ if self.vcs_subpath:
return utils.url_join(self.module.vcs_web, self.vcs_subpath)
- elif self.is_head():
+ if self.is_head():
return utils.url_join(self.module.vcs_web, "trunk")
- else:
- return utils.url_join(self.module.vcs_web, "branches", self.name)
+ return utils.url_join(self.module.vcs_web, "branches", self.name)
def get_vcs_web_log_url(self):
""" Link to browsable commit log """
if self.module.vcs_type == 'git':
return utils.url_join(self.module.vcs_web, 'commits', self.name)
- else:
- # Not implemented for other VCS
- return ""
+ # Not implemented for other VCS
+ return ""
@cached_property
def co_path(self):
@@ -390,11 +386,11 @@ class Branch(models.Model):
dirname.mkdir(parents=True, exist_ok=True)
return dirname
- def get_stats(self, typ, mandatory_langs=[]):
+ 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 a list of language objects whose stats should be added even if no translation
exists.
+ mandatory_langs is an iterable of language objects whose stats should be added even if no
translation exists.
"""
stats = OrderedDict()
stats_langs = {}
@@ -416,17 +412,17 @@ class Branch(models.Model):
if lang not in stats_langs[domain] and stats[domain][0].full_po:
stats[domain].append(FakeLangStatistics(stats[domain][0], lang))
# Sort
- for key, doms in stats.items():
- #Sort stats, pot first, then translated (desc), then language name
+ 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()))
return stats
- def get_doc_stats(self, mandatory_langs=[]):
+ def get_doc_stats(self, mandatory_langs=()):
if not self._doc_stats:
self._doc_stats = self.get_stats('doc', mandatory_langs)
return self._doc_stats
- def get_ui_stats(self, mandatory_langs=[]):
+ def get_ui_stats(self, mandatory_langs=()):
if not self._ui_stats:
self._ui_stats = self.get_stats('ui', mandatory_langs)
return self._ui_stats
@@ -476,8 +472,7 @@ class Branch(models.Model):
# *****************************
previous_pot = self.output_dir(dom.dtype) / ('%s.%s.pot' % (dom.potbase(),
self.name_escaped))
if not potfile:
- logger.error("Can't generate POT file for %s/%s." % (
- self.module.name, dom.name))
+ logger.error("Can’t generate POT file for %s/%s.", self.module.name, dom.name)
if previous_pot.exists():
# Use old POT file
potfile = previous_pot
@@ -503,7 +498,7 @@ class Branch(models.Model):
if potfile != previous_pot:
try:
shutil.copyfile(str(potfile), str(previous_pot))
- except Exception:
+ except Exception: # FIXME: too broad exception
pot_stat.set_error('error', gettext_noop("Can’t copy new POT file to public
location."))
# Send pot_has_changed signal
@@ -512,7 +507,9 @@ class Branch(models.Model):
# 7. Update language po files and update DB
# *****************************************
- stats_with_ext_errors = Statistics.objects.filter(branch=self, domain=dom,
information__type__endswith='-ext')
+ stats_with_ext_errors = Statistics.objects.filter(
+ branch=self, domain=dom, information__type__endswith='-ext'
+ )
langs_with_ext_errors = [stat.language.locale for stat in stats_with_ext_errors]
dom_langs = dom.get_lang_files(self.co_path)
for lang, pofile in dom_langs:
@@ -522,7 +519,7 @@ class Branch(models.Model):
if (not force and changed_status in (utils.NOT_CHANGED, utils.CHANGED_ONLY_FORMATTING)
and outpo.exists()
and pofile.stat().st_mtime < outpo.stat().st_mtime
- and not lang in langs_with_ext_errors):
+ and lang not in langs_with_ext_errors):
continue
# msgmerge po with recent pot
@@ -555,16 +552,18 @@ class Branch(models.Model):
stat.set_error('warn-ext', linguas['error'])
# Delete stats for unexisting langs
- Statistics.objects.filter(branch=self, domain=dom
- ).exclude(models.Q(language__isnull=True) | models.Q(language__locale__in=[dl[0] for dl
in dom_langs])
- ).delete()
+ Statistics.objects.filter(
+ branch=self, domain=dom
+ ).exclude(
+ models.Q(language__isnull=True) | models.Q(language__locale__in=[dl[0] for dl in
dom_langs])
+ ).delete()
# Check if doap file changed
if self.is_head() and self.file_changed("%s.doap" % self.module.name):
update_doap_infos(self.module)
def checkout(self):
""" Do a checkout or an update of the VCS files """
- logger.debug("Checking '%s.%s' out to '%s'…" % (self.module.name, self.name, self.co_path))
+ logger.debug("Checking “%s.%s” out to “%s”…", self.module.name, self.name, self.co_path)
self._repo.checkout()
def update_repo(self):
@@ -573,7 +572,7 @@ class Branch(models.Model):
WARNING: the calling method should acquire a lock
for the module to not mix checkouts in different threads/processes.
"""
- logger.debug("Updating '%s.%s' (in '%s')..." % (self.module.name, self.name, self.co_path))
+ logger.debug("Updating “%s.%s” (in “%s”)…", self.module.name, self.name, self.co_path)
self._repo.update()
def commit_po(self, po_file, domain, language, author):
@@ -625,6 +624,7 @@ DOMAIN_TYPE_CHOICES = (
('doc', 'Documentation')
)
+
class Domain(models.Model):
POT_METHOD_CHOICES = (
('auto', 'auto detected'),
@@ -647,13 +647,21 @@ class Domain(models.Model):
pot_params = models.CharField(
max_length=100, blank=True, help_text="Only for shell or URL methods"
)
- extra_its_dirs = models.TextField(blank=True,
- help_text="colon-separated directories containing extra .its/.loc files for gettext")
- linguas_location = models.CharField(max_length=50, blank=True,
- help_text="""Use 'no' for no LINGUAS check, or path/to/file#variable for a non-standard location.
- 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="""pogrep filter to strip po file from unprioritized strings (format: location|string, "-"
for no filter)""")
+ extra_its_dirs = models.TextField(
+ blank=True,
+ help_text="colon-separated directories containing extra .its/.loc files for gettext"
+ )
+ 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)"
+ )
+ 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)"
+ )
# 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='+')
branch_to = models.ForeignKey(Branch, null=True, blank=True, on_delete=models.PROTECT, related_name='+')
@@ -677,16 +685,14 @@ class Domain(models.Model):
if self.name[:2] == 'po':
# e.g. replace po by gimp (for ui), or po-plugins by gimp-plugins
return self.module.name + self.name[2:]
- elif self.name == 'help':
+ if self.name == 'help':
return "%s-help" % self.module.name
- else:
- return self.name
+ return self.name
def get_description(self):
if self.description:
return _(self.description)
- else:
- return self.potbase()
+ return self.potbase()
def has_standard_location(self):
return (self.dtype == 'ui' and self.layout == 'po/{lang}.po') or (
@@ -717,6 +723,7 @@ class Domain(models.Model):
[(lang, lang_file), ...] -> lang_file as Path.
"""
flist = []
+
def extract_lang(path):
path_str = str(path)
start = len(str(base_path)) + self.layout.find('{lang}') + 1
@@ -819,10 +826,9 @@ class Domain(models.Model):
if errors:
return "", errors
- elif not potfile.exists():
+ if not potfile.exists():
return "", (("error", gettext_noop("Unable to generate POT file")),)
- else:
- return potfile, ()
+ return potfile, ()
def get_xgettext_command(self, branch):
pot_command = ['xgettext',
@@ -906,22 +912,29 @@ class Domain(models.Model):
file_path = self.linguas_location
linguas_file = utils.MakefileWrapper(branch, base_path / file_path)
langs = linguas_file.read_variable(variable)
- return {'langs': langs.split() if langs is not None else None,
- 'error': gettext_noop("Entry for this language is not present in %(var)s variable in
%(file)s file." % {
- 'var': variable, 'file': file_path})}
+ return {
+ 'langs': langs.split() if langs is not None else None,
+ 'error': gettext_noop(
+ "Entry for this language is not present in %(var)s variable in %(file)s file." % {
+ 'var': variable, 'file': file_path
+ }
+ )
+ }
# Standard linguas location
if self.dtype == 'ui':
return utils.get_ui_linguas(branch, self.base_dir)
- elif self.dtype == 'doc':
+ if self.dtype == 'doc':
return utils.get_doc_linguas(branch, self.base_dir)
- else:
- raise ValueError("Domain dtype should be one of 'ui', 'doc'")
+ raise ValueError("Domain dtype should be one of 'ui', 'doc'")
+
RELEASE_STATUS_CHOICES = (
('official', 'Official'),
('unofficial', 'Unofficial'),
('xternal', 'External')
)
+
+
class Release(models.Model):
name = models.SlugField(max_length=20)
description = models.CharField(max_length=50)
@@ -945,12 +958,14 @@ class Release(models.Model):
def excluded_domains(self):
# Compute domains which doesn't apply for this release due to limited domain
# (by branch_from/branch_to).
- limited_stats = Statistics.objects.select_related('branch', 'domain'
- ).filter(branch__releases=self
- ).filter(language__isnull=True
- ).filter(models.Q(domain__branch_from__isnull=False) |
- models.Q(domain__branch_to__isnull=False))
- return set([st.domain for st in limited_stats if not st.branch.has_domain(st.domain)])
+ limited_stats = Statistics.objects.select_related(
+ 'branch', 'domain'
+ ).filter(
+ branch__releases=self
+ ).filter(
+ language__isnull=True
+ ).filter(models.Q(domain__branch_from__isnull=False) | models.Q(domain__branch_to__isnull=False))
+ return {st.domain for st in limited_stats if not st.branch.has_domain(st.domain)}
@classmethod
def total_by_releases(cls, dtype, releases):
@@ -987,19 +1002,24 @@ class Release(models.Model):
stats[locale]['stats'][releases.index(rel)] = line['trans']
# Compute percentages
- def perc(x, y):
+ def compute_percentage(x, y):
return int(x / y * 100)
- for k in stats.keys():
- stats[k]['stats'] = list(map(perc, stats[k]['stats'], totals))
+
+ for k in stats:
+ stats[k]['stats'] = list(map(compute_percentage, stats[k]['stats'], totals))
stats[k]['diff'] = stats[k]['stats'][-1] - stats[k]['stats'][0]
return stats
def total_strings(self):
""" Returns the total number of strings in the release as a tuple (doc_total, ui_total) """
# Use pot stats to compute total sum
- qs = Statistics.objects.filter(branch__category__release=self, language__isnull=True
- ).exclude(domain__in=self.excluded_domains).values('domain__dtype'
- ).annotate(untrans=models.Sum('full_po__untranslated'))
+ qs = Statistics.objects.filter(
+ branch__category__release=self, language__isnull=True
+ ).exclude(
+ domain__in=self.excluded_domains
+ ).values(
+ 'domain__dtype'
+ ).annotate(untrans=models.Sum('full_po__untranslated'))
totals = Counter()
for line in qs:
totals[line['domain__dtype']] += line['untrans']
@@ -1008,13 +1028,19 @@ class Release(models.Model):
def total_part_for_all_langs(self):
""" Return total partial UI strings for each language """
total_part_ui_strings = {}
- all_ui_pots = Statistics.objects.select_related('part_po'
- ).exclude(domain__in=self.excluded_domains
- ).filter(language__isnull=True, branch__releases=self, domain__dtype='ui')
- all_ui_stats = Statistics.objects.select_related('part_po', 'language'
- ).exclude(domain__in=self.excluded_domains
- ).filter(language__isnull=False, branch__releases=self, domain__dtype='ui'
- ).values('branch_id', 'domain_id', 'language__locale', 'part_po__translated', 'part_po__fuzzy',
'part_po__untranslated')
+ all_ui_pots = Statistics.objects.select_related(
+ 'part_po'
+ ).exclude(
+ domain__in=self.excluded_domains
+ ).filter(language__isnull=True, branch__releases=self, domain__dtype='ui')
+ all_ui_stats = Statistics.objects.select_related(
+ 'part_po', 'language'
+ ).exclude(
+ domain__in=self.excluded_domains
+ ).filter(
+ language__isnull=False, branch__releases=self, domain__dtype='ui'
+ ).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)
@@ -1028,14 +1054,22 @@ class Release(models.Model):
""" For partial UI stats, the total number can differ from lang to lang, so we
are bound to iterate each stats to sum it """
if all_pots is None:
- all_pots = Statistics.objects.select_related('part_po'
- ).exclude(domain__in=self.excluded_domains
- ).filter(language__isnull=True, branch__releases=self, domain__dtype='ui')
+ all_pots = Statistics.objects.select_related(
+ 'part_po'
+ ).exclude(
+ domain__in=self.excluded_domains
+ ).filter(language__isnull=True, branch__releases=self, domain__dtype='ui')
if all_stats_d is None:
- all_stats = Statistics.objects.select_related('part_po', 'language'
- ).exclude(domain__in=self.excluded_domains
- ).filter(language=lang, branch__releases=self, domain__dtype='ui'
- ).values('branch_id', 'domain_id', 'language__locale', 'part_po__translated',
'part_po__fuzzy', 'part_po__untranslated')
+ all_stats = Statistics.objects.select_related(
+ 'part_po', 'language'
+ ).exclude(
+ domain__in=self.excluded_domains
+ ).filter(
+ language=lang, branch__releases=self, domain__dtype='ui'
+ ).values(
+ 'branch_id', 'domain_id', 'language__locale', 'part_po__translated',
+ 'part_po__fuzzy', 'part_po__untranslated'
+ )
all_stats_d = {
"%d-%d-%s" % (st['branch_id'], st['domain_id'], st['language__locale']): sum(filter(
None, [st['part_po__translated'], st['part_po__fuzzy'], st['part_po__untranslated']]
@@ -1137,23 +1171,32 @@ class Release(models.Model):
stats[locale]['doc']['translated_perc'] = int(100 * row['trans'] / total_docstrings)
stats[locale]['doc']['fuzzy_perc'] = int(100 * row['fuzzy'] / total_docstrings)
stats[locale]['doc']['untranslated_perc'] = int(
- 100 * stats[locale]['doc']['untranslated'] / total_docstrings)
+ 100 * stats[locale]['doc']['untranslated'] / total_docstrings
+ )
if row['domain__dtype'] == 'ui':
stats[locale]['ui']['translated'] = row['trans']
stats[locale]['ui']['fuzzy'] = row['fuzzy']
stats[locale]['ui']['untranslated'] = total_uistrings - (row['trans'] + row['fuzzy'])
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'])
+ stats[locale]['ui_part']['untranslated'] = total_uistrings_part[locale] - (
+ row['trans_p'] + row['fuzzy_p']
+ )
if total_uistrings > 0:
stats[locale]['ui']['translated_perc'] = int(100 * row['trans'] / total_uistrings)
stats[locale]['ui']['fuzzy_perc'] = int(100 * row['fuzzy'] / total_uistrings)
stats[locale]['ui']['untranslated_perc'] = int(
100 * stats[locale]['ui']['untranslated'] / total_uistrings)
if total_uistrings_part.get(locale, 0) > 0:
- stats[locale]['ui_part']['translated_perc'] = int(100 * row['trans_p']
/total_uistrings_part[locale])
- stats[locale]['ui_part']['fuzzy_perc'] = int(100 * row['fuzzy_p']
/total_uistrings_part[locale])
- stats[locale]['ui_part']['untranslated_perc'] = int(100 *
stats[locale]['ui_part']['untranslated'] / total_uistrings_part[locale])
+ stats[locale]['ui_part']['translated_perc'] = int(
+ 100 * row['trans_p'] / total_uistrings_part[locale]
+ )
+ stats[locale]['ui_part']['fuzzy_perc'] = int(
+ 100 * row['fuzzy_p'] / total_uistrings_part[locale]
+ )
+ stats[locale]['ui_part']['untranslated_perc'] = int(
+ 100 * stats[locale]['ui_part']['untranslated'] / total_uistrings_part[locale]
+ )
results = list(stats.values())
# Sort by most translated first
@@ -1175,10 +1218,11 @@ class Release(models.Model):
partial = False
if dtype == "ui-part":
dtype, partial = "ui", True
- pot_stats = Statistics.objects.exclude(domain__in=self.excluded_domains
- ).filter(language=None, branch__releases=self, domain__dtype=dtype, full_po__isnull=False)
- po_stats = dict([("%s-%s" % (st.branch_id, st.domain_id), st)
- for st in Statistics.objects.filter(language=lang, branch__releases=self,
domain__dtype=dtype)])
+ pot_stats = Statistics.objects.exclude(
+ domain__in=self.excluded_domains
+ ).filter(language=None, branch__releases=self, domain__dtype=dtype, full_po__isnull=False)
+ po_stats = {f"{st.branch_id}-{st.domain_id}": st
+ for st in Statistics.objects.filter(language=lang, branch__releases=self,
domain__dtype=dtype)}
lang_files = []
last_modif_date = datetime(1970, 1, 1)
# Create list of files
@@ -1255,54 +1299,47 @@ class PoFile(models.Model):
def pot_size(self, words=False):
if words:
return self.translated_words + self.fuzzy_words + self.untranslated_words
- else:
- return self.translated + self.fuzzy + self.untranslated
+ return self.translated + self.fuzzy + self.untranslated
def fig_count(self):
""" If stat of a document type, get the number of figures in the document """
- return self.figures and len(self.figures) or 0
+ return len(self.figures) if self.figures else 0
def tr_percentage(self):
pot_size = self.pot_size()
if pot_size == 0:
return 0
- else:
- return int(100*self.translated/pot_size)
+ return int(100 * self.translated / pot_size)
def tr_word_percentage(self):
pot_size = self.pot_size(words=True)
if pot_size == 0:
return 0
- else:
- return int(100*self.translated_words/pot_size)
+ return int(100 * self.translated_words / pot_size)
def fu_percentage(self):
pot_size = self.pot_size()
if pot_size == 0:
return 0
- else:
- return int(100*self.fuzzy/pot_size)
+ return int(100 * self.fuzzy / pot_size)
def fu_word_percentage(self):
pot_size = self.pot_size(words=True)
if pot_size == 0:
return 0
- else:
- return int(100*self.fuzzy_words/pot_size)
+ return int(100 * self.fuzzy_words / pot_size)
def un_percentage(self):
pot_size = self.pot_size()
if pot_size == 0:
return 0
- else:
- return int(100*self.untranslated/pot_size)
+ return int(100 * self.untranslated / pot_size)
def un_word_percentage(self):
pot_size = self.pot_size(words=True)
if pot_size == 0:
return 0
- else:
- return int(100*self.untranslated_words/pot_size)
+ return int(100 * self.untranslated_words / pot_size)
def update_stats(self):
stats = utils.po_file_stats(Path(self.full_path))
@@ -1369,48 +1406,47 @@ class Statistics(models.Model):
""" Returns the type of the domain (ui, docbook, mallard) """
if self.domain.dtype == "ui":
return "ui"
- else:
- return self.domain.doc_format(self.branch).format
+ return self.domain.doc_format(self.branch).format
def tr_percentage(self, scope='full'):
if scope == 'full' and self.full_po:
return self.full_po.tr_percentage()
- elif scope == 'part' and self.part_po:
+ if scope == 'part' and self.part_po:
return self.part_po.tr_percentage()
return 0
def tr_word_percentage(self, scope='full'):
if scope == 'full' and self.full_po:
return self.full_po.tr_word_percentage()
- elif scope == 'part' and self.part_po:
+ if scope == 'part' and self.part_po:
return self.part_po.tr_word_percentage()
return 0
def fu_percentage(self, scope='full'):
if scope == 'full' and self.full_po:
return self.full_po.fu_percentage()
- elif scope == 'part' and self.part_po:
+ if scope == 'part' and self.part_po:
return self.part_po.fu_percentage()
return 0
def fu_word_percentage(self, scope='full'):
if scope == 'full' and self.full_po:
return self.full_po.fu_word_percentage()
- elif scope == 'part' and self.part_po:
+ if scope == 'part' and self.part_po:
return self.part_po.fu_word_percentage()
return 0
def un_percentage(self, scope='full'):
if scope == 'full' and self.full_po:
return self.full_po.un_percentage()
- elif scope == 'part' and self.part_po:
+ if scope == 'part' and self.part_po:
return self.part_po.un_percentage()
return 0
def un_word_percentage(self, scope='full'):
if scope == 'full' and self.full_po:
return self.full_po.un_word_percentage()
- elif scope == 'part' and self.part_po:
+ if scope == 'part' and self.part_po:
return self.part_po.un_word_percentage()
return 0
@@ -1420,8 +1456,7 @@ class Statistics(models.Model):
'lang_name': _(self.language.name),
'lang_locale': self.language.locale
}
- else:
- return "pot file"
+ return "pot file"
@property
def module_name(self):
@@ -1436,9 +1471,10 @@ class Statistics(models.Model):
def filename(self, potfile=False, reduced=False):
if not self.is_pot_stats() and not potfile:
- return "%s.%s.%s.%spo" % (self.domain.potbase(), self.branch.name_escaped, self.language.locale,
reduced and "reduced." or "")
- else:
- return "%s.%s.%spot" % (self.domain.potbase(), self.branch.name_escaped, reduced and "reduced."
or "")
+ return "%s.%s.%s.%spo" % (
+ self.domain.potbase(), self.branch.name_escaped, self.language.locale, reduced and
"reduced." or ""
+ )
+ return "%s.%s.%spot" % (self.domain.potbase(), self.branch.name_escaped, reduced and "reduced." or
"")
def pot_text(self):
if not self.full_po:
@@ -1446,7 +1482,7 @@ class Statistics(models.Model):
pot_size = self.full_po.pot_size()
pot_words_size = self.full_po.pot_size(words=True)
fig_count = self.full_po.fig_count()
- """ Return stat table header: 'POT file (n messages) - updated on ??-??-???? tz' """
+ # 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
@@ -1584,7 +1620,9 @@ class Statistics(models.Model):
self.save()
# Try to compute a reduced po file
- if (self.full_po.fuzzy + self.full_po.untranslated) > 0 and not self.branch.is_archive_only()
and self.domain.red_filter != '-':
+ if ((self.full_po.fuzzy + self.full_po.untranslated) > 0
+ and not self.branch.is_archive_only()
+ and self.domain.red_filter != '-'):
# Generate partial_po and store partial stats
if self.full_po.path.endswith('.pot'):
part_po_path = self.full_po.full_path.with_suffix(".reduced.pot")
@@ -1594,7 +1632,8 @@ class Statistics(models.Model):
part_stats = utils.po_file_stats(part_po_path)
if (part_stats['translated'] + part_stats['fuzzy'] + part_stats['untranslated'] ==
stats['translated'] + stats['fuzzy'] + stats['untranslated']):
- # No possible gain, set part_po = full_po so it is possible to compute complete stats at
database level
+ # No possible gain, set part_po = full_po so it is possible to
+ # compute complete stats at database level
part_po_equals_full_po()
part_po_path.unlink()
else:
@@ -1621,7 +1660,7 @@ class Statistics(models.Model):
for err in fig_errors:
self.set_error(*err)
- logger.debug("%s:\n%s" % (self.language, str(stats)))
+ logger.debug("%s:\n%s", self.language, str(stats))
def set_error(self, tp, description):
Information.objects.create(statistics=self, type=tp, description=description)
@@ -1630,7 +1669,9 @@ class Statistics(models.Model):
""" Return a message of type 1.'error', or 2.'warn, or 3.'warn """
error = None
for e in self.information_set.all():
- if not error or e.type in ('error', 'error-ext') or (e.type in ('warn','warn-ext') and
error.type == 'info'):
+ if (not error
+ or e.type in ('error', 'error-ext')
+ or (e.type in ('warn', 'warn-ext') and error.type == 'info')):
error = e
return error
@@ -1670,6 +1711,7 @@ class Statistics(models.Model):
}
"""
# Import here to prevent a circular dependency
+ # pylint: disable=import-outside-toplevel
from vertimus.models import State, Action
scope = "full"
@@ -1683,18 +1725,24 @@ class Statistics(models.Model):
# Sorted by module to allow grouping ('fake' stats)
pot_stats = Statistics.objects.select_related('domain', 'branch__module', 'full_po', 'part_po')
if release:
- pot_stats = pot_stats.filter(language=None, branch__releases=release,
domain__dtype=dtype).order_by('branch__module__name')
- categ_names = dict([(cat.branch_id, cat.name.name)
- for cat in Category.objects.select_related('branch',
'name').filter(release=release)])
+ pot_stats = pot_stats.filter(
+ language=None, branch__releases=release, domain__dtype=dtype
+ ).order_by('branch__module__name')
+ categ_names = {
+ cat.branch_id: cat.name.name
+ for cat in Category.objects.select_related('branch', 'name').filter(release=release)
+ }
else:
pot_stats = pot_stats.filter(language=None, domain__dtype=dtype).order_by('branch__module__name')
tr_stats = Statistics.objects.select_related('domain', 'language', 'branch__module', 'full_po',
'part_po')
if release:
- tr_stats = tr_stats.filter(language=lang, branch__releases=release,
domain__dtype=dtype).order_by('branch__module__id')
+ tr_stats = tr_stats.filter(
+ language=lang, branch__releases=release, domain__dtype=dtype
+ ).order_by('branch__module__id')
else:
tr_stats = tr_stats.filter(language=lang, domain__dtype=dtype).order_by('branch__module__id')
- tr_stats_dict = dict([("%d-%d" % (st.branch.id, st.domain.id),st) for st in tr_stats])
+ tr_stats_dict = {f"{st.branch_id}-{st.domain_id}": st for st in tr_stats}
infos_dict = Information.get_info_dict(lang)
@@ -1704,11 +1752,11 @@ class Statistics(models.Model):
vt_states = vt_states.filter(language=lang, branch__releases=release, domain__dtype=dtype)
else:
vt_states = vt_states.filter(language=lang, domain__dtype=dtype)
- vt_states_dict = dict([("%d-%d" % (vt.branch.id, vt.domain.id),vt) for vt in vt_states])
+ vt_states_dict = {f"{vt.branch_id}-{vt.domain_id}": vt for vt in vt_states}
# Get comments from last action of State objects
actions = Action.objects.filter(state_db__in=vt_states, comment__isnull=False).order_by('created')
- actions_dict = dict([(act.state_db_id, act) for act in actions])
+ actions_dict = {action.state_db_id: action for action in actions}
for vt_state in vt_states_dict.values():
if vt_state.id in actions_dict:
vt_state.last_comment = actions_dict[vt_state.id].comment
@@ -1719,7 +1767,7 @@ class Statistics(models.Model):
categ_key = "Default"
if release:
categ_key = categ_names.get(stat.branch_id)
- domname = stat.domain.description and _(stat.domain.description) or ""
+ domname = _(stat.domain.description) if stat.domain.description else ""
branchname = stat.branch.name
module = stat.branch.module
if categ_key not in stats['categs']:
@@ -1761,8 +1809,12 @@ class Statistics(models.Model):
# Here we add the 2nd or more stat to the same module-branch
if len(stats['categs'][categ_key]['modules'][module][branchname]) == 2:
# Create a fake statistics object for module summary
- stats['categs'][categ_key]['modules'][module][branchname][0][1] =
FakeSummaryStatistics(stat.domain.module, stat.branch, dtype)
-
stats['categs'][categ_key]['modules'][module][branchname][0][1].trans(stats['categs'][categ_key]['modules'][module][branchname][1][1])
+ stats['categs'][categ_key]['modules'][module][branchname][0][1] = FakeSummaryStatistics(
+ stat.domain.module, stat.branch, dtype
+ )
+ stats['categs'][categ_key]['modules'][module][branchname][0][1].trans(
+ stats['categs'][categ_key]['modules'][module][branchname][1][1]
+ )
stats['categs'][categ_key]['modules'][module][branchname].append([domname, stat])
stats['categs'][categ_key]['modules'][module][branchname][0][1].trans(stat)
@@ -1773,13 +1825,13 @@ class Statistics(models.Model):
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 key, categ in stats['categs'].items():
+ 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'])
# Sort domains
for mod in categ['modules'].values():
- for branch, doms in mod.items():
+ for doms in mod.values():
doms.sort(key=itemgetter(0))
# Sort errors
stats['all_errors'].sort()
@@ -1861,20 +1913,17 @@ class FakeSummaryStatistics:
def tr_percentage(self, scope='full'):
if self.pot_size() == 0:
return 0
- else:
- return int(100*self._translated/self.pot_size())
+ return int(100 * self._translated / self.pot_size())
def fu_percentage(self, scope='full'):
if self.pot_size() == 0:
return 0
- else:
- return int(100*self._fuzzy/self.pot_size())
+ return int(100 * self._fuzzy / self.pot_size())
def un_percentage(self, scope='full'):
if self.pot_size() == 0:
return 0
- else:
- return int(100*self._untranslated/self.pot_size())
+ return int(100*self._untranslated/self.pot_size())
class StatisticsArchived(models.Model):
@@ -1891,15 +1940,18 @@ class StatisticsArchived(models.Model):
class Meta:
db_table = 'statistics_archived'
+
INFORMATION_TYPE_CHOICES = (
('info', 'Information'),
- ('warn','Warning'),
- ('error','Error'),
+ ('warn', 'Warning'),
+ ('error', 'Error'),
# Type of warning/error external to po file itself (LINGUAS, images, etc.)
# po files containing these are always rechecked
- ('warn-ext','Warning (external)'),
- ('error-ext','Error (external)')
+ ('warn-ext', 'Warning (external)'),
+ ('error-ext', 'Error (external)')
)
+
+
class Information(models.Model):
statistics = models.ForeignKey('Statistics', on_delete=models.CASCADE)
# Priority of a stats message
@@ -1935,7 +1987,7 @@ class Information(models.Model):
text = _(text)
- #FIXME: if multiple substitutions, works only if order of %s is unchanged in translated string
+ # 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)
return text
@@ -1948,6 +2000,7 @@ class Information(models.Model):
}
return link
+
class InformationArchived(models.Model):
statistics = models.ForeignKey('StatisticsArchived', on_delete=models.CASCADE)
# Priority of a stats message
@@ -1965,5 +2018,6 @@ def try_int(value):
except ValueError:
return value
+
def split_name(value):
return tuple(try_int(part) for part in re.split(r"[-\.]", value))
diff --git a/stats/tests/tests.py b/stats/tests/tests.py
index 9b35e72a..12d72f2f 100644
--- a/stats/tests/tests.py
+++ b/stats/tests/tests.py
@@ -83,6 +83,14 @@ class ModuleTestCase(TestCase):
self.assertEqual(response.status_code, 404)
def test_module_bugs_reporting(self):
+ self.assertEqual(
+ self.mod.get_bugs_i18n_url(),
+ 'https://gitlab.gnome.org/GNOME/gnome-hello/issues/?state=opened&label_name[]=8.%20Translation'
+ )
+ 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'
+ )
response = self.client.get(reverse('module', args=[self.mod.name]))
self.assertContains(
response,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]