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 553cc0a  Add unfinished releases to the sidebar
553cc0a is described below

commit 553cc0a13c6e00b3e07c55ab666442ef6ef73ef4
Author: Sean B. Palmer <[email protected]>
AuthorDate: Tue Apr 22 19:31:04 2025 +0100

    Add unfinished releases to the sidebar
---
 atr/db/__init__.py                  | 31 +++++++++++++++++++++++++++++++
 atr/routes/candidate.py             |  2 --
 atr/routes/mapping.py               | 35 +++++++++++++++++++++++++++++++++++
 atr/server.py                       |  3 +++
 atr/templates/includes/sidebar.html | 13 +++++++++++++
 atr/templates/index-committer.html  | 10 +---------
 6 files changed, 83 insertions(+), 11 deletions(-)

diff --git a/atr/db/__init__.py b/atr/db/__init__.py
index 517e013..c19cfb9 100644
--- a/atr/db/__init__.py
+++ b/atr/db/__init__.py
@@ -31,6 +31,7 @@ import sqlmodel.sql.expression as expression
 
 import atr.config as config
 import atr.db.models as models
+import atr.user as user
 import atr.util as util
 
 if TYPE_CHECKING:
@@ -688,6 +689,36 @@ async def tasks_ongoing(project_name: str, version_name: 
str, draft_revision: st
         return result.scalar_one()
 
 
+async def unfinished_releases(asfuid: str) -> dict[str, list[models.Release]]:
+    releases: dict[str, list[models.Release]] = {}
+    async with session() as data:
+        user_projects = await user.projects(asfuid)
+        user_projects.sort(key=lambda p: p.display_name)
+
+        active_phases = [
+            models.ReleasePhase.RELEASE_CANDIDATE_DRAFT,
+            models.ReleasePhase.RELEASE_CANDIDATE,
+            models.ReleasePhase.RELEASE_PREVIEW,
+        ]
+        for project in user_projects:
+            stmt = (
+                sqlmodel.select(models.Release)
+                .where(
+                    models.Release.project_id == project.id,
+                    
validate_instrumented_attribute(models.Release.phase).in_(active_phases),
+                )
+                .options(select_in_load(models.Release.project))
+                
.order_by(validate_instrumented_attribute(models.Release.created).desc())
+            )
+            result = await data.execute(stmt)
+            active_releases = list(result.scalars().all())
+            if active_releases:
+                active_releases.sort(key=lambda r: r.created, reverse=True)
+                releases[project.short_display_name] = active_releases
+
+    return releases
+
+
 def validate_instrumented_attribute(obj: Any) -> orm.InstrumentedAttribute:
     """Check if the given object is an InstrumentedAttribute."""
     if not isinstance(obj, orm.InstrumentedAttribute):
diff --git a/atr/routes/candidate.py b/atr/routes/candidate.py
index 0a915df..d6c2b53 100644
--- a/atr/routes/candidate.py
+++ b/atr/routes/candidate.py
@@ -96,7 +96,6 @@ async def resolve_release(
             latest_vote_task = task
             break
 
-    logging.warning(f"Latest vote task 2: {latest_vote_task}")
     if latest_vote_task and (latest_vote_task.status == 
models.TaskStatus.COMPLETED) and latest_vote_task.result:
         task_mid = _task_mid(latest_vote_task)
         archive_url = await _task_archive_url(task_mid)
@@ -220,7 +219,6 @@ async def _resolve_get(session: routes.CommitterSession) -> 
str:
                 latest_vote_task = task
                 break
 
-        logging.warning(f"Latest vote task: {latest_vote_task}")
         if latest_vote_task and (latest_vote_task.status == 
models.TaskStatus.COMPLETED) and latest_vote_task.result:
             task_mid = _task_mid(latest_vote_task)
             archive_url = await _task_archive_url(task_mid)
diff --git a/atr/routes/mapping.py b/atr/routes/mapping.py
new file mode 100644
index 0000000..878bbc7
--- /dev/null
+++ b/atr/routes/mapping.py
@@ -0,0 +1,35 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+import atr.db.models as models
+import atr.routes.candidate as candidate
+import atr.routes.draft as draft
+import atr.routes.preview as preview
+import atr.util as util
+
+
+def release_as_url(release: models.Release) -> str:
+    if release.phase.value == "release_candidate_draft":
+        return util.as_url(draft.compose, project_name=release.project.name, 
version_name=release.version)
+    elif release.phase.value == "release_candidate":
+        resolve_release = candidate.resolve_release  # type: ignore[has-type]
+        return util.as_url(resolve_release, project_name=release.project.name, 
version_name=release.version)
+    elif release.phase.value == "release_preview":
+        announce_release = preview.announce_release  # type: ignore[has-type]
+        return util.as_url(announce_release, 
project_name=release.project.name, version_name=release.version)
+    else:
+        raise ValueError(f"Unknown release phase: {release.phase}")
diff --git a/atr/server.py b/atr/server.py
index f49ce1e..2fea323 100644
--- a/atr/server.py
+++ b/atr/server.py
@@ -106,6 +106,7 @@ def app_setup_context(app: base.QuartApp) -> None:
     @app.context_processor
     async def app_wide() -> dict[str, Any]:
         import atr.metadata as metadata
+        import atr.routes.mapping as mapping
         import atr.routes.modules as modules
 
         return {
@@ -116,6 +117,8 @@ def app_setup_context(app: base.QuartApp) -> None:
             "is_viewing_as_admin_fn": util.is_user_viewing_as_admin,
             "is_committee_member_fn": user.is_committee_member,
             "routes": modules,
+            "unfinished_releases_fn": db.unfinished_releases,
+            "release_as_url": mapping.release_as_url,
             "version": metadata.version,
         }
 
diff --git a/atr/templates/includes/sidebar.html 
b/atr/templates/includes/sidebar.html
index 8c6dd5e..73404fc 100644
--- a/atr/templates/includes/sidebar.html
+++ b/atr/templates/includes/sidebar.html
@@ -29,6 +29,19 @@
   </div>
   <nav>
     {% if current_user %}
+      {% set unfinished_releases = unfinished_releases_fn(current_user.uid) %}
+      {% if unfinished_releases %}
+        {% for project_short_display_name, releases in 
unfinished_releases.items() %}
+          <h3>{{ project_short_display_name }}</h3>
+          <ul>
+            {% for release in releases %}
+              <li>
+                <a href="{{ release_as_url(release) }}">{{ release.version 
}}</a>
+              </li>
+            {% endfor %}
+          </ul>
+        {% endfor %}
+      {% endif %}
       <h3>Release candidate drafts</h3>
       <ul>
         <li>
diff --git a/atr/templates/index-committer.html 
b/atr/templates/index-committer.html
index a361faa..25a534e 100644
--- a/atr/templates/index-committer.html
+++ b/atr/templates/index-committer.html
@@ -119,15 +119,7 @@
         <div class="d-flex flex-wrap gap-3">
           {% for release in active_releases %}
             {% set current_phase_index = phase_index_map.get(release.phase, 
-1) %}
-            {% if release.phase.value == "release_candidate_draft" %}
-              {% set release_link = as_url(routes.draft.compose, 
project_name=release.project.name, version_name=release.version) %}
-            {% elif release.phase.value == "release_candidate" %}
-              {% set release_link = as_url(routes.candidate.resolve_release, 
project_name=release.project.name, version_name=release.version) %}
-            {% elif release.phase.value == "release_preview" %}
-              {% set release_link = as_url(routes.preview.announce_release, 
project_name=release.project.name, version_name=release.version) %}
-            {% endif %}
-
-            <a href="{{ release_link }}" class="text-decoration-none">
+            <a href="{{ release_as_url(release) }}" 
class="text-decoration-none">
               <div class="card h-100 shadow-sm hover-lift atr-cursor-pointer 
page-card">
                 <div class="card-body text-center d-flex flex-column 
justify-content-center">
                   <div class="fw-semibold mb-3 fs-4 page-version">{{ 
release.version }}</div>


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to