Merge lp:~adiroiban/launchpad/bug-583934 into lp:launchpad

Proposed by Adi Roiban
Status: Work in progress
Proposed branch: lp:~adiroiban/launchpad/bug-583934
Merge into: lp:launchpad
Diff against target: 500 lines (+299/-17)
9 files modified
lib/lp/translations/browser/configure.zcml (+8/-0)
lib/lp/translations/browser/pofile.py (+12/-4)
lib/lp/translations/configure.zcml (+8/-0)
lib/lp/translations/doc/pofile.txt (+15/-0)
lib/lp/translations/interfaces/pofile.py (+29/-11)
lib/lp/translations/interfaces/statistics.py (+84/-0)
lib/lp/translations/interfaces/webservice.py (+3/-0)
lib/lp/translations/model/pofile.py (+57/-2)
lib/lp/translations/stories/webservice/xx-pofile.txt (+83/-0)
To merge this branch: bzr merge lp:~adiroiban/launchpad/bug-583934
Reviewer Review Type Date Requested Status
Canonical Launchpad Engineering Pending
Review via email: mp+26965@code.launchpad.net

Commit message

Initial API for POFile and translation statistics attributes.

Description of the change

= Bug 583934 =
Right now there is on API for exporting POFile attributes

== Proposed fix ==
This branch is only for POFile attributes and is part of the Translations Reporting API (https://dev.launchpad.net/Translations/Specs/ReportingAPI).

The main part of this branch is to define a translation statistics interface that is suitable for the API.

== Pre-implementation notes ==
IRC chat start from here http://irclogs.ubuntu.com/2010/06/07/%23launchpad-reviews.html#t17:21

Outstanding issues:
 * due to message sharing translation statistics are not updated in real time
   * this affects the last_updated, last_translator, untranslated ... etc
   * still, this branch is independent of this problem and we can go on and define/design the API

 * due to future work for ubuntu-upstream message sharing there may be changes in the way ubuntu changes from upstream are computed/represented.
  * we don't know how exactly this changes will look so maybe it is best to go on with this branch and make the changes only if they are required.

* Jeroen noted that (length - translated messages) is not always equal with untranslated messages, and we should count translation credits and outer automatic translations as "translated messages"

=== Launchpad Translations Reporting API Call - 2010-06-23 ===

Real-time stats update:

• There will be improvements, but they will most probably be gradual
• Not dependent on the message sharing script to complete
• For the current intended consumers the current delay is ok
• Only the series the translator is working will be updated in
real-time, other shared series will take a week to update. We assume
that the translator will mostly work on the translation focus series.

IRosettaStats:

• Not the best interface for statistics, the reason being because it is
parameterised on language
• The language parameter is only used for IPOTemplate, and in turn that
interface is only using message count
• Jeroen: it was probably done this way to a llow a script to be very
generic. It might be worth removing the IRosettaStats interface (or just
the language parameter) and see what breaks on the test suite
• Adi: replacement proposal with ITranslationStats
• An intended improvement is to also provide more descriptive names to
the interface
• AGREED that we should be working on a new translations interface

ITranslationStats:

• Adi's proposal for a new interface for statistics as a replacement for
IRosettaStats
• Right now only used for counters, which is ok if they are attributes
of POFiles
• If we want it to be generic, it might need to be extended and require
a 'target' attribute. This 'target' would be then replaced by a
reference to a language, source package, distro series, etc.
• AGREED to have a stats object to implement the ITranslationStatistics
interface and provide a method on objects to query those statistics. As
an example, a POFile would have a 'stats' attribute instead of separate
attributes for each of the counts. No conclusion on the implementation
on how to link back the object to query with the returned statistics.
Henning would like to see a merge proposal with actual code for better
understanding. Adi will submit it first with the 'target' parameter in
ITranslationStatistics approach.

Other:

• Attribute names: translated_message_*_count - if they are in their own
class, then the 'translated_message' prefix is not needed.
• Recife branch to land roughly on October. Agreed to work on the devel
branch instead for API work in order not to be a blocker. We should go
ahead with an API that's easily extensible and exposes only the
essential counts (e.g. translated, untranslated, unreviewed) and not
worry with the Recife branch changes. When the Recife branch is merged
with trunk, then we can extend the API and expose the rest.
• Jeroen and Henninge happy with a weekly call to track progress and
implementation discussion. David to follow up with Adi (dropped from the
call in the last few minutes) if he'd be happy to.

ACTIONS:

• Adi to submit a merge proposal: experiment removing the language
parameter and fixing what breaks
• Adi to resubmit the existing POFile API merge proposal:
        ∘ replace IRosettaStats with the new ITranslationStats interface
        ∘ with the 'target' parameter
        ∘ for now expose only attributes not dependent on the
        Ubuntu/upstream message sharing work (i.e. no "new" or "chaged"
        counts)
• David to set up a weekly call with Adi, Henning and Jeroen

== Implementation details ==

=== IRosettaStats problem ===
The current IRosettaStats interface is not suitable for API export since the number of various statistic are returned using a method that takes a language as a parameter.

From my point of view IRosettaStats needs a complete rework, since that language parameter is only used for IPOTemplate, and IPOTemplates only implement the message_count part of this interface. I would say that we could change the IPOTemplate to not implement IRosettaStats and remove the language parameter from all IRosettaStats methods.

=== ITranslationStatistics ===
Since this rework of IRosettaStats require some time and thinking, to move things forwards I have started a new interface ITranslationStatistics

Right now ITranslationStatistics has only those attributes required by the API.
In the future (maibe LP Epic) maybe we could decide the new shape of translation statistics interface and extend it to replace the current IRosettaStats.

There will be different TranslationStatistics object implementing the ITranslationStatistics interface in order to agregate the statistics for various translation templates sets.

=== Choosing what attribute to export for the translations statistics ===

Right now IRosettaStats contains a lot of counters, some of them having only internal API usage.

I tried to choose the one that are relevant to end users and use names that will describe their values.
Still, translation_message_new_count is challenging as that "new" can have many meanings.
Mabye we should have "translation_message_new_in_launchpad_count" and "translation_message_changed_in_launchpad_count".

Below is the list of the attribute names that will be exported in the API:

* changed_count
* new_count
* total_count
* translated_count
* unreviewed_count
* language
* template

=== Lint warnings ===

Since the lint warnings were already in the code, I will fix them after this branch is approved in order to make the review process easier.

== Tests ==

./bin/test -t xx-pofile.txt

== Demo and Q/A ==
Point your browser to https://launchpad.dev/api/devel/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es

You should see the exported attributes.

There is also an „translation_statistics_link” attribute available at :
https://launchpad.dev/api/devel/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+statistics

This will export the ITranslationStatistics attributes

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/lp/translations/configure.zcml
  lib/lp/translations/browser/configure.zcml
  lib/lp/translations/browser/pofile.py
  lib/lp/translations/interfaces/pofile.py
  lib/lp/translations/interfaces/statistics.py
  lib/lp/translations/interfaces/webservice.py
  lib/lp/translations/model/pofile.py
  lib/lp/translations/stories/webservice/xx-pofile.txt

== Pyflakes notices ==

lib/lp/translations/interfaces/webservice.py
    17: 'IHasTranslationImports' imported but unused
    17: 'ITranslationImportQueue' imported but unused
    17: 'ITranslationImportQueueEntry' imported but unused
    22: 'IPOTemplate' imported but unused
    24: 'IPOFile' imported but unused
    26: 'ITranslationStatistics' imported but unused

== Pylint notices ==

lib/lp/translations/browser/pofile.py
    158: [E1002, POFileBaseView.initialize] Use super on an old style class
    682: [E1002, POFileTranslateView.initialize] Use super on an old style class

To post a comment you must log in.
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

We discussed how ongoing developments are going to affect some of these counts. A quick update on that: with the Recife work, the "changed count" and "new count" on an Ubuntu POFile can still work pretty much the way they did. The difference will be that they will compare the messages that are current in the Ubuntu POFile (each message either diverged or shared) to the shared messages on the upstream side. That way we still get useful numbers.

Another question is how we count "new" or "changed" in project POFiles, i.e. ones that are not part of Ubuntu. We'll no longer have upstream-vs-downstream information for those. We can count new/changed messages compared to the shared Ubuntu translations, and I think that will be useful. The meaning is very different from what we have now though.

lp:~adiroiban/launchpad/bug-583934 updated
10916. By Adi Roiban

Export POFile statistics as external object.

10917. By Adi Roiban

Add tests.

10918. By Adi Roiban

Add forgetten test file.

10919. By Adi Roiban

Add model tests.

Unmerged revisions

10919. By Adi Roiban

Add model tests.

10918. By Adi Roiban

Add forgetten test file.

10917. By Adi Roiban

Add tests.

10916. By Adi Roiban

Export POFile statistics as external object.

10915. By Adi Roiban

Update statistics docstring for pre-implementation chat.

10914. By Adi Roiban

Merge devel.

10913. By Adi Roiban

Export POFile attributes and TranslationsStatistics API.

10912. By Adi Roiban

Merge bug-525371.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/translations/browser/configure.zcml'
--- lib/lp/translations/browser/configure.zcml 2010-04-19 08:11:52 +0000
+++ lib/lp/translations/browser/configure.zcml 2010-07-05 21:28:29 +0000
@@ -187,6 +187,14 @@
187 layer="canonical.launchpad.layers.TranslationsLayer"/>187 layer="canonical.launchpad.layers.TranslationsLayer"/>
188 <facet188 <facet
189 facet="translations">189 facet="translations">
190 <browser:url
191 for="lp.translations.interfaces.statistics.ITranslationStatistics"
192 path_expression="string:+statistics"
193 attribute_to_parent="source"
194 rootsite="translations"/>
195 </facet>
196 <facet
197 facet="translations">
190 <browser:navigation198 <browser:navigation
191 module="lp.translations.browser.pofile"199 module="lp.translations.browser.pofile"
192 classes="200 classes="
193201
=== modified file 'lib/lp/translations/browser/pofile.py'
--- lib/lp/translations/browser/pofile.py 2010-06-17 08:28:33 +0000
+++ lib/lp/translations/browser/pofile.py 2010-07-05 21:28:29 +0000
@@ -14,6 +14,7 @@
14 'POFileTranslateView',14 'POFileTranslateView',
15 'POFileUploadView',15 'POFileUploadView',
16 'POFileView',16 'POFileView',
17 'POFileStatisticsView',
17 ]18 ]
1819
19import re20import re
@@ -63,12 +64,19 @@
63 usedfor = IPOFile64 usedfor = IPOFile
6465
65 def traverse(self, name):66 def traverse(self, name):
66 """Return the IPOMsgSet associated with the given name."""67 """Return the IPOMsgSet associated with the given name.
67 assert self.request.method in ['GET', 'HEAD', 'POST'], (68
68 'We only know about GET, HEAD, and POST')69 '+statistics' is used to retrieve the `ITranslationStatistics` entry,
70 while INTEGER to retrieve the specific `ITranslationMessage`.
71 """
72 assert self.request.method in ['GET', 'HEAD', 'PATCH', 'POST'], (
73 'We only know about GET, HEAD, PATCH and POST')
6974
70 try:75 try:
71 sequence = int(name)76 if name == u'+statistics':
77 return self.context.translation_statistics
78 else:
79 sequence = int(name)
72 except ValueError:80 except ValueError:
73 # The URL does not have a number to do the traversal.81 # The URL does not have a number to do the traversal.
74 raise NotFoundError(82 raise NotFoundError(
7583
=== modified file 'lib/lp/translations/configure.zcml'
--- lib/lp/translations/configure.zcml 2010-05-04 13:42:25 +0000
+++ lib/lp/translations/configure.zcml 2010-07-05 21:28:29 +0000
@@ -357,6 +357,14 @@
357 interface="lp.translations.interfaces.pofile.IPOFile"/>357 interface="lp.translations.interfaces.pofile.IPOFile"/>
358 </class>358 </class>
359359
360 <!-- PO File Statistics-->
361
362 <class
363 class="lp.translations.model.pofile.POFileStatistics">
364 <allow
365 interface="lp.translations.interfaces.statistics.ITranslationStatistics"/>
366 </class>
367
360 <!-- POFileSet -->368 <!-- POFileSet -->
361369
362 <securedutility370 <securedutility
363371
=== modified file 'lib/lp/translations/doc/pofile.txt'
--- lib/lp/translations/doc/pofile.txt 2010-02-01 15:06:20 +0000
+++ lib/lp/translations/doc/pofile.txt 2010-07-05 21:28:29 +0000
@@ -831,6 +831,21 @@
831 >>> print pofile_changed.newCount()831 >>> print pofile_changed.newCount()
832 0832 0
833833
834Translation statistics are also available via the 'translation_statistics'
835attribute from the `IPOFile`. It will return an object implementing the
836`ITranslationStatistics` interface.
837
838 >>> print pofile_changed.translation_statistics.total_count
839 22
840 >>> print pofile_changed.translation_statistics.translated_count
841 7
842 >>> print pofile_changed.translation_statistics.new_count
843 0
844 >>> print pofile_changed.translation_statistics.changed_count
845 1
846 >>> print pofile_changed.translation_statistics.unreviewed_count
847 1
848
834We make sure that submissions on untranslated strings are not considered849We make sure that submissions on untranslated strings are not considered
835'changed in Launchpad', since this method is mainly designed to help850'changed in Launchpad', since this method is mainly designed to help
836translators revert translations to published translations.851translators revert translations to published translations.
837852
=== modified file 'lib/lp/translations/interfaces/pofile.py'
--- lib/lp/translations/interfaces/pofile.py 2010-05-27 14:38:56 +0000
+++ lib/lp/translations/interfaces/pofile.py 2010-07-05 21:28:29 +0000
@@ -21,16 +21,20 @@
2121
22from lazr.restful.declarations import (22from lazr.restful.declarations import (
23 exported, export_as_webservice_entry)23 exported, export_as_webservice_entry)
24from lazr.restful.fields import Reference, ReferenceChoice
2425
25from canonical.launchpad import _26from canonical.launchpad import _
27from canonical.launchpad.fields import PublicPersonChoice
26from canonical.launchpad.webapp.interfaces import ILaunchBag28from canonical.launchpad.webapp.interfaces import ILaunchBag
27from lp.registry.interfaces.person import IPerson29from lp.registry.interfaces.person import IPerson
30from lp.services.worlddata.interfaces.language import ILanguage
28from lp.translations.interfaces.potemplate import IPOTemplate31from lp.translations.interfaces.potemplate import IPOTemplate
29from lp.translations.interfaces.rosettastats import IRosettaStats32from lp.translations.interfaces.rosettastats import IRosettaStats
30from lp.translations.interfaces.translationgroup import (33from lp.translations.interfaces.translationgroup import (
31 TranslationPermission)34 TranslationPermission)
32from lp.translations.interfaces.translationsperson import (35from lp.translations.interfaces.translationsperson import (
33 ITranslationsPerson)36 ITranslationsPerson)
37from lp.translations.interfaces.statistics import ITranslationStatistics
3438
3539
36class IPOFile(IRosettaStats):40class IPOFile(IRosettaStats):
@@ -47,12 +51,12 @@
47 title=_('The translation file template.'),51 title=_('The translation file template.'),
48 required=True, readonly=True, schema=IPOTemplate)52 required=True, readonly=True, schema=IPOTemplate)
4953
50 language = Choice(54 language = exported(ReferenceChoice(
51 title=_('Language of this PO file.'),55 title=_('Language of this PO file.'),
52 vocabulary='Language', required=True)56 vocabulary='Language', required=True, schema=ILanguage))
5357
54 variant = TextLine(58 variant = exported(TextLine(
55 title=_('The language variant for this translation file.'))59 title=_('The language variant for this translation file.')))
5660
57 title = TextLine(61 title = TextLine(
58 title=_('The translation file title.'), required=True, readonly=True)62 title=_('The translation file title.'), required=True, readonly=True)
@@ -73,23 +77,28 @@
73 title=_('A flag indicating whether the header is fuzzy.'),77 title=_('A flag indicating whether the header is fuzzy.'),
74 required=True)78 required=True)
7579
76 lasttranslator = Object(80 lasttranslator = exported(
77 title=_('Last person that translated a message.'), schema=IPerson)81 Reference(
82 title=_('Last person that translated a message.'),
83 schema=IPerson),
84 exported_as='last_translator')
7885
79 date_changed = Datetime(86 date_changed = exported(
80 title=_('When this file was last changed.'), readonly=False,87 Datetime(
81 required=True)88 title=_('When this file was last changed.'), readonly=False,
89 required=True),
90 exported_as='date_last_updated')
8291
83 lastparsed = Datetime(title=_('Last time this pofile was parsed.'))92 lastparsed = Datetime(title=_('Last time this pofile was parsed.'))
8493
85 owner = Choice(94 owner = exported(PublicPersonChoice(
86 title=_('Translation file owner'),95 title=_('Translation file owner'),
87 required=True,96 required=True,
88 description=_('''97 description=_('''
89 The owner of the translation file in Launchpad can edit its98 The owner of the translation file in Launchpad can edit its
90 translations and upload new versions.99 translations and upload new versions.
91 '''),100 '''),
92 vocabulary="ValidOwner")101 vocabulary="ValidOwner"))
93102
94 path = TextLine(103 path = TextLine(
95 title=_('The path to the file that was imported'),104 title=_('The path to the file that was imported'),
@@ -141,6 +150,15 @@
141 '''),150 '''),
142 required=True, readonly=True)151 required=True, readonly=True)
143152
153 translation_statistics = exported(
154 Reference(
155 title=_('Translation statistics.'),
156 description=_('''
157 An `ITranslationStatistics` used to retrieve statistics for
158 this translation file.
159 '''),
160 schema=ITranslationStatistics))
161
144 def translatedCount():162 def translatedCount():
145 """163 """
146 Returns the number of message sets which this PO file has current164 Returns the number of message sets which this PO file has current
147165
=== added file 'lib/lp/translations/interfaces/statistics.py'
--- lib/lp/translations/interfaces/statistics.py 1970-01-01 00:00:00 +0000
+++ lib/lp/translations/interfaces/statistics.py 2010-07-05 21:28:29 +0000
@@ -0,0 +1,84 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4__metaclass__ = type
5
6__all__ = [
7 'ITranslationStatistics',
8 ]
9
10from zope.interface import Attribute, Interface
11from zope.schema import Int
12
13from lazr.restful.declarations import exported, export_as_webservice_entry
14from lazr.restful.fields import Reference
15
16from canonical.launchpad import _
17from lp.services.worlddata.interfaces.language import ILanguage
18from lp.translations.interfaces.potemplate import IPOTemplate
19
20
21class ITranslationStatistics(Interface):
22 """Interface for getting translation statistics of various entities."""
23
24 export_as_webservice_entry()
25
26 source = Attribute(
27 _('The source used for generating the translation statistics.'))
28
29 language = exported(Reference(
30 title=_('Source language used for translation statistics.'),
31 required=True, readonly=True,
32 schema=ILanguage))
33
34 template = exported(Reference(
35 title=_('Source template used for translation statistics.'),
36 description=_('''
37 This field is not set when translation statistics are aggregated
38 for multiple templates.
39 '''),
40 readonly=True,
41 schema=IPOTemplate))
42
43 total_count = exported(Int(
44 title=_('Number of total translation messages.'),
45 description=_('''
46 Total number of translation messages for this entry.
47 '''),
48 required=True, readonly=True))
49
50 translated_count = exported(Int(
51 title=_('Number of translated translation messages.'),
52 description=_('''
53 The number of translation messages for this entry where we have a
54 current translation.
55 '''),
56 required=True, readonly=True))
57
58 new_count = exported(Int(
59 title=_('Number of newly translated translation messages '
60 'in Launchpad.'),
61 description=_('''
62 The number of translation messages for this entry where we have a
63 new translation in Launchpad.
64 '''),
65 required=True, readonly=True))
66
67 changed_count = exported(Int(
68 title=_('Number of changed translated translation messages '
69 ' in Launchpad.'),
70 description=_('''
71 The number of translation messages for this entry where the
72 current translation in Launchpad differs to the one in the
73 original translation file.
74 '''),
75 required=True, readonly=True))
76
77 unreviewed_count = exported(Int(
78 title=_('Number of translation messages with unreviewed '
79 'suggestions.'),
80 description=_('''
81 Unreviewed translations are those which contain suggestions
82 submitted later than the last review date.
83 '''),
84 required=True, readonly=True))
085
=== modified file 'lib/lp/translations/interfaces/webservice.py'
--- lib/lp/translations/interfaces/webservice.py 2010-05-20 19:04:09 +0000
+++ lib/lp/translations/interfaces/webservice.py 2010-07-05 21:28:29 +0000
@@ -11,6 +11,7 @@
11 'IPOTemplate',11 'IPOTemplate',
12 'ITranslationImportQueue',12 'ITranslationImportQueue',
13 'ITranslationImportQueueEntry',13 'ITranslationImportQueueEntry',
14 'ITranslationStatistics',
14 ]15 ]
1516
16from lp.translations.interfaces.translationimportqueue import (17from lp.translations.interfaces.translationimportqueue import (
@@ -22,3 +23,5 @@
22 IPOTemplate)23 IPOTemplate)
23from lp.translations.interfaces.pofile import (24from lp.translations.interfaces.pofile import (
24 IPOFile)25 IPOFile)
26from lp.translations.interfaces.statistics import (
27 ITranslationStatistics)
2528
=== modified file 'lib/lp/translations/model/pofile.py'
--- lib/lp/translations/model/pofile.py 2010-04-23 14:46:43 +0000
+++ lib/lp/translations/model/pofile.py 2010-07-05 21:28:29 +0000
@@ -12,6 +12,7 @@
12 'POFileSet',12 'POFileSet',
13 'POFileToChangedFromPackagedAdapter',13 'POFileToChangedFromPackagedAdapter',
14 'POFileToTranslationFileDataAdapter',14 'POFileToTranslationFileDataAdapter',
15 'POFileStatistics',
15 ]16 ]
1617
17import datetime18import datetime
@@ -57,6 +58,7 @@
57from lp.translations.interfaces.translationsperson import (58from lp.translations.interfaces.translationsperson import (
58 ITranslationsPerson)59 ITranslationsPerson)
59from lp.translations.interfaces.translations import TranslationConstants60from lp.translations.interfaces.translations import TranslationConstants
61from lp.translations.interfaces.statistics import ITranslationStatistics
60from lp.translations.model.pomsgid import POMsgID62from lp.translations.model.pomsgid import POMsgID
61from lp.translations.model.potmsgset import POTMsgSet63from lp.translations.model.potmsgset import POTMsgSet
62from lp.translations.model.translationimportqueue import (64from lp.translations.model.translationimportqueue import (
@@ -431,6 +433,49 @@
431 return TranslatableMessage(potmsgset, self)433 return TranslatableMessage(potmsgset, self)
432434
433435
436class POFileStatistics:
437 """Retrieves translation statistics for a `IPOFile`."""
438 implements(ITranslationStatistics)
439
440 def __init__(self, pofile):
441 self.source = pofile
442
443 @property
444 def language(self):
445 """See `ITranslationStatistics`."""
446 return self.source.language
447
448 @property
449 def template(self):
450 """See `ITranslationStatistics`."""
451 return self.source.potemplate
452
453 @property
454 def total_count(self):
455 """See `ITranslationStatistics`."""
456 return self.source.potemplate.messageCount()
457
458 @property
459 def translated_count(self):
460 """See `ITranslationStatistics`."""
461 return self.source.currentcount + self.source.rosettacount
462
463 @property
464 def new_count(self):
465 """See `ITranslationStatistics`."""
466 return self.source.rosettacount - self.source.updatescount
467
468 @property
469 def changed_count(self):
470 """See `ITranslationStatistics`."""
471 return self.source.updatescount
472
473 @property
474 def unreviewed_count(self):
475 """See `ITranslationStatistics`."""
476 return self.source.unreviewed_count
477
478
434class POFile(SQLBase, POFileMixIn):479class POFile(SQLBase, POFileMixIn):
435 implements(IPOFile)480 implements(IPOFile)
436481
@@ -570,7 +615,7 @@
570 return u','.join(emails)615 return u','.join(emails)
571 elif credits_type == TranslationCreditsType.KDE_NAMES:616 elif credits_type == TranslationCreditsType.KDE_NAMES:
572 names = []617 names = []
573 618
574 if text is not None:619 if text is not None:
575 if text == u'':620 if text == u'':
576 text = SPACE621 text = SPACE
@@ -593,7 +638,7 @@
593 text = text[:header_index]638 text = text[:header_index]
594 else:639 else:
595 text += u'\n\n'640 text += u'\n\n'
596 641
597 text += LP_CREDIT_HEADER642 text += LP_CREDIT_HEADER
598 for contributor in self.contributors:643 for contributor in self.contributors:
599 text += ("\n %s %s" %644 text += ("\n %s %s" %
@@ -899,6 +944,11 @@
899 self.rosettacount,944 self.rosettacount,
900 self.unreviewed_count)945 self.unreviewed_count)
901946
947 @property
948 def translation_statistics(self):
949 """See `IPOFile`."""
950 return POFileStatistics(self)
951
902 def _appendCompletePluralFormsConditions(self, query,952 def _appendCompletePluralFormsConditions(self, query,
903 table_name='TranslationMessage'):953 table_name='TranslationMessage'):
904 """Add conditions to implement ITranslationMessage.is_complete in SQL.954 """Add conditions to implement ITranslationMessage.is_complete in SQL.
@@ -1349,6 +1399,11 @@
1349 return self.potemplate.messageCount()1399 return self.potemplate.messageCount()
13501400
1351 @property1401 @property
1402 def translation_statistics(self):
1403 """See `IPOFile`."""
1404 return POFileStatistics(self)
1405
1406 @property
1352 def title(self):1407 def title(self):
1353 """See `IPOFile`."""1408 """See `IPOFile`."""
1354 title = '%s translation of %s' % (1409 title = '%s translation of %s' % (
13551410
=== added file 'lib/lp/translations/stories/webservice/xx-pofile.txt'
--- lib/lp/translations/stories/webservice/xx-pofile.txt 1970-01-01 00:00:00 +0000
+++ lib/lp/translations/stories/webservice/xx-pofile.txt 2010-07-05 21:28:29 +0000
@@ -0,0 +1,83 @@
1Translation file webservices
2============================
3
4
5Getting the attributes of a translation file
6--------------------------------------------
7
8Anonymous users have read access to translation file attributes.
9
10 >>> from lazr.restful.testing.webservice import pprint_entry
11 >>> translation_file = anon_webservice.get(
12 ... '/ubuntu/hoary/+source/evolution/'
13 ... '+pots/evolution-2.2/es').jsonBody()
14 >>> pprint_entry(translation_file)
15 date_last_updated: u'2005-06-06T20:05:03.244905+00:00'
16 id: 12
17 language_link: u'http://.../+languages/es'
18 last_translator_link: u'http://.../~tsukimi'
19 owner_link: u'http://.../~ubuntu-translators'
20 resource_type_link: u'http://.../#translation_file'
21 self_link:
22 u'http://.../ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es'
23 translation_statistics_link:
24 u'http://.../+source/evolution/+pots/evolution-2.2/es/+statistics'
25 variant: None
26
27
28Retrieving translation statistics for a translation file
29--------------------------------------------------------
30
31"translation_statistics" attribute from a translation file, will retrieve the
32translation statistics for this translation file.
33
34
35 >>> from zope.component import getUtility
36 >>> from zope.security.proxy import removeSecurityProxy
37 >>> from canonical.launchpad.interfaces.launchpad import (
38 ... ILaunchpadCelebrities)
39 >>> from lp.translations.interfaces.potemplate import IPOTemplateSet
40 >>> login('admin@canonical.com')
41 >>> hoary = getUtility(ILaunchpadCelebrities).ubuntu.getSeries('hoary')
42 >>> translation_templates = hoary.getTranslationTemplates()
43 >>> translation_template = translation_templates[0]
44 >>> translation_file = removeSecurityProxy(
45 ... translation_template.getPOFileByLang('es'))
46 >>> template_name = translation_template.name
47 >>> package_name = translation_template.translationtarget.name
48 >>> total_count = translation_template.messageCount()
49 >>> new_count = (
50 ... translation_file.rosettacount - translation_file.updatescount)
51 >>> translated_count = (
52 ... translation_file.currentcount + translation_file.rosettacount)
53 >>> unreviewed_count = translation_file.unreviewed_count
54 >>> changed_count = translation_file.updatescount
55 >>> logout()
56
57 >>> statistics = anon_webservice.get(
58 ... '/ubuntu/hoary/+source/%s/'
59 ... '+pots/%s/es/+statistics' % (
60 ... package_name, template_name)).jsonBody()
61 >>> print statistics['resource_type_link']
62 http://.../#translation_statistics
63 >>> print statistics['language_link']
64 http://.../+languages/es
65 >>> statistics['self_link'].find(
66 ... '/ubuntu/hoary/+source/%s/+pots/%s/es/+statistics' % (
67 ... package_name, template_name)) != -1
68 True
69 >>> statistics['template_link'].find(
70 ... '/ubuntu/hoary/+source/%s/+pots/%s' % (
71 ... package_name, template_name)) != -1
72 True
73 >>> statistics['changed_count'] == changed_count
74 True
75 >>> statistics['new_count'] == new_count
76 True
77 >>> statistics['total_count'] == total_count
78 True
79 >>> statistics['translated_count'] == translated_count
80 True
81 >>> statistics['unreviewed_count'] == unreviewed_count
82 True
83