c6406fdbd5
This type is for when TC and PTL elections must overlap. There is a built in assumption that each "phase" of an election: - Nominations - Campaigning - Election are completely synchronous NOTE: This change leaves the template-emails broken, as I do not add new functions for combined_$email. This is because I'd like to If64c075b4a799574bc51ccd13019d472ee9a4b0e merge first, and I'd also like to avoid to many rebases Change-Id: Id39c50e71dc91bea8034ce443287463094e2f37f
155 lines
5.7 KiB
Python
Executable File
155 lines
5.7 KiB
Python
Executable File
# 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
|