source: sasview/src/sas/qtgui/Perspectives/Fitting/ReportPageLogic.py @ 5330be2

ESS_GUIESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since 5330be2 was 5330be2, checked in by Piotr Rozyczko <rozyczko@…>, 11 months ago

Fix 2D chart failures with reporting. SASVIEW-950

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