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-releases.git


The following commit(s) were added to refs/heads/main by this push:
     new 48cdb79  Unify the release file viewing mechanisms
48cdb79 is described below

commit 48cdb7961e0b435a11430537de0423fac0fc350e
Author: Sean B. Palmer <[email protected]>
AuthorDate: Sun Nov 16 16:27:34 2025 +0000

    Unify the release file viewing mechanisms
---
 atr/construct.py                               |   4 +-
 atr/get/candidate.py                           |  73 --------
 atr/get/draft.py                               |  33 ----
 atr/get/file.py                                | 220 ++++++++++++++++++++++---
 atr/get/finish.py                              |   4 +-
 atr/get/preview.py                             | 178 --------------------
 atr/get/projects.py                            |  13 +-
 atr/get/release.py                             |  57 -------
 atr/post/announce.py                           |   2 +-
 atr/templates/check-selected-release-info.html |   2 +-
 atr/templates/phase-view.html                  | 136 ---------------
 atr/templates/releases-finished.html           |   2 +-
 12 files changed, 205 insertions(+), 519 deletions(-)

diff --git a/atr/construct.py b/atr/construct.py
index 2ed558e..78dd112 100644
--- a/atr/construct.py
+++ b/atr/construct.py
@@ -66,9 +66,9 @@ async def announce_release_body(body: str, options: 
AnnounceReleaseOptions) -> s
             raise RuntimeError(f"Release {options.project_name} 
{options.version_name} has no committee")
         committee = release.committee
 
-    routes_release_view = get.release.view  # type: ignore[has-type]
+    routes_file_selected = get.file.selected
     download_path = util.as_url(
-        routes_release_view, project_name=options.project_name, 
version_name=options.version_name
+        routes_file_selected, project_name=options.project_name, 
version_name=options.version_name
     )
     # TODO: This download_url should probably be for the proxy download 
directory, not the ATR view
     download_url = f"https://{host}{download_path}";
diff --git a/atr/get/candidate.py b/atr/get/candidate.py
index dcce63c..d57f756 100644
--- a/atr/get/candidate.py
+++ b/atr/get/candidate.py
@@ -16,76 +16,3 @@
 # under the License.
 
 """candidate.py"""
-
-import asfquart.base as base
-
-import atr.blueprints.get as get
-import atr.db as db
-import atr.log as log
-import atr.models.sql as sql
-import atr.template as template
-import atr.util as util
-import atr.web as web
-
-
[email protected]("/candidate/view/<project_name>/<version_name>")
-async def view(session: web.Committer, project_name: str, version_name: str) 
-> web.WerkzeugResponse | str:
-    """View all the files in the rsync upload directory for a release."""
-    await session.check_access(project_name)
-
-    # Check that the release exists
-    async with db.session() as data:
-        release = await data.release(name=sql.release_name(project_name, 
version_name), _project=True).demand(
-            base.ASFQuartException("Release does not exist", errorcode=404)
-        )
-
-    # Convert async generator to list
-    file_stats = [
-        stat
-        async for stat in util.content_list(
-            util.get_unfinished_dir(), project_name, version_name, 
release.unwrap_revision_number
-        )
-    ]
-    # Sort the files by FileStat.path
-    file_stats.sort(key=lambda fs: fs.path)
-    log.debug(f"File stats: {file_stats}")
-
-    return await template.render(
-        # TODO: Move to somewhere appropriate
-        "phase-view.html",
-        file_stats=file_stats,
-        release=release,
-        format_datetime=util.format_datetime,
-        format_file_size=util.format_file_size,
-        format_permissions=util.format_permissions,
-        phase="release candidate",
-        phase_key="candidate",
-    )
-
-
[email protected]("/candidate/view/<project_name>/<version_name>/<path:file_path>")
-async def view_path(
-    session: web.Committer, project_name: str, version_name: str, file_path: 
str
-) -> web.WerkzeugResponse | str:
-    """View the content of a specific file in the release candidate."""
-    await session.check_access(project_name)
-
-    release = await session.release(project_name, version_name, 
phase=sql.ReleasePhase.RELEASE_CANDIDATE)
-    _max_view_size = 1 * 1024 * 1024
-    full_path = util.release_directory(release) / file_path
-    content_listing = await util.archive_listing(full_path)
-    content, is_text, is_truncated, error_message = await 
util.read_file_for_viewer(full_path, _max_view_size)
-    return await template.render(
-        "file-selected-path.html",
-        release=release,
-        project_name=project_name,
-        version_name=version_name,
-        file_path=file_path,
-        content=content,
-        is_text=is_text,
-        is_truncated=is_truncated,
-        error_message=error_message,
-        format_file_size=util.format_file_size,
-        phase_key="candidate",
-        content_listing=content_listing,
-    )
diff --git a/atr/get/draft.py b/atr/get/draft.py
index daa028a..2383972 100644
--- a/atr/get/draft.py
+++ b/atr/get/draft.py
@@ -62,36 +62,3 @@ async def tools(session: web.Committer, project_name: str, 
version_name: str, fi
         format_file_size=util.format_file_size,
         empty_form=await forms.Empty.create_form(),
     )
-
-
-# TODO: Should we deprecate this and ensure compose covers it all?
-# If we did that, we'd lose the exhaustive use of the abstraction
[email protected]("/draft/view/<project_name>/<version_name>")
-async def view(session: web.Committer, project_name: str, version_name: str) 
-> web.WerkzeugResponse | str:
-    """View all the files in the rsync upload directory for a release."""
-    await session.check_access(project_name)
-
-    release = await session.release(project_name, version_name)
-
-    # Convert async generator to list
-    revision_number = release.latest_revision_number
-    file_stats = []
-    if revision_number is not None:
-        file_stats = [
-            stat
-            async for stat in util.content_list(util.get_unfinished_dir(), 
project_name, version_name, revision_number)
-        ]
-    # Sort the files by FileStat.path
-    file_stats.sort(key=lambda fs: fs.path)
-
-    return await template.render(
-        # TODO: Move to somewhere appropriate
-        "phase-view.html",
-        file_stats=file_stats,
-        release=release,
-        format_datetime=util.format_datetime,
-        format_file_size=util.format_file_size,
-        format_permissions=util.format_permissions,
-        phase="release candidate draft",
-        phase_key="draft",
-    )
diff --git a/atr/get/file.py b/atr/get/file.py
index c8ccd79..fcbc758 100644
--- a/atr/get/file.py
+++ b/atr/get/file.py
@@ -15,44 +15,210 @@
 # specific language governing permissions and limitations
 # under the License.
 
+from typing import Literal
 
 import atr.blueprints.get as get
+import atr.get.compose as compose
+import atr.get.finish as finish
+import atr.get.vote as vote
+import atr.htm as htm
+import atr.models.sql as sql
+import atr.shared.distribution as distribution
 import atr.template as template
 import atr.util as util
 import atr.web as web
 
+type Phase = Literal["COMPOSE", "VOTE", "FINISH"]
 
[email protected]("/file/<project_name>/<version_name>/<path:file_path>")
-async def selected_path(
-    session: web.Committer, project_name: str, version_name: str, file_path: 
str
-) -> web.WerkzeugResponse | str:
-    """View the content of a specific file in the release candidate draft."""
-    # TODO: Make this independent of the release phase
+
[email protected]("/file/<project_name>/<version_name>")
+async def selected(session: web.Committer, project_name: str, version_name: 
str) -> str:
+    """View all the files in a release (any phase)."""
     await session.check_access(project_name)
 
-    release = await session.release(project_name, version_name)
+    release = await session.release(project_name, version_name, phase=None)
 
-    # Limit to 256 KiB
-    _max_view_size = 256 * 1024
-    full_path = util.release_directory(release) / file_path
+    revision_number = release.latest_revision_number
+    file_stats = []
+    if revision_number is not None:
+        if release.phase == sql.ReleasePhase.RELEASE:
+            file_stats = [stat async for stat in 
util.content_list(util.get_finished_dir(), project_name, version_name)]
+        else:
+            file_stats = [
+                stat
+                async for stat in util.content_list(
+                    util.get_unfinished_dir(), project_name, version_name, 
revision_number
+                )
+            ]
+    file_stats.sort(key=lambda fs: fs.path)
 
-    # Attempt to get an archive listing
-    # This will be None if the file is not an archive
-    content_listing = await util.archive_listing(full_path)
+    block = htm.Block()
+
+    nav_info = _get_navigation_info(release)
+    if nav_info:
+        back_url, back_label, phase_label = nav_info
+        distribution.html_nav(block, back_url, back_label, phase_label)
+
+    block.h1["Files in ", htm.strong[release.project.short_display_name], " ", 
htm.em[release.version]]
+
+    block.div(".card.mb-4")[
+        
htm.div(".card-header.d-flex.justify-content-between.align-items-center")[
+            htm.h3(".mb-0")["Release information"]
+        ],
+        htm.div(".card-body")[
+            htm.div(".row")[
+                htm.div(".col-md-6")[
+                    htm.p[htm.strong["Project:"], " ", 
release.project.display_name],
+                    htm.p[htm.strong["Label:"], " ", release.name],
+                ],
+                htm.div(".col-md-6")[htm.p[htm.strong["Created:"], " ", 
release.created.strftime("%Y-%m-%d %H:%M:%S")]],
+            ]
+        ],
+    ]
+
+    files_card = htm.Block(htm.div, classes=".card.mb-4")
+    
files_card.div(".card-header.d-flex.justify-content-between.align-items-center")[htm.h3(".mb-0")["Files"]]
+
+    if file_stats:
+        tbody = htm.Block(htm.tbody)
+        for stat in file_stats:
+            if stat.is_file:
+                file_url = util.as_url(
+                    selected_path,
+                    project_name=release.project.name,
+                    version_name=release.version,
+                    file_path=stat.path,
+                )
+                file_link = htm.a(href=file_url)[stat.path]
+            else:
+                file_link = htm.strong[stat.path + "/"]
+
+            tbody.tr[
+                htm.td[util.format_permissions(stat.permissions)],
+                htm.td[file_link],
+                htm.td[util.format_file_size(stat.size) if stat.is_file else 
"-"],
+                htm.td[util.format_datetime(stat.modified)],
+            ]
+
+        files_card.div(".card-body")[
+            htm.div(".table-responsive")[
+                htm.table(".table.table-striped.table-hover")[
+                    htm.thead[
+                        htm.tr[
+                            htm.th["Permissions"],
+                            htm.th["File path"],
+                            htm.th["Size"],
+                            htm.th["Modified"],
+                        ]
+                    ],
+                    tbody.collect(),
+                ]
+            ]
+        ]
+    else:
+        phase_name = _phase_display_name(release.phase)
+        files_card.div(".card-body")[htm.div(".alert.alert-info")[f"This 
{phase_name} does not have any files."]]
+
+    block.append(files_card.collect())
+
+    return await template.blank(f"Files in {release.short_display_name}", 
content=block.collect())
+
+
[email protected]("/file/<project_name>/<version_name>/<path:file_path>")
+async def selected_path(session: web.Committer, project_name: str, 
version_name: str, file_path: str) -> str:
+    """View the content of a specific file in a release (any phase)."""
+    await session.check_access(project_name)
 
+    release = await session.release(project_name, version_name, phase=None)
+    _max_view_size = 512 * 1024
+    full_path = util.release_directory(release) / file_path
+    content_listing = await util.archive_listing(full_path)
     content, is_text, is_truncated, error_message = await 
util.read_file_for_viewer(full_path, _max_view_size)
-    return await template.render(
-        "file-selected-path.html",
-        release=release,
-        project_name=project_name,
-        version_name=version_name,
-        file_path=file_path,
-        content=content,
-        is_text=is_text,
-        is_truncated=is_truncated,
-        error_message=error_message,
-        content_listing=content_listing,
-        format_file_size=util.format_file_size,
-        phase_key="draft",
-        max_view_size=util.format_file_size(_max_view_size),
+
+    block = htm.Block()
+
+    back_url = util.as_url(selected, project_name=release.project.name, 
version_name=release.version)
+    phase_name = _phase_display_name(release.phase)
+    block.a(href=back_url, class_="atr-back-link")[f"← Back to {phase_name} 
files"]
+
+    block.div(".p-3.mt-4.mb-4.bg-light.border.rounded")[
+        htm.h2(".mt-0")[f"Viewing file: {file_path}"],
+        htm.p(".mb-0")[htm.strong["Release:"], " ", release.name],
+    ]
+
+    if content_listing:
+        items = [htm.li(".list-group-item.py-1.px-3.small")[item] for item in 
content_listing]
+        block.div(".card.mb-3")[
+            htm.div(".card-header")[htm.h3(".mb-0")[f"Archive contents 
({len(content_listing)})"]],
+            
htm.div(".card-body.p-0")[htm.ul(".list-group.list-group-flush")[*items]],
+        ]
+
+    if error_message:
+        block.div(".alert.alert-danger")[error_message]
+    elif content is not None:
+        if content_listing:
+            details_block = htm.Block(htm.details, classes=".mb-3")
+            details_block.summary(".mb-2")["View raw file content"]
+            _render_file_content(details_block, content, is_text, 
is_truncated, _max_view_size)
+            block.append(details_block.collect())
+        else:
+            _render_file_content(block, content, is_text, is_truncated, 
_max_view_size)
+    else:
+        block.div(".alert.alert-secondary")["No content available for this 
file."]
+
+    return await template.blank(
+        f"View 
{release.project.short_display_name}/{release.version}/{file_path}", 
content=block.collect()
     )
+
+
+def _render_file_content(block: htm.Block, content: str, is_text: bool, 
is_truncated: bool, max_view_size: int) -> None:
+    card = htm.Block(htm.div, classes=".card.mb-4")
+    card.div(".card-header")[htm.h3(".mb-0")["File content" + (" (Hexdump)" if 
not is_text else "")]]
+
+    if is_text:
+        
card.div(".card-body.p-0")[htm.pre(".bg-light.p-4.rounded-bottom.mb-0.text-break")[content]]
+    else:
+        
card.div(".card-body.p-0")[htm.pre(".bg-light.p-4.rounded-bottom.mb-0.text-break")[htm.code[content]]]
+
+    if is_truncated:
+        card.div(".card-footer.text-muted.small")[
+            f"Note: File content truncated to the first 
{util.format_file_size(max_view_size)}."
+        ]
+
+    block.append(card.collect())
+
+
+def _get_navigation_info(release: sql.Release) -> tuple[str, str, Phase] | 
None:
+    """Get back URL, back label, and phase label based on release phase."""
+    if release.phase == sql.ReleasePhase.RELEASE_CANDIDATE_DRAFT:
+        return (
+            util.as_url(compose.selected, project_name=release.project.name, 
version_name=release.version),
+            f"Compose {release.short_display_name}",
+            "COMPOSE",
+        )
+    elif release.phase == sql.ReleasePhase.RELEASE_CANDIDATE:
+        return (
+            util.as_url(vote.selected, project_name=release.project.name, 
version_name=release.version),
+            f"Vote on {release.short_display_name}",
+            "VOTE",
+        )
+    elif release.phase == sql.ReleasePhase.RELEASE_PREVIEW:
+        return (
+            util.as_url(finish.selected, project_name=release.project.name, 
version_name=release.version),
+            f"Finish {release.short_display_name}",
+            "FINISH",
+        )
+    return None
+
+
+def _phase_display_name(phase: sql.ReleasePhase) -> str:
+    """Get a display name for the phase."""
+    if phase == sql.ReleasePhase.RELEASE_CANDIDATE_DRAFT:
+        return "draft"
+    elif phase == sql.ReleasePhase.RELEASE_CANDIDATE:
+        return "candidate"
+    elif phase == sql.ReleasePhase.RELEASE_PREVIEW:
+        return "preview"
+    elif phase == sql.ReleasePhase.RELEASE:
+        return "release"
+    return "release"
diff --git a/atr/get/finish.py b/atr/get/finish.py
index 3033b5e..dfc03ad 100644
--- a/atr/get/finish.py
+++ b/atr/get/finish.py
@@ -35,7 +35,7 @@ import atr.form as form
 import atr.get.announce as announce
 import atr.get.distribution as distribution
 import atr.get.download as download
-import atr.get.preview as preview
+import atr.get.file as file
 import atr.get.revisions as revisions
 import atr.get.root as root
 import atr.htm as htm
@@ -412,7 +412,7 @@ def _render_release_card(release: sql.Release) -> 
htm.Element:
                     ".btn.btn-secondary.me-2",
                     title=f"Show files for {release.name}",
                     href=util.as_url(
-                        preview.view,
+                        file.selected,
                         project_name=release.project.name,
                         version_name=release.version,
                     ),
diff --git a/atr/get/preview.py b/atr/get/preview.py
index ed423ca..13a8339 100644
--- a/atr/get/preview.py
+++ b/atr/get/preview.py
@@ -14,181 +14,3 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-
-import atr.blueprints.get as get
-import atr.get.finish as finish
-import atr.htm as htm
-import atr.models.sql as sql
-import atr.shared as shared
-import atr.template as template
-import atr.util as util
-import atr.web as web
-
-
[email protected]("/preview/view/<project_name>/<version_name>")
-async def view(session: web.Committer, project_name: str, version_name: str) 
-> str:
-    """View all the files in the rsync upload directory for a release."""
-    await session.check_access(project_name)
-
-    release = await session.release(project_name, version_name, 
phase=sql.ReleasePhase.RELEASE_PREVIEW)
-
-    # Convert async generator to list
-    # There must be a revision on a preview
-    file_stats = [
-        stat
-        async for stat in util.content_list(
-            util.get_unfinished_dir(), project_name, version_name, 
release.unwrap_revision_number
-        )
-    ]
-    # Sort the files by FileStat.path
-    file_stats.sort(key=lambda fs: fs.path)
-
-    block = htm.Block()
-
-    shared.distribution.html_nav(
-        block,
-        util.as_url(finish.selected, project_name=release.project.name, 
version_name=release.version),
-        f"Finish {release.short_display_name}",
-        "FINISH",
-    )
-
-    block.h1[
-        "Files in ",
-        htm.strong[release.project.short_display_name],
-        " ",
-        htm.em[release.version],
-    ]
-
-    block.div(".card.mb-4")[
-        
htm.div(".card-header.d-flex.justify-content-between.align-items-center")[
-            htm.h3(".mb-0")["Release information"]
-        ],
-        htm.div(".card-body")[
-            htm.div(".row")[
-                htm.div(".col-md-6")[
-                    htm.p[htm.strong["Project:"], " ", 
release.project.display_name],
-                    htm.p[htm.strong["Label:"], " ", release.name],
-                ],
-                htm.div(".col-md-6")[
-                    htm.p[htm.strong["Created:"], " ", 
release.created.strftime("%Y-%m-%d %H:%M:%S")],
-                ],
-            ]
-        ],
-    ]
-
-    files_card = htm.Block(htm.div, classes=".card.mb-4")
-    
files_card.div(".card-header.d-flex.justify-content-between.align-items-center")[htm.h3(".mb-0")["Files"]]
-
-    if file_stats:
-        tbody = htm.Block(htm.tbody)
-        for stat in file_stats:
-            if stat.is_file:
-                file_url = util.as_url(
-                    view_path,
-                    project_name=release.project.name,
-                    version_name=release.version,
-                    file_path=stat.path,
-                )
-                file_link = htm.a(href=file_url)[stat.path]
-            else:
-                file_link = htm.strong[stat.path + "/"]
-
-            tbody.tr[
-                htm.td[util.format_permissions(stat.permissions)],
-                htm.td[file_link],
-                htm.td[util.format_file_size(stat.size) if stat.is_file else 
"-"],
-                htm.td[util.format_datetime(stat.modified)],
-            ]
-
-        files_card.div(".card-body")[
-            htm.div(".table-responsive")[
-                htm.table(".table.table-striped.table-hover")[
-                    htm.thead[
-                        htm.tr[
-                            htm.th["Permissions"],
-                            htm.th["File path"],
-                            htm.th["Size"],
-                            htm.th["Modified"],
-                        ]
-                    ],
-                    tbody.collect(),
-                ]
-            ]
-        ]
-    else:
-        files_card.div(".card-body")[htm.div(".alert.alert-info")["This 
release preview does not have any files."]]
-
-    block.append(files_card.collect())
-
-    return await template.blank(
-        f"Files in {release.short_display_name}",
-        content=block.collect(),
-    )
-
-
[email protected]("/preview/view/<project_name>/<version_name>/<path:file_path>")
-async def view_path(session: web.Committer, project_name: str, version_name: 
str, file_path: str) -> str:
-    """View the content of a specific file in the release preview."""
-    await session.check_access(project_name)
-
-    release = await session.release(project_name, version_name, 
phase=sql.ReleasePhase.RELEASE_PREVIEW)
-    _max_view_size = 1 * 1024 * 1024
-    full_path = util.release_directory(release) / file_path
-    content_listing = await util.archive_listing(full_path)
-    content, is_text, is_truncated, error_message = await 
util.read_file_for_viewer(full_path, _max_view_size)
-
-    block = htm.Block()
-
-    back_url = util.as_url(
-        view,
-        project_name=release.project.name,
-        version_name=release.version,
-    )
-    block.a(href=back_url, class_="atr-back-link")["← Back to preview"]
-
-    block.div(".p-3.mt-4.mb-4.bg-light.border.rounded")[
-        htm.h2(".mt-0")[f"Viewing file: {file_path}"],
-        htm.p(".mb-0")[htm.strong["Release:"], " ", release.name],
-    ]
-
-    if content_listing:
-        items = [htm.li(".list-group-item.py-1.px-3.small")[item] for item in 
content_listing]
-        block.div(".card.mb-3")[
-            htm.div(".card-header")[htm.h3(".mb-0")[f"Archive contents 
({len(content_listing)})"]],
-            
htm.div(".card-body.p-0")[htm.ul(".list-group.list-group-flush")[*items]],
-        ]
-
-    if error_message:
-        block.div(".alert.alert-danger")[error_message]
-    elif content is not None:
-        if content_listing:
-            details_block = htm.Block(htm.details, classes=".mb-3")
-            details_block.summary(".mb-2")["View raw file content"]
-            _render_file_content(details_block, content, is_text, 
is_truncated, _max_view_size)
-            block.append(details_block.collect())
-        else:
-            _render_file_content(block, content, is_text, is_truncated, 
_max_view_size)
-    else:
-        block.div(".alert.alert-secondary")["No content available for this 
file."]
-
-    return await template.blank(
-        f"View 
{release.project.short_display_name}/{release.version}/{file_path}",
-        content=block.collect(),
-    )
-
-
-def _render_file_content(block: htm.Block, content: str, is_text: bool, 
is_truncated: bool, max_view_size: int) -> None:
-    card = htm.Block(htm.div, classes=".card.mb-4")
-    card.div(".card-header")[htm.h3(".mb-0")["File content" + (" (Hexdump)" if 
not is_text else "")]]
-
-    if is_text:
-        
card.div(".card-body.p-0")[htm.pre(".bg-light.p-4.rounded-bottom.mb-0.text-break")[content]]
-    else:
-        
card.div(".card-body.p-0")[htm.pre(".bg-light.p-4.rounded-bottom.mb-0.text-break")[htm.code[content]]]
-
-    if is_truncated:
-        card.div(".card-footer.text-muted.small")[
-            f"Note: File content truncated to the first 
{util.format_file_size(max_view_size)}."
-        ]
-
-    block.append(card.collect())
diff --git a/atr/get/projects.py b/atr/get/projects.py
index 9d72441..aa4a15e 100644
--- a/atr/get/projects.py
+++ b/atr/get/projects.py
@@ -28,9 +28,7 @@ import atr.db.interaction as interaction
 import atr.form as form
 import atr.forms as forms
 import atr.get.committees as committees
-import atr.get.draft as draft
-import atr.get.preview as preview
-import atr.get.release as release
+import atr.get.file as file
 import atr.get.start as start
 import atr.htm as htm
 import atr.models.sql as sql
@@ -41,7 +39,6 @@ import atr.template as template
 import atr.user as user
 import atr.util as util
 import atr.web as web
-from atr.get import candidate
 
 
 @get.committer("/project/add/<committee_name>")
@@ -449,7 +446,7 @@ async def _render_releases_sections(
             draft_buttons.append(
                 htm.a(
                     ".btn.btn-sm.btn-outline-secondary.py-2.px-3",
-                    href=util.as_url(draft.view, project_name=project.name, 
version_name=drf.version),
+                    href=util.as_url(file.selected, project_name=project.name, 
version_name=drf.version),
                     title=f"View draft {project.name} {drf.version}",
                 )[
                     f"{project.name} {drf.version} ",
@@ -466,7 +463,7 @@ async def _render_releases_sections(
             candidate_buttons.append(
                 htm.a(
                     ".btn.btn-sm.btn-outline-info.py-2.px-3",
-                    href=util.as_url(candidate.view, 
project_name=project.name, version_name=cnd.version),
+                    href=util.as_url(file.selected, project_name=project.name, 
version_name=cnd.version),
                     title=f"View candidate {project.name} {cnd.version}",
                 )[
                     f"{project.name} {cnd.version} ",
@@ -483,7 +480,7 @@ async def _render_releases_sections(
             preview_buttons.append(
                 htm.a(
                     ".btn.btn-sm.btn-outline-warning.py-2.px-3",
-                    href=util.as_url(preview.view, project_name=project.name, 
version_name=prv.version),
+                    href=util.as_url(file.selected, project_name=project.name, 
version_name=prv.version),
                     title=f"View preview {project.name} {prv.version}",
                 )[
                     f"{project.name} {prv.version} ",
@@ -500,7 +497,7 @@ async def _render_releases_sections(
             release_buttons.append(
                 htm.a(
                     ".btn.btn-sm.btn-outline-success.py-2.px-3",
-                    href=util.as_url(release.view, project_name=project.name, 
version_name=rel.version),
+                    href=util.as_url(file.selected, project_name=project.name, 
version_name=rel.version),
                     title=f"View release {project.name} {rel.version}",
                 )[
                     f"{project.name} {rel.version} ",
diff --git a/atr/get/release.py b/atr/get/release.py
index 4c17f9c..10a7061 100644
--- a/atr/get/release.py
+++ b/atr/get/release.py
@@ -90,60 +90,3 @@ async def select(session: web.Committer, project_name: str) 
-> str:
     return await template.render(
         "release-select.html", project=project, releases=releases, 
format_datetime=util.format_datetime
     )
-
-
[email protected]("/release/view/<project_name>/<version_name>")
-async def view(session: web.Committer | None, project_name: str, version_name: 
str) -> web.WerkzeugResponse | str:
-    """View all the files in the rsync upload directory for a release."""
-    async with db.session() as data:
-        release_name = sql.release_name(project_name, version_name)
-        release = await data.release(name=release_name, _project=True).demand(
-            base.ASFQuartException(f"Release {version_name} not found", 
errorcode=404)
-        )
-
-    # Convert async generator to list
-    file_stats = [stat async for stat in 
util.content_list(util.get_finished_dir(), project_name, version_name)]
-    # Sort the files by FileStat.path
-    file_stats.sort(key=lambda fs: fs.path)
-
-    return await template.render(
-        # TODO: Move to somewhere appropriate
-        "phase-view.html",
-        file_stats=file_stats,
-        release=release,
-        format_datetime=util.format_datetime,
-        format_file_size=util.format_file_size,
-        format_permissions=util.format_permissions,
-        phase="release",
-        phase_key="release",
-    )
-
-
[email protected]("/release/view/<project_name>/<version_name>/<path:file_path>")
-async def view_path(
-    session: web.Committer | None, project_name: str, version_name: str, 
file_path: str
-) -> web.WerkzeugResponse | str:
-    """View the content of a specific file in the final release."""
-    async with db.session() as data:
-        release_name = sql.release_name(project_name, version_name)
-        release = await data.release(name=release_name, _project=True).demand(
-            base.ASFQuartException(f"Release {version_name} not found", 
errorcode=404)
-        )
-    _max_view_size = 1 * 1024 * 1024
-    full_path = util.release_directory(release) / file_path
-    content_listing = await util.archive_listing(full_path)
-    content, is_text, is_truncated, error_message = await 
util.read_file_for_viewer(full_path, _max_view_size)
-    return await template.render(
-        "file-selected-path.html",
-        release=release,
-        project_name=project_name,
-        version_name=version_name,
-        file_path=file_path,
-        content=content,
-        is_text=is_text,
-        is_truncated=is_truncated,
-        error_message=error_message,
-        format_file_size=util.format_file_size,
-        phase_key="release",
-        content_listing=content_listing,
-    )
diff --git a/atr/post/announce.py b/atr/post/announce.py
index 873799b..56d2417 100644
--- a/atr/post/announce.py
+++ b/atr/post/announce.py
@@ -78,7 +78,7 @@ async def selected(
             get.announce.selected, error=str(e), project_name=project_name, 
version_name=version_name
         )
 
-    routes_release_finished = get.release.finished  # type: ignore[has-type]
+    routes_release_finished = get.release.finished
     return await session.redirect(
         routes_release_finished,
         success="Preview successfully announced",
diff --git a/atr/templates/check-selected-release-info.html 
b/atr/templates/check-selected-release-info.html
index 127f5c2..4fd99fc 100644
--- a/atr/templates/check-selected-release-info.html
+++ b/atr/templates/check-selected-release-info.html
@@ -79,7 +79,7 @@
       {% elif phase == "release_candidate" %}
         <a href="{{ as_url(get.download.all_selected, 
project_name=release.project.name, version_name=release.version) }}"
            class="btn btn-primary"><i class="bi bi-download me-1"></i> 
Download files</a>
-        <a href="{{ as_url(get.candidate.view, 
project_name=release.project.name, version_name=release.version) }}"
+        <a href="{{ as_url(get.file.selected, 
project_name=release.project.name, version_name=release.version) }}"
            class="btn btn-secondary"><i class="bi bi-eye me-1"></i> View 
files</a>
         {% if can_resolve and release.vote_manual %}
           <a href="{{ as_url(get.manual.resolve_selected, 
project_name=release.project.name, version_name=release.version) }}"
diff --git a/atr/templates/phase-view.html b/atr/templates/phase-view.html
deleted file mode 100644
index 946a676..0000000
--- a/atr/templates/phase-view.html
+++ /dev/null
@@ -1,136 +0,0 @@
-{% extends "layouts/base.html" %}
-
-{% block title %}
-  Files in {{ release.short_display_name }} ~ ATR
-{% endblock title %}
-
-{% block description %}
-  View the files in the {{ release.short_display_name }} release.
-{% endblock description %}
-
-{% block content %}
-  <p class="d-flex justify-content-between align-items-center">
-    {# TODO: Use mappings.py #}
-    {% if phase_key == "draft" %}
-      <a href="{{ as_url(get.compose.selected, 
project_name=release.project.name, version_name=release.version) }}"
-         class="atr-back-link">← Back to Compose {{ release.short_display_name 
}}</a>
-      <span>
-        <strong class="atr-phase-one atr-phase-symbol">①</strong>
-        <span class="atr-phase-one atr-phase-label">COMPOSE</span>
-        <span class="atr-phase-arrow">→</span>
-        <span class="atr-phase-symbol-other">②</span>
-        <span class="atr-phase-arrow">→</span>
-        <span class="atr-phase-symbol-other">③</span>
-      </span>
-    {% elif phase_key == "candidate" %}
-      <a href="{{ as_url(get.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>
-        <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>
-    {% elif phase_key == "preview" %}
-      <a href="{{ as_url(get.finish.selected, 
project_name=release.project.name, version_name=release.version) }}"
-         class="atr-back-link">← Back to Finish {{ release.short_display_name 
}}</a>
-      <span>
-        <span class="atr-phase-symbol-other">①</span>
-        <span class="atr-phase-arrow">→</span>
-        <span class="atr-phase-symbol-other">②</span>
-        <span class="atr-phase-arrow">→</span>
-        <strong class="atr-phase-three atr-phase-symbol">③</strong>
-        <span class="atr-phase-three atr-phase-label">FINISH</span>
-      </span>
-    {% else %}
-      <a href="{{ as_url(get.release.releases) }}" class="atr-back-link">← 
Back to Releases</a>
-    {% endif %}
-  </p>
-
-  <h1>
-    Files in <strong>{{ release.project.short_display_name }}</strong> <em>{{ 
release.version }}</em>
-  </h1>
-
-  <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">
-      <div class="row">
-        <div class="col-md-6">
-          <p>
-            <strong>Project:</strong> {{ release.project.display_name }}
-          </p>
-          <p>
-            <strong>Label:</strong> {{ release.name }}
-          </p>
-        </div>
-        <div class="col-md-6">
-          <p>
-            <strong>Created:</strong> {{ release.created.strftime("%Y-%m-%d 
%H:%M:%S") }}
-          </p>
-        </div>
-      </div>
-    </div>
-  </div>
-
-  <div class="card mb-4">
-    <div class="card-header d-flex justify-content-between align-items-center">
-      <h5 class="mb-0">Files</h5>
-    </div>
-    <div class="card-body">
-      {% if file_stats|length > 0 %}
-        <div class="table-responsive">
-          <table class="table table-striped table-hover">
-            <thead>
-              <tr>
-                <th>Permissions</th>
-                <th>File path</th>
-                <th>Size</th>
-                <th>Modified</th>
-              </tr>
-            </thead>
-            <tbody>
-              {% for stat in file_stats %}
-                <tr>
-                  <td>{{ format_permissions(stat.permissions) }}</td>
-                  <td>
-                    {% if stat.is_file %}
-                      {% if phase_key == "draft" %}
-                        {% set file_url = as_url(get.file.selected_path, 
project_name=release.project.name, version_name=release.version, 
file_path=stat.path) %}
-                      {% elif phase_key == "candidate" %}
-                        {% set file_url = as_url(get.candidate.view_path, 
project_name=release.project.name, version_name=release.version, 
file_path=stat.path) %}
-                      {% elif phase_key == "preview" %}
-                        {% set file_url = as_url(get.preview.view_path, 
project_name=release.project.name, version_name=release.version, 
file_path=stat.path) %}
-                      {% elif phase_key == "release" %}
-                        {% set file_url = as_url(get.release.view_path, 
project_name=release.project.name, version_name=release.version, 
file_path=stat.path) %}
-                      {% else %}
-                        {# TODO: Should probably disable the link here #}
-                        {% set file_url = "#" %}
-                      {% endif %}
-                      <a href="{{ file_url }}">{{ stat.path }}</a>
-                    {% else %}
-                      <strong>{{ stat.path }}/</strong>
-                    {% endif %}
-                  </td>
-                  <td>
-                    {% if stat.is_file %}
-                      {{ format_file_size(stat.size) }}
-                    {% else %}
-                      -
-                    {% endif %}
-                  </td>
-                  <td>{{ format_datetime(stat.modified) }}</td>
-                </tr>
-              {% endfor %}
-            </tbody>
-          </table>
-        </div>
-      {% else %}
-        <div class="alert alert-info">This {{ phase }} does not have any 
files.</div>
-      {% endif %}
-    </div>
-  </div>
-{% endblock content %}
diff --git a/atr/templates/releases-finished.html 
b/atr/templates/releases-finished.html
index 7d9d456..bc63ca5 100644
--- a/atr/templates/releases-finished.html
+++ b/atr/templates/releases-finished.html
@@ -29,7 +29,7 @@
                    class="btn btn-outline-primary w-100 mb-2">
                   <i class="bi bi-download me-1"></i> Download files
                 </a>
-                <a href="{{ as_url(get.release.view, 
project_name=release.project.name, version_name=release.version) }}"
+                <a href="{{ as_url(get.file.selected, 
project_name=release.project.name, version_name=release.version) }}"
                    class="btn btn-outline-secondary w-100">
                   <i class="bi bi-folder2-open me-1"></i> View files
                 </a>


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


Reply via email to