diff --git a/stackalytics/dashboard/helpers.py b/stackalytics/dashboard/helpers.py index 590ed214e..1b886b2ab 100644 --- a/stackalytics/dashboard/helpers.py +++ b/stackalytics/dashboard/helpers.py @@ -95,6 +95,10 @@ def extend_record(record): record['mention_date']) record['blueprint_link'] = make_blueprint_link(record['module'], record['name']) + elif record['record_type'] in ['bugr', 'bugf']: + record['number'] = record['web_link'].split('/')[-1] + record['title'] = filter_bug_title(record['title']) + record['status_class'] = re.sub('\s+', '', record['status']) return record @@ -307,3 +311,7 @@ def make_page_title(project_type_inst, release, module_inst, company, if release != 'all': s += ' during OpenStack %s release' % release.capitalize() return s + + +def filter_bug_title(title): + return re.sub(r'^(?:Bug #\d+.+:\s+)"(.*)"', r'\1', title) diff --git a/stackalytics/dashboard/templates/_macros/activity_log.html b/stackalytics/dashboard/templates/_macros/activity_log.html index 88fb059b3..34ce629e7 100644 --- a/stackalytics/dashboard/templates/_macros/activity_log.html +++ b/stackalytics/dashboard/templates/_macros/activity_log.html @@ -151,9 +151,9 @@ show_record_type=True, show_user_gravatar=True, gravatar_size=32, show_all=True)
Mention count: ${mention_count}, last mention on ${mention_date_str}
{%/if%} {%elif ((record_type == "bugf") || (record_type == "bugr")) %} -
“${title}”
-
Status: ${status}
-
Importance: ${importance}
+
Bug “${title}” (${number})
+
Status: ${status}
+
Importance: ${importance}
{%elif record_type == "ci_vote" %}
New CI vote in change request ${review_number} {%if is_merged %}(Merged){%/if%}
diff --git a/stackalytics/processor/bps.py b/stackalytics/processor/bps.py index 07a34278b..92e27c8ed 100644 --- a/stackalytics/processor/bps.py +++ b/stackalytics/processor/bps.py @@ -41,8 +41,18 @@ def log(repo, modified_since): for record_draft in launchpad_utils.lp_bug_generator(module, modified_since): + # record_draft can be a bug or bug target and + # in the latter case it can be from a different module + bug_target = record_draft['bug_target_name'].split('/') + target_module = bug_target[0] + if target_module != module: + continue # ignore foreigners + record = {} + if len(bug_target) == 2: + record['release'] = bug_target[1] # treat target as release + for field in LINK_FIELDS: link = record_draft[field + '_link'] if link: @@ -58,7 +68,7 @@ def log(repo, modified_since): bug_id = _get_bug_id(record_draft['web_link']) record['module'] = module - record['id'] = utils.get_bug_id(module, bug_id) + record['id'] = utils.make_bug_id(bug_id, module, record.get('release')) LOG.debug('New bug: %s', record) yield record diff --git a/stackalytics/processor/utils.py b/stackalytics/processor/utils.py index 63bfcf6f9..1a2315d71 100644 --- a/stackalytics/processor/utils.py +++ b/stackalytics/processor/utils.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import calendar import cgi import datetime import gzip @@ -69,7 +70,7 @@ def member_date_to_timestamp(d): def iso8601_to_timestamp(s): - return int(time.mktime(iso8601.parse_date(s).timetuple())) + return calendar.timegm(iso8601.parse_date(s).utctimetuple()) def timestamp_to_date(timestamp): @@ -232,8 +233,11 @@ def get_blueprint_id(module, name): return module + ':' + name -def get_bug_id(module, bug_id): - return module + '/' + bug_id +def make_bug_id(bug_id, module, release=None): + if release: + return '/'.join([module, release, bug_id]) + else: + return '/'.join([module, bug_id]) def get_patch_id(review_id, patch_number): diff --git a/tests/unit/test_bps.py b/tests/unit/test_bps.py new file mode 100644 index 000000000..cf487bd81 --- /dev/null +++ b/tests/unit/test_bps.py @@ -0,0 +1,221 @@ +# Copyright (c) 2015 Mirantis Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import mock +import testtools + +from stackalytics.processor import bps + + +BUG = json.loads(""" +{ + "date_closed": "2015-06-02T17:31:05.820479+00:00", + "date_assigned": "2015-06-02T17:31:44.957976+00:00", + "title": "Bug #1458945 in Sahara: \\\"Use graduated oslo.policy\\\"", + "bug_link": "https://api.launchpad.net/devel/bugs/1458945", + "bug_watch_link": null, + "milestone_link": null, + "date_left_closed": null, + "date_fix_committed": "2015-06-02T17:31:05.820479+00:00", + "date_fix_released": "2015-06-02T17:31:05.820479+00:00", + "date_in_progress": "2015-06-02T17:31:05.820479+00:00", + "resource_type_link": "https://api.launchpad.net/devel/#bug_task", + "status": "Fix Released", + "bug_target_name": "sahara", + "importance": "Medium", + "assignee_link": "https://api.launchpad.net/devel/~slukjanov", + "date_triaged": "2015-06-02T17:31:05.820479+00:00", + "self_link": "https://api.launchpad.net/devel/sahara/+bug/1458945", + "target_link": "https://api.launchpad.net/devel/sahara", + "bug_target_display_name": "Sahara", + "related_tasks_collection_link": + "https://api.launchpad.net/devel/sahara/+bug/1458945/related_tasks", + "date_confirmed": "2015-06-02T17:31:05.820479+00:00", + "date_left_new": "2015-06-02T17:31:05.820479+00:00", + "web_link": "https://bugs.launchpad.net/sahara/+bug/1458945", + "owner_link": "https://api.launchpad.net/devel/~samueldmq", + "date_created": "2015-06-02T13:35:54.101235+00:00", + "date_incomplete": null, + "is_complete": true +} +""") + +ANOTHER_MILESTONE_BUG = json.loads(""" +{ + "date_closed": "2015-06-02T17:31:05.820479+00:00", + "date_assigned": "2015-06-02T17:31:44.957976+00:00", + "title": "Bug #1458945 in Sahara Kilo: \\\"Use graduated oslo.policy\\\"", + "bug_link": "https://api.launchpad.net/devel/bugs/1458945", + "bug_watch_link": null, + "milestone_link": null, + "date_left_closed": null, + "date_fix_committed": "2015-06-02T17:31:05.820479+00:00", + "date_fix_released": "2015-06-02T17:31:05.820479+00:00", + "date_in_progress": "2015-06-02T17:31:05.820479+00:00", + "resource_type_link": "https://api.launchpad.net/devel/#bug_task", + "status": "Fix Released", + "bug_target_name": "sahara/kilo", + "importance": "Medium", + "assignee_link": "https://api.launchpad.net/devel/~slukjanov", + "date_triaged": "2015-06-02T17:31:05.820479+00:00", + "self_link": "https://api.launchpad.net/devel/sahara/kilo/+bug/1458945", + "target_link": "https://api.launchpad.net/devel/sahara/kilo", + "bug_target_display_name": "Sahara Kilo", + "related_tasks_collection_link": + "https://api.launchpad.net/devel/sahara/kilo/+bug/1458945/related_tasks", + "date_confirmed": "2015-06-02T17:31:05.820479+00:00", + "date_left_new": "2015-06-02T17:31:05.820479+00:00", + "web_link": "https://bugs.launchpad.net/sahara/kilo/+bug/1458945", + "owner_link": "https://api.launchpad.net/devel/~samueldmq", + "date_created": "2015-06-02T13:35:54.101235+00:00", + "date_incomplete": null, + "is_complete": true +} +""") + +LINKED_BUG = json.loads(""" +{ + "date_closed": "2015-06-24T20:59:57.982386+00:00", + "date_assigned": "2015-06-18T06:46:03.741208+00:00", + "title": "Bug #1458945 in Barbican: \\\"Use graduated oslo.policy\\\"", + "bug_link": "https://api.launchpad.net/devel/bugs/1458945", + "bug_watch_link": null, + "milestone_link": + "https://api.launchpad.net/devel/barbican/+milestone/liberty-1", + "date_left_closed": null, + "date_fix_committed": "2015-06-18T06:45:39.997949+00:00", + "date_fix_released": "2015-06-24T20:59:57.982386+00:00", + "date_in_progress": "2015-06-18T06:45:39.997949+00:00", + "resource_type_link": "https://api.launchpad.net/devel/#bug_task", + "status": "Fix Released", + "bug_target_name": "barbican", + "importance": "Medium", + "assignee_link": "https://api.launchpad.net/devel/~juan-osorio-robles", + "date_triaged": "2015-06-18T06:45:39.997949+00:00", + "self_link": "https://api.launchpad.net/devel/barbican/+bug/1458945", + "target_link": "https://api.launchpad.net/devel/barbican", + "bug_target_display_name": "Barbican", + "related_tasks_collection_link": + "https://api.launchpad.net/devel/barbican/+bug/1458945/related_tasks", + "date_confirmed": "2015-06-18T06:45:39.997949+00:00", + "date_left_new": "2015-06-18T06:45:39.997949+00:00", + "web_link": "https://bugs.launchpad.net/barbican/+bug/1458945", + "owner_link": "https://api.launchpad.net/devel/~samueldmq", + "date_created": "2015-05-26T17:47:32.438795+00:00", + "date_incomplete": null, + "is_complete": true +} +""") + + +class TestBps(testtools.TestCase): + def setUp(self): + super(TestBps, self).setUp() + p_module_exists = mock.patch( + 'stackalytics.processor.launchpad_utils.lp_module_exists') + m_module_exists = p_module_exists.start() + m_module_exists.return_value = True + + @mock.patch('stackalytics.processor.launchpad_utils.lp_bug_generator') + def test_log(self, lp_bug_generator): + repo = { + 'module': 'sahara' + } + modified_since = 1234567890 + lp_bug_generator.return_value = iter([BUG]) + + expected = [{ + 'assignee': 'slukjanov', + 'date_created': 1433252154, + 'date_fix_committed': 1433266265, + 'id': 'sahara/1458945', + 'importance': 'Medium', + 'module': 'sahara', + 'owner': 'samueldmq', + 'status': 'Fix Released', + 'title': 'Bug #1458945 in Sahara: "Use graduated oslo.policy"', + 'web_link': 'https://bugs.launchpad.net/sahara/+bug/1458945' + }] + + actual = list(bps.log(repo, modified_since)) + + self.assertEqual(expected, actual) + + @mock.patch('stackalytics.processor.launchpad_utils.lp_bug_generator') + def test_log_additional_module(self, lp_bug_generator): + # bug linked to another project should not appear + repo = { + 'module': 'sahara' + } + modified_since = 1234567890 + lp_bug_generator.return_value = iter([BUG, LINKED_BUG]) + + expected = [{ + 'assignee': 'slukjanov', + 'date_created': 1433252154, + 'date_fix_committed': 1433266265, + 'id': 'sahara/1458945', + 'importance': 'Medium', + 'module': 'sahara', + 'owner': 'samueldmq', + 'status': 'Fix Released', + 'title': 'Bug #1458945 in Sahara: "Use graduated oslo.policy"', + 'web_link': 'https://bugs.launchpad.net/sahara/+bug/1458945' + }] + + actual = list(bps.log(repo, modified_since)) + + self.assertEqual(expected, actual) + + @mock.patch('stackalytics.processor.launchpad_utils.lp_bug_generator') + def test_log_additional_milestone(self, lp_bug_generator): + # bug linked to different milestone should be mapped to the release + repo = { + 'module': 'sahara' + } + modified_since = 1234567890 + lp_bug_generator.return_value = iter([BUG, ANOTHER_MILESTONE_BUG]) + + expected = [{ + 'assignee': 'slukjanov', + 'date_created': 1433252154, + 'date_fix_committed': 1433266265, + 'id': 'sahara/1458945', + 'importance': 'Medium', + 'module': 'sahara', + 'owner': 'samueldmq', + 'status': 'Fix Released', + 'title': 'Bug #1458945 in Sahara: "Use graduated oslo.policy"', + 'web_link': 'https://bugs.launchpad.net/sahara/+bug/1458945' + }, { + 'assignee': 'slukjanov', + 'date_created': 1433252154, + 'date_fix_committed': 1433266265, + 'id': 'sahara/kilo/1458945', + 'importance': 'Medium', + 'module': 'sahara', + 'release': 'kilo', + 'owner': 'samueldmq', + 'status': 'Fix Released', + 'title': 'Bug #1458945 in Sahara Kilo: ' + '"Use graduated oslo.policy"', + 'web_link': 'https://bugs.launchpad.net/sahara/kilo/+bug/1458945' + + }] + + actual = list(bps.log(repo, modified_since)) + + self.assertEqual(expected, actual) diff --git a/tests/unit/test_web_utils.py b/tests/unit/test_web_utils.py index 5f4e96c8d..c8d2cfb6f 100644 --- a/tests/unit/test_web_utils.py +++ b/tests/unit/test_web_utils.py @@ -150,3 +150,12 @@ Implements Blueprint ''' + ( get_default.side_effect = make({'param': 'foo'}) self.assertEqual(['foo'], parameters.get_parameter({}, 'param')) self.assertEqual([], parameters.get_parameter({}, 'other')) + + def test_filter_bug_title(self): + bug_title = ('Bug #1459454 in Barbican: "Stored key certificate ' + 'order does not set PK on generated container"') + expected = ('Stored key certificate order does not set PK ' + 'on generated container') + + actual = helpers.filter_bug_title(bug_title) + self.assertEqual(expected, actual)