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 bdf626b  Move the vote resolution form from its own page onto the vote 
page
bdf626b is described below

commit bdf626bf9575119c1ff9bb896b41f806395bf4f7
Author: Sean B. Palmer <[email protected]>
AuthorDate: Mon May 5 15:12:12 2025 +0100

    Move the vote resolution form from its own page onto the vote page
---
 atr/routes/compose.py                        |   3 +
 atr/routes/resolve.py                        |  45 +-------
 atr/templates/candidate-resolve-release.html | 154 ---------------------------
 atr/templates/check-selected.html            |  35 +++++-
 atr/templates/phase-view.html                |   5 +-
 atr/templates/release-select.html            |   3 +-
 playwright/test.py                           |   6 +-
 7 files changed, 48 insertions(+), 203 deletions(-)

diff --git a/atr/routes/compose.py b/atr/routes/compose.py
index 5673f5e..11820f6 100644
--- a/atr/routes/compose.py
+++ b/atr/routes/compose.py
@@ -28,6 +28,7 @@ import atr.db.models as models
 import atr.revision as revision
 import atr.routes as routes
 import atr.routes.draft as draft
+import atr.routes.resolve as resolve
 import atr.util as util
 
 if TYPE_CHECKING:
@@ -96,6 +97,7 @@ async def check(
 
     delete_draft_form = await draft.DeleteForm.create_form()
     delete_file_form = await draft.DeleteFileForm.create_form()
+    resolve_form = await resolve.ResolveForm.create_form()
 
     return await quart.render_template(
         "check-selected.html",
@@ -123,6 +125,7 @@ async def check(
         models=models,
         task_mid=task_mid,
         form=form,
+        resolve_form=resolve_form,
     )
 
 
diff --git a/atr/routes/resolve.py b/atr/routes/resolve.py
index 748adcc..da2b35e 100644
--- a/atr/routes/resolve.py
+++ b/atr/routes/resolve.py
@@ -29,6 +29,7 @@ import atr.db.models as models
 import atr.routes as routes
 import atr.routes.compose as compose
 import atr.routes.finish as finish
+import atr.routes.vote as vote
 import atr.util as util
 
 
@@ -62,44 +63,6 @@ def release_latest_vote_task(release: models.Release) -> 
models.Task | None:
     return None
 
 
[email protected]("/resolve/<project_name>/<version_name>", 
measure_performance=False)
-async def selected(session: routes.CommitterSession, project_name: str, 
version_name: str) -> response.Response | str:
-    """Resolve the vote on a release candidate."""
-    await session.check_access(project_name)
-
-    release = await session.release(
-        project_name,
-        version_name,
-        phase=models.ReleasePhase.RELEASE_CANDIDATE,
-        with_committee=True,
-        with_tasks=True,
-    )
-
-    form = await ResolveForm.create_form()
-
-    latest_vote_task = release_latest_vote_task(release)
-    task_mid = None
-    archive_url = None
-    if latest_vote_task is not None:
-        task_mid = task_mid_get(latest_vote_task)
-        archive_url = await _task_archive_url_cached(task_mid)
-
-    if ("LOCAL_DEBUG" in os.environ) and (latest_vote_task is not None):
-        latest_vote_task.status = models.TaskStatus.COMPLETED
-        latest_vote_task.result = [json.dumps({"mid": 
"[email protected]"})]
-
-    return await quart.render_template(
-        "candidate-resolve-release.html",
-        release=release,
-        format_artifact_name=_format_artifact_name,
-        form=form,
-        format_datetime=util.format_datetime,
-        vote_task=latest_vote_task,
-        task_mid=task_mid,
-        archive_url=archive_url,
-    )
-
-
 @routes.committer("/resolve/<project_name>/<version_name>", methods=["POST"], 
measure_performance=False)
 async def selected_post(
     session: routes.CommitterSession, project_name: str, version_name: str
@@ -112,14 +75,14 @@ async def selected_post(
         for _field, errors in form.errors.items():
             for error in errors:
                 await quart.flash(f"{error}", "error")
-        return await session.redirect(selected, project_name=project_name, 
version_name=version_name)
+        return await session.redirect(vote.selected, 
project_name=project_name, version_name=version_name)
 
     candidate_name = form.candidate_name.data
     vote_result = form.vote_result.data
 
     if not candidate_name:
         return await session.redirect(
-            selected, error="Missing candidate name", 
project_name=project_name, version_name=version_name
+            vote.selected, error="Missing candidate name", 
project_name=project_name, version_name=version_name
         )
 
     # Extract project name
@@ -127,7 +90,7 @@ async def selected_post(
         project_name, version_name = candidate_name.rsplit("-", 1)
     except ValueError:
         return await session.redirect(
-            selected, error="Invalid candidate name format", 
project_name=project_name, version_name=version_name
+            vote.selected, error="Invalid candidate name format", 
project_name=project_name, version_name=version_name
         )
 
     # Check that the user has access to the project
diff --git a/atr/templates/candidate-resolve-release.html 
b/atr/templates/candidate-resolve-release.html
deleted file mode 100644
index a0a4f6e..0000000
--- a/atr/templates/candidate-resolve-release.html
+++ /dev/null
@@ -1,154 +0,0 @@
-{% extends "layouts/base.html" %}
-
-{% block title %}
-  Resolve vote for {{ release.short_display_name }} ~ ATR
-{% endblock title %}
-
-{% block description %}
-  Resolve the vote for the {{ release.project.display_name }} {{ 
release.version }} release candidate.
-{% endblock description %}
-
-{% block content %}
-  <p class="d-flex justify-content-between align-items-center">
-    <a href="{{ as_url(routes.vote.selected, 
project_name=release.project.name, version_name=release.version) }}"
-       class="atr-back-link">← Back to Vote on {{ 
release.project.short_display_name }} {{ release.version }}</a>
-    <span>
-      <span class="atr-phase-symbol-other">①</span>
-      <span class="atr-phase-arrow">→</span>
-      <strong class="atr-phase-two atr-phase-symbol">②</strong>
-      <span class="atr-phase-two atr-phase-label">VOTE</span>
-      <span class="atr-phase-arrow">→</span>
-      <span class="atr-phase-symbol-other">③</span>
-    </span>
-  </p>
-
-  <h1>
-    Resolve vote for <strong>{{ release.project.short_display_name }}</strong> 
<em>{{ release.version }}</em>
-  </h1>
-  <p>
-    Resolve the vote for the <strong>release candidate</strong> for {{ 
release.project.display_name }} {{ release.version }}. If you resolve a vote as 
passed, the release candidate will be promoted to the next stage, making it a 
release preview. If you resolve a vote as failed, the release candidate will be 
returned to the draft stage.
-  </p>
-
-  <div class="card mb-4">
-    <div class="card-header d-flex justify-content-between align-items-center">
-      <h5 class="mb-0">Release information</h5>
-    </div>
-    <div class="card-body pb-0 mb-1">
-      <div class="row">
-        <div class="col-md-6">
-          <p>
-            <strong>Project:</strong>
-            <a href="{{ as_url(routes.projects.view, 
name=release.project.name) }}">{{ release.project.display_name }}</a>
-          </p>
-          <p>
-            <strong>Label:</strong> {{ release.name }}
-          </p>
-        </div>
-        <div class="col-md-6">
-          <p>
-            <strong>Created:</strong> {{ format_datetime(release.created) }}
-          </p>
-          {% if release.vote_started %}
-            <p>
-              <strong>Vote Started:</strong> {{ 
format_datetime(release.vote_started) }}
-            </p>
-          {% endif %}
-        </div>
-      </div>
-    </div>
-    <div class="card-body">
-      <a href="{{ as_url(routes.download.all_selected, 
project_name=release.project.name, version_name=release.version) }}"
-         class="btn btn-primary me-2"><i class="bi bi-download me-1"></i> 
Download files</a>
-      <a href="{{ as_url(routes.candidate.view, 
project_name=release.project.name, version_name=release.version) }}"
-         class="btn btn-secondary me-2"><i class="bi bi-eye me-1"></i> View 
files</a>
-      <a href="{{ as_url(routes.vote.selected, 
project_name=release.project.name, version_name=release.version) }}"
-         class="btn btn-success"><i class="bi bi-check-circle me-1"></i> Vote 
on release</a>
-    </div>
-  </div>
-
-  <div class="card mb-4">
-    <div class="card-header">
-      <h5 class="mb-0">Vote email details</h5>
-    </div>
-    <div class="card-body">
-      <div class="p-3 border rounded bg-white mb-3">
-        {% if vote_task %}
-          {% if vote_task.status.value == "completed" %}
-            <p class="mb-0 text-success fw-semibold">
-              <i class="bi bi-check-circle me-1"></i> Vote email sent: {{ 
format_datetime(vote_task.completed) }}
-            </p>
-            {% if task_mid %}
-              <p class="mt-2 mb-0 text-muted ps-4">
-                Message-ID: <code class="user-select-all">{{ task_mid }}</code>
-              </p>
-            {% endif %}
-          {% elif vote_task.status.value == "failed" %}
-            <p class="mb-1 text-danger fw-semibold">
-              <i class="bi bi-x-circle me-1"></i> Vote email failed: {{ 
format_datetime(vote_task.completed) }}
-            </p>
-            <div class="alert alert-danger mt-2 mb-0 p-2" role="alert">
-              <p class="mb-0 p-2 text-danger">{{ vote_task.error }}</p>
-            </div>
-          {% else %}
-            <p class="mb-0 text-warning fw-semibold">
-              <i class="bi bi-hourglass-split me-1"></i> Vote email task 
status: {{ vote_task.status.value.upper() }}
-              {% if vote_task.started %}
-                (Started: {{ format_datetime(vote_task.started) }})
-              {% else %}
-                (Added: {{ format_datetime(vote_task.added) }})
-              {% endif %}
-            </p>
-          {% endif %}
-          {% if archive_url %}
-            <p class="mt-2 mb-0 text-muted ps-4">
-              <a href="{{ archive_url }}"
-                 rel="noopener"
-                 target="_blank"
-                 title="View vote email thread in the archive (opens in new 
window)">View vote email thread in the archive <i class="bi 
bi-box-arrow-up-right ms-1"></i></a>
-            </p>
-          {% elif task_mid %}
-            <p class="mt-2 mb-0 text-muted ps-4">Could not retrieve archive 
URL for this message.</p>
-          {% endif %}
-        {% else %}
-          <p class="mb-0 text-muted">
-            <i class="bi bi-question-circle me-1"></i> Vote email: No vote 
initiation task found for this release.
-          </p>
-        {% endif %}
-      </div>
-    </div>
-  </div>
-
-  <div class="card mb-4">
-    <div class="card-header">
-      <h5 class="mb-0">Resolve vote</h5>
-    </div>
-    <div class="card-body">
-      <form method="post"
-            action="{{ as_url(routes.resolve.selected_post, 
project_name=release.project.name, version_name=release.version) }}"
-            class="atr-canary py-4 px-5"
-            novalidate>
-        <input type="hidden" name="candidate_name" value="{{ release.name }}" 
/>
-        {{ form.csrf_token }}
-
-        <div class="mb-3 pb-3 row border-bottom">
-          <label class="col-sm-3 col-form-label text-sm-end fw-semibold">{{ 
form.vote_result.label.text }}:</label>
-          <div class="col-sm-9 pt-2">
-            {% for subfield in form.vote_result %}
-              <div class="form-check form-check-inline">
-                {{ subfield(class="form-check-input" + (" is-invalid" if 
form.vote_result.errors else "") , id=subfield.id ~ "_" ~ loop.index) }}
-                <label class="form-check-label" for="{{ subfield.id }}_{{ 
loop.index }}">{{ subfield.label.text }}</label>
-              </div>
-            {% endfor %}
-            {% if form.vote_result.errors %}
-              <div class="invalid-feedback d-block">{{ 
form.vote_result.errors[0] }}</div>
-            {% endif %}
-          </div>
-        </div>
-
-        <div class="row">
-          <div class="col-sm-9 offset-sm-3">{{ form.submit(class_="btn 
btn-primary mt-3") }}</div>
-        </div>
-      </form>
-    </div>
-  </div>
-{% endblock content %}
diff --git a/atr/templates/check-selected.html 
b/atr/templates/check-selected.html
index 564286d..4ba6e9a 100644
--- a/atr/templates/check-selected.html
+++ b/atr/templates/check-selected.html
@@ -102,8 +102,7 @@
            class="btn btn-primary me-2"><i class="bi bi-download me-1"></i> 
Download files</a>
         <a href="{{ as_url(routes.candidate.view, 
project_name=release.project.name, version_name=release.version) }}"
            class="btn btn-secondary me-2"><i class="bi bi-eye me-1"></i> View 
files</a>
-        <a href="{{ as_url(routes.resolve.selected, 
project_name=release.project.name, version_name=release.version) }}"
-           class="btn btn-success"><i class="bi bi-clipboard-check me-1"></i> 
Resolve vote</a>
+        <a href="#resolve-vote" class="btn btn-success"><i class="bi 
bi-clipboard-check me-1"></i> Resolve vote</a>
       </div>
     {% endif %}
   </div>
@@ -329,6 +328,38 @@
         <div class="col-md-9 offset-md-3">{{ form.submit(class_="btn 
btn-primary") }}</div>
       </div>
     </form>
+
+    <h2 id="resolve-vote">Resolve vote</h2>
+    <div class="border rounded bg-warning-subtle p-3 mb-3">
+      <i class="bi bi-info-circle me-1"></i>
+      <strong>NOTE:</strong> We are allowing a vote to be resolved early in 
order to facilitate testing. This is not the final behaviour.
+    </div>
+    <form method="post"
+          action="{{ as_url(routes.resolve.selected_post, 
project_name=release.project.name, version_name=release.version) }}"
+          class="atr-canary py-4 px-5"
+          novalidate>
+      <input type="hidden" name="candidate_name" value="{{ release.name }}" />
+      {{ resolve_form.csrf_token }}
+
+      <div class="mb-3 pb-3 row border-bottom">
+        <label class="col-sm-3 col-form-label text-sm-end fw-semibold">{{ 
resolve_form.vote_result.label.text }}:</label>
+        <div class="col-sm-9 pt-2">
+          {% for subfield in resolve_form.vote_result %}
+            <div class="form-check form-check-inline">
+              {{ subfield(class="form-check-input" + (" is-invalid" if 
resolve_form.vote_result.errors else "") , id=subfield.id ~ "_" ~ loop.index) }}
+              <label class="form-check-label" for="{{ subfield.id }}_{{ 
loop.index }}">{{ subfield.label.text }}</label>
+            </div>
+          {% endfor %}
+          {% if resolve_form.vote_result.errors %}
+            <div class="invalid-feedback d-block">{{ 
resolve_form.vote_result.errors[0] }}</div>
+          {% endif %}
+        </div>
+      </div>
+
+      <div class="row">
+        <div class="col-sm-9 offset-sm-3">{{ resolve_form.submit(class_="btn 
btn-primary mt-3") }}</div>
+      </div>
+    </form>
   {% endif %}
 {% endblock content %}
 
diff --git a/atr/templates/phase-view.html b/atr/templates/phase-view.html
index c3d1121..e098fd0 100644
--- a/atr/templates/phase-view.html
+++ b/atr/templates/phase-view.html
@@ -10,6 +10,7 @@
 
 {% block content %}
   <p class="d-flex justify-content-between align-items-center">
+    {# TODO: Use mappings.py #}
     {% if phase_key == "draft" %}
       <a href="{{ as_url(routes.compose.selected, 
project_name=release.project.name, version_name=release.version) }}"
          class="atr-back-link">← Back to Compose {{ release.short_display_name 
}}</a>
@@ -22,8 +23,8 @@
         <span class="atr-phase-symbol-other">③</span>
       </span>
     {% elif phase_key == "candidate" %}
-      <a href="{{ as_url(routes.resolve.selected, 
project_name=release.project.name, version_name=release.version) }}"
-         class="atr-back-link">← Back to Resolve vote for {{ 
release.short_display_name }}</a>
+      <a href="{{ as_url(routes.vote.selected, 
project_name=release.project.name, version_name=release.version) }}"
+         class="atr-back-link">← Back to Vote for {{ 
release.short_display_name }}</a>
       <span>
         <span class="atr-phase-symbol-other">①</span>
         <span class="atr-phase-arrow">→</span>
diff --git a/atr/templates/release-select.html 
b/atr/templates/release-select.html
index 0c7f8a5..9ec8b91 100644
--- a/atr/templates/release-select.html
+++ b/atr/templates/release-select.html
@@ -28,11 +28,12 @@
       {% for release in releases %}
         {% set phase = release.phase.value %}
         {% set target_url = None %}
+        {# TODO: Use mappings.py #}
         {% if phase == "release_candidate_draft" %}
           {% set target_url = as_url(routes.compose.selected, 
project_name=project.name, version_name=release.version) %}
           {% set badge_class = "bg-primary" %}
         {% elif phase == "release_candidate" %}
-          {% set target_url = as_url(routes.resolve.selected, 
project_name=project.name, version_name=release.version) %}
+          {% set target_url = as_url(routes.vote.selected, 
project_name=project.name, version_name=release.version) %}
           {% set badge_class = "bg-warning text-dark" %}
         {% elif phase == "release_preview" %}
           {% set target_url = as_url(routes.announce.selected, 
project_name=project.name, version_name=release.version) %}
diff --git a/playwright/test.py b/playwright/test.py
index 3d0ae2d..6bccd3c 100644
--- a/playwright/test.py
+++ b/playwright/test.py
@@ -182,9 +182,9 @@ def lifecycle_04_start_vote(page: sync_api.Page, 
credentials: Credentials, versi
 
 
 def lifecycle_05_resolve_vote(page: sync_api.Page, credentials: Credentials, 
version_name: str) -> None:
-    logging.info(f"Navigating to the candidate resolve page for 
tooling-test-example {version_name}")
-    go_to_path(page, f"/resolve/tooling-test-example/{version_name}")
-    logging.info("Candidate resolve page loaded successfully")
+    logging.info(f"Navigating to the vote page for tooling-test-example 
{version_name}")
+    go_to_path(page, f"/vote/tooling-test-example/{version_name}")
+    logging.info("Vote page loaded successfully")
 
     logging.info(f"Locating the form to resolve the vote for 
tooling-test-example {version_name}")
     form_locator = page.locator(


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

Reply via email to