Merge lp:~wgrant/launchpad/gina-3.0-support into lp:launchpad/db-devel

Proposed by William Grant
Status: Merged
Approved by: Julian Edwards
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~wgrant/launchpad/gina-3.0-support
Merge into: lp:launchpad/db-devel
Prerequisite: lp:~wgrant/launchpad/distroseries-source-format-selection
Diff against target: 1838 lines (+1124/-159)
25 files modified
lib/canonical/launchpad/fields/__init__.py (+61/-1)
lib/canonical/launchpad/helpers.py (+1/-64)
lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js (+257/-0)
lib/lp/archiveuploader/tests/test_utils.py (+18/-0)
lib/lp/archiveuploader/utils.py (+14/-0)
lib/lp/bugs/browser/bugtarget.py (+28/-0)
lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt (+22/-0)
lib/lp/bugs/stories/bugattachments/20-edit-bug-attachment.txt (+38/-0)
lib/lp/bugs/templates/bug-portlet-attachments.pt (+49/-0)
lib/lp/bugs/templates/bugtarget-filebug-search.pt (+45/-0)
lib/lp/bugs/templates/bugtasks-and-nominations-table.pt (+47/-0)
lib/lp/code/browser/branch.py (+8/-2)
lib/lp/code/templates/branchmergeproposal-index.pt (+25/-0)
lib/lp/registry/browser/distroseries.py (+56/-24)
lib/lp/registry/browser/tests/packaging-views.txt (+58/-0)
lib/lp/registry/interfaces/person.py (+6/-0)
lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt (+98/-48)
lib/lp/registry/tests/test_productseries_vocabularies.py (+75/-0)
lib/lp/soyuz/doc/gina.txt (+11/-11)
lib/lp/soyuz/scripts/gina/handlers.py (+44/-7)
lib/lp/soyuz/scripts/tests/gina_test_archive/dists/breezy/main/source/Sources (+17/-0)
lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0-1.dsc (+22/-0)
lib/lp/soyuz/scripts/tests/test_gina.py (+15/-0)
lib/lp/translations/stories/distroseries/xx-distroseries-templates.txt (+65/-2)
lib/lp/translations/templates/object-templates.pt (+44/-0)
To merge this branch: bzr merge lp:~wgrant/launchpad/gina-3.0-support
Reviewer Review Type Date Requested Status
Данило Шеган (community) release-critical Approve
Julian Edwards (community) Approve
Review via email: mp+15478@code.launchpad.net
To post a comment you must log in.
Revision history for this message
William Grant (wgrant) wrote :

gina needs to support 3.0 (quilt) and 3.0 (native) sources. To do this, it just needs to identify files as the correct types. Since archiveuploader already has a function to do this, I've altered gina to use that rather than the duplicated canonical.launchpad.helpers.getFileType. As gina was the final callsite of getFileType, I've removed that.

Since determine_source_file_type only does source files, I've added determine_binary_file_type. It could possibly go along with determine_source_file_type in lp.archiveuploader.utils, but I'm not quite sure.

For testing, I've added a 3.0 (quilt) source to the test archive.

Revision history for this message
Julian Edwards (julian-edwards) wrote :

 review approve
 merge approve

> gina needs to support 3.0 (quilt) and 3.0 (native) sources. To do this, it
> just needs to identify files as the correct types. Since archiveuploader
> already has a function to do this, I've altered gina to use that rather
> than the duplicated canonical.launchpad.helpers.getFileType. As gina was
> the final callsite of getFileType, I've removed that.
>
> Since determine_source_file_type only does source files, I've added
> determine_binary_file_type. It could possibly go along with
> determine_source_file_type in lp.archiveuploader.utils, but I'm not quite
> sure.

I think it should go there to be consistent.

>
> For testing, I've added a 3.0 (quilt) source to the test archive.
>

Great, thanks for making this change William! It looks good to me so I
approved it. There's just a couple of minor comments inline.

[snip]

>=== modified file 'lib/lp/soyuz/scripts/gina/handlers.py'
>--- lib/lp/soyuz/scripts/gina/handlers.py 2009-11-07 09:50:37 +0000
>+++ lib/lp/soyuz/scripts/gina/handlers.py 2009-12-01 10:10:48 +0000
>@@ -29,6 +29,7 @@
>
> from lp.archivepublisher.diskpool import poolify
> from lp.archiveuploader.tagfiles import parse_tagfile
>+from lp.archiveuploader.utils import determine_source_file_type
>
> from canonical.database.sqlbase import sqlvalues
>
>@@ -46,10 +47,22 @@
>
> from lp.registry.interfaces.person import IPersonSet,
> PersonCreationRationale from lp.registry.interfaces.sourcepackage import
> SourcePackageType +from lp.soyuz.interfaces.binarypackagerelease import
> BinaryPackageFileType from lp.soyuz.interfaces.binarypackagename import
> IBinaryPackageNameSet from lp.soyuz.interfaces.build import BuildStatus
> from lp.soyuz.interfaces.publishing import PackagePublishingStatus
>-from canonical.launchpad.helpers import getFileType, getBinaryPackageFormat
>+from canonical.launchpad.helpers import getBinaryPackageFormat

I think Gina is the only thing using getBinaryPackageFormat - it might be
worth de-cluttering helpers.py and moving it to the gina module.

>+
>+
>+def determine_binary_file_type(filename):
>+ """Determine the BinaryPackageFileType of the given filename."""
>+
>+ if filename.endswith(".deb"):
>+ return BinaryPackageFileType.DEB
>+ elif filename.endswith(".udeb"):
>+ return BinaryPackageFileType.DEB

This should be UDEB I think?

Everything else looks good! Remind me to land this and the other branch when
PQM opens.

Cheers
J

review: Approve
Revision history for this message
William Grant (wgrant) wrote :

Hi Julian,

Thanks for the review.

On Tue, 2009-12-01 at 16:33 +0000, Julian Edwards wrote:
> Review: Approve
> review approve
> merge approve
>
> > gina needs to support 3.0 (quilt) and 3.0 (native) sources. To do this, it
> > just needs to identify files as the correct types. Since archiveuploader
> > already has a function to do this, I've altered gina to use that rather
> > than the duplicated canonical.launchpad.helpers.getFileType. As gina was
> > the final callsite of getFileType, I've removed that.
> >
> > Since determine_source_file_type only does source files, I've added
> > determine_binary_file_type. It could possibly go along with
> > determine_source_file_type in lp.archiveuploader.utils, but I'm not quite
> > sure.
>
> I think it should go there to be consistent.

Moved.

> >
> > For testing, I've added a 3.0 (quilt) source to the test archive.
> >
>
> Great, thanks for making this change William! It looks good to me so I
> approved it. There's just a couple of minor comments inline.
>
> [snip]
>
> >=== modified file 'lib/lp/soyuz/scripts/gina/handlers.py'
> >--- lib/lp/soyuz/scripts/gina/handlers.py 2009-11-07 09:50:37 +0000
> >+++ lib/lp/soyuz/scripts/gina/handlers.py 2009-12-01 10:10:48 +0000
> >@@ -29,6 +29,7 @@
> >
> > from lp.archivepublisher.diskpool import poolify
> > from lp.archiveuploader.tagfiles import parse_tagfile
> >+from lp.archiveuploader.utils import determine_source_file_type
> >
> > from canonical.database.sqlbase import sqlvalues
> >
> >@@ -46,10 +47,22 @@
> >
> > from lp.registry.interfaces.person import IPersonSet,
> > PersonCreationRationale from lp.registry.interfaces.sourcepackage import
> > SourcePackageType +from lp.soyuz.interfaces.binarypackagerelease import
> > BinaryPackageFileType from lp.soyuz.interfaces.binarypackagename import
> > IBinaryPackageNameSet from lp.soyuz.interfaces.build import BuildStatus
> > from lp.soyuz.interfaces.publishing import PackagePublishingStatus
> >-from canonical.launchpad.helpers import getFileType, getBinaryPackageFormat
> >+from canonical.launchpad.helpers import getBinaryPackageFormat
>
> I think Gina is the only thing using getBinaryPackageFormat - it might be
> worth de-cluttering helpers.py and moving it to the gina module.

Done. I also removed getBinaryPackageExtension, as there were no
callsites.

> >+
> >+
> >+def determine_binary_file_type(filename):
> >+ """Determine the BinaryPackageFileType of the given filename."""
> >+
> >+ if filename.endswith(".deb"):
> >+ return BinaryPackageFileType.DEB
> >+ elif filename.endswith(".udeb"):
> >+ return BinaryPackageFileType.DEB
>
> This should be UDEB I think?

Oops, yes. I've added a test for determine_binary_file_type.

> Everything else looks good! Remind me to land this and the other branch when
> PQM opens.

Both are still blocked on an updated buildbot AMI, but that should
appear on Monday.

The diff on LP is now insanely huge (it looks like it might not have
taken into account the prerequisite branch), but the correct
intermediate diff is attached.

1=== modified file 'lib/canonical/launchpad/helpers.py'
2--- lib/canonical/launchpad/helpers.py 2009-11-25 10:26:03 +0000
3+++ lib/canonical/launchpad/helpers.py 2009-12-10 12:14:13 +0000
4@@ -26,8 +26,7 @@
5
6 import canonical
7 from canonical.launchpad.interfaces import (
8- BinaryPackageFormat, ILaunchBag, IRequestPreferredLanguages,
9- IRequestLocalLanguages)
10+ ILaunchBag, IRequestPreferredLanguages, IRequestLocalLanguages)
11
12
13 # pylint: disable-msg=W0102
14@@ -466,52 +465,6 @@
15 long(sha.new(message_id).hexdigest(), 16), 62))
16
17
18-BINARYPACKAGE_EXTENSIONS = {
19- BinaryPackageFormat.DEB: '.deb',
20- BinaryPackageFormat.UDEB: '.udeb',
21- BinaryPackageFormat.RPM: '.rpm'}
22-
23-
24-class UnrecognizedBinaryFormat(Exception):
25-
26- def __init__(self, fname, *args):
27- Exception.__init__(self, *args)
28- self.fname = fname
29-
30- def __str__(self):
31- return '%s is not recognized as a binary file.' % self.fname
32-
33-
34-def getBinaryPackageFormat(fname):
35- """Return the BinaryPackageFormat for the given filename.
36-
37- >>> getBinaryPackageFormat('mozilla-firefox_0.9_i386.deb').name
38- 'DEB'
39- >>> getBinaryPackageFormat('debian-installer.9_all.udeb').name
40- 'UDEB'
41- >>> getBinaryPackageFormat('network-manager.9_i386.rpm').name
42- 'RPM'
43- """
44- for key, value in BINARYPACKAGE_EXTENSIONS.items():
45- if fname.endswith(value):
46- return key
47-
48- raise UnrecognizedBinaryFormat(fname)
49-
50-
51-def getBinaryPackageExtension(format):
52- """Return the file extension for the given BinaryPackageFormat.
53-
54- >>> getBinaryPackageExtension(BinaryPackageFormat.DEB)
55- '.deb'
56- >>> getBinaryPackageExtension(BinaryPackageFormat.UDEB)
57- '.udeb'
58- >>> getBinaryPackageExtension(BinaryPackageFormat.RPM)
59- '.rpm'
60- """
61- return BINARYPACKAGE_EXTENSIONS[format]
62-
63-
64 def intOrZero(value):
65 """Return int(value) or 0 if the conversion fails.
66
67
68=== modified file 'lib/lp/archiveuploader/tests/test_utils.py'
69--- lib/lp/archiveuploader/tests/test_utils.py 2009-11-18 02:58:23 +0000
70+++ lib/lp/archiveuploader/tests/test_utils.py 2009-12-10 13:32:27 +0000
71@@ -10,6 +10,7 @@
72 import shutil
73
74 from lp.registry.interfaces.sourcepackage import SourcePackageFileType
75+from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFileType
76 from lp.archiveuploader.tests import datadir
77
78
79@@ -72,6 +73,23 @@
80 self.assertEquals(None, determine_source_file_type('foo_1.0'))
81 self.assertEquals(None, determine_source_file_type('foo_1.0.blah.gz'))
82
83+ def test_determine_binary_file_type(self):
84+ """lp.archiveuploader.utils.determine_binary_file_type should work."""
85+ from lp.archiveuploader.utils import determine_binary_file_type
86+
87+ # .deb -> DEB
88+ self.assertEquals(
89+ determine_binary_file_type('foo_1.0-1_all.deb'),
90+ BinaryPackageFileType.DEB)
91+
92+ # .udeb -> UDEB
93+ self.assertEquals(
94+ determine_binary_file_type('foo_1.0-1_all.udeb'),
95+ BinaryPackageFileType.UDEB)
96+
97+ self.assertEquals(determine_binary_file_type('foo_1.0'), None)
98+ self.assertEquals(determine_binary_file_type('foo_1.0.notdeb'), None)
99+
100 def testPrefixMultilineString(self):
101 """lp.archiveuploader.utils.prefix_multi_line_string should work"""
102 from lp.archiveuploader.utils import prefix_multi_line_string
103
104=== modified file 'lib/lp/archiveuploader/utils.py'
105--- lib/lp/archiveuploader/utils.py 2009-11-14 02:59:34 +0000
106+++ lib/lp/archiveuploader/utils.py 2009-12-10 13:34:56 +0000
107@@ -17,6 +17,7 @@
108 're_changes_file_name',
109 're_extract_src_version',
110 'get_source_file_extension',
111+ 'determine_binary_file_type',
112 'determine_source_file_type',
113 'prefix_multi_line_string',
114 'safe_fix_maintainer',
115@@ -88,6 +89,19 @@
116 return None
117
118
119+def determine_binary_file_type(filename):
120+ """Determine the BinaryPackageFileType of the given filename."""
121+ # Avoid circular imports.
122+ from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFileType
123+
124+ if filename.endswith(".deb"):
125+ return BinaryPackageFileType.DEB
126+ elif filename.endswith(".udeb"):
127+ return BinaryPackageFileType.UDEB
128+ else:
129+ return None
130+
131+
132 def prefix_multi_line_string(str, prefix, include_blank_lines=0):
133 """Utility function to split an input string and prefix,
134
135
136=== modified file 'lib/lp/soyuz/scripts/gina/handlers.py'
137--- lib/lp/soyuz/scripts/gina/handlers.py 2009-11-25 10:26:03 +0000
138+++ lib/lp/soyuz/scripts/gina/handlers.py 2009-12-10 12:15:49 +0000
139@@ -29,7 +29,8 @@
140
141 from lp.archivepublisher.diskpool import poolify
142 from lp.archiveuploader.tagfiles import parse_tagfile
143-from lp.archiveuploader.utils import determine_source_file_type
144+from lp.archiveuploader.utils import (determine_binary_file_type,
145+ determine_source_file_type)
146
147 from canonical.database.sqlbase import sqlvalues
148
149@@ -47,22 +48,10 @@
150
151 from lp.registry.interfaces.person import IPersonSet, PersonCreationRationale
152 from lp.registry.interfaces.sourcepackage import SourcePackageType
153-from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFileType
154 from lp.soyuz.interfaces.binarypackagename import IBinaryPackageNameSet
155+from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFormat
156 from lp.soyuz.interfaces.build import BuildStatus
157 from lp.soyuz.interfaces.publishing import PackagePublishingStatus
158-from canonical.launchpad.helpers import getBinaryPackageFormat
159-
160-
161-def determine_binary_file_type(filename):
162- """Determine the BinaryPackageFileType of the given filename."""
163-
164- if filename.endswith(".deb"):
165- return BinaryPackageFileType.DEB
166- elif filename.endswith(".udeb"):
167- return BinaryPackageFileType.DEB
168- else:
169- return None
170
171
172 def check_not_in_librarian(files, archive_root, directory):
173@@ -91,6 +80,39 @@
174 return to_upload
175
176
177+BINARYPACKAGE_EXTENSIONS = {
178+ BinaryPackageFormat.DEB: '.deb',
179+ BinaryPackageFormat.UDEB: '.udeb',
180+ BinaryPackageFormat.RPM: '.rpm'}
181+
182+
183+class UnrecognizedBinaryFormat(Exception):
184+
185+ def __init__(self, fname, *args):
186+ Exception.__init__(self, *args)
187+ self.fname = fname
188+
189+ def __str__(self):
190+ return '%s is not recognized as a binary file.' % self.fname
191+
192+
193+def getBinaryPackageFormat(fname):
194+ """Return the BinaryPackageFormat for the given filename.
195+
196+ >>> getBinaryPackageFormat('mozilla-firefox_0.9_i386.deb').name
197+ 'DEB'
198+ >>> getBinaryPackageFormat('debian-installer.9_all.udeb').name
199+ 'UDEB'
200+ >>> getBinaryPackageFormat('network-manager.9_i386.rpm').name
201+ 'RPM'
202+ """
203+ for key, value in BINARYPACKAGE_EXTENSIONS.items():
204+ if fname.endswith(value):
205+ return key
206+
207+ raise UnrecognizedBinaryFormat(fname)
208+
209+
210 class DataSetupError(Exception):
211 """Raised when required data is found to be missing in the database"""
212
213
214=== added file 'lib/lp/soyuz/scripts/tests/test_gina.py'
215--- lib/lp/soyuz/scripts/tests/test_gina.py 1970-01-01 00:00:00 +0000
216+++ lib/lp/soyuz/scripts/tests/test_gina.py 2009-12-10 12:19:14 +0000
217@@ -0,0 +1,15 @@
218+# Copyright 2009 Canonical Ltd. This software is licensed under the
219+# GNU Affero General Public License version 3 (see the file LICENSE).
220+
221+import unittest
222+
223+from zope.testing.doctest import DocTestSuite
224+
225+import lp.soyuz.scripts.gina.handlers
226+
227+
228+def test_suite():
229+ suite = unittest.TestSuite()
230+ suite.addTest(DocTestSuite(lp.soyuz.scripts.gina.handlers))
231+ return suite
232+
Revision history for this message
Данило Шеган (danilo) wrote :

Provided dogfood testing proves OK, looks good. Though, I'd prefer refactoring to be left for when we are outside RC mode.

review: Approve (release-critical)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/fields/__init__.py'
2--- lib/canonical/launchpad/fields/__init__.py 2009-12-09 23:11:31 +0000
3+++ lib/canonical/launchpad/fields/__init__.py 2009-12-14 08:43:38 +0000
4@@ -760,6 +760,14 @@
5 """Return True if the person is public."""
6 from canonical.launchpad.interfaces import IPerson, PersonVisibility
7 if not IPerson.providedBy(person):
8+<<<<<<< TREE
9+=======
10+ return False
11+ if person.visibility == PersonVisibility.PUBLIC:
12+ return True
13+ else:
14+ # PRIVATE_MEMBERSHIP or PRIVATE.
15+>>>>>>> MERGE-SOURCE
16 return False
17 return person.visibility == PersonVisibility.PUBLIC
18
19@@ -768,7 +776,17 @@
20 """True if the person/team has private membership visibility."""
21 from canonical.launchpad.interfaces import IPerson, PersonVisibility
22 if not IPerson.providedBy(person):
23- return False
24+<<<<<<< TREE
25+=======
26+ return False
27+ if person.visibility == PersonVisibility.PRIVATE_MEMBERSHIP:
28+ # PRIVATE_MEMBERSHIP.
29+ return True
30+ else:
31+ # PUBLIC or PRIVATE.
32+>>>>>>> MERGE-SOURCE
33+ return False
34+<<<<<<< TREE
35 return person.visibility == PersonVisibility.PRIVATE_MEMBERSHIP
36
37
38@@ -791,9 +809,33 @@
39
40
41 class PublicPersonChoice(PersonChoice):
42+=======
43+
44+
45+class PrivateTeamNotAllowed(ConstraintNotSatisfied):
46+ __doc__ = _("A private team is not allowed.")
47+
48+
49+class PrivateMembershipTeamNotAllowed(ConstraintNotSatisfied):
50+ __doc__ = _("A private-membership team is not allowed.")
51+
52+
53+class PersonChoice(Choice):
54+ """A person or team.
55+
56+ This is useful as a superclass and provides a clearer error message than
57+ "Constraint not satisfied".
58+ """
59+ implements(IReferenceChoice)
60+ schema = IObject # Will be set to IPerson once IPerson is defined.
61+
62+
63+class PublicPersonChoice(PersonChoice):
64+>>>>>>> MERGE-SOURCE
65 """A person or team who is public."""
66
67 def constraint(self, value):
68+<<<<<<< TREE
69 if is_public_person(value):
70 return True
71 else:
72@@ -802,6 +844,16 @@
73
74
75 class ParticipatingPersonChoice(PersonChoice):
76+=======
77+ if is_valid_public_person(value):
78+ return True
79+ else:
80+ # The vocabulary prevents the revealing of private team names.
81+ raise PrivateTeamNotAllowed(value)
82+
83+
84+class ParticipatingPersonChoice(PersonChoice):
85+>>>>>>> MERGE-SOURCE
86 """A person or team who is not a private membership team.
87
88 A person can participate in all contexts. A PRIVATE team can participate
89@@ -811,8 +863,16 @@
90 """
91
92 def constraint(self, value):
93+<<<<<<< TREE
94 if not is_private_membership_person(value):
95 return True
96 else:
97 # The vocabulary prevents the revealing of private team names.
98 raise PrivateMembershipTeamNotAllowed(value)
99+=======
100+ if not is_private_membership(value):
101+ return True
102+ else:
103+ # The vocabulary prevents the revealing of private team names.
104+ raise PrivateMembershipTeamNotAllowed(value)
105+>>>>>>> MERGE-SOURCE
106
107=== modified file 'lib/canonical/launchpad/helpers.py'
108--- lib/canonical/launchpad/helpers.py 2009-11-14 02:55:15 +0000
109+++ lib/canonical/launchpad/helpers.py 2009-12-14 08:43:38 +0000
110@@ -26,9 +26,7 @@
111
112 import canonical
113 from canonical.launchpad.interfaces import (
114- BinaryPackageFormat, BinaryPackageFileType, ILaunchBag,
115- IRequestPreferredLanguages, IRequestLocalLanguages,
116- SourcePackageFileType)
117+ ILaunchBag, IRequestPreferredLanguages, IRequestLocalLanguages)
118
119
120 # pylint: disable-msg=W0102
121@@ -467,67 +465,6 @@
122 long(sha.new(message_id).hexdigest(), 16), 62))
123
124
125-def getFileType(fname):
126- if fname.endswith(".deb"):
127- return BinaryPackageFileType.DEB
128- if fname.endswith(".udeb"):
129- return BinaryPackageFileType.DEB
130- if fname.endswith(".dsc"):
131- return SourcePackageFileType.DSC
132- if fname.endswith(".diff.gz"):
133- return SourcePackageFileType.DIFF
134- if fname.endswith(".orig.tar.gz"):
135- return SourcePackageFileType.ORIG_TARBALL
136- if fname.endswith(".tar.gz"):
137- return SourcePackageFileType.NATIVE_TARBALL
138-
139-
140-BINARYPACKAGE_EXTENSIONS = {
141- BinaryPackageFormat.DEB: '.deb',
142- BinaryPackageFormat.UDEB: '.udeb',
143- BinaryPackageFormat.RPM: '.rpm'}
144-
145-
146-class UnrecognizedBinaryFormat(Exception):
147-
148- def __init__(self, fname, *args):
149- Exception.__init__(self, *args)
150- self.fname = fname
151-
152- def __str__(self):
153- return '%s is not recognized as a binary file.' % self.fname
154-
155-
156-def getBinaryPackageFormat(fname):
157- """Return the BinaryPackageFormat for the given filename.
158-
159- >>> getBinaryPackageFormat('mozilla-firefox_0.9_i386.deb').name
160- 'DEB'
161- >>> getBinaryPackageFormat('debian-installer.9_all.udeb').name
162- 'UDEB'
163- >>> getBinaryPackageFormat('network-manager.9_i386.rpm').name
164- 'RPM'
165- """
166- for key, value in BINARYPACKAGE_EXTENSIONS.items():
167- if fname.endswith(value):
168- return key
169-
170- raise UnrecognizedBinaryFormat(fname)
171-
172-
173-def getBinaryPackageExtension(format):
174- """Return the file extension for the given BinaryPackageFormat.
175-
176- >>> getBinaryPackageExtension(BinaryPackageFormat.DEB)
177- '.deb'
178- >>> getBinaryPackageExtension(BinaryPackageFormat.UDEB)
179- '.udeb'
180- >>> getBinaryPackageExtension(BinaryPackageFormat.RPM)
181- '.rpm'
182- """
183- return BINARYPACKAGE_EXTENSIONS[format]
184-
185-
186 def intOrZero(value):
187 """Return int(value) or 0 if the conversion fails.
188
189
190=== modified file 'lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js'
191--- lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js 2009-12-11 11:09:35 +0000
192+++ lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js 2009-12-14 08:43:37 +0000
193@@ -25,6 +25,7 @@
194 * The NodeList of possible duplicates.
195 */
196 var bug_already_reported_expanders;
197+<<<<<<< TREE
198 /*
199 * The search field on the +filebug form
200 */
201@@ -45,6 +46,16 @@
202 * The URL of the inline +filebug form.
203 */
204 var filebug_form_url;
205+=======
206+/*
207+ * The search field on the +filebug form
208+ */
209+var search_field;
210+/*
211+ * The search button on the +filebug form
212+ */
213+var search_button;
214+>>>>>>> MERGE-SOURCE
215 /*
216 * The boilerplate elements for the do-you-want-to-subscribe
217 * FormOverlay.
218@@ -119,6 +130,7 @@
219 }
220
221 /**
222+<<<<<<< TREE
223 * Search for bugs that may match the text that the user has entered and
224 * display them in-line.
225 */
226@@ -203,6 +215,93 @@
227 }
228
229 /*
230+=======
231+ * Search for bugs that may match the text that the user has entered and
232+ * display them in-line.
233+ */
234+function search_for_and_display_dupes() {
235+ function show_failure_message(transaction_id, response, args) {
236+ // If the request failed due to a timeout, display a message
237+ // explaining how the user may be able to work around it.
238+ var error_message = '';
239+ if (response.status == 503) {
240+ // We treat 503 (service unavailable) as a timeout because
241+ // that's what timeouts in LP return.
242+ error_message =
243+ "Searching for your bug in Launchpad took too long. " +
244+ "Try reducing the number of words in the summary " +
245+ "field and click \"Check again\" to retry your search. " +
246+ "Alternatively, you can enter the details of your bug " +
247+ "below.";
248+ } else {
249+ // Any other error code gets a generic message.
250+ error_message =
251+ "An error occured whilst trying to find bugs matching " +
252+ "the summary you entered. Click \"Check again\" to retry " +
253+ "your search. Alternatively, you can enter the " +
254+ "details of your bug below.";
255+ }
256+
257+ var error_node = Y.Node.create('<p></p>');
258+ error_node.set('text', error_message);
259+ Y.one('#possible-duplicates').appendChild(error_node);
260+
261+ Y.one('#spinner').addClass(UNSEEN);
262+ show_bug_reporting_form();
263+
264+ Y.one(Y.DOM.byId('field.title')).set(
265+ 'value', search_field.get('value'));
266+ search_button.set('value', 'Check again');
267+ search_button.removeClass(UNSEEN);
268+ }
269+
270+ function on_success(transaction_id, response, args) {
271+ // Hide the spinner and show the duplicates.
272+ Y.one('#spinner').addClass(UNSEEN);
273+
274+ var duplicate_div = Y.one('#possible-duplicates');
275+ duplicate_div.set(INNER_HTML, response.responseText);
276+
277+ bug_already_reported_expanders = Y.all(
278+ 'img.bug-already-reported-expander');
279+ if (Y.Lang.isValue(bug_already_reported_expanders)) {
280+ // If there are duplicates shown, set up the JavaScript of
281+ // the duplicates that have been returned.
282+ Y.bugs.setup_dupes();
283+ } else {
284+ // Otherwise, show the bug reporting form.
285+ show_bug_reporting_form();
286+ }
287+
288+ // Copy the value from the search field into the title field
289+ // on the filebug form.
290+ Y.one('#bug_reporting_form input[name=field.title]').set(
291+ 'value', search_field.get('value'));
292+
293+ // Finally, change the label on the search button and show it
294+ // again.
295+ search_button.set('value', 'Check again');
296+ search_button.removeClass(UNSEEN);
297+ }
298+
299+ var search_term = encodeURI(search_field.get('value'));
300+ var search_url_base = Y.one(
301+ '#duplicate-search-url').getAttribute('href');
302+ var search_url = search_url_base + '?title=' + search_term;
303+
304+ // Hide the button, show the spinner and clear the contents of the
305+ // possible duplicates div.
306+ search_button.addClass(UNSEEN);
307+ Y.one('#spinner').removeClass(UNSEEN);
308+ Y.one('#possible-duplicates').set(INNER_HTML, '');
309+
310+ config = {on: {success: on_success,
311+ failure: show_failure_message}};
312+ Y.io(search_url, config);
313+}
314+
315+/*
316+>>>>>>> MERGE-SOURCE
317 * Create the overlay for a user to optionally subscribe to a bug that
318 * affects them.
319 * @param form The form to which the FormOverlay is going to be
320@@ -265,11 +364,21 @@
321
322 // Add an on-click handler to the radio buttons to ensure that their
323 // labels' styles are set correctly when they're selected.
324+<<<<<<< TREE
325 var radio_buttons = form.queryAll('input.subscribe-option');
326 Y.each(radio_buttons, function(radio_button) {
327 var weight = radio_button.get('checked') ? 'bold' : 'normal';
328 radio_button.get('parentNode').setStyle('fontWeight', weight);
329 });
330+=======
331+ var radio_buttons = form.queryAll('input.subscribe-option');
332+ if (Y.Lang.isValue(radio_buttons)) {
333+ Y.each(radio_buttons, function(radio_button) {
334+ var weight = radio_button.get('checked') ? 'bold' : 'normal';
335+ radio_button.get('parentNode').setStyle('fontWeight', weight);
336+ });
337+ }
338+>>>>>>> MERGE-SOURCE
339
340 return subscribe_form_overlay;
341 }
342@@ -465,9 +574,142 @@
343 });
344 };
345
346+/**
347+ * Set up the dupe finder, overriding the default behaviour of the
348+ * +filebug search form.
349+ */
350+function set_up_dupe_finder(transaction_id, response, args) {
351+ var filebug_form_container = Y.one('#filebug-form-container');
352+ filebug_form_container.set(INNER_HTML, response.responseText);
353+
354+ // Activate the extra options collapsible section on the bug
355+ // reporting form.
356+ var bug_reporting_form = Y.one('#bug_reporting_form');
357+ if (Y.Lang.isValue(bug_reporting_form)) {
358+ activateCollapsibles();
359+ }
360+
361+ search_button = Y.one(Y.DOM.byId('field.actions.search'));
362+
363+ // Change the name and id of the search field so that it doesn't
364+ // confuse the view when we submit a bug report.
365+ search_field = Y.one(Y.DOM.byId('field.title'));
366+ search_field.set('name', 'field.search');
367+ search_field.set('id', 'field.search');
368+
369+ // Update the label on the search button so that it no longer
370+ // says "Continue".
371+ search_button.set('value', 'Next');
372+
373+ // Set up the handler for the search form.
374+ search_form = Y.one('#filebug-search-form');
375+ search_form.on('submit', function(e) {
376+ // Prevent the event from propagating; we don't want to reload
377+ // the page.
378+ e.halt();
379+ search_for_and_display_dupes();
380+ });
381+}
382+
383+Y.bugs.setup_dupes = function() {
384+ bug_already_reported_expanders = Y.all(
385+ 'img.bug-already-reported-expander');
386+ bug_reporting_form = Y.one('#bug_reporting_form');
387+
388+ if (Y.Lang.isValue(bug_already_reported_expanders)) {
389+ // Collapse all the details divs, since we don't want them
390+ // expanded first up.
391+ Y.each(Y.all('div.duplicate-details'), function(div) {
392+ collapse_bug_details(div);
393+ });
394+
395+ // Set up the onclick handlers for the expanders.
396+ Y.each(Y.all('.similar-bug'), function(row) {
397+ var bug_details_div = row.query('div.duplicate-details');
398+ var image = row.query('img.bug-already-reported-expander');
399+ var bug_title_link = row.query('.duplicate-bug-link');
400+ var view_bug_link = row.query('.view-bug-link');
401+
402+ // Shut down the default action for the link and mark it
403+ // as a JS'd link. We do this as it's simpler than
404+ // trying to find all the bits of the row that we want
405+ // to make clickable.
406+ bug_title_link.addClass('js-action');
407+ bug_title_link.on('click', function(e) {
408+ e.preventDefault();
409+ });
410+
411+ // The "view this bug" link shouldn't trigger the
412+ // collapsible, so we stop the event from propagating.
413+ view_bug_link.on('click', function(e) {
414+ e.stopPropagation();
415+ });
416+
417+ // The same is true for the collapsible section. People
418+ // may want to copy and paste this, which involves
419+ // clicking, so we stop the onclick event from
420+ // propagating here, too.
421+ bug_details_div.on('click', function(e) {
422+ e.stopPropagation();
423+ });
424+
425+ // Set up the on focus handler for the link so that
426+ // tabbing will expand the different bugs.
427+ bug_title_link.on('focus', function(e) {
428+ if (!bug_details_div.hasClass('lazr-opened')) {
429+ var anim = Y.lazr.effects.slide_out(bug_details_div);
430+ anim.run();
431+
432+ image.set(SRC, EXPANDER_EXPANDED);
433+
434+ // If the bug reporting form is shown, hide it.
435+ if (bug_reporting_form.getStyle(DISPLAY) == BLOCK) {
436+ bug_reporting_form.addClass(UNSEEN);
437+ }
438+ }
439+ });
440+
441+ row.on('click', function(e) {
442+ if (bug_details_div.hasClass('lazr-opened')) {
443+ collapse_bug_details(image);
444+ } else {
445+ var anim = Y.lazr.effects.slide_out(bug_details_div);
446+ anim.run();
447+
448+ image.set(SRC, EXPANDER_EXPANDED);
449+ }
450+ });
451+ });
452+
453+ // Hide the bug reporting form.
454+ bug_reporting_form.addClass(UNSEEN);
455+ }
456+
457+ bug_not_reported_button = Y.one('#bug-not-already-reported');
458+ if (Y.Lang.isValue(bug_not_reported_button)) {
459+ // The bug_not_reported_button won't show up if there aren't any
460+ // possible duplicates.
461+ bug_not_reported_button.on('click', show_bug_reporting_form);
462+ }
463+
464+ // Attach the form overlay to the "Yes, this is my bug" forms.
465+ var this_is_my_bug_forms = Y.all('form.this-is-my-bug-form');
466+ Y.each(this_is_my_bug_forms, function(form) {
467+ var subscribe_form_overlay = create_subscribe_overlay(form);
468+
469+ form.on('submit', function(e) {
470+ // We don't care about the original event, so stop it
471+ // and show the form overlay that we just created.
472+ e.halt();
473+ subscribe_form_overlay.show();
474+ });
475+ });
476+};
477+
478 Y.bugs.setup_dupe_finder = function() {
479 Y.log("In setup_dupe_finder");
480 Y.on('domready', function() {
481+<<<<<<< TREE
482 config = {on: {success: set_up_dupe_finder,
483 failure: function() {}}};
484
485@@ -481,6 +723,21 @@
486 'href');
487 Y.io(filebug_form_url, config);
488 }
489+=======
490+ config = {on: {success: set_up_dupe_finder,
491+ failure: function() {}}};
492+
493+ // Load the filebug form asynchronously. If this fails we
494+ // degrade to the standard mode for bug filing, clicking through
495+ // to the second part of the bug filing form.
496+ var filebug_form_url_element = Y.one(
497+ '#filebug-form-url');
498+ if (Y.Lang.isValue(filebug_form_url_element)) {
499+ var filebug_form_url = filebug_form_url_element.getAttribute(
500+ 'href');
501+ Y.io(filebug_form_url, config);
502+ }
503+>>>>>>> MERGE-SOURCE
504 });
505 };
506
507
508=== modified file 'lib/lp/archiveuploader/tests/test_utils.py'
509--- lib/lp/archiveuploader/tests/test_utils.py 2009-11-18 02:58:23 +0000
510+++ lib/lp/archiveuploader/tests/test_utils.py 2009-12-14 08:43:38 +0000
511@@ -10,6 +10,7 @@
512 import shutil
513
514 from lp.registry.interfaces.sourcepackage import SourcePackageFileType
515+from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFileType
516 from lp.archiveuploader.tests import datadir
517
518
519@@ -72,6 +73,23 @@
520 self.assertEquals(None, determine_source_file_type('foo_1.0'))
521 self.assertEquals(None, determine_source_file_type('foo_1.0.blah.gz'))
522
523+ def test_determine_binary_file_type(self):
524+ """lp.archiveuploader.utils.determine_binary_file_type should work."""
525+ from lp.archiveuploader.utils import determine_binary_file_type
526+
527+ # .deb -> DEB
528+ self.assertEquals(
529+ determine_binary_file_type('foo_1.0-1_all.deb'),
530+ BinaryPackageFileType.DEB)
531+
532+ # .udeb -> UDEB
533+ self.assertEquals(
534+ determine_binary_file_type('foo_1.0-1_all.udeb'),
535+ BinaryPackageFileType.UDEB)
536+
537+ self.assertEquals(determine_binary_file_type('foo_1.0'), None)
538+ self.assertEquals(determine_binary_file_type('foo_1.0.notdeb'), None)
539+
540 def testPrefixMultilineString(self):
541 """lp.archiveuploader.utils.prefix_multi_line_string should work"""
542 from lp.archiveuploader.utils import prefix_multi_line_string
543
544=== modified file 'lib/lp/archiveuploader/utils.py'
545--- lib/lp/archiveuploader/utils.py 2009-11-14 02:59:34 +0000
546+++ lib/lp/archiveuploader/utils.py 2009-12-14 08:43:37 +0000
547@@ -17,6 +17,7 @@
548 're_changes_file_name',
549 're_extract_src_version',
550 'get_source_file_extension',
551+ 'determine_binary_file_type',
552 'determine_source_file_type',
553 'prefix_multi_line_string',
554 'safe_fix_maintainer',
555@@ -88,6 +89,19 @@
556 return None
557
558
559+def determine_binary_file_type(filename):
560+ """Determine the BinaryPackageFileType of the given filename."""
561+ # Avoid circular imports.
562+ from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFileType
563+
564+ if filename.endswith(".deb"):
565+ return BinaryPackageFileType.DEB
566+ elif filename.endswith(".udeb"):
567+ return BinaryPackageFileType.UDEB
568+ else:
569+ return None
570+
571+
572 def prefix_multi_line_string(str, prefix, include_blank_lines=0):
573 """Utility function to split an input string and prefix,
574
575
576=== modified file 'lib/lp/bugs/browser/bugtarget.py'
577--- lib/lp/bugs/browser/bugtarget.py 2009-12-11 10:58:46 +0000
578+++ lib/lp/bugs/browser/bugtarget.py 2009-12-14 08:43:37 +0000
579@@ -360,6 +360,11 @@
580 if 'field.actions' in key] != [] or
581 self.user.inTeam(bug_supervisor))
582
583+ @property
584+ def use_asynchronous_dupefinder(self):
585+ """Return True if the asynchronous dupe finder can be used."""
586+ return IProduct.providedBy(self.context)
587+
588 def getPackageNameFieldCSSClass(self):
589 """Return the CSS class for the packagename field."""
590 if self.widget_errors.get("packagename"):
591@@ -700,6 +705,7 @@
592 """Override this method in base classes to show the filebug form."""
593 raise NotImplementedError
594
595+<<<<<<< TREE
596 @property
597 def inline_filebug_base_url(self):
598 """Return the base URL for the current request.
599@@ -729,6 +735,28 @@
600 url = urlappend(url, self.extra_data_token)
601 return url
602
603+=======
604+ @property
605+ def inline_filebug_form_url(self):
606+ """The URL to the inline filebug form.
607+
608+ If a token was passed to this view, it will be be passed through
609+ to the inline bug filing form via the returned URL.
610+ """
611+ url = canonical_url(self.context, view_name='+filebug-inline-form')
612+ if self.extra_data_token is not None:
613+ url = urlappend(url, self.extra_data_token)
614+ return url
615+
616+ @property
617+ def duplicate_search_url(self):
618+ """The URL to the inline duplicate search view."""
619+ url = canonical_url(self.context, view_name='+filebug-show-similar')
620+ if self.extra_data_token is not None:
621+ url = urlappend(url, self.extra_data_token)
622+ return url
623+
624+>>>>>>> MERGE-SOURCE
625 def publishTraverse(self, request, name):
626 """See IBrowserPublisher."""
627 if self.extra_data_token is not None:
628
629=== modified file 'lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt'
630--- lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt 2009-12-10 17:29:26 +0000
631+++ lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt 2009-12-14 08:43:38 +0000
632@@ -43,6 +43,7 @@
633 u'Test description.'
634
635
636+<<<<<<< TREE
637 == URLs to additional FileBug elements ==
638
639 FileBugViewBase's inline_filebug_base_url returns the base URL for all
640@@ -90,6 +91,27 @@
641 http://launchpad.dev/evolution/+filebug-show-similar
642
643
644+=======
645+== URLs to additional FileBug elements ==
646+
647+FileBugViewBase provides properties that return the URLs of further
648+useful parts of the +filebug process.
649+
650+The inline_filebug_form_url property returns the URL of the inline
651+filebug form so that it may be loaded asynchronously.
652+
653+ >>> print filebug_view.inline_filebug_form_url
654+ http://launchpad.dev/ubuntu/+source/mozilla-firefox/+filebug-inline-form
655+
656+Similarly, the duplicate_search_url property returns the base URL for
657+the duplicate search view, which can be used to load the list of
658+possible duplicates for a bug asynchronously.
659+
660+ >>> print filebug_view.duplicate_search_url
661+ http://launchpad.dev/ubuntu/+source/mozilla-firefox/+filebug-show-similar
662+
663+
664+>>>>>>> MERGE-SOURCE
665 == Adding extra info to filed bugs ==
666
667 It's possible for bug reporting tools to upload a file with debug
668
669=== modified file 'lib/lp/bugs/stories/bugattachments/20-edit-bug-attachment.txt'
670--- lib/lp/bugs/stories/bugattachments/20-edit-bug-attachment.txt 2009-12-10 15:12:22 +0000
671+++ lib/lp/bugs/stories/bugattachments/20-edit-bug-attachment.txt 2009-12-14 08:43:38 +0000
672@@ -23,6 +23,7 @@
673
674 ...we're redirected to the bug page
675
676+<<<<<<< TREE
677 >>> user_browser.url
678 'http://bugs.launchpad.dev/firefox/+bug/1'
679
680@@ -67,3 +68,40 @@
681 >>> patch_checkbox = user_browser.getControl('This attachment is a patch')
682 >>> patch_checkbox.selected
683 True
684+=======
685+ >>> user_browser.url
686+ 'http://bugs.launchpad.dev/firefox/+bug/1'
687+
688+ >>> 'Another title' in user_browser.contents
689+ True
690+
691+We can edit the attachment to be a patch.
692+
693+ >>> user_browser.getLink(url='+attachment/1').click()
694+ >>> user_browser.getControl('This attachment is a patch').selected = True
695+ >>> user_browser.getControl('Change').click()
696+
697+ >>> user_browser.url
698+ 'http://bugs.launchpad.dev/firefox/+bug/1'
699+
700+The attachment that became a patch is now shown in the portlet "Patches"...
701+
702+ >>> patches = find_portlet(user_browser.contents, 'Patches')
703+ >>> for li_tag in patches.findAll('li', 'download-attachment'):
704+ ... print li_tag.a.renderContents()
705+ Another title
706+
707+...while it is gone from the portlet "Bug attachments".
708+
709+ >>> attachments = find_portlet(user_browser.contents, 'Bug attachments')
710+ >>> for li_tag in attachments.findAll('li', 'download-attachment'):
711+ ... print li_tag.a.renderContents()
712+ bar.txt
713+
714+The content type of a patch is automatically changed to text/plain.
715+
716+ >>> user_browser.getLink(url='+attachment/1').click()
717+ >>> user_browser.getControl('Content Type').value
718+ 'text/plain'
719+
720+>>>>>>> MERGE-SOURCE
721
722=== modified file 'lib/lp/bugs/templates/bug-portlet-attachments.pt'
723--- lib/lp/bugs/templates/bug-portlet-attachments.pt 2009-12-10 15:07:18 +0000
724+++ lib/lp/bugs/templates/bug-portlet-attachments.pt 2009-12-14 08:43:39 +0000
725@@ -2,6 +2,7 @@
726 xmlns:tal="http://xml.zope.org/namespaces/tal"
727 xmlns:metal="http://xml.zope.org/namespaces/metal"
728 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
729+<<<<<<< TREE
730 tal:omit-tag="">
731 <div tal:condition="view/regular_attachments" class="portlet"
732 id="portlet-attachments">
733@@ -48,4 +49,52 @@
734 </li>
735 </ul>
736 </div>
737+=======
738+ tal:omit-tag="">
739+ <div tal:condition="view/regular_attachments" class="portlet"
740+ id="portlet-attachments">
741+ <h2>Bug attachments</h2>
742+ <ul>
743+ <li class="download-attachment"
744+ tal:repeat="attachment view/regular_attachments">
745+ <a tal:attributes="href attachment/libraryfile/http_url"
746+ tal:content="attachment/title"
747+ class="sprite download-icon">
748+ Attachment Title
749+ </a>
750+ <small>
751+ (<a tal:attributes="href attachment/fmt:url">edit</a>)
752+ </small>
753+ </li>
754+ </ul>
755+ <ul>
756+ <li>
757+ <a tal:attributes="href view/current_bugtask/fmt:url/+addcomment"
758+ class="sprite add">Add attachment</a>
759+ </li>
760+ </ul>
761+ </div>
762+ <div tal:condition="view/patches" class="portlet" id="portlet-patches">
763+ <h2>Patches</h2>
764+ <ul>
765+ <li class="download-attachment"
766+ tal:repeat="attachment view/patches">
767+ <a tal:attributes="href attachment/libraryfile/http_url"
768+ tal:content="attachment/title"
769+ class="sprite download-icon">
770+ Attachment Title
771+ </a>
772+ <small>
773+ (<a tal:attributes="href attachment/fmt:url">edit</a>)
774+ </small>
775+ </li>
776+ </ul>
777+ <ul>
778+ <li>
779+ <a tal:attributes="href view/current_bugtask/fmt:url/+addcomment"
780+ class="sprite add">Add patch</a>
781+ </li>
782+ </ul>
783+ </div>
784+>>>>>>> MERGE-SOURCE
785 </div>
786
787=== modified file 'lib/lp/bugs/templates/bugtarget-filebug-search.pt'
788--- lib/lp/bugs/templates/bugtarget-filebug-search.pt 2009-12-11 10:58:46 +0000
789+++ lib/lp/bugs/templates/bugtarget-filebug-search.pt 2009-12-14 08:43:38 +0000
790@@ -5,6 +5,7 @@
791 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
792 metal:use-macro="view/macro:page/main_only">
793
794+<<<<<<< TREE
795 <metal:block fill-slot="head_epilogue">
796 <script type="text/javascript"
797 tal:condition="devmode"
798@@ -22,6 +23,27 @@
799 </script>
800 </metal:block>
801
802+=======
803+ <metal:block fill-slot="head_epilogue">
804+ <script type="text/javascript"
805+ tal:condition="devmode"
806+ tal:content="string:var yui_base='${yui}';"></script>
807+ <script type="text/javascript"
808+ tal:condition="devmode"
809+ tal:define="lp_js string:${icingroot}/build"
810+ tal:attributes="src string:${lp_js}/bugs/filebug-dupefinder.js"></script>
811+
812+ <tal:dupe-finder-js condition="view/use_asynchronous_dupefinder">
813+ <script type="text/javascript">
814+ LPS.use(
815+ 'base', 'node', 'oop', 'event', 'bugs.dupe_finder', function(Y) {
816+ Y.bugs.setup_dupe_finder();
817+ });
818+ </script>
819+ </tal:dupe-finder-js>
820+ </metal:block>
821+
822+>>>>>>> MERGE-SOURCE
823 <div metal:fill-slot="heading">
824 <div tal:condition="view/isPrivate" id="privacy" class="aside private">
825 This report will be private, though you can disclose it later.
826@@ -106,6 +128,7 @@
827 We add this to hide the standard action buttons.
828 </tal:comment>
829 </div>
830+<<<<<<< TREE
831 </div>
832 </div>
833
834@@ -128,3 +151,25 @@
835 </div>
836
837 </html>
838+=======
839+ </div>
840+ </div>
841+ </tal:uses-malone>
842+ </div>
843+
844+ <div id="possible-duplicates" style="text-align: left;">
845+ </div>
846+ <div id="filebug-form-container" style="display: none;">
847+ </div>
848+
849+ <p style="display: none" tal:condition="view/use_asynchronous_dupefinder">
850+ <a id="filebug-form-url"
851+ tal:attributes="href view/inline_filebug_form_url"></a>
852+ <a id="duplicate-search-url"
853+ tal:attributes="href view/duplicate_search_url"></a>
854+ </p>
855+
856+ </div>
857+
858+</html>
859+>>>>>>> MERGE-SOURCE
860
861=== modified file 'lib/lp/bugs/templates/bugtasks-and-nominations-table.pt'
862--- lib/lp/bugs/templates/bugtasks-and-nominations-table.pt 2009-12-11 14:00:13 +0000
863+++ lib/lp/bugs/templates/bugtasks-and-nominations-table.pt 2009-12-14 08:43:38 +0000
864@@ -112,7 +112,54 @@
865 define="link context_menu/nominate"
866 condition="link/enabled"
867 replace="structure link/render" />
868+<<<<<<< TREE
869 </tal:also-affects-links>
870+=======
871+ <span id="affectsmetoo" style="display: inline"
872+ tal:condition="link/enabled"
873+ tal:define="link context_menu/affectsmetoo;
874+ affected view/current_user_affected_status">
875+
876+ <tal:comment condition="nothing">
877+ This .static section is shown in browsers with javascript
878+ enabled, and before setup_me_too is run.
879+ </tal:comment>
880+ <span class="static">
881+ <tal:affected condition="affected">
882+ <img width="14" height="14" src="/@@/flame-icon" alt="" />
883+ This bug affects me too
884+ </tal:affected>
885+ <tal:not-affected condition="not:affected">
886+ This bug doesn't affect me
887+ </tal:not-affected>
888+ <a href="+affectsmetoo">
889+ <img class="editicon" src="/@@/edit" alt="Edit" />
890+ </a>
891+ </span>
892+
893+ <tal:comment condition="nothing">
894+ This .dynamic section is used by setup_me_too to display
895+ controls and information in the correct places.
896+ </tal:comment>
897+ <span class="dynamic unseen">
898+ <img src="/@@/flame-icon" alt=""/>
899+ <a href="+affectsmetoo" class="js-action"
900+ ><span class="value">Does this bug affect you?</span></a>
901+ <img class="editicon" src="/@@/edit" alt="Edit" />
902+ </span>
903+
904+ <script type="text/javascript" tal:content="string:
905+ LPS.use('event', 'bugs.bugtask_index', function(Y) {
906+ Y.on('load', function(e) {
907+ Y.bugs.setup_me_too(${view/current_user_affected_js_status});
908+ }, window);
909+ });
910+ ">
911+ </script>
912+
913+ </span>
914+ </tal:also-affects-links>
915+>>>>>>> MERGE-SOURCE
916 </div>
917
918 </tal:root>
919
920=== modified file 'lib/lp/code/browser/branch.py'
921--- lib/lp/code/browser/branch.py 2009-12-11 00:56:16 +0000
922+++ lib/lp/code/browser/branch.py 2009-12-14 08:43:37 +0000
923@@ -83,8 +83,14 @@
924 UICreatableBranchType)
925 from lp.code.errors import InvalidBranchMergeProposal
926 from lp.code.interfaces.branch import (
927- BranchCreationForbidden, BranchExists, IBranch,
928- user_has_special_branch_access)
929+<<<<<<< TREE
930+ BranchCreationForbidden, BranchExists, IBranch,
931+ user_has_special_branch_access)
932+=======
933+ BranchCreationForbidden, BranchExists, IBranch,
934+ user_has_special_branch_access)
935+from lp.code.interfaces.branchmergeproposal import InvalidBranchMergeProposal
936+>>>>>>> MERGE-SOURCE
937 from lp.code.interfaces.branchtarget import IBranchTarget
938 from lp.code.interfaces.codeimport import CodeImportReviewStatus
939 from lp.code.interfaces.codeimportjob import (
940
941=== modified file 'lib/lp/code/templates/branchmergeproposal-index.pt'
942--- lib/lp/code/templates/branchmergeproposal-index.pt 2009-12-11 05:07:57 +0000
943+++ lib/lp/code/templates/branchmergeproposal-index.pt 2009-12-14 08:43:37 +0000
944@@ -110,6 +110,7 @@
945 </div>
946
947 <div class="yui-g">
948+<<<<<<< TREE
949 <tal:not-logged-in condition="not: view/user">
950 <div align="center" id="add-comment-login-first">
951 To post a comment you must <a href="+login">log in</a>.
952@@ -117,6 +118,9 @@
953 </tal:not-logged-in>
954
955 <div tal:define="link menu/add_comment"
956+=======
957+ <div tal:define="link menu/add_comment"
958+>>>>>>> MERGE-SOURCE
959 tal:condition="link/enabled"
960 tal:content="structure link/render">
961 Add comment
962@@ -125,6 +129,7 @@
963 <div id="conversation"
964 tal:content="structure view/conversation/@@+render"/>
965 </div>
966+<<<<<<< TREE
967
968 <tal:logged-in condition="view/user">
969 <div tal:define="comment_form nocall:context/@@+comment;
970@@ -146,6 +151,22 @@
971 </div>
972 </tal:logged-in>
973
974+=======
975+ <!-- Hide inline commenting if YUI isn't used. -->
976+ <div id="inline-add-comment" style="display: none">
977+ <tal:comment replace="structure context/@@+comment/++form++" />
978+ <div class="actions" id="launchpad-form-actions">
979+ <input type="submit" id="field.actions.add" name="field.actions.add" value="Save Comment" class="button" />
980+ </div>
981+ </div>
982+
983+ <script type="text/javascript">
984+ LPS.use('lp.comment', function(Y) {
985+ var comment = new Y.lp.CodeReviewComment();
986+ comment.render();
987+ })
988+ </script>
989+>>>>>>> MERGE-SOURCE
990 <div class="yui-g">
991 <div class="yui-u first">
992 <div id="source-revisions"
993@@ -190,7 +211,11 @@
994 string:&lt;script id='codereview-script' type='text/javascript'&gt;" />
995 conf = <tal:status-config replace="view/status_config" />
996 <!--
997+<<<<<<< TREE
998 LPS.use('io-base', 'code.codereview', 'code.branchmergeproposal', 'lp.comment',
999+=======
1000+ LPS.use('io-base', 'code.codereview', 'code.branchmergeproposal',
1001+>>>>>>> MERGE-SOURCE
1002 function(Y) {
1003
1004 Y.on('load', function() {
1005
1006=== modified file 'lib/lp/registry/browser/distroseries.py'
1007--- lib/lp/registry/browser/distroseries.py 2009-12-09 19:47:23 +0000
1008+++ lib/lp/registry/browser/distroseries.py 2009-12-14 08:43:39 +0000
1009@@ -462,27 +462,59 @@
1010 """A View to show series package to upstream package relationships."""
1011
1012 label = 'Mapping series packages to upstream project series'
1013- page_title = 'Upstream packaging links'
1014-
1015- @cachedproperty
1016- def unlinked_translatables(self):
1017- """The sourcepackages that lack a link to a productseries."""
1018- packages = self.context.getUnlinkedTranslatableSourcePackages()
1019- if self.context.distribution.full_functionality:
1020- # Launchpad knows exactly what is published in the series.
1021- packages = [package for package in packages
1022- if package.currentrelease is not None]
1023- return packages
1024-
1025- @cachedproperty
1026- def show_unlinked_translatables(self):
1027- """Are there unlinked translatables and should they be shown."""
1028- return (
1029- len(self.unlinked_translatables) > 0
1030- and self.cached_packagings.start == 0)
1031-
1032- @cachedproperty
1033- def cached_packagings(self):
1034- """The batched upstream packaging links."""
1035- packagings = self.context.packagings
1036- return BatchNavigator(packagings, self.request, size=200)
1037+<<<<<<< TREE
1038+ page_title = 'Upstream packaging links'
1039+
1040+ @cachedproperty
1041+ def unlinked_translatables(self):
1042+ """The sourcepackages that lack a link to a productseries."""
1043+ packages = self.context.getUnlinkedTranslatableSourcePackages()
1044+ if self.context.distribution.full_functionality:
1045+ # Launchpad knows exactly what is published in the series.
1046+ packages = [package for package in packages
1047+ if package.currentrelease is not None]
1048+ return packages
1049+
1050+ @cachedproperty
1051+ def show_unlinked_translatables(self):
1052+ """Are there unlinked translatables and should they be shown."""
1053+ return (
1054+ len(self.unlinked_translatables) > 0
1055+ and self.cached_packagings.start == 0)
1056+
1057+ @cachedproperty
1058+ def cached_packagings(self):
1059+ """The batched upstream packaging links."""
1060+ packagings = self.context.packagings
1061+ return BatchNavigator(packagings, self.request, size=200)
1062+=======
1063+ page_title = 'Upstream packaging links'
1064+
1065+ @cachedproperty
1066+ def unlinked_translatables(self):
1067+ """The sourcepackages that lack a link to a productseries."""
1068+ packages = self.context.getUnlinkedTranslatableSourcePackages()
1069+ if self.context.distribution.full_functionality:
1070+ # Launchpad knows exactly what is published in the series.
1071+ packages = [package for package in packages
1072+ if package.currentrelease is not None]
1073+ return packages
1074+
1075+ @cachedproperty
1076+ def show_unlinked_translatables(self):
1077+ """Are there unlinked translatables and should they be shown."""
1078+ return (
1079+ len(self.unlinked_translatables) > 0
1080+ and self.cached_packagings.start == 0)
1081+
1082+ @cachedproperty
1083+ def cached_packagings(self):
1084+ """The batched upstream packaging links."""
1085+ packagings = self.context.packagings
1086+ if self.context.distribution.full_functionality:
1087+ # Launchpad knows exactly what is published in the series.
1088+ packagings = [
1089+ packaging for packaging in packagings
1090+ if packaging.sourcepackage.currentrelease is not None]
1091+ return BatchNavigator(packagings, self.request, size=200)
1092+>>>>>>> MERGE-SOURCE
1093
1094=== modified file 'lib/lp/registry/browser/tests/packaging-views.txt'
1095--- lib/lp/registry/browser/tests/packaging-views.txt 2009-12-10 20:03:11 +0000
1096+++ lib/lp/registry/browser/tests/packaging-views.txt 2009-12-14 08:43:38 +0000
1097@@ -341,6 +341,7 @@
1098 sarge hotter
1099 7.0 hotter
1100 grumpy hotter
1101+<<<<<<< TREE
1102
1103
1104 Distro series +packaging view
1105@@ -390,3 +391,60 @@
1106 ... hoary, name='+packaging', query_string='start=2')
1107 >>> view.show_unlinked_translatables
1108 False
1109+=======
1110+
1111+
1112+Distro series +packaging view
1113+-----------------------------
1114+
1115+The DistroSeriesPackagesView shows the packages in a distro series that
1116+are linked to upstream projects.
1117+
1118+ >>> view = create_initialized_view(hoary, name='+packaging')
1119+ >>> print view.label
1120+ Mapping series packages to upstream project series
1121+
1122+ >>> print view.page_title
1123+ Upstream packaging links
1124+
1125+The view provides a property to get the source packages that have
1126+translations, but are not linked to an upstream project. The view filters
1127+unpublished versions from full functionality distributions like Ubuntu.
1128+
1129+ >>> hoary.getUnlinkedTranslatableSourcePackages()
1130+ [<SourcePackage .../hoary/pmount>, <SourcePackage .../hoary/mozilla>]
1131+
1132+ >>> view.show_unlinked_translatables
1133+ True
1134+
1135+ >>> view.unlinked_translatables
1136+ [<SourcePackage ubuntu/hoary/pmount>]
1137+
1138+A distro series can have thousands of upstream packaging links. The view
1139+provides a batch navigator to access the packagings. The default batch size
1140+is 200. The view filters unpublished versions from full functionality
1141+distributions like Ubuntu.
1142+
1143+ >>> for packaging in hoary.packagings:
1144+ ... print packaging.sourcepackagename.name
1145+ evolution
1146+ mozilla-firefox
1147+ netapplet
1148+
1149+ >>> batch_navigator = view.cached_packagings
1150+ >>> batch_navigator.default_size
1151+ 200
1152+
1153+ >>> for packaging in batch_navigator.batch:
1154+ ... print packaging.sourcepackagename.name
1155+ evolution
1156+ netapplet
1157+
1158+The show_unlinked_translatables property is False when the batch is after the
1159+first batch.
1160+
1161+ >>> view = create_initialized_view(
1162+ ... hoary, name='+packaging', query_string='start=2')
1163+ >>> view.show_unlinked_translatables
1164+ False
1165+>>>>>>> MERGE-SOURCE
1166
1167=== modified file 'lib/lp/registry/interfaces/person.py'
1168--- lib/lp/registry/interfaces/person.py 2009-12-09 23:11:31 +0000
1169+++ lib/lp/registry/interfaces/person.py 2009-12-14 08:43:39 +0000
1170@@ -65,10 +65,16 @@
1171
1172 from canonical.database.sqlbase import block_implicit_flushes
1173 from canonical.launchpad.fields import (
1174+<<<<<<< TREE
1175 BlacklistableContentNameField, IconImageUpload,
1176 is_private_membership_person, is_public_person, LogoImageUpload,
1177 MugshotImageUpload, PasswordField, PersonChoice, PublicPersonChoice,
1178 StrippedTextLine)
1179+=======
1180+ BlacklistableContentNameField, IconImageUpload, is_private_membership,
1181+ is_valid_public_person, LogoImageUpload, MugshotImageUpload,
1182+ PasswordField, PersonChoice, PublicPersonChoice, StrippedTextLine)
1183+>>>>>>> MERGE-SOURCE
1184 from canonical.launchpad.interfaces.account import AccountStatus, IAccount
1185 from canonical.launchpad.interfaces.emailaddress import IEmailAddress
1186 from lp.app.interfaces.headings import IRootContext
1187
1188=== modified file 'lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt'
1189--- lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt 2009-12-10 19:58:31 +0000
1190+++ lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt 2009-12-14 08:43:38 +0000
1191@@ -1,48 +1,98 @@
1192-Distro series packaging
1193-=======================
1194-
1195-The distro series packaging page is accssible to any user from the distro
1196-series +index page.
1197-
1198- >>> anon_browser.open('http://launchpad.dev/ubuntu/hoary')
1199- >>> anon_browser.getLink('Upstream links').click()
1200- >>> print anon_browser.title
1201- Upstream packaging links : ...
1202-
1203-The page lists the the source packages that have translations, but are
1204-not linked to an upstream project to sync them to.
1205-
1206- >>> content = find_main_content(anon_browser.contents)
1207- >>> print extract_text(find_tag_by_id(content, 'unlinked-translatables'))
1208- Unlinked translatable packages
1209- pmount
1210-
1211-The page lists the upstream packaging links.
1212-
1213- >>> print extract_text(find_tag_by_id(content, 'packagings'))
1214- Source Package Upstream Project Series
1215- evolution Evolution trunk series
1216- mozilla-firefox Mozilla Firefox 1.0 series
1217- netapplet NetApplet trunk series
1218-
1219-The packaging links are batched so that users can view the thousands of
1220-links packages. Users can also hack the URL to set their own batch size.
1221-
1222- >>> anon_browser.open(
1223- ... 'http://launchpad.dev/ubuntu/hoary/+packaging?start=0&batch=1')
1224- >>> print extract_text(find_tag_by_id(
1225- ... anon_browser.contents, 'packagings'))
1226- Source Package Upstream Project Series
1227- evolution Evolution trunk series
1228-
1229- >>> anon_browser.getLink('Next', index=0).click()
1230- >>> content = find_main_content(anon_browser.contents)
1231- >>> print extract_text(find_tag_by_id(content, 'packagings'))
1232- Source Package Upstream Project Series
1233- mozilla-firefox Mozilla Firefox 1.0 series
1234-
1235-The unlinked translatable source packages is not displayed on pages after
1236-the first page.
1237-
1238- >>> print find_tag_by_id(content, 'unlinked_translatables')
1239- None
1240+<<<<<<< TREE
1241+Distro series packaging
1242+=======================
1243+
1244+The distro series packaging page is accssible to any user from the distro
1245+series +index page.
1246+
1247+ >>> anon_browser.open('http://launchpad.dev/ubuntu/hoary')
1248+ >>> anon_browser.getLink('Upstream links').click()
1249+ >>> print anon_browser.title
1250+ Upstream packaging links : ...
1251+
1252+The page lists the the source packages that have translations, but are
1253+not linked to an upstream project to sync them to.
1254+
1255+ >>> content = find_main_content(anon_browser.contents)
1256+ >>> print extract_text(find_tag_by_id(content, 'unlinked-translatables'))
1257+ Unlinked translatable packages
1258+ pmount
1259+
1260+The page lists the upstream packaging links.
1261+
1262+ >>> print extract_text(find_tag_by_id(content, 'packagings'))
1263+ Source Package Upstream Project Series
1264+ evolution Evolution trunk series
1265+ mozilla-firefox Mozilla Firefox 1.0 series
1266+ netapplet NetApplet trunk series
1267+
1268+The packaging links are batched so that users can view the thousands of
1269+links packages. Users can also hack the URL to set their own batch size.
1270+
1271+ >>> anon_browser.open(
1272+ ... 'http://launchpad.dev/ubuntu/hoary/+packaging?start=0&batch=1')
1273+ >>> print extract_text(find_tag_by_id(
1274+ ... anon_browser.contents, 'packagings'))
1275+ Source Package Upstream Project Series
1276+ evolution Evolution trunk series
1277+
1278+ >>> anon_browser.getLink('Next', index=0).click()
1279+ >>> content = find_main_content(anon_browser.contents)
1280+ >>> print extract_text(find_tag_by_id(content, 'packagings'))
1281+ Source Package Upstream Project Series
1282+ mozilla-firefox Mozilla Firefox 1.0 series
1283+
1284+The unlinked translatable source packages is not displayed on pages after
1285+the first page.
1286+
1287+ >>> print find_tag_by_id(content, 'unlinked_translatables')
1288+ None
1289+=======
1290+Distro series packaging
1291+=======================
1292+
1293+The distro series packaging page is accssible to any user from the distro
1294+series +index page.
1295+
1296+ >>> anon_browser.open('http://launchpad.dev/ubuntu/hoary')
1297+ >>> anon_browser.getLink('Upstream links').click()
1298+ >>> print anon_browser.title
1299+ Upstream packaging links : ...
1300+
1301+The page lists the the source packages that have translations, but are
1302+not linked to an upstream project to sync them to.
1303+
1304+ >>> content = find_main_content(anon_browser.contents)
1305+ >>> print extract_text(find_tag_by_id(content, 'unlinked-translatables'))
1306+ Unlinked translatable packages
1307+ pmount
1308+
1309+The page lists the upstream packaging links.
1310+
1311+ >>> print extract_text(find_tag_by_id(content, 'packagings'))
1312+ Source Package Upstream Project Series
1313+ evolution Evolution trunk series
1314+ netapplet NetApplet trunk series
1315+
1316+The packaging links are batched so that users can view the thousands of
1317+links packages. Users can also hack the URL to set their own batch size.
1318+
1319+ >>> anon_browser.open(
1320+ ... 'http://launchpad.dev/ubuntu/hoary/+packaging?start=0&batch=1')
1321+ >>> print extract_text(find_tag_by_id(
1322+ ... anon_browser.contents, 'packagings'))
1323+ Source Package Upstream Project Series
1324+ evolution Evolution trunk series
1325+
1326+ >>> anon_browser.getLink('Next', index=0).click()
1327+ >>> content = find_main_content(anon_browser.contents)
1328+ >>> print extract_text(find_tag_by_id(content, 'packagings'))
1329+ Source Package Upstream Project Series
1330+ netapplet NetApplet trunk series
1331+
1332+The unlinked translatable source packages is not displayed on pages after
1333+the first page.
1334+
1335+ >>> print find_tag_by_id(content, 'unlinked_translatables')
1336+ None
1337+>>>>>>> MERGE-SOURCE
1338
1339=== modified file 'lib/lp/registry/tests/test_productseries_vocabularies.py'
1340--- lib/lp/registry/tests/test_productseries_vocabularies.py 2009-12-09 23:11:31 +0000
1341+++ lib/lp/registry/tests/test_productseries_vocabularies.py 2009-12-14 08:43:39 +0000
1342@@ -1,3 +1,4 @@
1343+<<<<<<< TREE
1344 # Copyright 2009 Canonical Ltd. This software is licensed under the
1345 # GNU Affero General Public License version 3 (see the file LICENSE).
1346
1347@@ -80,3 +81,77 @@
1348
1349 def test_suite():
1350 return TestLoader().loadTestsFromName(__name__)
1351+=======
1352+# Copyright 2009 Canonical Ltd. This software is licensed under the
1353+# GNU Affero General Public License version 3 (see the file LICENSE).
1354+
1355+"""Test the milestone vocabularies."""
1356+
1357+__metaclass__ = type
1358+
1359+from unittest import TestLoader
1360+from operator import attrgetter
1361+
1362+from lp.registry.vocabularies import ProductSeriesVocabulary
1363+from lp.testing import TestCaseWithFactory
1364+
1365+from canonical.testing import DatabaseFunctionalLayer
1366+
1367+
1368+class TestProductSeriesVocabulary(TestCaseWithFactory):
1369+ """Test that the ProductSeriesVocabulary behaves as expected."""
1370+ layer = DatabaseFunctionalLayer
1371+
1372+ def setUp(self):
1373+ super(TestProductSeriesVocabulary, self).setUp()
1374+ self.vocabulary = ProductSeriesVocabulary()
1375+ self.product_prefix = 'asdf987-'
1376+ self.series1_prefix = 'qwerty-'
1377+ self.product = self.factory.makeProduct(
1378+ self.product_prefix + 'product1')
1379+ self.series = self.factory.makeProductSeries(
1380+ product=self.product, name=self.series1_prefix + "series1")
1381+
1382+ def tearDown(self):
1383+ super(TestProductSeriesVocabulary, self).tearDown()
1384+
1385+ def test_search(self):
1386+ series2 = self.factory.makeProductSeries(product=self.product)
1387+ # Search by product name.
1388+ result = self.vocabulary.search(self.product.name)
1389+ self.assertEqual(
1390+ [self.series, series2].sort(key=attrgetter('id')),
1391+ list(result).sort(key=attrgetter('id')))
1392+ # Search by series name.
1393+ result = self.vocabulary.search(self.series.name)
1394+ self.assertEqual([self.series], list(result))
1395+ # Search by series2 name.
1396+ result = self.vocabulary.search(series2.name)
1397+ self.assertEqual([series2], list(result))
1398+ # Search by product & series name substrings.
1399+ result = self.vocabulary.search(
1400+ '%s/%s' % (self.product_prefix, self.series1_prefix))
1401+ self.assertEqual([self.series], list(result))
1402+
1403+ def test_toTerm(self):
1404+ term = self.vocabulary.toTerm(self.series)
1405+ self.assertEqual(
1406+ '%s/%s' % (self.product.name, self.series.name),
1407+ term.token)
1408+ self.assertEqual(self.series, term.value)
1409+
1410+ def test_getTermByToken(self):
1411+ token = '%s/%s' % (self.product.name, self.series.name)
1412+ term = self.vocabulary.getTermByToken(token)
1413+ self.assertEqual(token, term.token)
1414+ self.assertEqual(self.series, term.value)
1415+
1416+ def test_getTermByToken_LookupError(self):
1417+ self.assertRaises(
1418+ LookupError,
1419+ self.vocabulary.getTermByToken, 'does/notexist')
1420+
1421+
1422+def test_suite():
1423+ return TestLoader().loadTestsFromName(__name__)
1424+>>>>>>> MERGE-SOURCE
1425
1426=== modified file 'lib/lp/soyuz/doc/gina.txt'
1427--- lib/lp/soyuz/doc/gina.txt 2009-09-09 21:41:04 +0000
1428+++ lib/lp/soyuz/doc/gina.txt 2009-12-14 08:43:38 +0000
1429@@ -187,7 +187,7 @@
1430 16
1431 >>> count = SourcePackageRelease.select().count()
1432 >>> count - orig_spr_count
1433- 16
1434+ 17
1435
1436 Check that x11proto-damage has its Build-Depends-Indep value correctly set:
1437
1438@@ -317,13 +317,13 @@
1439 util-linux (again, poor thing)
1440
1441 >>> print SSPPH.select().count() - orig_sspph_count
1442- 19
1443+ 20
1444
1445 >>> print SSPPH.selectBy(
1446 ... componentID=1,
1447 ... pocket=PackagePublishingPocket.RELEASE).count() - \
1448 ... orig_sspph_main_count
1449- 19
1450+ 20
1451
1452 === Testing Binary Package Results ===
1453
1454@@ -441,11 +441,11 @@
1455 >>> print p.name
1456 cjwatson
1457 >>> print Person.select().count() - orig_person_count
1458- 12
1459+ 13
1460 >>> print TeamParticipation.select().count() - orig_tp_count
1461- 12
1462+ 13
1463 >>> print EmailAddress.select().count() - orig_email_count
1464- 12
1465+ 13
1466
1467
1468 === Re-run Gina ===
1469@@ -515,13 +515,13 @@
1470 changed, etc.
1471
1472 >>> SourcePackageRelease.select().count() - orig_spr_count
1473- 16
1474+ 17
1475 >>> print Person.select().count() - orig_person_count
1476- 12
1477+ 13
1478 >>> print TeamParticipation.select().count() - orig_tp_count
1479- 12
1480+ 13
1481 >>> print EmailAddress.select().count() - orig_email_count
1482- 12
1483+ 13
1484 >>> BinaryPackageRelease.select().count() - orig_bpr_count
1485 40
1486 >>> Build.select().count() - orig_build_count
1487@@ -532,7 +532,7 @@
1488 >>> SBPPH.select().count() - orig_sbpph_count
1489 47
1490 >>> print SSPPH.select().count() - orig_sspph_count
1491- 21
1492+ 22
1493
1494 Check that the overrides we did were correctly issued. We can't use selectOneBy
1495 because, of course, there may be multiple rows published for that package --
1496
1497=== modified file 'lib/lp/soyuz/scripts/gina/handlers.py'
1498--- lib/lp/soyuz/scripts/gina/handlers.py 2009-11-10 01:00:23 +0000
1499+++ lib/lp/soyuz/scripts/gina/handlers.py 2009-12-14 08:43:39 +0000
1500@@ -29,6 +29,8 @@
1501
1502 from lp.archivepublisher.diskpool import poolify
1503 from lp.archiveuploader.tagfiles import parse_tagfile
1504+from lp.archiveuploader.utils import (determine_binary_file_type,
1505+ determine_source_file_type)
1506
1507 from canonical.database.sqlbase import sqlvalues
1508
1509@@ -47,9 +49,9 @@
1510 from lp.registry.interfaces.person import IPersonSet, PersonCreationRationale
1511 from lp.registry.interfaces.sourcepackage import SourcePackageType
1512 from lp.soyuz.interfaces.binarypackagename import IBinaryPackageNameSet
1513+from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFormat
1514 from lp.soyuz.interfaces.build import BuildStatus
1515 from lp.soyuz.interfaces.publishing import PackagePublishingStatus
1516-from canonical.launchpad.helpers import getFileType, getBinaryPackageFormat
1517
1518
1519 def check_not_in_librarian(files, archive_root, directory):
1520@@ -78,6 +80,39 @@
1521 return to_upload
1522
1523
1524+BINARYPACKAGE_EXTENSIONS = {
1525+ BinaryPackageFormat.DEB: '.deb',
1526+ BinaryPackageFormat.UDEB: '.udeb',
1527+ BinaryPackageFormat.RPM: '.rpm'}
1528+
1529+
1530+class UnrecognizedBinaryFormat(Exception):
1531+
1532+ def __init__(self, fname, *args):
1533+ Exception.__init__(self, *args)
1534+ self.fname = fname
1535+
1536+ def __str__(self):
1537+ return '%s is not recognized as a binary file.' % self.fname
1538+
1539+
1540+def getBinaryPackageFormat(fname):
1541+ """Return the BinaryPackageFormat for the given filename.
1542+
1543+ >>> getBinaryPackageFormat('mozilla-firefox_0.9_i386.deb').name
1544+ 'DEB'
1545+ >>> getBinaryPackageFormat('debian-installer.9_all.udeb').name
1546+ 'UDEB'
1547+ >>> getBinaryPackageFormat('network-manager.9_i386.rpm').name
1548+ 'RPM'
1549+ """
1550+ for key, value in BINARYPACKAGE_EXTENSIONS.items():
1551+ if fname.endswith(value):
1552+ return key
1553+
1554+ raise UnrecognizedBinaryFormat(fname)
1555+
1556+
1557 class DataSetupError(Exception):
1558 """Raised when required data is found to be missing in the database"""
1559
1560@@ -613,9 +648,10 @@
1561 # SourcePackageReleaseFile entry on lp db.
1562 for fname, path in to_upload:
1563 alias = getLibraryAlias(path, fname)
1564- SourcePackageReleaseFile(sourcepackagerelease=spr.id,
1565- libraryfile=alias,
1566- filetype=getFileType(fname))
1567+ SourcePackageReleaseFile(
1568+ sourcepackagerelease=spr.id,
1569+ libraryfile=alias,
1570+ filetype=determine_source_file_type(fname))
1571 log.info('Package file %s included into library' % fname)
1572
1573 return spr
1574@@ -805,9 +841,10 @@
1575 (bin_name.name, bin.version))
1576
1577 alias = getLibraryAlias(path, fname)
1578- BinaryPackageFile(binarypackagerelease=binpkg.id,
1579- libraryfile=alias,
1580- filetype=getFileType(fname))
1581+ BinaryPackageFile(
1582+ binarypackagerelease=binpkg.id,
1583+ libraryfile=alias,
1584+ filetype=determine_binary_file_type(fname))
1585 log.info('Package file %s included into library' % fname)
1586
1587 # Return the binarypackage object.
1588
1589=== modified file 'lib/lp/soyuz/scripts/tests/gina_test_archive/dists/breezy/main/source/Sources'
1590--- lib/lp/soyuz/scripts/tests/gina_test_archive/dists/breezy/main/source/Sources 2005-11-05 18:06:43 +0000
1591+++ lib/lp/soyuz/scripts/tests/gina_test_archive/dists/breezy/main/source/Sources 2009-12-14 08:43:39 +0000
1592@@ -139,6 +139,23 @@
1593 7947f81d47c3ccdadf05d5e4a120b098 41936 x11proto-damage_6.8.99.7.orig.tar.gz
1594 a4e78902d591d87ab864cedc1e83278d 2201 x11proto-damage_6.8.99.7-2.diff.gz
1595
1596+Package: bar
1597+Binary: bar
1598+Version: 1.0-1
1599+Priority: optional
1600+Section: misc
1601+Maintainer: Launchpad team <launchpad@lists.canonical.com>
1602+Architecture: all
1603+Standards-Version: 3.6.2
1604+Format: 3.0 (quilt)
1605+Directory: pool/main/b/bar
1606+Files:
1607+ 662977e76501561c902c34bf94a2054d 1115 bar_1.0-1.dsc
1608+ eed105761436486f7eebaa8c017bc59a 178 bar_1.0.orig-comp1.tar.gz
1609+ 0a30b50fa846e75261808ee1f41d1cbe 156 bar_1.0.orig-comp2.tar.bz2
1610+ fc1464e5985b962a042d5354452f361d 164 bar_1.0.orig.tar.gz
1611+ e68110184d4d9e7afd81520cc490b396 737 bar_1.0-1.debian.tar.bz2
1612+
1613 Bogus, bogus, bogus
1614 ain't invalid stanza's neat?
1615
1616
1617=== modified file 'lib/lp/soyuz/scripts/tests/gina_test_archive/dists/breezy/main/source/Sources.gz'
1618Binary files lib/lp/soyuz/scripts/tests/gina_test_archive/dists/breezy/main/source/Sources.gz 2005-11-11 11:35:22 +0000 and lib/lp/soyuz/scripts/tests/gina_test_archive/dists/breezy/main/source/Sources.gz 2009-12-14 08:43:38 +0000 differ
1619=== added directory 'lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar'
1620=== added file 'lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0-1.debian.tar.bz2'
1621Binary files lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0-1.debian.tar.bz2 1970-01-01 00:00:00 +0000 and lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0-1.debian.tar.bz2 2009-12-14 08:43:39 +0000 differ
1622=== added file 'lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0-1.dsc'
1623--- lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0-1.dsc 1970-01-01 00:00:00 +0000
1624+++ lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0-1.dsc 2009-12-14 08:43:38 +0000
1625@@ -0,0 +1,22 @@
1626+Format: 3.0 (quilt)
1627+Source: bar
1628+Binary: bar
1629+Architecture: any
1630+Version: 1.0-1
1631+Maintainer: Launchpad team <launchpad@lists.canonical.com>
1632+Standards-Version: 3.6.2
1633+Checksums-Sha1:
1634+ e7a106587e0f11220e27ccc5db7527cae05e4054 178 bar_1.0.orig-comp1.tar.gz
1635+ a645cc0856469f549f11bb9bdc4ca6dda4eedd4b 156 bar_1.0.orig-comp2.tar.bz2
1636+ 73a04163fee97fd2257ab266bd48f1d3d528e012 164 bar_1.0.orig.tar.gz
1637+ 727de2395d3a3e35c375159db01072935fea02f8 737 bar_1.0-1.debian.tar.bz2
1638+Checksums-Sha256:
1639+ aff0bbc01c5883ac76e89cba7a9b6cd79b521183f0de86c93e93ab8804f5a256 178 bar_1.0.orig-comp1.tar.gz
1640+ 955131b3a5bd881c008ff822c0f9b7544350647fe1f355f65a138e0e5e5e8d71 156 bar_1.0.orig-comp2.tar.bz2
1641+ f1ecff929899b567f45d6734b69d59a4f3c04dabce3cc8e6ed6d64073eda360e 164 bar_1.0.orig.tar.gz
1642+ 3d2137f9f80f4b6c80f73acb8b3ac7986c962c4268d8948f54823d27148a6116 737 bar_1.0-1.debian.tar.bz2
1643+Files:
1644+ eed105761436486f7eebaa8c017bc59a 178 bar_1.0.orig-comp1.tar.gz
1645+ 0a30b50fa846e75261808ee1f41d1cbe 156 bar_1.0.orig-comp2.tar.bz2
1646+ fc1464e5985b962a042d5354452f361d 164 bar_1.0.orig.tar.gz
1647+ e68110184d4d9e7afd81520cc490b396 737 bar_1.0-1.debian.tar.bz2
1648
1649=== added file 'lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig-comp1.tar.gz'
1650Binary files lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig-comp1.tar.gz 1970-01-01 00:00:00 +0000 and lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig-comp1.tar.gz 2009-12-14 08:43:39 +0000 differ
1651=== added file 'lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig-comp2.tar.bz2'
1652Binary files lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig-comp2.tar.bz2 1970-01-01 00:00:00 +0000 and lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig-comp2.tar.bz2 2009-12-14 08:43:39 +0000 differ
1653=== added file 'lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig.tar.gz'
1654Binary files lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig.tar.gz 1970-01-01 00:00:00 +0000 and lib/lp/soyuz/scripts/tests/gina_test_archive/pool/main/b/bar/bar_1.0.orig.tar.gz 2009-12-14 08:43:38 +0000 differ
1655=== added file 'lib/lp/soyuz/scripts/tests/test_gina.py'
1656--- lib/lp/soyuz/scripts/tests/test_gina.py 1970-01-01 00:00:00 +0000
1657+++ lib/lp/soyuz/scripts/tests/test_gina.py 2009-12-14 08:43:39 +0000
1658@@ -0,0 +1,15 @@
1659+# Copyright 2009 Canonical Ltd. This software is licensed under the
1660+# GNU Affero General Public License version 3 (see the file LICENSE).
1661+
1662+import unittest
1663+
1664+from zope.testing.doctest import DocTestSuite
1665+
1666+import lp.soyuz.scripts.gina.handlers
1667+
1668+
1669+def test_suite():
1670+ suite = unittest.TestSuite()
1671+ suite.addTest(DocTestSuite(lp.soyuz.scripts.gina.handlers))
1672+ return suite
1673+
1674
1675=== modified file 'lib/lp/translations/stories/distroseries/xx-distroseries-templates.txt'
1676--- lib/lp/translations/stories/distroseries/xx-distroseries-templates.txt 2009-12-10 12:46:11 +0000
1677+++ lib/lp/translations/stories/distroseries/xx-distroseries-templates.txt 2009-12-14 08:43:39 +0000
1678@@ -1,5 +1,12 @@
1679-Templates view for DistroSeries
1680-===============================
1681+<<<<<<< TREE
1682+Templates view for DistroSeries
1683+===============================
1684+=======
1685+
1686+
1687+Templates view for DistroSeries
1688+===============================
1689+>>>>>>> MERGE-SOURCE
1690
1691 The +templates view for DistroSeries gives an overview of the translation
1692 templates in this series and provides easy access to the various subpages of
1693@@ -18,6 +25,7 @@
1694 >>> print user_browser.url
1695 http://translations.launchpad.dev/ubuntu/hoary/+templates
1696
1697+<<<<<<< TREE
1698
1699 The templates table
1700 -------------------
1701@@ -58,6 +66,47 @@
1702 ...
1703 mozilla pkgconf-mozilla 2005-05-06 Download
1704 ...
1705+=======
1706+The templates table
1707+-------------------
1708+
1709+Full template listing for a distribution series is reached by following
1710+a link from the distribution series translations page.
1711+
1712+ >>> anon_browser.open(
1713+ ... 'http://translations.launchpad.dev/ubuntu/hoary')
1714+ >>> anon_browser.getLink('full list of templates').click()
1715+
1716+Full listing of templates shows source package name, template name and
1717+the date of last update for this distribution series.
1718+
1719+ >>> table = find_tag_by_id(anon_browser.contents, 'templates_table')
1720+ >>> print extract_text(table)
1721+ Source package Template name Last update
1722+ evolution disabled-template 2007-01-05
1723+ evolution evolution-2.2 2005-05-06
1724+ evolution man 2006-08-14
1725+ mozilla pkgconf-mozilla 2005-05-06
1726+ pmount man 2006-08-14
1727+ pmount pmount 2005-05-06
1728+
1729+
1730+Logged-in users will see a link from distro series
1731+ >>> user_browser.open(
1732+ ... 'http://translations.launchpad.dev/ubuntu/hoary')
1733+ >>> user_browser.getLink('full list of templates').click()
1734+
1735+Logged-in users can also choose to download all translations for each
1736+of the templates.
1737+
1738+ >>> table = find_tag_by_id(user_browser.contents, 'templates_table')
1739+ >>> print extract_text(table)
1740+ Source package Template name Last update Actions
1741+ evolution disabled-template 2007-01-05 Download
1742+ ...
1743+ mozilla pkgconf-mozilla 2005-05-06 Download
1744+ ...
1745+>>>>>>> MERGE-SOURCE
1746
1747 Administrator can see all editing options.
1748
1749@@ -69,6 +118,7 @@
1750
1751 >>> table = find_tag_by_id(admin_browser.contents, 'templates_table')
1752 >>> print extract_text(table)
1753+<<<<<<< TREE
1754 Source package Template name Last update Actions
1755 evolution disabled-template (inactive) 2007-01-05 Edit Upload Download Administer
1756 evolution evolution-2.2 2005-05-06 Edit Upload Download Administer
1757@@ -137,6 +187,19 @@
1758
1759 Links to the templates
1760 ----------------------
1761+=======
1762+ Source package Template name Last update Actions
1763+ evolution disabled-template 2007-01-05 Edit Upload Download Administer
1764+ evolution evolution-2.2 2005-05-06 Edit Upload Download Administer
1765+ evolution man 2006-08-14 Edit Upload Download Administer
1766+ mozilla pkgconf-mozilla 2005-05-06 Edit Upload Download Administer
1767+ pmount man 2006-08-14 Edit Upload Download Administer
1768+ pmount pmount 2005-05-06 Edit Upload Download Administer
1769+
1770+
1771+Links to the templates
1772+----------------------
1773+>>>>>>> MERGE-SOURCE
1774
1775 Clicking on a template name will take the user to that template's overview
1776 page.
1777
1778=== modified file 'lib/lp/translations/templates/object-templates.pt'
1779--- lib/lp/translations/templates/object-templates.pt 2009-12-10 12:46:11 +0000
1780+++ lib/lp/translations/templates/object-templates.pt 2009-12-14 08:43:37 +0000
1781@@ -82,6 +82,7 @@
1782 </tr>
1783 </thead>
1784 <tbody>
1785+<<<<<<< TREE
1786 <tal:templates repeat="template view/iter_templates">
1787 <tal:not-current condition="not: template/iscurrent">
1788 <tal:admin condition="template/required:launchpad.TranslationsAdmin">
1789@@ -133,6 +134,49 @@
1790 </div>
1791 </td>
1792 </tr>
1793+=======
1794+ <tr tal:repeat="template view/iter_templates" class="template_row">
1795+ <td tal:condition="view/is_distroseries"
1796+ tal:content="template/sourcepackagename/name"
1797+ class="sourcepackage_column">Source package
1798+ </td>
1799+ <td class="template_column"><a tal:attributes="href template/fmt:url"
1800+ tal:content="template/name">Template name</a></td>
1801+ <td class="lastupdate_column">
1802+ <span class="sortkey"
1803+ tal:condition="template/date_last_updated"
1804+ tal:content="template/date_last_updated/fmt:datetime">
1805+ time sort key
1806+ </span>
1807+ <span class="lastupdate_column"
1808+ tal:condition="template/date_last_updated"
1809+ tal:attributes="
1810+ title template/date_last_updated/fmt:datetime"
1811+ tal:content="
1812+ template/date_last_updated/fmt:approximatedate"
1813+ >
1814+ 2009-09-23
1815+ </span>
1816+ </td>
1817+ <td class="actions_column"
1818+ tal:condition="context/required:launchpad.AnyPerson">
1819+ <div class="template_links">
1820+ <tal:maintainer condition="template/required:launchpad.Edit">
1821+ <a tal:attributes="href string:${template/fmt:url}/+edit;
1822+ title string:Edit ${template/name}'s details">
1823+ <img src="/@@/edit" />&nbsp;Edit</a>
1824+ <a tal:attributes="href string:${template/fmt:url}/+upload;
1825+ title string:Upload translations to ${template/name}">
1826+ <img src="/@@/add" />&nbsp;Upload</a>
1827+ </tal:maintainer>
1828+ <a tal:attributes="href string:${template/fmt:url}/+export;
1829+ title string:Download translations from ${template/name}">
1830+ <img src="/@@/download" />&nbsp;Download</a>
1831+ <tal:admin condition="template/required:launchpad.Admin">
1832+ <a tal:attributes="href string:${template/fmt:url}/+admin;
1833+ title string:Administer ${template/name}">
1834+ <img src="/@@/edit" />&nbsp;Administer</a>
1835+>>>>>>> MERGE-SOURCE
1836 </tal:admin>
1837 </tal:not-current>
1838 <tal:current condition="template/iscurrent">

Subscribers

People subscribed via source and target branches

to status/vote changes: