Repository: locustio/locust

Issue:
Add Modern UI HTML Report to --html Option
### Prerequisites

- [X] I am using [the latest version of Locust](https://github.com/locustio/locust/releases/)
- [X] I am suggesting a new feature, not asking a question

### Description

When using the `--html` option with `--modern-ui`, the modern HTML template should be used


Discussion:

Resolving patch:
diff --git a/locust/html.py b/locust/html.py
--- a/locust/html.py
+++ b/locust/html.py
@@ -10,26 +10,31 @@
 from html import escape
 from json import dumps
 from .runners import MasterRunner, STATE_STOPPED, STATE_STOPPING
-from flask import render_template as flask_render_template
 
 
 PERCENTILES_FOR_HTML_REPORT = [0.50, 0.6, 0.7, 0.8, 0.9, 0.95, 0.99, 1.0]
 
 
-def render_template(file, **kwargs):
-    templates_path = os.path.join(pathlib.Path(__file__).parent.absolute(), "templates")
-    env = Environment(loader=FileSystemLoader(templates_path), extensions=["jinja2.ext.do"])
+def render_template(file, template_path, **kwargs):
+    env = Environment(loader=FileSystemLoader(template_path), extensions=["jinja2.ext.do"])
     template = env.get_template(file)
     return template.render(**kwargs)
 
 
 def get_html_report(
     environment,
-    static_path=os.path.join(os.path.dirname(__file__), "static"),
     show_download_link=True,
     use_modern_ui=False,
     theme="",
 ):
+    root_path = os.path.dirname(os.path.abspath(__file__))
+    if use_modern_ui:
+        static_path = os.path.join(root_path, "webui", "dist", "assets")
+        template_path = os.path.join(root_path, "webui", "dist")
+    else:
+        static_path = os.path.join(root_path, "static")
+        template_path = os.path.join(root_path, "templates")
+
     stats = environment.runner.stats
 
     start_ts = stats.start_time
@@ -94,8 +99,9 @@ def get_html_report(
     }
 
     if use_modern_ui:
-        res = flask_render_template(
+        res = render_template(
             "report.html",
+            template_path,
             template_args={
                 "is_report": True,
                 "requests_statistics": [stat.to_dict(escape_string_values=True) for stat in requests_statistics],
@@ -128,6 +134,7 @@ def get_html_report(
     else:
         res = render_template(
             "report.html",
+            template_path,
             int=int,
             round=round,
             escape=escape,
diff --git a/locust/main.py b/locust/main.py
--- a/locust/main.py
+++ b/locust/main.py
@@ -495,8 +495,8 @@ def sig_term_handler():
         logger.info("Got SIGTERM signal")
         shutdown()
 
-    def save_html_report():
-        html_report = get_html_report(environment, show_download_link=False)
+    def save_html_report(use_modern_ui=False):
+        html_report = get_html_report(environment, show_download_link=False, use_modern_ui=use_modern_ui)
         logger.info("writing html report to file: %s", options.html_file)
         with open(options.html_file, "w", encoding="utf-8") as file:
             file.write(html_report)
@@ -512,10 +512,10 @@ def save_html_report():
 
         main_greenlet.join()
         if options.html_file:
-            save_html_report()
+            save_html_report(options.modern_ui)
     except KeyboardInterrupt:
         if options.html_file:
-            save_html_report()
+            save_html_report(options.modern_ui)
     except Exception:
         raise
     shutdown()
diff --git a/locust/web.py b/locust/web.py
--- a/locust/web.py
+++ b/locust/web.py
@@ -110,9 +110,9 @@ def __init__(
         self.app = app
         app.jinja_env.add_extension("jinja2.ext.do")
         app.debug = True
-        self.root_path = os.path.dirname(os.path.abspath(__file__))
-        app.root_path = self.root_path
-        self.webui_build_path = f"{self.root_path}/webui/dist"
+        root_path = os.path.dirname(os.path.abspath(__file__))
+        app.root_path = root_path
+        self.webui_build_path = os.path.join(root_path, "webui", "dist")
         self.app.config["BASIC_AUTH_ENABLED"] = False
         self.auth: Optional[BasicAuth] = None
         self.greenlet: Optional[gevent.Greenlet] = None
@@ -144,9 +144,7 @@ def handle_exception(error):
 
         @app.route("/assets/<path:path>")
         def send_assets(path):
-            webui_build_path = self.webui_build_path
-
-            return send_from_directory(f"{webui_build_path}/assets", path)
+            return send_from_directory(os.path.join(self.webui_build_path, "assets"), path)
 
         @app.route("/")
         @self.auth_required_if_enabled
@@ -287,16 +285,9 @@ def reset_stats() -> str:
         @app.route("/stats/report")
         @self.auth_required_if_enabled
         def stats_report() -> Response:
-            if self.modern_ui:
-                self.set_static_modern_ui()
-                static_path = f"{self.webui_build_path}/assets"
-            else:
-                static_path = f"{self.root_path}/static"
-
             theme = request.args.get("theme", "")
             res = get_html_report(
                 self.environment,
-                static_path=static_path,
                 show_download_link=not request.args.get("download"),
                 use_modern_ui=self.modern_ui,
                 theme=theme,
@@ -542,7 +533,7 @@ def wrapper(*args, **kwargs):
 
     def set_static_modern_ui(self):
         self.app.template_folder = self.webui_build_path
-        self.app.static_folder = f"{self.webui_build_path}/assets/"
+        self.app.static_folder = os.path.join(self.webui_build_path, "assets")
         self.app.static_url_path = "/assets/"
 
     def update_template_args(self):


Test patch: