Add new combined election type
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
This commit is contained in:
parent
30ddc77e97
commit
c6406fdbd5
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,6 +15,7 @@ doc/source/archive_toc.rst
|
|||||||
doc/source/*/*.rst
|
doc/source/*/*.rst
|
||||||
doc/source/ptl.rst
|
doc/source/ptl.rst
|
||||||
doc/source/tc.rst
|
doc/source/tc.rst
|
||||||
|
doc/source/combined.rst
|
||||||
doc/source/events.rst
|
doc/source/events.rst
|
||||||
doc/source/configuration.rst
|
doc/source/configuration.rst
|
||||||
doc/source/results/*/announce_ptl.rst
|
doc/source/results/*/announce_ptl.rst
|
||||||
|
@ -99,10 +99,8 @@ def build_lists(app):
|
|||||||
if utils.election_is_running():
|
if utils.election_is_running():
|
||||||
# Current candidates
|
# Current candidates
|
||||||
candidates_list = utils.build_candidates_list()
|
candidates_list = utils.build_candidates_list()
|
||||||
if not utils.is_tc_election():
|
election_type = utils.conf.get('election_type', '').lower()
|
||||||
render_list("ptl", candidates_list)
|
render_list(election_type, candidates_list)
|
||||||
else:
|
|
||||||
render_list("tc", candidates_list)
|
|
||||||
|
|
||||||
# Archived elections
|
# Archived elections
|
||||||
previous_toc = [
|
previous_toc = [
|
||||||
@ -127,13 +125,9 @@ class CandidatesDirective(Directive):
|
|||||||
def run(self):
|
def run(self):
|
||||||
if not utils.election_is_running():
|
if not utils.election_is_running():
|
||||||
return []
|
return []
|
||||||
|
election_type = utils.conf.get('election_type', '').lower()
|
||||||
|
|
||||||
rst = '.. include:: '
|
rst = '.. include:: %s.rst' % (election_type)
|
||||||
if utils.is_tc_election():
|
|
||||||
rst += 'tc.rst'
|
|
||||||
else:
|
|
||||||
rst += 'ptl.rst'
|
|
||||||
|
|
||||||
result = ViewList()
|
result = ViewList()
|
||||||
for idx, line in enumerate(rst.splitlines()):
|
for idx, line in enumerate(rst.splitlines()):
|
||||||
result.append(line, 'CandidatesDirective', idx)
|
result.append(line, 'CandidatesDirective', idx)
|
||||||
|
17
doc/source/_exts/combined.jinja
Normal file
17
doc/source/_exts/combined.jinja
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{{ election.capitalize() }} TC Candidates
|
||||||
|
======================
|
||||||
|
|
||||||
|
{% for candidate in candidates['TC'] %}
|
||||||
|
* `{{ candidate['fullname'] }} {% if candidate['ircname'] is not none %}({{ candidate['ircname'] }}){% endif %} <{{ candidate['url'] }}>`__
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{{ election.capitalize() }} PTL Candidates
|
||||||
|
======================
|
||||||
|
{% for project in projects|sort %}{% if project != 'TC' %}
|
||||||
|
* {{ project.replace('_', ' ') }}
|
||||||
|
|
||||||
|
{% for candidate in candidates[project] %}
|
||||||
|
* `{{ candidate['fullname'] }} {% if candidate['ircname'] is not none %}({{ candidate['ircname'] }}){% endif %} <{{ candidate['url'] }}>`__
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% endif %}{% endfor %}
|
@ -55,6 +55,7 @@ def main():
|
|||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
projects = utils.get_projects(tag=args.tag, fallback_to_master=True)
|
projects = utils.get_projects(tag=args.tag, fallback_to_master=True)
|
||||||
|
election_type = utils.conf.get('election_type', '').lower()
|
||||||
|
|
||||||
for review in get_reviews():
|
for review in get_reviews():
|
||||||
if review['status'] != 'NEW':
|
if review['status'] != 'NEW':
|
||||||
@ -77,7 +78,10 @@ def main():
|
|||||||
candiate_ok = checks.validate_member(filepath)
|
candiate_ok = checks.validate_member(filepath)
|
||||||
|
|
||||||
if candiate_ok:
|
if candiate_ok:
|
||||||
if not utils.is_tc_election():
|
# If we're a PTL election OR if the team is not TC we need
|
||||||
|
# to check for validating changes
|
||||||
|
if (election_type == 'ptl'
|
||||||
|
or (election_type == 'combined' and team != 'TC')):
|
||||||
if args.interactive:
|
if args.interactive:
|
||||||
print('The following commit and profile validate this '
|
print('The following commit and profile validate this '
|
||||||
'candidate:')
|
'candidate:')
|
||||||
|
@ -125,6 +125,7 @@ def main():
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
projects = utils.get_projects(tag=args.tag, fallback_to_master=True)
|
projects = utils.get_projects(tag=args.tag, fallback_to_master=True)
|
||||||
|
election_type = utils.conf.get('election_type', '').lower()
|
||||||
|
|
||||||
if args.files:
|
if args.files:
|
||||||
to_process = args.files
|
to_process = args.files
|
||||||
@ -134,13 +135,24 @@ def main():
|
|||||||
to_process = utils.find_candidate_files(election=args.release)
|
to_process = utils.find_candidate_files(election=args.release)
|
||||||
|
|
||||||
for filepath in to_process:
|
for filepath in to_process:
|
||||||
|
email = utils.get_email(filepath)
|
||||||
|
team = os.path.basename(os.path.dirname(filepath))
|
||||||
|
|
||||||
|
# Some kind souls remove the .placeholder file when they upload
|
||||||
|
# a candidacy
|
||||||
|
if email == '.placeholder':
|
||||||
|
continue
|
||||||
|
|
||||||
candidate_ok = True
|
candidate_ok = True
|
||||||
|
|
||||||
candidate_ok &= validate_filename(filepath)
|
candidate_ok &= validate_filename(filepath)
|
||||||
candidate_ok &= validate_member(filepath)
|
candidate_ok &= validate_member(filepath)
|
||||||
|
|
||||||
if candidate_ok and not utils.is_tc_election():
|
if candidate_ok:
|
||||||
candidate_ok &= check_for_changes(projects, filepath, args.limit)
|
if (election_type == 'ptl'
|
||||||
|
or (election_type == 'combined' and team != 'TC')):
|
||||||
|
candidate_ok &= check_for_changes(projects, filepath,
|
||||||
|
args.limit)
|
||||||
|
|
||||||
errors |= not candidate_ok
|
errors |= not candidate_ok
|
||||||
|
|
||||||
|
@ -133,6 +133,9 @@ def main():
|
|||||||
|
|
||||||
args = parser.parse_args()
|
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():
|
if utils.is_tc_election():
|
||||||
print('This tool only works for PTL elections not TC')
|
print('This tool only works for PTL elections not TC')
|
||||||
return 0
|
return 0
|
||||||
|
@ -31,7 +31,9 @@ fmt_args = dict(
|
|||||||
start_release=start_release,
|
start_release=start_release,
|
||||||
time_frame=time_frame,
|
time_frame=time_frame,
|
||||||
)
|
)
|
||||||
if utils.is_tc_election():
|
|
||||||
|
election_type = utils.conf.get('election_type', '').lower()
|
||||||
|
if election_type in ['tc', 'combined']:
|
||||||
fmt_args.update(dict(
|
fmt_args.update(dict(
|
||||||
start_nominations=utils.get_event('TC Nominations')['start_str'],
|
start_nominations=utils.get_event('TC Nominations')['start_str'],
|
||||||
end_nominations=utils.get_event('TC Nominations')['end_str'],
|
end_nominations=utils.get_event('TC Nominations')['end_str'],
|
||||||
@ -42,7 +44,11 @@ if utils.is_tc_election():
|
|||||||
poll_name='%s TC Election' % (conf['release'].capitalize()),
|
poll_name='%s TC Election' % (conf['release'].capitalize()),
|
||||||
))
|
))
|
||||||
template_names += ['campaigning_kickoff']
|
template_names += ['campaigning_kickoff']
|
||||||
else:
|
|
||||||
|
# NOTE(tonyb): In the case of a "combined" election we assume that the dates
|
||||||
|
# for each "phase" (nominations, campaigning or elections) overlap so updating
|
||||||
|
# the end_nominations key here with the PTL date should be safe
|
||||||
|
if election_type in ['ptl', 'combined']:
|
||||||
# NOTE(tonyb): We need an empty item last to ensure the path ends in a
|
# NOTE(tonyb): We need an empty item last to ensure the path ends in a
|
||||||
# tailing '/'
|
# tailing '/'
|
||||||
stats.collect_project_stats(os.path.join(utils.CANDIDATE_PATH,
|
stats.collect_project_stats(os.path.join(utils.CANDIDATE_PATH,
|
||||||
|
@ -41,26 +41,35 @@ class TestGerritUtils(base.ElectionTestCase):
|
|||||||
|
|
||||||
|
|
||||||
class TestFindCandidateFiles(base.ElectionTestCase):
|
class TestFindCandidateFiles(base.ElectionTestCase):
|
||||||
@mock.patch.object(utils, 'is_tc_election', return_value=False)
|
|
||||||
@mock.patch('os.path.exists', return_value=True)
|
@mock.patch('os.path.exists', return_value=True)
|
||||||
@mock.patch('os.listdir', side_effect=[['SomeProject', 'TC'],
|
@mock.patch('os.listdir', side_effect=[['SomeProject', 'TC'],
|
||||||
['invalid@example.com']])
|
['invalid@example.com']])
|
||||||
def test_ptl_lists(self, mock_listdir, mock_path_exists,
|
def test_ptl_lists(self, mock_listdir, mock_path_exists):
|
||||||
mock_is_tc_election):
|
with mock.patch.dict(utils.conf, dict(election_type='ptl')):
|
||||||
candidate_files = utils.find_candidate_files(election='fake')
|
candidate_files = utils.find_candidate_files(election='fake')
|
||||||
self.assertEqual(['candidates/fake/SomeProject/invalid@example.com'],
|
self.assertEqual(['candidates/fake/SomeProject/invalid@example.com'],
|
||||||
candidate_files)
|
candidate_files)
|
||||||
|
|
||||||
@mock.patch.object(utils, 'is_tc_election', return_value=True)
|
|
||||||
@mock.patch('os.path.exists', return_value=True)
|
@mock.patch('os.path.exists', return_value=True)
|
||||||
@mock.patch('os.listdir', side_effect=[['SomeProject', 'TC'],
|
@mock.patch('os.listdir', side_effect=[['SomeProject', 'TC'],
|
||||||
['invalid@example.com']])
|
['invalid@example.com']])
|
||||||
def test_tc_lists(self, mock_listdir, mock_path_exists,
|
def test_tc_lists(self, mock_listdir, mock_path_exists):
|
||||||
mock_is_tc_election):
|
with mock.patch.dict(utils.conf, dict(election_type='tc')):
|
||||||
candidate_files = utils.find_candidate_files(election='fake')
|
candidate_files = utils.find_candidate_files(election='fake')
|
||||||
self.assertEqual(['candidates/fake/TC/invalid@example.com'],
|
self.assertEqual(['candidates/fake/TC/invalid@example.com'],
|
||||||
candidate_files)
|
candidate_files)
|
||||||
|
|
||||||
|
@mock.patch('os.path.exists', return_value=True)
|
||||||
|
@mock.patch('os.listdir', side_effect=[['SomeProject', 'TC'],
|
||||||
|
['invalid@example.com'],
|
||||||
|
['invalid@example.com']])
|
||||||
|
def test_combined_lists(self, mock_listdir, mock_path_exists):
|
||||||
|
with mock.patch.dict(utils.conf, dict(election_type='combined')):
|
||||||
|
candidate_files = utils.find_candidate_files(election='fake')
|
||||||
|
self.assertEqual(['candidates/fake/SomeProject/invalid@example.com',
|
||||||
|
'candidates/fake/TC/invalid@example.com'],
|
||||||
|
candidate_files)
|
||||||
|
|
||||||
|
|
||||||
class TestBuildCandidatesList(base.ElectionTestCase):
|
class TestBuildCandidatesList(base.ElectionTestCase):
|
||||||
@mock.patch.object(utils, 'lookup_member')
|
@mock.patch.object(utils, 'lookup_member')
|
||||||
|
@ -286,17 +286,18 @@ def election_is_running():
|
|||||||
|
|
||||||
def find_candidate_files(election=conf['release']):
|
def find_candidate_files(election=conf['release']):
|
||||||
election_path = os.path.join(CANDIDATE_PATH, election)
|
election_path = os.path.join(CANDIDATE_PATH, election)
|
||||||
|
election_type = conf.get('election_type', '').lower()
|
||||||
if os.path.exists(election_path):
|
if os.path.exists(election_path):
|
||||||
project_list = os.listdir(election_path)
|
project_list = os.listdir(election_path)
|
||||||
else:
|
else:
|
||||||
project_list = []
|
project_list = []
|
||||||
|
|
||||||
if is_tc_election():
|
if election_type == 'tc':
|
||||||
project_list = list(filter(
|
project_list = list(filter(
|
||||||
lambda p: p in ['TC'],
|
lambda p: p in ['TC'],
|
||||||
project_list
|
project_list
|
||||||
))
|
))
|
||||||
else:
|
elif election_type == 'ptl':
|
||||||
project_list = list(filter(
|
project_list = list(filter(
|
||||||
lambda p: p not in ['TC'],
|
lambda p: p not in ['TC'],
|
||||||
project_list
|
project_list
|
||||||
|
Loading…
Reference in New Issue
Block a user