diff --git a/openstack_election/cmds/render_statistics.py b/openstack_election/cmds/render_statistics.py index 5101b6d1..1bccfe14 100755 --- a/openstack_election/cmds/render_statistics.py +++ b/openstack_election/cmds/render_statistics.py @@ -69,6 +69,53 @@ def collect_project_stats(basedir, verbose, projects): ', '.join(candidates))) +def election_summary(): + now = datetime.datetime.now(tz=pytz.utc) + now = now.replace(microsecond=0) + event = utils.get_event('PTL Nominations') + start, end = event['start'], event['end'] + duration = (end - start) + remaining = (end - now) + progress = (duration - remaining) + + p_progress = as_percentage(progress.total_seconds(), + duration.total_seconds()) + p_candidate = as_percentage(counts['with_candidate'], counts['projects']) + p_nominations = as_percentage(counts['nominations'], counts['projects']) + + need_election.sort() + without_candidate.sort() + + output = "" + output += ("%-25s @ %s\n" % ("Nominations started", as_utctime(start))) + output += ("%-25s @ %s\n" % ("Nominations end", as_utctime(end))) + output += ("%-25s : %s\n" % ("Nominations duration", duration)) + output += ("%-25s : %s\n" % ("Nominations remaining", remaining)) + output += ("%-25s : %6.2f%%\n" % ("Nominations progress", p_progress)) + output += ("-" * 51) + output += ("\n") + output += ("%-25s : %5d\n" % ("Projects[1]", counts['projects'])) + output += ("%-25s : %5d (%6.2f%%)\n" % ("Projects with candidates", + counts['with_candidate'], + p_candidate)) + output += ("%-25s : %5d (%6.2f%%)\n" % ("Projects with election", + counts['nominations'], + p_nominations)) + output += ("-" * 51) + output += ("\n") + output += ("%-25s : %d (%s)\n" % ("Need election", + len(need_election), + " ".join(need_election))) + output += ("%-25s : %d (%s)\n" % ("Need appointment", + len(without_candidate), + " ".join(without_candidate))) + output += ("=" * 51) + output += ("\n") + output += ("%-25s @ %s\n" % ("Stats gathered", as_utctime(now))) + + return output + + def main(): parser = argparse.ArgumentParser(description='Investigate PTL Nominations') parser.add_argument('-v', '--verbose', action="count", default=0, @@ -94,45 +141,9 @@ def main(): args.basedir = os.path.expanduser(args.basedir) collect_project_stats(args.basedir, args.verbose, args.projects) - now = datetime.datetime.now(tz=pytz.utc) - now = now.replace(microsecond=0) - event = utils.get_event('PTL Nominations') - start, end = event['start'], event['end'] - duration = (end - start) - remaining = (end - now) - progress = (duration - remaining) - - p_progress = as_percentage(progress.total_seconds(), - duration.total_seconds()) - p_candidate = as_percentage(counts['with_candidate'], counts['projects']) - p_nominations = as_percentage(counts['nominations'], counts['projects']) - - need_election.sort() - without_candidate.sort() - if args.verbose: print("-" * 51) - print("%-25s @ %s" % ("Nominations started", as_utctime(start))) - print("%-25s @ %s" % ("Nominations end", as_utctime(end))) - print("%-25s : %s" % ("Nominations duration", duration)) - print("%-25s : %s" % ("Nominations remaining", remaining)) - print("%-25s : %6.2f%%" % ("Nominations progress", p_progress)) - print("-" * 51) - print("%-25s : %5d" % ("Projects[1]", counts['projects'])) - print("%-25s : %5d (%6.2f%%)" % ("Projects with candidates", - counts['with_candidate'], - p_candidate)) - print("%-25s : %5d (%6.2f%%)" % ("Projects with election", - counts['nominations'], - p_nominations)) - print("-" * 51) - print("%-25s : %d (%s)" % ("Need election", len(need_election), - " ".join(need_election))) - print("%-25s : %d (%s)" % ("Need appointment", len(without_candidate), - " ".join(without_candidate))) - print("=" * 51) - print("%-25s @ %s" % ("Stats gathered", as_utctime(now))) - + print(election_summary()) print("") print("[1] These numbers include the following projects that have a " "candidate that is approved my only a single election official:" diff --git a/openstack_election/cmds/template_emails.py b/openstack_election/cmds/template_emails.py index eaede250..f73668ed 100644 --- a/openstack_election/cmds/template_emails.py +++ b/openstack_election/cmds/template_emails.py @@ -2,8 +2,10 @@ from __future__ import print_function from __future__ import unicode_literals import argparse +import os import sys +from openstack_election.cmds import render_statistics as stats from openstack_election import config from openstack_election import utils @@ -35,6 +37,11 @@ if conf['election_type'] == 'tc': release=conf['release'], ) elif conf['election_type'] == 'ptl': + # NOTE(tonyb): We need an empty item last to ensure the path ends in a + # tailing '/' + stats.collect_project_stats(os.path.join(utils.CANDIDATE_PATH, + conf['release'], ''), + False, []) ptl_fmt_args = dict( nom_end_date=utils.get_event('PTL Nominations')['end_str'], time_frame=time_frame, @@ -42,6 +49,9 @@ elif conf['election_type'] == 'ptl': ending_release=end_release, future_release=end_release.lower(), email_deadline=conf['timeframe']['email_deadline'], + num_projects_without_candidates=len(stats.without_candidate), + election_summary_stats=stats.election_summary(), + leaderless_url=LEADERLESS_URL, ) @@ -114,7 +124,7 @@ Happy running, print(email_text % (ptl_fmt_args)) -def ptl_nominations_last_days(num_projects_without_candidates): +def ptl_nominations_last_days(): email_text = """ A quick reminder that we are in the last hours for PTL candidate nominations. @@ -126,19 +136,21 @@ Make sure your nomination has been submitted to the openstack/election repository and approved by election officials. Election statistics[2]: +%(election_summary_stats)s -This means that with approximately 2 days left, %s projects will +This means that with approximately 2 days left, %(num_projects_without_candidates)s projects will be deemed leaderless. In this case the TC will oversee PTL selection as described by [3]. Thank you, [1] http://governance.openstack.org/election/#how-to-submit-a-candidacy -[2] Assuming the open reviews below are validated +[2] Any open reviews at https://review.openstack.org/#/q/is:open+project:openstack/election -[3] %s""" + have not been factored into these stats. +[3] %(leaderless_url)s""" # noqa - print(email_text % (num_projects_without_candidates, LEADERLESS_URL)) + print(email_text % (ptl_fmt_args)) def ptl_end_nominations(projects_no_candidates, @@ -465,6 +477,7 @@ def main(): parser_ptl = cmd_parsers.add_parser('ptl') parser_ptl.add_argument('template', choices=['election_season', 'nominations_kickoff', + 'nominations_last_days', ]) parser_tc = cmd_parsers.add_parser('tc') parser_tc.add_argument('template',