~james-w/launchpad-work-items-tracker/spec-group-completion-by-priority

« back to all changes in this revision

Viewing changes to collect

  • Committer: James Westby
  • Date: 2010-12-07 14:06:17 UTC
  • mfrom: (240.1.7 trunk)
  • Revision ID: james.westby@linaro.org-20101207140617-22qyl0ov5s99m04i
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
13
13
 
14
14
debug = False
15
15
 
 
16
# map a bug task status to a WI status (default: 'todo')
 
17
bug_wi_states = {
 
18
    "Won't Fix": 'postponed',
 
19
    'In Progress': 'inprogress',
 
20
    'Fix Committed': 'inprogress',
 
21
    'Fix Released': 'done',
 
22
    'Opinion': None,
 
23
    'Invalid': None,
 
24
}
 
25
 
 
26
 
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
18
29
data_errors = None
227
238
        data_error(bp_url, "\tComplexity line '%s' could not be parsed %s" % (line, ValueError))
228
239
 
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.'''
232
243
 
233
244
    line = line.replace('<br />', '').replace('</div>', '').replace('</p>',
248
259
        state = 'todo'
249
260
    if state == 'completed':
250
261
        state = 'done'
251
 
    if state == 'inprogress' and not allow_inprogress:
252
 
        state = 'todo'
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"' %
258
266
                (state, desc))
259
267
        return
279
287
    result_list.append((desc, state, assignee, milestone))
280
288
 
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'''
284
292
 
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')
 
298
        if state is None:
290
299
            continue
291
300
 
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
326
335
 
327
 
        if task.status == 'Fix Released':
328
 
            state = 'done'
329
 
        elif task.status == "Won't Fix":
330
 
            state = 'postponed'
331
 
        elif allow_inprogress and task.status in ('In Progress', 'Fix Committed'):
332
 
            state = 'inprogress'
333
 
        else:
334
 
            state = 'todo'
335
 
 
336
336
        if task.assignee:
337
337
            assignee = task.assignee.name
338
338
        else:
354
354
            return word
355
355
    return None
356
356
 
357
 
def lp_import_blueprint_workitems(lp, db, bp_name, bp_url, contents, release,
358
 
        inprogress_teams):
 
357
def lp_import_blueprint_workitems(lp, db, bp_name, bp_url, contents, release):
359
358
    '''Collect work items from a Launchpad blueprint.
360
359
 
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]
387
386
 
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
393
 
            break
394
 
 
395
387
    cur.execute('SELECT name FROM milestones')
396
388
    valid_milestones = [m[0] for m in cur]
397
389
 
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)
423
415
 
424
416
            if '</p>' in l:
425
417
                dbg('lp_import_blueprint_workitems(): closing work items block with line: ' + l)
431
423
            m = bugnum_re.search(l)
432
424
            if m and lp:
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)
436
428
 
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))
477
469
 
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.
480
472
 
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.
483
474
    '''
484
475
    cur = db.cursor()
485
476
    cur.execute('SELECT count(*) FROM milestones')
488
479
        return
489
480
 
490
481
    dbg('lp_import_milestones(): importing from Launchpad')
491
 
    if is_project:
492
 
        milestones = lp.projects[name].all_milestones
493
 
    else:
494
 
        milestones = lp.distributions['ubuntu'].getSeries(name_or_version=name).all_milestones
495
 
 
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))
524
510
    db.commit()
525
511
 
 
512
def lp_import_bug_workitems(lp_project, db, cfg):
 
513
    '''Collect work items from a bug list query'''
 
514
 
 
515
    if 'work_item_bugs' not in cfg:
 
516
        return
 
517
 
 
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))
 
522
 
 
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')
 
527
        if state is None:
 
528
            dbg('lp_import_bug_workitems: ignoring #%s: %s (status: %s)' % (id, title, task.status))
 
529
            continue
 
530
        dbg('lp_import_bug_workitems: #%s: %s (%s)' % (id, title, state))
 
531
 
 
532
        if task.milestone:
 
533
            milestone = task.milestone.name
 
534
        else:
 
535
            milestone = None
 
536
        if task.assignee:
 
537
            assignee = task.assignee.name
 
538
        else:
 
539
            assignee = None
 
540
        
 
541
        db.cursor().execute('INSERT INTO work_items VALUES (?, ?, ?, ?, ?, date(CURRENT_TIMESTAMP))',
 
542
                (title, name, state, assignee, milestone))
 
543
 
526
544
def lp_import(db, cfg, name_pattern = None):
527
545
    '''Collect blueprint work items and status from Launchpad into DB.'''
528
546
 
537
555
        urls.append(url)
538
556
 
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'])
543
561
        urls.append(url)
544
562
    else:
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'])
547
566
 
548
567
    extra_projects = cfg.get('extra_projects', [])
549
568
    for extra_project in extra_projects:
550
569
        import_project(extra_project)
551
570
 
 
571
    lp_import_milestones(lp_project, db)
552
572
    lp_import_teams(lp, db, cfg)
553
573
 
554
574
    for url in urls:
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)
560
581
 
561
582
########################################################################
562
583
#
568
589
    '''Correctly escape data intended to be included in a URL'''
569
590
    return urllib.quote(url)
570
591
 
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.
573
594
 
574
595
    Sections at == are treated as groups, first paragraph is status
640
661
                    elif 'BLOCKED' in f:
641
662
                        istatus = 'blocked'
642
663
                        break
643
 
                    elif 'INPROGRESS' in f and allow_inprogress:
 
664
                    elif 'INPROGRESS' in f:
644
665
                        istatus = 'inprogress'
645
666
                        break
646
667
                    else:
923
944
 
924
945
    cfg = report_tools.load_config(opts.config)
925
946
 
 
947
    if 'bug_status_map' in cfg:
 
948
        bug_wi_states.update(cfg['bug_status_map'])
 
949
 
926
950
    db = get_db(opts.database)
927
951
 
928
952
    # reset status for current day