# -*- coding: utf-8 -*- import base64 import datetime import re import sys import tempfile import logging from io import BytesIO import urllib.parse from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas import sas.qtgui.Utilities.GuiUtils as GuiUtils from sas.qtgui.Perspectives.Fitting import FittingUtilities class ReportPageLogic(object): """ Logic for the Report Page functionality. Refactored from FittingWidget. """ def __init__(self, parent=None, kernel_module=None, data=None, index=None, model=None): self.parent = parent self.kernel_module = kernel_module self.data = data self._index = index self.model = model @staticmethod def cleanhtml(raw_html): """Remove html tags from a document""" cleanr = re.compile('<.*?>') cleantext = re.sub(cleanr, '', raw_html) return cleantext def reportList(self): """ Return the HTML version of the full report """ if self.kernel_module is None: report_txt = "No model defined" report_html = HEADER % report_txt images = [] return [report_html, report_txt, images] # Get plot image from plotpanel images = self.getImages() imagesHTML = "" if images is not None: imagesHTML = self.buildPlotsForReport(images) report_header = self.reportHeader() report_parameters = self.reportParams() report_html = report_header + report_parameters + imagesHTML report_txt = self.cleanhtml(report_html) report_list = [report_html, report_txt, images] return report_list def reportHeader(self): """ Look at widget state and extract report header info """ report = "" title = self.data.name current_time = datetime.datetime.now().strftime("%I:%M%p, %B %d, %Y") filename = self.data.filename modelname = self.kernel_module.id if hasattr(self.data, 'xmin'): qrange_min = self.data.xmin qrange_max = self.data.xmax else: qrange_min = min(self.data.x) qrange_max = max(self.data.x) qrange = "min = {}, max = {}".format(qrange_min, qrange_max) title = title + " [" + current_time + "]" title_name = HEADER % title report = title_name report = report + CENTRE % "File name:{}\n".format(filename) report = report + CENTRE % "Model name:{}\n".format(modelname) report = report + CENTRE % "Q Range: {}\n".format(qrange) chi2_repr = GuiUtils.formatNumber(self.parent.chi2, high=True) report = report + CENTRE % "Chi2/Npts:{}\n".format(chi2_repr) return report def buildPlotsForReport(self, images): """ Convert Matplotlib figure 'fig' into a tag for HTML use using base64 encoding. """ html = FEET_1 % self.data.filename for fig in images: canvas = FigureCanvas(fig) png_output = BytesIO() try: fig.savefig(png_output, format="png", dpi=75) except PermissionError as ex: logging.error("Creating of the report failed: %s"%str(ex)) return data64 = base64.b64encode(png_output.getvalue()) data_to_print = urllib.parse.quote(data64) feet = FEET_2 if sys.platform == "darwin": # Mac feet = FEET_3 html += feet.format(data_to_print) html += ELINE png_output.close() del canvas return html def reportParams(self): """ Look at widget state and extract parameters """ pars = FittingUtilities.getStandardParam(self.model) if pars is None: return "" report = "" plus_minus = " ± " for value in pars: try: par_name = value[1] par_fixed = not value[0] par_value = value[2] par_unit = value[7] # Convert units for nice display par_unit = GuiUtils.convertUnitToHTML(par_unit.strip()) if par_fixed: error = " (fixed)" else: error = plus_minus + str(value[4][1]) param = par_name + " = " + par_value + error + " " + par_unit except IndexError as ex: # corrupted model. Complain and skip the line logging.error("Error in parsing parameters: "+str(ex)) continue report += CENTRE % param + "\n" return report def getImages(self): """ Create MPL figures for the current fit """ graphs = [] modelname = self.kernel_module.name if not modelname or self._index is None: return None plot_ids = [plot.id for plot in GuiUtils.plotsFromModel(modelname, self._index)] # Active plots import sas.qtgui.Plotting.PlotHelper as PlotHelper shown_plot_names = PlotHelper.currentPlots() # current_plots = list of graph names of currently shown plots # which are related to this dataset current_plots = [name for name in shown_plot_names if PlotHelper.plotById(name).data.id in plot_ids] for name in current_plots: # get the plotter object first plotter = PlotHelper.plotById(name) graphs.append(plotter.figure) return graphs # Simple html report template HEADER = "\n" HEADER += "
\n" HEADER += " \n" HEADER += "\n" HEADER += "\n" HEADER += "\n" HEADER += "" PARA = "
%s \n" PARA += "
" CENTRE = "
"""