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 a3a6fbd Add a tools page for candidate draft files
a3a6fbd is described below
commit a3a6fbd766d3562b3143a9b98d6b47ef88ddac26
Author: Sean B. Palmer <[email protected]>
AuthorDate: Tue Mar 25 19:30:24 2025 +0200
Add a tools page for candidate draft files
---
atr/routes/files.py | 45 ++++++++++++++++++++++++++++++++++++++++++
atr/templates/files-list.html | 30 +++++++++++++++++-----------
atr/templates/files-tools.html | 35 ++++++++++++++++++++++++++++++++
3 files changed, 98 insertions(+), 12 deletions(-)
diff --git a/atr/routes/files.py b/atr/routes/files.py
index b6543fe..2e064de 100644
--- a/atr/routes/files.py
+++ b/atr/routes/files.py
@@ -461,3 +461,48 @@ async def root_files_checks(session: CommitterSession,
project_name: str, versio
all_tasks_completed=all_tasks_completed,
format_file_size=routes.format_file_size,
)
+
+
+@committer_route("/files/tools/<project_name>/<version_name>/<path:file_path>")
+async def root_files_tools(session: CommitterSession, project_name: str,
version_name: str, file_path: str) -> str:
+ """Show the tools for a specific file."""
+ # Check that the user has access to the project
+ if not any((p.name == project_name) for p in (await
session.user_projects)):
+ raise base.ASFQuartException("You do not have access to this project",
errorcode=403)
+
+ async with db.session() as data:
+ # Check that the release exists
+ release = await data.release(name=f"{project_name}-{version_name}",
_project=True).demand(
+ base.ASFQuartException("Release does not exist", errorcode=404)
+ )
+
+ full_path = str(util.get_candidate_draft_dir() / project_name /
version_name / file_path)
+
+ # Check that the file exists
+ if not await aiofiles.os.path.exists(full_path):
+ raise base.ASFQuartException("File does not exist", errorcode=404)
+
+ modified = int(await aiofiles.os.path.getmtime(full_path))
+ file_size = await aiofiles.os.path.getsize(full_path)
+
+ file_data = {
+ "filename": pathlib.Path(file_path).name,
+ "bytes_size": file_size,
+ "uploaded": datetime.datetime.fromtimestamp(modified, tz=datetime.UTC),
+ }
+
+ return await quart.render_template(
+ "files-tools.html",
+ project_name=project_name,
+ version_name=version_name,
+ file_path=file_path,
+ file_data=file_data,
+ release=release,
+ format_file_size=routes.format_file_size,
+ )
+
+
+@committer_route("/files/delete/<project_name>/<version_name>/<path:file_path>",
methods=["POST"])
+async def root_files_delete(session: CommitterSession, project_name: str,
version_name: str, file_path: str) -> str:
+ """Delete a specific file from the release candidate."""
+ return ""
diff --git a/atr/templates/files-list.html b/atr/templates/files-list.html
index 33d94ee..305fea5 100644
--- a/atr/templates/files-list.html
+++ b/atr/templates/files-list.html
@@ -10,7 +10,9 @@
{% block content %}
<h1>Files for {{ release.project.display_name }} {{ version_name }}</h1>
- <p class="intro">This page shows the files for the {{ project_name }} {{
version_name }} candidate draft.</p>
+ <p class="intro">
+ This page shows the files for the {{ project_name }} {{ version_name }}
<strong>candidate draft</strong>.
+ </p>
<div class="card mb-4">
<div class="card-header d-flex justify-content-between align-items-center">
@@ -57,8 +59,7 @@
<thead>
<tr>
<th>Path</th>
- <th>Warnings and errors</th>
- <th>Checks</th>
+ <th>Tools, checks, warnings, and errors</th>
</tr>
</thead>
<tbody>
@@ -73,12 +74,10 @@
<a href="{{ url_for('root_download',
phase='candidate-draft', project=release.project.name, version=release.version,
path=path) }}">{{ path }}</a>
{% endif %}
</td>
- <td>
- {% for warning in warnings[path] %}<div class="alert
alert-warning p-0 px-2 mt-0 mb-2">{{ warning }}</div>{% endfor %}
- {% for error in errors[path] %}<div class="alert
alert-danger p-0 px-2 mt-0 mb-2">{{ error }}</div>{% endfor %}
- </td>
<td>
{% if path in tasks and tasks[path]|length > 0 %}
+ <a href="{{ url_for('root_files_tools',
project_name=project_name, version_name=version_name, file_path=path)
}}">Tools</a> ·
+ <a href="{{ url_for('root_files_checks',
project_name=project_name, version_name=version_name, file_path=path) }}">Check
results</a>
{% set completed_count = namespace(value=0) %}
{% set failed_count = namespace(value=0) %}
{% set active_count = namespace(value=0) %}
@@ -96,7 +95,7 @@
{% endif %}
{% endfor %}
- <div class="d-flex flex-wrap gap-2 mb-2">
+ <div class="d-flex flex-wrap gap-2 mt-2 mb-3">
{% if completed_count.value > 0 %}<span class="badge
bg-success">{{ completed_count.value }} Passed</span>{% endif %}
{% if failed_count.value > 0 %}
<span class="badge bg-danger">{{ failed_count.value
}} {{ "Issue" if failed_count.value == 1 else "Issues" }}</span>
@@ -104,13 +103,20 @@
{% if active_count.value > 0 %}<span class="badge
bg-info">{{ active_count.value }} Running</span>{% endif %}
{% if queued_count.value > 0 %}<span class="badge
bg-secondary">{{ queued_count.value }} Pending</span>{% endif %}
</div>
-
- <a href="{{ url_for('root_files_checks',
project_name=project_name, version_name=version_name, file_path=path) }}">
- View all check results
- </a>
{% else %}
+ <a href="{{ url_for('root_files_tools',
project_name=project_name, version_name=version_name, file_path=path)
}}">Tools</a> ·
No check results
{% endif %}
+ {% for warning in warnings[path] %}
+ <div class="alert alert-warning p-1 px-2 mt-2 mb-2">
+ <i class="fas fa-exclamation-triangle"></i> {{ warning
}}
+ </div>
+ {% endfor %}
+ {% for error in errors[path] %}
+ <div class="alert alert-danger p-1 px-2 mt-2 mb-2">
+ <i class="fas fa-exclamation-triangle"></i> {{ error }}
+ </div>
+ {% endfor %}
</td>
</tr>
{% endfor %}
diff --git a/atr/templates/files-tools.html b/atr/templates/files-tools.html
new file mode 100644
index 0000000..25aac5e
--- /dev/null
+++ b/atr/templates/files-tools.html
@@ -0,0 +1,35 @@
+{% extends "layouts/base.html" %}
+
+{% block title %}
+ File tools ~ ATR
+{% endblock title %}
+
+{% block description %}
+ Manage a file in the candidate draft using tools.
+{% endblock description %}
+
+{% block content %}
+ <a href="{{ url_for('root_files_list', project_name=project_name,
version_name=version_name) }}"
+ class="back-link">← Back to Files List</a>
+
+ <div class="p-3 mb-4 bg-light border rounded">
+ <h2 class="mt-0">File details</h2>
+ <p>
+ <strong>Filename:</strong> {{ file_data.filename }}
+ </p>
+ <p>
+ <strong>Size:</strong> {{ format_file_size(file_data.bytes_size) }}
+ </p>
+ <p class="mb-0">
+ <strong>Uploaded:</strong> {{ file_data.uploaded.strftime("%Y-%m-%d
%H:%M UTC") }}
+ </p>
+ </div>
+
+ <h2>Tools</h2>
+ <h3>Delete file</h3>
+ <p>This tool deletes the file from the candidate draft.</p>
+ <form method="post"
+ action="{{ url_for('root_files_delete', project_name=project_name,
version_name=version_name, file_path=file_path) }}">
+ <button type="submit" class="btn btn-danger" disabled>Delete file</button>
+ </form>
+{% endblock content %}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]