From fda7315f43b02cfae3050ece68a4d7cb26a826b4 Mon Sep 17 00:00:00 2001 From: toufic ar Date: Fri, 15 May 2026 02:24:12 +0300 Subject: restructure webUI --- web/__init__.py | 166 +++++++++++++++++++++++++++++++++++++++++++++ web/favicon.ico | Bin 0 -> 302430 bytes web/templates/base.html | 74 ++++++++++++++++++++ web/templates/home.html | 66 ++++++++++++++++++ web/templates/project.html | 119 ++++++++++++++++++++++++++++++++ web/templates/run.html | 19 ++++++ 6 files changed, 444 insertions(+) create mode 100644 web/__init__.py create mode 100644 web/favicon.ico create mode 100644 web/templates/base.html create mode 100644 web/templates/home.html create mode 100644 web/templates/project.html create mode 100644 web/templates/run.html (limited to 'web') 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("/", 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("//", 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 diff --git a/web/favicon.ico b/web/favicon.ico new file mode 100644 index 0000000..a4c56d6 Binary files /dev/null and b/web/favicon.ico differ diff --git a/web/templates/base.html b/web/templates/base.html new file mode 100644 index 0000000..81d6a59 --- /dev/null +++ b/web/templates/base.html @@ -0,0 +1,74 @@ + + + + {% block head %} + + makeshiftci{% block title %}{% endblock %} + + {% endblock %} + + {% block extrastyle %}{% endblock %} + + +
+
+

makeshiftci{% block path %}{% endblock %}

+ {% block headingcontent %}{% endblock %} +
+ {% block content %}{% endblock %} +
+ + + diff --git a/web/templates/home.html b/web/templates/home.html new file mode 100644 index 0000000..20ef75a --- /dev/null +++ b/web/templates/home.html @@ -0,0 +1,66 @@ +{% extends "base.html" %} {% block extrastyle %} + +{% endblock %} {% block content %} +
+ {% for project in projects %} + + {% endfor %} +
+{% endblock %} + + diff --git a/web/templates/project.html b/web/templates/project.html new file mode 100644 index 0000000..9b0aa58 --- /dev/null +++ b/web/templates/project.html @@ -0,0 +1,119 @@ +{% extends "base.html" %} {% block title %}/{{ project_path }}{% endblock %} {% +block path %}/{{ project_path }}{% endblock %} +{% block extrastyle %} + +{% endblock %} {% block headingcontent %} +

{{ project.name }}

+

{{ project.url }}{{ project.cron }}

+{% endblock %} {% block content %} + +
+ {% for run in runlist %} +
+

run + #{{ run.number }}{{ run.date }}

+
+

+
+ {% endfor %} +
+{% endblock %} + + diff --git a/web/templates/run.html b/web/templates/run.html new file mode 100644 index 0000000..77f9f90 --- /dev/null +++ b/web/templates/run.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} {% block title %}/{{ project_path }}/{{ run_number +}}{% endblock %} {% block path %}/{{ project_path }}/{{ run_number }}{% endblock +%} {% block content %} +

+
+{% for n in run %}
+
+{% endfor %} {% endblock %}
+
+
-- 
cgit v1.2.3