launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #06045
[Merge] lp:~bac/launchpad/904335-create-milestones-tags into lp:launchpad
Brad Crittenden has proposed merging lp:~bac/launchpad/904335-create-milestones-tags into lp:launchpad with lp:~bac/launchpad/904335-export-tags as a prerequisite.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #904335 in Launchpad itself: "Project groups need a way to aggregate project milestones"
https://bugs.launchpad.net/launchpad/+bug/904335
For more details, see:
https://code.launchpad.net/~bac/launchpad/904335-create-milestones-tags/+merge/87669
Add fields for include milestone tags when a new milestone is created.
Tests:
bin/test -vv lp.registry.javascript.tests.test_milestone_creation
--
https://code.launchpad.net/~bac/launchpad/904335-create-milestones-tags/+merge/87669
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~bac/launchpad/904335-create-milestones-tags into lp:launchpad.
=== modified file 'lib/lp/bugs/interfaces/bugtask.py'
--- lib/lp/bugs/interfaces/bugtask.py 2012-01-05 19:12:36 +0000
+++ lib/lp/bugs/interfaces/bugtask.py 2012-01-05 19:12:40 +0000
@@ -918,8 +918,7 @@
"""
def userHasBugSupervisorPrivileges(user):
- """Is the user a privledged one, allowed to changed details on a
- bug?
+ """Is the user privileged and allowed to change details on a bug?
:return: A boolean.
"""
=== modified file 'lib/lp/bugs/javascript/bug_tags_entry.js'
--- lib/lp/bugs/javascript/bug_tags_entry.js 2011-08-09 14:18:02 +0000
+++ lib/lp/bugs/javascript/bug_tags_entry.js 2012-01-05 19:12:40 +0000
@@ -46,9 +46,9 @@
tag_list_span.all(A).each(function(anchor) {
tags.push(anchor.get(INNER_HTML));
});
-
+
var tag_list = tags.join(' ');
- /* If there are tags then add a space to the end of the string so the user
+ /* If there are tags then add a space to the end of the string so the user
doesn't have to type one. */
if (tag_list != "") {
tag_list += ' ';
@@ -61,6 +61,13 @@
*/
var base_url = window.location.href.split('/+bug')[0] + '/+bugs?field.tag=';
+namespace.parse_tags = function(tag_string) {
+ var tags = Y.Array(
+ Y.Lang.trim(tag_string).split(new RegExp('\\s+'))).filter(
+ function(elem) { return elem !== ''; });
+ return tags;
+};
+
/**
* Save the currently entered tags and switch inline editing off.
*
@@ -68,9 +75,7 @@
*/
var save_tags = function() {
var lp_client = new Y.lp.client.Launchpad();
- var tags = Y.Array(
- Y.Lang.trim(tag_input.get(VALUE)).split(new RegExp('\\s+'))).filter(
- function(elem) { return elem !== ''; });
+ var tags = namespace.parse_tags(tag_input.get(VALUE));
var bug = new Y.lp.client.Entry(
lp_client, LP.cache[BUG], LP.cache[BUG].self_link);
bug.removeAttr('http_etag');
@@ -243,4 +248,3 @@
}, "0.1", {"requires": ["base", "io-base", "node", "substitute", "node-menunav",
"lazr.base", "lp.anim", "lazr.autocomplete",
"lp.client"]});
-
=== added file 'lib/lp/bugs/javascript/tests/test_bug_tags_entry.html'
--- lib/lp/bugs/javascript/tests/test_bug_tags_entry.html 1970-01-01 00:00:00 +0000
+++ lib/lp/bugs/javascript/tests/test_bug_tags_entry.html 2012-01-05 19:12:40 +0000
@@ -0,0 +1,25 @@
+<html>
+ <head>
+ <title>Launchpad bug tags entry</title>
+ <!-- YUI and test setup -->
+ <script type="text/javascript"
+ src="../../../../canonical/launchpad/icing/yui/yui/yui.js">
+ </script>
+ <link rel="stylesheet" href="../../../app/javascript/testing/test.css" />
+ <script type="text/javascript"
+ src="../../../app/javascript/testing/testrunner.js"></script>
+
+ <!-- Other dependencies -->
+ <script type="text/javascript" src="../../../app/javascript/lp.js"></script>
+ <script type="text/javascript" src="../../../app/javascript/client.js"></script>
+ <script type="text/javascript" src="../../../app/javascript/testing/mockio.js"></script>
+
+ <!-- The module under test -->
+ <script type="text/javascript" src="../bug_tags_entry.js"></script>
+
+ <!-- The test suite -->
+ <script type="text/javascript" src="test_bug_tags_entry.js"></script>
+ </head>
+ <body class="yui3-skin-sam">
+ </body>
+</html>
=== added file 'lib/lp/bugs/javascript/tests/test_bug_tags_entry.js'
--- lib/lp/bugs/javascript/tests/test_bug_tags_entry.js 1970-01-01 00:00:00 +0000
+++ lib/lp/bugs/javascript/tests/test_bug_tags_entry.js 2012-01-05 19:12:40 +0000
@@ -0,0 +1,44 @@
+YUI().use('lp.testing.runner', 'lp.testing.mockio', 'test', 'console',
+ 'lp.client', 'node-event-simulate', 'lp.bugs.bug_tags_entry',
+ function(Y) {
+
+ var module = Y.lp.bugs.bug_tags_entry;
+ var suite = new Y.Test.Suite("Bug tags entry Tests");
+
+ suite.add(new Y.Test.Case({
+ name: 'Tags parsing',
+
+ test_empty_string: function() {
+ var tag_string = '';
+ var results = module.parse_tags(tag_string);
+ Y.ArrayAssert.itemsAreEqual([], results);
+ },
+
+ test_one_item: function() {
+ var tag_string = 'cow';
+ var results = module.parse_tags(tag_string);
+ Y.ArrayAssert.itemsAreEqual(['cow'], results);
+ },
+
+ test_two_items: function() {
+ var tag_string = 'cow pig';
+ var results = module.parse_tags(tag_string);
+ Y.ArrayAssert.itemsAreEqual(['cow', 'pig'], results);
+ },
+
+ test_spaces: function() {
+ var tag_string = ' ';
+ var results = module.parse_tags(tag_string);
+ Y.ArrayAssert.itemsAreEqual([], results);
+ },
+
+ test_items_with_spaces: function() {
+ var tag_string = ' cow pig chicken ';
+ var results = module.parse_tags(tag_string);
+ Y.ArrayAssert.itemsAreEqual(['cow', 'pig', 'chicken'], results);
+ }
+
+ }));
+
+ Y.lp.testing.Runner.run(suite);
+});
=== modified file 'lib/lp/registry/browser/milestone.py'
--- lib/lp/registry/browser/milestone.py 2012-01-05 19:12:36 +0000
+++ lib/lp/registry/browser/milestone.py 2012-01-05 19:12:40 +0000
@@ -31,7 +31,10 @@
implements,
Interface,
)
-from zope.schema import Choice, TextLine
+from zope.schema import (
+ Choice,
+ TextLine,
+ )
from lp import _
from lp.app.browser.launchpadform import (
@@ -428,14 +431,34 @@
custom_widget('dateexpected', DateWidget)
+ def extendFields(self):
+ """See `LaunchpadFormView`.
+
+ Add a text-entry widget for milestone tags since there is not property
+ on the interface.
+ """
+ tag_entry = TextLine(
+ __name__='tags',
+ title=u'Tags',
+ required=False)
+ self.form_fields += form.Fields(
+ tag_entry, render_context=self.render_context)
+ # Make an instance attribute to avoid mutating the class attribute.
+ self.field_names = self.field_names[:]
+ # Insert the tags field before the summary.
+ self.field_names[3:3] = [tag_entry.__name__]
+
@action(_('Register Milestone'), name='register')
def register_action(self, action, data):
"""Use the newMilestone method on the context to make a milestone."""
- self.context.newMilestone(
+ milestone = self.context.newMilestone(
name=data.get('name'),
code_name=data.get('code_name'),
dateexpected=data.get('dateexpected'),
summary=data.get('summary'))
+ tags = data.get('tags')
+ if tags:
+ milestone.setTags(tags.split(), self.user)
self.next_url = canonical_url(self.context)
@property
=== modified file 'lib/lp/registry/browser/tests/milestone-views.txt'
--- lib/lp/registry/browser/tests/milestone-views.txt 2011-12-30 08:13:14 +0000
+++ lib/lp/registry/browser/tests/milestone-views.txt 2012-01-05 19:12:40 +0000
@@ -508,12 +508,12 @@
>>> owner = firefox.owner
>>> login_person(owner)
- >>> view = create_view(firefox_1_0, '+addmilestone')
+ >>> view = create_initialized_view(firefox_1_0, '+addmilestone')
>>> print view.label
Register a new milestone
>>> view.field_names
- ['name', 'code_name', 'dateexpected', 'summary']
+ ['name', 'code_name', 'dateexpected', 'tags', 'summary']
The view provides an action_url and cancel_url properties that form
submitting the form or aborting the action.
=== modified file 'lib/lp/registry/browser/tests/test_milestone.py'
--- lib/lp/registry/browser/tests/test_milestone.py 2012-01-05 19:12:36 +0000
+++ lib/lp/registry/browser/tests/test_milestone.py 2012-01-05 19:12:40 +0000
@@ -65,7 +65,7 @@
}
view = create_initialized_view(
self.series, '+addmilestone', form=form)
- # It's important to make sure no errors occured, but
+ # It's important to make sure no errors occured,
# but also confirm that the milestone was created.
self.assertEqual([], view.errors)
self.assertEqual('1.1', self.product.milestones[0].name)
@@ -84,6 +84,19 @@
"like YYYY-MM-DD format. The year must be after 1900.")
self.assertEqual(expected_msg, error_msg)
+ def test_add_milestone_with_tags(self):
+ tags = u'zed alpha'
+ form = {
+ 'field.name': '1.1',
+ 'field.tags': tags,
+ 'field.actions.register': 'Register Milestone',
+ }
+ view = create_initialized_view(
+ self.series, '+addmilestone', form=form)
+ self.assertEqual([], view.errors)
+ expected = sorted(tags.split())
+ self.assertEqual(expected, list(self.product.milestones[0].getTags()))
+
class TestMilestoneMemcache(MemcacheTestCase):
=== modified file 'lib/lp/registry/interfaces/milestone.py'
--- lib/lp/registry/interfaces/milestone.py 2012-01-05 19:12:36 +0000
+++ lib/lp/registry/interfaces/milestone.py 2012-01-05 19:12:40 +0000
@@ -236,7 +236,6 @@
description=_("Space-separated keywords for classifying "
"this milestone."),
value_type=TextLine()))
-
@call_with(user=REQUEST_USER)
@export_write_operation()
@operation_for_version('devel')
=== added file 'lib/lp/registry/javascript/__init__.py'
--- lib/lp/registry/javascript/__init__.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/javascript/__init__.py 2012-01-05 19:12:40 +0000
@@ -0,0 +1,1 @@
+"""Tests for lp.registry.javascript.tests module."""
=== modified file 'lib/lp/registry/javascript/milestoneoverlay.js'
--- lib/lp/registry/javascript/milestoneoverlay.js 2011-08-09 14:18:02 +0000
+++ lib/lp/registry/javascript/milestoneoverlay.js 2012-01-05 19:12:40 +0000
@@ -1,4 +1,4 @@
-/* Copyright 2009 Canonical Ltd. This software is licensed under the
+/* Copyright 2009-2011 Canonical Ltd. This software is licensed under the
* GNU Affero General Public License version 3 (see the file LICENSE).
*
* A milestone form overlay that can create a milestone within any page.
@@ -14,6 +14,7 @@
var milestone_form_uri;
var series_uri;
var next_step;
+ var client_sync = false;
var save_new_milestone = function(data) {
@@ -33,14 +34,43 @@
milestone_form.hide();
// Reset the HTML form inside the widget.
milestone_form.get('contentBox').one('form').reset();
- next_step(parameters);
- };
-
- var client = new Y.lp.client.Launchpad();
+ if (Y.Lang.isValue(next_step)) {
+ next_step(parameters);
+ }
+ };
+
+ var set_milestone_tags = function(milestone) {
+ var tagstring = data['field.tags'][0].toLowerCase();
+ var tags = Y.lp.bugs.bug_tags_entry.parse_tags(tagstring);
+
+ if (tags.length === 0) {
+ return finish_new_milestone();
+ }
+
+ var parameters = {
+ tags: tags
+ };
+ milestone.named_post('setTags', {
+ parameters: parameters,
+ on: {
+ success: finish_new_milestone,
+ failure: function (ignore, response, args) {
+ var error_box = Y.one('#milestone-error');
+ var error_message = '<strong>' + response.statusText +
+ '</strong><p>' +
+ response.responseText +
+ '</p>';
+ milestone_form.showError(error_message);
+ }
+ }
+ });
+ };
+
+ var client = new Y.lp.client.Launchpad({sync: client_sync});
client.named_post(series_uri, 'newMilestone', {
parameters: parameters,
on: {
- success: finish_new_milestone,
+ success: set_milestone_tags,
failure: function (ignore, response, args) {
var error_box = Y.one('#milestone-error');
var error_message = '<strong>' + response.statusText +
@@ -52,7 +82,7 @@
}
});
};
-
+ module.save_new_milestone = save_new_milestone;
var setup_milestone_form = function () {
var form_submit_button = Y.Node.create(
@@ -70,6 +100,7 @@
Y.lp.app.calendar.add_calendar_widgets();
milestone_form.show();
};
+ module.setup_milestone_form = setup_milestone_form;
var show_milestone_form = function(e) {
e.preventDefault();
@@ -82,6 +113,29 @@
}
};
+ var configure = function(config) {
+ if (config === undefined) {
+ throw new Error(
+ "Missing attach_widget config for milestoneoverlay.");
+ }
+ if (config.milestone_form_uri === undefined ||
+ config.series_uri === undefined) {
+ throw new Error(
+ "attach_widget config for milestoneoverlay has " +
+ "undefined properties.");
+ }
+ milestone_form_uri = config.milestone_form_uri;
+ series_uri = config.series_uri;
+ // For testing purposes next_step may be undefined.
+ if (Y.Lang.isValue(config.next_step)) {
+ next_step = config.next_step;
+ }
+ if (Y.Lang.isValue(config.sync)) {
+ client_sync = config.sync;
+ }
+ };
+ module.configure = configure;
+
/**
* Attaches a milestone form overlay widget to an element.
*
@@ -100,20 +154,7 @@
if (Y.UA.ie) {
return;
}
- if (config === undefined) {
- throw new Error(
- "Missing attach_widget config for milestoneoverlay.");
- }
- if (config.milestone_form_uri === undefined ||
- config.series_uri === undefined ||
- config.next_step === undefined) {
- throw new Error(
- "attach_widget config for milestoneoverlay has " +
- "undefined properties.");
- }
- milestone_form_uri = config.milestone_form_uri;
- series_uri = config.series_uri;
- next_step = config.next_step;
+ configure(config);
config.activate_node.on('click', show_milestone_form);
};
@@ -123,5 +164,6 @@
"lp.anim",
"lazr.formoverlay",
"lp.app.calendar",
- "lp.client"
+ "lp.client",
+ "lp.bugs.bug_tags_entry"
]});
=== added file 'lib/lp/registry/javascript/tests/__init__.py'
--- lib/lp/registry/javascript/tests/__init__.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/javascript/tests/__init__.py 2012-01-05 19:12:40 +0000
@@ -0,0 +1,1 @@
+"""Tests for lp.registry.javascript.tests module."""
=== added file 'lib/lp/registry/javascript/tests/test_milestone_creation.js'
--- lib/lp/registry/javascript/tests/test_milestone_creation.js 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/javascript/tests/test_milestone_creation.js 2012-01-05 19:12:40 +0000
@@ -0,0 +1,172 @@
+YUI({
+ base: '/+icing/yui/',
+ filter: 'raw', combine: false, fetchCSS: false
+}).use('test',
+ 'lp.client',
+ 'lp.testing.serverfixture',
+ 'lp.registry.milestonetable',
+ 'lp.registry.milestoneoverlay',
+ function(Y) {
+
+/**
+ * Integration tests for milestoneoverlay.
+ */
+var suite = new Y.Test.Suite("lp.registry.javascript.milestoneoverlay Tests");
+var serverfixture = Y.lp.testing.serverfixture;
+// Define the module-under-test.
+var the_module = Y.lp.registry.milestoneoverlay;
+
+var makeTestConfig = function(config) {
+ if (Y.Lang.isUndefined(config)) {
+ config = {};
+ }
+ config.on = Y.merge(
+ {
+ success: function(result) {
+ config.successful = true;
+ config.result = result;
+ },
+ failure: function(tid, response, args) {
+ config.successful = false;
+ config.result = {tid: tid, response: response, args: args};
+ }
+ },
+ config.on);
+ return config;
+};
+
+/**
+ * Test milestoneoverlay interaction via the API, such as creating
+ * milestones and adding tags.
+ */
+suite.add(new Y.Test.Case({
+ name: 'Milestone creation tests',
+
+ tearDown: function() {
+ // Always do this.
+ serverfixture.teardown(this);
+ },
+
+ test_configure: function() {
+ // Ensure configuring the module works as it will be needed in
+ // subsequent tests.
+ var config = {
+ milestone_form_uri: 'a',
+ series_uri: 'b',
+ next_step: 'c'
+ };
+ the_module.configure(config);
+ },
+
+ test_milestone_test_fixture_setup: function() {
+ // Setup the fixture, retrieving the objects we need for the test.
+ var data = serverfixture.setup(this, 'setup');
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig();
+ var product = new Y.lp.client.Entry(
+ client, data.product, data.product.self_link);
+ var product_name = product.get('name');
+ Y.Assert.areEqual('my-test-project', product_name);
+ },
+
+ test_milestone_creation_no_tags: function() {
+ // Setup the fixture, retrieving the objects we need for the test.
+ var data = serverfixture.setup(this, 'setup');
+
+ // Initialize the milestoneoverlay module.
+ var milestone_table = Y.lp.registry.milestonetable;
+ var config = {
+ milestone_form_uri: data.milestone_form_uri,
+ series_uri: data.series_uri,
+ //next_step: milestone_table.get_milestone_row,
+ sync: true
+ };
+ the_module.configure(config);
+ the_module.setup_milestone_form();
+
+ var milestone_name = 'new-milestone';
+ var code_name = 'new-codename';
+ var params = {
+ 'field.name': [milestone_name],
+ 'field.code_name': [code_name],
+ 'field.dateexpected': [''],
+ 'field.tags': [''],
+ 'field.summary': ['']
+ };
+
+ // Test the creation of the new milestone.
+ the_module.save_new_milestone(params);
+
+ // Verify the milestone was created.
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var product = new Y.lp.client.Entry(
+ client, data.product, data.product.self_link);
+ config = makeTestConfig({parameters: {name: milestone_name}});
+ // Get the new milestone.
+ product.named_get('getMilestone', config);
+ Y.Assert.isTrue(config.successful, 'Getting milestone failed');
+ var milestone = config.result;
+ Y.Assert.isInstanceOf(Y.lp.client.Entry, milestone);
+ Y.Assert.areEqual(milestone_name, milestone.get('name'));
+ Y.Assert.areEqual(code_name, milestone.get('code_name'));
+ // Ensure no tags are created.
+ config = makeTestConfig({parameters: {}});
+ milestone.named_get('getTags', config);
+ Y.Assert.isTrue(config.successful, 'call to getTags failed');
+ var expected = [];
+ Y.ArrayAssert.itemsAreEqual(expected, config.result);
+ },
+
+ test_milestone_creation_with_tags: function() {
+ // Setup the fixture, retrieving the objects we need for the test.
+ var data = serverfixture.setup(this, 'setup');
+
+ // Initialize the milestoneoverlay module.
+ var milestone_table = Y.lp.registry.milestonetable;
+ var config = {
+ milestone_form_uri: data.milestone_form_uri,
+ series_uri: data.series_uri,
+ //next_step: milestone_table.get_milestone_row,
+ sync: true
+ };
+ the_module.configure(config);
+ the_module.setup_milestone_form();
+
+ var milestone_name = 'new-milestone';
+ var code_name = 'new-codename';
+ var tags = ['zeta alpha beta'];
+ var params = {
+ 'field.name': [milestone_name],
+ 'field.code_name': [code_name],
+ 'field.dateexpected': [''],
+ 'field.tags': tags,
+ 'field.summary': ['']
+ };
+
+ // Test the creation of the new milestone.
+ the_module.save_new_milestone(params);
+
+ // Verify the milestone was created.
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var product = new Y.lp.client.Entry(
+ client, data.product, data.product.self_link);
+ config = makeTestConfig({parameters: {name: milestone_name}});
+ // Get the new milestone.
+ product.named_get('getMilestone', config);
+ Y.Assert.isTrue(config.successful, 'Getting milestone failed');
+ var milestone = config.result;
+ Y.Assert.isInstanceOf(Y.lp.client.Entry, milestone);
+ Y.Assert.areEqual(milestone_name, milestone.get('name'));
+ Y.Assert.areEqual(code_name, milestone.get('code_name'));
+ // Ensure the tags are created.
+ config = makeTestConfig({parameters: {}});
+ milestone.named_get('getTags', config);
+ Y.Assert.isTrue(config.successful, 'call to getTags failed');
+ var expected = ["alpha", "beta", "zeta"];
+ Y.ArrayAssert.itemsAreEqual(expected, config.result);
+ }
+}));
+
+// The last line is necessary. Include it.
+serverfixture.run(suite);
+});
=== added file 'lib/lp/registry/javascript/tests/test_milestone_creation.py'
--- lib/lp/registry/javascript/tests/test_milestone_creation.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/javascript/tests/test_milestone_creation.py 2012-01-05 19:12:40 +0000
@@ -0,0 +1,39 @@
+# Copyright 2011-2012 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Support for the lp.registry.javascript.milestoneoverlay YUIXHR tests.
+"""
+
+__metaclass__ = type
+__all__ = []
+
+from lp.services.webapp.publisher import canonical_url
+from lp.testing import person_logged_in
+from lp.testing.factory import LaunchpadObjectFactory
+from lp.testing.yuixhr import (
+ login_as_person,
+ make_suite,
+ setup,
+ )
+
+
+factory = LaunchpadObjectFactory()
+
+
+@setup
+def setup(request, data):
+ owner = factory.makePerson()
+ with person_logged_in(owner):
+ product = factory.makeProduct(name="my-test-project", owner=owner)
+ product_series = factory.makeProductSeries(
+ name="new-series", product=product)
+ data['product'] = product
+ data['series_uri'] = canonical_url(
+ product_series, path_only_if_possible=True)
+ data['milestone_form_uri'] = (
+ canonical_url(product_series) + '/+addmilestone/++form++')
+ login_as_person(owner)
+
+
+def test_suite():
+ return make_suite(__name__)
=== modified file 'lib/lp/registry/model/distroseries.py'
--- lib/lp/registry/model/distroseries.py 2012-01-03 05:05:39 +0000
+++ lib/lp/registry/model/distroseries.py 2012-01-05 19:12:40 +0000
@@ -1408,12 +1408,15 @@
return distroarchseries
def newMilestone(self, name, dateexpected=None, summary=None,
- code_name=None):
+ code_name=None, tags=None):
"""See `IDistroSeries`."""
- return Milestone(
+ milestone = Milestone(
name=name, code_name=code_name,
dateexpected=dateexpected, summary=summary,
distribution=self.distribution, distroseries=self)
+ if tags:
+ milestone.setTags(tags.split())
+ return milestone
def getLatestUploads(self):
"""See `IDistroSeries`."""
=== modified file 'lib/lp/registry/model/milestone.py'
--- lib/lp/registry/model/milestone.py 2012-01-05 19:12:36 +0000
+++ lib/lp/registry/model/milestone.py 2012-01-05 19:12:40 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
# pylint: disable-msg=E0611,W0212
=== modified file 'lib/lp/registry/model/product.py'
--- lib/lp/registry/model/product.py 2011-12-30 06:14:56 +0000
+++ lib/lp/registry/model/product.py 2012-01-05 19:12:40 +0000
@@ -926,10 +926,11 @@
def getMilestone(self, name):
"""See `IProduct`."""
- return Milestone.selectOne("""
+ results = Milestone.selectOne("""
product = %s AND
name = %s
""" % sqlvalues(self.id, name))
+ return results
def createBug(self, bug_params):
"""See `IBugTarget`."""
=== modified file 'lib/lp/registry/model/productseries.py'
--- lib/lp/registry/model/productseries.py 2011-12-30 06:14:56 +0000
+++ lib/lp/registry/model/productseries.py 2012-01-05 19:12:40 +0000
@@ -541,11 +541,14 @@
return history
def newMilestone(self, name, dateexpected=None, summary=None,
- code_name=None):
+ code_name=None, tags=None):
"""See IProductSeries."""
- return Milestone(
+ milestone = Milestone(
name=name, dateexpected=dateexpected, summary=summary,
product=self.product, productseries=self, code_name=code_name)
+ if tags:
+ milestone.setTags(tags.split())
+ return milestone
def getTemplatesCollection(self):
"""See `IHasTranslationTemplates`."""