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]

Reply via email to