Repository: fitbenchmarking/fitbenchmarking

Issue:
Update the fitting report template
1. The `Graph 1` and  `Graph 2` can be consolidated into one interactive graph or can be two sub plots stacked horizontally or vertically (if the axis ranges of them are expected to be different).
2. `Table 1` can be removed.
3. The **Number of Parameters** and **Number of Data Points** can be moved to `Table 2`.

Graph 1
<img width="788" alt="Image" src="https://github.com/user-attachments/assets/d8fe00a3-5855-4192-83e6-e599b86cfeb1" />

Graph 2
<img width="778" alt="Image" src="https://github.com/user-attachments/assets/17aab1ad-1caf-46aa-a8ed-35755c33c7b3" />

Table 1 
<img width="845" alt="Image" src="https://github.com/user-attachments/assets/e33d384d-4954-41bd-8fc4-dcffa0c5c234" />

Table 2 
<img width="853" alt="Image" src="https://github.com/user-attachments/assets/cd59af41-5312-442b-9a29-73a9a8cb656c" />

Discussion:
coveralls: 
[![Coverage Status](https://coveralls.io/builds/72796552/badge)](https://coveralls.io/builds/72796552)

coverage: 90.489% (+0.001%) from 90.488%
when pulling **e3396ecf69d80db7449c2c089612c88d4a4a17fc on RabiyaF:1451-update-fitting-report**
into **82716374bf37f0fe0e6f6a5fc5cc14fa7d036976 on fitbenchmarking:master**.


Patch:
diff --git a/docs/source/users/running.rst b/docs/source/users/running.rst
index ed6f6530..ac9f7096 100644
--- a/docs/source/users/running.rst
+++ b/docs/source/users/running.rst
@@ -111,7 +111,7 @@ They can be changed using the arguments in the table below.
        | to create plots during runtime.
    * - *Don't make plots*
      -
-     - ``--don't_make_plots``
+     - ``--dont_make_plots``
      - | Use this option if you have decided
        | not to create plots during runtime.
    * - *Open results browser*
diff --git a/fitbenchmarking/results_processing/fitting_report.py b/fitbenchmarking/results_processing/fitting_report.py
index ac0c135f..28d11907 100644
--- a/fitbenchmarking/results_processing/fitting_report.py
+++ b/fitbenchmarking/results_processing/fitting_report.py
@@ -55,21 +55,18 @@ def create_prob_group(result, support_pages_dir, options):
     file_path = os.path.join(support_pages_dir, file_name)
 
     # Bool for print message/insert image
-    fit_success = init_success = pdf_success = options.make_plots
+    fit_success = pdf_success = options.make_plots
 
     if options.make_plots:
-        fig_fit, fig_start, fig_pdf = get_figure_paths(result)
+        fig_fit, fig_pdf = get_figure_paths(result)
         fit_success = fig_fit != ""
-        init_success = fig_start != ""
         pdf_success = fig_pdf != ""
         if not fit_success:
             fig_fit = result.figure_error
-        if not init_success:
-            fig_start = result.figure_error
         if not pdf_success:
             fig_pdf = result.figure_error
     else:
-        fig_fit = fig_start = fig_pdf = (
+        fig_fit = fig_pdf = (
             "Re-run with make_plots set to yes "
             "in the ini file to generate plots."
         )
@@ -114,8 +111,6 @@ def create_prob_group(result, support_pages_dir, options):
                 runtime=f"{result.runtime:.4g}",
                 energy=energy_disp,
                 is_best_fit=result.is_best_fit,
-                initial_plot_available=init_success,
-                initial_plot=fig_start,
                 min_params=result.fin_function_params,
                 fitted_plot_available=fit_success,
                 fitted_plot=fig_fit,
@@ -140,13 +135,12 @@ def get_figure_paths(result):
     :type result: fitbenchmarking.utils.fitbm_result.FittingProblem
 
     :return: the paths to the required figures
-    :rtype: tuple(str, str, str)
+    :rtype: tuple(str, str)
     """
     return tuple(
         os.path.join("figures", link) if link else ""
         for link in [
             result.figure_link,
-            result.start_figure_link,
             result.posterior_plots,
         ]
     )
diff --git a/fitbenchmarking/results_processing/plots.py b/fitbenchmarking/results_processing/plots.py
index 8a0369fd..791c82ff 100644
--- a/fitbenchmarking/results_processing/plots.py
+++ b/fitbenchmarking/results_processing/plots.py
@@ -31,6 +31,7 @@ class Plot:
     }
     _summary_best_plot_line = {"width": 2}
     _summary_plot_line = {"width": 1, "dash": "dash"}
+    _starting_guess_plot_line = {"width": 1, "color": "#5F8575"}
     _subplots_line = {"width": 1, "color": "red"}
     _error_dict = {
         "type": "data",
@@ -131,20 +132,20 @@ class Plot:
                         row=1,
                         col=i + 1,
                     )
-            else:  # plot starting guess if minimizer not provided
-                fig.add_trace(
-                    go.Scatter(
-                        x=self.result.spinw_plot_info["ebin_cens"],
-                        y=df["y"][df["minimizer"] == "Starting Guess"][
-                            (data_len * i) : (data_len * (i + 1))
-                        ],
-                        name="Starting Guess",
-                        line=self._subplots_line,
-                        showlegend=i == 0,
-                    ),
-                    row=1,
-                    col=i + 1,
-                )
+            # plot starting guess if minimizer not provided
+            fig.add_trace(
+                go.Scatter(
+                    x=self.result.spinw_plot_info["ebin_cens"],
+                    y=df["y"][df["minimizer"] == "Starting Guess"][
+                        (data_len * i) : (data_len * (i + 1))
+                    ],
+                    name="Starting Guess",
+                    line=self._starting_guess_plot_line,
+                    showlegend=i == 0,
+                ),
+                row=1,
+                col=i + 1,
+            )
             # plot data in both cases
             fig.add_trace(
                 go.Scatter(
@@ -249,6 +250,8 @@ class Plot:
         y_best = df["y"][df["best"]]
         x_data = df["x"][df["minimizer"] == "Data"]
         y_data = df["y"][df["minimizer"] == "Data"]
+        x_start = df[df["minimizer"] == "Starting Guess"]["x"]
+        y_start = df[df["minimizer"] == "Starting Guess"]["y"]
         self._error_dict["array"] = df["e"][df["minimizer"] == "Data"]
 
         for minimizer in df["minimizer"].unique():
@@ -277,6 +280,15 @@ class Plot:
                             )
                         )
                     # add the raw data as a scatter plot
+                    fig.add_trace(
+                        go.Scatter(
+                            x=x_start,
+                            y=y_start,
+                            mode="lines+markers",
+                            name="Starting Guess",
+                            line=self._starting_guess_plot_line,
+                        )
+                    )
                     fig.add_trace(
                         go.Scatter(
                             x=x_data,
diff --git a/fitbenchmarking/templates/fitting_report_template.html b/fitbenchmarking/templates/fitting_report_template.html
index e666d047..a43e4006 100644
--- a/fitbenchmarking/templates/fitting_report_template.html
+++ b/fitbenchmarking/templates/fitting_report_template.html
@@ -17,72 +17,6 @@
                 </div>
                 <p>{{ description }}</p>
                 <hr>
-                <section class="accordion">
-                    <input type="checkbox" name="collapse" id="start-accordion" checked="checked">
-                    <h2 class="handle">
-                        <label for="start-accordion">Problem Outline</label>
-                    </h2>
-                    <br>
-                    <div class="content">
-                        <table class="simple-table">
-                            <colgroup>
-                                <col width="30%" />
-                                <col width="70%" />
-                            </colgroup>
-                            <tbody valign="top">
-                                <!-- Summary Section -->
-                                <tr>
-                                    <td class="heading" colspan="2">Summary</td>
-                                </tr>
-                                <tr>
-                                    <td class="label">Number of parameters</td>
-                                    <td>{{ n_params }}</td>
-                                </tr>
-                                <tr>
-                                    <td class="label">Number of data points</td>
-                                    <td>{{ n_data_points }}</td>
-                                </tr>
-                                <!-- Functions Section -->
-                                <tr>
-                                    <td class="heading" colspan="2">Function</td>
-                                </tr>
-                                <tr>
-                                    <td class="label">Form</td>
-                                    <td>{{ equation }}</td>
-                                </tr>
-                                <!-- Initial Parameters Section -->
-                                <tr>
-                                    <td class="heading" colspan="2">Initial Parameters</td>
-                                </tr>
-                                {% if list_params %}
-                                    {% for param in initial_guess.split(", ") %}
-                                        {% set key, value = param.split("=") %}
-                                        <tr>
-                                            <td class="label">{{ key.strip() }}</td>
-                                            <td>{{ value.strip() }}</td>
-                                        </tr>
-                                    {% endfor %}
-                                {% else %}
-                                <tr>
-                                    <td colspan="2">Too many parameters to display</td>
-                                </tr>
-                                {% endif %}
-                            </tbody>
-                        </table>
-                        <div class="row">
-                            {% if initial_plot_available %}
-                                <iframe width="100%" height="500" frameborder="0" seamless="seamless" scrolling="no" src="{{ initial_plot }}"></iframe>
-                            {% else %}
-                                <h3>{{ initial_plot }}</h3>
-                            {% endif %}
-                        </div>
-                    </div>
-                </section>
-            <section class="accordion">
-                <input type="checkbox" name="collapse" id="fitting-results-accordion" checked="checked">
-                <h2 class="handle">
-                    <label for="fitting-results-accordion">Fitting Results</label>
-                </h2>
                 <div class="content">
                     {% if is_best_fit %}
                         <p>This is the <span class="pill">best</span> fit of the minimizers used.</p>
@@ -96,14 +30,26 @@
                             <col width="25%" />
                         </colgroup>
                         <tbody valign="top">
-                            <!-- Summary Section -->
+                            <!-- Problem Outline Section -->
+                            <tr>
+                                <td class="heading" colspan="4">Problem Outline</td>
+                            </tr>
+                            <tr>
+                                <td class="label">Number of parameters</td>
+                                <td colspan="3">{{ n_params }}</td>
+                            </tr>
                             <tr>
-                                <td class="heading" colspan="4">Summary</td>
+                                <td class="label">Number of data points</td>
+                                <td colspan="3">{{ n_data_points }}</td>
                             </tr>
                             <tr>
                                 <td class="label">Minimizer</td>
                                 <td colspan="3">{{ minimizer }}</td>
                             </tr>
+                            <!-- Summary Section -->
+                            <tr>
+                                <td class="heading" colspan="4">Fitting Metrics</td>
+                            </tr>
                             <tr>
                                 <td class="label">Accuracy</td>
                                 <td colspan="3">{{ accuracy }}</td>
@@ -186,7 +132,6 @@
                         </button>
                     </div>
                 </div>
-            </section>
         </div>
     </body>
 </html>


Test patch:
diff --git a/fitbenchmarking/results_processing/tests/test_fitting_report.py b/fitbenchmarking/results_processing/tests/test_fitting_report.py
index eb84964f..32b511c9 100644
--- a/fitbenchmarking/results_processing/tests/test_fitting_report.py
+++ b/fitbenchmarking/results_processing/tests/test_fitting_report.py
@@ -7,6 +7,8 @@ import os
 import unittest
 from tempfile import TemporaryDirectory
 
+from parameterized import parameterized
+
 import fitbenchmarking
 from fitbenchmarking import test_files
 from fitbenchmarking.results_processing import fitting_report
@@ -110,35 +112,32 @@ class GetFigurePathsTests(unittest.TestCase):
         self.options = Options()
         self.result = load_mock_results()[0]
 
-    def test_with_links(self):
+    @parameterized.expand(
+        [
+            ("", ""),
+            ("some_link", "other_link"),
+        ]
+    )
+    def test_with_links(self, f_link, p_link):
         """
-        Tests that the returned links are correct when links are passed in.
+        Tests the output of get_figure_paths.
         """
-        self.result.figure_link = "some_link"
-        self.result.start_figure_link = "other_link"
-        self.result.posterior_plots = "another_link"
-        figure_link, start_link, posterior_link = (
-            fitting_report.get_figure_paths(self.result)
-        )
-        self.assertEqual(figure_link, os.path.join("figures", "some_link"))
-        self.assertEqual(start_link, os.path.join("figures", "other_link"))
-        self.assertEqual(
-            posterior_link, os.path.join("figures", "another_link")
+        self.result.figure_link = f_link
+        self.result.posterior_plots = p_link
+
+        figure_link, posterior_link = fitting_report.get_figure_paths(
+            self.result
         )
 
-    def test_no_links(self):
-        """
-        Tests that links are not changed if an empty string is given.
-        """
-        self.result.figure_link = ""
-        self.result.start_figure_link = ""
-        self.result.posterior_plots = ""
-        figure_link, start_link, posterior_link = (
-            fitting_report.get_figure_paths(self.result)
+        expected_figure_link = (
+            os.path.join("figures", f_link) if f_link else ""
+        )
+        expected_posterior_link = (
+            os.path.join("figures", p_link) if p_link else ""
         )
-        self.assertEqual(figure_link, "")
-        self.assertEqual(start_link, "")
-        self.assertEqual(posterior_link, "")
+
+        self.assertEqual(figure_link, expected_figure_link)
+        self.assertEqual(posterior_link, expected_posterior_link)
 
 
 if __name__ == "__main__":