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

Proposed by Adi Roiban
Status: Merged
Merged at revision: 10953
Proposed branch: lp:~adiroiban/launchpad/bug-525371
Merge into: lp:launchpad
Diff against target: 1034 lines (+338/-153)
18 files modified
lib/canonical/launchpad/interfaces/_schema_circular_imports.py (+18/-4)
lib/canonical/launchpad/security.py (+11/-2)
lib/lp/registry/configure.zcml (+0/-6)
lib/lp/registry/interfaces/distroseries.py (+5/-3)
lib/lp/registry/interfaces/productseries.py (+3/-1)
lib/lp/registry/interfaces/sourcepackage.py (+8/-6)
lib/lp/registry/model/distroseries.py (+5/-8)
lib/lp/registry/model/productseries.py (+2/-4)
lib/lp/registry/model/sourcepackage.py (+2/-4)
lib/lp/registry/stories/webservice/xx-distroseries.txt (+2/-1)
lib/lp/registry/stories/webservice/xx-project-registry.txt (+10/-5)
lib/lp/translations/browser/potemplate.py (+11/-12)
lib/lp/translations/interfaces/pofile.py (+12/-5)
lib/lp/translations/interfaces/potemplate.py (+107/-82)
lib/lp/translations/interfaces/webservice.py (+15/-0)
lib/lp/translations/model/distroseries_translations_copy.py (+1/-1)
lib/lp/translations/model/potemplate.py (+5/-9)
lib/lp/translations/stories/webservice/xx-potemplate.txt (+121/-0)
To merge this branch: bzr merge lp:~adiroiban/launchpad/bug-525371
Reviewer Review Type Date Requested Status
Данило Шеган (community) Needs Fixing
Jeroen T. Vermeulen Pending
Review via email: mp+25423@code.launchpad.net

Commit message

Export POTemplate attributes in the API.

Description of the change

= Bug 525371 =

To help creating various translations statistics (ie for Ubuntu translations) it would be nice to have an LP API that could export the following information about a POTemplate:

 * template name in Launchpad
 * priority
 * length
 * date last updated
 * domain name
 * iscurrent (active or not)
 * source package
 * is in lang pack
 * in how many languages it is translates (nice to have)

The API should return a list of all templates for a series, but also the info for a single template

The requirements are tracked on this wiki page:
https://dev.launchpad.net/Translations/Specs/ReportingAPI

== Proposed fix ==

This is the first step in implementing an API for POTemplates.

We will start with exposing the POTemplates attributes.

Here are the exported attributes:

    active: True
    all_pofiles_collection_link: u'http://.../pmount/+pots/pmount/all_pofiles'
    date_last_updated: u'2005-05-06T20:09:23.775993+00:00'
    description: None
    exported_in_languagepacks: True
    format: u'PO format'
    id: 2
    language_count: 8
    message_count: 63
    name: u'pmount'
    owner_link: u'http://.../~rosetta-admins'
    path: u'po/template.pot'
    priority: 0
    resource_type_link: u'http://.../#potemplate'
    self_link: u'http://.../ubuntu/hoary/+source/pmount/+pots/pmount'
    translation_domain: u'pmount'

== Pre-implementation notes ==

During the last UDS I have talked with Henning and we had a pre-implementation talk.
He agreed with the current exported attributes. He said that he can not comment on the technical implementation details for the API.

If you think it is needed you can ask someone experienced with the LP API to review this branch.

== Implementation details ==

POFile API export is just a stub to illustrate how pofiles are linked to the corresponding po templates.

I don't know how to fix the /usr/share lint errors. Any hints are much appreciated.

== Tests ==

  lib/lp/registry/stories/webservice/xx-distroseries.txt
  lib/lp/registry/stories/webservice/xx-project-registry.txt
  lib/lp/registry/stories/webservice/xx-source-package.txt
  lib/lp/translations/stories/webservice/xx-potemplate.txt

== Demo and Q/A ==

To get the potemplates for a sourcepackage
curl -ks https://launchpad.dev/api/devel/ubuntu/hoary/+source/evolution/all_translation_templates

To get the potemplates for a productseries
curl -ks https://launchpad.dev/api/devel/evolution/trunk/all_translation_templates

To get the potemplates for a distroseries
curl -ks https://launchpad.dev/api/devel/ubuntu/hoary/all_translation_templates

"all_translation_templates" are linked from the sourcepackage, distroseries and productseries
curl -ks https://launchpad.dev/api/devel/ubuntu/hoary/+source/evolution
curl -ks https://launchpad.dev/api/devel/ubuntu/hoary
curl -ks https://launchpad.dev/api/devel/evolution/trunk

= 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/interfaces/webservice.py

== Pyflakes notices ==

lib/lp/translations/interfaces/webservice.py
    8: 'IHasTranslationImports' imported but unused
    8: 'ITranslationImportQueue' imported but unused
    8: 'ITranslationImportQueueEntry' imported but unused
    13: 'IPOTemplate' imported but unused
    15: 'IPOFile' imported but unused
warning: Not importing directory '/usr/share/pyshared/lazr': missing __init__.py
warning: Not importing directory '/usr/share/pyshared/lazr': missing __init__.py
warning: Not importing directory '/usr/share/pyshared/lazr': missing __init__.py

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

Hi Adi,

It's really great that you're doing this. I'm reading it through and it looks good to me so far. One note though about the testing in xx-distroseries.txt.

You've got the problem here that you have to rely on existing test data, which can be a bit arbitrary. For example, the fact that there are 6 templates in the distroseries is pretty meaningless in the context of the test. But it's a lot of trouble to set up a fresh distroseries with fresh translation templates, especially if the existing tests don't do it either.

So here's an idea: instead of printing the exact number of templates, why not show that int(all_translation_templates['total_size']) is identical to len(list(getUtility(ILaunchpadCelebrities).ubuntu.getSeries('hoary'))).

Jeroen

Revision history for this message
Adi Roiban (adiroiban) wrote :
Download full text (4.5 KiB)

Hi,

I have improved the tests to check for templates count from the db.

Here is the latest diff:

=== modified file 'lib/lp/registry/stories/webservice/xx-distroseries.txt'
--- lib/lp/registry/stories/webservice/xx-distroseries.txt 2010-05-17 09:35:18 +0000
+++ lib/lp/registry/stories/webservice/xx-distroseries.txt 2010-05-18 14:51:40 +0000
@@ -107,9 +107,19 @@
 All templates associated to a distribution series are available at the
 'all_translation_templates' collection link.

+ >>> from zope.component import getUtility
+ >>> from canonical.launchpad.interfaces.launchpad import (
+ ... ILaunchpadCelebrities)
+ >>> from lp.translations.interfaces.potemplate import IPOTemplateSet
+ >>> login('<email address hidden>')
+ >>> hoary = getUtility(ILaunchpadCelebrities).ubuntu.getSeries('hoary')
+ >>> templates = getUtility(IPOTemplateSet).getSubset(distroseries=hoary)
+ >>> db_count = len(list(templates))
+ >>> logout()
     >>> all_translation_templates = anon_webservice.get(
     ... '/ubuntu/hoary/all_translation_templates').jsonBody()
- >>> print all_translation_templates['total_size']
- 6
+ >>> api_count = all_translation_templates['total_size']
+ >>> api_count == db_count
+ True
     >>> print(all_translation_templates['entries'][0]['resource_type_link'])
     http://.../#potemplate

=== modified file 'lib/lp/registry/stories/webservice/xx-project-registry.txt'
--- lib/lp/registry/stories/webservice/xx-project-registry.txt 2010-05-17 09:35:18 +0000
+++ lib/lp/registry/stories/webservice/xx-project-registry.txt 2010-05-18 15:10:45 +0000
@@ -897,11 +897,20 @@
 "all_translation_templates" will list all POTemplates associated with this
 product series.

+
+ >>> from zope.component import getUtility
+ >>> from lp.translations.interfaces.potemplate import IPOTemplateSet
+ >>> login('<email address hidden>')
+ >>> templates = getUtility(
+ ... IPOTemplateSet).getSubset(productseries=foobadoo)
+ >>> db_count = len(list(templates))
+ >>> logout()
     >>> all_translation_templates = anon_webservice.get(
     ... babadoo_foobadoo[
     ... 'all_translation_templates_collection_link']).jsonBody()
- >>> print all_translation_templates['total_size']
- 1
+ >>> api_count = all_translation_templates['total_size']
+ >>> api_count == db_count
+ True
     >>> print(all_translation_templates['entries'][0]['resource_type_link'])
     http://.../#potemplate

=== modified file 'lib/lp/registry/stories/webservice/xx-source-package.txt'
--- lib/lp/registry/stories/webservice/xx-source-package.txt 2010-05-17 09:35:18 +0000
+++ lib/lp/registry/stories/webservice/xx-source-package.txt 2010-05-18 15:23:51 +0000
@@ -130,10 +130,27 @@
 All translation templates for a source package are available at the
 'all_translation_templates' collection link.

+
+ >>> from zope.component import getUtility
+ >>> from canonical.launchpad.interfaces.launchpad import (
+ ... ILaunchpadCelebrities)
+ >>> from lp.registry.interfaces.sourcepackagename import (
+ ... ISourcePackageNameSet)
+ >>> from lp.translations.interfaces.potemplate import IPOTemplateSet
+ >...

Read more...

Revision history for this message
Данило Шеган (danilo) wrote :
Download full text (6.0 KiB)

Hi Adi,

Thanks a lot for the work on this. Overall, it's pretty good.

У пон, 17. 05 2010. у 11:07 +0000, Adi Roiban пише:

> == Tests ==
>
> lib/lp/registry/stories/webservice/xx-distroseries.txt
> lib/lp/registry/stories/webservice/xx-project-registry.txt
> lib/lp/registry/stories/webservice/xx-source-package.txt
> lib/lp/translations/stories/webservice/xx-potemplate.txt

It usually helps if you provide a bin/test command which runs them for
lazy reviewers to copy-paste them :)

> == Demo and Q/A ==
>
> To get the potemplates for a sourcepackage
> curl -ks
https://launchpad.dev/api/devel/ubuntu/hoary/+source/evolution/all_translation_templates
>
> To get the potemplates for a productseries
> curl -ks
https://launchpad.dev/api/devel/evolution/trunk/all_translation_templates
>
> To get the potemplates for a distroseries
> curl -ks
https://launchpad.dev/api/devel/ubuntu/hoary/all_translation_templates
>
> "all_translation_templates" are linked from the sourcepackage,
distroseries and productseries
> curl -ks
https://launchpad.dev/api/devel/ubuntu/hoary/+source/evolution
> curl -ks https://launchpad.dev/api/devel/ubuntu/hoary
> curl -ks https://launchpad.dev/api/devel/evolution/trunk
>
> = 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/interfaces/webservice.py

There are many more files with many more lint issues: you should try to
run it after you commit your final change, not before (if you've got
uncommitted changes, it lints just the uncommitted files). Please fix
all the issues.

> == Pyflakes notices ==
>
> lib/lp/translations/interfaces/webservice.py
> 8: 'IHasTranslationImports' imported but unused
> 8: 'ITranslationImportQueue' imported but unused
> 8: 'ITranslationImportQueueEntry' imported but unused
> 13: 'IPOTemplate' imported but unused
> 15: 'IPOFile' imported but unused

You should add the following to webservice.py if that's the intention:

__all__ = [
    'IHasTranslationImports',
    'IPOFile',
    'IPOTemplate',
    'ITranslationImportQueue',
    'ITranslationImportQueueEntry',
    ]

But, why is this necessary?

> warning: Not importing directory '/usr/share/pyshared/lazr': missing
__init__.py
> warning: Not importing directory '/usr/share/pyshared/lazr': missing
__init__.py
> warning: Not importing directory '/usr/share/pyshared/lazr': missing
__init__.py

This might be a bug in lazr that needs fixing. Can you please check and
file appropriate bug if it is?

> === modified file 'lib/canonical/launchpad/security.py'
> --- lib/canonical/launchpad/security.py 2010-05-17 12:43:53
+0000
> +++ lib/canonical/launchpad/security.py 2010-05-19 12:09:43
+0000
> @@ -61,8 +61,7 @@ from lp.hardwaredb.interfaces.hwdb impor
> from lp.services.worlddata.interfaces.language import ILanguage,
ILanguageSet
> from lp.translations.interfaces.languagepack import ILanguagePack
> from canonical.launchpad.interfaces.launchpad import (
> - IBazaarApplication, IHasBug, IHasDrivers, ILaunchpadCelebrities,
> - IPersonRoles)
> + IHas...

Read more...

Revision history for this message
Данило Шеган (danilo) :
review: Needs Fixing
Revision history for this message
Adi Roiban (adiroiban) wrote :
Download full text (18.4 KiB)

On Wed, 2010-05-19 at 14:43 +0000, Данило Шеган wrote:
> Hi Adi,
>
> Thanks a lot for the work on this. Overall, it's pretty good.
>
> У пон, 17. 05 2010. у 11:07 +0000, Adi Roiban пише:
>
> > == Tests ==
> >
> > lib/lp/registry/stories/webservice/xx-distroseries.txt
> > lib/lp/registry/stories/webservice/xx-project-registry.txt
> > lib/lp/registry/stories/webservice/xx-source-package.txt
> > lib/lp/translations/stories/webservice/xx-potemplate.txt
>
> It usually helps if you provide a bin/test command which runs them for
> lazy reviewers to copy-paste them :)

Sorry. I will add it with the next MP.

./bin/test -t xx-potemplate.txt -t xx-distroseries.txt \
 -t xx-project-registry.txt -t xx-source-package.txt

:)

[snip]
> > = 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/interfaces/webservice.py
>
> There are many more files with many more lint issues: you should try to
> run it after you commit your final change, not before (if you've got
> uncommitted changes, it lints just the uncommitted files). Please fix
> all the issues.

I have fixed some of the other warning.

There are still some warnings for code like:

    @operation_parameters(
        pocket=Choice(
            title=_("Pocket"), required=True,
            vocabulary=DBEnumeratedType))

and it looks fine to me, but pylint complains about:

lib/lp/registry/interfaces/sourcepackage.py
    191: [C0322, ISourcePackage.getBranch] Operator not preceded by a
space
    required=True,
    ^
    vocabulary=DBEnumeratedType))

> > == Pyflakes notices ==
> >
> > lib/lp/translations/interfaces/webservice.py
> > 8: 'IHasTranslationImports' imported but unused
> > 8: 'ITranslationImportQueue' imported but unused
> > 8: 'ITranslationImportQueueEntry' imported but unused
> > 13: 'IPOTemplate' imported but unused
> > 15: 'IPOFile' imported but unused
>
> You should add the following to webservice.py if that's the intention:
>
> __all__ = [
> 'IHasTranslationImports',
> 'IPOFile',
> 'IPOTemplate',
> 'ITranslationImportQueue',
> 'ITranslationImportQueueEntry',
> ]
>
> But, why is this necessary?

Only interfaces listed in webservices.py will be exported by
lazr.restful.

Adding __all__ will still generate Pyflakes notices as they are still
unused in that file.

> > warning: Not importing directory '/usr/share/pyshared/lazr': missing
> __init__.py
> > warning: Not importing directory '/usr/share/pyshared/lazr': missing
> __init__.py
> > warning: Not importing directory '/usr/share/pyshared/lazr': missing
> __init__.py
>
> This might be a bug in lazr that needs fixing. Can you please check and
> file appropriate bug if it is?

Is is not a problem in lazr. I have the same problem with:
'/usr/share/pyshared/google': missing __init__.py

Those file were installed as dependencies for ubuntuone-client-gnome by
python-protobuf and python-lazr-uri, python-lazr-restfulclient and are
maintained by main Ubuntu developers... not in the LP PPA.

> > === modified file 'lib/...

Revision history for this message
Данило Шеган (danilo) wrote :
Download full text (6.3 KiB)

Hi Adi,

Thanks for the updates. There's still just a little bit more to do :)

У сре, 19. 05 2010. у 20:13 +0000, Adi Roiban пише:

> There are still some warnings for code like:
>
> @operation_parameters(
> pocket=Choice(
> title=_("Pocket"), required=True,
> vocabulary=DBEnumeratedType))
>
> and it looks fine to me, but pylint complains about:
>
> lib/lp/registry/interfaces/sourcepackage.py
> 191: [C0322, ISourcePackage.getBranch] Operator not preceded by a
> space
> required=True,
> ^
> vocabulary=DBEnumeratedType))
>
>
> > > == Pyflakes notices ==
> > >
> > > lib/lp/translations/interfaces/webservice.py
> > > 8: 'IHasTranslationImports' imported but unused
> > > 8: 'ITranslationImportQueue' imported but unused
> > > 8: 'ITranslationImportQueueEntry' imported but unused
> > > 13: 'IPOTemplate' imported but unused
> > > 15: 'IPOFile' imported but unused
> >
> > You should add the following to webservice.py if that's the intention:
> >
> > __all__ = [
> > 'IHasTranslationImports',
> > 'IPOFile',
> > 'IPOTemplate',
> > 'ITranslationImportQueue',
> > 'ITranslationImportQueueEntry',
> > ]
> >
> > But, why is this necessary?
>
> Only interfaces listed in webservices.py will be exported by
> lazr.restful.
>
> Adding __all__ will still generate Pyflakes notices as they are still
> unused in that file.

It actually fixed it for me. I'm attaching a patch which fixes all the
lint issues I've seen. It would be nicer if you did that instead :)

Also, "operator not preceded by space" lint issues in this particular
context seem to be opposed to our style guide. It would be best to
raise this on launchpad-dev mailing list to come to an agreement and to
see if we can fix either pylint or style guide.

> > > warning: Not importing directory '/usr/share/pyshared/lazr': missing
> > __init__.py
> > > warning: Not importing directory '/usr/share/pyshared/lazr': missing
> > __init__.py
> > > warning: Not importing directory '/usr/share/pyshared/lazr': missing
> > __init__.py
> >
> > This might be a bug in lazr that needs fixing. Can you please check and
> > file appropriate bug if it is?
>
> Is is not a problem in lazr. I have the same problem with:
> '/usr/share/pyshared/google': missing __init__.py
>
> Those file were installed as dependencies for ubuntuone-client-gnome by
> python-protobuf and python-lazr-uri, python-lazr-restfulclient and are
> maintained by main Ubuntu developers... not in the LP PPA.

Well, LAZR is a library shared between Launchpad and UbuntuOne. It's
still a bug that would need to be filed. I've worked-around it locally
by doing "sudo touch /usr/share/pyshared/lazr/__init__.py", and there is
probably no reason why the package shouldn't do the same for everybody.

> Should POTemplate and POFile API be available only to authenticated
> users?

Not, it's fine if they are anonymously available.

> > === modified file
> > 'lib/lp/registry/stories/webservice/xx-distroseries.txt'
> > === modified file
> > 'lib/lp/registry/stories/webservice/xx-project-registry.txt'
> > === modified file
> > 'lib/lp/registry/stories/webservice/...

Read more...

=== modified file 'lib/lp/registry/interfaces/distroseries.py'
--- lib/lp/registry/interfaces/distroseries.py 2010-05-19 17:04:11 +0000
+++ lib/lp/registry/interfaces/distroseries.py 2010-05-20 16:10:38 +0000
@@ -114,7 +114,7 @@ class DistroSeriesVersionField(UniqueFie
114114
115 def _validate(self, version):115 def _validate(self, version):
116 """See `UniqueField`."""116 """See `UniqueField`."""
117 super(DistroSeriesVersionField, self)._validate(version)117 DistroSeriesVersionField._validate(self, version)
118 if not sane_version(version):118 if not sane_version(version):
119 raise LaunchpadValidationError(119 raise LaunchpadValidationError(
120 "%s is not a valid version" % version)120 "%s is not a valid version" % version)
@@ -423,38 +423,38 @@ class IDistroSeriesPublic(
423423
424 @operation_parameters(424 @operation_parameters(
425 created_since_date=Datetime(425 created_since_date=Datetime(
426 title=_("Created Since Timestamp"),426 title = _("Created Since Timestamp"),
427 description=_(427 description = _(
428 "Return items that are more recent than this timestamp."),428 "Return items that are more recent than this timestamp."),
429 required=False),429 required = False),
430 status=Choice(430 status=Choice(
431 # Really PackageUploadCustomFormat, patched in431 # Really PackageUploadCustomFormat, patched in
432 # _schema_circular_imports.py432 # _schema_circular_imports.py
433 vocabulary=DBEnumeratedType,433 vocabulary = DBEnumeratedType,
434 title=_("Package Upload Status"),434 title = _("Package Upload Status"),
435 description=_("Return only items that have this status."),435 description = _("Return only items that have this status."),
436 required=False),436 required = False),
437 archive=Reference(437 archive=Reference(
438 # Really IArchive, patched in _schema_circular_imports.py438 # Really IArchive, patched in _schema_circular_imports.py
439 schema=Interface,439 schema = Interface,
440 title=_("Archive"),440 title = _("Archive"),
441 description=_("Return only items for this archive."),441 description = _("Return only items for this archive."),
442 required=False),442 required = False),
443 pocket=Choice(443 pocket=Choice(
444 # Really PackagePublishingPocket, patched in444 # Really PackagePublishingPocket, patched in
445 # _schema_circular_imports.py445 # _schema_circular_imports.py
446 vocabulary=DBEnumeratedType,446 vocabulary = DBEnumeratedType,
447 title=_("Pocket"),447 title = _("Pocket"),
448 description=_("Return only items targeted to this pocket"),448 description = _("Return only items targeted to this pocket"),
449 required=False),449 required = False),
450 custom_type=Choice(450 custom_type=Choice(
451 # Really PackageUploadCustomFormat, patched in451 # Really PackageUploadCustomFormat, patched in
452 # _schema_circular_imports.py452 # _schema_circular_imports.py
453 vocabulary=DBEnumeratedType,453 vocabulary = DBEnumeratedType,
454 title=_("Custom Type"),454 title = _("Custom Type"),
455 description=_("Return only items with custom files of this "455 description = _("Return only items with custom files of this "
456 "type."),456 "type."),
457 required=False),457 required = False)
458 )458 )
459 # Really IPackageUpload, patched in _schema_circular_imports.py459 # Really IPackageUpload, patched in _schema_circular_imports.py
460 @operation_returns_collection_of(Interface)460 @operation_returns_collection_of(Interface)
461461
=== modified file 'lib/lp/registry/interfaces/sourcepackage.py'
--- lib/lp/registry/interfaces/sourcepackage.py 2010-05-17 10:16:03 +0000
+++ lib/lp/registry/interfaces/sourcepackage.py 2010-05-20 16:13:43 +0000
@@ -187,8 +187,8 @@ class ISourcePackage(IBugTarget, IHasBra
187 # in _schema_circular_imports.187 # in _schema_circular_imports.
188 @operation_parameters(188 @operation_parameters(
189 pocket=Choice(189 pocket=Choice(
190 title=_("Pocket"), required=True,190 title = _("Pocket"), required = True,
191 vocabulary=DBEnumeratedType))191 vocabulary = DBEnumeratedType))
192 # Actually returns an IBranch, but we say Interface here to avoid circular192 # Actually returns an IBranch, but we say Interface here to avoid circular
193 # imports. Correct interface specified in _schema_circular_imports.193 # imports. Correct interface specified in _schema_circular_imports.
194 @operation_returns_entry(Interface)194 @operation_returns_entry(Interface)
@@ -205,9 +205,9 @@ class ISourcePackage(IBugTarget, IHasBra
205 # imports. Correct interface specific in _schema_circular_imports.205 # imports. Correct interface specific in _schema_circular_imports.
206 @operation_parameters(206 @operation_parameters(
207 pocket=Choice(207 pocket=Choice(
208 title=_("Pocket"), required=True,208 title = _("Pocket"), required = True,
209 vocabulary=DBEnumeratedType),209 vocabulary = DBEnumeratedType),
210 branch=Reference(Interface, title=_("Branch"), required=False))210 branch = Reference(Interface, title = _("Branch"), required = False))
211 @call_with(registrant=REQUEST_USER)211 @call_with(registrant=REQUEST_USER)
212 @export_write_operation()212 @export_write_operation()
213 def setBranch(pocket, branch, registrant):213 def setBranch(pocket, branch, registrant):
214214
=== modified file 'lib/lp/translations/interfaces/webservice.py'
--- lib/lp/translations/interfaces/webservice.py 2010-05-18 15:27:05 +0000
+++ lib/lp/translations/interfaces/webservice.py 2010-05-20 16:02:50 +0000
@@ -14,3 +14,11 @@ from lp.translations.interfaces.potempla
14 IPOTemplate)14 IPOTemplate)
15from lp.translations.interfaces.pofile import (15from lp.translations.interfaces.pofile import (
16 IPOFile)16 IPOFile)
17
18__all__ = [
19 'IHasTranslationImports',
20 'IPOFile',
21 'IPOTemplate',
22 'ITranslationImportQueue',
23 'ITranslationImportQueueEntry',
24 ]
1725
=== modified file 'lib/lp/translations/model/potemplate.py'
--- lib/lp/translations/model/potemplate.py 2010-05-17 10:16:03 +0000
+++ lib/lp/translations/model/potemplate.py 2010-05-20 16:19:09 +0000
@@ -1297,8 +1297,8 @@ class POTemplateSet:
1297 elif sourcepackagename is None:1297 elif sourcepackagename is None:
1298 # Multiple matches, and for a product not a package.1298 # Multiple matches, and for a product not a package.
1299 logging.warn(1299 logging.warn(
1300 "Found %d templates with path '%s' for productseries %s" % (1300 "Found %d templates with path '%s' for productseries %s",
1301 len(matches), path, productseries.title))1301 len(matches), path, productseries.title)
1302 return None1302 return None
1303 else:1303 else:
1304 # Multiple matches, for a distribution package. Prefer a1304 # Multiple matches, for a distribution package. Prefer a
@@ -1316,9 +1316,9 @@ class POTemplateSet:
1316 else:1316 else:
1317 logging.warn(1317 logging.warn(
1318 "Found %d templates with path '%s' for package %s "1318 "Found %d templates with path '%s' for package %s "
1319 "(%d matched on from_sourcepackagename)." % (1319 "(%d matched on from_sourcepackagename).",
1320 len(matches), path, sourcepackagename.name,1320 len(matches), path, sourcepackagename.name,
1321 len(preferred_matches)))1321 len(preferred_matches))
1322 return None1322 return None
13231323
1324 @staticmethod1324 @staticmethod
@@ -1510,10 +1510,6 @@ class POTemplateToTranslationFileDataAda
1510 if flag1510 if flag
1511 ])1511 ])
15121512
1513 # Store sequences so we can detect later whether we changed the
1514 # message.
1515 sequence = row.sequence
1516
1517 # Store the message.1513 # Store the message.
1518 messages.append(msgset)1514 messages.append(msgset)
15191515
15201516
Revision history for this message
Adi Roiban (adiroiban) wrote :
Download full text (10.5 KiB)

On Thu, 2010-05-20 at 16:33 +0000, Данило Шеган wrote:
> Hi Adi,
>
> Thanks for the updates. There's still just a little bit more to do :)
>
> У сре, 19. 05 2010. у 20:13 +0000, Adi Roiban пише:
>
> > There are still some warnings for code like:
> >
> > @operation_parameters(
> > pocket=Choice(
> > title=_("Pocket"), required=True,
> > vocabulary=DBEnumeratedType))
> >
> > and it looks fine to me, but pylint complains about:
> >
> > lib/lp/registry/interfaces/sourcepackage.py
> > 191: [C0322, ISourcePackage.getBranch] Operator not preceded by a
> > space
> > required=True,
> > ^
> > vocabulary=DBEnumeratedType))
> >
> >
> > > > == Pyflakes notices ==
> > > >
> > > > lib/lp/translations/interfaces/webservice.py
> > > > 8: 'IHasTranslationImports' imported but unused
> > > > 8: 'ITranslationImportQueue' imported but unused
> > > > 8: 'ITranslationImportQueueEntry' imported but unused
> > > > 13: 'IPOTemplate' imported but unused
> > > > 15: 'IPOFile' imported but unused
> > >
> > > You should add the following to webservice.py if that's the intention:
> > >
> > > __all__ = [
> > > 'IHasTranslationImports',
> > > 'IPOFile',
> > > 'IPOTemplate',
> > > 'ITranslationImportQueue',
> > > 'ITranslationImportQueueEntry',
> > > ]
> > >
> > > But, why is this necessary?
> >
> > Only interfaces listed in webservices.py will be exported by
> > lazr.restful.
> >
> > Adding __all__ will still generate Pyflakes notices as they are still
> > unused in that file.
>
> It actually fixed it for me. I'm attaching a patch which fixes all the
> lint issues I've seen. It would be nicer if you did that instead :)

I have no idea why this is not fixing for me.

Here is the output for `make lint` after applying your patch:

= 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/registry/interfaces/distroseries.py
  lib/lp/registry/interfaces/sourcepackage.py
  lib/lp/translations/interfaces/webservice.py
  lib/lp/translations/model/potemplate.py

== Pyflakes notices ==

lib/lp/translations/interfaces/webservice.py
    16: 'IHasTranslationImports' imported but unused
    16: 'ITranslationImportQueue' imported but unused
    16: 'ITranslationImportQueueEntry' imported but unused
    21: 'IPOTemplate' imported but unused
    23: 'IPOFile' imported but unused

> Also, "operator not preceded by space" lint issues in this particular
> context seem to be opposed to our style guide. It would be best to
> raise this on launchpad-dev mailing list to come to an agreement and to
> see if we can fix either pylint or style guide.

I have sent and email to lp-dev ML.

> > > > warning: Not importing directory '/usr/share/pyshared/lazr': missing
> > > __init__.py
> > > > warning: Not importing directory '/usr/share/pyshared/lazr': missing
> > > __init__.py
> > > > warning: Not importing directory '/usr/share/pyshared/lazr': missing
> > > __init__.py
> > >
> > > This might be a bug in lazr that needs fixing. Can you please check and
> > > file app...

Revision history for this message
Данило Шеган (danilo) wrote :
Download full text (8.5 KiB)

Hey Adi, we are getting very close. Just a few more small issues to
fix.

У чет, 20. 05 2010. у 18:00 +0000, Adi Roiban пише:

> > It actually fixed it for me. I'm attaching a patch which fixes all the
> > lint issues I've seen. It would be nicer if you did that instead :)
>
> I have no idea why this is not fixing for me.
>
> Here is the output for `make lint` after applying your patch:
>
> = 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/registry/interfaces/distroseries.py
> lib/lp/registry/interfaces/sourcepackage.py
> lib/lp/translations/interfaces/webservice.py
> lib/lp/translations/model/potemplate.py
>
> == Pyflakes notices ==
>
> lib/lp/translations/interfaces/webservice.py
> 16: 'IHasTranslationImports' imported but unused
> 16: 'ITranslationImportQueue' imported but unused
> 16: 'ITranslationImportQueueEntry' imported but unused
> 21: 'IPOTemplate' imported but unused
> 23: 'IPOFile' imported but unused

Hum, very interesting. Perhaps it's a problem with pyflakes
incompatibilities? (FWIW, it seems your branch now specifies __all__
twice)

FWIW, defining __all__ fixes pyflakes warnings for me, but not the
pylint warnings.

> > Also, "operator not preceded by space" lint issues in this particular
> > context seem to be opposed to our style guide. It would be best to
> > raise this on launchpad-dev mailing list to come to an agreement and to
> > see if we can fix either pylint or style guide.
>
> I have sent and email to lp-dev ML.

Cool. It seems everybody agrees that we should not change the
styleguide, but instead not use pylint as is. You can ignore those
warnings and revert that bit of my patch. It'd still be very useful to
resolve the pyflakes warnings that you are getting, though.

> I have filled a bug report for that:
> https://bugs.edge.launchpad.net/ubuntu/+source/lazr.restfulclient/+bug/583426

Excellent, thanks a lot.

> [snip]
> >
> > > > > === modified file 'lib/lp/translations/interfaces/potemplate.py'
> > > > ...
> > > > > source_file = Object(
> > > > > title=_('Source file for this translation template'),
> > > > > readonly=True, schema=ILibraryFileAlias)
> > > >
> > > > I don't remember why we decided not to export this one? It could be
> > > > useful for things like xpipo conversion (with XPI-based translations,
> > > > it should contain a reference to imported en-US.xpi).
> > > >
> > > > Though, we should probably just export it as the public URL, so not a
> > > > big deal (let's not block this branch on this).
> > >
> > > I will leave it for another branch, but for me, it would make sense if
> > > this attribute would be used for all translation templates, not only
> > > XPI.
> >
> > Well, we can reconstruct the gettext POT file completely from what we
> > have in the DB. Thus, it's not needed.
>
> My note was about creating a more consistent API so that in the API
> source_file (or whatever name we choose for it) would return the
> template file for all formats, not only XPI.

Sure...

> Instead of...

Read more...

Revision history for this message
Adi Roiban (adiroiban) wrote :
Download full text (5.1 KiB)

On Thu, 2010-05-20 at 18:48 +0000, Данило Шеган wrote:
> Hey Adi, we are getting very close. Just a few more small issues to
> fix.
>
> У чет, 20. 05 2010. у 18:00 +0000, Adi Roiban пише:
>
> > > It actually fixed it for me. I'm attaching a patch which fixes all the
> > > lint issues I've seen. It would be nicer if you did that instead :)
> >
> > I have no idea why this is not fixing for me.
> >
> > Here is the output for `make lint` after applying your patch:
> >
> > = 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/registry/interfaces/distroseries.py
> > lib/lp/registry/interfaces/sourcepackage.py
> > lib/lp/translations/interfaces/webservice.py
> > lib/lp/translations/model/potemplate.py
> >
> > == Pyflakes notices ==
> >
> > lib/lp/translations/interfaces/webservice.py
> > 16: 'IHasTranslationImports' imported but unused
> > 16: 'ITranslationImportQueue' imported but unused
> > 16: 'ITranslationImportQueueEntry' imported but unused
> > 21: 'IPOTemplate' imported but unused
> > 23: 'IPOFile' imported but unused
>
> Hum, very interesting. Perhaps it's a problem with pyflakes
> incompatibilities? (FWIW, it seems your branch now specifies __all__
> twice)
>
> FWIW, defining __all__ fixes pyflakes warnings for me, but not the
> pylint warnings.

I have removed the duplicate __all__ and only leave it before the
imports.

[snip]
> > > Add __all__: have you tried it at all? It totally works for me, and I'd
> > > be very surprised if it doesn't work for you. Also, __all__ is required
> > > in all our modules as well (importfascist used to check that before,
> > > maybe it doesn't anymore)
>
> I'd really like to get this resolved. It seems the line length is also
> slightly differently defined for you:
>
> === modified file 'lib/lp/translations/interfaces/pofile.py'
> --- lib/lp/translations/interfaces/pofile.py 2010-05-19 17:04:11 +0000
> +++ lib/lp/translations/interfaces/pofile.py 2010-05-20 17:58:16 +0000
> @@ -127,9 +127,9 @@
> '''),
> required=False)
>
> - translation_messages = Attribute(_('''
> - All `ITranslationMessage` objects related to this translation file.
> - '''))
> + translation_messages = Attribute(_(
> + "All `ITranslationMessage` objects related to this "
> + "translation file."))
>
> plural_forms = Int(
> title=_('Number of plural forms for the language of this PO file.'),
>
>
> This is not needed for me, because just putting it as
>
> translation_messages = Attribute(_(
> 'All `ITranslationMessage` objects related to this translation file.'
> ))
>
> works fine. Not that there's anything wrong with your approach, I am just noting this.

This will break the styleguide as there is a space before the ')'

> > > > === modified file 'lib/lp/translations/interfaces/pofile.py'
> > > > --- lib/lp/translations/interfaces/pofile.py 2010-03-08 21:06:34 +0000
> > > > +++ lib/lp/translations/interfaces/pofile.py 2010-05-19 17:04:11 +0...

Read more...

Revision history for this message
Данило Шеган (danilo) wrote :

Hi Adi,

У чет, 20. 05 2010. у 19:06 +0000, Adi Roiban пише:
> > This is not needed for me, because just putting it as
> >
> > translation_messages = Attribute(_(
> > 'All `ITranslationMessage` objects related to this translation file.'
> > ))
> >
> > works fine. Not that there's anything wrong with your approach, I am just noting this.
>
> This will break the styleguide as there is a space before the ')'

Heh, this is not really a space: it's a multiline wrapping of braces,
and we do exactly the same thing with tuples (we'd do it with imports as
well if we didn't have too many of those), as mentioned on [1]:

  something = (
      FirstVeryLongName,
      SecondVeryLongName,
  )

[1] https://dev.launchpad.net/PythonStyleGuide#Multiline%20braces

Anyway, as I said, your style is just as fine as well.

 review approve
 merge approve

I'll get this into ec2 land soon.

Cheers,
Danilo

review: Approve
Revision history for this message
Данило Шеган (danilo) wrote :
Download full text (3.5 KiB)

Hi Adi,

Ok, I realized there are more things to fix first. We've discussed them
over on IRC, but I will repeat myself a bit for reference.

 review needs-fixing

Attribute names should include as little gettextisms as possible:
"all_pofiles" and "all_potemplates" should be "translation_files" and
"translation_templates" (this one is already almost alright in the
branch: I am not sure about the "all").

And further on that topic, we should only show most relevant translation
templates: these are usually only the current, non-obsolete ones.
Obsolete templates are useful only for management purposes, and if we
want, we can export them either separately, or export all of

  getCurrentTranslationTemplates
  getObsoleteTranslationTemplates
  getTranslationTemplates

There is no need to wrap either of the methods in a property in order to
export them. What's else, these methods expose some implementation
details (like "just_ids" parameter, needed for performance reasons
elsewhere) and we should not get them exported as such.

Since I want to see this branch move in soon, I don't want to dwell on
cleaning up these right now.

Now, performance reasons are very important to consider here. We may
have to investigate whether `doNotSnapshot` is needed, and how batching
works over Collection results ([1] suggests it splits results into
batches of 75, but we'd have to check that). If API framework splits
the results into batches and only serializes a single batch (vs
serializing a whole result set), it'd be ok even if we don't use
doNotSnapshot (unless it turns out to be too slow once it hits
edge/staging, when we'll need to fix it).

Also, at the moment, we should export only the most useful list of
templates (because most of this code needs slight refactoring, we should
not export all methods and then stop exporting them soon after). At the
moment, for the "reporting" use case, I believe only "current" templates
make sense to be exported. Now, exporting it with just_ids parameter is
fine as well, and we can fix it with the refactoring in the future.

Next, we did mention how it should already allow read-write access if
you've got sufficient privileges. In my tests that has turned out to be
false. This is definitely not a blocker (it's ok if we start with
read-only API), but we at least need to understand why this is so and
how is write access created (so we don't do it by accident).

Example script I tried out (after doing 'make run' in this branch, run
this in a python console and log in with sufficient privileges, eg.
<email address hidden>:test):

  cachedir = "/home/danilo/.launchpadlib/cache/"
  from launchpadlib.launchpad import Launchpad
  launchpad = Launchpad.login_with('dev testing', 'dev', cachedir)
  ubuntu = launchpad.distributions['ubuntu']
  hoary = ubuntu.series[2]
  pots = hoary.all_translation_templates
  pot = pots[0]
  pot.translation_domain = 'something_else'
  pot.lp_save()

So, suggested course of action to get this landed:

 0. fix "all_pofile" attribute name
 1. Check if we get batching of results on API method calls/attributes
for free (without needing to do anything else)
 2. If 1 is true, check if batching snapshots the en...

Read more...

review: Needs Fixing
Revision history for this message
Adi Roiban (adiroiban) wrote :
Download full text (16.2 KiB)

On Wed, 2010-05-26 at 16:12 +0000, Данило Шеган wrote:
> Review: Needs Fixing
> Hi Adi,
>
> Ok, I realized there are more things to fix first. We've discussed them
> over on IRC, but I will repeat myself a bit for reference.
>
> review needs-fixing
>
> Attribute names should include as little gettextisms as possible:
> "all_pofiles" and "all_potemplates" should be "translation_files" and
> "translation_templates" (this one is already almost alright in the
> branch: I am not sure about the "all").

renamed all_pofiles to translation_files
removed all_translation_templates since its content will be provided by
a method.

I have renamed the name used for exporting pofile and potemplate entries
and collections.

> And further on that topic, we should only show most relevant translation
> templates: these are usually only the current, non-obsolete ones.
> Obsolete templates are useful only for management purposes, and if we
> want, we can export them either separately, or export all of
>
> getCurrentTranslationTemplates
> getObsoleteTranslationTemplates
> getTranslationTemplates
>
> There is no need to wrap either of the methods in a property in order to
> export them. What's else, these methods expose some implementation
> details (like "just_ids" parameter, needed for performance reasons
> elsewhere) and we should not get them exported as such.
>
> Since I want to see this branch move in soon, I don't want to dwell on
> cleaning up these right now.

We can hide the "just_ids" in the exported API call.
We can export getCurrentTranslationTemplates() method, with just_ids
hidden and always set to False.

I still think that exporting getTranslationTemplates() will make the API
simpler (rather than exporting all 3 methods) and the client side check
for active/inactive templates is not that complicated.

> Now, performance reasons are very important to consider here. We may
> have to investigate whether `doNotSnapshot` is needed, and how batching
> works over Collection results ([1] suggests it splits results into
> batches of 75, but we'd have to check that). If API framework splits
> the results into batches and only serializes a single batch (vs
> serializing a whole result set), it'd be ok even if we don't use
> doNotSnapshot (unless it turns out to be too slow once it hits
> edge/staging, when we'll need to fix it).

I will raise this discussion over the lp-dev ML. I still have to read
the code and see exactly why, where and how those snosphots are
needed/used in order to ask a valid question.

I think that we should land this branch and the start of next cycle.
The first attempt should be without doNotSnapshot. If something goes
wrong we can reconsider our options.

> Also, at the moment, we should export only the most useful list of
> templates (because most of this code needs slight refactoring, we should
> not export all methods and then stop exporting them soon after). At the
> moment, for the "reporting" use case, I believe only "current" templates
> make sense to be exported. Now, exporting it with just_ids parameter is
> fine as well, and we can fix it with the refactoring in the future.

See above comments.

> Next, we...

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

We've been discussing this in IRC. It turns out that IDistroSeries.getTranslationTemplates wasn't batching properly because it used shortlist() to limit its results. As a side effect, that listifies the query result—causing all its results to be fetched and marshaled. The batching logic then slices the resulting list instead of the query result, which is somewhat pointless.

The docstring deliberately makes no promise that getTranslationTemplates returns a list, so it's perfectly valid to return the query result directly instead of a shortlisted version of it. Unfortunately the distroseries translations copying script does assert that len(child.getTranslationTemplates()) == 0. This assumes a list-like return value; with a query it should assert that child.getTranslationTemplates.is_empty().

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Revision 10369 looks good to me (except the part where you update the getTranslationTemplates docstring to say it returns a list) but brings another problem to my attention: the purpose of the privileges check in POTemplateSubsetNavigation.traverse is a bit unclear. The comment also contains a typo ("is a is a").

You can also make the check easier to read by eliminating individual, simple cases:

if official_rosetta and potemplate.is_current:
    # This template is available for translation.
    return potemplate
elif check_permission('launchpad.Edit', potemplate):
    # User has special privileges for this template.
    return potemplate
else:
    raise NotFoundError(name)

Also, a bit higher up, I don't think the assertion for "unknown context" carries its weight. We have database constraints enforcing it. So might as well do something simpler such as:

if potemplate.distribution is None:
    product_or_distro = potemplate.productseries.product
else:
    product_or_distro = potemplate.distroseries.distribution
official_rosetta = product_or_distro.official_rosetta

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

For the record I should add that we first discussed the bigger issues (performance, security, various batching hazards) as per Danilo's proposed course of action above. All those points have been satisfied, so the nitpicking about small bits of code means that the branch is in good shape. There is no problematic snapshotting, performance is good, and with the removal of that shortlist we no longer fetch all templates in a distroseries at once.

Revision history for this message
Adi Roiban (adiroiban) wrote :
Download full text (4.6 KiB)

Hi,

Here is the diff with the changes discussed in the latest comment.

If everything is ok, can you please sent this branch to ec2-test?

=== modified file 'lib/lp/registry/model/distroseries.py'
--- lib/lp/registry/model/distroseries.py 2010-05-19 17:24:21 +0000
+++ lib/lp/registry/model/distroseries.py 2010-06-03 15:06:35 +0000
@@ -1886,7 +1886,7 @@
         """See `IHasTranslationTemplates`."""
         result = POTemplate.selectBy(distroseries=self,
                                      orderBy=['-priority', 'name'])
- return shortlist(result, 2000)
+ return result

     def getCurrentTranslationTemplates(self, just_ids=False):
         """See `IHasTranslationTemplates`."""

=== modified file 'lib/lp/registry/model/productseries.py'
--- lib/lp/registry/model/productseries.py 2010-05-17 09:35:18 +0000
+++ lib/lp/registry/model/productseries.py 2010-06-03 15:06:14 +0000
@@ -427,7 +427,7 @@
         """See `IHasTranslationTemplates`."""
         result = POTemplate.selectBy(
             productseries=self, orderBy=['-priority', 'name'])
- return shortlist(result, 300)
+ return result

     def getCurrentTranslationTemplates(self, just_ids=False):
         """See `IHasTranslationTemplates`."""

=== modified file 'lib/lp/registry/model/sourcepackage.py'
--- lib/lp/registry/model/sourcepackage.py 2010-05-17 09:35:18 +0000
+++ lib/lp/registry/model/sourcepackage.py 2010-06-03 15:19:06 +0000
@@ -602,7 +602,7 @@
         result = POTemplate.selectBy(
             distroseries=self.distroseries,
             sourcepackagename=self.sourcepackagename)
- return shortlist(result.orderBy(['-priority', 'name']), 300)
+ return result.orderBy(['-priority', 'name'])

     def getCurrentTranslationTemplates(self, just_ids=False):
         """See `IHasTranslationTemplates`."""

=== modified file 'lib/lp/translations/browser/potemplate.py'
--- lib/lp/translations/browser/potemplate.py 2010-05-27 14:38:56 +0000
+++ lib/lp/translations/browser/potemplate.py 2010-06-03 16:02:24 +0000
@@ -774,18 +774,17 @@

         # Get whether the target for the requested template is officially
         # using Launchpad Translations.
- if potemplate.distribution is not None:
- official_rosetta = potemplate.distribution.official_rosetta
- elif potemplate.product is not None:
- official_rosetta = potemplate.product.official_rosetta
+ if potemplate.distribution is None:
+ product_or_distro = potemplate.productseries.product
         else:
- raise AssertionError('Unknown context for %s' % potemplate.title)
+ product_or_distro = potemplate.distroseries.distribution
+ official_rosetta = product_or_distro.official_rosetta

- if ((official_rosetta and potemplate.iscurrent) or
- check_permission('launchpad.Edit', potemplate)):
- # The target is using officially Launchpad Translations and the
- # template is available to be translated, or the user is a is a
- # Launchpad administrator in which case we show everything.
+ if official_rosetta and potemplate.iscurrent:
+ # This template is av...

Read more...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/interfaces/_schema_circular_imports.py'
--- lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2010-05-14 08:05:23 +0000
+++ lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2010-06-03 16:14:30 +0000
@@ -24,6 +24,8 @@
24 patch_plain_parameter_type, patch_choice_parameter_type,24 patch_plain_parameter_type, patch_choice_parameter_type,
25 patch_reference_property)25 patch_reference_property)
2626
27from canonical.launchpad.interfaces.message import (
28 IIndexedMessage, IMessage, IUserToUserEmail)
27from lp.registry.interfaces.structuralsubscription import (29from lp.registry.interfaces.structuralsubscription import (
28 IStructuralSubscription, IStructuralSubscriptionTarget)30 IStructuralSubscription, IStructuralSubscriptionTarget)
29from lp.bugs.interfaces.bug import IBug, IFrontPageBugAddForm31from lp.bugs.interfaces.bug import IBug, IFrontPageBugAddForm
@@ -52,16 +54,17 @@
52 ISourcePackageRecipe)54 ISourcePackageRecipe)
53from lp.code.interfaces.sourcepackagerecipebuild import (55from lp.code.interfaces.sourcepackagerecipebuild import (
54 ISourcePackageRecipeBuild)56 ISourcePackageRecipeBuild)
57from lp.hardwaredb.interfaces.hwdb import HWBus, IHWSubmission
55from lp.registry.interfaces.distribution import IDistribution58from lp.registry.interfaces.distribution import IDistribution
56from lp.registry.interfaces.distributionmirror import IDistributionMirror59from lp.registry.interfaces.distributionmirror import IDistributionMirror
57from lp.registry.interfaces.distributionsourcepackage import (60from lp.registry.interfaces.distributionsourcepackage import (
58 IDistributionSourcePackage)61 IDistributionSourcePackage)
59from lp.registry.interfaces.distroseries import IDistroSeries62from lp.registry.interfaces.distroseries import IDistroSeries
60from lp.registry.interfaces.person import IPerson, IPersonPublic63from lp.registry.interfaces.person import IPerson, IPersonPublic
61from lp.hardwaredb.interfaces.hwdb import HWBus, IHWSubmission
62from lp.registry.interfaces.pocket import PackagePublishingPocket64from lp.registry.interfaces.pocket import PackagePublishingPocket
63from lp.registry.interfaces.product import IProduct65from lp.registry.interfaces.product import IProduct
64from lp.registry.interfaces.productseries import IProductSeries66from lp.registry.interfaces.productseries import IProductSeries
67from lp.registry.interfaces.sourcepackage import ISourcePackage
65from lp.soyuz.interfaces.archive import IArchive68from lp.soyuz.interfaces.archive import IArchive
66from lp.soyuz.interfaces.archivepermission import (69from lp.soyuz.interfaces.archivepermission import (
67 IArchivePermission)70 IArchivePermission)
@@ -77,9 +80,9 @@
77from lp.soyuz.interfaces.queue import (80from lp.soyuz.interfaces.queue import (
78 IPackageUpload, PackageUploadCustomFormat, PackageUploadStatus)81 IPackageUpload, PackageUploadCustomFormat, PackageUploadStatus)
79from lp.soyuz.interfaces.sourcepackagerelease import ISourcePackageRelease82from lp.soyuz.interfaces.sourcepackagerelease import ISourcePackageRelease
80from lp.registry.interfaces.sourcepackage import ISourcePackage83from lp.translations.interfaces.pofile import IPOFile
81from canonical.launchpad.interfaces.message import (84from lp.translations.interfaces.potemplate import (
82 IIndexedMessage, IMessage, IUserToUserEmail)85 IPOTemplate, IPOTemplateSharingSubset, IPOTemplateSubset)
8386
8487
85IBranch['bug_branches'].value_type.schema = IBugBranch88IBranch['bug_branches'].value_type.schema = IBugBranch
@@ -394,5 +397,16 @@
394# IBugTracker397# IBugTracker
395patch_reference_property(IBugTracker, 'owner', IPerson)398patch_reference_property(IBugTracker, 'owner', IPerson)
396399
400# IPOTemplate
401patch_collection_property(IPOTemplate, 'pofiles', IPOFile)
402patch_reference_property(IPOTemplate, 'product', IProduct)
403
404# IPOTemplateSubset
405patch_reference_property(IPOTemplateSubset, 'distroseries', IDistroSeries)
406patch_reference_property(IPOTemplateSubset, 'productseries', IProductSeries)
407
408# IPOTemplateSharingSubset
409patch_reference_property(IPOTemplateSharingSubset, 'product', IProduct)
410
397# IProductSeries411# IProductSeries
398patch_reference_property(IProductSeries, 'product', IProduct)412patch_reference_property(IProductSeries, 'product', IProduct)
399413
=== modified file 'lib/canonical/launchpad/security.py'
--- lib/canonical/launchpad/security.py 2010-05-24 04:54:44 +0000
+++ lib/canonical/launchpad/security.py 2010-06-03 16:14:30 +0000
@@ -61,8 +61,7 @@
61from lp.services.worlddata.interfaces.language import ILanguage, ILanguageSet61from lp.services.worlddata.interfaces.language import ILanguage, ILanguageSet
62from lp.translations.interfaces.languagepack import ILanguagePack62from lp.translations.interfaces.languagepack import ILanguagePack
63from canonical.launchpad.interfaces.launchpad import (63from canonical.launchpad.interfaces.launchpad import (
64 IBazaarApplication, IHasBug, IHasDrivers, ILaunchpadCelebrities,64 IHasBug, IHasDrivers, ILaunchpadCelebrities, IPersonRoles)
65 IPersonRoles)
66from lp.registry.interfaces.role import IHasOwner65from lp.registry.interfaces.role import IHasOwner
67from lp.registry.interfaces.location import IPersonLocation66from lp.registry.interfaces.location import IPersonLocation
68from lp.registry.interfaces.mailinglist import IMailingListSet67from lp.registry.interfaces.mailinglist import IMailingListSet
@@ -1159,6 +1158,11 @@
1159 self.obj).checkAuthenticated(user))1158 self.obj).checkAuthenticated(user))
11601159
11611160
1161class ViewPOTemplates(AnonymousAuthorization):
1162 """Anyone can view an IPOTemplate."""
1163 usedfor = IPOTemplate
1164
1165
1162class AdminPOTemplateDetails(OnlyRosettaExpertsAndAdmins):1166class AdminPOTemplateDetails(OnlyRosettaExpertsAndAdmins):
1163 """Controls administration of an `IPOTemplate`.1167 """Controls administration of an `IPOTemplate`.
11641168
@@ -1211,6 +1215,11 @@
1211 usedfor = IProductSeries1215 usedfor = IProductSeries
12121216
12131217
1218class ViewPOFile(AnonymousAuthorization):
1219 """Anyone can view an IPOFile."""
1220 usedfor = IPOFile
1221
1222
1214class EditPOFileDetails(EditByOwnersOrAdmins):1223class EditPOFileDetails(EditByOwnersOrAdmins):
1215 usedfor = IPOFile1224 usedfor = IPOFile
12161225
12171226
=== modified file 'lib/lp/registry/configure.zcml'
--- lib/lp/registry/configure.zcml 2010-04-27 13:57:18 +0000
+++ lib/lp/registry/configure.zcml 2010-06-03 16:14:30 +0000
@@ -113,8 +113,6 @@
113 <allow113 <allow
114 interface="lp.translations.interfaces.translationimportqueue.IHasTranslationImports"/>114 interface="lp.translations.interfaces.translationimportqueue.IHasTranslationImports"/>
115 <allow115 <allow
116 interface="lp.translations.interfaces.potemplate.IHasTranslationTemplates"/>
117 <allow
118 interface="canonical.launchpad.interfaces.ICanPublishPackages"/>116 interface="canonical.launchpad.interfaces.ICanPublishPackages"/>
119 <require117 <require
120 permission="launchpad.Edit"118 permission="launchpad.Edit"
@@ -1325,8 +1323,6 @@
1325 interface="lp.bugs.interfaces.bugtarget.IHasBugHeat"/>1323 interface="lp.bugs.interfaces.bugtarget.IHasBugHeat"/>
1326 <allow1324 <allow
1327 interface="lp.translations.interfaces.translationimportqueue.IHasTranslationImports"/>1325 interface="lp.translations.interfaces.translationimportqueue.IHasTranslationImports"/>
1328 <allow
1329 interface="lp.translations.interfaces.potemplate.IHasTranslationTemplates"/>
1330 <require1326 <require
1331 permission="launchpad.Edit"1327 permission="launchpad.Edit"
1332 set_attributes="product name owner driver summary branch1328 set_attributes="product name owner driver summary branch
@@ -1529,8 +1525,6 @@
1529 interface="canonical.launchpad.interfaces.IHasBuildRecords"/>1525 interface="canonical.launchpad.interfaces.IHasBuildRecords"/>
1530 <allow1526 <allow
1531 interface="lp.translations.interfaces.translationimportqueue.IHasTranslationImports"/>1527 interface="lp.translations.interfaces.translationimportqueue.IHasTranslationImports"/>
1532 <allow
1533 interface="lp.translations.interfaces.potemplate.IHasTranslationTemplates"/>
15341528
1535 <!-- IQuestionTarget -->1529 <!-- IQuestionTarget -->
15361530
15371531
=== modified file 'lib/lp/registry/interfaces/distroseries.py'
--- lib/lp/registry/interfaces/distroseries.py 2010-04-27 22:22:20 +0000
+++ lib/lp/registry/interfaces/distroseries.py 2010-06-03 16:14:30 +0000
@@ -51,6 +51,7 @@
51 IStructuralSubscriptionTarget)51 IStructuralSubscriptionTarget)
52from lp.soyuz.interfaces.buildrecords import IHasBuildRecords52from lp.soyuz.interfaces.buildrecords import IHasBuildRecords
53from lp.translations.interfaces.languagepack import ILanguagePack53from lp.translations.interfaces.languagepack import ILanguagePack
54from lp.translations.interfaces.potemplate import IHasTranslationTemplates
5455
5556
56class DistroSeriesNameField(ContentNameField):57class DistroSeriesNameField(ContentNameField):
@@ -113,7 +114,7 @@
113114
114 def _validate(self, version):115 def _validate(self, version):
115 """See `UniqueField`."""116 """See `UniqueField`."""
116 super(DistroSeriesVersionField, self)._validate(version)117 DistroSeriesVersionField._validate(self, version)
117 if not sane_version(version):118 if not sane_version(version):
118 raise LaunchpadValidationError(119 raise LaunchpadValidationError(
119 "%s is not a valid version" % version)120 "%s is not a valid version" % version)
@@ -142,7 +143,7 @@
142class IDistroSeriesPublic(143class IDistroSeriesPublic(
143 ISeriesMixin, IHasAppointedDriver, IHasOwner, IBugTarget,144 ISeriesMixin, IHasAppointedDriver, IHasOwner, IBugTarget,
144 ISpecificationGoal, IHasMilestones, IHasOfficialBugTags,145 ISpecificationGoal, IHasMilestones, IHasOfficialBugTags,
145 IHasBuildRecords):146 IHasBuildRecords, IHasTranslationTemplates):
146 """Public IDistroSeries properties."""147 """Public IDistroSeries properties."""
147148
148 id = Attribute("The distroseries's unique number.")149 id = Attribute("The distroseries's unique number.")
@@ -593,7 +594,8 @@
593 :param dsc: string, original content of the dsc file594 :param dsc: string, original content of the dsc file
594 :param copyright: string, the original debian/copyright content595 :param copyright: string, the original debian/copyright content
595 :param changelog: LFA ID of the debian/changelog file in librarian596 :param changelog: LFA ID of the debian/changelog file in librarian
596 :param changelog_entry: string, changelog extracted from the changesfile597 :param changelog_entry: string, changelog extracted from the
598 changesfile
597 :param architecturehintlist: string, DSC architectures599 :param architecturehintlist: string, DSC architectures
598 :param builddepends: string, DSC build dependencies600 :param builddepends: string, DSC build dependencies
599 :param builddependsindep: string, DSC architecture independent build601 :param builddependsindep: string, DSC architecture independent build
600602
=== modified file 'lib/lp/registry/interfaces/productseries.py'
--- lib/lp/registry/interfaces/productseries.py 2010-03-25 20:36:46 +0000
+++ lib/lp/registry/interfaces/productseries.py 2010-06-03 16:14:30 +0000
@@ -32,6 +32,7 @@
32from lp.registry.interfaces.productrelease import IProductRelease32from lp.registry.interfaces.productrelease import IProductRelease
33from lp.blueprints.interfaces.specificationtarget import (33from lp.blueprints.interfaces.specificationtarget import (
34 ISpecificationGoal)34 ISpecificationGoal)
35from lp.translations.interfaces.potemplate import IHasTranslationTemplates
35from lp.translations.interfaces.translations import (36from lp.translations.interfaces.translations import (
36 TranslationsBranchImportMode)37 TranslationsBranchImportMode)
37from canonical.launchpad.interfaces.validation import validate_url38from canonical.launchpad.interfaces.validation import validate_url
@@ -91,7 +92,8 @@
9192
92class IProductSeriesPublic(93class IProductSeriesPublic(
93 ISeriesMixin, IHasAppointedDriver, IHasOwner, IBugTarget,94 ISeriesMixin, IHasAppointedDriver, IHasOwner, IBugTarget,
94 ISpecificationGoal, IHasMilestones, IHasOfficialBugTags):95 ISpecificationGoal, IHasMilestones, IHasOfficialBugTags,
96 IHasTranslationTemplates):
95 """Public IProductSeries properties."""97 """Public IProductSeries properties."""
96 # XXX Mark Shuttleworth 2004-10-14: Would like to get rid of id in98 # XXX Mark Shuttleworth 2004-10-14: Would like to get rid of id in
97 # interfaces, as soon as SQLobject allows using the object directly99 # interfaces, as soon as SQLobject allows using the object directly
98100
=== modified file 'lib/lp/registry/interfaces/sourcepackage.py'
--- lib/lp/registry/interfaces/sourcepackage.py 2010-04-01 20:26:16 +0000
+++ lib/lp/registry/interfaces/sourcepackage.py 2010-06-03 16:14:30 +0000
@@ -19,21 +19,23 @@
19from zope.interface import Attribute, Interface19from zope.interface import Attribute, Interface
20from zope.schema import Choice, Object, TextLine20from zope.schema import Choice, Object, TextLine
21from lazr.enum import DBEnumeratedType, DBItem21from lazr.enum import DBEnumeratedType, DBItem
22from lazr.restful.fields import Reference, ReferenceChoice
23from lazr.restful.declarations import (
24 call_with, export_as_webservice_entry, export_read_operation,
25 export_write_operation, exported, operation_parameters,
26 operation_returns_entry, REQUEST_USER)
2227
23from canonical.launchpad import _28from canonical.launchpad import _
24from lp.bugs.interfaces.bugtarget import IBugTarget, IHasOfficialBugTags29from lp.bugs.interfaces.bugtarget import IBugTarget, IHasOfficialBugTags
25from lp.code.interfaces.hasbranches import (30from lp.code.interfaces.hasbranches import (
26 IHasBranches, IHasCodeImports, IHasMergeProposals)31 IHasBranches, IHasCodeImports, IHasMergeProposals)
27from lp.soyuz.interfaces.component import IComponent32from lp.soyuz.interfaces.component import IComponent
28from lazr.restful.fields import Reference, ReferenceChoice33from lp.translations.interfaces.potemplate import IHasTranslationTemplates
29from lazr.restful.declarations import (
30 call_with, export_as_webservice_entry, export_read_operation,
31 export_write_operation, exported, operation_parameters,
32 operation_returns_entry, REQUEST_USER)
3334
3435
35class ISourcePackage(IBugTarget, IHasBranches, IHasMergeProposals,36class ISourcePackage(IBugTarget, IHasBranches, IHasMergeProposals,
36 IHasOfficialBugTags, IHasCodeImports):37 IHasOfficialBugTags, IHasCodeImports,
38 IHasTranslationTemplates):
37 """A SourcePackage. See the MagicSourcePackage specification. This39 """A SourcePackage. See the MagicSourcePackage specification. This
38 interface preserves as much as possible of the old SourcePackage40 interface preserves as much as possible of the old SourcePackage
39 interface from the SourcePackage table, with the new table-less41 interface from the SourcePackage table, with the new table-less
4042
=== modified file 'lib/lp/registry/model/distroseries.py'
--- lib/lp/registry/model/distroseries.py 2010-05-12 23:23:19 +0000
+++ lib/lp/registry/model/distroseries.py 2010-06-03 16:14:30 +0000
@@ -103,7 +103,6 @@
103from lp.translations.interfaces.languagepack import LanguagePackType103from lp.translations.interfaces.languagepack import LanguagePackType
104from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet104from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet
105from lp.soyuz.interfaces.queue import PackageUploadStatus105from lp.soyuz.interfaces.queue import PackageUploadStatus
106from lp.translations.interfaces.potemplate import IHasTranslationTemplates
107from lp.soyuz.interfaces.publishedpackage import (106from lp.soyuz.interfaces.publishedpackage import (
108 IPublishedPackageSet)107 IPublishedPackageSet)
109from lp.soyuz.interfaces.publishing import (108from lp.soyuz.interfaces.publishing import (
@@ -131,7 +130,7 @@
131 """A particular series of a distribution."""130 """A particular series of a distribution."""
132 implements(131 implements(
133 ICanPublishPackages, IDistroSeries, IHasBugHeat, IHasBuildRecords,132 ICanPublishPackages, IDistroSeries, IHasBugHeat, IHasBuildRecords,
134 IHasQueueItems, IHasTranslationTemplates)133 IHasQueueItems)
135134
136 _table = 'DistroSeries'135 _table = 'DistroSeries'
137 _defaultOrder = ['distribution', 'version']136 _defaultOrder = ['distribution', 'version']
@@ -1303,15 +1302,13 @@
1303 find_spec = (1302 find_spec = (
1304 DistroSeriesPackageCache,1303 DistroSeriesPackageCache,
1305 BinaryPackageName,1304 BinaryPackageName,
1306 SQL('rank(fti, ftq(%s)) AS rank' % sqlvalues(text))1305 SQL('rank(fti, ftq(%s)) AS rank' % sqlvalues(text)))
1307 )
1308 origin = [1306 origin = [
1309 DistroSeriesPackageCache,1307 DistroSeriesPackageCache,
1310 Join(1308 Join(
1311 BinaryPackageName,1309 BinaryPackageName,
1312 DistroSeriesPackageCache.binarypackagename ==1310 DistroSeriesPackageCache.binarypackagename ==
1313 BinaryPackageName.id1311 BinaryPackageName.id),
1314 )
1315 ]1312 ]
13161313
1317 # Note: When attempting to convert the query below into straight1314 # Note: When attempting to convert the query below into straight
@@ -1325,7 +1322,7 @@
1325 DistroSeriesPackageCache.name ILIKE '%%' || %s || '%%')1322 DistroSeriesPackageCache.name ILIKE '%%' || %s || '%%')
1326 """ % (quote(self),1323 """ % (quote(self),
1327 quote(self.distribution.all_distro_archive_ids),1324 quote(self.distribution.all_distro_archive_ids),
1328 quote(text), quote_like(text))1325 quote(text), quote_like(text)),
1329 ).config(distinct=True)1326 ).config(distinct=True)
13301327
1331 # Create a function that will decorate the results, converting1328 # Create a function that will decorate the results, converting
@@ -1889,7 +1886,7 @@
1889 """See `IHasTranslationTemplates`."""1886 """See `IHasTranslationTemplates`."""
1890 result = POTemplate.selectBy(distroseries=self,1887 result = POTemplate.selectBy(distroseries=self,
1891 orderBy=['-priority', 'name'])1888 orderBy=['-priority', 'name'])
1892 return shortlist(result, 2000)1889 return result
18931890
1894 def getCurrentTranslationTemplates(self, just_ids=False):1891 def getCurrentTranslationTemplates(self, just_ids=False):
1895 """See `IHasTranslationTemplates`."""1892 """See `IHasTranslationTemplates`."""
18961893
=== modified file 'lib/lp/registry/model/productseries.py'
--- lib/lp/registry/model/productseries.py 2010-03-24 02:53:42 +0000
+++ lib/lp/registry/model/productseries.py 2010-06-03 16:14:30 +0000
@@ -53,7 +53,6 @@
53from canonical.launchpad.helpers import shortlist53from canonical.launchpad.helpers import shortlist
54from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities54from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
55from lp.registry.interfaces.packaging import PackagingType55from lp.registry.interfaces.packaging import PackagingType
56from lp.translations.interfaces.potemplate import IHasTranslationTemplates
57from lp.blueprints.interfaces.specification import (56from lp.blueprints.interfaces.specification import (
58 SpecificationDefinitionStatus, SpecificationFilter,57 SpecificationDefinitionStatus, SpecificationFilter,
59 SpecificationGoalStatus, SpecificationImplementationStatus,58 SpecificationGoalStatus, SpecificationImplementationStatus,
@@ -84,7 +83,7 @@
84 HasTranslationImportsMixin, HasTranslationTemplatesMixin,83 HasTranslationImportsMixin, HasTranslationTemplatesMixin,
85 StructuralSubscriptionTargetMixin, SeriesMixin):84 StructuralSubscriptionTargetMixin, SeriesMixin):
86 """A series of product releases."""85 """A series of product releases."""
87 implements(IHasBugHeat, IProductSeries, IHasTranslationTemplates)86 implements(IHasBugHeat, IProductSeries)
8887
89 _table = 'ProductSeries'88 _table = 'ProductSeries'
9089
@@ -428,7 +427,7 @@
428 """See `IHasTranslationTemplates`."""427 """See `IHasTranslationTemplates`."""
429 result = POTemplate.selectBy(428 result = POTemplate.selectBy(
430 productseries=self, orderBy=['-priority', 'name'])429 productseries=self, orderBy=['-priority', 'name'])
431 return shortlist(result, 300)430 return result
432431
433 def getCurrentTranslationTemplates(self, just_ids=False):432 def getCurrentTranslationTemplates(self, just_ids=False):
434 """See `IHasTranslationTemplates`."""433 """See `IHasTranslationTemplates`."""
@@ -614,4 +613,3 @@
614 import_mode != TranslationsBranchImportMode.NO_IMPORT)613 import_mode != TranslationsBranchImportMode.NO_IMPORT)
615614
616 return Store.of(branch).find(ProductSeries, And(*conditions))615 return Store.of(branch).find(ProductSeries, And(*conditions))
617
618616
=== modified file 'lib/lp/registry/model/sourcepackage.py'
--- lib/lp/registry/model/sourcepackage.py 2010-04-14 18:31:51 +0000
+++ lib/lp/registry/model/sourcepackage.py 2010-06-03 16:14:30 +0000
@@ -54,7 +54,6 @@
54from canonical.launchpad.helpers import shortlist54from canonical.launchpad.helpers import shortlist
55from lp.soyuz.interfaces.buildrecords import IHasBuildRecords55from lp.soyuz.interfaces.buildrecords import IHasBuildRecords
56from lp.registry.interfaces.packaging import PackagingType56from lp.registry.interfaces.packaging import PackagingType
57from lp.translations.interfaces.potemplate import IHasTranslationTemplates
58from lp.registry.interfaces.distribution import NoPartnerArchive57from lp.registry.interfaces.distribution import NoPartnerArchive
59from lp.registry.interfaces.pocket import PackagePublishingPocket58from lp.registry.interfaces.pocket import PackagePublishingPocket
60from lp.soyuz.interfaces.publishing import PackagePublishingStatus59from lp.soyuz.interfaces.publishing import PackagePublishingStatus
@@ -170,8 +169,7 @@
170 """169 """
171170
172 implements(171 implements(
173 ISourcePackage, IHasBugHeat, IHasBuildRecords,172 ISourcePackage, IHasBugHeat, IHasBuildRecords, IQuestionTarget)
174 IHasTranslationTemplates, IQuestionTarget)
175173
176 classProvides(ISourcePackageFactory)174 classProvides(ISourcePackageFactory)
177175
@@ -604,7 +602,7 @@
604 result = POTemplate.selectBy(602 result = POTemplate.selectBy(
605 distroseries=self.distroseries,603 distroseries=self.distroseries,
606 sourcepackagename=self.sourcepackagename)604 sourcepackagename=self.sourcepackagename)
607 return shortlist(result.orderBy(['-priority', 'name']), 300)605 return result.orderBy(['-priority', 'name'])
608606
609 def getCurrentTranslationTemplates(self, just_ids=False):607 def getCurrentTranslationTemplates(self, just_ids=False):
610 """See `IHasTranslationTemplates`."""608 """See `IHasTranslationTemplates`."""
611609
=== modified file 'lib/lp/registry/stories/webservice/xx-distroseries.txt'
--- lib/lp/registry/stories/webservice/xx-distroseries.txt 2010-03-06 01:24:05 +0000
+++ lib/lp/registry/stories/webservice/xx-distroseries.txt 2010-06-03 16:14:30 +0000
@@ -1,4 +1,5 @@
1= Distribution Series =1Distribution Series
2===================
23
3We can get a distroseries object via a distribution object in several ways:4We can get a distroseries object via a distribution object in several ways:
45
56
=== modified file 'lib/lp/registry/stories/webservice/xx-project-registry.txt'
--- lib/lp/registry/stories/webservice/xx-project-registry.txt 2010-05-07 18:18:56 +0000
+++ lib/lp/registry/stories/webservice/xx-project-registry.txt 2010-06-03 16:14:30 +0000
@@ -858,8 +858,10 @@
858 >>> babadoo_foobadoo = webservice.get('/babadoo/foobadoo').jsonBody()858 >>> babadoo_foobadoo = webservice.get('/babadoo/foobadoo').jsonBody()
859 >>> pprint_entry(babadoo_foobadoo)859 >>> pprint_entry(babadoo_foobadoo)
860 active: True860 active: True
861 active_milestones_collection_link: u'http://.../babadoo/foobadoo/active_milestones'861 active_milestones_collection_link:
862 all_milestones_collection_link: u'http://.../babadoo/foobadoo/all_milestones'862 u'http://.../babadoo/foobadoo/active_milestones'
863 all_milestones_collection_link:
864 u'http://.../babadoo/foobadoo/all_milestones'
863 branch_link: u'http://.../~babadoo-owner/babadoo/fooey'865 branch_link: u'http://.../~babadoo-owner/babadoo/fooey'
864 bug_reporting_guidelines: None866 bug_reporting_guidelines: None
865 date_created: u'...'867 date_created: u'...'
@@ -1088,12 +1090,15 @@
1088 >>> pprint_entry(result)1090 >>> pprint_entry(result)
1089 date_uploaded: u'2005-06-06T08:59:51.926792+00:00'1091 date_uploaded: u'2005-06-06T08:59:51.926792+00:00'
1090 description: None1092 description: None
1091 file_link: u'http://.../firefox/trunk/0.9.2/+file/firefox_0.9.2.orig.tar.gz/file'1093 file_link:
1094 u'http://.../firefox/trunk/0.9.2/+file/firefox_0.9.2.orig.tar.gz/file'
1092 file_type: u'Code Release Tarball'1095 file_type: u'Code Release Tarball'
1093 project_release_link: u'http://.../firefox/trunk/0.9.2'1096 project_release_link: u'http://.../firefox/trunk/0.9.2'
1094 resource_type_link: u'http://.../#project_release_file'1097 resource_type_link: u'http://.../#project_release_file'
1095 self_link: u'http://.../firefox/trunk/0.9.2/+file/firefox_0.9.2.orig.tar.gz'1098 self_link:
1096 signature_link: u'http://.../firefox/trunk/0.9.2/+file/firefox_0.9.2.orig.tar.gz/signature'1099 u'http://.../firefox/trunk/0.9.2/+file/firefox_0.9.2.orig.tar.gz'
1100 signature_link:
1101 u'http://.../trunk/0.9.2/+file/firefox_0.9.2.orig.tar.gz/signature'
10971102
1098The actual file redirects to the librarian when accessed.1103The actual file redirects to the librarian when accessed.
10991104
11001105
=== modified file 'lib/lp/translations/browser/potemplate.py'
--- lib/lp/translations/browser/potemplate.py 2010-04-22 17:07:56 +0000
+++ lib/lp/translations/browser/potemplate.py 2010-06-03 16:14:30 +0000
@@ -763,8 +763,8 @@
763 def traverse(self, name):763 def traverse(self, name):
764 """Return the IPOTemplate associated with the given name."""764 """Return the IPOTemplate associated with the given name."""
765765
766 assert self.request.method in ['GET', 'HEAD', 'POST'], (766 assert self.request.method in ['GET', 'HEAD', 'PATCH', 'POST'], (
767 'We only know about GET, HEAD, and POST')767 'We only know about GET, HEAD, PATCH and POST')
768768
769 # Get the requested potemplate.769 # Get the requested potemplate.
770 potemplate = self.context.getPOTemplateByName(name)770 potemplate = self.context.getPOTemplateByName(name)
@@ -774,18 +774,17 @@
774774
775 # Get whether the target for the requested template is officially775 # Get whether the target for the requested template is officially
776 # using Launchpad Translations.776 # using Launchpad Translations.
777 if potemplate.distribution is not None:777 if potemplate.distribution is None:
778 official_rosetta = potemplate.distribution.official_rosetta778 product_or_distro = potemplate.productseries.product
779 elif potemplate.product is not None:
780 official_rosetta = potemplate.product.official_rosetta
781 else:779 else:
782 raise AssertionError('Unknown context for %s' % potemplate.title)780 product_or_distro = potemplate.distroseries.distribution
781 official_rosetta = product_or_distro.official_rosetta
783782
784 if ((official_rosetta and potemplate.iscurrent) or783 if official_rosetta and potemplate.iscurrent:
785 check_permission('launchpad.Edit', potemplate)):784 # This template is available for translation.
786 # The target is using officially Launchpad Translations and the785 return potemplate
787 # template is available to be translated, or the user is a is a786 elif check_permission('launchpad.Edit', potemplate):
788 # Launchpad administrator in which case we show everything.787 # User has Edit privileges for this template and can access it.
789 return potemplate788 return potemplate
790 else:789 else:
791 raise NotFoundError(name)790 raise NotFoundError(name)
792791
=== modified file 'lib/lp/translations/interfaces/pofile.py'
--- lib/lp/translations/interfaces/pofile.py 2010-01-08 12:39:51 +0000
+++ lib/lp/translations/interfaces/pofile.py 2010-06-03 16:14:30 +0000
@@ -19,6 +19,9 @@
19from zope.schema.vocabulary import (19from zope.schema.vocabulary import (
20 getVocabularyRegistry, SimpleTerm, SimpleVocabulary)20 getVocabularyRegistry, SimpleTerm, SimpleVocabulary)
2121
22from lazr.restful.declarations import (
23 exported, export_as_webservice_entry)
24
22from canonical.launchpad import _25from canonical.launchpad import _
23from canonical.launchpad.webapp.interfaces import ILaunchBag26from canonical.launchpad.webapp.interfaces import ILaunchBag
24from lp.registry.interfaces.person import IPerson27from lp.registry.interfaces.person import IPerson
@@ -33,8 +36,12 @@
33class IPOFile(IRosettaStats):36class IPOFile(IRosettaStats):
34 """A translation file."""37 """A translation file."""
3538
36 id = Int(39 export_as_webservice_entry(
37 title=_('The translation file id.'), required=True, readonly=True)40 singular_name="translation_file",
41 plural_name="translation_files")
42
43 id = exported(Int(
44 title=_('The translation file id.'), required=True, readonly=True))
3845
39 potemplate = Object(46 potemplate = Object(
40 title=_('The translation file template.'),47 title=_('The translation file template.'),
@@ -121,8 +128,8 @@
121 required=False)128 required=False)
122129
123 translation_messages = Attribute(_(130 translation_messages = Attribute(_(
124 'All `ITranslationMessage` objects related to this translation file.'131 "All `ITranslationMessage` objects related to this "
125 ))132 "translation file."))
126133
127 plural_forms = Int(134 plural_forms = Int(
128 title=_('Number of plural forms for the language of this PO file.'),135 title=_('Number of plural forms for the language of this PO file.'),
@@ -273,7 +280,7 @@
273280
274 def getTranslationRows():281 def getTranslationRows():
275 """Return exportable rows of translation data.282 """Return exportable rows of translation data.
276 283
277 :return: a list of `VPOExport` objects.284 :return: a list of `VPOExport` objects.
278 """285 """
279286
280287
=== modified file 'lib/lp/translations/interfaces/potemplate.py'
--- lib/lp/translations/interfaces/potemplate.py 2009-11-27 12:50:16 +0000
+++ lib/lp/translations/interfaces/potemplate.py 2010-06-03 16:14:30 +0000
@@ -7,13 +7,15 @@
7from zope.schema import (7from zope.schema import (
8 Bool, Bytes, Choice, Datetime, Int, Object, Text, TextLine)8 Bool, Bytes, Choice, Datetime, Int, Object, Text, TextLine)
9from lazr.enum import DBEnumeratedType, DBItem9from lazr.enum import DBEnumeratedType, DBItem
10from lazr.restful.fields import CollectionField, Reference
11from lazr.restful.declarations import (
12 exported, export_as_webservice_entry, export_read_operation,
13 operation_returns_collection_of)
1014
15from canonical.launchpad.fields import ParticipatingPersonChoice
11from canonical.launchpad.interfaces.launchpad import NotFoundError16from canonical.launchpad.interfaces.launchpad import NotFoundError
12from canonical.launchpad.interfaces.librarian import ILibraryFileAlias17from canonical.launchpad.interfaces.librarian import ILibraryFileAlias
13from lp.registry.interfaces.distribution import IDistribution18from lp.registry.interfaces.distribution import IDistribution
14from lp.registry.interfaces.distroseries import IDistroSeries
15from lp.registry.interfaces.product import IProduct
16from lp.registry.interfaces.productseries import IProductSeries
17from lp.translations.interfaces.rosettastats import IRosettaStats19from lp.translations.interfaces.rosettastats import IRosettaStats
18from lp.registry.interfaces.sourcepackagename import (20from lp.registry.interfaces.sourcepackagename import (
19 ISourcePackageName)21 ISourcePackageName)
@@ -74,95 +76,55 @@
74 search or complete listing is requested by the user. """)76 search or complete listing is requested by the user. """)
7577
7678
77class IHasTranslationTemplates(Interface):
78 """An entity that has translation templates attached.
79
80 Examples include `ISourcePackage`, `IDistroSeries`, and `IProductSeries`.
81 """
82
83 has_current_translation_templates = Bool(
84 title=_("Does this object have current translation templates?"),
85 readonly=True)
86
87 def getCurrentTranslationTemplates(just_ids=False):
88 """Return an iterator over all active translation templates.
89
90 A translation template is considered active when both
91 `IPOTemplate`.iscurrent and parent official_rosetta flags
92 are set to True.
93 """
94
95 def getCurrentTranslationFiles(just_ids=False):
96 """Return an iterator over all active translation files.
97
98 A translation file is active if it's attached to an
99 active translation template.
100 """
101
102 def getObsoleteTranslationTemplates():
103 """Return an iterator over its not active translation templates.
104
105 A translation template is considered not active when any of
106 `IPOTemplate`.iscurrent or `IDistribution`.official_rosetta flags
107 are set to False.
108 """
109
110 def getTranslationTemplates():
111 """Return an iterator over all its translation templates.
112
113 The returned templates are either obsolete or current.
114 """
115
116 def getTranslationTemplateFormats():
117 """A list of native formats for all current translation templates.
118 """
119
120
121class IPOTemplate(IRosettaStats):79class IPOTemplate(IRosettaStats):
122 """A translation template."""80 """A translation template."""
12381
124 id = Int(82 export_as_webservice_entry(
83 singular_name='translation_template',
84 plural_name='translation_templates')
85
86 id = exported(Int(
125 title=u"The translation template id.",87 title=u"The translation template id.",
126 required=True, readonly=True)88 required=True, readonly=True))
12789
128 name = TextLine(90 name = exported(TextLine(
129 title=_("Template name"),91 title=_("Template name"),
130 description=_("The name of this PO template, for example "92 description=_("The name of this PO template, for example "
131 "'evolution-2.2'. Each translation template has a "93 "'evolution-2.2'. Each translation template has a "
132 "unique name in its package. It's important to get this "94 "unique name in its package. It's important to get this "
133 "correct, because Launchpad will recommend alternative "95 "correct, because Launchpad will recommend alternative "
134 "translations based on the name."),96 "translations based on the name."),
135 required=True)97 required=True))
13698
137 translation_domain = TextLine(99 translation_domain = exported(TextLine(
138 title=_("Translation domain"),100 title=_("Translation domain"),
139 description=_("The translation domain for a translation template. "101 description=_("The translation domain for a translation template. "
140 "Used with PO file format when generating MO files for inclusion "102 "Used with PO file format when generating MO files for inclusion "
141 "in language pack or MO tarball exports."),103 "in language pack or MO tarball exports."),
142 required=True)104 required=True))
143105
144 description = Text(106 description = exported(Text(
145 title=_("Description"),107 title=_("Description"),
146 description=_("Please provide a brief description of the content "108 description=_("Please provide a brief description of the content "
147 "of this translation template, for example, telling translators "109 "of this translation template, for example, telling translators "
148 "if this template contains strings for end-users or other "110 "if this template contains strings for end-users or other "
149 "developers."),111 "developers."),
150 required=False)112 required=False))
151113
152 header = Text(114 header = Text(
153 title=_('Header'),115 title=_('Header'),
154 description=_("The standard template header in its native format."),116 description=_("The standard template header in its native format."),
155 required=True)117 required=True)
156118
157 iscurrent = Bool(119 iscurrent = exported(Bool(
158 title=_("Accept translations?"),120 title=_("Accept translations?"),
159 description=_(121 description=_(
160 "If unchecked, people can no longer change the template's "122 "If unchecked, people can no longer change the template's "
161 "translations."),123 "translations."),
162 required=True,124 required=True,
163 default=True)125 default=True), exported_as='active')
164126
165 owner = Choice(127 owner = exported(ParticipatingPersonChoice(
166 title=_("Owner"),128 title=_("Owner"),
167 required=True,129 required=True,
168 description=_(130 description=_(
@@ -170,7 +132,7 @@
170 "and change it's status, and can also upload new versions "132 "and change it's status, and can also upload new versions "
171 "of the template when a new release is made or when the "133 "of the template when a new release is made or when the "
172 "translation strings have been changed during development."),134 "translation strings have been changed during development."),
173 vocabulary="ValidOwner")135 vocabulary="ValidOwner"))
174136
175 productseries = Choice(137 productseries = Choice(
176 title=_("Series"),138 title=_("Series"),
@@ -209,29 +171,29 @@
209 required=False,171 required=False,
210 vocabulary="BinaryPackageName")172 vocabulary="BinaryPackageName")
211173
212 languagepack = Bool(174 languagepack = exported(Bool(
213 title=_("Include translations for this template in language packs?"),175 title=_("Include translations for this template in language packs?"),
214 description=_(176 description=_(
215 "Check this box if this template is part of a language pack so "177 "Check this box if this template is part of a language pack so "
216 "its translations should be exported that way."),178 "its translations should be exported that way."),
217 required=True,179 required=True,
218 default=False)180 default=False), exported_as='exported_in_languagepacks')
219181
220 path = TextLine(182 path = exported(TextLine(
221 title=_(183 title=_(
222 "Path of the template in the source tree, including filename."),184 "Path of the template in the source tree, including filename."),
223 required=False)185 required=False))
224186
225 source_file = Object(187 source_file = Object(
226 title=_('Source file for this translation template'),188 title=_('Source file for this translation template'),
227 readonly=True, schema=ILibraryFileAlias)189 readonly=True, schema=ILibraryFileAlias)
228190
229 source_file_format = Choice(191 source_file_format = exported(Choice(
230 title=_("File format for the source file"),192 title=_("File format for the source file"),
231 required=False,193 required=False,
232 vocabulary=TranslationFileFormat)194 vocabulary=TranslationFileFormat), exported_as='format')
233195
234 priority = Int(196 priority = exported(Int(
235 title=_('Priority'),197 title=_('Priority'),
236 required=True,198 required=True,
237 default=0,199 default=0,
@@ -240,7 +202,7 @@
240 'there are multiple templates, and you can use this as a way '202 'there are multiple templates, and you can use this as a way '
241 'of indicating which are more important and should be '203 'of indicating which are more important and should be '
242 'translated first. Pick any number - higher priority '204 'translated first. Pick any number - higher priority '
243 'templates will generally be listed first.'))205 'templates will generally be listed first.')))
244206
245 datecreated = Datetime(207 datecreated = Datetime(
246 title=_('When this translation template was created.'), required=True,208 title=_('When this translation template was created.'), required=True,
@@ -265,8 +227,12 @@
265 '''),227 '''),
266 vocabulary='TranslationPermission')228 vocabulary='TranslationPermission')
267229
268 pofiles = Attribute(230 pofiles = exported(
269 _('All `IPOFile` that exist for this template.'))231 CollectionField(
232 title=_("All translation files that exist for this template."),
233 # Really IPOFile, see _schema_circular_imports.py.
234 value_type=Reference(schema=Interface)),
235 exported_as='translation_files')
270236
271 relatives_by_name = Attribute(237 relatives_by_name = Attribute(
272 _('All `IPOTemplate` objects that have the same name asa this one.'))238 _('All `IPOTemplate` objects that have the same name asa this one.'))
@@ -287,17 +253,24 @@
287253
288 product = Object(254 product = Object(
289 title=_('The `IProduct` to which this translation template belongs.'),255 title=_('The `IProduct` to which this translation template belongs.'),
290 required=False, readonly=True, schema=IProduct)256 required=False, readonly=True,
257 # Really IProduct, see _schema_circular_imports.py.
258 schema=Interface)
291259
292 distribution = Object(260 distribution = Object(
293 title=_(261 title=_(
294 'The `IDistribution` to which this translation template belongs.'262 'The `IDistribution` to which this translation template '
295 ),263 'belongs.'),
296 readonly=True, schema=IDistribution)264 readonly=True, schema=IDistribution)
297265
298 language_count = Int(266 messagecount = exported(Int(
267 title=_('The number of translation messages for this template.'),
268 required=True, readonly=True),
269 exported_as='message_count')
270
271 language_count = exported(Int(
299 title=_('The number of languages for which we have translations.'),272 title=_('The number of languages for which we have translations.'),
300 required=True, readonly=True)273 required=True, readonly=True))
301274
302 translationtarget = Attribute(275 translationtarget = Attribute(
303 _('''276 _('''
@@ -305,9 +278,9 @@
305 This will either be an `ISourcePackage` or an `IProductSeries`.278 This will either be an `ISourcePackage` or an `IProductSeries`.
306 '''))279 '''))
307280
308 date_last_updated = Datetime(281 date_last_updated = exported(Datetime(
309 title=_('Date for last update'),282 title=_('Date for last update'),
310 required=True)283 required=True))
311284
312 uses_english_msgids = Bool(285 uses_english_msgids = Bool(
313 title=_("Uses English strings as msgids"), readonly=True,286 title=_("Uses English strings as msgids"), readonly=True,
@@ -537,12 +510,14 @@
537 distroseries = Object(510 distroseries = Object(
538 title=_(511 title=_(
539 'The `IDistroSeries` associated with this subset.'),512 'The `IDistroSeries` associated with this subset.'),
540 schema=IDistroSeries)513 # Really IDistroSeries, see _schema_circular_imports.py.
514 schema=Interface)
541515
542 productseries = Object(516 productseries = Object(
543 title=_(517 title=_(
544 'The `IProductSeries` associated with this subset.'),518 'The `IProductSeries` associated with this subset.'),
545 schema=IProductSeries)519 # Really IProductSeries, see _schema_circular_imports.py.
520 schema=Interface)
546521
547 iscurrent = Bool(522 iscurrent = Bool(
548 title=_("Filter for iscurrent flag."),523 title=_("Filter for iscurrent flag."),
@@ -599,9 +574,9 @@
599 def getClosestPOTemplate(path):574 def getClosestPOTemplate(path):
600 """Return a `IPOTemplate` with a path closer to given path, or None.575 """Return a `IPOTemplate` with a path closer to given path, or None.
601576
602 If there is no `IPOTemplate` with a common path with the given argument,577 If there is no `IPOTemplate` with a common path with the given,
603 or if there are more than one `IPOTemplate` with the same common path,578 argument or if there are more than one `IPOTemplate` with the same
604 and both are the closer ones, returns None.579 common path, and both are the closer ones, returns None.
605 """580 """
606581
607 def findUniquePathlessMatch(filename):582 def findUniquePathlessMatch(filename):
@@ -677,7 +652,8 @@
677 product = Object(652 product = Object(
678 title=_(653 title=_(
679 'The `IProduct` associated with this subset.'),654 'The `IProduct` associated with this subset.'),
680 schema=IProduct)655 # Really IProduct, see _schema_circular_imports.py.
656 schema=Interface)
681657
682 sourcepackagename = Object(658 sourcepackagename = Object(
683 title=_(659 title=_(
@@ -722,3 +698,52 @@
722 content = Bytes(698 content = Bytes(
723 title=_("PO Template File to Import"),699 title=_("PO Template File to Import"),
724 required=True)700 required=True)
701
702
703class IHasTranslationTemplates(Interface):
704 """An entity that has translation templates attached.
705
706 Examples include `ISourcePackage`, `IDistroSeries`, and `IProductSeries`.
707 """
708
709 has_current_translation_templates = Bool(
710 title=_("Does this object have current translation templates?"),
711 readonly=True)
712
713 def getCurrentTranslationTemplates(just_ids=False):
714 """Return an iterator over all active translation templates.
715
716 A translation template is considered active when both
717 `IPOTemplate`.iscurrent and parent official_rosetta flags
718 are set to True.
719 """
720
721 def getCurrentTranslationFiles(just_ids=False):
722 """Return an iterator over all active translation files.
723
724 A translation file is active if it's attached to an
725 active translation template.
726 """
727
728 def getObsoleteTranslationTemplates():
729 """Return an iterator over its not active translation templates.
730
731 A translation template is considered not active when any of
732 `IPOTemplate`.iscurrent or `IDistribution`.official_rosetta flags
733 are set to False.
734 """
735
736 @export_read_operation()
737 @operation_returns_collection_of(IPOTemplate)
738 def getTranslationTemplates():
739 """Return an iterator over all its translation templates.
740
741 The returned templates are either obsolete or current.
742 """
743
744 def getTranslationTemplateFormats():
745 """A list of native formats for all current translation templates.
746 """
747
748# Monkey patch for circular import avoidance done in
749# _schema_circular_imports.py
725750
=== modified file 'lib/lp/translations/interfaces/webservice.py'
--- lib/lp/translations/interfaces/webservice.py 2009-07-17 02:25:09 +0000
+++ lib/lp/translations/interfaces/webservice.py 2010-06-03 16:14:30 +0000
@@ -1,9 +1,24 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the1# Copyright 2009 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4# pylint: disable-msg=W0611
5
4"""All the interfaces that are exposed through the webservice."""6"""All the interfaces that are exposed through the webservice."""
57
8__all__ = [
9 'IHasTranslationImports',
10 'IPOFile',
11 'IPOTemplate',
12 'ITranslationImportQueue',
13 'ITranslationImportQueueEntry',
14 ]
15
6from lp.translations.interfaces.translationimportqueue import (16from lp.translations.interfaces.translationimportqueue import (
7 IHasTranslationImports,17 IHasTranslationImports,
8 ITranslationImportQueue,18 ITranslationImportQueue,
9 ITranslationImportQueueEntry)19 ITranslationImportQueueEntry)
20
21from lp.translations.interfaces.potemplate import (
22 IPOTemplate)
23from lp.translations.interfaces.pofile import (
24 IPOFile)
1025
=== modified file 'lib/lp/translations/model/distroseries_translations_copy.py'
--- lib/lp/translations/model/distroseries_translations_copy.py 2009-07-17 00:26:05 +0000
+++ lib/lp/translations/model/distroseries_translations_copy.py 2010-06-03 16:14:30 +0000
@@ -44,7 +44,7 @@
44 copier = MultiTableCopy(full_name, translation_tables, logger=logger)44 copier = MultiTableCopy(full_name, translation_tables, logger=logger)
4545
46 # Incremental copy of updates is no longer supported46 # Incremental copy of updates is no longer supported
47 assert len(child.getTranslationTemplates()) == 0, (47 assert child.getTranslationTemplates().is_empty(), (
48 "The child series must not yet have any translation templates.")48 "The child series must not yet have any translation templates.")
4949
50 logger.info(50 logger.info(
5151
=== modified file 'lib/lp/translations/model/potemplate.py'
--- lib/lp/translations/model/potemplate.py 2010-01-12 21:29:03 +0000
+++ lib/lp/translations/model/potemplate.py 2010-06-03 16:14:30 +0000
@@ -1297,8 +1297,8 @@
1297 elif sourcepackagename is None:1297 elif sourcepackagename is None:
1298 # Multiple matches, and for a product not a package.1298 # Multiple matches, and for a product not a package.
1299 logging.warn(1299 logging.warn(
1300 "Found %d templates with path '%s' for productseries %s" % (1300 "Found %d templates with path '%s' for productseries %s",
1301 len(matches), path, productseries.title))1301 len(matches), path, productseries.title)
1302 return None1302 return None
1303 else:1303 else:
1304 # Multiple matches, for a distribution package. Prefer a1304 # Multiple matches, for a distribution package. Prefer a
@@ -1316,9 +1316,9 @@
1316 else:1316 else:
1317 logging.warn(1317 logging.warn(
1318 "Found %d templates with path '%s' for package %s "1318 "Found %d templates with path '%s' for package %s "
1319 "(%d matched on from_sourcepackagename)." % (1319 "(%d matched on from_sourcepackagename).",
1320 len(matches), path, sourcepackagename.name,1320 len(matches), path, sourcepackagename.name,
1321 len(preferred_matches)))1321 len(preferred_matches))
1322 return None1322 return None
13231323
1324 @staticmethod1324 @staticmethod
@@ -1510,10 +1510,6 @@
1510 if flag1510 if flag
1511 ])1511 ])
15121512
1513 # Store sequences so we can detect later whether we changed the
1514 # message.
1515 sequence = row.sequence
1516
1517 # Store the message.1513 # Store the message.
1518 messages.append(msgset)1514 messages.append(msgset)
15191515
15201516
=== added file 'lib/lp/translations/stories/webservice/xx-potemplate.txt'
--- lib/lp/translations/stories/webservice/xx-potemplate.txt 1970-01-01 00:00:00 +0000
+++ lib/lp/translations/stories/webservice/xx-potemplate.txt 2010-06-03 16:14:30 +0000
@@ -0,0 +1,121 @@
1PO Template webservices
2=======================
3
4
5Getting the attributes of a POTemplate
6--------------------------------------
7
8Anonymous users have read access to PO templates attributes.
9
10 >>> from lazr.restful.testing.webservice import pprint_entry
11 >>> potemplate = anon_webservice.get(
12 ... '/ubuntu/hoary/+source/pmount/+pots/pmount').jsonBody()
13 >>> pprint_entry(potemplate)
14 active: True
15 date_last_updated: u'2005-05-06T20:09:23.775993+00:00'
16 description: None
17 exported_in_languagepacks: True
18 format: u'PO format'
19 id: 2
20 language_count: 8
21 message_count: 63
22 name: u'pmount'
23 owner_link: u'http://.../~rosetta-admins'
24 path: u'po/template.pot'
25 priority: 0
26 resource_type_link: u'http://.../#translation_template'
27 self_link: u'http://.../ubuntu/hoary/+source/pmount/+pots/pmount'
28 translation_domain: u'pmount'
29 translation_files_collection_link:
30 u'http://.../pmount/+pots/pmount/translation_files'
31
32"translation_files" will list all POFiles associated with this template.
33
34 >>> translation_files = anon_webservice.get(
35 ... potemplate['translation_files_collection_link']).jsonBody()
36 >>> print translation_files['total_size']
37 9
38 >>> print(translation_files['entries'][0]['resource_type_link'])
39 http://.../#translation_file
40
41
42Getting all potemplates for a distribution series
43-------------------------------------------------
44
45All templates associated to a distribution series are available from the
46'getTranslationTemplates' GET method.
47
48 >>> from zope.component import getUtility
49 >>> from canonical.launchpad.interfaces.launchpad import (
50 ... ILaunchpadCelebrities)
51 >>> from lp.translations.interfaces.potemplate import IPOTemplateSet
52 >>> login('admin@canonical.com')
53 >>> hoary = getUtility(ILaunchpadCelebrities).ubuntu.getSeries('hoary')
54 >>> templates = getUtility(IPOTemplateSet).getSubset(distroseries=hoary)
55 >>> db_count = len(list(templates))
56 >>> logout()
57 >>> all_translation_templates = anon_webservice.named_get(
58 ... '/ubuntu/hoary/', 'getTranslationTemplates').jsonBody()
59 >>> api_count = all_translation_templates['total_size']
60 >>> api_count == db_count
61 True
62 >>> print(all_translation_templates['entries'][0]['resource_type_link'])
63 http://.../#translation_template
64
65
66Getting all potemplates for a product series
67--------------------------------------------
68
69All translation templates for a product series are available using the
70'getTranslationTemplates' GET method.
71
72 >>> login('admin@canonical.com')
73 >>> productseries = factory.makeProductSeries()
74 >>> potemplate_1 = factory.makePOTemplate(productseries=productseries)
75 >>> potemplate_2 = factory.makePOTemplate(productseries=productseries)
76 >>> potemplate_count = 2
77 >>> logout()
78 >>> all_translation_templates = anon_webservice.named_get(
79 ... '/%s/%s' % (
80 ... productseries.product.name,
81 ... productseries.name),
82 ... 'getTranslationTemplates'
83 ... ).jsonBody()
84 >>> api_count = all_translation_templates['total_size']
85 >>> api_count == potemplate_count
86 True
87 >>> print(all_translation_templates['entries'][0]['resource_type_link'])
88 http://.../#translation_template
89
90
91Getting all translation templates for a source package
92------------------------------------------------------
93
94All translation templates for a source package are available using the
95'getTranslationTemplates' GET method.
96
97
98 >>> from zope.component import getUtility
99 >>> from canonical.launchpad.interfaces.launchpad import (
100 ... ILaunchpadCelebrities)
101 >>> from lp.registry.interfaces.sourcepackagename import (
102 ... ISourcePackageNameSet)
103 >>> from lp.translations.interfaces.potemplate import IPOTemplateSet
104 >>> login('admin@canonical.com')
105 >>> hoary = getUtility(ILaunchpadCelebrities).ubuntu.getSeries('hoary')
106 >>> evolution_package = getUtility(ISourcePackageNameSet)['evolution']
107 >>> templates = getUtility(
108 ... IPOTemplateSet).getSubset(
109 ... distroseries=hoary,
110 ... sourcepackagename=evolution_package)
111 >>> db_count = len(list(templates))
112 >>> logout()
113 >>> all_translation_templates = anon_webservice.named_get(
114 ... '/ubuntu/hoary/+source/evolution',
115 ... 'getTranslationTemplates').jsonBody()
116 >>> api_count = all_translation_templates['total_size']
117 >>> api_count == db_count
118 True
119 >>> print(all_translation_templates['entries'][0]['resource_type_link'])
120 http://.../#translation_template
121