Skip to content

Commit 6a52508

Browse files
committed
Allow participants to start a vote
1 parent a33fa7b commit 6a52508

File tree

3 files changed

+74
-74
lines changed

3 files changed

+74
-74
lines changed

atr/blueprints/api/api.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,12 +1180,12 @@ async def vote_start(data: models.api.VoteStartArgs) -> DictResponse:
11801180

11811181
try:
11821182
async with storage.write(asf_uid) as write:
1183-
wacm = await write.as_project_committee_member(data.project)
1184-
permitted_recipients = util.permitted_voting_recipients(asf_uid, wacm.committee_name)
1183+
wacp = await write.as_project_committee_participant(data.project)
1184+
permitted_recipients = util.permitted_voting_recipients(asf_uid, wacp.committee_name)
11851185
if data.email_to not in permitted_recipients:
11861186
raise exceptions.Forbidden("Invalid mailing list choice")
11871187
# TODO: Get fullname and use instead of asf_uid
1188-
task = await wacm.vote.start(
1188+
task = await wacp.vote.start(
11891189
data.email_to,
11901190
data.project,
11911191
data.version,

atr/routes/voting.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,8 @@ async def _selected_revision_data(
258258
vote_duration_choice: int = util.unwrap(form.vote_duration.data)
259259
subject_data: str = util.unwrap(form.subject.data)
260260
body_data: str = util.unwrap(form.body.data)
261-
async with storage.write_as_committee_member(committee.name) as wacm:
262-
_task = await wacm.vote.start(
261+
async with storage.write_as_committee_participant(committee.name) as wacp:
262+
_task = await wacp.vote.start(
263263
email_to,
264264
project_name,
265265
version_name,

atr/storage/writers/vote.py

Lines changed: 69 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,75 @@ async def send_user_vote(
125125

126126
return email_recipient, ""
127127

128+
async def start(
129+
self,
130+
email_to: str,
131+
project_name: str,
132+
version_name: str,
133+
selected_revision_number: str,
134+
vote_duration_choice: int,
135+
subject_data: str,
136+
body_data: str,
137+
asf_uid: str,
138+
asf_fullname: str,
139+
release: sql.Release | None = None,
140+
promote: bool = True,
141+
permitted_recipients: list[str] | None = None,
142+
) -> sql.Task:
143+
if release is None:
144+
release = await self.__data.release(
145+
project_name=project_name,
146+
version=version_name,
147+
_project=True,
148+
_committee=True,
149+
).demand(storage.AccessError("Release not found"))
150+
if permitted_recipients is None:
151+
permitted_recipients = util.permitted_voting_recipients(asf_uid, self.__committee_name)
152+
if email_to not in permitted_recipients:
153+
# This will be checked again by tasks/vote.py for extra safety
154+
log.info(f"Invalid mailing list choice: {email_to} not in {permitted_recipients}")
155+
raise storage.AccessError("Invalid mailing list choice")
156+
157+
if promote is True:
158+
# This verifies the state and sets the phase to RELEASE_CANDIDATE
159+
error = await interaction.promote_release(
160+
self.__data, release.name, selected_revision_number, vote_manual=False
161+
)
162+
if error:
163+
raise storage.AccessError(error)
164+
165+
# TODO: We also need to store the duration of the vote
166+
# We can't allow resolution of the vote until the duration has elapsed
167+
# But we allow the user to specify in the form
168+
# And yet we also have ReleasePolicy.min_hours
169+
# Presumably this sets the default, and the form takes precedence?
170+
# ReleasePolicy.min_hours can also be 0, though
171+
172+
# Create a task for vote initiation
173+
task = sql.Task(
174+
status=sql.TaskStatus.QUEUED,
175+
task_type=sql.TaskType.VOTE_INITIATE,
176+
task_args=tasks_vote.Initiate(
177+
release_name=release.name,
178+
email_to=email_to,
179+
vote_duration=vote_duration_choice,
180+
initiator_id=asf_uid,
181+
initiator_fullname=asf_fullname,
182+
subject=subject_data,
183+
body=body_data,
184+
).model_dump(),
185+
asf_uid=asf_uid,
186+
project_name=project_name,
187+
version_name=version_name,
188+
)
189+
self.__data.add(task)
190+
await self.__data.commit()
191+
192+
# TODO: We should log all outgoing email and the session so that users can confirm
193+
# And can be warned if there was a failure
194+
# (The message should be shown on the vote resolution page)
195+
return task
196+
128197

129198
class CommitteeMember(CommitteeParticipant):
130199
def __init__(
@@ -357,75 +426,6 @@ async def send_resolution(
357426
await self.__data.commit()
358427
return None
359428

360-
async def start(
361-
self,
362-
email_to: str,
363-
project_name: str,
364-
version_name: str,
365-
selected_revision_number: str,
366-
vote_duration_choice: int,
367-
subject_data: str,
368-
body_data: str,
369-
asf_uid: str,
370-
asf_fullname: str,
371-
release: sql.Release | None = None,
372-
promote: bool = True,
373-
permitted_recipients: list[str] | None = None,
374-
) -> sql.Task:
375-
if release is None:
376-
release = await self.__data.release(
377-
project_name=project_name,
378-
version=version_name,
379-
_project=True,
380-
_committee=True,
381-
).demand(storage.AccessError("Release not found"))
382-
if permitted_recipients is None:
383-
permitted_recipients = util.permitted_voting_recipients(asf_uid, self.__committee_name)
384-
if email_to not in permitted_recipients:
385-
# This will be checked again by tasks/vote.py for extra safety
386-
log.info(f"Invalid mailing list choice: {email_to} not in {permitted_recipients}")
387-
raise storage.AccessError("Invalid mailing list choice")
388-
389-
if promote is True:
390-
# This verifies the state and sets the phase to RELEASE_CANDIDATE
391-
error = await interaction.promote_release(
392-
self.__data, release.name, selected_revision_number, vote_manual=False
393-
)
394-
if error:
395-
raise storage.AccessError(error)
396-
397-
# TODO: We also need to store the duration of the vote
398-
# We can't allow resolution of the vote until the duration has elapsed
399-
# But we allow the user to specify in the form
400-
# And yet we also have ReleasePolicy.min_hours
401-
# Presumably this sets the default, and the form takes precedence?
402-
# ReleasePolicy.min_hours can also be 0, though
403-
404-
# Create a task for vote initiation
405-
task = sql.Task(
406-
status=sql.TaskStatus.QUEUED,
407-
task_type=sql.TaskType.VOTE_INITIATE,
408-
task_args=tasks_vote.Initiate(
409-
release_name=release.name,
410-
email_to=email_to,
411-
vote_duration=vote_duration_choice,
412-
initiator_id=asf_uid,
413-
initiator_fullname=asf_fullname,
414-
subject=subject_data,
415-
body=body_data,
416-
).model_dump(),
417-
asf_uid=asf_uid,
418-
project_name=project_name,
419-
version_name=version_name,
420-
)
421-
self.__data.add(task)
422-
await self.__data.commit()
423-
424-
# TODO: We should log all outgoing email and the session so that users can confirm
425-
# And can be warned if there was a failure
426-
# (The message should be shown on the vote resolution page)
427-
return task
428-
429429
# def __committee_member_or_admin(self, committee: sql.Committee, asf_uid: str) -> None:
430430
# if not (user.is_committee_member(committee, asf_uid) or user.is_admin(asf_uid)):
431431
# raise storage.AccessError("You do not have permission to perform this action")

0 commit comments

Comments
 (0)