Merge lp:~danilo/launchpad/translatedlanguage into lp:launchpad/db-devel
- translatedlanguage
- Merge into db-devel
Status: | Rejected | ||||
---|---|---|---|---|---|
Rejected by: | Данило Шеган | ||||
Proposed branch: | lp:~danilo/launchpad/translatedlanguage | ||||
Merge into: | lp:launchpad/db-devel | ||||
Prerequisite: | lp:~danilo/launchpad/pass-language-to-getdummy | ||||
Diff against target: |
2258 lines (+1076/-208) (has conflicts) 39 files modified
lib/lp/code/browser/tests/test_branchlisting.py (+4/-2) lib/lp/code/browser/tests/test_sourcepackagerecipe.py (+12/-5) lib/lp/code/browser/tests/test_sourcepackagerecipebuild.py (+4/-1) lib/lp/code/model/tests/test_branch.py (+4/-1) lib/lp/code/model/tests/test_linkedbranch.py (+7/-2) lib/lp/code/model/tests/test_sourcepackagerecipebuild.py (+5/-2) lib/lp/registry/browser/tests/distroseries-views.txt (+1/-1) lib/lp/registry/browser/tests/milestone-views.txt (+5/-1) lib/lp/registry/browser/tests/productseries-views.txt (+6/-2) lib/lp/registry/model/person.py (+1/-1) lib/lp/registry/model/productseries.py (+4/-4) lib/lp/registry/stories/webservice/xx-project-registry.txt (+4/-2) lib/lp/registry/tests/test_distroseries.py (+15/-6) lib/lp/registry/tests/test_sourcepackage.py (+9/-2) lib/lp/soyuz/adapters/tests/test_packagelocation.py (+4/-1) lib/lp/soyuz/browser/tests/archive-views.txt (+7/-2) lib/lp/soyuz/browser/tests/test_distrosourcepackagerelease.py (+7/-1) lib/lp/soyuz/browser/tests/test_sourcepackagerelease.py (+11/-5) lib/lp/soyuz/doc/archive.txt (+9/-4) lib/lp/soyuz/stories/soyuz/xx-distribution-archives.txt (+12/-5) lib/lp/soyuz/tests/test_publishing.py (+9/-3) lib/lp/testing/factory.py (+87/-11) lib/lp/testing/tests/test_factory.py (+50/-0) lib/lp/translations/browser/configure.zcml (+1/-1) lib/lp/translations/browser/serieslanguage.py (+11/-8) lib/lp/translations/browser/tests/test_breadcrumbs.py (+3/-1) lib/lp/translations/configure.zcml (+17/-0) lib/lp/translations/doc/translations-export-to-branch.txt (+5/-1) lib/lp/translations/interfaces/potemplate.py (+14/-0) lib/lp/translations/interfaces/productserieslanguage.py (+4/-29) lib/lp/translations/interfaces/translatedlanguage.py (+79/-0) lib/lp/translations/model/potemplate.py (+10/-3) lib/lp/translations/model/productserieslanguage.py (+20/-83) lib/lp/translations/model/translatedlanguage.py (+132/-0) lib/lp/translations/stories/buildfarm/xx-build-summary.txt (+8/-3) lib/lp/translations/tests/test_productserieslanguage.py (+10/-11) lib/lp/translations/tests/test_translatedlanguage.py (+462/-0) lib/lp/translations/tests/test_translationtemplatescollection.py (+19/-0) utilities/make-lp-user (+4/-4) Text conflict in lib/lp/code/interfaces/branch.py |
||||
To merge this branch: | bzr merge lp:~danilo/launchpad/translatedlanguage | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jeroen T. Vermeulen (community) | Needs Resubmitting | ||
Review via email: mp+30760@code.launchpad.net |
Commit message
Provide ITranslatedLanguage interface along with a TranslatedLangu
Description of the change
= ITranslatedLanguage =
This provides a generic ITranslatedLanguage interface for objects which are a translation of something (i.e. a productseries, distroseries, sourcepackage, template) into a single language, along with a mixin that implements this interface in a generic way.
Mixin is to replace most of the model code on DistroSeriesLan
The next steps would be to switch ProductSeriesLa
As a preparation for getting rid of IRosettaStats, I introduce a temporary statistics object implementation (a dict) which we want to switch everything to (a better one is in progress in one of Adi's branches).
The most interesting bit of the code is inside the mixin: POFilesByPOTemp
Unfortunately, for listifying TranslatedLangu
It is (somewhat) indirectly unit-tested inside the TranslationTemp
Full test is otherwise written in a way to make it easy to extend for testing over different types of objects implementing ITranslatedLang
= Tests =
bin/test -cvvt test_translated
= Demo & QA =
A few examples:
https:/
https:/
https:/
https:/
And to confirm we haven't broken DistroSeriesLan
https:/
https:/
= Launchpad lint =
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
./lib/lp/
736: E301 expected 1 blank line, found 2
750: E301 expected 1 blank line, found 2
784: E302 expected 2 blank lines, found 1
1312: E202 whitespace before ']'
1400: E202 whitespace before ']'
1407: E202 whitespace before ']'
1510: E202 whitespace before ']'
(E301 happens due to comments in interface definition, E202 because of multi-line list definitions; I am not changing these for now, though I did fix a bunch of lint issues; also, these are across two different files: interfaces/
Данило Шеган (danilo) wrote : | # |
Preview Diff
1 | === modified file 'lib/lp/code/browser/tests/test_branchlisting.py' | |||
2 | --- lib/lp/code/browser/tests/test_branchlisting.py 2010-05-28 09:54:45 +0000 | |||
3 | +++ lib/lp/code/browser/tests/test_branchlisting.py 2010-07-23 12:49:25 +0000 | |||
4 | @@ -32,12 +32,14 @@ | |||
5 | 32 | from lp.testing import ( | 32 | from lp.testing import ( |
6 | 33 | BrowserTestCase, TestCase, TestCaseWithFactory, login_person, | 33 | BrowserTestCase, TestCase, TestCaseWithFactory, login_person, |
7 | 34 | person_logged_in, time_counter) | 34 | person_logged_in, time_counter) |
8 | 35 | from lp.testing.factory import remove_security_proxy_and_shout_at_engineer | ||
9 | 35 | from lp.testing.views import create_initialized_view | 36 | from lp.testing.views import create_initialized_view |
10 | 36 | from canonical.launchpad.testing.pages import extract_text, find_tag_by_id | 37 | from canonical.launchpad.testing.pages import extract_text, find_tag_by_id |
11 | 37 | from canonical.launchpad.webapp import canonical_url | 38 | from canonical.launchpad.webapp import canonical_url |
12 | 38 | from canonical.launchpad.webapp.servers import LaunchpadTestRequest | 39 | from canonical.launchpad.webapp.servers import LaunchpadTestRequest |
13 | 39 | from canonical.testing.layers import DatabaseFunctionalLayer | 40 | from canonical.testing.layers import DatabaseFunctionalLayer |
14 | 40 | 41 | ||
15 | 42 | |||
16 | 41 | class TestListingToSortOrder(TestCase): | 43 | class TestListingToSortOrder(TestCase): |
17 | 42 | """Tests for the BranchSet._listingSortToOrderBy static method. | 44 | """Tests for the BranchSet._listingSortToOrderBy static method. |
18 | 43 | 45 | ||
19 | @@ -60,6 +62,7 @@ | |||
20 | 60 | 62 | ||
21 | 61 | def assertSortsEqual(self, sort_one, sort_two): | 63 | def assertSortsEqual(self, sort_one, sort_two): |
22 | 62 | """Assert that one list of sort specs is equal to another.""" | 64 | """Assert that one list of sort specs is equal to another.""" |
23 | 65 | |||
24 | 63 | def sort_data(sort): | 66 | def sort_data(sort): |
25 | 64 | return sort.suffix, sort.expr | 67 | return sort.suffix, sort.expr |
26 | 65 | self.assertEqual(map(sort_data, sort_one), map(sort_data, sort_two)) | 68 | self.assertEqual(map(sort_data, sort_one), map(sort_data, sort_two)) |
27 | @@ -352,7 +355,7 @@ | |||
28 | 352 | # series on the main site, not the code site. | 355 | # series on the main site, not the code site. |
29 | 353 | branch = self.factory.makeProductBranch() | 356 | branch = self.factory.makeProductBranch() |
30 | 354 | series = self.factory.makeProductSeries(product=branch.product) | 357 | series = self.factory.makeProductSeries(product=branch.product) |
32 | 355 | series.branch = branch | 358 | remove_security_proxy_and_shout_at_engineer(series).branch = branch |
33 | 356 | browser = self.getUserBrowser( | 359 | browser = self.getUserBrowser( |
34 | 357 | canonical_url(branch.product, rootsite='code')) | 360 | canonical_url(branch.product, rootsite='code')) |
35 | 358 | link = browser.getLink(re.compile('^' + series.name + '$')) | 361 | link = browser.getLink(re.compile('^' + series.name + '$')) |
36 | @@ -402,4 +405,3 @@ | |||
37 | 402 | 405 | ||
38 | 403 | def test_suite(): | 406 | def test_suite(): |
39 | 404 | return unittest.TestLoader().loadTestsFromName(__name__) | 407 | return unittest.TestLoader().loadTestsFromName(__name__) |
40 | 405 | |||
41 | 406 | 408 | ||
42 | === modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py' | |||
43 | --- lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2010-07-21 21:16:54 +0000 | |||
44 | +++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2010-07-23 12:49:25 +0000 | |||
45 | @@ -29,6 +29,7 @@ | |||
46 | 29 | from lp.registry.interfaces.pocket import PackagePublishingPocket | 29 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
47 | 30 | from lp.soyuz.model.processor import ProcessorFamily | 30 | from lp.soyuz.model.processor import ProcessorFamily |
48 | 31 | from lp.testing import ANONYMOUS, BrowserTestCase, login, logout | 31 | from lp.testing import ANONYMOUS, BrowserTestCase, login, logout |
49 | 32 | from lp.testing.factory import remove_security_proxy_and_shout_at_engineer | ||
50 | 32 | 33 | ||
51 | 33 | 34 | ||
52 | 34 | class TestCaseForRecipe(BrowserTestCase): | 35 | class TestCaseForRecipe(BrowserTestCase): |
53 | @@ -45,7 +46,9 @@ | |||
54 | 45 | self.squirrel = self.factory.makeDistroSeries( | 46 | self.squirrel = self.factory.makeDistroSeries( |
55 | 46 | displayname='Secret Squirrel', name='secret', version='100.04', | 47 | displayname='Secret Squirrel', name='secret', version='100.04', |
56 | 47 | distribution=self.ppa.distribution) | 48 | distribution=self.ppa.distribution) |
58 | 48 | self.squirrel.nominatedarchindep = self.squirrel.newArch( | 49 | naked_squirrel = remove_security_proxy_and_shout_at_engineer( |
59 | 50 | self.squirrel) | ||
60 | 51 | naked_squirrel.nominatedarchindep = self.squirrel.newArch( | ||
61 | 49 | 'i386', ProcessorFamily.get(1), False, self.chef, | 52 | 'i386', ProcessorFamily.get(1), False, self.chef, |
62 | 50 | supports_virtualized=True) | 53 | supports_virtualized=True) |
63 | 51 | 54 | ||
64 | @@ -494,7 +497,7 @@ | |||
65 | 494 | def makeBuildJob(self, recipe): | 497 | def makeBuildJob(self, recipe): |
66 | 495 | """Return a build associated with a buildjob.""" | 498 | """Return a build associated with a buildjob.""" |
67 | 496 | build = self.factory.makeSourcePackageRecipeBuild( | 499 | build = self.factory.makeSourcePackageRecipeBuild( |
69 | 497 | recipe=recipe, distroseries=self.squirrel, archive=self.ppa ) | 500 | recipe=recipe, distroseries=self.squirrel, archive=self.ppa) |
70 | 498 | self.factory.makeSourcePackageRecipeBuildJob(recipe_build=build) | 501 | self.factory.makeSourcePackageRecipeBuildJob(recipe_build=build) |
71 | 499 | return build | 502 | return build |
72 | 500 | 503 | ||
73 | @@ -527,6 +530,7 @@ | |||
74 | 527 | self.assertEqual( | 530 | self.assertEqual( |
75 | 528 | set([build1, build2, build3, build4, build5, build6]), | 531 | set([build1, build2, build3, build4, build5, build6]), |
76 | 529 | set(view.builds)) | 532 | set(view.builds)) |
77 | 533 | |||
78 | 530 | def set_day(build, day): | 534 | def set_day(build, day): |
79 | 531 | removeSecurityProxy(build).datebuilt = datetime( | 535 | removeSecurityProxy(build).datebuilt = datetime( |
80 | 532 | 2010, 03, day, tzinfo=utc) | 536 | 2010, 03, day, tzinfo=utc) |
81 | @@ -569,7 +573,8 @@ | |||
82 | 569 | woody = self.factory.makeDistroSeries( | 573 | woody = self.factory.makeDistroSeries( |
83 | 570 | name='woody', displayname='Woody', | 574 | name='woody', displayname='Woody', |
84 | 571 | distribution=self.ppa.distribution) | 575 | distribution=self.ppa.distribution) |
86 | 572 | woody.nominatedarchindep = woody.newArch( | 576 | naked_woody = remove_security_proxy_and_shout_at_engineer(woody) |
87 | 577 | naked_woody.nominatedarchindep = woody.newArch( | ||
88 | 573 | 'i386', ProcessorFamily.get(1), False, self.factory.makePerson(), | 578 | 'i386', ProcessorFamily.get(1), False, self.factory.makePerson(), |
89 | 574 | supports_virtualized=True) | 579 | supports_virtualized=True) |
90 | 575 | 580 | ||
91 | @@ -603,7 +608,8 @@ | |||
92 | 603 | woody = self.factory.makeDistroSeries( | 608 | woody = self.factory.makeDistroSeries( |
93 | 604 | name='woody', displayname='Woody', | 609 | name='woody', displayname='Woody', |
94 | 605 | distribution=self.ppa.distribution) | 610 | distribution=self.ppa.distribution) |
96 | 606 | woody.nominatedarchindep = woody.newArch( | 611 | naked_woody = remove_security_proxy_and_shout_at_engineer(woody) |
97 | 612 | naked_woody.nominatedarchindep = woody.newArch( | ||
98 | 607 | 'i386', ProcessorFamily.get(1), False, self.factory.makePerson(), | 613 | 'i386', ProcessorFamily.get(1), False, self.factory.makePerson(), |
99 | 608 | supports_virtualized=True) | 614 | supports_virtualized=True) |
100 | 609 | 615 | ||
101 | @@ -624,7 +630,8 @@ | |||
102 | 624 | woody = self.factory.makeDistroSeries( | 630 | woody = self.factory.makeDistroSeries( |
103 | 625 | name='woody', displayname='Woody', | 631 | name='woody', displayname='Woody', |
104 | 626 | distribution=self.ppa.distribution) | 632 | distribution=self.ppa.distribution) |
106 | 627 | woody.nominatedarchindep = woody.newArch( | 633 | naked_woody = remove_security_proxy_and_shout_at_engineer(woody) |
107 | 634 | naked_woody.nominatedarchindep = woody.newArch( | ||
108 | 628 | 'i386', ProcessorFamily.get(1), False, self.factory.makePerson(), | 635 | 'i386', ProcessorFamily.get(1), False, self.factory.makePerson(), |
109 | 629 | supports_virtualized=True) | 636 | supports_virtualized=True) |
110 | 630 | 637 | ||
111 | 631 | 638 | ||
112 | === modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipebuild.py' | |||
113 | --- lib/lp/code/browser/tests/test_sourcepackagerecipebuild.py 2010-07-19 09:22:06 +0000 | |||
114 | +++ lib/lp/code/browser/tests/test_sourcepackagerecipebuild.py 2010-07-23 12:49:25 +0000 | |||
115 | @@ -18,6 +18,7 @@ | |||
116 | 18 | from lp.buildmaster.interfaces.buildbase import BuildStatus | 18 | from lp.buildmaster.interfaces.buildbase import BuildStatus |
117 | 19 | from lp.soyuz.model.processor import ProcessorFamily | 19 | from lp.soyuz.model.processor import ProcessorFamily |
118 | 20 | from lp.testing import ANONYMOUS, BrowserTestCase, login, logout | 20 | from lp.testing import ANONYMOUS, BrowserTestCase, login, logout |
119 | 21 | from lp.testing.factory import remove_security_proxy_and_shout_at_engineer | ||
120 | 21 | 22 | ||
121 | 22 | 23 | ||
122 | 23 | class TestSourcePackageRecipeBuild(BrowserTestCase): | 24 | class TestSourcePackageRecipeBuild(BrowserTestCase): |
123 | @@ -36,7 +37,9 @@ | |||
124 | 36 | self.squirrel = self.factory.makeDistroSeries( | 37 | self.squirrel = self.factory.makeDistroSeries( |
125 | 37 | displayname='Secret Squirrel', name='secret', version='100.04', | 38 | displayname='Secret Squirrel', name='secret', version='100.04', |
126 | 38 | distribution=self.ppa.distribution) | 39 | distribution=self.ppa.distribution) |
128 | 39 | self.squirrel.nominatedarchindep = self.squirrel.newArch( | 40 | naked_squirrel = remove_security_proxy_and_shout_at_engineer( |
129 | 41 | self.squirrel) | ||
130 | 42 | naked_squirrel.nominatedarchindep = self.squirrel.newArch( | ||
131 | 40 | 'i386', ProcessorFamily.get(1), False, self.chef, | 43 | 'i386', ProcessorFamily.get(1), False, self.chef, |
132 | 41 | supports_virtualized=True) | 44 | supports_virtualized=True) |
133 | 42 | 45 | ||
134 | 43 | 46 | ||
135 | === modified file 'lib/lp/code/model/tests/test_branch.py' | |||
136 | --- lib/lp/code/model/tests/test_branch.py 2010-07-17 23:15:30 +0000 | |||
137 | +++ lib/lp/code/model/tests/test_branch.py 2010-07-23 12:49:25 +0000 | |||
138 | @@ -537,7 +537,7 @@ | |||
139 | 537 | jobs = list(getUtility(IBranchUpgradeJobSource).iterReady()) | 537 | jobs = list(getUtility(IBranchUpgradeJobSource).iterReady()) |
140 | 538 | self.assertEqual( | 538 | self.assertEqual( |
141 | 539 | jobs, | 539 | jobs, |
143 | 540 | [job,]) | 540 | [job, ]) |
144 | 541 | 541 | ||
145 | 542 | def test_requestUpgrade_no_upgrade_needed(self): | 542 | def test_requestUpgrade_no_upgrade_needed(self): |
146 | 543 | # If a branch doesn't need to be upgraded, requestUpgrade raises an | 543 | # If a branch doesn't need to be upgraded, requestUpgrade raises an |
147 | @@ -635,6 +635,7 @@ | |||
148 | 635 | branch = self.factory.makeProductBranch( | 635 | branch = self.factory.makeProductBranch( |
149 | 636 | product=fooix, owner=eric, name='trunk') | 636 | product=fooix, owner=eric, name='trunk') |
150 | 637 | linked_branch = ICanHasLinkedBranch(future) | 637 | linked_branch = ICanHasLinkedBranch(future) |
151 | 638 | login_person(fooix.owner) | ||
152 | 638 | linked_branch.setBranch(branch) | 639 | linked_branch.setBranch(branch) |
153 | 639 | self.assertEqual( | 640 | self.assertEqual( |
154 | 640 | [linked_branch], | 641 | [linked_branch], |
155 | @@ -827,6 +828,7 @@ | |||
156 | 827 | product = branch.product | 828 | product = branch.product |
157 | 828 | series = self.factory.makeProductSeries(product=product) | 829 | series = self.factory.makeProductSeries(product=product) |
158 | 829 | linked_branch = ICanHasLinkedBranch(series) | 830 | linked_branch = ICanHasLinkedBranch(series) |
159 | 831 | login_person(series.owner) | ||
160 | 830 | linked_branch.setBranch(branch) | 832 | linked_branch.setBranch(branch) |
161 | 831 | self.assertBzrIdentity(branch, linked_branch.bzr_path) | 833 | self.assertBzrIdentity(branch, linked_branch.bzr_path) |
162 | 832 | 834 | ||
163 | @@ -852,6 +854,7 @@ | |||
164 | 852 | removeSecurityProxy(branch.product)) | 854 | removeSecurityProxy(branch.product)) |
165 | 853 | series_link = ICanHasLinkedBranch(series) | 855 | series_link = ICanHasLinkedBranch(series) |
166 | 854 | product_link.setBranch(branch) | 856 | product_link.setBranch(branch) |
167 | 857 | login_person(series.owner) | ||
168 | 855 | series_link.setBranch(branch) | 858 | series_link.setBranch(branch) |
169 | 856 | self.assertBzrIdentity(branch, product_link.bzr_path) | 859 | self.assertBzrIdentity(branch, product_link.bzr_path) |
170 | 857 | 860 | ||
171 | 858 | 861 | ||
172 | === modified file 'lib/lp/code/model/tests/test_linkedbranch.py' | |||
173 | --- lib/lp/code/model/tests/test_linkedbranch.py 2010-04-16 15:06:55 +0000 | |||
174 | +++ lib/lp/code/model/tests/test_linkedbranch.py 2010-07-23 12:49:25 +0000 | |||
175 | @@ -18,6 +18,7 @@ | |||
176 | 18 | from lp.registry.interfaces.distroseries import NoSuchDistroSeries | 18 | from lp.registry.interfaces.distroseries import NoSuchDistroSeries |
177 | 19 | from lp.registry.interfaces.pocket import PackagePublishingPocket | 19 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
178 | 20 | from lp.testing import run_with_login, TestCaseWithFactory | 20 | from lp.testing import run_with_login, TestCaseWithFactory |
179 | 21 | from lp.testing.factory import remove_security_proxy_and_shout_at_engineer | ||
180 | 21 | 22 | ||
181 | 22 | 23 | ||
182 | 23 | class TestProductSeriesLinkedBranch(TestCaseWithFactory): | 24 | class TestProductSeriesLinkedBranch(TestCaseWithFactory): |
183 | @@ -27,7 +28,9 @@ | |||
184 | 27 | def test_branch(self): | 28 | def test_branch(self): |
185 | 28 | # The linked branch of a product series is its branch attribute. | 29 | # The linked branch of a product series is its branch attribute. |
186 | 29 | product_series = self.factory.makeProductSeries() | 30 | product_series = self.factory.makeProductSeries() |
188 | 30 | product_series.branch = self.factory.makeProductBranch( | 31 | naked_product_series = remove_security_proxy_and_shout_at_engineer( |
189 | 32 | product_series) | ||
190 | 33 | naked_product_series.branch = self.factory.makeProductBranch( | ||
191 | 31 | product=product_series.product) | 34 | product=product_series.product) |
192 | 32 | self.assertEqual( | 35 | self.assertEqual( |
193 | 33 | product_series.branch, ICanHasLinkedBranch(product_series).branch) | 36 | product_series.branch, ICanHasLinkedBranch(product_series).branch) |
194 | @@ -35,9 +38,11 @@ | |||
195 | 35 | def test_setBranch(self): | 38 | def test_setBranch(self): |
196 | 36 | # setBranch sets the linked branch of the product series. | 39 | # setBranch sets the linked branch of the product series. |
197 | 37 | product_series = self.factory.makeProductSeries() | 40 | product_series = self.factory.makeProductSeries() |
198 | 41 | naked_product_series = remove_security_proxy_and_shout_at_engineer( | ||
199 | 42 | product_series) | ||
200 | 38 | branch = self.factory.makeProductBranch( | 43 | branch = self.factory.makeProductBranch( |
201 | 39 | product=product_series.product) | 44 | product=product_series.product) |
203 | 40 | ICanHasLinkedBranch(product_series).setBranch(branch) | 45 | ICanHasLinkedBranch(naked_product_series).setBranch(branch) |
204 | 41 | self.assertEqual(branch, product_series.branch) | 46 | self.assertEqual(branch, product_series.branch) |
205 | 42 | 47 | ||
206 | 43 | def test_bzr_path(self): | 48 | def test_bzr_path(self): |
207 | 44 | 49 | ||
208 | === modified file 'lib/lp/code/model/tests/test_sourcepackagerecipebuild.py' | |||
209 | --- lib/lp/code/model/tests/test_sourcepackagerecipebuild.py 2010-07-20 12:12:46 +0000 | |||
210 | +++ lib/lp/code/model/tests/test_sourcepackagerecipebuild.py 2010-07-23 12:49:25 +0000 | |||
211 | @@ -37,6 +37,7 @@ | |||
212 | 37 | from lp.soyuz.model.processor import ProcessorFamily | 37 | from lp.soyuz.model.processor import ProcessorFamily |
213 | 38 | from lp.soyuz.tests.soyuzbuilddhelpers import WaitingSlave | 38 | from lp.soyuz.tests.soyuzbuilddhelpers import WaitingSlave |
214 | 39 | from lp.testing import ANONYMOUS, login, person_logged_in, TestCaseWithFactory | 39 | from lp.testing import ANONYMOUS, login, person_logged_in, TestCaseWithFactory |
215 | 40 | from lp.testing.factory import remove_security_proxy_and_shout_at_engineer | ||
216 | 40 | from lp.testing.fakemethod import FakeMethod | 41 | from lp.testing.fakemethod import FakeMethod |
217 | 41 | from lp.testing.mail_helpers import pop_notifications | 42 | from lp.testing.mail_helpers import pop_notifications |
218 | 42 | 43 | ||
219 | @@ -53,7 +54,9 @@ | |||
220 | 53 | distroseries_i386 = distroseries.newArch( | 54 | distroseries_i386 = distroseries.newArch( |
221 | 54 | 'i386', ProcessorFamily.get(1), False, person, | 55 | 'i386', ProcessorFamily.get(1), False, person, |
222 | 55 | supports_virtualized=True) | 56 | supports_virtualized=True) |
224 | 56 | distroseries.nominatedarchindep = distroseries_i386 | 57 | naked_distroseries = remove_security_proxy_and_shout_at_engineer( |
225 | 58 | distroseries) | ||
226 | 59 | naked_distroseries.nominatedarchindep = distroseries_i386 | ||
227 | 57 | 60 | ||
228 | 58 | return getUtility(ISourcePackageRecipeBuildSource).new( | 61 | return getUtility(ISourcePackageRecipeBuildSource).new( |
229 | 59 | distroseries=distroseries, | 62 | distroseries=distroseries, |
230 | @@ -320,7 +323,7 @@ | |||
231 | 320 | removeSecurityProxy(build).buildstate = BuildStatus.FULLYBUILT | 323 | removeSecurityProxy(build).buildstate = BuildStatus.FULLYBUILT |
232 | 321 | IStore(build).flush() | 324 | IStore(build).flush() |
233 | 322 | build.notify() | 325 | build.notify() |
235 | 323 | (message,) = pop_notifications() | 326 | (message, ) = pop_notifications() |
236 | 324 | requester = build.requester | 327 | requester = build.requester |
237 | 325 | requester_address = format_address( | 328 | requester_address = format_address( |
238 | 326 | requester.displayname, requester.preferredemail.email) | 329 | requester.displayname, requester.preferredemail.email) |
239 | 327 | 330 | ||
240 | === modified file 'lib/lp/registry/browser/tests/distroseries-views.txt' | |||
241 | --- lib/lp/registry/browser/tests/distroseries-views.txt 2010-05-24 22:04:19 +0000 | |||
242 | +++ lib/lp/registry/browser/tests/distroseries-views.txt 2010-07-23 12:49:25 +0000 | |||
243 | @@ -196,7 +196,7 @@ | |||
244 | 196 | >>> [field.__name__ for field in view.form_fields] | 196 | >>> [field.__name__ for field in view.form_fields] |
245 | 197 | ['displayname', 'title', 'summary', 'description', 'status'] | 197 | ['displayname', 'title', 'summary', 'description', 'status'] |
246 | 198 | 198 | ||
248 | 199 | >>> print view.widgets.get('status')._getFormValue() | 199 | >>> print view.widgets.get('status')._getFormValue().title |
249 | 200 | Active Development | 200 | Active Development |
250 | 201 | 201 | ||
251 | 202 | 202 | ||
252 | 203 | 203 | ||
253 | === modified file 'lib/lp/registry/browser/tests/milestone-views.txt' | |||
254 | --- lib/lp/registry/browser/tests/milestone-views.txt 2010-06-30 19:37:09 +0000 | |||
255 | +++ lib/lp/registry/browser/tests/milestone-views.txt 2010-07-23 12:49:25 +0000 | |||
256 | @@ -579,9 +579,13 @@ | |||
257 | 579 | The driver of a series that belongs to an `IDerivativeDistribution` is a | 579 | The driver of a series that belongs to an `IDerivativeDistribution` is a |
258 | 580 | release manager and can create milestones. | 580 | release manager and can create milestones. |
259 | 581 | 581 | ||
260 | 582 | >>> from lp.testing.factory import ( | ||
261 | 583 | ... remove_security_proxy_and_shout_at_engineer) | ||
262 | 582 | >>> distroseries = factory.makeDistroRelease(name='pumpkin') | 584 | >>> distroseries = factory.makeDistroRelease(name='pumpkin') |
263 | 583 | >>> driver = factory.makePerson(name='a-driver') | 585 | >>> driver = factory.makePerson(name='a-driver') |
265 | 584 | >>> distroseries.driver = driver | 586 | >>> naked_distroseries = remove_security_proxy_and_shout_at_engineer( |
266 | 587 | ... distroseries) | ||
267 | 588 | >>> naked_distroseries.driver = driver | ||
268 | 585 | >>> login_person(driver) | 589 | >>> login_person(driver) |
269 | 586 | 590 | ||
270 | 587 | >>> form = { | 591 | >>> form = { |
271 | 588 | 592 | ||
272 | === modified file 'lib/lp/registry/browser/tests/productseries-views.txt' | |||
273 | --- lib/lp/registry/browser/tests/productseries-views.txt 2010-06-13 02:01:25 +0000 | |||
274 | +++ lib/lp/registry/browser/tests/productseries-views.txt 2010-07-23 12:49:25 +0000 | |||
275 | @@ -255,6 +255,8 @@ | |||
276 | 255 | 255 | ||
277 | 256 | >>> from datetime import datetime | 256 | >>> from datetime import datetime |
278 | 257 | >>> from pytz import UTC | 257 | >>> from pytz import UTC |
279 | 258 | >>> from lp.testing.factory import ( | ||
280 | 259 | ... remove_security_proxy_and_shout_at_engineer) | ||
281 | 258 | 260 | ||
282 | 259 | >>> product = factory.makeProduct(name="field", displayname='Field') | 261 | >>> product = factory.makeProduct(name="field", displayname='Field') |
283 | 260 | >>> productseries = factory.makeProductSeries( | 262 | >>> productseries = factory.makeProductSeries( |
284 | @@ -263,7 +265,9 @@ | |||
285 | 263 | 265 | ||
286 | 264 | # Hack the creation date for testing purposes. | 266 | # Hack the creation date for testing purposes. |
287 | 265 | >>> test_date = datetime(2009, 05, 01, 19, 34, 24, tzinfo=UTC) | 267 | >>> test_date = datetime(2009, 05, 01, 19, 34, 24, tzinfo=UTC) |
289 | 266 | >>> productseries.datecreated = test_date | 268 | >>> naked_productseries = remove_security_proxy_and_shout_at_engineer( |
290 | 269 | ... productseries) | ||
291 | 270 | >>> naked_productseries.datecreated = test_date | ||
292 | 267 | 271 | ||
293 | 268 | Users without edit permission cannot access the view. | 272 | Users without edit permission cannot access the view. |
294 | 269 | 273 | ||
295 | @@ -470,7 +474,7 @@ | |||
296 | 470 | 474 | ||
297 | 471 | The series status is set to obsolete and the releasefileglob was set to None. | 475 | The series status is set to obsolete and the releasefileglob was set to None. |
298 | 472 | 476 | ||
300 | 473 | >>> print productseries.status | 477 | >>> print productseries.status.title |
301 | 474 | Obsolete | 478 | Obsolete |
302 | 475 | >>> print productseries.releasefileglob | 479 | >>> print productseries.releasefileglob |
303 | 476 | None | 480 | None |
304 | 477 | 481 | ||
305 | === modified file 'lib/lp/registry/model/person.py' | |||
306 | --- lib/lp/registry/model/person.py 2010-07-22 05:11:59 +0000 | |||
307 | +++ lib/lp/registry/model/person.py 2010-07-23 12:49:25 +0000 | |||
308 | @@ -2495,7 +2495,7 @@ | |||
309 | 2495 | creation_rationale, comment=comment) | 2495 | creation_rationale, comment=comment) |
310 | 2496 | db_updated = True | 2496 | db_updated = True |
311 | 2497 | 2497 | ||
313 | 2498 | return IPerson(account), db_updated | 2498 | return IPerson(account), db_updated |
314 | 2499 | 2499 | ||
315 | 2500 | def newTeam(self, teamowner, name, displayname, teamdescription=None, | 2500 | def newTeam(self, teamowner, name, displayname, teamdescription=None, |
316 | 2501 | subscriptionpolicy=TeamSubscriptionPolicy.MODERATED, | 2501 | subscriptionpolicy=TeamSubscriptionPolicy.MODERATED, |
317 | 2502 | 2502 | ||
318 | === modified file 'lib/lp/registry/model/productseries.py' | |||
319 | --- lib/lp/registry/model/productseries.py 2010-07-15 15:01:18 +0000 | |||
320 | +++ lib/lp/registry/model/productseries.py 2010-07-23 12:49:25 +0000 | |||
321 | @@ -469,8 +469,8 @@ | |||
322 | 469 | pofile.currentCount(), | 469 | pofile.currentCount(), |
323 | 470 | pofile.updatesCount(), | 470 | pofile.updatesCount(), |
324 | 471 | pofile.rosettaCount(), | 471 | pofile.rosettaCount(), |
327 | 472 | pofile.unreviewedCount(), | 472 | pofile.unreviewedCount()) |
328 | 473 | pofile.date_changed) | 473 | psl.last_changed_date = pofile.date_changed |
329 | 474 | results.append(psl) | 474 | results.append(psl) |
330 | 475 | else: | 475 | else: |
331 | 476 | # If there is more than one template, do a single | 476 | # If there is more than one template, do a single |
332 | @@ -512,8 +512,8 @@ | |||
333 | 512 | for (language, imported, changed, new, unreviewed, | 512 | for (language, imported, changed, new, unreviewed, |
334 | 513 | last_changed) in ordered_results: | 513 | last_changed) in ordered_results: |
335 | 514 | psl = ProductSeriesLanguage(self, language) | 514 | psl = ProductSeriesLanguage(self, language) |
338 | 515 | psl.setCounts( | 515 | psl.setCounts(total, imported, changed, new, unreviewed) |
339 | 516 | total, imported, changed, new, unreviewed, last_changed) | 516 | psl.last_changed_date = last_changed |
340 | 517 | results.append(psl) | 517 | results.append(psl) |
341 | 518 | 518 | ||
342 | 519 | return results | 519 | return results |
343 | 520 | 520 | ||
344 | === modified file 'lib/lp/registry/stories/webservice/xx-project-registry.txt' | |||
345 | --- lib/lp/registry/stories/webservice/xx-project-registry.txt 2010-06-14 18:32:58 +0000 | |||
346 | +++ lib/lp/registry/stories/webservice/xx-project-registry.txt 2010-07-23 12:49:25 +0000 | |||
347 | @@ -847,15 +847,17 @@ | |||
348 | 847 | The entry for a project series is available at its canonical URL on the | 847 | The entry for a project series is available at its canonical URL on the |
349 | 848 | virtual host. | 848 | virtual host. |
350 | 849 | 849 | ||
351 | 850 | >>> from zope.security.proxy import removeSecurityProxy | ||
352 | 850 | >>> login('test@canonical.com') | 851 | >>> login('test@canonical.com') |
353 | 851 | >>> babadoo_owner = factory.makePerson(name='babadoo-owner') | 852 | >>> babadoo_owner = factory.makePerson(name='babadoo-owner') |
354 | 852 | >>> babadoo = factory.makeProduct(name='babadoo', owner=babadoo_owner) | 853 | >>> babadoo = factory.makeProduct(name='babadoo', owner=babadoo_owner) |
355 | 853 | >>> foobadoo = factory.makeProductSeries( | 854 | >>> foobadoo = factory.makeProductSeries( |
356 | 854 | ... product=babadoo, name='foobadoo', owner=babadoo_owner) | 855 | ... product=babadoo, name='foobadoo', owner=babadoo_owner) |
358 | 855 | >>> foobadoo.summary = u'Foobadoo support for Babadoo' | 856 | >>> removeSecurityProxy(foobadoo).summary = ( |
359 | 857 | ... u'Foobadoo support for Babadoo') | ||
360 | 856 | >>> fooey = factory.makeAnyBranch( | 858 | >>> fooey = factory.makeAnyBranch( |
361 | 857 | ... product=babadoo, name='fooey', owner=babadoo_owner) | 859 | ... product=babadoo, name='fooey', owner=babadoo_owner) |
363 | 858 | >>> foobadoo.branch = fooey | 860 | >>> removeSecurityProxy(foobadoo).branch = fooey |
364 | 859 | >>> logout() | 861 | >>> logout() |
365 | 860 | 862 | ||
366 | 861 | >>> babadoo_foobadoo = webservice.get('/babadoo/foobadoo').jsonBody() | 863 | >>> babadoo_foobadoo = webservice.get('/babadoo/foobadoo').jsonBody() |
367 | 862 | 864 | ||
368 | === modified file 'lib/lp/registry/tests/test_distroseries.py' | |||
369 | --- lib/lp/registry/tests/test_distroseries.py 2010-06-23 23:23:28 +0000 | |||
370 | +++ lib/lp/registry/tests/test_distroseries.py 2010-07-23 12:49:25 +0000 | |||
371 | @@ -24,6 +24,7 @@ | |||
372 | 24 | active_publishing_status, PackagePublishingStatus) | 24 | active_publishing_status, PackagePublishingStatus) |
373 | 25 | from lp.soyuz.model.processor import ProcessorFamilySet | 25 | from lp.soyuz.model.processor import ProcessorFamilySet |
374 | 26 | from lp.testing import TestCase, TestCaseWithFactory | 26 | from lp.testing import TestCase, TestCaseWithFactory |
375 | 27 | from lp.testing.factory import remove_security_proxy_and_shout_at_engineer | ||
376 | 27 | from lp.soyuz.tests.test_publishing import SoyuzTestPublisher | 28 | from lp.soyuz.tests.test_publishing import SoyuzTestPublisher |
377 | 28 | from lp.translations.interfaces.translations import ( | 29 | from lp.translations.interfaces.translations import ( |
378 | 29 | TranslationsBranchImportMode) | 30 | TranslationsBranchImportMode) |
379 | @@ -285,7 +286,9 @@ | |||
380 | 285 | self.linkPackage('hot') | 286 | self.linkPackage('hot') |
381 | 286 | self.makeSeriesPackage('cold') | 287 | self.makeSeriesPackage('cold') |
382 | 287 | product_series = self.linkPackage('cold') | 288 | product_series = self.linkPackage('cold') |
384 | 288 | product_series.product.bugtraker = self.factory.makeBugTracker() | 289 | naked_product_series = remove_security_proxy_and_shout_at_engineer( |
385 | 290 | product_series) | ||
386 | 291 | naked_product_series.product.bugtraker = self.factory.makeBugTracker() | ||
387 | 289 | packagings = self.series.getPrioritizedlPackagings() | 292 | packagings = self.series.getPrioritizedlPackagings() |
388 | 290 | names = [packaging.sourcepackagename.name for packaging in packagings] | 293 | names = [packaging.sourcepackagename.name for packaging in packagings] |
389 | 291 | expected = [u'hot', u'cold', u'linked'] | 294 | expected = [u'hot', u'cold', u'linked'] |
390 | @@ -296,7 +299,9 @@ | |||
391 | 296 | self.linkPackage('translatable') | 299 | self.linkPackage('translatable') |
392 | 297 | self.makeSeriesPackage('withbranch') | 300 | self.makeSeriesPackage('withbranch') |
393 | 298 | product_series = self.linkPackage('withbranch') | 301 | product_series = self.linkPackage('withbranch') |
395 | 299 | product_series.branch = self.factory.makeBranch() | 302 | naked_product_series = remove_security_proxy_and_shout_at_engineer( |
396 | 303 | product_series) | ||
397 | 304 | naked_product_series.branch = self.factory.makeBranch() | ||
398 | 300 | packagings = self.series.getPrioritizedlPackagings() | 305 | packagings = self.series.getPrioritizedlPackagings() |
399 | 301 | names = [packaging.sourcepackagename.name for packaging in packagings] | 306 | names = [packaging.sourcepackagename.name for packaging in packagings] |
400 | 302 | expected = [u'translatable', u'linked', u'withbranch'] | 307 | expected = [u'translatable', u'linked', u'withbranch'] |
401 | @@ -308,8 +313,10 @@ | |||
402 | 308 | self.linkPackage('translatable') | 313 | self.linkPackage('translatable') |
403 | 309 | self.makeSeriesPackage('importabletranslatable') | 314 | self.makeSeriesPackage('importabletranslatable') |
404 | 310 | product_series = self.linkPackage('importabletranslatable') | 315 | product_series = self.linkPackage('importabletranslatable') |
407 | 311 | product_series.branch = self.factory.makeBranch() | 316 | naked_product_series = remove_security_proxy_and_shout_at_engineer( |
408 | 312 | product_series.translations_autoimport_mode = ( | 317 | product_series) |
409 | 318 | naked_product_series.branch = self.factory.makeBranch() | ||
410 | 319 | naked_product_series.translations_autoimport_mode = ( | ||
411 | 313 | TranslationsBranchImportMode.IMPORT_TEMPLATES) | 320 | TranslationsBranchImportMode.IMPORT_TEMPLATES) |
412 | 314 | packagings = self.series.getPrioritizedlPackagings() | 321 | packagings = self.series.getPrioritizedlPackagings() |
413 | 315 | names = [packaging.sourcepackagename.name for packaging in packagings] | 322 | names = [packaging.sourcepackagename.name for packaging in packagings] |
414 | @@ -343,7 +350,9 @@ | |||
415 | 343 | 350 | ||
416 | 344 | new_distroseries = ( | 351 | new_distroseries = ( |
417 | 345 | self.factory.makeDistroRelease(name=u"sampleseries")) | 352 | self.factory.makeDistroRelease(name=u"sampleseries")) |
419 | 346 | new_distroseries.hide_all_translations = False | 353 | naked_new_distroseries = remove_security_proxy_and_shout_at_engineer( |
420 | 354 | new_distroseries) | ||
421 | 355 | naked_new_distroseries.hide_all_translations = False | ||
422 | 347 | transaction.commit() | 356 | transaction.commit() |
423 | 348 | translatables = self._get_translatables() | 357 | translatables = self._get_translatables() |
424 | 349 | self.failUnlessEqual( | 358 | self.failUnlessEqual( |
425 | @@ -365,7 +374,7 @@ | |||
426 | 365 | translatables, | 374 | translatables, |
427 | 366 | self._ref_translatables(u"sampleseries"))) | 375 | self._ref_translatables(u"sampleseries"))) |
428 | 367 | 376 | ||
430 | 368 | new_distroseries.hide_all_translations = True | 377 | naked_new_distroseries.hide_all_translations = True |
431 | 369 | transaction.commit() | 378 | transaction.commit() |
432 | 370 | translatables = self._get_translatables() | 379 | translatables = self._get_translatables() |
433 | 371 | self.failUnlessEqual( | 380 | self.failUnlessEqual( |
434 | 372 | 381 | ||
435 | === modified file 'lib/lp/registry/tests/test_sourcepackage.py' | |||
436 | --- lib/lp/registry/tests/test_sourcepackage.py 2010-02-24 03:06:54 +0000 | |||
437 | +++ lib/lp/registry/tests/test_sourcepackage.py 2010-07-23 12:49:25 +0000 | |||
438 | @@ -22,6 +22,7 @@ | |||
439 | 22 | from lp.code.interfaces.seriessourcepackagebranch import ( | 22 | from lp.code.interfaces.seriessourcepackagebranch import ( |
440 | 23 | IMakeOfficialBranchLinks) | 23 | IMakeOfficialBranchLinks) |
441 | 24 | from lp.testing import TestCaseWithFactory | 24 | from lp.testing import TestCaseWithFactory |
442 | 25 | from lp.testing.factory import remove_security_proxy_and_shout_at_engineer | ||
443 | 25 | from lp.testing.views import create_initialized_view | 26 | from lp.testing.views import create_initialized_view |
444 | 26 | from canonical.testing.layers import DatabaseFunctionalLayer | 27 | from canonical.testing.layers import DatabaseFunctionalLayer |
445 | 27 | 28 | ||
446 | @@ -252,11 +253,17 @@ | |||
447 | 252 | 253 | ||
448 | 253 | self.obsolete_productseries = self.factory.makeProductSeries( | 254 | self.obsolete_productseries = self.factory.makeProductSeries( |
449 | 254 | name='obsolete', product=self.product) | 255 | name='obsolete', product=self.product) |
451 | 255 | self.obsolete_productseries.status = SeriesStatus.OBSOLETE | 256 | naked_obsolete_productseries = ( |
452 | 257 | remove_security_proxy_and_shout_at_engineer( | ||
453 | 258 | self.obsolete_productseries)) | ||
454 | 259 | naked_obsolete_productseries.status = SeriesStatus.OBSOLETE | ||
455 | 256 | 260 | ||
456 | 257 | self.dev_productseries = self.factory.makeProductSeries( | 261 | self.dev_productseries = self.factory.makeProductSeries( |
457 | 258 | name='current', product=self.product) | 262 | name='current', product=self.product) |
459 | 259 | self.dev_productseries.status = SeriesStatus.DEVELOPMENT | 263 | naked_dev_productseries = ( |
460 | 264 | remove_security_proxy_and_shout_at_engineer( | ||
461 | 265 | self.dev_productseries)) | ||
462 | 266 | naked_dev_productseries.status = SeriesStatus.DEVELOPMENT | ||
463 | 260 | 267 | ||
464 | 261 | self.distribution = self.factory.makeDistribution( | 268 | self.distribution = self.factory.makeDistribution( |
465 | 262 | name='youbuntu', displayname='Youbuntu', owner=self.owner) | 269 | name='youbuntu', displayname='Youbuntu', owner=self.owner) |
466 | 263 | 270 | ||
467 | === modified file 'lib/lp/soyuz/adapters/tests/test_packagelocation.py' | |||
468 | --- lib/lp/soyuz/adapters/tests/test_packagelocation.py 2010-06-22 12:08:51 +0000 | |||
469 | +++ lib/lp/soyuz/adapters/tests/test_packagelocation.py 2010-07-23 12:49:25 +0000 | |||
470 | @@ -12,8 +12,10 @@ | |||
471 | 12 | from lp.soyuz.interfaces.archive import ArchivePurpose | 12 | from lp.soyuz.interfaces.archive import ArchivePurpose |
472 | 13 | from lp.soyuz.interfaces.component import IComponentSet | 13 | from lp.soyuz.interfaces.component import IComponentSet |
473 | 14 | from lp.testing import TestCaseWithFactory | 14 | from lp.testing import TestCaseWithFactory |
474 | 15 | from lp.testing.factory import remove_security_proxy_and_shout_at_engineer | ||
475 | 15 | from canonical.testing import LaunchpadZopelessLayer | 16 | from canonical.testing import LaunchpadZopelessLayer |
476 | 16 | 17 | ||
477 | 18 | |||
478 | 17 | class TestPackageLocation(TestCaseWithFactory): | 19 | class TestPackageLocation(TestCaseWithFactory): |
479 | 18 | """Test the `PackageLocation` class.""" | 20 | """Test the `PackageLocation` class.""" |
480 | 19 | layer = LaunchpadZopelessLayer | 21 | layer = LaunchpadZopelessLayer |
481 | @@ -34,7 +36,8 @@ | |||
482 | 34 | returned_location = self.factory.makeCopyArchiveLocation( | 36 | returned_location = self.factory.makeCopyArchiveLocation( |
483 | 35 | distribution=ubuntu, name='now-comes-the-mystery', | 37 | distribution=ubuntu, name='now-comes-the-mystery', |
484 | 36 | owner=self.factory.makePerson(name='mysteryman')) | 38 | owner=self.factory.makePerson(name='mysteryman')) |
486 | 37 | copy_archive = returned_location.archive | 39 | copy_archive = remove_security_proxy_and_shout_at_engineer( |
487 | 40 | returned_location).archive | ||
488 | 38 | 41 | ||
489 | 39 | # Now use the created copy archive to test the build_package_location | 42 | # Now use the created copy archive to test the build_package_location |
490 | 40 | # helper (called via getPackageLocation): | 43 | # helper (called via getPackageLocation): |
491 | 41 | 44 | ||
492 | === modified file 'lib/lp/soyuz/browser/tests/archive-views.txt' | |||
493 | --- lib/lp/soyuz/browser/tests/archive-views.txt 2010-05-21 10:59:09 +0000 | |||
494 | +++ lib/lp/soyuz/browser/tests/archive-views.txt 2010-07-23 12:49:25 +0000 | |||
495 | @@ -15,11 +15,14 @@ | |||
496 | 15 | 15 | ||
497 | 16 | >>> from lp.registry.interfaces.distribution import ( | 16 | >>> from lp.registry.interfaces.distribution import ( |
498 | 17 | ... IDistributionSet) | 17 | ... IDistributionSet) |
499 | 18 | >>> from lp.testing.factory import ( | ||
500 | 19 | ... remove_security_proxy_and_shout_at_engineer) | ||
501 | 18 | >>> ubuntu = getUtility(IDistributionSet)['ubuntu'] | 20 | >>> ubuntu = getUtility(IDistributionSet)['ubuntu'] |
502 | 19 | >>> copy_location = factory.makeCopyArchiveLocation( | 21 | >>> copy_location = factory.makeCopyArchiveLocation( |
503 | 20 | ... distribution=ubuntu, | 22 | ... distribution=ubuntu, |
504 | 21 | ... name="intrepid-security-rebuild") | 23 | ... name="intrepid-security-rebuild") |
506 | 22 | >>> copy_archive = copy_location.archive | 24 | >>> copy_archive = remove_security_proxy_and_shout_at_engineer( |
507 | 25 | ... copy_location).archive | ||
508 | 23 | 26 | ||
509 | 24 | And let's create two views to compare: | 27 | And let's create two views to compare: |
510 | 25 | 28 | ||
511 | @@ -65,8 +68,10 @@ | |||
512 | 65 | 0 | 68 | 0 |
513 | 66 | 69 | ||
514 | 67 | # Create a copy-request to Celso's PPA. | 70 | # Create a copy-request to Celso's PPA. |
515 | 71 | >>> naked_copy_location = remove_security_proxy_and_shout_at_engineer( | ||
516 | 72 | ... copy_location) | ||
517 | 68 | >>> package_copy_request = ubuntu.main_archive.requestPackageCopy( | 73 | >>> package_copy_request = ubuntu.main_archive.requestPackageCopy( |
519 | 69 | ... copy_location, copy_archive.owner) | 74 | ... naked_copy_location, copy_archive.owner) |
520 | 70 | 75 | ||
521 | 71 | >>> len(copy_archive_view.package_copy_requests) | 76 | >>> len(copy_archive_view.package_copy_requests) |
522 | 72 | 1 | 77 | 1 |
523 | 73 | 78 | ||
524 | === modified file 'lib/lp/soyuz/browser/tests/test_distrosourcepackagerelease.py' | |||
525 | --- lib/lp/soyuz/browser/tests/test_distrosourcepackagerelease.py 2010-07-20 12:06:36 +0000 | |||
526 | +++ lib/lp/soyuz/browser/tests/test_distrosourcepackagerelease.py 2010-07-23 12:49:25 +0000 | |||
527 | @@ -14,6 +14,7 @@ | |||
528 | 14 | DistributionSourcePackageRelease) | 14 | DistributionSourcePackageRelease) |
529 | 15 | from lp.soyuz.tests.test_publishing import SoyuzTestPublisher | 15 | from lp.soyuz.tests.test_publishing import SoyuzTestPublisher |
530 | 16 | from lp.testing import TestCaseWithFactory | 16 | from lp.testing import TestCaseWithFactory |
531 | 17 | from lp.testing.factory import remove_security_proxy_and_shout_at_engineer | ||
532 | 17 | from lp.testing.views import create_initialized_view | 18 | from lp.testing.views import create_initialized_view |
533 | 18 | 19 | ||
534 | 19 | 20 | ||
535 | @@ -27,7 +28,12 @@ | |||
536 | 27 | # The package must be published for the page to render. | 28 | # The package must be published for the page to render. |
537 | 28 | stp = SoyuzTestPublisher() | 29 | stp = SoyuzTestPublisher() |
538 | 29 | distroseries = stp.setUpDefaultDistroSeries() | 30 | distroseries = stp.setUpDefaultDistroSeries() |
540 | 30 | distro = distroseries.distribution | 31 | naked_distroseries = remove_security_proxy_and_shout_at_engineer( |
541 | 32 | distroseries) | ||
542 | 33 | # XXX Abel Deuring, 2010-07-21, bug 608240. This is scary. But | ||
543 | 34 | # if we use distroseries.distribution instead, | ||
544 | 35 | # test_spr_files_deleted() and test_spr_files_one() fail. | ||
545 | 36 | distro = naked_distroseries.distribution | ||
546 | 31 | source_package_release = stp.getPubSource().sourcepackagerelease | 37 | source_package_release = stp.getPubSource().sourcepackagerelease |
547 | 32 | self.dspr = DistributionSourcePackageRelease( | 38 | self.dspr = DistributionSourcePackageRelease( |
548 | 33 | distro, source_package_release) | 39 | distro, source_package_release) |
549 | 34 | 40 | ||
550 | === modified file 'lib/lp/soyuz/browser/tests/test_sourcepackagerelease.py' | |||
551 | --- lib/lp/soyuz/browser/tests/test_sourcepackagerelease.py 2010-07-20 12:06:36 +0000 | |||
552 | +++ lib/lp/soyuz/browser/tests/test_sourcepackagerelease.py 2010-07-23 12:49:25 +0000 | |||
553 | @@ -16,6 +16,7 @@ | |||
554 | 16 | from canonical.testing import ( | 16 | from canonical.testing import ( |
555 | 17 | DatabaseFunctionalLayer, LaunchpadFunctionalLayer) | 17 | DatabaseFunctionalLayer, LaunchpadFunctionalLayer) |
556 | 18 | from lp.testing import TestCaseWithFactory | 18 | from lp.testing import TestCaseWithFactory |
557 | 19 | from lp.testing.factory import remove_security_proxy_and_shout_at_engineer | ||
558 | 19 | from lp.testing.views import create_initialized_view | 20 | from lp.testing.views import create_initialized_view |
559 | 20 | 21 | ||
560 | 21 | 22 | ||
561 | @@ -64,20 +65,23 @@ | |||
562 | 64 | 65 | ||
563 | 65 | def test_highlighted_copyright_is_None(self): | 66 | def test_highlighted_copyright_is_None(self): |
564 | 66 | expected = '' | 67 | expected = '' |
566 | 67 | self.source_package_release.copyright = None | 68 | remove_security_proxy_and_shout_at_engineer( |
567 | 69 | self.source_package_release).copyright = None | ||
568 | 68 | view = create_initialized_view( | 70 | view = create_initialized_view( |
569 | 69 | self.source_package_release, '+copyright') | 71 | self.source_package_release, '+copyright') |
570 | 70 | self.assertEqual(expected, view.highlighted_copyright) | 72 | self.assertEqual(expected, view.highlighted_copyright) |
571 | 71 | 73 | ||
572 | 72 | def test_highlighted_copyright_no_matches(self): | 74 | def test_highlighted_copyright_no_matches(self): |
573 | 73 | expected = 'nothing to see and/or do.' | 75 | expected = 'nothing to see and/or do.' |
575 | 74 | self.source_package_release.copyright = expected | 76 | remove_security_proxy_and_shout_at_engineer( |
576 | 77 | self.source_package_release).copyright = expected | ||
577 | 75 | view = create_initialized_view( | 78 | view = create_initialized_view( |
578 | 76 | self.source_package_release, '+copyright') | 79 | self.source_package_release, '+copyright') |
579 | 77 | self.assertEqual(expected, view.highlighted_copyright) | 80 | self.assertEqual(expected, view.highlighted_copyright) |
580 | 78 | 81 | ||
581 | 79 | def test_highlighted_copyright_match_url(self): | 82 | def test_highlighted_copyright_match_url(self): |
583 | 80 | self.source_package_release.copyright = ( | 83 | remove_security_proxy_and_shout_at_engineer( |
584 | 84 | self.source_package_release).copyright = ( | ||
585 | 81 | 'Downloaded from https://upstream.dom/fnord/no/ and') | 85 | 'Downloaded from https://upstream.dom/fnord/no/ and') |
586 | 82 | expected = ( | 86 | expected = ( |
587 | 83 | 'Downloaded from ' | 87 | 'Downloaded from ' |
588 | @@ -88,7 +92,8 @@ | |||
589 | 88 | self.assertEqual(expected, view.highlighted_copyright) | 92 | self.assertEqual(expected, view.highlighted_copyright) |
590 | 89 | 93 | ||
591 | 90 | def test_highlighted_copyright_match_path(self): | 94 | def test_highlighted_copyright_match_path(self): |
593 | 91 | self.source_package_release.copyright = ( | 95 | remove_security_proxy_and_shout_at_engineer( |
594 | 96 | self.source_package_release).copyright = ( | ||
595 | 92 | 'See /usr/share/common-licenses/GPL') | 97 | 'See /usr/share/common-licenses/GPL') |
596 | 93 | expected = ( | 98 | expected = ( |
597 | 94 | 'See ' | 99 | 'See ' |
598 | @@ -98,7 +103,8 @@ | |||
599 | 98 | self.assertEqual(expected, view.highlighted_copyright) | 103 | self.assertEqual(expected, view.highlighted_copyright) |
600 | 99 | 104 | ||
601 | 100 | def test_highlighted_copyright_match_multiple(self): | 105 | def test_highlighted_copyright_match_multiple(self): |
603 | 101 | self.source_package_release.copyright = ( | 106 | remove_security_proxy_and_shout_at_engineer( |
604 | 107 | self.source_package_release).copyright = ( | ||
605 | 102 | 'See /usr/share/common-licenses/GPL or https://osi.org/mit') | 108 | 'See /usr/share/common-licenses/GPL or https://osi.org/mit') |
606 | 103 | expected = ( | 109 | expected = ( |
607 | 104 | 'See ' | 110 | 'See ' |
608 | 105 | 111 | ||
609 | === modified file 'lib/lp/soyuz/doc/archive.txt' | |||
610 | --- lib/lp/soyuz/doc/archive.txt 2010-07-21 09:41:48 +0000 | |||
611 | +++ lib/lp/soyuz/doc/archive.txt 2010-07-23 12:49:25 +0000 | |||
612 | @@ -1285,10 +1285,15 @@ | |||
613 | 1285 | The IArchive interface includes a convenience method for creating a | 1285 | The IArchive interface includes a convenience method for creating a |
614 | 1286 | package copy request: | 1286 | package copy request: |
615 | 1287 | 1287 | ||
616 | 1288 | >>> from lp.testing.factory import ( | ||
617 | 1289 | ... remove_security_proxy_and_shout_at_engineer) | ||
618 | 1288 | >>> requestor = factory.makePerson(name='me-copy') | 1290 | >>> requestor = factory.makePerson(name='me-copy') |
619 | 1289 | >>> copy_target = factory.makeCopyArchiveLocation( | 1291 | >>> copy_target = factory.makeCopyArchiveLocation( |
620 | 1290 | ... distribution=ubuntu, name='my-copy-archive', owner=requestor) | 1292 | ... distribution=ubuntu, name='my-copy-archive', owner=requestor) |
622 | 1291 | >>> pcr = ubuntu.main_archive.requestPackageCopy(copy_target, requestor) | 1293 | >>> naked_copy_target = remove_security_proxy_and_shout_at_engineer( |
623 | 1294 | ... copy_target) | ||
624 | 1295 | >>> pcr = ubuntu.main_archive.requestPackageCopy( | ||
625 | 1296 | ... naked_copy_target, requestor) | ||
626 | 1292 | >>> print pcr | 1297 | >>> print pcr |
627 | 1293 | Package copy request | 1298 | Package copy request |
628 | 1294 | source = primary/hoary/-/RELEASE | 1299 | source = primary/hoary/-/RELEASE |
629 | @@ -1301,7 +1306,7 @@ | |||
630 | 1301 | The requestPackageCopy method can also take an optional suite name: | 1306 | The requestPackageCopy method can also take an optional suite name: |
631 | 1302 | 1307 | ||
632 | 1303 | >>> package_copy_request = ubuntu.main_archive.requestPackageCopy( | 1308 | >>> package_copy_request = ubuntu.main_archive.requestPackageCopy( |
634 | 1304 | ... copy_target, requestor, suite="hoary-updates"); | 1309 | ... naked_copy_target, requestor, suite="hoary-updates"); |
635 | 1305 | >>> print package_copy_request | 1310 | >>> print package_copy_request |
636 | 1306 | Package copy request | 1311 | Package copy request |
637 | 1307 | source = primary/hoary/-/UPDATES | 1312 | source = primary/hoary/-/UPDATES |
638 | @@ -1412,9 +1417,9 @@ | |||
639 | 1412 | 1417 | ||
640 | 1413 | COPY archives use a URL format of <distro-name>-<archive-name>: | 1418 | COPY archives use a URL format of <distro-name>-<archive-name>: |
641 | 1414 | 1419 | ||
643 | 1415 | >>> print copy_target.archive.is_copy | 1420 | >>> print naked_copy_target.archive.is_copy |
644 | 1416 | True | 1421 | True |
646 | 1417 | >>> print copy_target.archive.archive_url | 1422 | >>> print naked_copy_target.archive.archive_url |
647 | 1418 | http://rebuild-test.internal/ubuntu-my-copy-archive/ubuntu | 1423 | http://rebuild-test.internal/ubuntu-my-copy-archive/ubuntu |
648 | 1419 | 1424 | ||
649 | 1420 | If the archive is private, the url may be different as private PPAs | 1425 | If the archive is private, the url may be different as private PPAs |
650 | 1421 | 1426 | ||
651 | === modified file 'lib/lp/soyuz/stories/soyuz/xx-distribution-archives.txt' | |||
652 | --- lib/lp/soyuz/stories/soyuz/xx-distribution-archives.txt 2010-01-12 08:24:36 +0000 | |||
653 | +++ lib/lp/soyuz/stories/soyuz/xx-distribution-archives.txt 2010-07-23 12:49:25 +0000 | |||
654 | @@ -6,6 +6,8 @@ | |||
655 | 6 | >>> from canonical.launchpad.interfaces import ( | 6 | >>> from canonical.launchpad.interfaces import ( |
656 | 7 | ... IDistributionSet) | 7 | ... IDistributionSet) |
657 | 8 | >>> from lp.registry.interfaces.person import IPersonSet | 8 | >>> from lp.registry.interfaces.person import IPersonSet |
658 | 9 | >>> from lp.testing.factory import ( | ||
659 | 10 | ... remove_security_proxy_and_shout_at_engineer) | ||
660 | 9 | >>> login('foo.bar@canonical.com') | 11 | >>> login('foo.bar@canonical.com') |
661 | 10 | >>> ubuntu = getUtility(IDistributionSet)['ubuntu'] | 12 | >>> ubuntu = getUtility(IDistributionSet)['ubuntu'] |
662 | 11 | 13 | ||
663 | @@ -13,18 +15,21 @@ | |||
664 | 13 | >>> copy_location = factory.makeCopyArchiveLocation( | 15 | >>> copy_location = factory.makeCopyArchiveLocation( |
665 | 14 | ... distribution=ubuntu, owner=joe, | 16 | ... distribution=ubuntu, owner=joe, |
666 | 15 | ... name="intrepid-security-rebuild") | 17 | ... name="intrepid-security-rebuild") |
668 | 16 | >>> copy_archive = copy_location.archive | 18 | >>> naked_copy_location = remove_security_proxy_and_shout_at_engineer( |
669 | 19 | ... copy_location) | ||
670 | 20 | >>> copy_archive = naked_copy_location.archive | ||
671 | 17 | >>> copy_archive.enabled | 21 | >>> copy_archive.enabled |
672 | 18 | True | 22 | True |
673 | 19 | 23 | ||
674 | 20 | >>> package_copy_request = ubuntu.main_archive.requestPackageCopy( | 24 | >>> package_copy_request = ubuntu.main_archive.requestPackageCopy( |
676 | 21 | ... copy_location, copy_archive.owner) | 25 | ... naked_copy_location, copy_archive.owner) |
677 | 22 | 26 | ||
678 | 23 | >>> nopriv = getUtility(IPersonSet).getByName('no-priv') | 27 | >>> nopriv = getUtility(IPersonSet).getByName('no-priv') |
679 | 24 | >>> disabled_location = factory.makeCopyArchiveLocation( | 28 | >>> disabled_location = factory.makeCopyArchiveLocation( |
680 | 25 | ... distribution=ubuntu, owner=nopriv, | 29 | ... distribution=ubuntu, owner=nopriv, |
681 | 26 | ... name="disabled-security-rebuild", enabled=False) | 30 | ... name="disabled-security-rebuild", enabled=False) |
683 | 27 | >>> disabled_archive = disabled_location.archive | 31 | >>> disabled_archive = remove_security_proxy_and_shout_at_engineer( |
684 | 32 | ... disabled_location).archive | ||
685 | 28 | >>> disabled_archive.enabled | 33 | >>> disabled_archive.enabled |
686 | 29 | False | 34 | False |
687 | 30 | 35 | ||
688 | @@ -119,12 +124,14 @@ | |||
689 | 119 | >>> copy_location = factory.makeCopyArchiveLocation( | 124 | >>> copy_location = factory.makeCopyArchiveLocation( |
690 | 120 | ... distribution=ubuntu, | 125 | ... distribution=ubuntu, |
691 | 121 | ... name="intrepid-private-security-rebuild") | 126 | ... name="intrepid-private-security-rebuild") |
693 | 122 | >>> copy_archive = copy_location.archive | 127 | >>> naked_copy_location = remove_security_proxy_and_shout_at_engineer( |
694 | 128 | ... copy_location) | ||
695 | 129 | >>> copy_archive = naked_copy_location.archive | ||
696 | 123 | >>> copy_archive.buildd_secret = 'really secret' | 130 | >>> copy_archive.buildd_secret = 'really secret' |
697 | 124 | >>> copy_archive.private = True | 131 | >>> copy_archive.private = True |
698 | 125 | >>> copy_archive.owner.displayname = "Harry Potter" | 132 | >>> copy_archive.owner.displayname = "Harry Potter" |
699 | 126 | >>> package_copy_request = ubuntu.main_archive.requestPackageCopy( | 133 | >>> package_copy_request = ubuntu.main_archive.requestPackageCopy( |
701 | 127 | ... copy_location, copy_archive.owner) | 134 | ... naked_copy_location, copy_archive.owner) |
702 | 128 | >>> pub_src = stp.getPubSource( | 135 | >>> pub_src = stp.getPubSource( |
703 | 129 | ... archive=copy_archive, architecturehintlist='any') | 136 | ... archive=copy_archive, architecturehintlist='any') |
704 | 130 | >>> pub_bins = stp.getPubBinaries(pub_source=pub_src) | 137 | >>> pub_bins = stp.getPubBinaries(pub_source=pub_src) |
705 | 131 | 138 | ||
706 | === modified file 'lib/lp/soyuz/tests/test_publishing.py' | |||
707 | --- lib/lp/soyuz/tests/test_publishing.py 2010-07-23 12:49:10 +0000 | |||
708 | +++ lib/lp/soyuz/tests/test_publishing.py 2010-07-23 12:49:25 +0000 | |||
709 | @@ -43,7 +43,8 @@ | |||
710 | 43 | from lp.soyuz.interfaces.queue import PackageUploadStatus | 43 | from lp.soyuz.interfaces.queue import PackageUploadStatus |
711 | 44 | from canonical.launchpad.scripts import FakeLogger | 44 | from canonical.launchpad.scripts import FakeLogger |
712 | 45 | from lp.testing import TestCaseWithFactory | 45 | from lp.testing import TestCaseWithFactory |
714 | 46 | from lp.testing.factory import LaunchpadObjectFactory | 46 | from lp.testing.factory import ( |
715 | 47 | LaunchpadObjectFactory, remove_security_proxy_and_shout_at_engineer) | ||
716 | 47 | from lp.testing.fakemethod import FakeMethod | 48 | from lp.testing.fakemethod import FakeMethod |
717 | 48 | 49 | ||
718 | 49 | 50 | ||
719 | @@ -145,7 +146,10 @@ | |||
720 | 145 | PackageUploadStatus.DONE: 'setDone', | 146 | PackageUploadStatus.DONE: 'setDone', |
721 | 146 | PackageUploadStatus.ACCEPTED: 'setAccepted', | 147 | PackageUploadStatus.ACCEPTED: 'setAccepted', |
722 | 147 | } | 148 | } |
724 | 148 | method = getattr(package_upload, status_to_method[upload_status]) | 149 | naked_package_upload = remove_security_proxy_and_shout_at_engineer( |
725 | 150 | package_upload) | ||
726 | 151 | method = getattr( | ||
727 | 152 | naked_package_upload, status_to_method[upload_status]) | ||
728 | 149 | method() | 153 | method() |
729 | 150 | 154 | ||
730 | 151 | return package_upload | 155 | return package_upload |
731 | @@ -221,7 +225,9 @@ | |||
732 | 221 | changes_file_name=changes_file_name, | 225 | changes_file_name=changes_file_name, |
733 | 222 | changes_file_content=changes_file_content, | 226 | changes_file_content=changes_file_content, |
734 | 223 | upload_status=upload_status) | 227 | upload_status=upload_status) |
736 | 224 | package_upload.addSource(spr) | 228 | naked_package_upload = remove_security_proxy_and_shout_at_engineer( |
737 | 229 | package_upload) | ||
738 | 230 | naked_package_upload.addSource(spr) | ||
739 | 225 | 231 | ||
740 | 226 | if filename is None: | 232 | if filename is None: |
741 | 227 | filename = "%s_%s.dsc" % (sourcename, version) | 233 | filename = "%s_%s.dsc" % (sourcename, version) |
742 | 228 | 234 | ||
743 | === modified file 'lib/lp/testing/factory.py' | |||
744 | --- lib/lp/testing/factory.py 2010-07-23 12:49:10 +0000 | |||
745 | +++ lib/lp/testing/factory.py 2010-07-23 12:49:25 +0000 | |||
746 | @@ -13,8 +13,10 @@ | |||
747 | 13 | __metaclass__ = type | 13 | __metaclass__ = type |
748 | 14 | __all__ = [ | 14 | __all__ = [ |
749 | 15 | 'GPGSigningContext', | 15 | 'GPGSigningContext', |
750 | 16 | 'is_security_proxied_or_harmless', | ||
751 | 16 | 'LaunchpadObjectFactory', | 17 | 'LaunchpadObjectFactory', |
752 | 17 | 'ObjectFactory', | 18 | 'ObjectFactory', |
753 | 19 | 'remove_security_proxy_and_shout_at_engineer', | ||
754 | 18 | ] | 20 | ] |
755 | 19 | 21 | ||
756 | 20 | from contextlib import nested | 22 | from contextlib import nested |
757 | @@ -25,18 +27,22 @@ | |||
758 | 25 | from email.mime.text import MIMEText | 27 | from email.mime.text import MIMEText |
759 | 26 | from email.mime.multipart import MIMEMultipart | 28 | from email.mime.multipart import MIMEMultipart |
760 | 27 | from itertools import count | 29 | from itertools import count |
761 | 30 | from operator import isSequenceType | ||
762 | 28 | import os.path | 31 | import os.path |
763 | 29 | from random import randint | 32 | from random import randint |
764 | 30 | from StringIO import StringIO | 33 | from StringIO import StringIO |
765 | 34 | import sys | ||
766 | 31 | from textwrap import dedent | 35 | from textwrap import dedent |
767 | 32 | from threading import local | 36 | from threading import local |
768 | 37 | from types import InstanceType | ||
769 | 33 | 38 | ||
770 | 34 | import pytz | 39 | import pytz |
771 | 35 | 40 | ||
772 | 36 | from twisted.python.util import mergeFunctionMetadata | 41 | from twisted.python.util import mergeFunctionMetadata |
773 | 37 | 42 | ||
774 | 38 | from zope.component import ComponentLookupError, getUtility | 43 | from zope.component import ComponentLookupError, getUtility |
776 | 39 | from zope.security.proxy import removeSecurityProxy | 44 | from zope.security.proxy import ( |
777 | 45 | builtin_isinstance, Proxy, ProxyFactory, removeSecurityProxy) | ||
778 | 40 | 46 | ||
779 | 41 | from canonical.autodecorate import AutoDecorate | 47 | from canonical.autodecorate import AutoDecorate |
780 | 42 | from canonical.config import config | 48 | from canonical.config import config |
781 | @@ -152,7 +158,6 @@ | |||
782 | 152 | ANONYMOUS, | 158 | ANONYMOUS, |
783 | 153 | login, | 159 | login, |
784 | 154 | login_as, | 160 | login_as, |
785 | 155 | logout, | ||
786 | 156 | run_with_login, | 161 | run_with_login, |
787 | 157 | temp_dir, | 162 | temp_dir, |
788 | 158 | time_counter, | 163 | time_counter, |
789 | @@ -325,7 +330,7 @@ | |||
790 | 325 | branch_id, rcstype, url, cvs_root, cvs_module) | 330 | branch_id, rcstype, url, cvs_root, cvs_module) |
791 | 326 | 331 | ||
792 | 327 | 332 | ||
794 | 328 | class LaunchpadObjectFactory(ObjectFactory): | 333 | class BareLaunchpadObjectFactory(ObjectFactory): |
795 | 329 | """Factory methods for creating Launchpad objects. | 334 | """Factory methods for creating Launchpad objects. |
796 | 330 | 335 | ||
797 | 331 | All the factory methods should be callable with no parameters. | 336 | All the factory methods should be callable with no parameters. |
798 | @@ -356,7 +361,7 @@ | |||
799 | 356 | 361 | ||
800 | 357 | location = PackageLocation(copy_archive, distribution, distroseries, | 362 | location = PackageLocation(copy_archive, distribution, distroseries, |
801 | 358 | pocket) | 363 | pocket) |
803 | 359 | return location | 364 | return ProxyFactory(location) |
804 | 360 | 365 | ||
805 | 361 | def makeAccount(self, displayname, email=None, password=None, | 366 | def makeAccount(self, displayname, email=None, password=None, |
806 | 362 | status=AccountStatus.ACTIVE, | 367 | status=AccountStatus.ACTIVE, |
807 | @@ -744,8 +749,8 @@ | |||
808 | 744 | # We don't want to login() as the person used to create the product, | 749 | # We don't want to login() as the person used to create the product, |
809 | 745 | # so we remove the security proxy before creating the series. | 750 | # so we remove the security proxy before creating the series. |
810 | 746 | naked_product = removeSecurityProxy(product) | 751 | naked_product = removeSecurityProxy(product) |
813 | 747 | return naked_product.newSeries(owner=owner, name=name, | 752 | return ProxyFactory( |
814 | 748 | summary=summary) | 753 | naked_product.newSeries(owner=owner, name=name, summary=summary)) |
815 | 749 | 754 | ||
816 | 750 | def makeProject(self, name=None, displayname=None, title=None, | 755 | def makeProject(self, name=None, displayname=None, title=None, |
817 | 751 | homepageurl=None, summary=None, owner=None, | 756 | homepageurl=None, summary=None, owner=None, |
818 | @@ -828,7 +833,7 @@ | |||
819 | 828 | url = self.getUniqueURL() | 833 | url = self.getUniqueURL() |
820 | 829 | else: | 834 | else: |
821 | 830 | raise UnknownBranchTypeError( | 835 | raise UnknownBranchTypeError( |
823 | 831 | 'Unrecognized branch type: %r' % (branch_type,)) | 836 | 'Unrecognized branch type: %r' % (branch_type, )) |
824 | 832 | 837 | ||
825 | 833 | namespace = get_branch_namespace( | 838 | namespace = get_branch_namespace( |
826 | 834 | owner, product=product, distroseries=distroseries, | 839 | owner, product=product, distroseries=distroseries, |
827 | @@ -1034,7 +1039,7 @@ | |||
828 | 1034 | CodeReviewNotificationLevel.NOEMAIL, subscribed_by) | 1039 | CodeReviewNotificationLevel.NOEMAIL, subscribed_by) |
829 | 1035 | 1040 | ||
830 | 1036 | def makeDiff(self, diff_text=DIFF): | 1041 | def makeDiff(self, diff_text=DIFF): |
832 | 1037 | return Diff.fromFile(StringIO(diff_text), len(diff_text)) | 1042 | return ProxyFactory(Diff.fromFile(StringIO(diff_text), len(diff_text))) |
833 | 1038 | 1043 | ||
834 | 1039 | def makePreviewDiff(self, conflicts=u''): | 1044 | def makePreviewDiff(self, conflicts=u''): |
835 | 1040 | diff = self.makeDiff() | 1045 | diff = self.makeDiff() |
836 | @@ -1614,6 +1619,7 @@ | |||
837 | 1614 | return series | 1619 | return series |
838 | 1615 | 1620 | ||
839 | 1616 | def makeLanguage(self, language_code=None, name=None): | 1621 | def makeLanguage(self, language_code=None, name=None): |
840 | 1622 | """Makes a language given the language_code and name.""" | ||
841 | 1617 | if language_code is None: | 1623 | if language_code is None: |
842 | 1618 | language_code = self.getUniqueString('lang') | 1624 | language_code = self.getUniqueString('lang') |
843 | 1619 | if name is None: | 1625 | if name is None: |
844 | @@ -1680,7 +1686,7 @@ | |||
845 | 1680 | description=self.getUniqueString(), | 1686 | description=self.getUniqueString(), |
846 | 1681 | parent_series=parent_series, owner=distribution.owner) | 1687 | parent_series=parent_series, owner=distribution.owner) |
847 | 1682 | series.status = status | 1688 | series.status = status |
849 | 1683 | return series | 1689 | return ProxyFactory(series) |
850 | 1684 | 1690 | ||
851 | 1685 | # Most people think of distro releases as distro series. | 1691 | # Most people think of distro releases as distro series. |
852 | 1686 | makeDistroSeries = makeDistroRelease | 1692 | makeDistroSeries = makeDistroRelease |
853 | @@ -1789,7 +1795,7 @@ | |||
854 | 1789 | 1795 | ||
855 | 1790 | def makeRecipeText(self, *branches): | 1796 | def makeRecipeText(self, *branches): |
856 | 1791 | if len(branches) == 0: | 1797 | if len(branches) == 0: |
858 | 1792 | branches = (self.makeAnyBranch(),) | 1798 | branches = (self.makeAnyBranch(), ) |
859 | 1793 | base_branch = branches[0] | 1799 | base_branch = branches[0] |
860 | 1794 | other_branches = branches[1:] | 1800 | other_branches = branches[1:] |
861 | 1795 | text = MINIMAL_RECIPE_TEXT % base_branch.bzr_identity | 1801 | text = MINIMAL_RECIPE_TEXT % base_branch.bzr_identity |
862 | @@ -1952,7 +1958,7 @@ | |||
863 | 1952 | productseries = self.makeProductSeries(owner=owner) | 1958 | productseries = self.makeProductSeries(owner=owner) |
864 | 1953 | # Make it use Translations, otherwise there's little point | 1959 | # Make it use Translations, otherwise there's little point |
865 | 1954 | # to us creating a template for it. | 1960 | # to us creating a template for it. |
867 | 1955 | productseries.product.official_rosetta = True | 1961 | removeSecurityProxy(productseries).product.official_rosetta = True |
868 | 1956 | templateset = getUtility(IPOTemplateSet) | 1962 | templateset = getUtility(IPOTemplateSet) |
869 | 1957 | subset = templateset.getSubset( | 1963 | subset = templateset.getSubset( |
870 | 1958 | distroseries, sourcepackagename, productseries) | 1964 | distroseries, sourcepackagename, productseries) |
871 | @@ -2727,3 +2733,73 @@ | |||
872 | 2727 | new_uuid = getUtility(ITemporaryStorageManager).new(blob, expires) | 2733 | new_uuid = getUtility(ITemporaryStorageManager).new(blob, expires) |
873 | 2728 | 2734 | ||
874 | 2729 | return getUtility(ITemporaryStorageManager).fetch(new_uuid) | 2735 | return getUtility(ITemporaryStorageManager).fetch(new_uuid) |
875 | 2736 | |||
876 | 2737 | |||
877 | 2738 | # Some factory methods return simple Python types. We don't add | ||
878 | 2739 | # security wrappers for them, as well as for objects created by | ||
879 | 2740 | # other Python libraries. | ||
880 | 2741 | unwrapped_types = ( | ||
881 | 2742 | DSCFile, InstanceType, Message, datetime, int, str, unicode) | ||
882 | 2743 | |||
883 | 2744 | def is_security_proxied_or_harmless(obj): | ||
884 | 2745 | """Check that the given object is security wrapped or a harmless object.""" | ||
885 | 2746 | if obj is None: | ||
886 | 2747 | return True | ||
887 | 2748 | if builtin_isinstance(obj, Proxy): | ||
888 | 2749 | return True | ||
889 | 2750 | if type(obj) in unwrapped_types: | ||
890 | 2751 | return True | ||
891 | 2752 | if isSequenceType(obj): | ||
892 | 2753 | for element in obj: | ||
893 | 2754 | if not is_security_proxied_or_harmless(element): | ||
894 | 2755 | return False | ||
895 | 2756 | return True | ||
896 | 2757 | return False | ||
897 | 2758 | |||
898 | 2759 | |||
899 | 2760 | class LaunchpadObjectFactory: | ||
900 | 2761 | """A wrapper around `BareLaunchpadObjectFactory`. | ||
901 | 2762 | |||
902 | 2763 | Ensure that each object created by a `BareLaunchpadObjectFactory` method | ||
903 | 2764 | is either of a simple Python type or is security proxied. | ||
904 | 2765 | |||
905 | 2766 | A warning message is printed to stderr if a factory method creates | ||
906 | 2767 | an object without a security proxy. | ||
907 | 2768 | |||
908 | 2769 | Whereever you see such a warning: fix it! | ||
909 | 2770 | """ | ||
910 | 2771 | def __init__(self): | ||
911 | 2772 | self._factory = BareLaunchpadObjectFactory() | ||
912 | 2773 | |||
913 | 2774 | def __getattr__(self, name): | ||
914 | 2775 | attr = getattr(self._factory, name) | ||
915 | 2776 | if callable(attr): | ||
916 | 2777 | |||
917 | 2778 | def guarded_method(*args, **kw): | ||
918 | 2779 | result = attr(*args, **kw) | ||
919 | 2780 | if not is_security_proxied_or_harmless(result): | ||
920 | 2781 | message = ( | ||
921 | 2782 | "PLEASE FIX: LaunchpadObjectFactory.%s returns an " | ||
922 | 2783 | "unproxied object." % name) | ||
923 | 2784 | print >>sys.stderr, message | ||
924 | 2785 | return result | ||
925 | 2786 | return guarded_method | ||
926 | 2787 | else: | ||
927 | 2788 | return attr | ||
928 | 2789 | |||
929 | 2790 | |||
930 | 2791 | def remove_security_proxy_and_shout_at_engineer(obj): | ||
931 | 2792 | """Remove an object's security proxy and print a warning. | ||
932 | 2793 | |||
933 | 2794 | A number of LaunchpadObjectFactory methods returned objects without | ||
934 | 2795 | a security proxy. This is now no longer possible, but a number of | ||
935 | 2796 | tests rely on unrestricted access to object attributes. | ||
936 | 2797 | |||
937 | 2798 | This function should only be used in legacy tests which fail because | ||
938 | 2799 | they expect unproxied objects. | ||
939 | 2800 | """ | ||
940 | 2801 | print >>sys.stderr, ( | ||
941 | 2802 | "\nWarning: called removeSecurityProxy() for %r without a check if " | ||
942 | 2803 | "this reasonable. Look for a call of " | ||
943 | 2804 | "remove_security_proxy_and_shout_at_engineer(some_object)." % obj) | ||
944 | 2805 | return removeSecurityProxy(obj) | ||
945 | 2730 | 2806 | ||
946 | === modified file 'lib/lp/testing/tests/test_factory.py' | |||
947 | --- lib/lp/testing/tests/test_factory.py 2010-07-23 12:49:10 +0000 | |||
948 | +++ lib/lp/testing/tests/test_factory.py 2010-07-23 12:49:25 +0000 | |||
949 | @@ -8,12 +8,14 @@ | |||
950 | 8 | import unittest | 8 | import unittest |
951 | 9 | 9 | ||
952 | 10 | from zope.component import getUtility | 10 | from zope.component import getUtility |
953 | 11 | from zope.security.proxy import removeSecurityProxy | ||
954 | 11 | 12 | ||
955 | 12 | from canonical.launchpad.webapp.interfaces import ILaunchBag | 13 | from canonical.launchpad.webapp.interfaces import ILaunchBag |
956 | 13 | from canonical.testing.layers import DatabaseFunctionalLayer | 14 | from canonical.testing.layers import DatabaseFunctionalLayer |
957 | 14 | from lp.code.enums import CodeImportReviewStatus | 15 | from lp.code.enums import CodeImportReviewStatus |
958 | 15 | from lp.testing import TestCaseWithFactory | 16 | from lp.testing import TestCaseWithFactory |
959 | 16 | from lp.services.worlddata.interfaces.language import ILanguage | 17 | from lp.services.worlddata.interfaces.language import ILanguage |
960 | 18 | from lp.testing.factory import is_security_proxied_or_harmless | ||
961 | 17 | 19 | ||
962 | 18 | 20 | ||
963 | 19 | class TestFactory(TestCaseWithFactory): | 21 | class TestFactory(TestCaseWithFactory): |
964 | @@ -66,6 +68,54 @@ | |||
965 | 66 | self.assertIsNot(None, person) | 68 | self.assertIsNot(None, person) |
966 | 67 | self.assertEqual(person, current_person) | 69 | self.assertEqual(person, current_person) |
967 | 68 | 70 | ||
968 | 71 | def test_is_security_proxied_or_harmless__none(self): | ||
969 | 72 | # is_security_proxied_or_harmless() considers the None object | ||
970 | 73 | # to be a harmless object. | ||
971 | 74 | self.assertTrue(is_security_proxied_or_harmless(None)) | ||
972 | 75 | |||
973 | 76 | def test_is_security_proxied_or_harmless__int(self): | ||
974 | 77 | # is_security_proxied_or_harmless() considers integers | ||
975 | 78 | # to be harmless. | ||
976 | 79 | self.assertTrue(is_security_proxied_or_harmless(1)) | ||
977 | 80 | |||
978 | 81 | def test_is_security_proxied_or_harmless__string(self): | ||
979 | 82 | # is_security_proxied_or_harmless() considers strings | ||
980 | 83 | # to be harmless. | ||
981 | 84 | self.assertTrue(is_security_proxied_or_harmless('abc')) | ||
982 | 85 | |||
983 | 86 | def test_is_security_proxied_or_harmless__unicode(self): | ||
984 | 87 | # is_security_proxied_or_harmless() considers unicode objects | ||
985 | 88 | # to be harmless. | ||
986 | 89 | self.assertTrue(is_security_proxied_or_harmless(u'abc')) | ||
987 | 90 | |||
988 | 91 | def test_is_security_proxied_or_harmless__proxied_object(self): | ||
989 | 92 | # is_security_proxied_or_harmless() treats security proxied | ||
990 | 93 | # objects as harmless. | ||
991 | 94 | proxied_person = self.factory.makePerson() | ||
992 | 95 | self.assertTrue(is_security_proxied_or_harmless(proxied_person)) | ||
993 | 96 | |||
994 | 97 | def test_is_security_proxied_or_harmless__unproxied_object(self): | ||
995 | 98 | # is_security_proxied_or_harmless() treats security proxied | ||
996 | 99 | # objects as harmless. | ||
997 | 100 | unproxied_person = removeSecurityProxy(self.factory.makePerson()) | ||
998 | 101 | self.assertFalse(is_security_proxied_or_harmless(unproxied_person)) | ||
999 | 102 | |||
1000 | 103 | def test_is_security_proxied_or_harmless__sequence_harmless_content(self): | ||
1001 | 104 | # is_security_proxied_or_harmless() checks all elements | ||
1002 | 105 | # of a sequence. If all elements are harmless, so is the | ||
1003 | 106 | # sequence. | ||
1004 | 107 | proxied_person = self.factory.makePerson() | ||
1005 | 108 | self.assertTrue( | ||
1006 | 109 | is_security_proxied_or_harmless([1, '2', proxied_person])) | ||
1007 | 110 | |||
1008 | 111 | def test_is_security_proxied_or_harmless__sequence_harmful_content(self): | ||
1009 | 112 | # is_security_proxied_or_harmless() checks all elements | ||
1010 | 113 | # of a sequence. If at least one element is harmful, so is the | ||
1011 | 114 | # sequence. | ||
1012 | 115 | unproxied_person = removeSecurityProxy(self.factory.makePerson()) | ||
1013 | 116 | self.assertFalse( | ||
1014 | 117 | is_security_proxied_or_harmless([1, '2', unproxied_person])) | ||
1015 | 118 | |||
1016 | 69 | 119 | ||
1017 | 70 | def test_suite(): | 120 | def test_suite(): |
1018 | 71 | return unittest.TestLoader().loadTestsFromName(__name__) | 121 | return unittest.TestLoader().loadTestsFromName(__name__) |
1019 | 72 | 122 | ||
1020 | === modified file 'lib/lp/translations/browser/configure.zcml' | |||
1021 | --- lib/lp/translations/browser/configure.zcml 2010-07-23 12:49:10 +0000 | |||
1022 | +++ lib/lp/translations/browser/configure.zcml 2010-07-23 12:49:25 +0000 | |||
1023 | @@ -269,7 +269,7 @@ | |||
1024 | 269 | <browser:url | 269 | <browser:url |
1025 | 270 | for="lp.translations.interfaces.productserieslanguage.IProductSeriesLanguage" | 270 | for="lp.translations.interfaces.productserieslanguage.IProductSeriesLanguage" |
1026 | 271 | path_expression="string:+lang/${language/code}" | 271 | path_expression="string:+lang/${language/code}" |
1028 | 272 | attribute_to_parent="productseries" | 272 | attribute_to_parent="parent" |
1029 | 273 | rootsite="translations"/> | 273 | rootsite="translations"/> |
1030 | 274 | <browser:navigation | 274 | <browser:navigation |
1031 | 275 | module="lp.translations.browser.serieslanguage" | 275 | module="lp.translations.browser.serieslanguage" |
1032 | 276 | 276 | ||
1033 | === modified file 'lib/lp/translations/browser/serieslanguage.py' | |||
1034 | --- lib/lp/translations/browser/serieslanguage.py 2010-03-04 07:31:38 +0000 | |||
1035 | +++ lib/lp/translations/browser/serieslanguage.py 2010-07-23 12:49:25 +0000 | |||
1036 | @@ -29,7 +29,7 @@ | |||
1037 | 29 | 29 | ||
1038 | 30 | 30 | ||
1039 | 31 | class BaseSeriesLanguageView(LaunchpadView): | 31 | class BaseSeriesLanguageView(LaunchpadView): |
1041 | 32 | """View base class to render translation status for an | 32 | """View base class to render translation status for an |
1042 | 33 | `IDistroSeries` and `IProductSeries` | 33 | `IDistroSeries` and `IProductSeries` |
1043 | 34 | 34 | ||
1044 | 35 | This class should not be directly instantiated. | 35 | This class should not be directly instantiated. |
1045 | @@ -46,12 +46,15 @@ | |||
1046 | 46 | self.translationgroup = translationgroup | 46 | self.translationgroup = translationgroup |
1047 | 47 | self.form = self.request.form | 47 | self.form = self.request.form |
1048 | 48 | 48 | ||
1055 | 49 | self.batchnav = BatchNavigator( | 49 | if IDistroSeriesLanguage.providedBy(self.context): |
1056 | 50 | self.series.getCurrentTranslationTemplates(), | 50 | self.batchnav = BatchNavigator( |
1057 | 51 | self.request) | 51 | self.series.getCurrentTranslationTemplates(), |
1058 | 52 | 52 | self.request) | |
1059 | 53 | self.pofiles = self.context.getPOFilesFor( | 53 | self.pofiles = self.context.getPOFilesFor( |
1060 | 54 | self.batchnav.currentBatch()) | 54 | self.batchnav.currentBatch()) |
1061 | 55 | else: | ||
1062 | 56 | self.batchnav = BatchNavigator(self.context.pofiles, self.request) | ||
1063 | 57 | self.pofiles = self.batchnav.currentBatch() | ||
1064 | 55 | 58 | ||
1065 | 56 | @property | 59 | @property |
1066 | 57 | def translation_group(self): | 60 | def translation_group(self): |
1067 | @@ -77,7 +80,7 @@ | |||
1068 | 77 | @property | 80 | @property |
1069 | 78 | def access_level_description(self): | 81 | def access_level_description(self): |
1070 | 79 | """Must not be called when there's no translation group.""" | 82 | """Must not be called when there's no translation group.""" |
1072 | 80 | 83 | ||
1073 | 81 | if is_read_only(): | 84 | if is_read_only(): |
1074 | 82 | return ( | 85 | return ( |
1075 | 83 | "No work can be done on these translations while Launchpad " | 86 | "No work can be done on these translations while Launchpad " |
1076 | 84 | 87 | ||
1077 | === modified file 'lib/lp/translations/browser/tests/test_breadcrumbs.py' | |||
1078 | --- lib/lp/translations/browser/tests/test_breadcrumbs.py 2010-07-19 15:31:57 +0000 | |||
1079 | +++ lib/lp/translations/browser/tests/test_breadcrumbs.py 2010-07-23 12:49:25 +0000 | |||
1080 | @@ -9,6 +9,7 @@ | |||
1081 | 9 | 9 | ||
1082 | 10 | from lp.services.worlddata.interfaces.language import ILanguageSet | 10 | from lp.services.worlddata.interfaces.language import ILanguageSet |
1083 | 11 | from lp.testing.breadcrumbs import BaseBreadcrumbTestCase | 11 | from lp.testing.breadcrumbs import BaseBreadcrumbTestCase |
1084 | 12 | from lp.testing.factory import remove_security_proxy_and_shout_at_engineer | ||
1085 | 12 | from lp.translations.interfaces.distroserieslanguage import ( | 13 | from lp.translations.interfaces.distroserieslanguage import ( |
1086 | 13 | IDistroSeriesLanguageSet) | 14 | IDistroSeriesLanguageSet) |
1087 | 14 | from lp.translations.interfaces.productserieslanguage import ( | 15 | from lp.translations.interfaces.productserieslanguage import ( |
1088 | @@ -109,7 +110,8 @@ | |||
1089 | 109 | name='crumb-tester', displayname="Crumb Tester") | 110 | name='crumb-tester', displayname="Crumb Tester") |
1090 | 110 | series = self.factory.makeDistroRelease( | 111 | series = self.factory.makeDistroRelease( |
1091 | 111 | name="test", version="1.0", distribution=distribution) | 112 | name="test", version="1.0", distribution=distribution) |
1093 | 112 | series.hide_all_translations = False | 113 | naked_series = remove_security_proxy_and_shout_at_engineer(series) |
1094 | 114 | naked_series.hide_all_translations = False | ||
1095 | 113 | serieslanguage = getUtility(IDistroSeriesLanguageSet).getDummy( | 115 | serieslanguage = getUtility(IDistroSeriesLanguageSet).getDummy( |
1096 | 114 | series, self.language) | 116 | series, self.language) |
1097 | 115 | 117 | ||
1098 | 116 | 118 | ||
1099 | === modified file 'lib/lp/translations/configure.zcml' | |||
1100 | --- lib/lp/translations/configure.zcml 2010-07-23 12:49:10 +0000 | |||
1101 | +++ lib/lp/translations/configure.zcml 2010-07-23 12:49:25 +0000 | |||
1102 | @@ -399,6 +399,16 @@ | |||
1103 | 399 | interface="lp.translations.interfaces.productserieslanguage.IProductSeriesLanguageSet"/> | 399 | interface="lp.translations.interfaces.productserieslanguage.IProductSeriesLanguageSet"/> |
1104 | 400 | </securedutility> | 400 | </securedutility> |
1105 | 401 | 401 | ||
1106 | 402 | <!-- TranslatedLanguage --> | ||
1107 | 403 | <facet | ||
1108 | 404 | facet="translations"> | ||
1109 | 405 | <class | ||
1110 | 406 | class="lp.translations.model.translatedlanguage.POFilesByPOTemplates"> | ||
1111 | 407 | <allow | ||
1112 | 408 | interface="lp.translations.interfaces.translatedlanguage.IPOFilesByPOTemplates"/> | ||
1113 | 409 | </class> | ||
1114 | 410 | </facet> | ||
1115 | 411 | |||
1116 | 402 | <!-- POTemplate --> | 412 | <!-- POTemplate --> |
1117 | 403 | <facet | 413 | <facet |
1118 | 404 | facet="translations"> | 414 | facet="translations"> |
1119 | @@ -430,6 +440,13 @@ | |||
1120 | 430 | provides="lp.translations.interfaces.translationcommonformat.ITranslationFileData" | 440 | provides="lp.translations.interfaces.translationcommonformat.ITranslationFileData" |
1121 | 431 | factory="lp.translations.model.potemplate.POTemplateToTranslationFileDataAdapter"/> | 441 | factory="lp.translations.model.potemplate.POTemplateToTranslationFileDataAdapter"/> |
1122 | 432 | 442 | ||
1123 | 443 | <!-- TranslationTemplatesCollection --> | ||
1124 | 444 | <class | ||
1125 | 445 | class="lp.translations.model.potemplate.TranslationTemplatesCollection"> | ||
1126 | 446 | <allow | ||
1127 | 447 | interface="lp.translations.interfaces.potemplate.ITranslationTemplatesCollection"/> | ||
1128 | 448 | </class> | ||
1129 | 449 | |||
1130 | 433 | <!-- POTemplateSet --> | 450 | <!-- POTemplateSet --> |
1131 | 434 | 451 | ||
1132 | 435 | <securedutility | 452 | <securedutility |
1133 | 436 | 453 | ||
1134 | === modified file 'lib/lp/translations/doc/translations-export-to-branch.txt' | |||
1135 | --- lib/lp/translations/doc/translations-export-to-branch.txt 2010-06-16 07:22:57 +0000 | |||
1136 | +++ lib/lp/translations/doc/translations-export-to-branch.txt 2010-07-23 12:49:25 +0000 | |||
1137 | @@ -220,8 +220,12 @@ | |||
1138 | 220 | >>> from email import message_from_string | 220 | >>> from email import message_from_string |
1139 | 221 | >>> from lp.services.mail import stub | 221 | >>> from lp.services.mail import stub |
1140 | 222 | >>> from lp.codehosting.vfs import get_rw_server | 222 | >>> from lp.codehosting.vfs import get_rw_server |
1141 | 223 | >>> from lp.testing.factory import ( | ||
1142 | 224 | ... remove_security_proxy_and_shout_at_engineer) | ||
1143 | 223 | >>> productseries = factory.makeProductSeries() | 225 | >>> productseries = factory.makeProductSeries() |
1145 | 224 | >>> productseries.translations_branch = factory.makeBranch() | 226 | >>> naked_productseries = remove_security_proxy_and_shout_at_engineer( |
1146 | 227 | ... productseries) | ||
1147 | 228 | >>> naked_productseries.translations_branch = factory.makeBranch() | ||
1148 | 225 | >>> template = factory.makePOTemplate(productseries=productseries) | 229 | >>> template = factory.makePOTemplate(productseries=productseries) |
1149 | 226 | >>> potmsgset = factory.makePOTMsgSet(template) | 230 | >>> potmsgset = factory.makePOTMsgSet(template) |
1150 | 227 | >>> pofile = removeSecurityProxy( | 231 | >>> pofile = removeSecurityProxy( |
1151 | 228 | 232 | ||
1152 | === modified file 'lib/lp/translations/interfaces/potemplate.py' | |||
1153 | --- lib/lp/translations/interfaces/potemplate.py 2010-07-23 12:49:10 +0000 | |||
1154 | +++ lib/lp/translations/interfaces/potemplate.py 2010-07-23 12:49:25 +0000 | |||
1155 | @@ -781,5 +781,19 @@ | |||
1156 | 781 | exist for it. | 781 | exist for it. |
1157 | 782 | """ | 782 | """ |
1158 | 783 | 783 | ||
1159 | 784 | class ITranslationTemplatesCollection(Interface): | ||
1160 | 785 | """A `Collection` of `POTemplate`s.""" | ||
1161 | 786 | |||
1162 | 787 | def joinOuterPOFile(language=None): | ||
1163 | 788 | """Outer-join `POFile` into the collection. | ||
1164 | 789 | |||
1165 | 790 | :return: A `TranslationTemplatesCollection` with an added outer | ||
1166 | 791 | join to `POFile`. | ||
1167 | 792 | """ | ||
1168 | 793 | |||
1169 | 794 | def select(*args): | ||
1170 | 795 | """Return a ResultSet for this collection with values set to args.""" | ||
1171 | 796 | |||
1172 | 797 | |||
1173 | 784 | # Monkey patch for circular import avoidance done in | 798 | # Monkey patch for circular import avoidance done in |
1174 | 785 | # _schema_circular_imports.py | 799 | # _schema_circular_imports.py |
1175 | 786 | 800 | ||
1176 | === modified file 'lib/lp/translations/interfaces/productserieslanguage.py' | |||
1177 | --- lib/lp/translations/interfaces/productserieslanguage.py 2010-07-19 15:31:57 +0000 | |||
1178 | +++ lib/lp/translations/interfaces/productserieslanguage.py 2010-07-23 12:49:25 +0000 | |||
1179 | @@ -5,13 +5,13 @@ | |||
1180 | 5 | 5 | ||
1181 | 6 | from lazr.restful.fields import Reference | 6 | from lazr.restful.fields import Reference |
1182 | 7 | 7 | ||
1186 | 8 | from zope.interface import Attribute, Interface | 8 | from zope.interface import Interface |
1187 | 9 | from zope.schema import ( | 9 | from zope.schema import Choice, TextLine |
1185 | 10 | Choice, Datetime, TextLine) | ||
1188 | 11 | 10 | ||
1189 | 12 | from canonical.launchpad import _ | 11 | from canonical.launchpad import _ |
1190 | 13 | from lp.translations.interfaces.pofile import IPOFile | 12 | from lp.translations.interfaces.pofile import IPOFile |
1191 | 14 | from lp.translations.interfaces.rosettastats import IRosettaStats | 13 | from lp.translations.interfaces.rosettastats import IRosettaStats |
1192 | 14 | from lp.translations.interfaces.translatedlanguage import ITranslatedLanguage | ||
1193 | 15 | 15 | ||
1194 | 16 | __metaclass__ = type | 16 | __metaclass__ = type |
1195 | 17 | 17 | ||
1196 | @@ -21,13 +21,9 @@ | |||
1197 | 21 | ] | 21 | ] |
1198 | 22 | 22 | ||
1199 | 23 | 23 | ||
1201 | 24 | class IProductSeriesLanguage(IRosettaStats): | 24 | class IProductSeriesLanguage(IRosettaStats, ITranslatedLanguage): |
1202 | 25 | """Per-language statistics for a product series.""" | 25 | """Per-language statistics for a product series.""" |
1203 | 26 | 26 | ||
1204 | 27 | language = Choice( | ||
1205 | 28 | title=_('Language to gather statistics for.'), | ||
1206 | 29 | vocabulary='Language', required=True, readonly=True) | ||
1207 | 30 | |||
1208 | 31 | pofile = Reference( | 27 | pofile = Reference( |
1209 | 32 | title=_("A POFile if there is only one POTemplate for the series."), | 28 | title=_("A POFile if there is only one POTemplate for the series."), |
1210 | 33 | schema=IPOFile, required=False, readonly=True) | 29 | schema=IPOFile, required=False, readonly=True) |
1211 | @@ -41,27 +37,6 @@ | |||
1212 | 41 | title=_("Title for the per-language per-series page."), | 37 | title=_("Title for the per-language per-series page."), |
1213 | 42 | required=False) | 38 | required=False) |
1214 | 43 | 39 | ||
1215 | 44 | pofiles = Attribute("The set of pofiles in this distroseries for this " | ||
1216 | 45 | "language. This includes only the real pofiles where translations " | ||
1217 | 46 | "exist.") | ||
1218 | 47 | |||
1219 | 48 | |||
1220 | 49 | last_changed_date = Datetime( | ||
1221 | 50 | title=_('When this file was last changed.')) | ||
1222 | 51 | |||
1223 | 52 | def getPOFilesFor(potemplates): | ||
1224 | 53 | """Return `POFiles` for each of `potemplates`, in the same order. | ||
1225 | 54 | |||
1226 | 55 | For any `POTemplate` that does not have a translation to the | ||
1227 | 56 | required language, a `DummyPOFile` is provided. | ||
1228 | 57 | """ | ||
1229 | 58 | |||
1230 | 59 | def setCounts(total, imported, changed, new, unreviewed, last_changed): | ||
1231 | 60 | """Set aggregated message counts for ProductSeriesLanguage.""" | ||
1232 | 61 | |||
1233 | 62 | def recalculateCounts(total, imported, changed, new, unreviewed): | ||
1234 | 63 | """Recalculate message counts for this ProductSeriesLanguage.""" | ||
1235 | 64 | |||
1236 | 65 | 40 | ||
1237 | 66 | class IProductSeriesLanguageSet(Interface): | 41 | class IProductSeriesLanguageSet(Interface): |
1238 | 67 | """The set of productserieslanguages.""" | 42 | """The set of productserieslanguages.""" |
1239 | 68 | 43 | ||
1240 | === added file 'lib/lp/translations/interfaces/translatedlanguage.py' | |||
1241 | --- lib/lp/translations/interfaces/translatedlanguage.py 1970-01-01 00:00:00 +0000 | |||
1242 | +++ lib/lp/translations/interfaces/translatedlanguage.py 2010-07-23 12:49:25 +0000 | |||
1243 | @@ -0,0 +1,79 @@ | |||
1244 | 1 | # Copyright 2009 Canonical Ltd. This software is licensed under the | ||
1245 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
1246 | 3 | |||
1247 | 4 | # pylint: disable-msg=E0211,E0213 | ||
1248 | 5 | |||
1249 | 6 | from zope.interface import Attribute, Interface | ||
1250 | 7 | from zope.interface.common.sequence import IFiniteSequence | ||
1251 | 8 | from zope.schema import Datetime, Object | ||
1252 | 9 | |||
1253 | 10 | from canonical.launchpad import _ | ||
1254 | 11 | from lp.services.worlddata.interfaces.language import ILanguage | ||
1255 | 12 | from lp.translations.interfaces.potemplate import IHasTranslationTemplates | ||
1256 | 13 | from lp.registry.interfaces.person import IPerson | ||
1257 | 14 | |||
1258 | 15 | __metaclass__ = type | ||
1259 | 16 | |||
1260 | 17 | __all__ = [ | ||
1261 | 18 | 'IPOFilesByPOTemplates', | ||
1262 | 19 | 'ITranslatedLanguage', | ||
1263 | 20 | ] | ||
1264 | 21 | |||
1265 | 22 | |||
1266 | 23 | class ITranslatedLanguage(Interface): | ||
1267 | 24 | """Interface for providing translations for context by language. | ||
1268 | 25 | |||
1269 | 26 | It expects `parent` to provide `IHasTranslationTemplates`. | ||
1270 | 27 | """ | ||
1271 | 28 | |||
1272 | 29 | language = Object( | ||
1273 | 30 | title=_('Language to gather statistics and POFiles for.'), | ||
1274 | 31 | schema=ILanguage) | ||
1275 | 32 | |||
1276 | 33 | parent = Object( | ||
1277 | 34 | title=_('A parent with translation templates.'), | ||
1278 | 35 | schema=IHasTranslationTemplates) | ||
1279 | 36 | |||
1280 | 37 | pofiles = Attribute( | ||
1281 | 38 | _('Iterator over all POFiles for this context and language.')) | ||
1282 | 39 | |||
1283 | 40 | translation_statistics = Attribute( | ||
1284 | 41 | _('A dict containing relevant aggregated statistics counts.')) | ||
1285 | 42 | |||
1286 | 43 | def setCounts(total, translated, new, changed, unreviewed): | ||
1287 | 44 | """Set aggregated message counts for ITranslatedLanguage.""" | ||
1288 | 45 | |||
1289 | 46 | def recalculateCounts(): | ||
1290 | 47 | """Recalculate message counts for this ITranslatedLanguage.""" | ||
1291 | 48 | |||
1292 | 49 | last_changed_date = Datetime( | ||
1293 | 50 | title=_('When was this translation last changed.'), | ||
1294 | 51 | readonly=False, required=True) | ||
1295 | 52 | |||
1296 | 53 | last_translator = Object( | ||
1297 | 54 | title=_('Last person that translated something in this context.'), | ||
1298 | 55 | schema=IPerson) | ||
1299 | 56 | |||
1300 | 57 | |||
1301 | 58 | class IPOFilesByPOTemplates(IFiniteSequence): | ||
1302 | 59 | """Iterate `IPOFile`s for (`ILanguage`, `ITranslationTemplateCollection`). | ||
1303 | 60 | |||
1304 | 61 | This is a wrapper for Storm ResultSet that enables optimized slicing | ||
1305 | 62 | by doing it lazily on the query, thus allowing DummyPOFile objects | ||
1306 | 63 | to be returned while still not doing more than one database query. | ||
1307 | 64 | |||
1308 | 65 | It subclasses `IFiniteSequence` so it can easily be used with the | ||
1309 | 66 | BatchNavigator. | ||
1310 | 67 | """ | ||
1311 | 68 | |||
1312 | 69 | def __getitem__(selector): | ||
1313 | 70 | """Get an element or slice of `IPOFile`s for given templates.""" | ||
1314 | 71 | |||
1315 | 72 | def __getslice__(start, end): | ||
1316 | 73 | """Deprecated, and implemented through __getitem__.""" | ||
1317 | 74 | |||
1318 | 75 | def __iter__(): | ||
1319 | 76 | """Iterates over all `IPOFile`s for given templates.""" | ||
1320 | 77 | |||
1321 | 78 | def __len__(): | ||
1322 | 79 | """Provides count of `IPOTemplate`s in a template collection.""" | ||
1323 | 0 | 80 | ||
1324 | === modified file 'lib/lp/translations/model/potemplate.py' | |||
1325 | --- lib/lp/translations/model/potemplate.py 2010-07-23 12:49:10 +0000 | |||
1326 | +++ lib/lp/translations/model/potemplate.py 2010-07-23 12:49:25 +0000 | |||
1327 | @@ -1565,7 +1565,8 @@ | |||
1328 | 1565 | @property | 1565 | @property |
1329 | 1566 | def has_current_translation_templates(self): | 1566 | def has_current_translation_templates(self): |
1330 | 1567 | """See `IHasTranslationTemplates`.""" | 1567 | """See `IHasTranslationTemplates`.""" |
1332 | 1568 | return bool(self.getCurrentTranslationTemplates(just_ids=True).any()) | 1568 | return bool( |
1333 | 1569 | self.getCurrentTranslationTemplates(just_ids=True).any()) | ||
1334 | 1569 | 1570 | ||
1335 | 1570 | def getCurrentTranslationFiles(self, just_ids=False): | 1571 | def getCurrentTranslationFiles(self, just_ids=False): |
1336 | 1571 | """See `IHasTranslationTemplates`.""" | 1572 | """See `IHasTranslationTemplates`.""" |
1337 | @@ -1660,10 +1661,16 @@ | |||
1338 | 1660 | """ | 1661 | """ |
1339 | 1661 | return self.joinInner(POFile, POTemplate.id == POFile.potemplateID) | 1662 | return self.joinInner(POFile, POTemplate.id == POFile.potemplateID) |
1340 | 1662 | 1663 | ||
1342 | 1663 | def joinOuterPOFile(self): | 1664 | def joinOuterPOFile(self, language=None): |
1343 | 1664 | """Outer-join `POFile` into the collection. | 1665 | """Outer-join `POFile` into the collection. |
1344 | 1665 | 1666 | ||
1345 | 1666 | :return: A `TranslationTemplatesCollection` with an added outer | 1667 | :return: A `TranslationTemplatesCollection` with an added outer |
1346 | 1667 | join to `POFile`. | 1668 | join to `POFile`. |
1347 | 1668 | """ | 1669 | """ |
1349 | 1669 | return self.joinOuter(POFile, POTemplate.id == POFile.potemplateID) | 1670 | if language is not None: |
1350 | 1671 | return self.joinOuter( | ||
1351 | 1672 | POFile, And(POTemplate.id == POFile.potemplateID, | ||
1352 | 1673 | POFile.languageID == language.id)) | ||
1353 | 1674 | else: | ||
1354 | 1675 | return self.joinOuter( | ||
1355 | 1676 | POFile, POTemplate.id == POFile.potemplateID) | ||
1356 | 1670 | 1677 | ||
1357 | === modified file 'lib/lp/translations/model/productserieslanguage.py' | |||
1358 | --- lib/lp/translations/model/productserieslanguage.py 2010-07-19 15:38:51 +0000 | |||
1359 | +++ lib/lp/translations/model/productserieslanguage.py 2010-07-23 12:49:25 +0000 | |||
1360 | @@ -12,17 +12,13 @@ | |||
1361 | 12 | 12 | ||
1362 | 13 | from zope.interface import implements | 13 | from zope.interface import implements |
1363 | 14 | 14 | ||
1364 | 15 | from storm.expr import Coalesce, Sum | ||
1365 | 16 | from storm.store import Store | ||
1366 | 17 | |||
1367 | 18 | from lp.translations.utilities.rosettastats import RosettaStats | 15 | from lp.translations.utilities.rosettastats import RosettaStats |
1370 | 19 | from lp.translations.model.pofile import POFile | 16 | from lp.translations.model.translatedlanguage import TranslatedLanguageMixin |
1369 | 20 | from lp.translations.model.potemplate import get_pofiles_for, POTemplate | ||
1371 | 21 | from lp.translations.interfaces.productserieslanguage import ( | 17 | from lp.translations.interfaces.productserieslanguage import ( |
1372 | 22 | IProductSeriesLanguage, IProductSeriesLanguageSet) | 18 | IProductSeriesLanguage, IProductSeriesLanguageSet) |
1373 | 23 | 19 | ||
1374 | 24 | 20 | ||
1376 | 25 | class ProductSeriesLanguage(RosettaStats): | 21 | class ProductSeriesLanguage(RosettaStats, TranslatedLanguageMixin): |
1377 | 26 | """See `IProductSeriesLanguage`.""" | 22 | """See `IProductSeriesLanguage`.""" |
1378 | 27 | implements(IProductSeriesLanguage) | 23 | implements(IProductSeriesLanguage) |
1379 | 28 | 24 | ||
1380 | @@ -30,56 +26,14 @@ | |||
1381 | 30 | assert 'en' != language.code, ( | 26 | assert 'en' != language.code, ( |
1382 | 31 | 'English is not a translatable language.') | 27 | 'English is not a translatable language.') |
1383 | 32 | RosettaStats.__init__(self) | 28 | RosettaStats.__init__(self) |
1384 | 29 | TranslatedLanguageMixin.__init__(self) | ||
1385 | 33 | self.productseries = productseries | 30 | self.productseries = productseries |
1386 | 31 | self.parent = productseries | ||
1387 | 34 | self.language = language | 32 | self.language = language |
1388 | 35 | self.variant = variant | 33 | self.variant = variant |
1389 | 36 | self.pofile = pofile | 34 | self.pofile = pofile |
1390 | 37 | self.id = 0 | 35 | self.id = 0 |
1436 | 38 | self._last_changed_date = None | 36 | self.last_changed_date = None |
1392 | 39 | |||
1393 | 40 | # Reset all cached counts. | ||
1394 | 41 | self.setCounts() | ||
1395 | 42 | |||
1396 | 43 | def setCounts(self, total=0, imported=0, changed=0, new=0, | ||
1397 | 44 | unreviewed=0, last_changed=None): | ||
1398 | 45 | """See `IProductSeriesLanguage`.""" | ||
1399 | 46 | self._messagecount = total | ||
1400 | 47 | # "currentcount" in RosettaStats conflicts our recent terminology | ||
1401 | 48 | # and is closer to "imported" (except that it doesn't include | ||
1402 | 49 | # "changed") translations. | ||
1403 | 50 | self._currentcount = imported | ||
1404 | 51 | self._updatescount = changed | ||
1405 | 52 | self._rosettacount = new | ||
1406 | 53 | self._unreviewed_count = unreviewed | ||
1407 | 54 | if last_changed is not None: | ||
1408 | 55 | self._last_changed_date = last_changed | ||
1409 | 56 | |||
1410 | 57 | def _getMessageCount(self): | ||
1411 | 58 | store = Store.of(self.language) | ||
1412 | 59 | query = store.find(Sum(POTemplate.messagecount), | ||
1413 | 60 | POTemplate.productseries==self.productseries, | ||
1414 | 61 | POTemplate.iscurrent==True) | ||
1415 | 62 | total, = query | ||
1416 | 63 | if total is None: | ||
1417 | 64 | total = 0 | ||
1418 | 65 | return total | ||
1419 | 66 | |||
1420 | 67 | def recalculateCounts(self): | ||
1421 | 68 | """See `IProductSeriesLanguage`.""" | ||
1422 | 69 | store = Store.of(self.language) | ||
1423 | 70 | query = store.find( | ||
1424 | 71 | (Coalesce(Sum(POFile.currentcount), 0), | ||
1425 | 72 | Coalesce(Sum(POFile.updatescount), 0), | ||
1426 | 73 | Coalesce(Sum(POFile.rosettacount), 0), | ||
1427 | 74 | Coalesce(Sum(POFile.unreviewed_count), 0)), | ||
1428 | 75 | POFile.language==self.language, | ||
1429 | 76 | POFile.variant==None, | ||
1430 | 77 | POFile.potemplate==POTemplate.id, | ||
1431 | 78 | POTemplate.productseries==self.productseries, | ||
1432 | 79 | POTemplate.iscurrent==True) | ||
1433 | 80 | imported, changed, new, unreviewed = query[0] | ||
1434 | 81 | self.setCounts(self._getMessageCount(), imported, changed, | ||
1435 | 82 | new, unreviewed) | ||
1437 | 83 | 37 | ||
1438 | 84 | @property | 38 | @property |
1439 | 85 | def title(self): | 39 | def title(self): |
1440 | @@ -90,46 +44,29 @@ | |||
1441 | 90 | self.productseries.displayname) | 44 | self.productseries.displayname) |
1442 | 91 | 45 | ||
1443 | 92 | def messageCount(self): | 46 | def messageCount(self): |
1446 | 93 | """See `IProductSeriesLanguage`.""" | 47 | """See `IRosettaStats`.""" |
1447 | 94 | return self._messagecount | 48 | return self._translation_statistics['total_count'] |
1448 | 95 | 49 | ||
1449 | 96 | def currentCount(self, language=None): | 50 | def currentCount(self, language=None): |
1452 | 97 | """See `IProductSeriesLanguage`.""" | 51 | """See `IRosettaStats`.""" |
1453 | 98 | return self._currentcount | 52 | translated = self._translation_statistics['translated_count'] |
1454 | 53 | current = translated - self.rosettaCount(language) | ||
1455 | 54 | return current | ||
1456 | 99 | 55 | ||
1457 | 100 | def updatesCount(self, language=None): | 56 | def updatesCount(self, language=None): |
1460 | 101 | """See `IProductSeriesLanguage`.""" | 57 | """See `IRosettaStats`.""" |
1461 | 102 | return self._updatescount | 58 | return self._translation_statistics['changed_count'] |
1462 | 103 | 59 | ||
1463 | 104 | def rosettaCount(self, language=None): | 60 | def rosettaCount(self, language=None): |
1466 | 105 | """See `IProductSeriesLanguage`.""" | 61 | """See `IRosettaStats`.""" |
1467 | 106 | return self._rosettacount | 62 | new = self._translation_statistics['new_count'] |
1468 | 63 | changed = self._translation_statistics['changed_count'] | ||
1469 | 64 | rosetta = new + changed | ||
1470 | 65 | return rosetta | ||
1471 | 107 | 66 | ||
1472 | 108 | def unreviewedCount(self): | 67 | def unreviewedCount(self): |
1497 | 109 | """See `IProductSeriesLanguage`.""" | 68 | """See `IRosettaStats`.""" |
1498 | 110 | return self._unreviewed_count | 69 | return self._translation_statistics['unreviewed_count'] |
1475 | 111 | |||
1476 | 112 | @property | ||
1477 | 113 | def last_changed_date(self): | ||
1478 | 114 | """See `IProductSeriesLanguage`.""" | ||
1479 | 115 | return self._last_changed_date | ||
1480 | 116 | |||
1481 | 117 | @property | ||
1482 | 118 | def pofiles(self): | ||
1483 | 119 | """See `IProductSeriesLanguage`.""" | ||
1484 | 120 | store = Store.of(self.language) | ||
1485 | 121 | result = store.find( | ||
1486 | 122 | POFile, | ||
1487 | 123 | POFile.language==self.language, | ||
1488 | 124 | POFile.variant==self.variant, | ||
1489 | 125 | POFile.potemplate==POTemplate.id, | ||
1490 | 126 | POTemplate.productseries==self.productseries, | ||
1491 | 127 | POTemplate.iscurrent==True) | ||
1492 | 128 | return result.order_by(['-priority']) | ||
1493 | 129 | |||
1494 | 130 | def getPOFilesFor(self, potemplates): | ||
1495 | 131 | """See `IProductSeriesLanguage`.""" | ||
1496 | 132 | return get_pofiles_for(potemplates, self.language, self.variant) | ||
1499 | 133 | 70 | ||
1500 | 134 | 71 | ||
1501 | 135 | class ProductSeriesLanguageSet: | 72 | class ProductSeriesLanguageSet: |
1502 | 136 | 73 | ||
1503 | === added file 'lib/lp/translations/model/translatedlanguage.py' | |||
1504 | --- lib/lp/translations/model/translatedlanguage.py 1970-01-01 00:00:00 +0000 | |||
1505 | +++ lib/lp/translations/model/translatedlanguage.py 2010-07-23 12:49:25 +0000 | |||
1506 | @@ -0,0 +1,132 @@ | |||
1507 | 1 | # Copyright 2010 Canonical Ltd. This software is licensed under the | ||
1508 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
1509 | 3 | |||
1510 | 4 | __all__ = ['TranslatedLanguageMixin'] | ||
1511 | 5 | |||
1512 | 6 | import pytz | ||
1513 | 7 | |||
1514 | 8 | from zope.interface import implements | ||
1515 | 9 | |||
1516 | 10 | from storm.expr import Coalesce, Desc, Max, Sum | ||
1517 | 11 | |||
1518 | 12 | from lp.translations.interfaces.potemplate import IHasTranslationTemplates | ||
1519 | 13 | from lp.translations.interfaces.translatedlanguage import ( | ||
1520 | 14 | IPOFilesByPOTemplates, ITranslatedLanguage) | ||
1521 | 15 | from lp.translations.model.pofile import POFile | ||
1522 | 16 | from lp.translations.model.potemplate import POTemplate | ||
1523 | 17 | |||
1524 | 18 | |||
1525 | 19 | class POFilesByPOTemplates(object): | ||
1526 | 20 | """See `IPOFilesByPOTemplates`.""" | ||
1527 | 21 | implements(IPOFilesByPOTemplates) | ||
1528 | 22 | |||
1529 | 23 | def __init__(self, templates_collection, language): | ||
1530 | 24 | self.templates_collection = templates_collection | ||
1531 | 25 | self.language = language | ||
1532 | 26 | |||
1533 | 27 | def _getDummyOrPOFile(self, potemplate, pofile): | ||
1534 | 28 | if pofile is None: | ||
1535 | 29 | return potemplate.getDummyPOFile(self.language, | ||
1536 | 30 | check_for_existing=False) | ||
1537 | 31 | else: | ||
1538 | 32 | return pofile | ||
1539 | 33 | |||
1540 | 34 | def _getPOTemplatesAndPOFilesResultSet(self): | ||
1541 | 35 | current_templates = self.templates_collection | ||
1542 | 36 | pofiles = current_templates.joinOuterPOFile(self.language) | ||
1543 | 37 | results = pofiles.select(POTemplate, POFile).order_by( | ||
1544 | 38 | Desc(POTemplate.priority), POTemplate.name) | ||
1545 | 39 | return results | ||
1546 | 40 | |||
1547 | 41 | def _getPOFilesForResultSet(self, resultset, selector=None): | ||
1548 | 42 | pofiles_list = [] | ||
1549 | 43 | if selector is None: | ||
1550 | 44 | results = resultset | ||
1551 | 45 | else: | ||
1552 | 46 | results = resultset[selector] | ||
1553 | 47 | for potemplate, pofile in results: | ||
1554 | 48 | pofiles_list.append(self._getDummyOrPOFile(potemplate, pofile)) | ||
1555 | 49 | return pofiles_list | ||
1556 | 50 | |||
1557 | 51 | def __getitem__(self, selector): | ||
1558 | 52 | resultset = self._getPOTemplatesAndPOFilesResultSet() | ||
1559 | 53 | if isinstance(selector, slice): | ||
1560 | 54 | return self._getPOFilesForResultSet(resultset, selector) | ||
1561 | 55 | else: | ||
1562 | 56 | potemplate, pofile = resultset[selector] | ||
1563 | 57 | return self._getDummyOrPOFile(potemplate, pofile) | ||
1564 | 58 | |||
1565 | 59 | def __iter__(self): | ||
1566 | 60 | resultset = self._getPOTemplatesAndPOFilesResultSet() | ||
1567 | 61 | for pofile in self._getPOFilesForResultSet(resultset): | ||
1568 | 62 | yield pofile | ||
1569 | 63 | |||
1570 | 64 | def __len__(self): | ||
1571 | 65 | return self.templates_collection.select(POTemplate).count() | ||
1572 | 66 | |||
1573 | 67 | def __nonzero__(self): | ||
1574 | 68 | return bool(self.templates_collection.select(POTemplate).any()) | ||
1575 | 69 | |||
1576 | 70 | |||
1577 | 71 | class TranslatedLanguageMixin(object): | ||
1578 | 72 | """See `ITranslatedLanguage`.""" | ||
1579 | 73 | implements(ITranslatedLanguage) | ||
1580 | 74 | |||
1581 | 75 | language = None | ||
1582 | 76 | parent = None | ||
1583 | 77 | |||
1584 | 78 | def __init__(self): | ||
1585 | 79 | self.setCounts(total=0, translated=0, new=0, changed=0, unreviewed=0) | ||
1586 | 80 | |||
1587 | 81 | @property | ||
1588 | 82 | def pofiles(self): | ||
1589 | 83 | """See `ITranslatedLanguage`.""" | ||
1590 | 84 | assert IHasTranslationTemplates.providedBy(self.parent), ( | ||
1591 | 85 | "Parent object should implement `IHasTranslationTemplates`.") | ||
1592 | 86 | current_templates = self.parent.getCurrentTemplatesCollection() | ||
1593 | 87 | return POFilesByPOTemplates(current_templates, self.language) | ||
1594 | 88 | |||
1595 | 89 | @property | ||
1596 | 90 | def translation_statistics(self): | ||
1597 | 91 | """See `ITranslatedLanguage`.""" | ||
1598 | 92 | # This is a temporary translation statistics 'object' to allow | ||
1599 | 93 | # smoother migration from IRosettaStats to something much nicer. | ||
1600 | 94 | return self._translation_statistics | ||
1601 | 95 | |||
1602 | 96 | def setCounts(self, total, translated, new, changed, unreviewed): | ||
1603 | 97 | """See `ITranslatedLanguage`.""" | ||
1604 | 98 | untranslated = total - translated | ||
1605 | 99 | self._translation_statistics = { | ||
1606 | 100 | 'total_count': total, | ||
1607 | 101 | 'translated_count': translated, | ||
1608 | 102 | 'new_count': new, | ||
1609 | 103 | 'changed_count': changed, | ||
1610 | 104 | 'unreviewed_count': unreviewed, | ||
1611 | 105 | 'untranslated_count': untranslated, | ||
1612 | 106 | } | ||
1613 | 107 | |||
1614 | 108 | def recalculateCounts(self): | ||
1615 | 109 | """See `ITranslatedLanguage`.""" | ||
1616 | 110 | templates = self.parent.getCurrentTemplatesCollection() | ||
1617 | 111 | pofiles = templates.joinOuterPOFile(self.language) | ||
1618 | 112 | total_count_results = list( | ||
1619 | 113 | pofiles.select(Coalesce(Sum(POTemplate.messagecount), 0), | ||
1620 | 114 | Coalesce(Sum(POFile.currentcount), 0), | ||
1621 | 115 | Coalesce(Sum(POFile.updatescount), 0), | ||
1622 | 116 | Coalesce(Sum(POFile.rosettacount), 0), | ||
1623 | 117 | Coalesce(Sum(POFile.unreviewed_count), 0), | ||
1624 | 118 | Max(POFile.date_changed))) | ||
1625 | 119 | total, imported, changed, rosetta, unreviewed, date_changed = ( | ||
1626 | 120 | total_count_results[0]) | ||
1627 | 121 | translated = imported + rosetta | ||
1628 | 122 | new = rosetta - changed | ||
1629 | 123 | self.setCounts(total, translated, new, changed, unreviewed) | ||
1630 | 124 | |||
1631 | 125 | # We have to add a timezone to the otherwise naive-datetime object | ||
1632 | 126 | # (because we've gotten it using Max() aggregate function). | ||
1633 | 127 | if date_changed is not None: | ||
1634 | 128 | date_changed = date_changed.replace(tzinfo=pytz.UTC) | ||
1635 | 129 | self.last_changed_date = date_changed | ||
1636 | 130 | |||
1637 | 131 | last_changed_date = None | ||
1638 | 132 | last_translator = None | ||
1639 | 0 | 133 | ||
1640 | === modified file 'lib/lp/translations/stories/buildfarm/xx-build-summary.txt' | |||
1641 | --- lib/lp/translations/stories/buildfarm/xx-build-summary.txt 2010-03-16 11:09:46 +0000 | |||
1642 | +++ lib/lp/translations/stories/buildfarm/xx-build-summary.txt 2010-07-23 12:49:25 +0000 | |||
1643 | @@ -14,6 +14,8 @@ | |||
1644 | 14 | ... ILibraryFileAliasSet) | 14 | ... ILibraryFileAliasSet) |
1645 | 15 | >>> from canonical.launchpad.scripts.logger import QuietFakeLogger | 15 | >>> from canonical.launchpad.scripts.logger import QuietFakeLogger |
1646 | 16 | >>> from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet | 16 | >>> from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet |
1647 | 17 | >>> from lp.testing.factory import ( | ||
1648 | 18 | ... remove_security_proxy_and_shout_at_engineer) | ||
1649 | 17 | >>> from lp.testing.fakemethod import FakeMethod | 19 | >>> from lp.testing.fakemethod import FakeMethod |
1650 | 18 | >>> from lp.translations.interfaces.translations import ( | 20 | >>> from lp.translations.interfaces.translations import ( |
1651 | 19 | ... TranslationsBranchImportMode) | 21 | ... TranslationsBranchImportMode) |
1652 | @@ -29,12 +31,15 @@ | |||
1653 | 29 | 31 | ||
1654 | 30 | >>> productseries = factory.makeProductSeries(owner=owner) | 32 | >>> productseries = factory.makeProductSeries(owner=owner) |
1655 | 31 | >>> product = productseries.product | 33 | >>> product = productseries.product |
1657 | 32 | >>> product.official_rosetta = True | 34 | >>> naked_product = remove_security_proxy_and_shout_at_engineer(product) |
1658 | 35 | >>> naked_product.official_rosetta = True | ||
1659 | 33 | >>> branch = factory.makeProductBranch(product=product, owner=owner) | 36 | >>> branch = factory.makeProductBranch(product=product, owner=owner) |
1660 | 34 | >>> branch_url = branch.unique_name | 37 | >>> branch_url = branch.unique_name |
1661 | 35 | 38 | ||
1664 | 36 | >>> productseries.branch = factory.makeBranch() | 39 | >>> naked_productseries = remove_security_proxy_and_shout_at_engineer( |
1665 | 37 | >>> productseries.translations_autoimport_mode = ( | 40 | ... productseries) |
1666 | 41 | >>> naked_productseries.branch = factory.makeBranch() | ||
1667 | 42 | >>> naked_productseries.translations_autoimport_mode = ( | ||
1668 | 38 | ... TranslationsBranchImportMode.IMPORT_TEMPLATES) | 43 | ... TranslationsBranchImportMode.IMPORT_TEMPLATES) |
1669 | 39 | >>> specific_job = factory.makeTranslationTemplatesBuildJob(branch=branch) | 44 | >>> specific_job = factory.makeTranslationTemplatesBuildJob(branch=branch) |
1670 | 40 | >>> buildqueue = getUtility(IBuildQueueSet).getByJob(specific_job.job) | 45 | >>> buildqueue = getUtility(IBuildQueueSet).getByJob(specific_job.job) |
1671 | 41 | 46 | ||
1672 | === modified file 'lib/lp/translations/tests/test_productserieslanguage.py' | |||
1673 | --- lib/lp/translations/tests/test_productserieslanguage.py 2010-07-21 09:35:41 +0000 | |||
1674 | +++ lib/lp/translations/tests/test_productserieslanguage.py 2010-07-23 12:49:25 +0000 | |||
1675 | @@ -173,8 +173,9 @@ | |||
1676 | 173 | self.productseries, self.language) | 173 | self.productseries, self.language) |
1677 | 174 | self.assertEquals(psl.messageCount(), 0) | 174 | self.assertEquals(psl.messageCount(), 0) |
1678 | 175 | 175 | ||
1681 | 176 | # So, we need to get it through productseries.productserieslanguages. | 176 | # We explicitely ask for stats to be recalculated. |
1682 | 177 | psl = self.productseries.productserieslanguages[0] | 177 | psl.recalculateCounts() |
1683 | 178 | |||
1684 | 178 | self.assertPSLStatistics(psl, | 179 | self.assertPSLStatistics(psl, |
1685 | 179 | (pofile.messageCount(), | 180 | (pofile.messageCount(), |
1686 | 180 | pofile.translatedCount(), | 181 | pofile.translatedCount(), |
1687 | @@ -199,17 +200,14 @@ | |||
1688 | 199 | self.setPOFileStatistics(pofile2, 1, 1, 1, 1, pofile2.date_changed) | 200 | self.setPOFileStatistics(pofile2, 1, 1, 1, 1, pofile2.date_changed) |
1689 | 200 | 201 | ||
1690 | 201 | psl = self.productseries.productserieslanguages[0] | 202 | psl = self.productseries.productserieslanguages[0] |
1696 | 202 | 203 | # We explicitely ask for stats to be recalculated. | |
1697 | 203 | # The psl.last_changed_date here is a naive datetime. So, for sake of | 204 | psl.recalculateCounts() |
1693 | 204 | # the tests, we should make pofile2 naive when checking if it matches | ||
1694 | 205 | # the last calculated changed date, that should be the same as | ||
1695 | 206 | # pofile2, created last. | ||
1698 | 207 | 205 | ||
1699 | 208 | # Total is a sum of totals in both POTemplates (10+20). | 206 | # Total is a sum of totals in both POTemplates (10+20). |
1700 | 209 | # Translated is a sum of imported and rosetta translations, | 207 | # Translated is a sum of imported and rosetta translations, |
1701 | 210 | # which adds up as (4+3)+(1+1). | 208 | # which adds up as (4+3)+(1+1). |
1702 | 211 | self.assertPSLStatistics(psl, (30, 9, 5, 4, 3, 6, | 209 | self.assertPSLStatistics(psl, (30, 9, 5, 4, 3, 6, |
1704 | 212 | pofile2.date_changed.replace(tzinfo=None))) | 210 | pofile2.date_changed)) |
1705 | 213 | self.assertPSLStatistics(psl, ( | 211 | self.assertPSLStatistics(psl, ( |
1706 | 214 | pofile1.messageCount() + pofile2.messageCount(), | 212 | pofile1.messageCount() + pofile2.messageCount(), |
1707 | 215 | pofile1.translatedCount() + pofile2.translatedCount(), | 213 | pofile1.translatedCount() + pofile2.translatedCount(), |
1708 | @@ -217,7 +215,7 @@ | |||
1709 | 217 | pofile1.rosettaCount() + pofile2.rosettaCount(), | 215 | pofile1.rosettaCount() + pofile2.rosettaCount(), |
1710 | 218 | pofile1.updatesCount() + pofile2.updatesCount(), | 216 | pofile1.updatesCount() + pofile2.updatesCount(), |
1711 | 219 | pofile1.unreviewedCount() + pofile2.unreviewedCount(), | 217 | pofile1.unreviewedCount() + pofile2.unreviewedCount(), |
1713 | 220 | pofile2.date_changed.replace(tzinfo=None))) | 218 | pofile2.date_changed)) |
1714 | 221 | 219 | ||
1715 | 222 | def test_recalculateCounts(self): | 220 | def test_recalculateCounts(self): |
1716 | 223 | # Test that recalculateCounts works correctly. | 221 | # Test that recalculateCounts works correctly. |
1717 | @@ -236,13 +234,14 @@ | |||
1718 | 236 | 234 | ||
1719 | 237 | psl = self.psl_set.getProductSeriesLanguage(self.productseries, | 235 | psl = self.psl_set.getProductSeriesLanguage(self.productseries, |
1720 | 238 | self.language) | 236 | self.language) |
1722 | 239 | # recalculateCounts() doesn't recalculate the last changed date. | 237 | |
1723 | 240 | psl.recalculateCounts() | 238 | psl.recalculateCounts() |
1724 | 241 | # Total is a sum of totals in both POTemplates (10+20). | 239 | # Total is a sum of totals in both POTemplates (10+20). |
1725 | 242 | # Translated is a sum of imported and rosetta translations, | 240 | # Translated is a sum of imported and rosetta translations, |
1726 | 243 | # which adds up as (1+3)+(1+1). | 241 | # which adds up as (1+3)+(1+1). |
1727 | 242 | # recalculateCounts() recalculates even the last changed date. | ||
1728 | 244 | self.assertPSLStatistics(psl, (30, 6, 2, 4, 3, 5, | 243 | self.assertPSLStatistics(psl, (30, 6, 2, 4, 3, 5, |
1730 | 245 | None)) | 244 | pofile2.date_changed)) |
1731 | 246 | 245 | ||
1732 | 247 | def test_recalculateCounts_no_pofiles(self): | 246 | def test_recalculateCounts_no_pofiles(self): |
1733 | 248 | # Test that recalculateCounts works correctly even when there | 247 | # Test that recalculateCounts works correctly even when there |
1734 | 249 | 248 | ||
1735 | === added file 'lib/lp/translations/tests/test_translatedlanguage.py' | |||
1736 | --- lib/lp/translations/tests/test_translatedlanguage.py 1970-01-01 00:00:00 +0000 | |||
1737 | +++ lib/lp/translations/tests/test_translatedlanguage.py 2010-07-23 12:49:25 +0000 | |||
1738 | @@ -0,0 +1,462 @@ | |||
1739 | 1 | # Copyright 2010 Canonical Ltd. This software is licensed under the | ||
1740 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
1741 | 3 | |||
1742 | 4 | __metaclass__ = type | ||
1743 | 5 | |||
1744 | 6 | from zope.component import getUtility | ||
1745 | 7 | from zope.interface.verify import verifyObject | ||
1746 | 8 | from zope.security.proxy import removeSecurityProxy | ||
1747 | 9 | |||
1748 | 10 | from lp.translations.interfaces.productserieslanguage import ( | ||
1749 | 11 | IProductSeriesLanguageSet) | ||
1750 | 12 | from lp.translations.interfaces.translatedlanguage import ITranslatedLanguage | ||
1751 | 13 | from lp.translations.model.pofile import DummyPOFile | ||
1752 | 14 | from lp.testing import TestCaseWithFactory | ||
1753 | 15 | from canonical.testing import ZopelessDatabaseLayer | ||
1754 | 16 | |||
1755 | 17 | |||
1756 | 18 | class TestTranslatedLanguageMixin(TestCaseWithFactory): | ||
1757 | 19 | """Test TranslatedLanguageMixin.""" | ||
1758 | 20 | |||
1759 | 21 | layer = ZopelessDatabaseLayer | ||
1760 | 22 | |||
1761 | 23 | def setUp(self): | ||
1762 | 24 | # Create a productseries that uses translations. | ||
1763 | 25 | TestCaseWithFactory.setUp(self) | ||
1764 | 26 | self.productseries = self.factory.makeProductSeries() | ||
1765 | 27 | self.productseries.product.official_rosetta = True | ||
1766 | 28 | self.parent = self.productseries | ||
1767 | 29 | self.psl_set = getUtility(IProductSeriesLanguageSet) | ||
1768 | 30 | self.language = self.factory.makeLanguage('sr@test') | ||
1769 | 31 | |||
1770 | 32 | def getTranslatedLanguage(self, language): | ||
1771 | 33 | return self.psl_set.getProductSeriesLanguage(self.productseries, | ||
1772 | 34 | language) | ||
1773 | 35 | |||
1774 | 36 | def addPOTemplate(self, number_of_potmsgsets=0, priority=0): | ||
1775 | 37 | potemplate = self.factory.makePOTemplate( | ||
1776 | 38 | productseries=self.productseries) | ||
1777 | 39 | for sequence in range(number_of_potmsgsets): | ||
1778 | 40 | self.factory.makePOTMsgSet(potemplate, sequence=sequence+1) | ||
1779 | 41 | removeSecurityProxy(potemplate).messagecount = number_of_potmsgsets | ||
1780 | 42 | potemplate.priority = priority | ||
1781 | 43 | return potemplate | ||
1782 | 44 | |||
1783 | 45 | def test_interface(self): | ||
1784 | 46 | translated_language = self.getTranslatedLanguage(self.language) | ||
1785 | 47 | self.assertTrue(verifyObject(ITranslatedLanguage, | ||
1786 | 48 | translated_language)) | ||
1787 | 49 | |||
1788 | 50 | def test_language(self): | ||
1789 | 51 | translated_language = self.getTranslatedLanguage(self.language) | ||
1790 | 52 | self.assertEqual(self.language, | ||
1791 | 53 | translated_language.language) | ||
1792 | 54 | |||
1793 | 55 | def test_parent(self): | ||
1794 | 56 | translated_language = self.getTranslatedLanguage(self.language) | ||
1795 | 57 | self.assertEqual(self.parent, | ||
1796 | 58 | translated_language.parent) | ||
1797 | 59 | |||
1798 | 60 | def test_pofiles_notemplates(self): | ||
1799 | 61 | translated_language = self.getTranslatedLanguage(self.language) | ||
1800 | 62 | self.assertEqual([], list(translated_language.pofiles)) | ||
1801 | 63 | |||
1802 | 64 | def test_pofiles_template_no_pofiles(self): | ||
1803 | 65 | translated_language = self.getTranslatedLanguage(self.language) | ||
1804 | 66 | potemplate = self.addPOTemplate() | ||
1805 | 67 | dummy_pofile = potemplate.getDummyPOFile(self.language) | ||
1806 | 68 | pofiles = list(translated_language.pofiles) | ||
1807 | 69 | self.assertEqual(1, len(pofiles)) | ||
1808 | 70 | |||
1809 | 71 | # When there are no actual PO files, we get a DummyPOFile object | ||
1810 | 72 | # instead. | ||
1811 | 73 | dummy_pofile = pofiles[0] | ||
1812 | 74 | naked_dummy = removeSecurityProxy(dummy_pofile) | ||
1813 | 75 | self.assertEqual(DummyPOFile, type(naked_dummy)) | ||
1814 | 76 | self.assertEqual(self.language, dummy_pofile.language) | ||
1815 | 77 | self.assertEqual(potemplate, dummy_pofile.potemplate) | ||
1816 | 78 | |||
1817 | 79 | # Two queries get executed when listifying | ||
1818 | 80 | # TranslatedLanguageMixin.pofiles: a len() does a count, and | ||
1819 | 81 | # then all POTemplates and POFiles are fetched with the other. | ||
1820 | 82 | self.assertStatementCount(2, list, translated_language.pofiles) | ||
1821 | 83 | |||
1822 | 84 | def test_pofiles_template_with_pofiles(self): | ||
1823 | 85 | translated_language = self.getTranslatedLanguage(self.language) | ||
1824 | 86 | potemplate = self.addPOTemplate() | ||
1825 | 87 | pofile = self.factory.makePOFile(self.language.code, potemplate) | ||
1826 | 88 | self.assertEqual([pofile], list(translated_language.pofiles)) | ||
1827 | 89 | |||
1828 | 90 | # Two queries get executed when listifying | ||
1829 | 91 | # TranslatedLanguageMixin.pofiles: a len() does a count, and | ||
1830 | 92 | # then all POTemplates and POFiles are fetched with the other. | ||
1831 | 93 | self.assertStatementCount(2, list, translated_language.pofiles) | ||
1832 | 94 | |||
1833 | 95 | def test_pofiles_two_templates(self): | ||
1834 | 96 | translated_language = self.getTranslatedLanguage(self.language) | ||
1835 | 97 | # Two templates with different priorities so they get sorted | ||
1836 | 98 | # appropriately. | ||
1837 | 99 | potemplate1 = self.addPOTemplate(priority=2) | ||
1838 | 100 | pofile1 = self.factory.makePOFile(self.language.code, potemplate1) | ||
1839 | 101 | potemplate2 = self.addPOTemplate(priority=1) | ||
1840 | 102 | pofile2 = self.factory.makePOFile(self.language.code, potemplate2) | ||
1841 | 103 | self.assertEqual([pofile1, pofile2], | ||
1842 | 104 | list(translated_language.pofiles)) | ||
1843 | 105 | |||
1844 | 106 | # Two queries get executed when listifying | ||
1845 | 107 | # TranslatedLanguageMixin.pofiles: a len() does a count, and | ||
1846 | 108 | # then all POTemplates and POFiles are fetched with the other. | ||
1847 | 109 | self.assertStatementCount(2, list, translated_language.pofiles) | ||
1848 | 110 | |||
1849 | 111 | def test_pofiles_two_templates_one_dummy(self): | ||
1850 | 112 | translated_language = self.getTranslatedLanguage(self.language) | ||
1851 | 113 | # Two templates with different priorities so they get sorted | ||
1852 | 114 | # appropriately. | ||
1853 | 115 | potemplate1 = self.addPOTemplate(priority=2) | ||
1854 | 116 | pofile1 = self.factory.makePOFile(self.language.code, potemplate1) | ||
1855 | 117 | potemplate2 = self.addPOTemplate(priority=1) | ||
1856 | 118 | pofiles = translated_language.pofiles | ||
1857 | 119 | self.assertEqual(pofile1, pofiles[0]) | ||
1858 | 120 | dummy_pofile = removeSecurityProxy(pofiles[1]) | ||
1859 | 121 | self.assertEqual(DummyPOFile, type(dummy_pofile)) | ||
1860 | 122 | |||
1861 | 123 | # Two queries get executed when listifying | ||
1862 | 124 | # TranslatedLanguageMixin.pofiles: a len() does a count, and | ||
1863 | 125 | # then all POTemplates and POFiles are fetched with the other. | ||
1864 | 126 | self.assertStatementCount(2, list, translated_language.pofiles) | ||
1865 | 127 | |||
1866 | 128 | def test_pofiles_slicing(self): | ||
1867 | 129 | # Slicing still works, and always does the same constant number | ||
1868 | 130 | # of queries (1). | ||
1869 | 131 | translated_language = self.getTranslatedLanguage(self.language) | ||
1870 | 132 | # Three templates with different priorities so they get sorted | ||
1871 | 133 | # appropriately. | ||
1872 | 134 | potemplate1 = self.addPOTemplate(priority=2) | ||
1873 | 135 | pofile1 = self.factory.makePOFile(self.language.code, potemplate1) | ||
1874 | 136 | potemplate2 = self.addPOTemplate(priority=1) | ||
1875 | 137 | pofile2 = self.factory.makePOFile(self.language.code, potemplate2) | ||
1876 | 138 | potemplate3 = self.addPOTemplate(priority=0) | ||
1877 | 139 | |||
1878 | 140 | pofiles = translated_language.pofiles[0:2] | ||
1879 | 141 | self.assertEqual([pofile1, pofile2], list(pofiles)) | ||
1880 | 142 | |||
1881 | 143 | # Slicing executes only a single query. | ||
1882 | 144 | get_slice = lambda of, start, end: list(of[start:end]) | ||
1883 | 145 | self.assertStatementCount(1, get_slice, | ||
1884 | 146 | translated_language.pofiles, 1, 3) | ||
1885 | 147 | |||
1886 | 148 | def test_pofiles_slicing_dummies(self): | ||
1887 | 149 | # Slicing includes DummyPOFiles. | ||
1888 | 150 | translated_language = self.getTranslatedLanguage(self.language) | ||
1889 | 151 | # Three templates with different priorities so they get sorted | ||
1890 | 152 | # appropriately. | ||
1891 | 153 | potemplate1 = self.addPOTemplate(priority=2) | ||
1892 | 154 | pofile1 = self.factory.makePOFile(self.language.code, potemplate1) | ||
1893 | 155 | potemplate2 = self.addPOTemplate(priority=1) | ||
1894 | 156 | pofile2 = self.factory.makePOFile(self.language.code, potemplate2) | ||
1895 | 157 | potemplate3 = self.addPOTemplate(priority=0) | ||
1896 | 158 | |||
1897 | 159 | pofiles = translated_language.pofiles[1:3] | ||
1898 | 160 | self.assertEqual(pofile2, pofiles[0]) | ||
1899 | 161 | dummy_pofile = removeSecurityProxy(pofiles[1]) | ||
1900 | 162 | self.assertEqual(DummyPOFile, type(dummy_pofile)) | ||
1901 | 163 | |||
1902 | 164 | def test_statistics_empty(self): | ||
1903 | 165 | translated_language = self.getTranslatedLanguage(self.language) | ||
1904 | 166 | |||
1905 | 167 | expected = { | ||
1906 | 168 | 'total_count': 0, | ||
1907 | 169 | 'translated_count': 0, | ||
1908 | 170 | 'new_count': 0, | ||
1909 | 171 | 'changed_count': 0, | ||
1910 | 172 | 'unreviewed_count': 0, | ||
1911 | 173 | 'untranslated_count': 0, | ||
1912 | 174 | } | ||
1913 | 175 | self.assertEqual(expected, | ||
1914 | 176 | translated_language.translation_statistics) | ||
1915 | 177 | |||
1916 | 178 | def test_setCounts_statistics(self): | ||
1917 | 179 | translated_language = self.getTranslatedLanguage(self.language) | ||
1918 | 180 | |||
1919 | 181 | total = 5 | ||
1920 | 182 | translated = 4 | ||
1921 | 183 | new = 3 | ||
1922 | 184 | changed = 2 | ||
1923 | 185 | unreviewed = 1 | ||
1924 | 186 | untranslated = total - translated | ||
1925 | 187 | |||
1926 | 188 | translated_language.setCounts( | ||
1927 | 189 | total, translated, new, changed, unreviewed) | ||
1928 | 190 | |||
1929 | 191 | expected = { | ||
1930 | 192 | 'total_count': total, | ||
1931 | 193 | 'translated_count': translated, | ||
1932 | 194 | 'new_count': new, | ||
1933 | 195 | 'changed_count': changed, | ||
1934 | 196 | 'unreviewed_count': unreviewed, | ||
1935 | 197 | 'untranslated_count': untranslated, | ||
1936 | 198 | } | ||
1937 | 199 | self.assertEqual(expected, | ||
1938 | 200 | translated_language.translation_statistics) | ||
1939 | 201 | |||
1940 | 202 | def test_recalculateCounts_empty(self): | ||
1941 | 203 | translated_language = self.getTranslatedLanguage(self.language) | ||
1942 | 204 | |||
1943 | 205 | translated_language.recalculateCounts() | ||
1944 | 206 | |||
1945 | 207 | expected = { | ||
1946 | 208 | 'total_count': 0, | ||
1947 | 209 | 'translated_count': 0, | ||
1948 | 210 | 'new_count': 0, | ||
1949 | 211 | 'changed_count': 0, | ||
1950 | 212 | 'unreviewed_count': 0, | ||
1951 | 213 | 'untranslated_count': 0, | ||
1952 | 214 | } | ||
1953 | 215 | self.assertEqual(expected, | ||
1954 | 216 | translated_language.translation_statistics) | ||
1955 | 217 | |||
1956 | 218 | def test_recalculateCounts_total_one_pofile(self): | ||
1957 | 219 | translated_language = self.getTranslatedLanguage(self.language) | ||
1958 | 220 | potemplate = self.addPOTemplate(number_of_potmsgsets=5) | ||
1959 | 221 | pofile = self.factory.makePOFile(self.language.code, potemplate) | ||
1960 | 222 | |||
1961 | 223 | translated_language.recalculateCounts() | ||
1962 | 224 | self.assertEqual( | ||
1963 | 225 | 5, translated_language.translation_statistics['total_count']) | ||
1964 | 226 | |||
1965 | 227 | def test_recalculateCounts_total_two_pofiles(self): | ||
1966 | 228 | translated_language = self.getTranslatedLanguage(self.language) | ||
1967 | 229 | potemplate1 = self.addPOTemplate(number_of_potmsgsets=5) | ||
1968 | 230 | pofile1 = self.factory.makePOFile(self.language.code, potemplate1) | ||
1969 | 231 | potemplate2 = self.addPOTemplate(number_of_potmsgsets=3) | ||
1970 | 232 | pofile2 = self.factory.makePOFile(self.language.code, potemplate2) | ||
1971 | 233 | |||
1972 | 234 | translated_language.recalculateCounts() | ||
1973 | 235 | self.assertEqual( | ||
1974 | 236 | 5+3, translated_language.translation_statistics['total_count']) | ||
1975 | 237 | |||
1976 | 238 | def test_recalculateCounts_translated_one_pofile(self): | ||
1977 | 239 | translated_language = self.getTranslatedLanguage(self.language) | ||
1978 | 240 | potemplate = self.addPOTemplate(number_of_potmsgsets=5) | ||
1979 | 241 | pofile = self.factory.makePOFile(self.language.code, potemplate) | ||
1980 | 242 | naked_pofile = removeSecurityProxy(pofile) | ||
1981 | 243 | # translated count is current + rosetta | ||
1982 | 244 | naked_pofile.currentcount = 3 | ||
1983 | 245 | naked_pofile.rosettacount = 1 | ||
1984 | 246 | |||
1985 | 247 | translated_language.recalculateCounts() | ||
1986 | 248 | self.assertEqual( | ||
1987 | 249 | 4, translated_language.translation_statistics['translated_count']) | ||
1988 | 250 | |||
1989 | 251 | def test_recalculateCounts_translated_two_pofiles(self): | ||
1990 | 252 | translated_language = self.getTranslatedLanguage(self.language) | ||
1991 | 253 | potemplate1 = self.addPOTemplate(number_of_potmsgsets=5) | ||
1992 | 254 | pofile1 = self.factory.makePOFile(self.language.code, potemplate1) | ||
1993 | 255 | naked_pofile1 = removeSecurityProxy(pofile1) | ||
1994 | 256 | # translated count is current + rosetta | ||
1995 | 257 | naked_pofile1.currentcount = 3 | ||
1996 | 258 | naked_pofile1.rosettacount = 1 | ||
1997 | 259 | |||
1998 | 260 | potemplate2 = self.addPOTemplate(number_of_potmsgsets=3) | ||
1999 | 261 | pofile2 = self.factory.makePOFile(self.language.code, potemplate2) | ||
2000 | 262 | naked_pofile2 = removeSecurityProxy(pofile2) | ||
2001 | 263 | # translated count is current + rosetta | ||
2002 | 264 | naked_pofile2.currentcount = 1 | ||
2003 | 265 | naked_pofile2.rosettacount = 1 | ||
2004 | 266 | |||
2005 | 267 | translated_language.recalculateCounts() | ||
2006 | 268 | self.assertEqual( | ||
2007 | 269 | 6, translated_language.translation_statistics['translated_count']) | ||
2008 | 270 | |||
2009 | 271 | def test_recalculateCounts_changed_one_pofile(self): | ||
2010 | 272 | translated_language = self.getTranslatedLanguage(self.language) | ||
2011 | 273 | potemplate = self.addPOTemplate(number_of_potmsgsets=5) | ||
2012 | 274 | pofile = self.factory.makePOFile(self.language.code, potemplate) | ||
2013 | 275 | naked_pofile = removeSecurityProxy(pofile) | ||
2014 | 276 | # translated count is current + rosetta | ||
2015 | 277 | naked_pofile.updatescount = 3 | ||
2016 | 278 | |||
2017 | 279 | translated_language.recalculateCounts() | ||
2018 | 280 | self.assertEqual( | ||
2019 | 281 | 3, translated_language.translation_statistics['changed_count']) | ||
2020 | 282 | |||
2021 | 283 | def test_recalculateCounts_changed_two_pofiles(self): | ||
2022 | 284 | translated_language = self.getTranslatedLanguage(self.language) | ||
2023 | 285 | potemplate1 = self.addPOTemplate(number_of_potmsgsets=5) | ||
2024 | 286 | pofile1 = self.factory.makePOFile(self.language.code, potemplate1) | ||
2025 | 287 | naked_pofile1 = removeSecurityProxy(pofile1) | ||
2026 | 288 | naked_pofile1.updatescount = 3 | ||
2027 | 289 | |||
2028 | 290 | potemplate2 = self.addPOTemplate(number_of_potmsgsets=3) | ||
2029 | 291 | pofile2 = self.factory.makePOFile(self.language.code, potemplate2) | ||
2030 | 292 | naked_pofile2 = removeSecurityProxy(pofile2) | ||
2031 | 293 | naked_pofile2.updatescount = 1 | ||
2032 | 294 | |||
2033 | 295 | translated_language.recalculateCounts() | ||
2034 | 296 | self.assertEqual( | ||
2035 | 297 | 4, translated_language.translation_statistics['changed_count']) | ||
2036 | 298 | |||
2037 | 299 | def test_recalculateCounts_new_one_pofile(self): | ||
2038 | 300 | translated_language = self.getTranslatedLanguage(self.language) | ||
2039 | 301 | potemplate = self.addPOTemplate(number_of_potmsgsets=5) | ||
2040 | 302 | pofile = self.factory.makePOFile(self.language.code, potemplate) | ||
2041 | 303 | naked_pofile = removeSecurityProxy(pofile) | ||
2042 | 304 | # new count is rosetta - changed | ||
2043 | 305 | naked_pofile.rosettacount = 3 | ||
2044 | 306 | naked_pofile.updatescount = 1 | ||
2045 | 307 | |||
2046 | 308 | translated_language.recalculateCounts() | ||
2047 | 309 | self.assertEqual( | ||
2048 | 310 | 2, translated_language.translation_statistics['new_count']) | ||
2049 | 311 | |||
2050 | 312 | def test_recalculateCounts_new_two_pofiles(self): | ||
2051 | 313 | translated_language = self.getTranslatedLanguage(self.language) | ||
2052 | 314 | potemplate1 = self.addPOTemplate(number_of_potmsgsets=5) | ||
2053 | 315 | pofile1 = self.factory.makePOFile(self.language.code, potemplate1) | ||
2054 | 316 | naked_pofile1 = removeSecurityProxy(pofile1) | ||
2055 | 317 | # new count is rosetta - changed | ||
2056 | 318 | naked_pofile1.rosettacount = 3 | ||
2057 | 319 | naked_pofile1.updatescount = 1 | ||
2058 | 320 | |||
2059 | 321 | potemplate2 = self.addPOTemplate(number_of_potmsgsets=3) | ||
2060 | 322 | pofile2 = self.factory.makePOFile(self.language.code, potemplate2) | ||
2061 | 323 | naked_pofile2 = removeSecurityProxy(pofile2) | ||
2062 | 324 | # new count is rosetta - changed | ||
2063 | 325 | naked_pofile2.rosettacount = 2 | ||
2064 | 326 | naked_pofile2.updatescount = 1 | ||
2065 | 327 | |||
2066 | 328 | translated_language.recalculateCounts() | ||
2067 | 329 | self.assertEqual( | ||
2068 | 330 | 3, translated_language.translation_statistics['new_count']) | ||
2069 | 331 | |||
2070 | 332 | def test_recalculateCounts_unreviewed_one_pofile(self): | ||
2071 | 333 | translated_language = self.getTranslatedLanguage(self.language) | ||
2072 | 334 | potemplate = self.addPOTemplate(number_of_potmsgsets=5) | ||
2073 | 335 | pofile = self.factory.makePOFile(self.language.code, potemplate) | ||
2074 | 336 | naked_pofile = removeSecurityProxy(pofile) | ||
2075 | 337 | # translated count is current + rosetta | ||
2076 | 338 | naked_pofile.unreviewed_count = 3 | ||
2077 | 339 | |||
2078 | 340 | translated_language.recalculateCounts() | ||
2079 | 341 | self.assertEqual( | ||
2080 | 342 | 3, translated_language.translation_statistics['unreviewed_count']) | ||
2081 | 343 | |||
2082 | 344 | def test_recalculateCounts_unreviewed_two_pofiles(self): | ||
2083 | 345 | translated_language = self.getTranslatedLanguage(self.language) | ||
2084 | 346 | potemplate1 = self.addPOTemplate(number_of_potmsgsets=5) | ||
2085 | 347 | pofile1 = self.factory.makePOFile(self.language.code, potemplate1) | ||
2086 | 348 | naked_pofile1 = removeSecurityProxy(pofile1) | ||
2087 | 349 | naked_pofile1.unreviewed_count = 3 | ||
2088 | 350 | |||
2089 | 351 | potemplate2 = self.addPOTemplate(number_of_potmsgsets=3) | ||
2090 | 352 | pofile2 = self.factory.makePOFile(self.language.code, potemplate2) | ||
2091 | 353 | naked_pofile2 = removeSecurityProxy(pofile2) | ||
2092 | 354 | naked_pofile2.unreviewed_count = 1 | ||
2093 | 355 | |||
2094 | 356 | translated_language.recalculateCounts() | ||
2095 | 357 | self.assertEqual( | ||
2096 | 358 | 4, translated_language.translation_statistics['unreviewed_count']) | ||
2097 | 359 | |||
2098 | 360 | def test_recalculateCounts_one_pofile(self): | ||
2099 | 361 | translated_language = self.getTranslatedLanguage(self.language) | ||
2100 | 362 | potemplate = self.addPOTemplate(number_of_potmsgsets=5) | ||
2101 | 363 | pofile = self.factory.makePOFile(self.language.code, potemplate) | ||
2102 | 364 | naked_pofile = removeSecurityProxy(pofile) | ||
2103 | 365 | # translated count is current + rosetta | ||
2104 | 366 | naked_pofile.currentcount = 3 | ||
2105 | 367 | naked_pofile.rosettacount = 1 | ||
2106 | 368 | # Changed count is 'updatescount' on POFile. | ||
2107 | 369 | # It has to be lower or equal to currentcount. | ||
2108 | 370 | naked_pofile.updatescount = 1 | ||
2109 | 371 | # new is rosettacount-updatescount. | ||
2110 | 372 | naked_pofile.newcount = 0 | ||
2111 | 373 | naked_pofile.unreviewed_count = 3 | ||
2112 | 374 | |||
2113 | 375 | translated_language.recalculateCounts() | ||
2114 | 376 | |||
2115 | 377 | expected = { | ||
2116 | 378 | 'total_count': 5, | ||
2117 | 379 | 'translated_count': 4, | ||
2118 | 380 | 'new_count': 0, | ||
2119 | 381 | 'changed_count': 1, | ||
2120 | 382 | 'unreviewed_count': 3, | ||
2121 | 383 | 'untranslated_count': 1, | ||
2122 | 384 | } | ||
2123 | 385 | self.assertEqual(expected, | ||
2124 | 386 | translated_language.translation_statistics) | ||
2125 | 387 | |||
2126 | 388 | def test_recalculateCounts_two_pofiles(self): | ||
2127 | 389 | translated_language = self.getTranslatedLanguage(self.language) | ||
2128 | 390 | |||
2129 | 391 | # Set up one template with a single PO file. | ||
2130 | 392 | potemplate1 = self.addPOTemplate(number_of_potmsgsets=5) | ||
2131 | 393 | pofile1 = self.factory.makePOFile(self.language.code, potemplate1) | ||
2132 | 394 | naked_pofile1 = removeSecurityProxy(pofile1) | ||
2133 | 395 | # translated count is current + rosetta | ||
2134 | 396 | naked_pofile1.currentcount = 2 | ||
2135 | 397 | naked_pofile1.rosettacount = 2 | ||
2136 | 398 | # Changed count is 'updatescount' on POFile. | ||
2137 | 399 | # It has to be lower or equal to currentcount. | ||
2138 | 400 | # new is rosettacount-updatescount. | ||
2139 | 401 | naked_pofile1.updatescount = 1 | ||
2140 | 402 | naked_pofile1.unreviewed_count = 3 | ||
2141 | 403 | |||
2142 | 404 | # Set up second template with a single PO file. | ||
2143 | 405 | potemplate2 = self.addPOTemplate(number_of_potmsgsets=3) | ||
2144 | 406 | pofile2 = self.factory.makePOFile(self.language.code, potemplate2) | ||
2145 | 407 | naked_pofile2 = removeSecurityProxy(pofile2) | ||
2146 | 408 | # translated count is current + rosetta | ||
2147 | 409 | naked_pofile2.currentcount = 1 | ||
2148 | 410 | naked_pofile2.rosettacount = 2 | ||
2149 | 411 | # Changed count is 'updatescount' on POFile. | ||
2150 | 412 | # It has to be lower or equal to currentcount. | ||
2151 | 413 | # new is rosettacount-updatescount. | ||
2152 | 414 | naked_pofile2.updatescount = 1 | ||
2153 | 415 | naked_pofile2.unreviewed_count = 1 | ||
2154 | 416 | |||
2155 | 417 | translated_language.recalculateCounts() | ||
2156 | 418 | |||
2157 | 419 | expected = { | ||
2158 | 420 | 'total_count': 8, | ||
2159 | 421 | 'translated_count': 7, | ||
2160 | 422 | 'new_count': 2, | ||
2161 | 423 | 'changed_count': 2, | ||
2162 | 424 | 'unreviewed_count': 4, | ||
2163 | 425 | 'untranslated_count': 1, | ||
2164 | 426 | } | ||
2165 | 427 | self.assertEqual(expected, | ||
2166 | 428 | translated_language.translation_statistics) | ||
2167 | 429 | |||
2168 | 430 | def test_recalculateCounts_two_templates_one_translation(self): | ||
2169 | 431 | # Make sure recalculateCounts works even if a POFile is missing | ||
2170 | 432 | # for one of the templates. | ||
2171 | 433 | translated_language = self.getTranslatedLanguage(self.language) | ||
2172 | 434 | |||
2173 | 435 | # Set up one template with a single PO file. | ||
2174 | 436 | potemplate1 = self.addPOTemplate(number_of_potmsgsets=5) | ||
2175 | 437 | pofile1 = self.factory.makePOFile(self.language.code, potemplate1) | ||
2176 | 438 | naked_pofile1 = removeSecurityProxy(pofile1) | ||
2177 | 439 | # translated count is current + rosetta | ||
2178 | 440 | naked_pofile1.currentcount = 2 | ||
2179 | 441 | naked_pofile1.rosettacount = 2 | ||
2180 | 442 | # Changed count is 'updatescount' on POFile. | ||
2181 | 443 | # It has to be lower or equal to currentcount. | ||
2182 | 444 | # new is rosettacount-updatescount. | ||
2183 | 445 | naked_pofile1.updatescount = 1 | ||
2184 | 446 | naked_pofile1.unreviewed_count = 3 | ||
2185 | 447 | |||
2186 | 448 | # Set up second template with a single PO file. | ||
2187 | 449 | potemplate2 = self.addPOTemplate(number_of_potmsgsets=3) | ||
2188 | 450 | |||
2189 | 451 | translated_language.recalculateCounts() | ||
2190 | 452 | |||
2191 | 453 | expected = { | ||
2192 | 454 | 'total_count': 8, | ||
2193 | 455 | 'translated_count': 4, | ||
2194 | 456 | 'new_count': 1, | ||
2195 | 457 | 'changed_count': 1, | ||
2196 | 458 | 'unreviewed_count': 3, | ||
2197 | 459 | 'untranslated_count': 4, | ||
2198 | 460 | } | ||
2199 | 461 | self.assertEqual(expected, | ||
2200 | 462 | translated_language.translation_statistics) | ||
2201 | 0 | 463 | ||
2202 | === modified file 'lib/lp/translations/tests/test_translationtemplatescollection.py' | |||
2203 | --- lib/lp/translations/tests/test_translationtemplatescollection.py 2010-07-17 16:19:38 +0000 | |||
2204 | +++ lib/lp/translations/tests/test_translationtemplatescollection.py 2010-07-23 12:49:25 +0000 | |||
2205 | @@ -205,3 +205,22 @@ | |||
2206 | 205 | ] | 205 | ] |
2207 | 206 | self.assertContentEqual( | 206 | self.assertContentEqual( |
2208 | 207 | expected_outcome, joined.select(POTemplate, POFile)) | 207 | expected_outcome, joined.select(POTemplate, POFile)) |
2209 | 208 | |||
2210 | 209 | def test_joinOuterPOFile_language(self): | ||
2211 | 210 | trunk = self.factory.makeProduct().getSeries('trunk') | ||
2212 | 211 | translated_template = self.factory.makePOTemplate(productseries=trunk) | ||
2213 | 212 | untranslated_template = self.factory.makePOTemplate( | ||
2214 | 213 | productseries=trunk) | ||
2215 | 214 | nl = translated_template.newPOFile('nl') | ||
2216 | 215 | de = translated_template.newPOFile('de') | ||
2217 | 216 | |||
2218 | 217 | collection = TranslationTemplatesCollection() | ||
2219 | 218 | by_series = collection.restrictProductSeries(trunk) | ||
2220 | 219 | joined = by_series.joinOuterPOFile(language=nl.language) | ||
2221 | 220 | |||
2222 | 221 | expected_outcome = [ | ||
2223 | 222 | (translated_template, nl), | ||
2224 | 223 | (untranslated_template, None), | ||
2225 | 224 | ] | ||
2226 | 225 | self.assertContentEqual( | ||
2227 | 226 | expected_outcome, joined.select(POTemplate, POFile)) | ||
2228 | 208 | 227 | ||
2229 | === modified file 'utilities/make-lp-user' | |||
2230 | --- utilities/make-lp-user 2010-04-27 19:48:39 +0000 | |||
2231 | +++ utilities/make-lp-user 2010-07-23 12:49:25 +0000 | |||
2232 | @@ -46,7 +46,6 @@ | |||
2233 | 46 | from canonical.launchpad.interfaces import ( | 46 | from canonical.launchpad.interfaces import ( |
2234 | 47 | IPersonSet, | 47 | IPersonSet, |
2235 | 48 | ISSHKeySet, | 48 | ISSHKeySet, |
2236 | 49 | SSHKeyType, | ||
2237 | 50 | TeamMembershipStatus, | 49 | TeamMembershipStatus, |
2238 | 51 | ) | 50 | ) |
2239 | 52 | from canonical.launchpad.interfaces.gpghandler import IGPGHandler | 51 | from canonical.launchpad.interfaces.gpghandler import IGPGHandler |
2240 | @@ -111,8 +110,8 @@ | |||
2241 | 111 | ssh_dir = os.path.expanduser('~/.ssh') | 110 | ssh_dir = os.path.expanduser('~/.ssh') |
2242 | 112 | key_set = getUtility(ISSHKeySet) | 111 | key_set = getUtility(ISSHKeySet) |
2243 | 113 | key_guesses = [ | 112 | key_guesses = [ |
2246 | 114 | (SSHKeyType.RSA, 'id_rsa.pub'), | 113 | ("ssh-rsa", 'id_rsa.pub'), |
2247 | 115 | (SSHKeyType.DSA, 'id_dsa.pub'), | 114 | ("ssh-dsa", 'id_dsa.pub'), |
2248 | 116 | ] | 115 | ] |
2249 | 117 | for key_type, guessed_filename in key_guesses: | 116 | for key_type, guessed_filename in key_guesses: |
2250 | 118 | guessed_filename = os.path.join(ssh_dir, guessed_filename) | 117 | guessed_filename = os.path.join(ssh_dir, guessed_filename) |
2251 | @@ -125,7 +124,8 @@ | |||
2252 | 125 | except (OSError, IOError): | 124 | except (OSError, IOError): |
2253 | 126 | continue | 125 | continue |
2254 | 127 | public_key = public_key.split()[1] | 126 | public_key = public_key.split()[1] |
2256 | 128 | key_set.new(person, key_type, public_key, 'Added by utility script.') | 127 | key_set.new(person, |
2257 | 128 | "%s %s %s" % (key_type, public_key, 'Added by utility script.')) | ||
2258 | 129 | print 'Registered SSH key: %s' % (guessed_filename,) | 129 | print 'Registered SSH key: %s' % (guessed_filename,) |
2259 | 130 | 130 | ||
2260 | 131 | 131 |
Was this really meant to be proposed for db-devel? I'm recognizing changes from other people's branches that I reviewed this week in the diff.