This is an automated email from the ASF dual-hosted git repository.
tn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-atr-experiments.git
The following commit(s) were added to refs/heads/main by this push:
new 3efcf10 chore: use async requests for updating the list of projects
3efcf10 is described below
commit 3efcf104866ddfec5ed75d9b4ddf1cff98691886
Author: Thomas Neidhart <[email protected]>
AuthorDate: Tue Mar 4 16:24:38 2025 +0100
chore: use async requests for updating the list of projects
---
atr/blueprints/admin/admin.py | 176 +++++++++++++-----------
atr/blueprints/admin/templates/update-pmcs.html | 57 +++++++-
2 files changed, 148 insertions(+), 85 deletions(-)
diff --git a/atr/blueprints/admin/admin.py b/atr/blueprints/admin/admin.py
index 6240234..ca603e2 100644
--- a/atr/blueprints/admin/admin.py
+++ b/atr/blueprints/admin/admin.py
@@ -16,12 +16,14 @@
# under the License.
from collections import defaultdict
+from collections.abc import Mapping
from pathlib import Path
from statistics import mean, median, stdev
+from typing import Any
import aiofiles.os
import httpx
-from quart import current_app, flash, redirect, render_template, request,
url_for
+from quart import current_app, flash, render_template, request
from sqlmodel import select
from werkzeug.wrappers.response import Response
@@ -176,98 +178,106 @@ async def admin_data(model: str = "PMC") -> str:
@blueprint.route("/projects/update", methods=["GET", "POST"])
-async def admin_projects_update() -> str | Response:
+async def admin_projects_update() -> str | Response | tuple[Mapping[str, Any],
int]:
"""Update projects from remote data."""
if request.method == "POST":
try:
- apache_projects = await get_projects_data()
- podlings_data = await get_current_podlings_data()
- groups_data = await get_groups_data()
+ updated_count = await _update_pmcs()
+ return {
+ "message": f"Successfully updated {updated_count} projects
(PMCs and PPMCs) with membership data",
+ "category": "success",
+ }, 200
except httpx.RequestError as e:
- await flash(f"Failed to fetch data: {e!s}", "error")
- return redirect(url_for("admin.admin_projects_update"))
-
- updated_count = 0
-
- try:
- async with get_session() as db_session:
- async with db_session.begin():
- # First update PMCs
- for project in apache_projects.projects:
- name = project.name
- # Skip non-PMC committees
- if not project.pmc:
- continue
-
- # Get or create PMC
- statement = select(PMC).where(PMC.project_name == name)
- pmc = (await
db_session.execute(statement)).scalar_one_or_none()
- if not pmc:
- pmc = PMC(project_name=name)
- db_session.add(pmc)
-
- # Update PMC data from groups.json
- pmc_members = groups_data.get(f"{name}-pmc")
- committers = groups_data.get(name)
- pmc.pmc_members = pmc_members if pmc_members is not
None else []
- pmc.committers = committers if committers is not None
else []
- # Ensure this is set for PMCs
- pmc.is_podling = False
-
- # For release managers, use PMC members for now
- # TODO: Consider a more sophisticated way to determine
release managers
- pmc.release_managers = pmc.pmc_members
-
- updated_count += 1
-
- # Then add PPMCs (podlings)
- for podling_name, podling_data in podlings_data:
- # Get or create PPMC
- statement = select(PMC).where(PMC.project_name ==
podling_name)
- ppmc = (await
db_session.execute(statement)).scalar_one_or_none()
- if not ppmc:
- ppmc = PMC(project_name=podling_name)
- db_session.add(ppmc)
-
- # Update PPMC data from groups.json
- ppmc.is_podling = True
- pmc_members = groups_data.get(f"{podling_name}-pmc")
- committers = groups_data.get(podling_name)
- ppmc.pmc_members = pmc_members if pmc_members is not
None else []
- ppmc.committers = committers if committers is not None
else []
- # Use PPMC members as release managers
- ppmc.release_managers = ppmc.pmc_members
-
- updated_count += 1
-
- # Add special entry for Tooling PMC
- # Not clear why, but it's not in the Whimsy data
- statement = select(PMC).where(PMC.project_name ==
"tooling")
- tooling_pmc = (await
db_session.execute(statement)).scalar_one_or_none()
- if not tooling_pmc:
- tooling_pmc = PMC(project_name="tooling")
- db_session.add(tooling_pmc)
- updated_count += 1
-
- # Update Tooling PMC data
- # Could put this in the "if not tooling_pmc" block, perhaps
- tooling_pmc.pmc_members = ["wave", "tn", "sbp"]
- tooling_pmc.committers = ["wave", "tn", "sbp"]
- tooling_pmc.release_managers = ["wave"]
- tooling_pmc.is_podling = False
-
- await flash(
- f"Successfully updated {updated_count} projects (PMCs and
PPMCs) with membership data", "success"
- )
+ return {
+ "message": f"Failed to fetch data: {e!s}",
+ "category": "error",
+ }, 200
except Exception as e:
- await flash(f"Failed to update projects: {e!s}", "error")
-
- return redirect(url_for("admin.admin_projects_update"))
+ return {
+ "message": f"Failed to update projects: {e!s}",
+ "category": "error",
+ }, 200
# For GET requests, show the update form
return await render_template("update-pmcs.html")
+async def _update_pmcs() -> int:
+ apache_projects = await get_projects_data()
+ podlings_data = await get_current_podlings_data()
+ groups_data = await get_groups_data()
+
+ updated_count = 0
+
+ async with get_session() as db_session:
+ async with db_session.begin():
+ # First update PMCs
+ for project in apache_projects.projects:
+ name = project.name
+ # Skip non-PMC committees
+ if not project.pmc:
+ continue
+
+ # Get or create PMC
+ statement = select(PMC).where(PMC.project_name == name)
+ pmc = (await
db_session.execute(statement)).scalar_one_or_none()
+ if not pmc:
+ pmc = PMC(project_name=name)
+ db_session.add(pmc)
+
+ # Update PMC data from groups.json
+ pmc_members = groups_data.get(f"{name}-pmc")
+ committers = groups_data.get(name)
+ pmc.pmc_members = pmc_members if pmc_members is not None else
[]
+ pmc.committers = committers if committers is not None else []
+ # Ensure this is set for PMCs
+ pmc.is_podling = False
+
+ # For release managers, use PMC members for now
+ # TODO: Consider a more sophisticated way to determine release
managers
+ pmc.release_managers = pmc.pmc_members
+
+ updated_count += 1
+
+ # Then add PPMCs (podlings)
+ for podling_name, podling_data in podlings_data:
+ # Get or create PPMC
+ statement = select(PMC).where(PMC.project_name == podling_name)
+ ppmc = (await
db_session.execute(statement)).scalar_one_or_none()
+ if not ppmc:
+ ppmc = PMC(project_name=podling_name)
+ db_session.add(ppmc)
+
+ # Update PPMC data from groups.json
+ ppmc.is_podling = True
+ pmc_members = groups_data.get(f"{podling_name}-pmc")
+ committers = groups_data.get(podling_name)
+ ppmc.pmc_members = pmc_members if pmc_members is not None else
[]
+ ppmc.committers = committers if committers is not None else []
+ # Use PPMC members as release managers
+ ppmc.release_managers = ppmc.pmc_members
+
+ updated_count += 1
+
+ # Add special entry for Tooling PMC
+ # Not clear why, but it's not in the Whimsy data
+ statement = select(PMC).where(PMC.project_name == "tooling")
+ tooling_pmc = (await
db_session.execute(statement)).scalar_one_or_none()
+ if not tooling_pmc:
+ tooling_pmc = PMC(project_name="tooling")
+ db_session.add(tooling_pmc)
+ updated_count += 1
+
+ # Update Tooling PMC data
+ # Could put this in the "if not tooling_pmc" block, perhaps
+ tooling_pmc.pmc_members = ["wave", "tn", "sbp"]
+ tooling_pmc.committers = ["wave", "tn", "sbp"]
+ tooling_pmc.release_managers = ["wave"]
+ tooling_pmc.is_podling = False
+
+ return updated_count
+
+
@blueprint.route("/debug/database")
async def admin_debug_database() -> str:
"""Debug information about the database."""
diff --git a/atr/blueprints/admin/templates/update-pmcs.html
b/atr/blueprints/admin/templates/update-pmcs.html
index bbb272d..16714ed 100644
--- a/atr/blueprints/admin/templates/update-pmcs.html
+++ b/atr/blueprints/admin/templates/update-pmcs.html
@@ -30,6 +30,10 @@
background: #047;
}
+ button:disabled {
+ color: gray;
+ }
+
div.warning {
margin: 1.5rem 0;
padding: 1rem;
@@ -84,7 +88,56 @@
</ul>
</div>
- <form method="post">
- <button type="submit">Update Projects</button>
+ <div id="status"></div>
+
+ <form action="javascript:submitForm().then(_ => { return false; })">
+ <button type="submit" id="submitButton">Update Projects</button>
</form>
+
+ <script>
+ const submitForm = async () => {
+ const button = document.getElementById("submitButton");
+ button.disabled = true;
+ document.body.style.cursor = 'wait'
+
+ const statusElement = document.getElementById("status");
+ while (statusElement.firstChild) {
+ statusElement.firstChild.remove();
+ }
+
+ try {
+ const response = await fetch(window.location.href, {
+ method: "POST",
+ });
+
+ if (!response.ok) {
+ addStatusMessage(statusElement, "Could not make network
request", "error");
+ return
+ }
+
+ const data = await response.json();
+ addStatusMessage(statusElement, data.message, data.category)
+ } catch (error) {
+ addStatusMessage(statusElement, error, "error")
+ } finally {
+ button.disabled = false;
+ document.body.style.cursor = 'default'
+ }
+ };
+
+ function addStatusMessage(parentElement, message, category) {
+ const divElement = document.createElement("div");
+ divElement.classList.add("status-message");
+ divElement.classList.add(category);
+ if (category === "error") {
+ const prefixElement = document.createElement("strong");
+ const textElement = document.createTextNode("Error: ");
+ prefixElement.appendChild(textElement);
+ divElement.appendChild(prefixElement);
+ }
+ const textNode = document.createTextNode(message);
+ divElement.appendChild(textNode);
+ parentElement.appendChild(divElement);
+ }
+ </script>
{% endblock content %}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]