source: sasview/src/sas/qtgui/Perspectives/Fitting/ReportPageLogic.py @ 57be490

ESS_GUIESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since 57be490 was 57be490, checked in by Piotr Rozyczko <rozyczko@…>, 6 years ago

Merged ESS_GUI_reporting

  • Property mode set to 100755
File size: 6.8 KB
Line 
1# -*- coding: utf-8 -*-
2import base64
3import datetime
4import re
5import logging
6from io import BytesIO
7import urllib.parse
8
9from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
10
11from sas.qtgui.Plotting.Plotter import Plotter
12from sas.qtgui.Plotting.Plotter2D import Plotter2D
13from sas.qtgui.Plotting.PlotterData import Data1D
14from sas.qtgui.Plotting.PlotterData import Data2D
15
16import sas.qtgui.Utilities.GuiUtils as GuiUtils
17from sas.qtgui.Perspectives.Fitting import FittingUtilities
18
19class ReportPageLogic(object):
20    """
21    Logic for the Report Page functionality. Refactored from FittingWidget.
22    """
23    def __init__(self, parent=None, kernel_module=None, data=None, index=None, model=None):
24
25        self.parent = parent
26        self.kernel_module = kernel_module
27        self.data = data
28        self._index = index
29        self.model = model
30
31    @staticmethod
32    def cleanhtml(raw_html):
33        """Remove html tags from a document"""
34        cleanr = re.compile('<.*?>')
35        cleantext = re.sub(cleanr, '', raw_html)
36        return cleantext
37
38    def reportList(self):
39        """
40        Return the HTML version of the full report
41        """
42        if self.kernel_module is None:
43            report_txt = "No model defined"
44            report_html = HEADER % report_txt
45            images = []
46            return [report_html, report_txt, images]
47
48        # Get plot image from plotpanel
49        images = self.getImages()
50
51        imagesHTML = ""
52        if images is not None:
53            imagesHTML = self.buildPlotsForReport(images)
54
55        report_header = self.reportHeader()
56
57        report_parameters = self.reportParams()
58
59        report_html = report_header + report_parameters + imagesHTML
60
61        report_txt = self.cleanhtml(report_html)
62
63        report_list = [report_html, report_txt, images]
64
65        return report_list
66
67    def reportHeader(self):
68        """
69        Look at widget state and extract report header info
70        """
71        report = ""
72
73        title = self.data.name
74        current_time = datetime.datetime.now().strftime("%I:%M%p, %B %d, %Y")
75        filename = self.data.filename
76        modelname = self.kernel_module.id
77        if hasattr(self.data, 'xmin'):
78            qrange_min = self.data.xmin
79            qrange_max = self.data.xmax
80        else:
81            qrange_min = min(self.data.x)
82            qrange_max = max(self.data.x)
83        qrange = "min = {}, max = {}".format(qrange_min, qrange_max)
84
85        title = title + " [" + current_time + "]"
86        title_name = HEADER % title
87        report = title_name
88        report = report + CENTRE % "File name:{}\n".format(filename)
89        report = report + CENTRE % "Model name:{}\n".format(modelname)
90        report = report + CENTRE % "Q Range: {}\n".format(qrange)
91        chi2_repr = GuiUtils.formatNumber(self.parent.chi2, high=True)
92        report = report + CENTRE % "Chi2/Npts:{}\n".format(chi2_repr)
93
94        return report
95
96    def buildPlotsForReport(self, images):
97        """ Convert Matplotlib figure 'fig' into a <img> tag for HTML use using base64 encoding. """
98        html = FEET_1 % self.data.filename
99
100        for fig in images:
101            canvas = FigureCanvas(fig)
102            png_output = BytesIO()
103            canvas.print_png(png_output)
104            data = png_output.getvalue()
105            data64 = base64.b64encode(data)
106            data_to_print = urllib.parse.quote(data64)
107            html += '<img src="data:image/png;base64,{}">'.format(data_to_print)
108
109        return html
110
111    def reportParams(self):
112        """
113        Look at widget state and extract parameters
114        """
115        pars = FittingUtilities.getStandardParam(self.model)
116        if pars is None:
117            return ""
118
119        report = ""
120        plus_minus = " &#177; "
121        for value in pars:
122            try:
123                par_name = value[1]
124                par_fixed = not value[0]
125                par_value = value[2]
126                par_unit = value[7]
127                # Convert units for nice display
128                par_unit = GuiUtils.convertUnitToHTML(par_unit.strip())
129                if par_fixed:
130                    error = " (fixed)"
131                else:
132                    error = plus_minus + str(value[4][1])
133                param = par_name + " = " + par_value + error + " " + par_unit
134            except IndexError as ex:
135                # corrupted model. Complain and skip the line
136                logging.error("Error in parsing parameters: "+str(ex))
137                continue
138            report += CENTRE % param + "\n"
139
140        return report
141
142    def getImages(self):
143        """
144        Create MPL figures for the current fit
145        """
146        graphs = []
147        modelname = self.kernel_module.name
148        if not modelname or self._index is None:
149            return None
150        plots = GuiUtils.plotsFromModel(modelname, self._index)
151        # Call show on requested plots
152        # All same-type charts in one plot
153        for plot_set in plots:
154            if isinstance(plot_set, Data1D):
155                if 'residuals' in plot_set.title.lower():
156                    res_plot = Plotter(self, quickplot=True)
157                    res_plot.plot(plot_set)
158                    graphs.append(res_plot.figure)
159                    continue
160                if not 'new_plot' in locals():
161                    new_plot = Plotter(self, quickplot=True)
162                new_plot.plot(plot_set)
163            elif isinstance(plot_set, Data2D):
164                plot2D = Plotter2D(self, quickplot=True)
165                plot2D.item = self._index
166                plot2D.plot(plot_set)
167                graphs.append(plot2D.figure)
168            else:
169                msg = "Incorrect data type passed to Plotting"
170                raise AttributeError(msg)
171
172        if 'new_plot' in locals() and isinstance(new_plot.data, Data1D):
173            graphs.append(new_plot.figure)
174
175        return graphs
176
177
178# Simple html report template
179HEADER = "<html>\n"
180HEADER += "<head>\n"
181HEADER += "<meta http-equiv=Content-Type content='text/html; "
182HEADER += "charset=utf-8'> \n"
183HEADER += "<meta name=Generator >\n"
184HEADER += "</head>\n"
185HEADER += "<body lang=EN-US>\n"
186HEADER += "<div class=WordSection1>\n"
187HEADER += "<p class=MsoNormal><b><span ><center><font size='4' >"
188HEADER += "%s</font></center></span></center></b></p>"
189HEADER += "<p class=MsoNormal>&nbsp;</p>"
190PARA = "<p class=MsoNormal><font size='4' > %s \n"
191PARA += "</font></p>"
192CENTRE = "<p class=MsoNormal><center><font size='4' > %s \n"
193CENTRE += "</font></center></p>"
194FEET_1 = \
195"""
196<p class=MsoNormal>&nbsp;</p>
197<br>
198<p class=MsoNormal><b><span ><center> <font size='4' > Graph
199</font></span></center></b></p>
200<p class=MsoNormal>&nbsp;</p>
201<center>
202<br><font size='4' >Model Computation</font>
203<br><font size='4' >Data: "%s"</font><br>
204"""
205FEET_2 = \
206"""<img src="%s" width="540"></img>
207"""
208ELINE = """<p class=MsoNormal>&nbsp;</p>
209"""
210
Note: See TracBrowser for help on using the repository browser.