16
# map a bug task status to a WI status (default: 'todo')
18
"Won't Fix": 'postponed',
19
'In Progress': 'inprogress',
20
'Fix Committed': 'inprogress',
21
'Fix Released': 'done',
16
27
# if this is None, data errors go to stderr; if this is a list of (spec_name,
17
28
# msg) tuples, data errors get collected here and mailed out at the end
227
238
data_error(bp_url, "\tComplexity line '%s' could not be parsed %s" % (line, ValueError))
229
240
def parse_blueprint_workitem(line, default_assignee, milestone,
230
blueprint_url, launchpad, allow_inprogress, result_list):
241
blueprint_url, launchpad, result_list):
231
242
'''Parse a work item line of a blueprint whiteboard.'''
233
244
line = line.replace('<br />', '').replace('</div>', '').replace('</p>',
249
260
if state == 'completed':
251
if state == 'inprogress' and not allow_inprogress:
253
262
if state in ('postpone', 'dropped', 'drop'):
254
263
state = 'postponed'
255
if state not in report_tools.valid_states and \
256
(state != 'inprogress' or not allow_inprogress):
264
if state not in report_tools.valid_states:
257
265
data_error(blueprint_url, 'invalid state "%s" for work item "%s"' %
279
287
result_list.append((desc, state, assignee, milestone))
281
289
def follow_blueprint_buglink(bugnum, default_assignee, blueprint_name,
282
launchpad, allow_inprogress, release, default_milestone, result_list):
290
launchpad, release, default_milestone, result_list):
283
291
'''Query launchpad for the information on a linked bug'''
285
293
bugnum = int(bugnum)
286
294
dbg('follow_blueprint_buglink(): processing bug %i (default milestone: %s)' % (bugnum, default_milestone))
287
295
bug = launchpad.bugs[bugnum]
288
296
for task in bug.bug_tasks:
289
if task.status == 'Invalid':
297
state = bug_wi_states.get(task.status, 'todo')
292
301
# only look at tasks on Ubuntu or Ubuntu packages; we don't want
324
333
if rtype != 'distribution':
325
334
desc += ' (%s)' % target.name
327
if task.status == 'Fix Released':
329
elif task.status == "Won't Fix":
331
elif allow_inprogress and task.status in ('In Progress', 'Fix Committed'):
336
336
if task.assignee:
337
337
assignee = task.assignee.name
357
def lp_import_blueprint_workitems(lp, db, bp_name, bp_url, contents, release,
357
def lp_import_blueprint_workitems(lp, db, bp_name, bp_url, contents, release):
359
358
'''Collect work items from a Launchpad blueprint.
361
360
This includes work items from the whiteboard as well as linked bugs.
385
384
cur.execute('SELECT team FROM teams WHERE name = ?', (spec_assignee,))
386
385
assignee_teams = [t[0] for t in cur]
388
allow_inprogress = False
389
for t in assignee_teams:
390
if t in inprogress_teams:
391
dbg(' %s allows in progress due to membership in team %s' % (spec_assignee, t))
392
allow_inprogress = True
395
387
cur.execute('SELECT name FROM milestones')
396
388
valid_milestones = [m[0] for m in cur]
419
411
if in_workitems_block:
420
412
dbg("\tworkitem (raw): '%s'" % (l.strip()))
421
413
parse_blueprint_workitem(l, spec_assignee, milestone or
422
spec_milestone, bp_url, lp, allow_inprogress, work_items)
414
spec_milestone, bp_url, lp, work_items)
425
417
dbg('lp_import_blueprint_workitems(): closing work items block with line: ' + l)
431
423
m = bugnum_re.search(l)
433
425
follow_blueprint_buglink(m.group(1), spec_assignee,
434
bp_name, lp, allow_inprogress, release,
426
bp_name, lp, release,
435
427
milestone or spec_milestone, work_items)
437
429
if '</div>' in l:
475
467
db.cursor().execute('INSERT INTO work_items VALUES (?, ?, ?, ?, ?, date(CURRENT_TIMESTAMP))',
476
468
(desc, bp_name, status, assignee, milestone))
478
def lp_import_milestones(lp, db, name, is_project):
470
def lp_import_milestones(lp_project, db):
479
471
'''Import milestones from Launchpad into DB.
481
If is_project is False, this imports milestones of the Ubuntu release
482
"name", otherwise from the project "name".
473
lp_project must be a Launchpad project or distro_series object.
484
475
cur = db.cursor()
485
476
cur.execute('SELECT count(*) FROM milestones')
490
481
dbg('lp_import_milestones(): importing from Launchpad')
492
milestones = lp.projects[name].all_milestones
494
milestones = lp.distributions['ubuntu'].getSeries(name_or_version=name).all_milestones
496
for ms in milestones:
482
for ms in lp_project.all_milestones:
497
483
if ms.date_targeted:
498
484
cur.execute('INSERT INTO milestones VALUES (?, ?)', (ms.name,
499
485
ms.date_targeted.strftime('%Y-%m-%d')))
523
509
(team_member.name, team))
512
def lp_import_bug_workitems(lp_project, db, cfg):
513
'''Collect work items from a bug list query'''
515
if 'work_item_bugs' not in cfg:
518
# synthesize a spec for this project
519
name = lp_project.title
520
db.cursor().execute("INSERT INTO specs VALUES (?, ?, 'Medium', 'Unknown', NULL, NULL, '', NULL, NULL, NULL, NULL, NULL, NULL)",
521
(name, 'https://launchpad.net/' + lp_project.name))
523
for task in lp_project.searchTasks(**cfg['work_item_bugs']):
524
id = task.self_link.split('/')[-1]
525
title = task.title.split('"', 1)[1].rstrip('"')
526
state = bug_wi_states.get(task.status, 'todo')
528
dbg('lp_import_bug_workitems: ignoring #%s: %s (status: %s)' % (id, title, task.status))
530
dbg('lp_import_bug_workitems: #%s: %s (%s)' % (id, title, state))
533
milestone = task.milestone.name
537
assignee = task.assignee.name
541
db.cursor().execute('INSERT INTO work_items VALUES (?, ?, ?, ?, ?, date(CURRENT_TIMESTAMP))',
542
(title, name, state, assignee, milestone))
526
544
def lp_import(db, cfg, name_pattern = None):
527
545
'''Collect blueprint work items and status from Launchpad into DB.'''
539
557
if 'release' in cfg:
540
lp_import_milestones(lp, db, cfg['release'], False)
558
lp_project = lp.distributions['ubuntu'].getSeries(name_or_version=cfg['release'])
541
559
url = '%s/ubuntu/%s/+specs?batch=300' % (
542
560
report_tools.blueprints_base_url, cfg['release'])
545
563
assert 'project' in cfg, 'Configuration needs to specify project or release'
564
lp_project = lp.projects[cfg['project']]
546
565
import_project(cfg['project'])
548
567
extra_projects = cfg.get('extra_projects', [])
549
568
for extra_project in extra_projects:
550
569
import_project(extra_project)
571
lp_import_milestones(lp_project, db)
552
572
lp_import_teams(lp, db, cfg)
557
577
contents = urllib.urlopen(url).read().decode('UTF-8')
558
578
if lp_import_blueprint(db, bp, url, contents):
559
579
lp_import_blueprint_workitems(lp, db, bp, url, contents, cfg.get('release'), cfg.get('inprogress_teams'))
580
lp_import_bug_workitems(lp_project, db, cfg)
561
582
########################################################################
568
589
'''Correctly escape data intended to be included in a URL'''
569
590
return urllib.quote(url)
571
def get_moin_workitems_group(url, default_assignee, allow_inprogress = False):
592
def get_moin_workitems_group(url, default_assignee):
572
593
'''Collect work items from a moin wiki URL.
574
595
Sections at == are treated as groups, first paragraph is status
924
945
cfg = report_tools.load_config(opts.config)
947
if 'bug_status_map' in cfg:
948
bug_wi_states.update(cfg['bug_status_map'])
926
950
db = get_db(opts.database)
928
952
# reset status for current day