aboutsummaryrefslogtreecommitdiff
path: root/web/__init__.py
diff options
context:
space:
mode:
authortoufic ar <contact@toufy.me>2026-05-15 02:24:12 +0300
committertoufic ar <contact@toufy.me>2026-05-15 02:24:12 +0300
commitfda7315f43b02cfae3050ece68a4d7cb26a826b4 (patch)
treedef28890a3c92cd7c2cb53d01f8e76c74fe362e7 /web/__init__.py
parentb36868f1fc3a6df0da4d931f94f3c39f1c50ee59 (diff)
downloadmakeshiftci-fda7315f43b02cfae3050ece68a4d7cb26a826b4.tar.gz
makeshiftci-fda7315f43b02cfae3050ece68a4d7cb26a826b4.zip
restructure webUI
Diffstat (limited to 'web/__init__.py')
-rw-r--r--web/__init__.py166
1 files changed, 166 insertions, 0 deletions
diff --git a/web/__init__.py b/web/__init__.py
new file mode 100644
index 0000000..3daef78
--- /dev/null
+++ b/web/__init__.py
@@ -0,0 +1,166 @@
+import json
+import os
+import queue
+import re
+import threading
+import time
+from typing import Any
+
+from flask import (
+ Flask,
+ abort,
+ send_from_directory,
+ stream_template,
+)
+
+MSCI_HOME = os.environ.get("MSCI_HOME")
+
+
+def get_project(project: str) -> dict[str, Any]:
+ with open(f"{MSCI_HOME}/projects/{project}.json", "r") as p:
+ return json.loads(p.read())
+
+
+def get_run(project: str, run: int) -> str:
+ with open(f"{MSCI_HOME}/stdout/public/{project}/{run}", "r") as r:
+ return r.read()
+
+
+def stream_run(project: str, run: int):
+ while True:
+ with open(f"{MSCI_HOME}/stdout/public/{project}/{run}", "r") as r:
+ txt = r.read()
+ yield txt
+ if "--MSCI_EXIT_" in txt:
+ break
+ time.sleep(0.5)
+
+
+def _projects(q: queue.Queue[tuple[str, dict[str, Any]] | None]):
+ if os.path.isdir(f"{MSCI_HOME}/projects"):
+ _projects = os.listdir(f"{MSCI_HOME}/projects")
+ for _p in _projects:
+ loaded = get_project(_p.replace(".json", ""))
+ if not loaded["hidden"]:
+ q.put((_p.replace(".json", ""), loaded))
+ else:
+ os.mkdir(f"{MSCI_HOME}/projects")
+ q.put(None)
+
+
+def projects():
+ q: queue.Queue[tuple[str, dict[str, Any]] | None] = queue.Queue()
+ threading.Thread(target=_projects, args=(q,), daemon=True).start()
+ while True:
+ pr = q.get()
+ if pr is None:
+ break
+ yield pr
+
+
+def _project_runs(project: str, q: queue.Queue[dict[str, Any] | None]):
+ pr_stdout = f"{MSCI_HOME}/stdout/public/{project}"
+ if os.path.isdir(pr_stdout):
+ _runs = sorted(os.listdir(pr_stdout), key=int)
+ for _r in _runs:
+ run_str = get_run(project, int(_r))
+ run_date = re.search(r"--MSCI_DATE\((.*?)\)--", run_str)
+ run_status = (
+ True
+ if "--MSCI_EXIT_SUCCESS--" in run_str
+ else False if "--MSCI_EXIT_FAILURE--" in run_str else None
+ )
+ run_data = {
+ "number": int(_r),
+ "date": run_date.group(1) if run_date else None,
+ "status": run_status,
+ }
+ q.put(run_data)
+ q.put(None)
+
+
+def project_runs(project: str):
+ q: queue.Queue[dict[str, Any] | None] = queue.Queue()
+ threading.Thread(
+ target=_project_runs,
+ args=(
+ project,
+ q,
+ ),
+ daemon=True,
+ ).start()
+ while True:
+ pr = q.get()
+ if pr is None:
+ break
+ yield pr
+
+
+def project_exists(name: str):
+ return (
+ os.path.isfile(f"{MSCI_HOME}/projects/{name}.json")
+ and get_project(name)["hidden"] == False
+ )
+
+
+def run_exists(project: str, run: int):
+ if project_exists(project):
+ if not os.path.isfile(f"{MSCI_HOME}/stdout/public/{project}/{run}"):
+ return False
+ return True
+ return False
+
+
+_app = Flask(__name__)
+_empty = Flask(__name__)
+
+
+@_empty.route("/", methods=["GET"])
+def ey():
+ return "MSCI_HOME not set"
+
+
+@_app.route("/favicon.ico")
+def favicon():
+ return send_from_directory(
+ _app.root_path,
+ "favicon.ico",
+ mimetype="image/vnd.microsoft.icon",
+ )
+
+
+@_app.route("/", methods=["GET"])
+def home():
+ return stream_template("home.html", projects=projects())
+
+
+@_app.route("/<string:project>", methods=["GET"])
+def project(project: str):
+ if project_exists(project):
+ return stream_template(
+ "project.html",
+ project=get_project(project),
+ project_path=project,
+ runlist=project_runs(project),
+ )
+ abort(404)
+
+
+@_app.route("/<string:project>/<int:run>", methods=["GET"])
+def run(project: str, run: int):
+ if run_exists(project, run):
+ return stream_template(
+ "run.html",
+ project=get_project(project),
+ project_path=project,
+ run=stream_run(project, run),
+ run_number=run,
+ )
+ abort(404)
+
+
+def create_app():
+ if not MSCI_HOME:
+ return _empty
+ else:
+ return _app