Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ result_server/received/

# Python bytecode cache
result_server/routes/__pycache__/
result_server/utils/__pycache__/

# editor backup files
# editor backup files and caches
*~
*.bak
*.pyc
9 changes: 8 additions & 1 deletion programs/qws/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,14 @@ case "$system" in
#ls ../results/
;;
RC_GH200)
echo FOM:11.22 FOM_version:dummy Exp:DummyFrom_qc-gh200 node_count:$nodes >> ../results/result
echo FOM:11.22 FOM_version:dummy_qc-gh200 Exp:confidential_null node_count:$nodes >> ../results/result
# with confidential key
echo FOM:11.22 FOM_version:dummy_qc-gh200 Exp:confidential_TeamA node_count:$nodes confidential:TeamA>> ../results/result
echo FOM:11.22 FOM_version:dummy_qc-gh200 Exp:confidential_TeamB node_count:$nodes confidential:TeamB>> ../results/result
echo FOM:11.22 FOM_version:dummy_qc-gh200 Exp:confidential_TeamC node_count:$nodes confidential:TeamC>> ../results/result
echo FOM:11.22 FOM_version:dummy_qc-gh200 Exp:confidential_TeamD node_count:$nodes confidential:TeamD>> ../results/result
echo FOM:11.22 FOM_version:dummy_qc-gh200 Exp:confidential_TeamE node_count:$nodes confidential:TeamE>> ../results/result
echo FOM:11.22 FOM_version:dummy_qc-gh200 Exp:confidential_TeamF node_count:$nodes confidential:TeamF>> ../results/result
;;
*)
echo "Unknown Running system: $system"
Expand Down
5 changes: 5 additions & 0 deletions result_server/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
# Create the Flask app and specify the templates folder
app = Flask(__name__, template_folder="templates")


# Set a secret key for session management (required for flash and OTP sessions)
# In production, use a secure random key, e.g., os.urandom(24)
app.secret_key = os.environ.get("FLASK_SECRET_KEY", "dev_secret_key")

# Register route blueprints
app.register_blueprint(receive_bp)
app.register_blueprint(results_bp)
Expand Down
174 changes: 82 additions & 92 deletions result_server/routes/results.py
Original file line number Diff line number Diff line change
@@ -1,111 +1,101 @@
import os
import json
from flask import Blueprint, render_template, send_from_directory, abort, jsonify
import re
from datetime import datetime
from flask import (
Blueprint, render_template, request, session,
redirect, url_for, flash, abort
)
from utils.results_loader import load_results_table
from utils.otp_manager import send_otp, verify_otp, get_affiliations
from utils.result_file import load_result_file, get_file_confidential_tags

results_bp = Blueprint("results", __name__)
SAVE_DIR = "received"


# ==========================================
# 公開用の結果一覧ページ
# ==========================================
@results_bp.route("/results")
def list_results():
files = os.listdir(SAVE_DIR)
json_files = sorted([f for f in files if f.endswith(".json")], reverse=True)
tgz_files = [f for f in files if f.endswith(".tgz")]
def results():
rows, columns = load_results_table(public_only=True)
return render_template("results.html", rows=rows, columns=columns)

rows = []
for json_file in json_files:
json_path = os.path.join(SAVE_DIR, json_file)
try:
with open(json_path, "r", encoding="utf-8") as f:
data = json.load(f)
code = data.get("code", "N/A")
sys = data.get("system", "N/A")
fom = data.get("FOM", "N/A")
fom_version = data.get("FOM_version", "N/A")
exp = data.get("Exp", "N/A")
cpu = data.get("cpu_name", "N/A")
gpu = data.get("gpu_name", "N/A")
nodes = data.get("node_count", "N/A")
cpus = data.get("cpus_per_node", "N/A")
gpus = data.get("gpus_per_node", "N/A")
cpu_cores = data.get("cpu_cores", "N/A")
except Exception:
code = sys = fom = cpu = gpu = nodes = cpus = gpus = cpu_cores = "Invalid"

match = re.search(r"\d{8}_\d{6}", json_file)
if match:
try:
ts = datetime.strptime(match.group(), "%Y%m%d_%H%M%S")
timestamp = ts.strftime("%Y-%m-%d %H:%M:%S")
except ValueError:
timestamp = "Invalid"
else:
timestamp = "Unknown"
# ==========================================
# 機密データ付きの結果ページ(OTP認証付き)
# ==========================================
@results_bp.route("/results_confidential", methods=["GET", "POST"])
def results_confidential():
# フォーム送信処理
if request.method == "POST":
email = request.form.get("email")
otp = request.form.get("otp")

uuid_match = re.search(r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", json_file, re.IGNORECASE)
if uuid_match:
uid = uuid_match.group(0)
else:
uid = None
if email and not otp:
# STEP1: メール送信
success, msg = send_otp(email)
if success:
flash("OTPをメールに送信しました")
session["otp_email"] = email
session["otp_stage"] = "otp"
else:
flash(msg)
session.pop("otp_email", None)
session["otp_stage"] = "email"
return redirect(url_for("results.results_confidential"))

if uid:
tgz_file = next((f for f in tgz_files if uid in f), None)
else:
tgz_file = None
elif otp:
# STEP2: OTP検証
otp_email = session.get("otp_email")
if otp_email and verify_otp(otp_email, otp):
session["authenticated_confidential"] = True
flash("認証成功")
session.pop("otp_stage", None) # 認証済みなので削除
else:
flash("OTP認証失敗")
session.pop("otp_email", None)
session.pop("authenticated_confidential", None)
session["otp_stage"] = "email"
return redirect(url_for("results.results_confidential"))

row = {
"timestamp": timestamp,
"code": code,
"exp": exp,
"fom": fom,
"fom_version": fom_version,
"system": sys,
"cpu": cpu,
"gpu": gpu,
"nodes": nodes,
"cpus": cpus,
"gpus": gpus,
"cpu_cores": cpu_cores,
"json_link": json_file,
"data_link": tgz_file,
}
# OTPステージ判定
authenticated = session.get("authenticated_confidential", False)
otp_email = session.get("otp_email")

rows.append(row)
if authenticated:
otp_stage = None # 認証済みなのでフォームは出さない
elif otp_email:
otp_stage = "otp" # OTP入力待ち
else:
otp_stage = "email" # メール入力待ち

columns = [
("Timestamp", "timestamp"),
("CODE", "code"),
("Exp", "exp"),
("FOM", "fom"),
("FOM version", "fom_version"),
("SYSTEM", "system"),
("CPU Name", "cpu"),
("GPU Name", "gpu"),
("Nodes", "nodes"),
("CPU/node", "cpus"),
("GPU/node", "gpus"),
("CPU Core Count", "cpu_cores"),
("JSON", "json_link"),
("PA Data", "data_link"),
]

return render_template("results.html", columns=columns, rows=rows)
# 結果テーブル読み込み(confidential制御は utils 内で処理)
rows, columns = load_results_table(
public_only=False,
session_email=otp_email,
authenticated=authenticated
)

return render_template(
"results_confidential.html",
rows=rows,
columns=columns,
authenticated=authenticated,
otp_stage=otp_stage
)

# ==========================================
# 個別結果ファイルの表示/ダウンロード
# ==========================================
@results_bp.route("/results/<filename>")
def show_result(filename):
filepath = os.path.join(SAVE_DIR, filename)
if not os.path.exists(filepath):
abort(404)
tags = get_file_confidential_tags(filename)

if filename.endswith(".json"):
try:
with open(filepath, "r", encoding="utf-8") as f:
data = json.load(f)
return jsonify(data)
except json.JSONDecodeError:
abort(400, "Invalid JSON")
else:
return send_from_directory(SAVE_DIR, filename)

if tags:
authenticated = session.get("authenticated_confidential", False)
email = session.get("otp_email")
affs = get_affiliations(email) if email else []
if not authenticated or not (set(tags) & set(affs)):
abort(403, "このファイルにアクセスする権限がありません")

return load_result_file(filename)
39 changes: 39 additions & 0 deletions result_server/templates/_results_table.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!-- templates/_results_table.html -->
<table id="resultsTable">
<thead>
<tr>
{% for col_name, _ in columns %}
{% if col_name in ["Timestamp", "SYSTEM", "CODE", "FOM", "FOM version", "Exp", "Nodes", "JSON", "PA Data"] %}
<th>{{ col_name }}</th>
{% endif %}
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in rows %}
<tr>
{% for _, key in columns %}
{% if key in ["json_link", "data_link"] %}
<td>
{% if row[key] %}
<a href="/results/{{ row[key] }}">
{{ "json" if key == "json_link" else "data" }}
</a>
{% else %}
-
{% endif %}
</td>
{% elif key == "system" %}
<td>
<a href="{{ url_for('hard_env', sys=row[key]) }}" class="underline-link">
{{ row[key] }}
</a>
</td>
{% elif key in ["timestamp", "code", "fom", "fom_version", "exp", "nodes"] %}
<td>{{ row[key] }}</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
75 changes: 6 additions & 69 deletions result_server/templates/results.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,11 @@
<meta charset="UTF-8">
<title>Uploaded Results</title>
<style>
table {
border-collapse: collapse;
width: 100%;
}

th, td {
border: 1px solid #ddd;
padding: 8px;
}

th {
background-color: #f2f2f2;
}

tr:hover {
background-color: #f5f5f5;
}
.underline-link {
text-decoration: underline;
color: darkblue;
cursor: pointer;
}
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; }
th { background-color: #f2f2f2; }
tr:hover { background-color: #f5f5f5; }
.underline-link { text-decoration: underline; color: darkblue; cursor: pointer; }
</style>
<script>
function filterTable() {
Expand All @@ -48,53 +31,7 @@ <h1>Uploaded Results</h1>
<input type="text" id="filterInput" onkeyup="filterTable()" placeholder="Search by multiple keywords...">
<br><br>

<table id="resultsTable">
<thead>
<tr>
{% for col_name, _ in columns %}
{% if col_name == "Timestamp" or col_name == "SYSTEM" or col_name == "CODE" or col_name == "FOM" or col_name == "FOM version" or col_name == "Exp" or col_name == "Nodes" or col_name == "JSON" or col_name == "PA Data" %}
<th>{{ col_name }}</th>
{% endif %}
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in rows %}
<tr>
{% for _, key in columns %}
{% if key == "json_link" or key == "data_link" %}
<td>
{% if row[key] %}
<a href="/results/{{ row[key] }}">
{% if key == "json_link" %}
json
{% elif key == "data_link" %}
data
{% endif %}
</a>
{% else %}
-
{% endif %}
</td>
{% endif%}
{% if key == "system" %}
<td>
<a href="{{ url_for('hard_env', sys=row[key]) }}" class="underline-link">
{{ row[key] }}
</a>
</td>
{% else %}
{% if key == "timestamp" or key == "code" or key == "fom" or key == "fom_version" or key == "exp" or key == "nodes" %}
<td>
{{ row[key] }}
</td>
{% endif %}
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% include "_results_table.html" %}

</body>
</html>
Loading