# 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. from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals import argparse import datetime import os import pytz from openstack_election import utils counts = {'projects': 0, 'nominations': 0, 'with_candidate': 0} need_election = [] without_candidate = [] def as_percentage(progress, total): try: p = (100.0 * (float(progress) / float(total))) except ZeroDivisionError: p = 0.0 return p def as_utctime(dt): return "%s" % (dt.strftime("%Y-%m-%d %T UTC")) def collect_project_stats(basedir, verbose, projects): global counts for directory, dirnames, filenames in sorted(os.walk(basedir)): project = directory[len(basedir):] if project == "TC": continue if project in projects: if verbose: print('Adding manual entry for project: %s' % (project)) filenames = ['fake@placeholder'] candidates = list(filter(lambda x: '@' in x, filenames)) candidates_count = len(candidates) if not filenames == []: counts['projects'] += 1 if candidates_count != 0: counts['with_candidate'] += 1 else: without_candidate.append(project) if candidates_count >= 2: counts['nominations'] += 1 need_election.append(project) if verbose: print("%-25s : (%d) %s" % (project, len(candidates), ', '.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, help='Increase program verbosity') parser.add_argument('-r', '--release', default=utils.conf['release'], help='Which nominations to look at') parser.add_argument('-b', '--basedir', default=os.getcwd(), help='Path to git clone of openstack/election') parser.add_argument('--projects', dest='projects', nargs='+', default=[], help=('These projects have candidates that are ' 'validated by one election but not yet' 'approved')) args = parser.parse_args() # NOTE(tonyb): If we're a "combined" election we'll have the required # events for the statistics to render collectly so we can just use a quick # 'is_tc' check here if utils.is_tc_election(): print('This tool only works for PTL elections not TC') return 0 args.basedir = os.path.join(args.basedir, 'candidates', args.release, '') args.basedir = os.path.expanduser(args.basedir) collect_project_stats(args.basedir, args.verbose, args.projects) if args.verbose: print("-" * 51) 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:" "\n\t%s\n" % (" ".join(args.projects))) return 0