Repository: fitbenchmarking/fitbenchmarking

Issue:
Fix function that writes plots to html and introduce test for it
The function for writing plotly plots to html files ("write_html_with_link_plotlyjs") is not working. It needs to be fix so that the internal function 'write_html' receives the correct plotly.js path.

Also a test should be introduced, that checks the size of the html file produced by the function .


Discussion:
github-actions[bot]: ## Unit & System Test Results
    4 files  ±0      4 suites  ±0   25m 31s [:stopwatch:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v2.11.0/README.md#the-symbols "duration of all tests") +16s
446 tests +1  438 [:heavy_check_mark:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v2.11.0/README.md#the-symbols "passed tests") +1    8 [:zzz:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v2.11.0/README.md#the-symbols "skipped / disabled tests") ±0  0 [:x:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v2.11.0/README.md#the-symbols "failed tests") ±0 
860 runs  +2  805 [:heavy_check_mark:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v2.11.0/README.md#the-symbols "passed tests") +2  55 [:zzz:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v2.11.0/README.md#the-symbols "skipped / disabled tests") ±0  0 [:x:](https://github.com/EnricoMi/publish-unit-test-result-action/blob/v2.11.0/README.md#the-symbols "failed tests") ±0 

Results for commit 9e114832. ± Comparison against base commit e349416a.

[test-results]:data:application/gzip;base64,H4sIADTCZWUC/03MSw6DIBSF4a0Yxh1wLw9pN9MQhIRUpeExMt170RZ0+H8nORtxfraJPAZ+G0gqPveYStTZh7UmCAZV6paPlctWz1SM2Ympk17+XekEp/1cgXawMYb4l1jW/VPJFu1SUdHl9yg6XB6Pvh6asCw+1yB3C8AVQxASNWopkavRgQFH6Yh8Qs0oV2jJ5wvYEYlQBwEAAA==


Patch:
diff --git a/fitbenchmarking/core/results_output.py b/fitbenchmarking/core/results_output.py
index cae709b4..ece8d42c 100644
--- a/fitbenchmarking/core/results_output.py
+++ b/fitbenchmarking/core/results_output.py
@@ -62,10 +62,10 @@ def save_results(options, results, group_name, failed_problems,
     best_results, results_dict = preprocess_data(results)
 
     pp_locations = performance_profiler.profile(results_dict, fig_dir,
-                                                supp_dir, options)
+                                                options)
 
     if options.make_plots:
-        create_plots(options, results_dict, best_results, fig_dir, supp_dir)
+        create_plots(options, results_dict, best_results, fig_dir)
 
     fitting_report.create(options=options,
                           results=results,
@@ -550,8 +550,7 @@ def _find_matching_tags(tag: 'str', lst: 'List[str]'):
             if re.fullmatch(tag, match)]
 
 
-def create_plots(options, results, best_results, figures_dir,
-                 support_pages_dir):
+def create_plots(options, results, best_results, figures_dir):
     """
     Create a plot for each result and store in the figures directory
 
@@ -567,8 +566,6 @@ def create_plots(options, results, best_results, figures_dir,
 
     :param figures_dir: Path to directory to store the figures in
     :type figures_dir: str
-    :param support_pages_dir: The support_pages directory
-    :type support_pages_dir: str
     """
     for best_dict, prob_result in zip(best_results.values(), results.values()):
         plot_dict = {}
@@ -612,8 +609,7 @@ def create_plots(options, results, best_results, figures_dir,
             try:
                 plot = plots.Plot(best_result=best_dict[cf],
                                   options=options,
-                                  figures_dir=figures_dir,
-                                  support_pages_dir=support_pages_dir)
+                                  figures_dir=figures_dir)
             except PlottingError as e:
                 for result in prob_result[cf]:
                     result.figure_error = str(e)
diff --git a/fitbenchmarking/results_processing/performance_profiler.py b/fitbenchmarking/results_processing/performance_profiler.py
index 1c4b1209..f0e4b497 100644
--- a/fitbenchmarking/results_processing/performance_profiler.py
+++ b/fitbenchmarking/results_processing/performance_profiler.py
@@ -8,7 +8,7 @@ import plotly.graph_objects as go
 from fitbenchmarking.results_processing.plots import Plot
 
 
-def profile(results, fig_dir, supp_dir, options):
+def profile(results, fig_dir, options):
     """
     Function that generates profiler plots
 
@@ -16,8 +16,6 @@ def profile(results, fig_dir, supp_dir, options):
     :type results: dict[str, dict[str, list[utils.fitbm_result.FittingResult]]]
     :param fig_dir: path to directory containing the figures
     :type fig_dir: str
-    :param supp_dir: path to the support_pages directory
-    :type supp_dir: str
     :param options: The options for the run
     :type options: utils.options.Options
 
@@ -25,8 +23,7 @@ def profile(results, fig_dir, supp_dir, options):
     :rtype: tuple(str, str)
     """
     acc_bound, runtime_bound = prepare_profile_data(results)
-    plot_path = plot(acc_bound, runtime_bound, fig_dir,
-                     supp_dir, options)
+    plot_path = plot(acc_bound, runtime_bound, fig_dir, options)
     return plot_path
 
 
@@ -69,7 +66,7 @@ def prepare_profile_data(results):
     return acc_dict, runtime_dict
 
 
-def plot(acc, runtime, fig_dir, supp_dir, options):
+def plot(acc, runtime, fig_dir, options):
     """
     Function that generates profiler plots
 
@@ -79,8 +76,6 @@ def plot(acc, runtime, fig_dir, supp_dir, options):
     :type runtime: dict
     :param fig_dir: path to directory containing the figures
     :type fig_dir: str
-    :param supp_dir: path to the support_pages directory
-    :type supp_dir: str
     :param options: The options for the run
     :type options: utils.options.Options
 
@@ -168,8 +163,7 @@ def plot(acc, runtime, fig_dir, supp_dir, options):
         Plot.write_html_with_link_plotlyjs(fig,
                                            fig_dir,
                                            this_filename_html,
-                                           options,
-                                           supp_dir)
+                                           options)
 
     return figure_path
 
diff --git a/fitbenchmarking/results_processing/plots.py b/fitbenchmarking/results_processing/plots.py
index 9aeb5f76..a2350fa8 100644
--- a/fitbenchmarking/results_processing/plots.py
+++ b/fitbenchmarking/results_processing/plots.py
@@ -30,7 +30,7 @@ class Plot:
     summary_best_plot_line = {"width": 2}
     summary_plot_line = {"width": 1}
 
-    def __init__(self, best_result, options, figures_dir, support_pages_dir):
+    def __init__(self, best_result, options, figures_dir):
         self.result = best_result
 
         self.plots_failed = True
@@ -45,11 +45,9 @@ class Plot:
         self.options = options
 
         self.figures_dir = figures_dir
-        self.support_pages_dir = support_pages_dir
 
     @staticmethod
-    def write_html_with_link_plotlyjs(fig, figures_dir, htmlfile, options,
-                                      support_pages_dir):
+    def write_html_with_link_plotlyjs(fig, figures_dir, htmlfile, options):
         """
         Writes an html file for the figure passed as input and
         includes link to the relevant plotly.js file.
@@ -62,12 +60,10 @@ class Plot:
         :type htmlfile: str
         :param options: The options for the run
         :type options: utils.options.Options
-        :param support_pages_dir: The support_pages directory
-        :type support_pages_dir: str
 
         :return: None
         """
-        plotly_path = get_js(options, support_pages_dir)
+        plotly_path = get_js(options, figures_dir).get('plotly')
         html_file_name = os.path.join(figures_dir, htmlfile)
         fig.write_html(html_file_name, include_plotlyjs=plotly_path)
 
@@ -108,8 +104,7 @@ class Plot:
         self.write_html_with_link_plotlyjs(fig,
                                            self.figures_dir,
                                            htmlfile,
-                                           self.options,
-                                           self.support_pages_dir)
+                                           self.options)
         return htmlfile
 
     @staticmethod
@@ -184,8 +179,7 @@ class Plot:
                 self.write_html_with_link_plotlyjs(fig,
                                                    self.figures_dir,
                                                    htmlfile,
-                                                   self.options,
-                                                   self.support_pages_dir)
+                                                   self.options)
 
                 htmlfiles[minimizer] = htmlfile
 
@@ -226,13 +220,11 @@ class Plot:
         self.write_html_with_link_plotlyjs(fig,
                                            self.figures_dir,
                                            html_fname,
-                                           self.options,
-                                           self.support_pages_dir)
+                                           self.options)
         return html_fname
 
     @classmethod
-    def plot_summary(cls, categories, title, options, figures_dir,
-                     supp_pages):
+    def plot_summary(cls, categories, title, options, figures_dir):
         """
         Create a comparison plot showing all fits from the results with the
         best for each category highlighted.
@@ -245,8 +237,6 @@ class Plot:
         :type options: utils.options.Options
         :param figures_dir: The directory to save the figures in
         :type figures_dir: str
-        :param supp_pages: Pat to the support_pages directory
-        :type supp_pages: str
 
         :return: The path to the new plot
         :rtype: str
@@ -319,7 +309,6 @@ class Plot:
         cls.write_html_with_link_plotlyjs(plotlyfig,
                                           figures_dir,
                                           html_fname,
-                                          options,
-                                          supp_pages)
+                                          options)
 
         return html_fname
diff --git a/fitbenchmarking/results_processing/problem_summary_page.py b/fitbenchmarking/results_processing/problem_summary_page.py
index 33abe027..45ebdcdf 100644
--- a/fitbenchmarking/results_processing/problem_summary_page.py
+++ b/fitbenchmarking/results_processing/problem_summary_page.py
@@ -40,8 +40,7 @@ def create(results, best_results, support_pages_dir, figures_dir, options):
             summary_plot_path = Plot.plot_summary(categories=problem_results,
                                                   title=categorised[0][1].name,
                                                   options=options,
-                                                  figures_dir=figures_dir,
-                                                  supp_pages=support_pages_dir)
+                                                  figures_dir=figures_dir)
 
         _create_summary_page(categorised_best_results=categorised,
                              summary_plot_path=summary_plot_path,


Test patch:
diff --git a/fitbenchmarking/core/tests/test_results_output.py b/fitbenchmarking/core/tests/test_results_output.py
index 9c1ca51b..784fb54f 100644
--- a/fitbenchmarking/core/tests/test_results_output.py
+++ b/fitbenchmarking/core/tests/test_results_output.py
@@ -168,7 +168,6 @@ class CreatePlotsTests(unittest.TestCase):
         """
         with TemporaryDirectory() as directory:
             self.results_dir = os.path.join(directory, 'figures_dir')
-            self.supp_dir = directory
 
             results, self.options = load_mock_results(
                 {'results_dir': self.results_dir})
@@ -200,7 +199,7 @@ class CreatePlotsTests(unittest.TestCase):
         # Return the above created `plot_instance`
         plot_mock.return_value = plot_instance
         create_plots(self.options, self.results,
-                     self.best_results, self.results_dir, self.supp_dir)
+                     self.best_results, self.results_dir)
         for problem_key in self.results.keys():
             best_in_prob = self.best_results[problem_key]
             results_in_prob = self.results[problem_key]
@@ -245,7 +244,7 @@ class CreatePlotsTests(unittest.TestCase):
         # Return the above created `plot_instance`
         plot_mock.return_value = plot_instance
         create_plots(self.options, self.results,
-                     self.best_results, self.results_dir, self.supp_dir)
+                     self.best_results, self.results_dir)
 
         for problem_key in self.results.keys():
             best_in_prob = self.best_results[problem_key]
@@ -282,7 +281,7 @@ class CreatePlotsTests(unittest.TestCase):
                 side_effect=PlottingError('Faked plot')):
 
             create_plots(self.options, self.results,
-                         self.best_results, self.results_dir, self.supp_dir)
+                         self.best_results, self.results_dir)
 
         expected = 'An error occurred during plotting.\nDetails: Faked plot'
 
diff --git a/fitbenchmarking/results_processing/tests/test_performance_profiler.py b/fitbenchmarking/results_processing/tests/test_performance_profiler.py
index 4d5e10fb..646b0fab 100644
--- a/fitbenchmarking/results_processing/tests/test_performance_profiler.py
+++ b/fitbenchmarking/results_processing/tests/test_performance_profiler.py
@@ -103,11 +103,9 @@ class PerformanceProfilerTests(unittest.TestCase):
         """
         Test that the performance profiler returns the expected paths
         """
-        supp_dir = os.path.join(self.fig_dir, 'support_pages')
         options = Options()
         acc, runtime = performance_profiler.profile(self.results,
                                                     self.fig_dir,
-                                                    supp_dir,
                                                     options)
 
         assert acc == "acc_profile.html"
diff --git a/fitbenchmarking/results_processing/tests/test_plots.py b/fitbenchmarking/results_processing/tests/test_plots.py
index 916c71bc..acfcf69c 100644
--- a/fitbenchmarking/results_processing/tests/test_plots.py
+++ b/fitbenchmarking/results_processing/tests/test_plots.py
@@ -8,12 +8,15 @@ import unittest
 from tempfile import TemporaryDirectory
 
 import pandas as pd
+import plotly.graph_objects as go
 
 from fitbenchmarking import test_files
 from fitbenchmarking.results_processing import plots
 from fitbenchmarking.utils.checkpoint import Checkpoint
 from fitbenchmarking.utils.exceptions import PlottingError
 from fitbenchmarking.utils.options import Options
+from fitbenchmarking.core.results_output import \
+    create_directories, create_index_page
 
 
 def load_mock_result():
@@ -52,17 +55,24 @@ class PlotTests(unittest.TestCase):
 
         self.opts = Options()
         self.opts.use_errors = True
-        # pylint: disable=consider-using-with
-        self.dir = TemporaryDirectory(dir=self.opts.results_dir)
-        self.dir2 = TemporaryDirectory(dir=self.opts.results_dir)
 
+        # pylint: disable=consider-using-with
+        self._dir = TemporaryDirectory()
+        self.opts.results_dir = self._dir.name
         # pylint: enable=consider-using-with
+
+        _, _, self.figures_dir = create_directories(options=self.opts,
+                                                    group_name='NIST_low')
+
+        # This copies the js directory, and therefore plotly.js, into the
+        # results directory. This is needed as plotly.js is used in a test.
+        create_index_page(self.opts, ['NIST_low'], self.figures_dir)
+
         best = [r for r in self.fr['Fake_Test_Data']
                 if r.is_best_fit and r.problem_tag == 'prob_1'][0]
         self.plot = plots.Plot(best_result=best,
                                options=self.opts,
-                               figures_dir=self.dir.name,
-                               support_pages_dir=self.dir2.name)
+                               figures_dir=self.figures_dir)
 
         self.df = {}
         # Create a dataframe for each row
@@ -104,7 +114,7 @@ class PlotTests(unittest.TestCase):
             self.df[('Fake_Test_Data', 'prob_1')])
 
         self.assertEqual(file_name, 'start_for_prob_1.html')
-        path = os.path.join(self.dir.name, file_name)
+        path = os.path.join(self.figures_dir, file_name)
         self.assertTrue(os.path.exists(path))
 
     def test_best_filename_return(self):
@@ -124,7 +134,7 @@ class PlotTests(unittest.TestCase):
 
         self.assertEqual(file_names['m10_[s1]_jj0'],
                          'm10_[s1]_jj0_fit_for_cf1_prob_1.html')
-        path = os.path.join(self.dir.name, file_names['m10_[s1]_jj0'])
+        path = os.path.join(self.figures_dir, file_names['m10_[s1]_jj0'])
         self.assertTrue(os.path.exists(path))
 
     def test_multivariate_plot(self):
@@ -136,8 +146,23 @@ class PlotTests(unittest.TestCase):
         with self.assertRaises(PlottingError):
             self.plot = plots.Plot(best_result=self.fr['Fake_Test_Data'][0],
                                    options=self.opts,
-                                   figures_dir=self.dir.name,
-                                   support_pages_dir=self.dir2.name)
+                                   figures_dir=self.figures_dir)
+
+    def test_write_html_with_link_plotlyjs(self):
+        """
+        Test that the function is producing output files smaller than 50KB.
+        """
+        fig = go.Figure()
+        htmlfile_name = 'figure.html'
+        self.plot.write_html_with_link_plotlyjs(fig=fig,
+                                                figures_dir=self.figures_dir,
+                                                htmlfile=htmlfile_name,
+                                                options=self.opts)
+
+        htmlfile_path = os.path.join(self.figures_dir, htmlfile_name)
+        file_size_KB = os.path.getsize(htmlfile_path)/1000
+
+        assert file_size_KB < 50
 
 
 if __name__ == "__main__":