From 56dd7bf94020ee0afca4fa7ab56ca68b7a7671a5 Mon Sep 17 00:00:00 2001 From: Yoshifumi Nakamura Date: Mon, 13 Apr 2026 16:58:10 +0900 Subject: [PATCH 01/89] feat: improve usage table readability --- result_server/templates/usage_report.html | 69 +++++++++++++++++++++-- 1 file changed, 63 insertions(+), 6 deletions(-) diff --git a/result_server/templates/usage_report.html b/result_server/templates/usage_report.html index 7e79fda..82293f8 100644 --- a/result_server/templates/usage_report.html +++ b/result_server/templates/usage_report.html @@ -9,15 +9,39 @@ @@ -35,6 +75,7 @@

Meta Information

+
@@ -47,17 +88,20 @@

Meta Information

Code{{ result.get('code', 'N/A') }}
Exp{{ result.get('exp', 'N/A') }}
Estimate Timestamp{{ estimate_meta.get('estimation_result_timestamp', 'N/A') }}
Performance Ratio{{ "%.3f"|format(result.get('performance_ratio', 'N/A')|float) if result.get('performance_ratio') not in [None, '', 'N/A', 'null', 'nan'] else result.get('performance_ratio', 'N/A') }}
+

Package Resolution

+
Current Requested{{ estimate_meta.get('current_package', {}).get('requested_estimation_package', 'N/A') }}
Current Applied{{ estimate_meta.get('current_package', {}).get('estimation_package', 'N/A') }}
Future Requested{{ estimate_meta.get('future_package', {}).get('requested_estimation_package', 'N/A') }}
Future Applied{{ estimate_meta.get('future_package', {}).get('estimation_package', 'N/A') }}
+

Measurement

{{ measurement | tojson(indent=2) }}
@@ -68,6 +112,7 @@

Confidence

Current System

+
@@ -81,10 +126,12 @@

Current System

System{{ current.get('system', 'N/A') }}
FOM{{ current.get('fom', 'N/A') }}
Model{{ current.get('model', {}).get('name', 'N/A') }}
Model Type{{ current.get('model', {}).get('type', 'N/A') }}
+

Future System

+
@@ -98,6 +145,7 @@

Future System

System{{ future.get('system', 'N/A') }}
FOM{{ future.get('fom', 'N/A') }}
Model{{ future.get('model', {}).get('name', 'N/A') }}
Model Type{{ future.get('model', {}).get('type', 'N/A') }}
+
@@ -112,6 +160,7 @@

Current Breakdown

{% if current_breakdown.get('sections') or current_breakdown.get('overlaps') %} {% if current_breakdown.get('sections') %}

Sections

+
@@ -125,7 +174,7 @@

Sections

- +
NameTimePackageScalingFallbackApplicability
{{ item.get('time', 'N/A') }} {{ item.get('estimation_package', 'N/A') }} {{ item.get('scaling_method', 'N/A') }}{{ item.get('fallback_used', '—') }}{{ item.get('fallback_used', '—') }} {% if applicability %}
{{ applicability.get('status', 'N/A') }}
@@ -140,9 +189,11 @@

Sections

{% endfor %}
+
{% endif %} {% if current_breakdown.get('overlaps') %}

Overlaps

+
@@ -156,7 +207,7 @@

Overlaps

- +
SectionsTimePackageScalingFallbackApplicability
{{ item.get('time', 'N/A') }} {{ item.get('estimation_package', 'N/A') }} {{ item.get('scaling_method', 'N/A') }}{{ item.get('fallback_used', '—') }}{{ item.get('fallback_used', '—') }} {% if applicability %}
{{ applicability.get('status', 'N/A') }}
@@ -171,6 +222,7 @@

Overlaps

{% endfor %}
+
{% endif %} {% else %}
No section or overlap breakdown is stored for the current-side estimate.
@@ -182,6 +234,7 @@

Future Breakdown

{% if future_breakdown.get('sections') or future_breakdown.get('overlaps') %} {% if future_breakdown.get('sections') %}

Sections

+
@@ -195,7 +248,7 @@

Sections

- +
NameTimePackageScalingFallbackApplicability
{{ item.get('time', 'N/A') }} {{ item.get('estimation_package', 'N/A') }} {{ item.get('scaling_method', 'N/A') }}{{ item.get('fallback_used', '—') }}{{ item.get('fallback_used', '—') }} {% if applicability %}
{{ applicability.get('status', 'N/A') }}
@@ -210,9 +263,11 @@

Sections

{% endfor %}
+
{% endif %} {% if future_breakdown.get('overlaps') %}

Overlaps

+
@@ -226,7 +281,7 @@

Overlaps

- +
SectionsTimePackageScalingFallbackApplicability
{{ item.get('time', 'N/A') }} {{ item.get('estimation_package', 'N/A') }} {{ item.get('scaling_method', 'N/A') }}{{ item.get('fallback_used', '—') }}{{ item.get('fallback_used', '—') }} {% if applicability %}
{{ applicability.get('status', 'N/A') }}
@@ -241,6 +296,7 @@

Overlaps

{% endfor %}
+
{% endif %} {% else %}
No section or overlap breakdown is stored for the future-side estimate.
From fba4efc455cead2e45bb85c2cb5c7f72d39c09b2 Mon Sep 17 00:00:00 2001 From: Yoshifumi Nakamura Date: Mon, 13 Apr 2026 17:14:41 +0900 Subject: [PATCH 03/89] feat: improve result detail readability --- result_server/templates/result_detail.html | 48 ++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/result_server/templates/result_detail.html b/result_server/templates/result_detail.html index 6e7b5e9..326599e 100644 --- a/result_server/templates/result_detail.html +++ b/result_server/templates/result_detail.html @@ -13,6 +13,44 @@ .meta-table th { text-align: left; min-width: 150px; background-color: #f2f2f2; } .meta-table td { min-width: 200px; } .quality-table th { min-width: 200px; width: 200px; } + .detail-table-wrap { overflow-x: auto; } + .meta-table th, + .quality-table th, + .scalar-table thead th, + .build-table th { + position: sticky; + top: 0; + z-index: 2; + box-shadow: inset 0 -1px 0 #d8e3e8; + } + .meta-table td, + .quality-table td, + .build-table td { + white-space: normal; + max-width: none; + vertical-align: top; + line-height: 1.45; + } + .meta-table td:first-child, + .meta-table th:first-child, + .quality-table td:first-child, + .quality-table th:first-child, + .scalar-table td:first-child, + .scalar-table th:first-child, + .build-table td:first-child, + .build-table th:first-child { + position: sticky; + left: 0; + z-index: 1; + background: #f9fcfd; + box-shadow: 10px 0 18px -18px rgba(15, 23, 42, 0.28); + } + .meta-table th:first-child, + .quality-table th:first-child, + .scalar-table th:first-child, + .build-table th:first-child { + z-index: 3; + } .chart-container { position: relative; width: 100%; max-width: 900px; height: 400px; margin: 0 auto; } .chart-fallback { padding: 20px; text-align: center; color: #999; border: 1px dashed #ddd; border-radius: 4px; } .scalar-table { width: auto; } @@ -40,6 +78,7 @@

Result Detail

Meta Information

+
@@ -57,11 +96,13 @@

Meta Information

{% endif %}
Code{{ result.get('code', 'N/A') }}
System{{ result.get('system', 'N/A') }}
CPUs per Node{{ result.get('cpus_per_node') }}
+
{% if quality %}

Quality

+
@@ -113,6 +154,7 @@

Quality

Level
+
{% endif %} @@ -135,6 +177,7 @@

Vector Metrics - Graph

Vector Metrics - Data Table

+
@@ -157,6 +200,7 @@

Vector Metrics - Data Table

{% endfor %}
+
{% endif %} @@ -167,6 +211,7 @@

Vector Metrics - Data Table

{% if scalar_keys | length >= 2 %}

Scalar Metrics

+
@@ -177,6 +222,7 @@

Scalar Metrics

{% endfor %}
MetricValue
+
{% endif %} {% endif %} @@ -186,6 +232,7 @@

Scalar Metrics

{% set build = result['build'] %}

Build Information

+
{% if build.get('spack') %} @@ -210,6 +257,7 @@

Build Information

{% endif %} {% endif %}
Build Tool{{ build.get('tool', 'N/A') }}
+
{% endif %} From dd29ff1eb0734d6025dc9c3e7dca194dfcbb551b Mon Sep 17 00:00:00 2001 From: Yoshifumi Nakamura Date: Mon, 13 Apr 2026 17:24:29 +0900 Subject: [PATCH 04/89] feat: improve system list usability --- result_server/app.py | 8 +- result_server/app_dev.py | 8 +- result_server/templates/home.html | 35 ++- result_server/templates/systemlist.html | 203 +++++++++++++++++- .../tests/test_systemlist_template.py | 96 +++++++++ result_server/utils/system_info.py | 17 ++ 6 files changed, 351 insertions(+), 16 deletions(-) create mode 100644 result_server/tests/test_systemlist_template.py diff --git a/result_server/app.py b/result_server/app.py index f4112b9..f3a1fff 100644 --- a/result_server/app.py +++ b/result_server/app.py @@ -108,9 +108,13 @@ def create_app(prefix="", base_dir=None): @app.route(f"{prefix}/systemlist") def systemlist(): - from utils.system_info import get_all_systems_info + from utils.system_info import get_all_systems_info, summarize_systems_info systems_info = get_all_systems_info() - return render_template("systemlist.html", systems_info=systems_info) + return render_template( + "systemlist.html", + systems_info=systems_info, + systems_summary=summarize_systems_info(systems_info), + ) return app diff --git a/result_server/app_dev.py b/result_server/app_dev.py index 386e665..d7c350c 100644 --- a/result_server/app_dev.py +++ b/result_server/app_dev.py @@ -114,7 +114,7 @@ def create_dev_app(base_dir): from flask_session import Session from routes.home import register_home_routes - from utils.system_info import get_all_systems_info + from utils.system_info import get_all_systems_info, summarize_systems_info app = Flask(__name__, template_folder="templates") @@ -167,7 +167,11 @@ def create_dev_app(base_dir): @app.route("/systemlist") def systemlist(): systems_info = get_all_systems_info() - return render_template("systemlist.html", systems_info=systems_info) + return render_template( + "systemlist.html", + systems_info=systems_info, + systems_summary=summarize_systems_info(systems_info), + ) return app diff --git a/result_server/templates/home.html b/result_server/templates/home.html index 38f1103..945ff11 100644 --- a/result_server/templates/home.html +++ b/result_server/templates/home.html @@ -208,10 +208,34 @@ .system-card { padding: 18px; } + .system-card-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + margin-bottom: 12px; + } .system-card h3 { - margin: 0 0 12px; + margin: 0; font-size: 18px; } + .system-badge { + display: inline-flex; + align-items: center; + padding: 4px 8px; + border-radius: 999px; + font-size: 12px; + font-weight: 600; + white-space: nowrap; + } + .system-badge.gpu { + background: #eef6ff; + color: #1d4ed8; + } + .system-badge.cpu { + background: #f3f4f6; + color: #4b5563; + } .system-meta { display: grid; gap: 7px; @@ -359,7 +383,14 @@

Available Systems

{% for system in systems %}
-

{{ system.name }}

+
+

{{ system.name }}

+ {% if system.gpu_name != '-' %} + GPU-enabled + {% else %} + CPU-only + {% endif %} +
CPU {{ system.cpu_name }}
CPU/node {{ system.cpu_per_node }} | Cores {{ system.cpu_cores }}
diff --git a/result_server/templates/systemlist.html b/result_server/templates/systemlist.html index 8a13dc2..cb51df2 100644 --- a/result_server/templates/systemlist.html +++ b/result_server/templates/systemlist.html @@ -4,6 +4,157 @@ Available Computing Systems {% include "_table_base.html" %} + + @@ -17,8 +168,37 @@

Available Systems

-
- +
+
+ {{ systems_summary.total_count }} + Connected systems registered in the portal. +
+
+ {{ systems_summary.gpu_enabled_count }} + Systems with GPU accelerators listed in system_info.csv. +
+
+ {{ systems_summary.cpu_only_count }} + CPU-only systems available for benchmark onboarding and browsing. +
+
+ +
+ + + + + {{ systems_summary.total_count }} visible +
+ +
+
@@ -32,19 +212,22 @@

Available Systems

{% for system_name, info in systems_info.items() %} - - - - - - - - + + + + + + + + {% endfor %}
SYSTEM
{{ info.name }}{{ info.cpu_name }}{{ info.cpu_per_node }}{{ info.cpu_cores }}{{ info.gpu_name }}{{ info.gpu_per_node }}{{ info.memory }}
{{ info.name }}{{ info.cpu_name }}{{ info.cpu_per_node }}{{ info.cpu_cores }}{{ info.gpu_name }}{{ info.gpu_per_node }}{{ info.memory }}
+

+ Hardware summaries are sourced from config/system_info.csv. Use this page for quick orientation, then follow the onboarding guides when adding new systems or applications. +

diff --git a/result_server/tests/test_systemlist_template.py b/result_server/tests/test_systemlist_template.py new file mode 100644 index 0000000..80ea052 --- /dev/null +++ b/result_server/tests/test_systemlist_template.py @@ -0,0 +1,96 @@ +import os +import sys +import types + + +def _setup_stubs(): + if "redis" not in sys.modules: + sys.modules["redis"] = types.ModuleType("redis") + + +_setup_stubs() + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) + +from flask import Flask, Blueprint + + +def test_systemlist_page_renders_summary_and_table(): + app = Flask( + __name__, + template_folder=os.path.join(os.path.dirname(__file__), "..", "templates"), + ) + app.config["TESTING"] = True + app.config["SECRET_KEY"] = "test-secret" + + @app.route("/") + def home(): + return "" + + @app.route("/systemlist") + def systemlist(): + return "" + + results_bp = Blueprint("results", __name__) + estimated_bp = Blueprint("estimated", __name__) + auth_bp = Blueprint("auth", __name__) + + @results_bp.route("/") + def results(): + return "" + + @estimated_bp.route("/") + def estimated_results(): + return "" + + @auth_bp.route("/login") + def login(): + return "" + + app.register_blueprint(results_bp, url_prefix="/results") + app.register_blueprint(estimated_bp, url_prefix="/estimated") + app.register_blueprint(auth_bp, url_prefix="/auth") + + with app.test_request_context("/systemlist"): + from flask import render_template + + html = render_template( + "systemlist.html", + systems_summary={ + "total_count": 2, + "gpu_enabled_count": 1, + "cpu_only_count": 1, + }, + systems_info={ + "Fugaku": { + "name": "Fugaku", + "cpu_name": "A64FX", + "cpu_per_node": "1", + "cpu_cores": "48", + "gpu_name": "-", + "gpu_per_node": "-", + "memory": "32GB", + }, + "MiyabiG": { + "name": "MiyabiG", + "cpu_name": "NVIDIA Grace CPU", + "cpu_per_node": "1", + "cpu_cores": "72", + "gpu_name": "NVIDIA Hopper H100 GPU", + "gpu_per_node": "1", + "memory": "120GB", + }, + }, + ) + + assert "Available Systems" in html + assert "Connected systems registered in the portal." in html + assert "Systems with GPU accelerators listed in" in html + assert "systems-table" in html + assert "systems-table-wrap" in html + assert "Filter systems by name, CPU, GPU, or memory" in html + assert "GPU-enabled" in html + assert "CPU-only" in html + assert "Hardware summaries are sourced from" in html + assert "Fugaku" in html + assert "MiyabiG" in html diff --git a/result_server/utils/system_info.py b/result_server/utils/system_info.py index b7f404d..8fb69f6 100644 --- a/result_server/utils/system_info.py +++ b/result_server/utils/system_info.py @@ -76,3 +76,20 @@ def get_all_systems_info(): if name not in ordered: ordered[name] = info return ordered + + +def summarize_systems_info(systems_info): + """Return lightweight summary counts for the system list page.""" + total_count = len(systems_info) + gpu_enabled_count = 0 + + for info in systems_info.values(): + gpu_name = (info.get("gpu_name") or "-").strip() + if gpu_name and gpu_name != "-": + gpu_enabled_count += 1 + + return { + "total_count": total_count, + "gpu_enabled_count": gpu_enabled_count, + "cpu_only_count": total_count - gpu_enabled_count, + } From 93d3544f194597e181d312b2f1bb1e1dce4492ed Mon Sep 17 00:00:00 2001 From: Yoshifumi Nakamura Date: Mon, 13 Apr 2026 17:30:47 +0900 Subject: [PATCH 05/89] feat: polish portal entry and auth pages --- result_server/templates/admin_users.html | 63 +++++++--- result_server/templates/auth_login.html | 128 ++++++++++++++----- result_server/templates/auth_setup.html | 128 +++++++++++++++---- result_server/templates/home.html | 40 ++++-- result_server/templates/usage_report.html | 24 ++-- result_server/tests/test_auth_templates.py | 140 +++++++++++++++++++++ 6 files changed, 434 insertions(+), 89 deletions(-) create mode 100644 result_server/tests/test_auth_templates.py diff --git a/result_server/templates/admin_users.html b/result_server/templates/admin_users.html index ac8d7ee..712b713 100644 --- a/result_server/templates/admin_users.html +++ b/result_server/templates/admin_users.html @@ -25,7 +25,35 @@ .status-ok { color: #15803d; font-weight: 600; } .status-pending { color: #b91c1c; font-weight: 600; } .inline-form { display: inline; } - .actions { white-space: nowrap; } + .actions { min-width: 280px; } + .actions-stack { + display: flex; + flex-wrap: wrap; + gap: 8px; + } + .users-table thead th { + position: sticky; + top: 0; + z-index: 2; + box-shadow: inset 0 -1px 0 #d8e3e8; + } + .users-table td:first-child, + .users-table th:first-child { + position: sticky; + left: 0; + z-index: 1; + background: #f9fcfd; + box-shadow: 10px 0 18px -18px rgba(15, 23, 42, 0.28); + } + .users-table thead th:first-child { + z-index: 3; + background: #eef6f8; + } + .users-table td { + max-width: none; + white-space: normal; + vertical-align: top; + } @@ -67,8 +95,9 @@

Add User

Registered Users

+

Review current user access, TOTP registration status, and affiliation updates in one place.

- +
@@ -90,20 +119,22 @@

Registered Users

{% endif %} {% else %} diff --git a/result_server/templates/auth_login.html b/result_server/templates/auth_login.html index 4570625..699cce0 100644 --- a/result_server/templates/auth_login.html +++ b/result_server/templates/auth_login.html @@ -3,33 +3,94 @@ Login - CX Portal + {% include "_table_base.html" %} {% include "_navigation.html" %} - + + {% if session.get('authenticated') and 'admin' in session.get('user_affiliations', []) %} +
+

Operations Views

+

+ These pages help administrators check node-hour usage, application/system coverage, configuration state, and portal access setup. +

+ +
+ {% endif %}
diff --git a/result_server/templates/usage_report.html b/result_server/templates/usage_report.html index 82293f8..a4db2d4 100644 --- a/result_server/templates/usage_report.html +++ b/result_server/templates/usage_report.html @@ -1,7 +1,7 @@ {% extends "_results_base.html" %} {% block title %}Usage Report{% endblock %} -{% block page_subtitle %}Review node-hour usage across systems, periods, and applications, and check application/system coverage from registered run settings.{% endblock %} +{% block page_subtitle %}Review node-hour usage, registered coverage, configuration checks, and latest result current-state across applications and systems.{% endblock %} {% block content %}
@@ -239,7 +239,7 @@

Configuration Checks

-

Lightweight diagnostics for registered systems, queue definitions, and application support coverage.

+

Lightweight checks for registered systems, queue definitions, and application support completeness.

Registered Systems

@@ -296,7 +296,7 @@

Partial Application Support

Application/System Coverage

-

Quick matrix of which registered systems are enabled in each application's list.csv.

+

Current support matrix based on list.csv plus detected build.sh / run.sh system branches.

enabled and implemented enabled in list.csv, script support incomplete @@ -340,7 +340,7 @@

Application/System Coverage

Result Quality Coverage

-

Current-state check based on the latest collected result for each application/system pair, showing whether top-level source tracking, breakdown output, and estimation-ready fields are present.

+

Current-state check based on the latest collected result for each application/system pair, showing source provenance, breakdown output, and estimation-ready fields.

{% if result_quality_rollup.rows %}
Email -
- - -
- {% if u.email != session.get('user_email') %} -
- -
-
- -
- {% endif %} +
+
+ + +
+ {% if u.email != session.get('user_email') %} +
+ +
+
+ +
+ {% endif %} +
@@ -348,14 +348,14 @@

Result Quality Coverage

- - - - - - - - + + + + + + + + diff --git a/result_server/tests/test_auth_templates.py b/result_server/tests/test_auth_templates.py new file mode 100644 index 0000000..9fceb0d --- /dev/null +++ b/result_server/tests/test_auth_templates.py @@ -0,0 +1,140 @@ +import os +import sys +import types + + +def _setup_stubs(): + if "redis" not in sys.modules: + sys.modules["redis"] = types.ModuleType("redis") + + +_setup_stubs() + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) + +from flask import Flask, Blueprint + + +def _make_app(): + app = Flask( + __name__, + template_folder=os.path.join(os.path.dirname(__file__), "..", "templates"), + ) + app.config["TESTING"] = True + app.config["SECRET_KEY"] = "test-secret" + + results_bp = Blueprint("results", __name__) + estimated_bp = Blueprint("estimated", __name__) + auth_bp = Blueprint("auth", __name__) + admin_bp = Blueprint("admin", __name__) + + @results_bp.route("/") + def results(): + return "" + + @estimated_bp.route("/") + def estimated_results(): + return "" + + @auth_bp.route("/login") + def login(): + return "" + + @auth_bp.route("/setup/") + def setup(token): + return token + + @admin_bp.route("/users") + def users(): + return "" + + @admin_bp.route("/users/add", methods=["POST"]) + def add_user(): + return "" + + @admin_bp.route("/users//affiliations", methods=["POST"]) + def update_affiliations(email): + return email + + @admin_bp.route("/users//reinvite", methods=["POST"]) + def reinvite_user(email): + return email + + @admin_bp.route("/users//delete", methods=["POST"]) + def delete_user(email): + return email + + app.register_blueprint(results_bp, url_prefix="/results") + app.register_blueprint(estimated_bp, url_prefix="/estimated") + app.register_blueprint(auth_bp, url_prefix="/auth") + app.register_blueprint(admin_bp, url_prefix="/admin") + + @app.route("/") + def home(): + return "" + + @app.route("/systemlist") + def systemlist(): + return "" + + return app + + +def test_auth_login_template_renders_portal_shell(): + app = _make_app() + with app.test_request_context("/auth/login"): + from flask import render_template + + html = render_template("auth_login.html", step="email") + + assert "Sign in with your email address and TOTP code" in html + assert "Step 2 of 2" not in html + assert "Continue" in html + + +def test_auth_setup_template_renders_portal_shell(): + app = _make_app() + with app.test_request_context("/auth/setup/token-1"): + from flask import render_template + + html = render_template( + "auth_setup.html", + error=False, + qr_data="data:image/png;base64,abc", + secret="SECRETKEY", + email="user@example.com", + token="token-1", + ) + + assert "Complete portal access setup" in html + assert "Invitation-based setup" in html + assert "Complete Setup" in html + + +def test_admin_users_template_renders_portal_table(): + app = _make_app() + with app.test_request_context("/admin/users"): + from flask import render_template, session + + session["user_email"] = "admin@example.com" + html = render_template( + "admin_users.html", + users=[ + { + "email": "admin@example.com", + "affiliations": ["admin"], + "has_totp": True, + }, + { + "email": "user@example.com", + "affiliations": ["dev"], + "has_totp": False, + }, + ], + invitation_url=None, + ) + + assert "Manage access, affiliations, and TOTP onboarding" in html + assert "Review current user access" in html + assert "Registered" in html + assert "Pending" in html From ab101e566e543dfc76239b216e514cc450f25662 Mon Sep 17 00:00:00 2001 From: Yoshifumi Nakamura Date: Mon, 13 Apr 2026 18:19:49 +0900 Subject: [PATCH 06/89] feat: improve portal list readability --- result_server/templates/_results_table.html | 59 ++++- .../templates/estimated_results.html | 68 ++++- result_server/templates/usage_report.html | 27 ++ .../tests/test_portal_list_templates.py | 233 ++++++++++++++++++ 4 files changed, 383 insertions(+), 4 deletions(-) create mode 100644 result_server/tests/test_portal_list_templates.py diff --git a/result_server/templates/_results_table.html b/result_server/templates/_results_table.html index b573e79..fbefac4 100644 --- a/result_server/templates/_results_table.html +++ b/result_server/templates/_results_table.html @@ -2,7 +2,63 @@ {% include "_pagination.html" %} -
Application SystemLatest ResultSource StatusSource TypeSource ReferenceMissing Source FieldsBreakdown PresentEstimation-ReadyRichLatest ResultTrackedTypeReferenceMissing FieldsBreakdownEst.-ReadyRich Warnings
+ + +

Use the server-side filters to narrow the table first, then select visible rows if you want to open the compare view.

+ +
+
@@ -191,6 +247,7 @@ {% endfor %}
+
+ +

Focus on system pairs, applied packages, and ratio at the list level, then open detail when you need breakdowns or fallback/applicability context.

+ +
+ @@ -137,7 +199,7 @@ -
Timestamp{{ applied_short }}{% if r.method_class or r.detail_level %}
{{ r.method_class }}{% if r.detail_level %} / {{ r.detail_level }}{% endif %}
{% endif %}
{{ r.estimate_uuid[:8] if r.estimate_uuid else '' }} {{ ratio }} +
{% else %} -

No collected result-quality data is available yet.

+

No collected result-quality data is available yet.

{% endif %}
diff --git a/result_server/templates/estimated_results.html b/result_server/templates/estimated_results.html index 41d4e39..b5d063e 100644 --- a/result_server/templates/estimated_results.html +++ b/result_server/templates/estimated_results.html @@ -59,7 +59,7 @@ {% include "_pagination.html" %} - + + onerror="document.getElementById('chartFallback').hidden = false; document.getElementById('vectorChart').style.display = 'none';"> - - - -{% include "_navigation.html" %} - -
-
- CX Portal -

Available Systems

-

This page summarizes the systems currently connected to the CX portal for browsing, comparison, and onboarding guidance.

-
+ applySystemFilters(); + } -
-
-
- {{ systems_summary.total_count }} - Connected systems registered in the portal. -
-
- {{ systems_summary.gpu_enabled_count }} - Systems with GPU accelerators listed in system_info.csv. -
-
- {{ systems_summary.cpu_only_count }} - CPU-only systems available for benchmark onboarding and browsing. -
-
+ document.addEventListener("DOMContentLoaded", () => { + applySystemFilters(); + }); + -
- - - - - {{ systems_summary.total_count }} visible +
+
+
+ {{ systems_summary.total_count }} + Connected systems registered in the portal.
- -
- - - - - - - - - - - - - - {% for system_name, info in systems_info.items() %} - - - - - - - - - - {% endfor %} - -
SYSTEMCPU NameCPU/nodeCPU Core CountGPU NameGPU/nodeMemory
{{ info.name }}{{ info.cpu_name }}{{ info.cpu_per_node }}{{ info.cpu_cores }}{{ info.gpu_name }}{{ info.gpu_per_node }}{{ info.memory }}
+
+ {{ systems_summary.gpu_enabled_count }} + Systems with GPU accelerators listed in system_info.csv.
-

- Hardware summaries are sourced from config/system_info.csv. Use this page for quick orientation, then follow the onboarding guides when adding new systems or applications. -

-
- +
+ {{ systems_summary.cpu_only_count }} + CPU-only systems available for benchmark onboarding and browsing.
-
-
+
+ +
+ + + + + {{ systems_summary.total_count }} visible +
- - +
+ + + + + + + + + + + + + + {% for system_name, info in systems_info.items() %} + + + + + + + + + + {% endfor %} + +
SYSTEMCPU NameCPU/nodeCPU Core CountGPU NameGPU/nodeMemory
{{ info.name }}{{ info.cpu_name }}{{ info.cpu_per_node }}{{ info.cpu_cores }}{{ info.gpu_name }}{{ info.gpu_per_node }}{{ info.memory }}
+
+

+ Hardware summaries are sourced from config/system_info.csv. Use this page for quick orientation, then follow the onboarding guides when adding new systems or applications. +

+
+ +
+
+{% endblock %} diff --git a/result_server/utils/estimated_table_rows.py b/result_server/utils/estimated_table_rows.py index e460bc7..ae3a6ca 100644 --- a/result_server/utils/estimated_table_rows.py +++ b/result_server/utils/estimated_table_rows.py @@ -1,5 +1,16 @@ from utils.result_records import format_numeric_value, split_display_timestamp +SCALING_SHORT_NAMES = { + "instrumented-app-sections-dummy": "instr-app-sec", + "scale-mock": "scale-mock", +} + +PACKAGE_SHORT_NAMES = { + "instrumented_app_sections_dummy": "instr_app_sec", + "lightweight_fom_scaling": "weakscaling", + "weakscaling": "weakscaling", +} + def build_estimated_table_row(filename, result_data, fallback_uuid=None, fallback_timestamp=None): current = result_data.get("current_system", {}) @@ -37,7 +48,7 @@ def build_estimated_table_row(filename, result_data, fallback_uuid=None, fallbac "requested_current_estimation_package": requested_current, "requested_future_estimation_package": requested_future, "estimate_uuid": estimate_uuid, - "estimate_uuid_short": estimate_uuid[:8] if estimate_uuid else "", + "estimate_uuid_short": _short_identifier(estimate_uuid), "performance_ratio": result_data.get("performance_ratio", ""), "performance_ratio_display": format_numeric_value(result_data.get("performance_ratio", "")), "json_link": filename, @@ -113,43 +124,47 @@ def _build_estimated_system_columns(group_label, key_prefix): def _format_scaling_short_name(value): - if value == "instrumented-app-sections-dummy": - return "instr-app-sec" - if value == "scale-mock": - return "scale-mock" - return value + return SCALING_SHORT_NAMES.get(value, value) def _format_package_short_name(value): - if value == "instrumented_app_sections_dummy": - return "instr_app_sec" - if value in ("lightweight_fom_scaling", "weakscaling"): - return "weakscaling" - return value + return PACKAGE_SHORT_NAMES.get(value, value) def _build_requested_package_title(requested_package, requested_current, requested_future): - title = requested_package or "" - if requested_current: - title += "\ncurrent-side: " + requested_current - if requested_future: - title += "\nfuture-side: " + requested_future - return title + return _build_multiline_title( + requested_package, + [ + ("current-side", requested_current), + ("future-side", requested_future), + ], + ) def _build_applied_package_title(applied_package, method_class, detail_level, current_package, future_package): - title = applied_package or "" - if method_class: - title += "\nclass: " + method_class - if detail_level: - title += "\ndetail: " + detail_level - if current_package: - title += "\ncurrent-side: " + current_package - if future_package: - title += "\nfuture-side: " + future_package - return title + return _build_multiline_title( + applied_package, + [ + ("class", method_class), + ("detail", detail_level), + ("current-side", current_package), + ("future-side", future_package), + ], + ) def _build_applied_package_meta_line(method_class, detail_level): meta_parts = [part for part in (method_class, detail_level) if part] return " / ".join(meta_parts) + + +def _build_multiline_title(base, labeled_values): + lines = [base] if base else [] + for label, value in labeled_values: + if value: + lines.append(f"{label}: {value}") + return "\n".join(lines) + + +def _short_identifier(value, length=8): + return value[:length] if value else "" From 7ff14d8495869bb7e60d0aa6880f9c820a6e8640 Mon Sep 17 00:00:00 2001 From: Yoshifumi Nakamura Date: Tue, 14 Apr 2026 18:02:53 +0900 Subject: [PATCH 62/89] refactor: simplify portal home shell --- result_server/templates/home.html | 738 +++++++++----------- result_server/utils/estimated_table_rows.py | 25 +- result_server/utils/result_records.py | 12 + 3 files changed, 344 insertions(+), 431 deletions(-) diff --git a/result_server/templates/home.html b/result_server/templates/home.html index 33c8774..5c0c494 100644 --- a/result_server/templates/home.html +++ b/result_server/templates/home.html @@ -1,432 +1,340 @@ - - - - - CX Portal - - - +{% extends "_results_base.html" %} -{% include "_navigation.html" %} +{% block title %}CX Portal{% endblock %} +{% block page_title %}CX Data Flow, Connected Systems, and Portal Access{% endblock %} +{% block page_subtitle %}This portal is part of the CX Framework, supporting continuous benchmarking, estimation, and feedback for integrated HPC applications.{% endblock %} -
-
-
- CX Portal -

CX Data Flow, Connected Systems, and Portal Access

-

- This portal is part of the overall system implementing the CX Framework, supporting - continuous benchmarking, continuous estimation, and continuous feedback for HPC - applications integrated into this portal. -

-
- Browse Results - View All Systems - {% if session.get('authenticated') %} - Open Estimated Results - {% else %} - Estimated Results (login required) - {% endif %} -
+{% block content %} + + +
+
+

Start Here

+

+ Browse measured and estimated results, inspect connected systems, and jump directly into onboarding guides for applications and sites. +

+
+ Browse Results + View All Systems + {% if session.get('authenticated') %} + Open Estimated Results + {% else %} + Estimated Results (login required) + {% endif %}
+
-
- -
-
- {% if session.get('authenticated') and 'admin' in session.get('user_affiliations', []) %} -
-

Operations Views

-

- These pages help administrators check node-hour usage, application/system coverage, configuration state, and portal access setup. -

- +
+
+

Main Entry Points

+

+ Start here for the main portal pages used to browse measured results, projected outputs, and connected systems. +

+ - {% endif %}
-
-
-
-

Available Systems

-

Connected systems are sourced from `config/system_info.csv` and shown here for quick orientation.

-
+
+

For Application Developers

+

+ These guides explain how to add a new benchmark application, enable a new site, and attach estimation support. +

+ -
- {% for system in systems %} -
-
-

{{ system.name }}

- {% if system.gpu_name != '-' %} - GPU-enabled - {% else %} - CPU-only - {% endif %} -
-
-
CPU {{ system.cpu_name }}
-
CPU/node {{ system.cpu_per_node }} | Cores {{ system.cpu_cores }}
-
GPU {{ system.gpu_name }}{% if system.gpu_per_node != '-' %} ({{ system.gpu_per_node }}/node){% endif %}
-
Memory {{ system.memory }}
-
-
- {% endfor %} -
-
+ + {% if session.get('authenticated') and 'admin' in session.get('user_affiliations', []) %} +
+

Operations Views

+

+ These pages help administrators check node-hour usage, application/system coverage, configuration state, and portal access setup. +

+
-
+ {% endif %} +
- - +
+
+
+

Available Systems

+

Connected systems are sourced from `config/system_info.csv` and shown here for quick orientation.

+
+
+
+ {% for system in systems %} +
+
+

{{ system.name }}

+ {% if system.gpu_name != '-' %} + GPU-enabled + {% else %} + CPU-only + {% endif %} +
+
+
CPU {{ system.cpu_name }}
+
CPU/node {{ system.cpu_per_node }} | Cores {{ system.cpu_cores }}
+
GPU {{ system.gpu_name }}{% if system.gpu_per_node != '-' %} ({{ system.gpu_per_node }}/node){% endif %}
+
Memory {{ system.memory }}
+
+
+ {% endfor %} +
+ +
+{% endblock %} diff --git a/result_server/utils/estimated_table_rows.py b/result_server/utils/estimated_table_rows.py index ae3a6ca..2a19983 100644 --- a/result_server/utils/estimated_table_rows.py +++ b/result_server/utils/estimated_table_rows.py @@ -1,4 +1,9 @@ -from utils.result_records import format_numeric_value, split_display_timestamp +from utils.result_records import ( + build_multiline_title, + format_numeric_value, + short_identifier, + split_display_timestamp, +) SCALING_SHORT_NAMES = { "instrumented-app-sections-dummy": "instr-app-sec", @@ -48,7 +53,7 @@ def build_estimated_table_row(filename, result_data, fallback_uuid=None, fallbac "requested_current_estimation_package": requested_current, "requested_future_estimation_package": requested_future, "estimate_uuid": estimate_uuid, - "estimate_uuid_short": _short_identifier(estimate_uuid), + "estimate_uuid_short": short_identifier(estimate_uuid), "performance_ratio": result_data.get("performance_ratio", ""), "performance_ratio_display": format_numeric_value(result_data.get("performance_ratio", "")), "json_link": filename, @@ -132,7 +137,7 @@ def _format_package_short_name(value): def _build_requested_package_title(requested_package, requested_current, requested_future): - return _build_multiline_title( + return build_multiline_title( requested_package, [ ("current-side", requested_current), @@ -142,7 +147,7 @@ def _build_requested_package_title(requested_package, requested_current, request def _build_applied_package_title(applied_package, method_class, detail_level, current_package, future_package): - return _build_multiline_title( + return build_multiline_title( applied_package, [ ("class", method_class), @@ -156,15 +161,3 @@ def _build_applied_package_title(applied_package, method_class, detail_level, cu def _build_applied_package_meta_line(method_class, detail_level): meta_parts = [part for part in (method_class, detail_level) if part] return " / ".join(meta_parts) - - -def _build_multiline_title(base, labeled_values): - lines = [base] if base else [] - for label, value in labeled_values: - if value: - lines.append(f"{label}: {value}") - return "\n".join(lines) - - -def _short_identifier(value, length=8): - return value[:length] if value else "" diff --git a/result_server/utils/result_records.py b/result_server/utils/result_records.py index 511c2ae..c21dadd 100644 --- a/result_server/utils/result_records.py +++ b/result_server/utils/result_records.py @@ -205,3 +205,15 @@ def split_display_timestamp(value): if " " not in value: return value, "" return value.split(" ", 1) + + +def short_identifier(value, length=8): + return value[:length] if value else "" + + +def build_multiline_title(base, labeled_values): + lines = [base] if base else [] + for label, value in labeled_values: + if value: + lines.append(f"{label}: {value}") + return "\n".join(lines) From 7bf76100d3debec66143af2eddc28853a48e3d59 Mon Sep 17 00:00:00 2001 From: Yoshifumi Nakamura Date: Tue, 14 Apr 2026 18:08:52 +0900 Subject: [PATCH 63/89] fix: simplify portal page headers --- result_server/templates/_navigation.html | 12 ++++++++++++ result_server/templates/_results_base.html | 1 - result_server/templates/_table_base.html | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/result_server/templates/_navigation.html b/result_server/templates/_navigation.html index 1808695..a2e738b 100644 --- a/result_server/templates/_navigation.html +++ b/result_server/templates/_navigation.html @@ -16,6 +16,17 @@ } .nav-left { display: flex; align-items: stretch; gap: 0; } .nav-right { margin-left: auto; display: flex; align-items: center; gap: 4px; } +.nav-brand { + display: inline-flex; + align-items: center; + padding: 10px 14px 10px 0; + margin-right: 8px; + color: #0f172a; + text-decoration: none; + font-weight: 700; + letter-spacing: -0.01em; + white-space: nowrap; +} .nav-link { text-decoration: none; color: #0f766e; @@ -88,6 +99,7 @@