Example #1
0
def main():
    parser = ArgumentParser(
        description="Calculate some statistics about project bugs.",
        epilog=dedent("""\
            Known caveats:
            Historical data uses the current task metadata rather than
            historical values. This is primarily due to performance
            considerations with the LP API and may be rectified in future (e.g.
            by mirroring the data persistently). As an example, if a bug is
            currently set to 'critical', it will show as critical in all time
            periods rather than progressing through different categories as it
            is triaged.
            """))
    parser.add_argument(
        '-p', '--project', default='projects/nova.json',
        help='JSON file describing the project to generate stats for.')
    args = parser.parse_args()
    projects = utils.get_projects_info(args.project, False)
    lp_project_listeners = {}
    listeners = set()
    if not projects:
        sys.stderr.write('No projects found: please specify one or more.\n')
        return 1
    launchpad = Launchpad.login_with(
        'openstack-releasing', 'production', credentials_file='.lpcreds')

    for project in projects:
        lp_projects = project.get('lp_projects', [])
        if not lp_projects:
            print "Please specify a project."
            return 1
        listener = Listener(project['name'], lp_projects)
        listeners.add(listener)
        for lp_project in project.get('lp_projects', []):
            lp_project_listeners.setdefault(lp_project, []).append(listener)

    statuses = ['New', 'Incomplete', 'Opinion', 'Invalid', "Won't Fix",
        'Confirmed', 'Triaged', 'In Progress', "Fix Committed", "Fix Released"]

    bugs_by_bug_link = {}
    for lp_project, receivers in lp_project_listeners.items():
        proj = launchpad.projects[lp_project]
        # Sort by id to make creating time periods easy.
        bugtasks = proj.searchTasks(status=statuses, order_by="id")
        for task in bugtasks:
            if task.bug_link not in bugs_by_bug_link:
                bugs_by_bug_link[task.bug_link] = task.bug
            bug = bugs_by_bug_link[task.bug_link]
            for receiver in receivers:
                receiver.categorise_task(task, bug)

    for listener in listeners:
        sys.stdout.write("Project: %s\n" % listener.name)
        sys.stdout.write("LP Projects: %s\n" % listener.lp_projects)
        table = prettytable.PrettyTable(
            ('Period', 'critical', 'high', 'undecided', 'other', 'total',
             'created', 'closed', 'critical-tags'))
        for period in listener.summarise():
            table.add_row(period)
        sys.stdout.write("%s\n" % table)
Example #2
0
def get_changes():
    #with open('changes.json') as f:
    #    return json.loads(f.read())
    projects = utils.get_projects_info(CONF.project_file)

    return utils.get_changes(projects, CONF.gerrit_user, CONF.ssh_key_file,
                             only_open=True)
def main(argv=None):
    if argv is None:
        argv = sys.argv

    optparser = optparse.OptionParser()
    optparser.add_option(
        '-p', '--project', default='projects/nova.json',
        help='JSON file describing the project to generate stats for')
    optparser.add_option(
        '-a', '--all', action='store_true',
        help='Generate stats across all known projects (*.json)')
    optparser.add_option(
        '-u', '--user', default=getpass.getuser(), help='gerrit user')
    optparser.add_option(
        '-k', '--key', default=None, help='ssh key for gerrit')
    optparser.add_option('-s', '--stable', action='store_true',
                         help='Include stable branch commits')
    optparser.add_option(
        '--server', default='review.opendev.org',
        help='Gerrit server to connect to')
    options, args = optparser.parse_args()
    projects = utils.get_projects_info(options.project, options.all)

    if not projects:
        print "Please specify a project."
        sys.exit(1)

    changes = utils.get_changes(projects, options.user, options.key,
                                only_open=True,
                                server=options.server)

    approved_and_rebased = set()
    for change in changes:
        if 'rowCount' in change:
            continue
        if not options.stable and 'stable' in change['branch']:
            continue
        if change['status'] != 'NEW':
            # Filter out WORKINPROGRESS
            continue
        for patch_set in change['patchSets'][:-1]:
            if (utils.patch_set_approved(patch_set)
                    and not utils.patch_set_approved(change['patchSets'][-1])):
                if has_negative_feedback(change['patchSets'][-1]):
                    continue
                approved_and_rebased.add("%s %s" % (change['url'],
                                                    change['subject']))

    for x in approved_and_rebased:
        print x
    print "total %d" % len(approved_and_rebased)
Example #4
0
def main(argv=None):
    if argv is None:
        argv = sys.argv

    optparser = optparse.OptionParser()
    optparser.add_option(
        '-p', '--project', default='projects/nova.json',
        help='JSON file describing the project to generate stats for')
    optparser.add_option(
        '-a', '--all', action='store_true',
        help='Generate stats across all known projects (*.json)')
    optparser.add_option(
        '-u', '--user', default=getpass.getuser(), help='gerrit user')
    optparser.add_option(
        '-k', '--key', default=None, help='ssh key for gerrit')
    optparser.add_option(
        '-s', '--stable', action='store_true',
        help='Include stable branch commits')
    optparser.add_option(
        '-l', '--longest-waiting', type='int', default=5,
        help='Show n changesets that have waited the longest)')
    optparser.add_option(
        '-m', '--waiting-more', type='int', default=7,
        help='Show number of changesets that have waited more than n days)')
    optparser.add_option(
        '-H', '--html', action='store_true',
        help='Use HTML output instead of plain text')
    optparser.add_option(
        '--server', default='review.openstack.org',
        help='Gerrit server to connect to')
    optparser.add_option(
        '--debug', action='store_true', help='Show extra debug output')
    optparser.add_option(
        '--projects-dir', default='./projects',
        help='Directory where to locate the project files')

    options, args = optparser.parse_args()

    logging.basicConfig(level=logging.ERROR)
    if options.debug:
        logging.root.setLevel(logging.DEBUG)

    projects = utils.get_projects_info(options.project, options.all,
                                       base_dir=options.projects_dir)

    if not projects:
        print "Please specify a project."
        sys.exit(1)

    changes = utils.get_changes(projects, options.user, options.key,
                                only_open=True, server=options.server)

    waiting_on_submitter = []
    waiting_on_reviewer = []

    now = datetime.datetime.utcnow()
    now_ts = calendar.timegm(now.timetuple())

    for change in changes:
        if 'rowCount' in change:
            continue
        if not options.stable and 'stable' in change['branch']:
            continue
        if change['status'] != 'NEW':
            # Filter out WORKINPROGRESS
            continue
        latest_patch = change['patchSets'][-1]
        if utils.patch_set_approved(latest_patch):
            # Ignore patches already approved and just waiting to merge
            continue
        waiting_for_review = True
        approvals = latest_patch.get('approvals', [])
        approvals.sort(key=lambda a: a['grantedOn'])
        for review in approvals:
            if review['type'] not in ('CRVW', 'VRIF',
                                      'Code-Review', 'Verified'):
                continue
            if review['value'] in ('-1', '-2'):
                waiting_for_review = False
                break

        change['age'] = utils.get_age_of_patch(latest_patch, now_ts)
        change['age2'] = utils.get_age_of_patch(change['patchSets'][0], now_ts)
        patch = find_oldest_no_nack(change)
        change['age3'] = utils.get_age_of_patch(patch, now_ts) if patch else 0

        if waiting_for_review:
            waiting_on_reviewer.append(change)
        else:
            waiting_on_submitter.append(change)

    stats = gen_stats(projects, waiting_on_reviewer, waiting_on_submitter,
                      options)

    if options.html:
        print_stats_html(stats)
    else:
        print_stats_txt(stats)
Example #5
0
def main(argv=None):
    if argv is None:
        argv = sys.argv

    optparser = optparse.OptionParser()
    optparser.add_option(
        '-p',
        '--project',
        default='projects/nova.json',
        help='JSON file describing the project to generate stats for')
    optparser.add_option(
        '-a',
        '--all',
        action='store_true',
        help='Generate stats across all known projects (*.json)')
    optparser.add_option('-u',
                         '--user',
                         default=getpass.getuser(),
                         help='gerrit user')
    optparser.add_option('-k',
                         '--key',
                         default=None,
                         help='ssh key for gerrit')
    optparser.add_option('-s',
                         '--stable',
                         action='store_true',
                         help='Include stable branch commits')
    optparser.add_option(
        '-l',
        '--longest-waiting',
        type='int',
        default=5,
        help='Show n changesets that have waited the longest)')
    optparser.add_option(
        '-m',
        '--waiting-more',
        type='int',
        default=7,
        help='Show number of changesets that have waited more than n days)')
    optparser.add_option('-H',
                         '--html',
                         action='store_true',
                         help='Use HTML output instead of plain text')
    optparser.add_option('--server',
                         default='review.openstack.org',
                         help='Gerrit server to connect to')
    optparser.add_option('--debug',
                         action='store_true',
                         help='Show extra debug output')
    optparser.add_option('--projects-dir',
                         default='./projects',
                         help='Directory where to locate the project files')
    optparser.add_option(
        '--output',
        '-o',
        default='-',
        help="Where to write output. - for stdout. The file will be appended"
        " if it exists.")

    options, args = optparser.parse_args()

    logging.basicConfig(level=logging.ERROR)
    if options.debug:
        logging.root.setLevel(logging.DEBUG)

    projects = utils.get_projects_info(options.project,
                                       options.all,
                                       base_dir=options.projects_dir)

    if not projects:
        print "Please specify a project."
        sys.exit(1)

    changes = utils.get_changes(projects,
                                options.user,
                                options.key,
                                only_open=True,
                                server=options.server)

    waiting_on_submitter = []
    waiting_on_reviewer = []

    now = datetime.datetime.utcnow()
    now_ts = calendar.timegm(now.timetuple())

    for change in changes:
        if 'rowCount' in change:
            continue
        if not options.stable and 'stable' in change['branch']:
            continue
        if utils.is_workinprogress(change):
            # Filter out WORKINPROGRESS
            continue
        latest_patch = change['patchSets'][-1]
        if utils.patch_set_approved(latest_patch):
            # Ignore patches already approved and just waiting to merge
            continue
        waiting_for_review = True
        approvals = latest_patch.get('approvals', [])
        approvals.sort(key=lambda a: a['grantedOn'])
        for review in approvals:
            if review['type'] not in ('CRVW', 'VRIF', 'Code-Review',
                                      'Verified'):
                continue
            if review['value'] in ('-1', '-2'):
                waiting_for_review = False
                break

        change['age'] = utils.get_age_of_patch(latest_patch, now_ts)
        change['age2'] = utils.get_age_of_patch(change['patchSets'][0], now_ts)
        patch = find_oldest_no_nack(change)
        change['age3'] = utils.get_age_of_patch(patch, now_ts) if patch else 0

        if waiting_for_review:
            waiting_on_reviewer.append(change)
        else:
            waiting_on_submitter.append(change)

    stats = gen_stats(projects, waiting_on_reviewer, waiting_on_submitter,
                      options)

    if options.output == '-':
        output = sys.stdout
    else:
        output = open(options.output, 'at')
    try:
        if options.html:
            print_stats_html(stats, f=output)
        else:
            print_stats_txt(stats, f=output)
    finally:
        if output is not sys.stdout:
            output.close()
Example #6
0
 def test_get_projects_info_single_name_projects_prefixed(self):
     projects = utils.get_projects_info('projects/stable.json')
     self.assertEqual(1, len(projects))
Example #7
0
 def test_get_projects_info_single_name(self):
     projects = utils.get_projects_info('nova')
     self.assertEqual(1, len(projects))
Example #8
0
 def test_project_definitions_load(self):
     utils.get_projects_info('', True)
Example #9
0
def main(argv=None):
    if argv is None:
        argv = sys.argv

    optparser = optparse.OptionParser()
    optparser.add_option(
        "-p", "--project", default="projects/nova.json", help="JSON file describing the project to generate stats for"
    )
    optparser.add_option("-a", "--all", action="store_true", help="Generate stats across all known projects (*.json)")
    optparser.add_option(
        "-s",
        "--stable",
        default="",
        metavar="BRANCH",
        help='Generate stats for the specified stable BRANCH ("havana") ' "across all integrated projects",
    )
    optparser.add_option(
        "-o",
        "--output",
        default="-",
        help="Where to write output. If - stdout is used and only one output "
        "format may be given. Otherwise the output format is appended to "
        "the output parameter to generate file names.",
    )
    optparser.add_option(
        "--outputs", default=["txt"], action="append", help="Select what outputs to generate. (txt,csv)."
    )
    optparser.add_option("-d", "--days", type="int", default=14, help="Number of days to consider")
    optparser.add_option("-u", "--user", default=getpass.getuser(), help="gerrit user")
    optparser.add_option("-P", "--password", default=getpass.getuser(), help="gerrit HTTP password")
    optparser.add_option("-k", "--key", default=None, help="ssh key for gerrit")
    optparser.add_option("-r", "--csv-rows", default=0, help="Max rows for CSV output", type="int", dest="csv_rows")
    optparser.add_option("--server", default="review.openstack.org", help="Gerrit server to connect to")

    options, args = optparser.parse_args()

    if options.stable:
        projects = utils.get_projects_info("projects/stable.json", False)
    else:
        projects = utils.get_projects_info(options.project, options.all)

    if not projects:
        print "Please specify a project."
        sys.exit(1)

    reviewers = {}

    now = datetime.datetime.utcnow()
    cut_off = now - datetime.timedelta(days=options.days)
    ts = calendar.timegm(cut_off.timetuple())
    now_ts = calendar.timegm(now.timetuple())

    change_stats = {"patches": 0, "created": 0, "involved": 0, "merged": 0, "abandoned": 0, "wip": 0}

    for project in projects:
        changes = utils.get_changes([project], options.user, options.key, stable=options.stable, server=options.server)
        for change in changes:
            patch_for_change = False
            first_patchset = True
            for patchset in change.get("patchSets", []):
                process_patchset(project, patchset, reviewers, ts, options)
                age = utils.get_age_of_patch(patchset, now_ts)
                if (now_ts - age) > ts:
                    change_stats["patches"] += 1
                    patch_for_change = True
                    if first_patchset:
                        change_stats["created"] += 1
                first_patchset = False
            if patch_for_change:
                change_stats["involved"] += 1
                if change["status"] == "MERGED":
                    change_stats["merged"] += 1
                elif change["status"] == "ABANDONED":
                    change_stats["abandoned"] += 1
                elif change["status"] == "WORKINPROGRESS":
                    change_stats["wip"] += 1

    reviewers = [(v, k) for k, v in reviewers.iteritems() if k.lower() not in ("jenkins", "smokestack")]
    reviewers.sort(reverse=True, key=lambda r: r[0]["total"])
    # Do logical processing of reviewers.
    reviewer_data = []
    totals = {"all": 0, "core": 0}
    for k, v in reviewers:
        in_core_team = False
        for project in projects:
            if v in utils.get_core_team(project, options.server, options.user, options.password):
                in_core_team = True
                break
        name = "%s%s" % (v, " **" if in_core_team else "")
        plus = float(k["votes"]["2"] + k["votes"]["1"])
        minus = float(k["votes"]["-2"] + k["votes"]["-1"])
        all_reviews = plus + minus
        ratio = ((plus / (all_reviews)) * 100) if all_reviews > 0 else 0
        r = (
            k["total"],
            k["votes"]["-2"],
            k["votes"]["-1"],
            k["votes"]["1"],
            k["votes"]["2"],
            k["votes"]["A"],
            "%5.1f%%" % ratio,
        )
        dratio = ((float(k["disagreements"]) / all_reviews) * 100) if all_reviews else 0.0
        d = (k["disagreements"], "%5.1f%%" % dratio)
        sratio = (float(k["total"]) / k["received"]) * 100 if k["received"] else 0
        s = (k["received"], "%5.1f%%" % sratio if k["received"] else "inf")
        reviewer_data.append((name, r, d, s))
        totals["all"] += k["total"]
        if in_core_team:
            totals["core"] += k["total"]
    # And output.
    writers = {"csv": write_csv, "txt": write_pretty}
    if options.output == "-":
        if len(options.outputs) != 1:
            raise Exception("Can only output one format to stdout.")
    for output in options.outputs:
        if options.output == "-":
            file_obj = sys.stdout
            on_done = None
        else:
            file_obj = open(options.output + "." + output, "wt")
            on_done = file_obj.close
        try:
            writer = writers[output]
            writer(reviewer_data, file_obj, options, reviewers, projects, totals, change_stats)
        finally:
            if on_done:
                on_done()
    return 0
Example #10
0
def main():
    parser = ArgumentParser(
        description="Get reviews for open bugs against a milestone")
    parser.add_argument(
        '-p',
        '--project',
        default='projects/nova.json',
        help='JSON file describing the project to generate stats for')
    parser.add_argument(
        '-m',
        '--milestone',
        default='',
        help='Only show bugs targeted to a specified milestone')
    parser.add_argument('-u',
                        '--user',
                        default=getpass.getuser(),
                        help='gerrit user')
    parser.add_argument('-k', '--key', default=None, help='ssh key for gerrit')

    args = parser.parse_args()

    projects = utils.get_projects_info(args.project, False)
    project_name = projects[0]['name']

    if not projects:
        print "Please specify a project."
        return 1

    launchpad = Launchpad.login_with('openstack-releasing', 'production')
    proj = launchpad.projects[project_name]
    statuses = ['New', 'Incomplete', 'Confirmed', 'Triaged', 'In Progress']
    if args.milestone:
        milestone = proj.getMilestone(name=args.milestone)
        bugtasks = proj.searchTasks(status=statuses, milestone=milestone)
    else:
        bugtasks = proj.searchTasks(status=statuses)
    bugs_by_id = {}
    for bt in bugtasks:
        bugs_by_id[str(bt.bug.id)] = bt

    milestones = {}

    changes = utils.get_changes(projects, args.user, args.key, only_open=True)
    bug_regex = re.compile('bug/(\d+)')
    for change in changes:
        if 'topic' not in change:
            continue
        match = bug_regex.match(change['topic'])
        if not match:
            continue
        bugid = match.group(1)
        try:
            bugtask = bugs_by_id[bugid]
            milestone = str(bugtask.milestone).split('/')[-1]
            if milestone == 'None':
                milestone = 'Untargeted'
        except KeyError:
            milestone = 'Bug does not exist for this project'

        milestones.setdefault(milestone, [])
        milestones[milestone].append((change['url'], bugid))

    print 'Reviews for bugs grouped by milestone for project: %s\n' % (
        project_name)

    for milestone, reviews in milestones.items():
        if args.milestone and milestone != args.milestone:
            continue
        print 'Milestone: %s' % milestone
        for review, bugid in reviews:
            print '--> %s -- https://bugs.launchpad.net/%s/+bug/%s' \
                % (review, project_name, bugid)
        print
Example #11
0
 def test_get_projects_info_single_name_projects_prefixed(self):
     projects = utils.get_projects_info('projects/stable.json')
     self.assertEqual(1, len(projects))
Example #12
0
 def test_get_projects_info_single_name(self):
     projects = utils.get_projects_info('nova')
     self.assertEqual(1, len(projects))
Example #13
0
 def test_project_definitions_load(self):
     utils.get_projects_info('', True)
def main():
    parser = ArgumentParser(
        description="Get reviews for open bugs against a milestone")
    parser.add_argument(
        '-p', '--project', default='projects/nova.json',
        help='JSON file describing the project to generate stats for')
    parser.add_argument(
        '-m', '--milestone', default='',
        help='Only show bugs targeted to a specified milestone')
    parser.add_argument(
        '-u', '--user', default=getpass.getuser(), help='gerrit user')
    parser.add_argument('-k', '--key', default=None, help='ssh key for gerrit')

    args = parser.parse_args()

    projects = utils.get_projects_info(args.project, False)
    project_name = projects[0]['name']

    if not projects:
        print "Please specify a project."
        return 1

    launchpad = Launchpad.login_with('openstack-releasing', 'production')
    proj = launchpad.projects[project_name]
    statuses = ['New', 'Incomplete', 'Confirmed', 'Triaged', 'In Progress']
    if args.milestone:
        milestone = proj.getMilestone(name=args.milestone)
        bugtasks = proj.searchTasks(status=statuses, milestone=milestone)
    else:
        bugtasks = proj.searchTasks(status=statuses)
    bugs_by_id = {}
    for bt in bugtasks:
        bugs_by_id[str(bt.bug.id)] = bt

    milestones = {}

    changes = utils.get_changes(projects, args.user, args.key, only_open=True)
    bug_regex = re.compile('bug/(\d+)')
    for change in changes:
        if 'topic' not in change:
            continue
        match = bug_regex.match(change['topic'])
        if not match:
            continue
        bugid = match.group(1)
        try:
            bugtask = bugs_by_id[bugid]
            milestone = str(bugtask.milestone).split('/')[-1]
            if milestone == 'None':
                milestone = 'Untargeted'
        except KeyError:
            milestone = 'Bug does not exist for this project'

        milestones.setdefault(milestone, [])
        milestones[milestone].append((change['url'], bugid))

    print 'Reviews for bugs grouped by milestone for project: %s\n' % (
        project_name)

    for milestone, reviews in milestones.items():
        if args.milestone and milestone != args.milestone:
            continue
        print 'Milestone: %s' % milestone
        for review, bugid in reviews:
            print '--> %s -- https://bugs.launchpad.net/%s/+bug/%s' \
                % (review, project_name, bugid)
        print
Example #15
0
def main():
    parser = ArgumentParser(
        description="Calculate some statistics about project bugs.",
        epilog=dedent("""\
            Known caveats:
            Historical data uses the current task metadata rather than
            historical values. This is primarily due to performance
            considerations with the LP API and may be rectified in future (e.g.
            by mirroring the data persistently). As an example, if a bug is
            currently set to 'critical', it will show as critical in all time
            periods rather than progressing through different categories as it
            is triaged.
            """))
    parser.add_argument(
        '-p',
        '--project',
        default='projects/nova.json',
        help='JSON file describing the project to generate stats for.')
    args = parser.parse_args()
    projects = utils.get_projects_info(args.project, False)
    lp_project_listeners = {}
    listeners = set()
    if not projects:
        sys.stderr.write('No projects found: please specify one or more.\n')
        return 1
    launchpad = Launchpad.login_with('openstack-releasing',
                                     'production',
                                     credentials_file='.lpcreds')

    for project in projects:
        lp_projects = project.get('lp_projects', [])
        if not lp_projects:
            print "Please specify a project."
            return 1
        listener = Listener(project['name'], lp_projects)
        listeners.add(listener)
        for lp_project in project.get('lp_projects', []):
            lp_project_listeners.setdefault(lp_project, []).append(listener)

    statuses = [
        'New', 'Incomplete', 'Opinion', 'Invalid', "Won't Fix", 'Confirmed',
        'Triaged', 'In Progress', "Fix Committed", "Fix Released"
    ]

    bugs_by_bug_link = {}
    for lp_project, receivers in lp_project_listeners.items():
        proj = launchpad.projects[lp_project]
        # Sort by id to make creating time periods easy.
        bugtasks = proj.searchTasks(status=statuses, order_by="id")
        for task in bugtasks:
            if task.bug_link not in bugs_by_bug_link:
                bugs_by_bug_link[task.bug_link] = task.bug
            bug = bugs_by_bug_link[task.bug_link]
            for receiver in receivers:
                receiver.categorise_task(task, bug)

    for listener in listeners:
        sys.stdout.write("Project: %s\n" % listener.name)
        sys.stdout.write("LP Projects: %s\n" % listener.lp_projects)
        table = prettytable.PrettyTable(
            ('Period', 'critical', 'high', 'undecided', 'other', 'total',
             'created', 'closed', 'critical-tags'))
        for period in listener.summarise():
            table.add_row(period)
        sys.stdout.write("%s\n" % table)
Example #16
0
def main(argv=None):
    if argv is None:
        argv = sys.argv

    optparser = optparse.OptionParser()
    optparser.add_option(
        '-p',
        '--project',
        default='projects/nova.json',
        help='JSON file describing the project to generate stats for')
    optparser.add_option(
        '-a',
        '--all',
        action='store_true',
        help='Generate stats across all known projects (*.json)')
    optparser.add_option(
        '-s',
        '--stable',
        default='',
        metavar='BRANCH',
        help='Generate stats for the specified stable BRANCH ("havana") '
        'across all integrated projects')
    optparser.add_option(
        '-o',
        '--output',
        default='-',
        help='Where to write output. If - stdout is used and only one output '
        'format may be given. Otherwise the output format is appended to '
        'the output parameter to generate file names.')
    optparser.add_option('--outputs',
                         default=['txt'],
                         action='append',
                         help='Select what outputs to generate. (txt,csv).')
    optparser.add_option('-d',
                         '--days',
                         type='int',
                         default=14,
                         help='Number of days to consider')
    optparser.add_option('-u',
                         '--user',
                         default=getpass.getuser(),
                         help='gerrit user')
    optparser.add_option('-P',
                         '--password',
                         default=getpass.getuser(),
                         help='gerrit HTTP password')
    optparser.add_option('-k',
                         '--key',
                         default=None,
                         help='ssh key for gerrit')
    optparser.add_option('-r',
                         '--csv-rows',
                         default=0,
                         help='Max rows for CSV output',
                         type='int',
                         dest='csv_rows')
    optparser.add_option('--server',
                         default='review.openstack.org',
                         help='Gerrit server to connect to')

    options, args = optparser.parse_args()

    if options.stable:
        projects = utils.get_projects_info('projects/stable.json', False)
    else:
        projects = utils.get_projects_info(options.project, options.all)

    if not projects:
        print "Please specify a project."
        sys.exit(1)

    reviewers = {}

    now = datetime.datetime.utcnow()
    cut_off = now - datetime.timedelta(days=options.days)
    ts = calendar.timegm(cut_off.timetuple())
    now_ts = calendar.timegm(now.timetuple())

    change_stats = {
        'patches': 0,
        'created': 0,
        'involved': 0,
        'merged': 0,
        'abandoned': 0,
        'wip': 0,
    }

    for project in projects:
        changes = utils.get_changes([project],
                                    options.user,
                                    options.key,
                                    stable=options.stable,
                                    server=options.server)
        for change in changes:
            patch_for_change = False
            first_patchset = True
            for patchset in change.get('patchSets', []):
                process_patchset(project, patchset, reviewers, ts, options)
                age = utils.get_age_of_patch(patchset, now_ts)
                if (now_ts - age) > ts:
                    change_stats['patches'] += 1
                    patch_for_change = True
                    if first_patchset:
                        change_stats['created'] += 1
                first_patchset = False
            if patch_for_change:
                change_stats['involved'] += 1
                if change['status'] == 'MERGED':
                    change_stats['merged'] += 1
                elif change['status'] == 'ABANDONED':
                    change_stats['abandoned'] += 1
                elif change['status'] == 'WORKINPROGRESS':
                    change_stats['wip'] += 1

    reviewers = [(v, k) for k, v in reviewers.iteritems()
                 if k.lower() not in ('jenkins', 'smokestack')]
    reviewers.sort(reverse=True, key=lambda r: r[0]['total'])
    # Do logical processing of reviewers.
    reviewer_data = []
    totals = {
        'all': 0,
        'core': 0,
    }
    for k, v in reviewers:
        in_core_team = False
        for project in projects:
            if v in utils.get_core_team(project, options.server, options.user,
                                        options.password):
                in_core_team = True
                break
        name = '%s%s' % (v, ' **' if in_core_team else '')
        plus = float(k['votes']['2'] + k['votes']['1'])
        minus = float(k['votes']['-2'] + k['votes']['-1'])
        all_reviews = plus + minus
        ratio = ((plus / (all_reviews)) * 100) if all_reviews > 0 else 0
        r = (k['total'], k['votes']['-2'], k['votes']['-1'], k['votes']['1'],
             k['votes']['2'], k['votes']['A'], "%5.1f%%" % ratio)
        dratio = (((float(k['disagreements']) / all_reviews) *
                   100) if all_reviews else 0.0)
        d = (k['disagreements'], "%5.1f%%" % dratio)
        sratio = ((float(k['total']) / k['received']) *
                  100 if k['received'] else 0)
        s = (k['received'], "%5.1f%%" % sratio if k['received'] else 'inf')
        reviewer_data.append((name, r, d, s))
        totals['all'] += k['total']
        if in_core_team:
            totals['core'] += k['total']
    # And output.
    writers = {
        'csv': write_csv,
        'txt': write_pretty,
    }
    if options.output == '-':
        if len(options.outputs) != 1:
            raise Exception("Can only output one format to stdout.")
    for output in options.outputs:
        if options.output == '-':
            file_obj = sys.stdout
            on_done = None
        else:
            file_obj = open(options.output + '.' + output, 'wt')
            on_done = file_obj.close
        try:
            writer = writers[output]
            writer(reviewer_data, file_obj, options, reviewers, projects,
                   totals, change_stats)
        finally:
            if on_done:
                on_done()
    return 0
Example #17
0
def main(argv=None):
    if argv is None:
        argv = sys.argv

    optparser = optparse.OptionParser()
    optparser.add_option(
        '-p', '--project', default='projects/nova.json',
        help='JSON file describing the project to generate stats for')
    optparser.add_option(
        '-a', '--all', action='store_true',
        help='Generate stats across all known projects (*.json)')
    optparser.add_option(
        '-s', '--stable', default='', metavar='BRANCH',
        help='Generate stats for the specified stable BRANCH ("havana") '
             'across all integrated projects')
    optparser.add_option(
        '-o', '--output', default='-',
        help='Where to write output. If - stdout is used and only one output'
            'format may be given. Otherwise the output format is appended to'
            'the output parameter to generate file names.')
    optparser.add_option(
        '--outputs', default=['txt'], action='append',
        help='Select what outputs to generate. (txt,csv).')
    optparser.add_option(
        '-d', '--days', type='int', default=14,
        help='Number of days to consider')
    optparser.add_option(
        '-u', '--user', default=getpass.getuser(), help='gerrit user')
    optparser.add_option(
        '-k', '--key', default=None, help='ssh key for gerrit')
    optparser.add_option(
        '-r', '--csv-rows', default=0, help='Max rows for CSV output',
        type='int', dest='csv_rows')

    options, args = optparser.parse_args()

    if options.stable:
        projects = utils.get_projects_info('projects/stable.json', False)
    else:
        projects = utils.get_projects_info(options.project, options.all)

    if not projects:
        print "Please specify a project."
        sys.exit(1)

    reviewers = {}

    now = datetime.datetime.utcnow()
    cut_off = now - datetime.timedelta(days=options.days)
    ts = calendar.timegm(cut_off.timetuple())
    now_ts = calendar.timegm(now.timetuple())

    change_stats = {
        'patches': 0,
        'created': 0,
        'involved': 0,
        'merged': 0,
        'abandoned': 0,
        'wip': 0,
    }

    for project in projects:
        changes = utils.get_changes([project], options.user, options.key,
                                    stable=options.stable)
        for change in changes:
            patch_for_change = False
            first_patchset = True
            for patchset in change.get('patchSets', []):
                process_patchset(project, patchset, reviewers, ts)
                age = utils.get_age_of_patch(patchset, now_ts)
                if (now_ts - age) > ts:
                    change_stats['patches'] += 1
                    patch_for_change = True
                    if first_patchset:
                        change_stats['created'] += 1
                first_patchset = False
            if patch_for_change:
                change_stats['involved'] += 1
                if change['status'] == 'MERGED':
                    change_stats['merged'] += 1
                elif change['status'] == 'ABANDONED':
                    change_stats['abandoned'] += 1
                elif change['status'] == 'WORKINPROGRESS':
                    change_stats['wip'] += 1

    reviewers = [(v, k) for k, v in reviewers.iteritems()
                 if k.lower() not in ('jenkins', 'smokestack')]
    reviewers.sort(reverse=True, key=lambda r: r[0]['total'])
    # Do logical processing of reviewers.
    reviewer_data = []
    totals = {
        'all': 0,
        'core': 0,
    }
    for k, v in reviewers:
        in_core_team = False
        for project in projects:
            if v in project['core-team']:
                in_core_team = True
                break
        name = '%s%s' % (v, ' **' if in_core_team else '')
        plus = float(k['votes']['2'] + k['votes']['1'])
        minus = float(k['votes']['-2'] + k['votes']['-1'])
        all_reviews = plus + minus
        ratio = ((plus / (all_reviews)) * 100) if all_reviews > 0 else 0
        r = (k['total'], k['votes']['-2'],
             k['votes']['-1'], k['votes']['1'],
             k['votes']['2'], k['votes']['A'], "%5.1f%%" % ratio)
        dratio = (((float(k['disagreements']) / all_reviews) * 100)
                  if all_reviews else 0.0)
        d = (k['disagreements'], "%5.1f%%" % dratio)
        sratio = ((float(k['total']) / k['received']) * 100
                  if k['received'] else 0)
        s = (k['received'], "%5.1f%%" % sratio if k['received'] else 'inf')
        reviewer_data.append((name, r, d, s))
        totals['all'] += k['total']
        if in_core_team:
            totals['core'] += k['total']
    # And output.
    writers = {
        'csv': write_csv,
        'txt': write_pretty,
        }
    if options.output == '-':
        if len(options.outputs) != 1:
            raise Exception("Can only output one format to stdout.")
    for output in options.outputs:
        if options.output == '-':
            file_obj = sys.stdout
            on_done = None
        else:
            file_obj = open(options.output + '.' + output, 'wt')
            on_done = file_obj.close
        try:
            writer = writers[output]
            writer(reviewer_data, file_obj, options, reviewers, projects,
                   totals, change_stats)
        finally:
            if on_done:
                on_done()
    return 0
Example #18
0
def main(argv=None):
    if argv is None:
        argv = sys.argv

    optparser = optparse.OptionParser()
    optparser.add_option(
        '-p',
        '--project',
        default='projects/nova.json',
        help='JSON file describing the project to generate stats for')
    optparser.add_option(
        '-a',
        '--all',
        action='store_true',
        help='Generate stats across all known projects (*.json)')
    optparser.add_option('-u',
                         '--user',
                         default=getpass.getuser(),
                         help='gerrit user')
    optparser.add_option('-k',
                         '--key',
                         default=None,
                         help='ssh key for gerrit')
    optparser.add_option('-s',
                         '--stable',
                         action='store_true',
                         help='Include stable branch commits')
    optparser.add_option('--server',
                         default='review.openstack.org',
                         help='Gerrit server to connect to')
    options, args = optparser.parse_args()
    projects = utils.get_projects_info(options.project, options.all)

    if not projects:
        print "Please specify a project."
        sys.exit(1)

    changes = utils.get_changes(projects,
                                options.user,
                                options.key,
                                only_open=True,
                                server=options.server)

    approved_and_rebased = set()
    for change in changes:
        if 'rowCount' in change:
            continue
        if not options.stable and 'stable' in change['branch']:
            continue
        if change['status'] != 'NEW':
            # Filter out WORKINPROGRESS
            continue
        for patch_set in change['patchSets'][:-1]:
            if (utils.patch_set_approved(patch_set)
                    and not utils.patch_set_approved(change['patchSets'][-1])):
                if has_negative_feedback(change['patchSets'][-1]):
                    continue
                approved_and_rebased.add("%s %s" %
                                         (change['url'], change['subject']))

    for x in approved_and_rebased:
        print x
    print "total %d" % len(approved_and_rebased)