Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 77 additions & 24 deletions apps/dashboards/readouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
'AND engrev.is_ready_for_localization '
'AND engrev.significance>=%s) ')


def _cursor():
"""Return a DB cursor for reading."""
return connections[router.db_for_read(Document)].cursor()
Expand Down Expand Up @@ -148,19 +149,14 @@ def single_result(sql, params):
current_revision__isnull=False,
is_localizable=True,
latest_localizable_revision__isnull=False)
total_docs = total.filter(is_template=False).count()
total_docs = (total.filter(is_template=False)
.filter(category__in=(10, 20, 60))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This removes articles in categories 30, 40 and 50 from the All Knowledge Base Articles total.

.count())
total_navigation = (total.filter(is_template=False)
.filter(category=50)
.count())
total_templates = total.filter(is_template=True).count()

# How many approved, up-to-date documents are there in German that have
# parents? Even though users are technically allowed to translate revisions
# that aren't marked as ready-for-l10n, we restrict this set to ready-to-
# localize documents because we restrict the denominator to those.
translated = Document.uncached.filter(
locale=locale,
is_archived=False,
current_revision__isnull=False,
parent__isnull=False,
parent__latest_localizable_revision__isnull=False)
# Translations whose based_on revision has no >10-significance, ready-for-
# l10n revisions after it. It *might* be possible to do this with the ORM
# by passing wheres and tables to extra():
Expand All @@ -176,12 +172,17 @@ def single_result(sql, params):
'AND engdoc.is_localizable '
'AND NOT EXISTS ' +
ANY_SIGNIFICANT_UPDATES)
# TODO: Optimize by running the above query just once and separating the
# templates from the non-templates with a GROUP BY.
translated_docs = single_result(up_to_date_translation_count,
(locale, False, MEDIUM_SIGNIFICANCE))
translated_templates = single_result(up_to_date_translation_count,
(locale, True, MEDIUM_SIGNIFICANCE))
translated_docs = single_result(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer this indentation style so much now :-). Much more predictable.

up_to_date_translation_count +
'AND transdoc.category in (10, 20, 60)',
(locale, False, MEDIUM_SIGNIFICANCE))
translated_navigation = single_result(
up_to_date_translation_count +
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took out this comment because it's more complicated to do now that there are three groupings and the groupings aren't easily handled with a group_by.

'AND transdoc.category = 50',
(locale, False, MEDIUM_SIGNIFICANCE))
translated_templates = single_result(
up_to_date_translation_count,
(locale, True, MEDIUM_SIGNIFICANCE))

# Of the top 20 most visited English articles, how many have up-to-date
# translations into German?
Expand Down Expand Up @@ -222,6 +223,14 @@ def single_result(sql, params):
description=_('How many of the approved templates '
'which allow translations have an approved '
'translation into this language')),
'navigation': dict(
title=_('Navigation Articles'),
url='#' + NavigationTranslationsReadout.slug,
numerator=translated_navigation, denominator=total_navigation,
percent=percent_or_100(translated_navigation, total_navigation),
description=_('How many of the approved navigation articles '
'which allow translations have an approved '
'translation into this language')),
'all': dict(
title=_('All Knowledge Base Articles'),
numerator=translated_docs, denominator=total_docs,
Expand Down Expand Up @@ -443,6 +452,48 @@ def _format_row(self, (eng_slug, eng_title, slug, title, significance,
eng_title, slug, title, None, significance, needs_review)


class NavigationTranslationsReadout(Readout):
"""Readout for navigation articles in non-default languages

Shows the navigation articles even if there are no translations of
them yet. This draws attention to navigation articles that we
should drop everything to translate.

"""
title = _lazy(u'Navigation Articles')
slug = 'template-navigation'
details_link_text = _lazy(u'All navigation articles...')
column3_label = ''
modes = []

def _query_and_params(self, max):
return (
'SELECT engdoc.slug, engdoc.title, transdoc.slug, '
'transdoc.title, ' +
MOST_SIGNIFICANT_CHANGE_READY_TO_TRANSLATE + ', ' +
NEEDS_REVIEW +

'FROM wiki_document engdoc '
'LEFT JOIN wiki_document transdoc ON '
'transdoc.parent_id=engdoc.id '
'AND transdoc.locale=%s '
'WHERE engdoc.locale=%s '
'AND engdoc.is_localizable '
'AND NOT engdoc.is_archived '
'AND engdoc.latest_localizable_revision_id IS NOT NULL '
'AND engdoc.category = 50 '
'AND NOT engdoc.is_template '
'ORDER BY COALESCE(transdoc.title, engdoc.title) ASC ' +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yikes!


self._limit_clause(max),
(self.locale, settings.WIKI_DEFAULT_LANGUAGE))

def _format_row(self, (eng_slug, eng_title, slug, title, significance,
needs_review)):
return _format_row_with_out_of_dateness(self.locale, eng_slug,
eng_title, slug, title, None, significance, needs_review)


class UntranslatedReadout(Readout):
title = _lazy(u'Untranslated')
short_title = _lazy(u'Untranslated')
Expand All @@ -455,9 +506,9 @@ def _query_and_params(self, max):
# against an inner query returning translated docs, and the left join
# yielded a faster-looking plan (on a production corpus).
#
# Find non-archived, localizable documents having at least one ready-
# for-localization revision. Of those, show the ones that have no
# translation.
# Find non-archived, localizable documents in categories 10,
# 20 and 60 having at least one ready- for-localization
# revision. Of those, show the ones that have no translation.
return ('SELECT parent.slug, parent.title, '
'wiki_revision.reviewed, dashboards_wikidocumentvisits.visits '
'FROM wiki_document parent '
Expand All @@ -470,6 +521,7 @@ def _query_and_params(self, max):
'dashboards_wikidocumentvisits.period=%s '
'WHERE '
'translated.id IS NULL AND parent.is_localizable AND '
'parent.category in (10, 20, 60) AND '
'parent.locale=%s AND NOT parent.is_archived '
+ self._order_clause() + self._limit_clause(max),
(self.locale, THIS_WEEK, settings.WIKI_DEFAULT_LANGUAGE))
Expand Down Expand Up @@ -564,7 +616,8 @@ def _query_and_params(self, max):
'AND dashboards_wikidocumentvisits.period=%s '
# We needn't check is_localizable, since the models ensure every
# document with translations has is_localizable set.
'WHERE transdoc.locale=%s AND NOT transdoc.is_archived '
'WHERE transdoc.locale=%s AND NOT transdoc.is_archived AND '
'transdoc.category in (10, 20, 60) '
+ self._order_clause() + self._limit_clause(max),
(MEDIUM_SIGNIFICANCE, self._max_significance, THIS_WEEK,
self.locale))
Expand Down Expand Up @@ -788,9 +841,9 @@ def _format_row(self, (slug, title, comment, visits)):

# L10n Dashboard tables that have their own whole-page views:
L10N_READOUTS = SortedDict((t.slug, t) for t in
[MostVisitedTranslationsReadout, TemplateTranslationsReadout,
UntranslatedReadout, OutOfDateReadout, NeedingUpdatesReadout,
UnreviewedReadout])
[MostVisitedTranslationsReadout, NavigationTranslationsReadout,
TemplateTranslationsReadout, UntranslatedReadout,
OutOfDateReadout, NeedingUpdatesReadout, UnreviewedReadout])

# Contributors ones:
CONTRIBUTOR_READOUTS = SortedDict((t.slug, t) for t in
Expand Down
1 change: 1 addition & 0 deletions apps/dashboards/templates/dashboards/localization.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ <h1>{{ title }}</h1>
{% set rows = overview_rows() %}
{{ overview_section(readouts, ((rows['most-visited'], True),
(rows['templates'], True),
(rows['navigation'], True),
(rows['all'], False))) }}

{% for readout in readouts.itervalues() %}
Expand Down
95 changes: 94 additions & 1 deletion apps/dashboards/tests/test_readouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
TemplateTranslationsReadout, overview_rows,
MostVisitedTranslationsReadout,
UnreadyForLocalizationReadout,
NeedsChangesReadout)
NeedsChangesReadout,
NavigationTranslationsReadout)
from sumo.tests import TestCase
from wiki.models import (MAJOR_SIGNIFICANCE, MEDIUM_SIGNIFICANCE,
TYPO_SIGNIFICANCE)
Expand Down Expand Up @@ -50,6 +51,28 @@ def test_counting_unready_templates(self):
r.save()
eq_(1, overview_rows('de')['templates']['denominator'])

def test_counting_unready_navigation(self):
"""Navigation articles without ready-for-l10n rev shouldn't count in
total.

"""
# Make a navigation doc with an approved but not-ready-for-l10n rev:
r = revision(document=document(title='smoo',
category=50,
is_localizable=True,
is_template=False,
save=True),
is_ready_for_localization=False,
is_approved=True,
save=True)

# It shouldn't show up in the total:
eq_(0, overview_rows('de')['navigation']['denominator'])

r.is_ready_for_localization = True
r.save()
eq_(1, overview_rows('de')['navigation']['denominator'])

def test_counting_unready_docs(self):
"""Docs without a ready-for-l10n rev shouldn't count in total."""
# Make a doc with an approved but not-ready-for-l10n rev:
Expand Down Expand Up @@ -104,6 +127,35 @@ def test_templates_and_docs_disjunct(self):
eq_(0, overview_rows('de')['all']['numerator'])
eq_(0, overview_rows('de')['all']['denominator'])

def test_all_articles_doesnt_have_30_40_50(self):
"""Make sure All Articles doesn't have 30, 40, and 50 articles"""
t = translated_revision(is_approved=True, save=True)
# It shows up in All when it's a normal doc:
eq_(1, overview_rows('de')['all']['numerator'])
eq_(1, overview_rows('de')['all']['denominator'])

# ...but not when it's a navigation article:
t.document.parent.title = t.document.title = 'thing'
t.document.parent.category = t.document.category = 50
t.document.parent.save()
t.document.save()
eq_(0, overview_rows('de')['all']['numerator'])
eq_(0, overview_rows('de')['all']['denominator'])

# ...or administration:
t.document.parent.category = t.document.category = 40
t.document.parent.save()
t.document.save()
eq_(0, overview_rows('de')['all']['numerator'])
eq_(0, overview_rows('de')['all']['denominator'])

# ...or how to contribute:
t.document.parent.category = t.document.category = 30
t.document.parent.save()
t.document.save()
eq_(0, overview_rows('de')['all']['numerator'])
eq_(0, overview_rows('de')['all']['denominator'])

def test_not_counting_outdated(self):
"""Out-of-date translations shouldn't count as "done".

Expand Down Expand Up @@ -328,6 +380,47 @@ def test_untranslated(self):
eq_(unicode(row['status']), 'Translation Needed')


class NavigationTranslationsTests(ReadoutTestCase):
"""Tests for the Navigation Translations readout"""

readout = NavigationTranslationsReadout

def test_not_navigation(self):
"""Documents not in navigation shouldn't show up in the list."""
t = translated_revision(is_approved=False, save=True)
t.document.category = 10
t.document.save()

t = translated_revision(is_approved=False, save=True)
t.document.category = 20
t.document.save()

t = translated_revision(is_approved=False, save=True)
t.document.category = 30
t.document.save()

t = translated_revision(is_approved=False, save=True)
t.document.category = 40
t.document.save()

t = translated_revision(is_approved=False, save=True)
t.document.category = 60
t.document.save()

self.assertRaises(IndexError, self.row)

def test_untranslated(self):
"""Assert untranslated navigation are labeled as such."""
d = document(title='Foo', category=50, save=True)
untranslated = revision(is_approved=True,
is_ready_for_localization=True,
document=d,
save=True)
row = self.row()
eq_(row['title'], untranslated.document.title)
eq_(unicode(row['status']), u'Translation Needed')


class UnreadyTests(ReadoutTestCase):
"""Tests for UnreadyForLocalizationReadout"""

Expand Down