This is an automated email from the ASF dual-hosted git repository.
sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-release.git
The following commit(s) were added to refs/heads/main by this push:
new c568c76 Add voter status to vote report, and improve the parser
c568c76 is described below
commit c568c76f9a33bd84fac48543587abc7c82bcd5c2
Author: Sean B. Palmer <[email protected]>
AuthorDate: Fri Jun 27 16:52:11 2025 +0100
Add voter status to vote report, and improve the parser
---
atr/routes/vote.py | 80 +++++++++++++++++++++++++++++-----------
atr/static/css/atr.css | 8 ++++
atr/templates/vote-tabulate.html | 14 +++++--
3 files changed, 76 insertions(+), 26 deletions(-)
diff --git a/atr/routes/vote.py b/atr/routes/vote.py
index 9db5df9..cbea482 100644
--- a/atr/routes/vote.py
+++ b/atr/routes/vote.py
@@ -35,7 +35,7 @@ import atr.tasks.message as message
import atr.template as template
import atr.util as util
-TEST_MID = "CAH5JyZo8QnWmg9CwRSwWY=givhxw4nilyenjo71fkdk81j5...@mail.gmail.com"
+TEST_MID = "CAFHDsVzgtfboqYF+a3owaNf+55MUiENWd3g53mU4rD=whkx...@mail.gmail.com"
class CastVoteForm(util.QuartFormTyped):
@@ -60,6 +60,7 @@ class Vote(enum.Enum):
class VoteEmail(schema.Strict):
asf_uid: str
from_email: str
+ status: str
asf_eid: str
iso_datetime: str
vote: Vote
@@ -215,15 +216,23 @@ async def _tabulate_votes(release: models.Release,
archive_url: str) -> dict[str
tabulated_votes = {}
thread_id = archive_url.split("/")[-1]
async for _mid, msg in util.thread_messages(thread_id):
- from_email = util.email_from_uid(msg.get("from_raw", ""))
- if not from_email:
+ from_raw = msg.get("from_raw", "")
+ from_email_lower = util.email_from_uid(from_raw)
+ if not from_email_lower:
continue
- if from_email.endswith("@apache.org"):
- asf_uid = from_email.split("@")[0]
- elif from_email in email_to_uid:
- asf_uid = email_to_uid[from_email]
+ from_email_lower = from_email_lower.removesuffix(".invalid")
+ asf_uid = None
+ if from_email_lower.endswith("@apache.org"):
+ asf_uid = from_email_lower.split("@")[0]
+ elif from_email_lower in email_to_uid:
+ asf_uid = email_to_uid[from_email_lower]
+
+ if asf_uid is None:
+ asf_uid = from_email_lower
+ status = "Unknown"
else:
- continue
+ list_raw = msg.get("list_raw", "")
+ status = await _tabulate_vote_status(asf_uid, list_raw, release)
subject = msg.get("subject", "")
if "[RESULT]" in subject:
@@ -245,7 +254,8 @@ async def _tabulate_votes(release: models.Release,
archive_url: str) -> dict[str
vote_email = VoteEmail(
asf_uid=asf_uid,
- from_email=from_email,
+ from_email=from_email_lower,
+ status=status,
asf_eid=msg.get("mid", ""),
iso_datetime=msg.get("date", ""),
vote=vote_cast,
@@ -260,6 +270,22 @@ async def _tabulate_votes(release: models.Release,
archive_url: str) -> dict[str
return tabulated_votes
+def _tabulate_vote_break(line: str) -> bool:
+ if line == "-- ":
+ # Start of a signature
+ return True
+ if line.startswith("On ") and (line[6:8] == ", "):
+ # Start of a quoted email
+ return True
+ if line.startswith("From: "):
+ # Start of a quoted email
+ return True
+ if line.startswith("________"):
+ # This is sometimes used as an "On " style quotation marker
+ return True
+ return False
+
+
def _tabulate_vote_castings(body: str) -> list[tuple[Vote, str]]:
castings = []
for line in body.split("\n"):
@@ -284,19 +310,6 @@ def _tabulate_vote_castings(body: str) -> list[tuple[Vote,
str]]:
return castings
-def _tabulate_vote_break(line: str) -> bool:
- if line == "-- ":
- # Start of a signature
- return True
- if line.startswith("On ") and line[6:8] == ", ":
- # Start of a quoted email
- return True
- if line.startswith("________"):
- # This is sometimes used as an "On " style quotation marker
- return True
- return False
-
-
def _tabulate_vote_continue(line: str) -> bool:
explanation_indicators = [
"[ ] +1",
@@ -314,6 +327,27 @@ def _tabulate_vote_continue(line: str) -> bool:
return False
+async def _tabulate_vote_status(asf_uid: str, list_raw: str, release:
models.Release) -> str:
+ status = "Unknown"
+ if util.is_dev_environment():
+ committee_label = list_raw.split(".apache.org", 1)[0].split(".", 1)[-1]
+ async with db.session() as data:
+ committee = await data.committee(name=committee_label).get()
+ if committee is not None:
+ if asf_uid in committee.committee_members:
+ status = "Binding"
+ else:
+ status = "Non-binding"
+ elif release.project is not None:
+ if release.project.committee is not None:
+ print(repr(asf_uid), release.project.committee.committee_members)
+ if asf_uid in release.project.committee.committee_members:
+ status = "Binding"
+ else:
+ status = "Non-binding"
+ return status
+
+
async def _task_archive_url(task_mid: str) -> str | None:
if "@" not in task_mid:
return None
@@ -340,6 +374,8 @@ async def _task_archive_url_cached(task_mid: str | None) ->
str | None:
dev_urls = {
"CAH5JyZo8QnWmg9CwRSwWY=givhxw4nilyenjo71fkdk81j5...@mail.gmail.com":
"https://lists.apache.org/thread/z0o7xnjnyw2o886rxvvq2ql4rdfn754w",
"[email protected]":
"https://lists.apache.org/thread/619hn4x796mh3hkk3kxg1xnl48dy2s64",
+ "CAA9ykM+bMPNk=bof9hj0o+mjn1igppoj+pkdzhcam0ddvi+...@mail.gmail.com":
"https://lists.apache.org/thread/x0m3p2xqjvflgtkb6oxqysm36cr9l5mg",
+ "CAFHDsVzgtfboqYF+a3owaNf+55MUiENWd3g53mU4rD=whkx...@mail.gmail.com":
"https://lists.apache.org/thread/brj0k3g8pq63g8f7xhmfg2rbt1240nts",
}
if task_mid in dev_urls:
return dev_urls[task_mid]
diff --git a/atr/static/css/atr.css b/atr/static/css/atr.css
index 727525d..91b0c33 100644
--- a/atr/static/css/atr.css
+++ b/atr/static/css/atr.css
@@ -480,6 +480,14 @@ aside.sidebar nav a:hover {
color: #cc0033;
}
+.atr-green {
+ color: #007700 !important;
+}
+
+.atr-red {
+ color: #cc0033 !important;
+}
+
.atr-hide {
display: none;
}
diff --git a/atr/templates/vote-tabulate.html b/atr/templates/vote-tabulate.html
index cb5110a..ed9586a 100644
--- a/atr/templates/vote-tabulate.html
+++ b/atr/templates/vote-tabulate.html
@@ -24,9 +24,10 @@
<table class="table table-striped">
<thead>
<tr>
- <th>ASF UID</th>
- <th>Vote</th>
- <th>Link</th>
+ <th>ASF UID or email</th>
+ <th class="text-center">Vote</th>
+ <th class="text-center">Status</th>
+ <th class="text-center">Link</th>
<th>Context</th>
</tr>
</thead>
@@ -34,7 +35,12 @@
{% for asf_uid, vote_email in tabulated_votes.items() %}
<tr>
<td class="atr-nowrap">{{ vote_email.asf_uid }}</td>
- <td class="atr-nowrap text-center">{{ vote_email.vote.value }}</td>
+ <td class="atr-nowrap text-center {% if vote_email.status ==
'Binding' %}fw-bold{% endif %} {% if vote_email.vote.value == 'Yes'
%}atr-green{% elif vote_email.vote.value == 'No' %}atr-red{% endif %}">
+ {{ vote_email.vote.value }}
+ </td>
+ <td class="atr-nowrap text-center {% if vote_email.status ==
'Binding' %}fw-bold{% endif %}">
+ {{ vote_email.status }}
+ </td>
<td class="atr-nowrap text-center">
<a href="https://lists.apache.org/thread/{{ vote_email.asf_eid
}}"
target="_blank">Email</a>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]