source: sasview/src/sas/sascalc/fit/pagestate.py @ 2a0f33f

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalcmagnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 2a0f33f was 2a0f33f, checked in by Paul Kienzle <pkienzle@…>, 7 years ago

move fit pagestate from gui to calc

  • Property mode set to 100644
File size: 55.2 KB
Line 
1"""
2    Class that holds a fit page state
3"""
4# TODO: Refactor code so we don't need to use getattr/setattr
5################################################################################
6# This software was developed by the University of Tennessee as part of the
7# Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
8# project funded by the US National Science Foundation.
9#
10# See the license text in license.txt
11#
12# copyright 2009, University of Tennessee
13################################################################################
14import time
15import os
16import sys
17import copy
18import logging
19import numpy as np
20import traceback
21
22import xml.dom.minidom
23from xml.dom.minidom import parseString
24from lxml import etree
25
26from sasmodels import convert
27import sasmodels.weights
28
29import sas.sascalc.dataloader
30from sas.sascalc.dataloader.readers.cansas_reader import Reader as CansasReader
31from sas.sascalc.dataloader.readers.cansas_reader import get_content, write_node
32from sas.sascalc.dataloader.data_info import Data2D, Collimation, Detector
33from sas.sascalc.dataloader.data_info import Process, Aperture
34
35logger = logging.getLogger(__name__)
36
37# Information to read/write state as xml
38FITTING_NODE_NAME = 'fitting_plug_in'
39CANSAS_NS = "cansas1d/1.0"
40
41CUSTOM_MODEL = 'Plugin Models'
42CUSTOM_MODEL_OLD = 'Customized Models'
43
44LIST_OF_DATA_ATTRIBUTES = [["is_data", "is_data", "bool"],
45                           ["group_id", "data_group_id", "string"],
46                           ["data_name", "data_name", "string"],
47                           ["data_id", "data_id", "string"],
48                           ["name", "name", "string"],
49                           ["data_name", "data_name", "string"]]
50LIST_OF_STATE_ATTRIBUTES = [["qmin", "qmin", "float"],
51                            ["qmax", "qmax", "float"],
52                            ["npts", "npts", "float"],
53                            ["categorycombobox", "categorycombobox", "string"],
54                            ["formfactorcombobox", "formfactorcombobox",
55                             "string"],
56                            ["structurecombobox", "structurecombobox",
57                             "string"],
58                            ["multi_factor", "multi_factor", "float"],
59                            ["magnetic_on", "magnetic_on", "bool"],
60                            ["enable_smearer", "enable_smearer", "bool"],
61                            ["disable_smearer", "disable_smearer", "bool"],
62                            ["pinhole_smearer", "pinhole_smearer", "bool"],
63                            ["slit_smearer", "slit_smearer", "bool"],
64                            ["enable_disp", "enable_disp", "bool"],
65                            ["disable_disp", "disable_disp", "bool"],
66                            ["dI_noweight", "dI_noweight", "bool"],
67                            ["dI_didata", "dI_didata", "bool"],
68                            ["dI_sqrdata", "dI_sqrdata", "bool"],
69                            ["dI_idata", "dI_idata", "bool"],
70                            ["enable2D", "enable2D", "bool"],
71                            ["cb1", "cb1", "bool"],
72                            ["tcChi", "tcChi", "float"],
73                            ["smearer", "smearer", "float"],
74                            ["smear_type", "smear_type", "string"],
75                            ["dq_l", "dq_l", "float"],
76                            ["dq_r", "dq_r", "float"],
77                            ["dx_percent", "dx_percent", "float"],
78                            ["dxl", "dxl", "float"],
79                            ["dxw", "dxw", "float"]]
80
81LIST_OF_MODEL_ATTRIBUTES = [["values", "values"],
82                            ["weights", "weights"]]
83
84DISPERSION_LIST = [["disp_obj_dict", "_disp_obj_dict", "string"]]
85
86LIST_OF_STATE_PARAMETERS = [["parameters", "parameters"],
87                            ["str_parameters", "str_parameters"],
88                            ["orientation_parameters", "orientation_params"],
89                            ["dispersity_parameters",
90                             "orientation_params_disp"],
91                            ["fixed_param", "fixed_param"],
92                            ["fittable_param", "fittable_param"]]
93LIST_OF_DATA_2D_ATTR = [["xmin", "xmin", "float"],
94                        ["xmax", "xmax", "float"],
95                        ["ymin", "ymin", "float"],
96                        ["ymax", "ymax", "float"],
97                        ["_xaxis", "_xaxis", "string"],
98                        ["_xunit", "_xunit", "string"],
99                        ["_yaxis", "_yaxis", "string"],
100                        ["_yunit", "_yunit", "string"],
101                        ["_zaxis", "_zaxis", "string"],
102                        ["_zunit", "_zunit", "string"]]
103LIST_OF_DATA_2D_VALUES = [["qx_data", "qx_data", "float"],
104                          ["qy_data", "qy_data", "float"],
105                          ["dqx_data", "dqx_data", "float"],
106                          ["dqy_data", "dqy_data", "float"],
107                          ["data", "data", "float"],
108                          ["q_data", "q_data", "float"],
109                          ["err_data", "err_data", "float"],
110                          ["mask", "mask", "bool"]]
111
112
113def parse_entry_helper(node, item):
114    """
115    Create a numpy list from value extrated from the node
116
117    :param node: node from each the value is stored
118    :param item: list name of three strings.the two first are name of data
119        attribute and the third one is the type of the value of that
120        attribute. type can be string, float, bool, etc.
121
122    : return: numpy array
123    """
124    if node is not None:
125        if item[2] == "string":
126            return str(node.get(item[0]).strip())
127        elif item[2] == "bool":
128            try:
129                return node.get(item[0]).strip() == "True"
130            except Exception:
131                return None
132        else:
133            try:
134                return float(node.get(item[0]))
135            except Exception:
136                return None
137
138
139class PageState(object):
140    """
141    Contains information to reconstruct a page of the fitpanel.
142    """
143    def __init__(self, parent=None, model=None, data=None):
144        """
145        Initialize the current state
146
147        :param model: a selected model within a page
148        :param data:
149
150        """
151        self.file = None
152        # Time of state creation
153        self.timestamp = time.time()
154        # Data member to store the dispersion object created
155        self._disp_obj_dict = {}
156        # ------------------------
157        # Data used for fitting
158        self.data = data
159        # model data
160        self.theory_data = None
161        # Is 2D
162        self.is_2D = False
163        self.images = None
164
165        # save additional information on data that dataloader.reader
166        # does not read
167        self.is_data = None
168        self.data_name = ""
169
170        if self.data is not None:
171            self.data_name = self.data.name
172        self.data_id = None
173        if self.data is not None and hasattr(self.data, "id"):
174            self.data_id = self.data.id
175        self.data_group_id = None
176        if self.data is not None and hasattr(self.data, "group_id"):
177            self.data_group_id = self.data.group_id
178
179        # reset True change the state of existing button
180        self.reset = False
181
182        # flag to allow data2D plot
183        self.enable2D = False
184        # model on which the fit would be performed
185        self.model = model
186        self.m_name = None
187        # list of process done to model
188        self.process = []
189        # fit page manager
190        self.manager = None
191        # Store the parent of this panel parent
192        # For this application fitpanel is the parent
193        self.parent = parent
194        # Event_owner is the owner of model event
195        self.event_owner = None
196        # page name
197        self.page_name = ""
198        # Contains link between model, its parameters, and panel organization
199        self.parameters = []
200        # String parameter list that can not be fitted
201        self.str_parameters = []
202        # Contains list of parameters that cannot be fitted and reference to
203        # panel objects
204        self.fixed_param = []
205        # Contains list of parameters with dispersity and reference to
206        # panel objects
207        self.fittable_param = []
208        # orientation parameters
209        self.orientation_params = []
210        # orientation parameters for gaussian dispersity
211        self.orientation_params_disp = []
212        # smearer info
213        self.smearer = None
214        self.smear_type = None
215        self.dq_l = None
216        self.dq_r = None
217        self.dx_percent = None
218        self.dx_old = False
219        self.dxl = None
220        self.dxw = None
221        # list of dispersion parameters
222        self.disp_list = []
223        if self.model is not None:
224            self.disp_list = self.model.getDispParamList()
225
226        self.disp_cb_dict = {}
227        self.values = {}
228        self.weights = {}
229
230        # contains link between a model and selected parameters to fit
231        self.param_toFit = []
232        # dictionary of model type and model class
233        self.model_list_box = None
234        # save the state of the context menu
235        self.saved_states = {}
236        # save selection of combobox
237        self.formfactorcombobox = None
238        self.categorycombobox = None
239        self.structurecombobox = None
240
241        # radio box to select type of model
242        # self.shape_rbutton = False
243        # self.shape_indep_rbutton = False
244        # self.struct_rbutton = False
245        # self.plugin_rbutton = False
246        # the indice of the current selection
247        self.disp_box = 0
248        # Qrange
249        # Q range
250        self.qmin = 0.001
251        self.qmax = 0.1
252        # reset data range
253        self.qmax_x = None
254        self.qmin_x = None
255
256        self.npts = None
257        self.name = ""
258        self.multi_factor = None
259        self.magnetic_on = False
260        # enable smearering state
261        self.enable_smearer = False
262        self.disable_smearer = True
263        self.pinhole_smearer = False
264        self.slit_smearer = False
265        # weighting options
266        self.dI_noweight = False
267        self.dI_didata = True
268        self.dI_sqrdata = False
269        self.dI_idata = False
270        # disperity selection
271        self.enable_disp = False
272        self.disable_disp = True
273
274        # state of selected all check button
275        self.cb1 = False
276        # store value of chisqr
277        self.tcChi = None
278        self.version = (1,0,0)
279
280    def clone(self):
281        """
282        Create a new copy of the current object
283        """
284        model = None
285        if self.model is not None:
286            model = self.model.clone()
287            model.name = self.model.name
288        obj = PageState(self.parent, model=model)
289        obj.file = copy.deepcopy(self.file)
290        obj.data = copy.deepcopy(self.data)
291        if self.data is not None:
292            self.data_name = self.data.name
293        obj.data_name = self.data_name
294        obj.is_data = self.is_data
295        obj.model_list_box = copy.deepcopy(self.model_list_box)
296
297        obj.categorycombobox = self.categorycombobox
298        obj.formfactorcombobox = self.formfactorcombobox
299        obj.structurecombobox = self.structurecombobox
300
301        # obj.shape_rbutton = self.shape_rbutton
302        # obj.shape_indep_rbutton = self.shape_indep_rbutton
303        # obj.struct_rbutton = self.struct_rbutton
304        # obj.plugin_rbutton = self.plugin_rbutton
305
306        obj.manager = self.manager
307        obj.event_owner = self.event_owner
308        obj.disp_list = copy.deepcopy(self.disp_list)
309
310        obj.enable2D = copy.deepcopy(self.enable2D)
311        obj.parameters = copy.deepcopy(self.parameters)
312        obj.str_parameters = copy.deepcopy(self.str_parameters)
313        obj.fixed_param = copy.deepcopy(self.fixed_param)
314        obj.fittable_param = copy.deepcopy(self.fittable_param)
315        obj.orientation_params = copy.deepcopy(self.orientation_params)
316        obj.orientation_params_disp = \
317            copy.deepcopy(self.orientation_params_disp)
318        obj.enable_disp = copy.deepcopy(self.enable_disp)
319        obj.disable_disp = copy.deepcopy(self.disable_disp)
320        obj.tcChi = self.tcChi
321
322        if len(self._disp_obj_dict) > 0:
323            for k, v in self._disp_obj_dict.iteritems():
324                obj._disp_obj_dict[k] = v
325        if len(self.disp_cb_dict) > 0:
326            for k, v in self.disp_cb_dict.iteritems():
327                obj.disp_cb_dict[k] = v
328        if len(self.values) > 0:
329            for k, v in self.values.iteritems():
330                obj.values[k] = v
331        if len(self.weights) > 0:
332            for k, v in self.weights.iteritems():
333                obj.weights[k] = v
334        obj.enable_smearer = copy.deepcopy(self.enable_smearer)
335        obj.disable_smearer = copy.deepcopy(self.disable_smearer)
336        obj.pinhole_smearer = copy.deepcopy(self.pinhole_smearer)
337        obj.slit_smearer = copy.deepcopy(self.slit_smearer)
338        obj.smear_type = copy.deepcopy(self.smear_type)
339        obj.dI_noweight = copy.deepcopy(self.dI_noweight)
340        obj.dI_didata = copy.deepcopy(self.dI_didata)
341        obj.dI_sqrdata = copy.deepcopy(self.dI_sqrdata)
342        obj.dI_idata = copy.deepcopy(self.dI_idata)
343        obj.dq_l = copy.deepcopy(self.dq_l)
344        obj.dq_r = copy.deepcopy(self.dq_r)
345        obj.dx_percent = copy.deepcopy(self.dx_percent)
346        obj.dx_old = copy.deepcopy(self.dx_old)
347        obj.dxl = copy.deepcopy(self.dxl)
348        obj.dxw = copy.deepcopy(self.dxw)
349        obj.disp_box = copy.deepcopy(self.disp_box)
350        obj.qmin = copy.deepcopy(self.qmin)
351        obj.qmax = copy.deepcopy(self.qmax)
352        obj.multi_factor = self.multi_factor
353        obj.magnetic_on = self.magnetic_on
354        obj.npts = copy.deepcopy(self.npts)
355        obj.cb1 = copy.deepcopy(self.cb1)
356        obj.smearer = copy.deepcopy(self.smearer)
357        obj.version = copy.deepcopy(self.version)
358
359        for name, state in self.saved_states.iteritems():
360            copy_name = copy.deepcopy(name)
361            copy_state = state.clone()
362            obj.saved_states[copy_name] = copy_state
363        return obj
364
365    def _old_first_model(self):
366        """
367        Handle save states from 4.0.1 and before where the first item in the
368        selection boxes of category, formfactor and structurefactor were not
369        saved.
370        :return: None
371        """
372        if self.categorycombobox == CUSTOM_MODEL_OLD:
373            self.categorycombobox = CUSTOM_MODEL
374        if self.formfactorcombobox == '':
375            FIRST_FORM = {
376                'Shapes' : 'BCCrystalModel',
377                'Uncategorized' : 'LineModel',
378                'StructureFactor' : 'HardsphereStructure',
379                'Ellipsoid' : 'core_shell_ellipsoid',
380                'Lamellae' : 'lamellar',
381                'Paracrystal' : 'bcc_paracrystal',
382                'Parallelepiped' : 'core_shell_parallelepiped',
383                'Shape Independent' : 'be_polyelectrolyte',
384                'Sphere' : 'adsorbed_layer',
385                'Structure Factor' : 'hardsphere',
386                CUSTOM_MODEL : ''
387            }
388            if self.categorycombobox == '':
389                if len(self.parameters) == 3:
390                    self.categorycombobox = "Shape-Independent"
391                    self.formfactorcombobox = 'PowerLawAbsModel'
392                elif len(self.parameters) == 9:
393                    self.categorycombobox = 'Cylinder'
394                    self.formfactorcombobox = 'barbell'
395                else:
396                    msg = "Save state does not have enough information to load"
397                    msg += " the all of the data."
398                    logger.warning(msg=msg)
399            else:
400                self.formfactorcombobox = FIRST_FORM[self.categorycombobox]
401
402    @staticmethod
403    def param_remap_to_sasmodels_convert(params, is_string=False):
404        """
405        Remaps the parameters for sasmodels conversion
406
407        :param params: list of parameters (likely self.parameters)
408        :return: remapped dictionary of parameters
409        """
410        p = dict()
411        for fittable, name, value, _, uncert, lower, upper, units in params:
412            if not value:
413                value = np.nan
414            if not uncert or uncert[1] == '' or uncert[1] == 'None':
415                uncert[0] = False
416                uncert[1] = np.nan
417            if not upper or upper[1] == '' or upper[1] == 'None':
418                upper[0] = False
419                upper[1] = np.nan
420            if not lower or lower[1] == '' or lower[1] == 'None':
421                lower[0] = False
422                lower[1] = np.nan
423            if is_string:
424                p[name] = str(value)
425            else:
426                p[name] = float(value)
427            p[name + ".fittable"] = bool(fittable)
428            p[name + ".std"] = float(uncert[1])
429            p[name + ".upper"] = float(upper[1])
430            p[name + ".lower"] = float(lower[1])
431            p[name + ".units"] = units
432        return p
433
434    @staticmethod
435    def param_remap_from_sasmodels_convert(params):
436        """
437        Converts {name : value} map back to [] param list
438        :param params: parameter map returned from sasmodels
439        :return: None
440        """
441        p_map = []
442        for name, info in params.iteritems():
443            if ".fittable" in name or ".std" in name or ".upper" in name or \
444                            ".lower" in name or ".units" in name:
445                pass
446            else:
447                fittable = params.get(name + ".fittable", True)
448                std = params.get(name + ".std", '0.0')
449                upper = params.get(name + ".upper", 'inf')
450                lower = params.get(name + ".lower", '-inf')
451                units = params.get(name + ".units")
452                if std is not None and std is not np.nan:
453                    std = [True, str(std)]
454                else:
455                    std = [False, '']
456                if lower is not None and lower is not np.nan:
457                    lower = [True, str(lower)]
458                else:
459                    lower = [True, '-inf']
460                if upper is not None and upper is not np.nan:
461                    upper = [True, str(upper)]
462                else:
463                    upper = [True, 'inf']
464                param_list = [bool(fittable), str(name), str(info),
465                              "+/-", std, lower, upper, str(units)]
466                p_map.append(param_list)
467        return p_map
468
469    def _convert_to_sasmodels(self):
470        """
471        Convert parameters to a form usable by sasmodels converter
472
473        :return: None
474        """
475        # Create conversion dictionary to send to sasmodels
476        self._old_first_model()
477        p = self.param_remap_to_sasmodels_convert(self.parameters)
478        structurefactor, params = convert.convert_model(self.structurecombobox,
479                                                        p, False, self.version)
480        formfactor, params = convert.convert_model(self.formfactorcombobox,
481                                                   params, False, self.version)
482        if len(self.str_parameters) > 0:
483            str_pars = self.param_remap_to_sasmodels_convert(
484                self.str_parameters, True)
485            formfactor, str_params = convert.convert_model(
486                self.formfactorcombobox, str_pars, False, self.version)
487            for key, value in str_params.iteritems():
488                params[key] = value
489
490        if self.formfactorcombobox == 'SphericalSLDModel':
491            self.multi_factor += 1
492        self.formfactorcombobox = formfactor
493        self.structurecombobox = structurefactor
494        self.parameters = []
495        self.parameters = self.param_remap_from_sasmodels_convert(params)
496
497    def _repr_helper(self, list, rep):
498        """
499        Helper method to print a state
500        """
501        for item in list:
502            rep += "parameter name: %s \n" % str(item[1])
503            rep += "value: %s\n" % str(item[2])
504            rep += "selected: %s\n" % str(item[0])
505            rep += "error displayed : %s \n" % str(item[4][0])
506            rep += "error value:%s \n" % str(item[4][1])
507            rep += "minimum displayed : %s \n" % str(item[5][0])
508            rep += "minimum value : %s \n" % str(item[5][1])
509            rep += "maximum displayed : %s \n" % str(item[6][0])
510            rep += "maximum value : %s \n" % str(item[6][1])
511            rep += "parameter unit: %s\n\n" % str(item[7])
512        return rep
513
514    def __repr__(self):
515        """
516        output string for printing
517        """
518        rep = "\nState name: %s\n" % self.file
519        t = time.localtime(self.timestamp)
520        time_str = time.strftime("%b %d %Y %H:%M:%S ", t)
521
522        rep += "State created: %s\n" % time_str
523        rep += "State form factor combobox selection: %s\n" % \
524               self.formfactorcombobox
525        rep += "State structure factor combobox selection: %s\n" % \
526               self.structurecombobox
527        rep += "is data : %s\n" % self.is_data
528        rep += "data's name : %s\n" % self.data_name
529        rep += "data's id : %s\n" % self.data_id
530        if self.model is not None:
531            m_name = self.model.__class__.__name__
532            if m_name == 'Model':
533                m_name = self.m_name
534            rep += "model name : %s\n" % m_name
535        else:
536            rep += "model name : None\n"
537        rep += "multi_factor : %s\n" % str(self.multi_factor)
538        rep += "magnetic_on : %s\n" % str(self.magnetic_on)
539        rep += "model type (Category) selected: %s\n" % self.categorycombobox
540        rep += "data : %s\n" % str(self.data)
541        rep += "Plotting Range: min: %s, max: %s, steps: %s\n" % \
542               (str(self.qmin), str(self.qmax), str(self.npts))
543        rep += "Dispersion selection : %s\n" % str(self.disp_box)
544        rep += "Smearing enable : %s\n" % str(self.enable_smearer)
545        rep += "Smearing disable : %s\n" % str(self.disable_smearer)
546        rep += "Pinhole smearer enable : %s\n" % str(self.pinhole_smearer)
547        rep += "Slit smearer enable : %s\n" % str(self.slit_smearer)
548        rep += "Dispersity enable : %s\n" % str(self.enable_disp)
549        rep += "Dispersity disable : %s\n" % str(self.disable_disp)
550        rep += "Slit smearer enable: %s\n" % str(self.slit_smearer)
551
552        rep += "dI_noweight : %s\n" % str(self.dI_noweight)
553        rep += "dI_didata : %s\n" % str(self.dI_didata)
554        rep += "dI_sqrdata : %s\n" % str(self.dI_sqrdata)
555        rep += "dI_idata : %s\n" % str(self.dI_idata)
556
557        rep += "2D enable : %s\n" % str(self.enable2D)
558        rep += "All parameters checkbox selected: %s\n" % self.cb1
559        rep += "Value of Chisqr : %s\n" % str(self.tcChi)
560        rep += "Smear object : %s\n" % self.smearer
561        rep += "Smear type : %s\n" % self.smear_type
562        rep += "dq_l  : %s\n" % self.dq_l
563        rep += "dq_r  : %s\n" % self.dq_r
564        rep += "dx_percent  : %s\n" % str(self.dx_percent)
565        rep += "dxl  : %s\n" % str(self.dxl)
566        rep += "dxw : %s\n" % str(self.dxw)
567        rep += "model  : %s\n\n" % str(self.model)
568        temp_parameters = []
569        temp_fittable_param = []
570        if self.data.__class__.__name__ == "Data2D":
571            self.is_2D = True
572        else:
573            self.is_2D = False
574        if self.data is not None:
575            if not self.is_2D:
576                for item in self.parameters:
577                    if item not in self.orientation_params:
578                        temp_parameters.append(item)
579                for item in self.fittable_param:
580                    if item not in self.orientation_params_disp:
581                        temp_fittable_param.append(item)
582            else:
583                temp_parameters = self.parameters
584                temp_fittable_param = self.fittable_param
585
586            rep += "number parameters(self.parameters): %s\n" % \
587                   len(temp_parameters)
588            rep = self._repr_helper(list=temp_parameters, rep=rep)
589            rep += "number str_parameters(self.str_parameters): %s\n" % \
590                   len(self.str_parameters)
591            rep = self._repr_helper(list=self.str_parameters, rep=rep)
592            rep += "number fittable_param(self.fittable_param): %s\n" % \
593                   len(temp_fittable_param)
594            rep = self._repr_helper(list=temp_fittable_param, rep=rep)
595        return rep
596
597    def _get_report_string(self):
598        """
599        Get the values (strings) from __str__ for report
600        """
601        # Dictionary of the report strings
602        repo_time = ""
603        model_name = ""
604        title = ""
605        title_name = ""
606        file_name = ""
607        param_string = ""
608        paramval_string = ""
609        chi2_string = ""
610        q_range = ""
611        strings = self.__repr__()
612        fixed_parameter = False
613        lines = strings.split('\n')
614        # get all string values from __str__()
615        for line in lines:
616            # Skip lines which are not key: value pairs, which includes
617            # blank lines and freeform notes in SASNotes fields.
618            if not ':' in line:
619                #msg = "Report string expected 'name: value' but got %r" % line
620                #logger.error(msg)
621                continue
622
623            name, value = [s.strip() for s in line.split(":", 1)]
624            if name == "State created":
625                repo_time = value
626            elif name == "parameter name":
627                val_name = value.split(".")
628                if len(val_name) > 1:
629                    if val_name[1].count("width"):
630                        param_string += value + ','
631                    else:
632                        continue
633                else:
634                    param_string += value + ','
635            elif name == "value":
636                param_string += value + ','
637            elif name == "selected":
638                # remember if it is fixed when reporting error value
639                fixed_parameter = (value == u'False')
640            elif name == "error value":
641                if fixed_parameter:
642                    param_string += '(fixed),'
643                else:
644                    param_string += value + ','
645            elif name == "parameter unit":
646                param_string += value + ':'
647            elif name == "Value of Chisqr":
648                chi2 = ("Chi2/Npts = " + value)
649                chi2_string = CENTRE % chi2
650            elif name == "Title":
651                if len(value.strip()) == 0:
652                    continue
653                title = value + " [" + repo_time + "]"
654                title_name = HEADER % title
655            elif name == "data":
656                try:
657                    # parsing "data : File:     filename [mmm dd hh:mm]"
658                    name = value.split(':', 1)[1].strip()
659                    file_value = "File name:" + name
660                    file_name = CENTRE % file_value
661                    if len(title) == 0:
662                        title = name + " [" + repo_time + "]"
663                        title_name = HEADER % title
664                except Exception:
665                    msg = "While parsing 'data: ...'\n"
666                    logger.error(msg + traceback.format_exc())
667            elif name == "model name":
668                try:
669                    modelname = "Model name:" + value
670                except Exception:
671                    modelname = "Model name:" + " NAN"
672                model_name = CENTRE % modelname
673
674            elif name == "Plotting Range":
675                try:
676                    parts = value.split(':')
677                    q_range = parts[0] + " = " + parts[1] \
678                            + " = " + parts[2].split(",")[0]
679                    q_name = ("Q Range:    " + q_range)
680                    q_range = CENTRE % q_name
681                except Exception:
682                    msg = "While parsing 'Plotting Range: ...'\n"
683                    logger.error(msg + traceback.format_exc())
684
685        paramval = ""
686        for lines in param_string.split(":"):
687            line = lines.split(",")
688            if len(lines) > 0:
689                param = line[0]
690                param += " = " + line[1]
691                if len(line[2].split()) > 0 and not line[2].count("None"):
692                    param += " +- " + line[2]
693                if len(line[3].split()) > 0 and not line[3].count("None"):
694                    param += " " + line[3]
695                if not paramval.count(param):
696                    paramval += param + "\n"
697                    paramval_string += CENTRE % param + "\n"
698
699        text_string = "\n\n\n%s\n\n%s\n%s\n%s\n\n%s" % \
700                      (title, file, q_name, chi2, paramval)
701
702        title_name = self._check_html_format(title_name)
703        file_name = self._check_html_format(file_name)
704        title = self._check_html_format(title)
705
706        html_string = title_name + "\n" + file_name + \
707                                   "\n" + model_name + \
708                                   "\n" + q_range + \
709                                   "\n" + chi2_string + \
710                                   "\n" + ELINE + \
711                                   "\n" + paramval_string + \
712                                   "\n" + ELINE + \
713                                   "\n" + FEET_1 % title
714
715        return html_string, text_string, title
716
717    def _check_html_format(self, name):
718        """
719        Check string '%' for html format
720        """
721        if name.count('%'):
722            name = name.replace('%', '&#37')
723
724        return name
725
726    def report(self, fig_urls):
727        """
728        Invoke report dialog panel
729
730        : param figs: list of pylab figures [list]
731        """
732        # get the strings for report
733        html_str, text_str, title = self._get_report_string()
734        # Allow 2 figures to append
735        image_links = [FEET_2%fig for fig in fig_urls]
736
737        # final report html strings
738        report_str = html_str + ELINE.join(image_links)
739
740        return report_str, text_str
741
742    def _to_xml_helper(self, thelist, element, newdoc):
743        """
744        Helper method to create xml file for saving state
745        """
746        for item in thelist:
747            sub_element = newdoc.createElement('parameter')
748            sub_element.setAttribute('name', str(item[1]))
749            sub_element.setAttribute('value', str(item[2]))
750            sub_element.setAttribute('selected_to_fit', str(item[0]))
751            sub_element.setAttribute('error_displayed', str(item[4][0]))
752            sub_element.setAttribute('error_value', str(item[4][1]))
753            sub_element.setAttribute('minimum_displayed', str(item[5][0]))
754            sub_element.setAttribute('minimum_value', str(item[5][1]))
755            sub_element.setAttribute('maximum_displayed', str(item[6][0]))
756            sub_element.setAttribute('maximum_value', str(item[6][1]))
757            sub_element.setAttribute('unit', str(item[7]))
758            element.appendChild(sub_element)
759
760    def to_xml(self, file="fitting_state.fitv", doc=None,
761               entry_node=None, batch_fit_state=None):
762        """
763        Writes the state of the fit panel to file, as XML.
764
765        Compatible with standalone writing, or appending to an
766        already existing XML document. In that case, the XML document is
767        required. An optional entry node in the XML document may also be given.
768
769        :param file: file to write to
770        :param doc: XML document object [optional]
771        :param entry_node: XML node within the XML document at which we
772                           will append the data [optional]
773        :param batch_fit_state: simultaneous fit state
774        """
775        from xml.dom.minidom import getDOMImplementation
776
777        # Check whether we have to write a standalone XML file
778        if doc is None:
779            impl = getDOMImplementation()
780            doc_type = impl.createDocumentType(FITTING_NODE_NAME, "1.0", "1.0")
781            newdoc = impl.createDocument(None, FITTING_NODE_NAME, doc_type)
782            top_element = newdoc.documentElement
783        else:
784            # We are appending to an existing document
785            newdoc = doc
786            try:
787                top_element = newdoc.createElement(FITTING_NODE_NAME)
788            except Exception:
789                string = etree.tostring(doc, pretty_print=True)
790                newdoc = parseString(string)
791                top_element = newdoc.createElement(FITTING_NODE_NAME)
792            if entry_node is None:
793                newdoc.documentElement.appendChild(top_element)
794            else:
795                try:
796                    entry_node.appendChild(top_element)
797                except Exception:
798                    node_name = entry_node.tag
799                    node_list = newdoc.getElementsByTagName(node_name)
800                    entry_node = node_list.item(0)
801                    entry_node.appendChild(top_element)
802
803        attr = newdoc.createAttribute("version")
804        from sas import sasview
805        attr.nodeValue = sasview.__version__
806        # attr.nodeValue = '1.0'
807        top_element.setAttributeNode(attr)
808
809        # File name
810        element = newdoc.createElement("filename")
811        if self.file is not None:
812            element.appendChild(newdoc.createTextNode(str(self.file)))
813        else:
814            element.appendChild(newdoc.createTextNode(str(file)))
815        top_element.appendChild(element)
816
817        element = newdoc.createElement("timestamp")
818        element.appendChild(newdoc.createTextNode(time.ctime(self.timestamp)))
819        attr = newdoc.createAttribute("epoch")
820        attr.nodeValue = str(self.timestamp)
821        element.setAttributeNode(attr)
822        top_element.appendChild(element)
823
824        # Inputs
825        inputs = newdoc.createElement("Attributes")
826        top_element.appendChild(inputs)
827
828        if self.data is not None and hasattr(self.data, "group_id"):
829            self.data_group_id = self.data.group_id
830        if self.data is not None and hasattr(self.data, "is_data"):
831            self.is_data = self.data.is_data
832        if self.data is not None:
833            self.data_name = self.data.name
834        if self.data is not None and hasattr(self.data, "id"):
835            self.data_id = self.data.id
836
837        for item in LIST_OF_DATA_ATTRIBUTES:
838            element = newdoc.createElement(item[0])
839            element.setAttribute(item[0], str(getattr(self, item[1])))
840            inputs.appendChild(element)
841
842        for item in LIST_OF_STATE_ATTRIBUTES:
843            element = newdoc.createElement(item[0])
844            element.setAttribute(item[0], str(getattr(self, item[1])))
845            inputs.appendChild(element)
846
847        # For self.values ={ disp_param_name: [vals,...],...}
848        # and for self.weights ={ disp_param_name: [weights,...],...}
849        for item in LIST_OF_MODEL_ATTRIBUTES:
850            element = newdoc.createElement(item[0])
851            value_list = getattr(self, item[1])
852            for key, value in value_list.iteritems():
853                sub_element = newdoc.createElement(key)
854                sub_element.setAttribute('name', str(key))
855                for val in value:
856                    sub_element.appendChild(newdoc.createTextNode(str(val)))
857
858                element.appendChild(sub_element)
859            inputs.appendChild(element)
860
861        # Create doc for the dictionary of self._disp_obj_dic
862        for tagname, varname, tagtype in DISPERSION_LIST:
863            element = newdoc.createElement(tagname)
864            value_list = getattr(self, varname)
865            for key, value in value_list.iteritems():
866                sub_element = newdoc.createElement(key)
867                sub_element.setAttribute('name', str(key))
868                sub_element.setAttribute('value', str(value))
869                element.appendChild(sub_element)
870            inputs.appendChild(element)
871
872        for item in LIST_OF_STATE_PARAMETERS:
873            element = newdoc.createElement(item[0])
874            self._to_xml_helper(thelist=getattr(self, item[1]),
875                                element=element, newdoc=newdoc)
876            inputs.appendChild(element)
877
878        # Combined and Simultaneous Fit Parameters
879        if batch_fit_state is not None:
880            batch_combo = newdoc.createElement('simultaneous_fit')
881            top_element.appendChild(batch_combo)
882
883            # Simultaneous Fit Number For Linking Later
884            element = newdoc.createElement('sim_fit_number')
885            element.setAttribute('fit_number', str(batch_fit_state.fit_page_no))
886            batch_combo.appendChild(element)
887
888            # Save constraints
889            constraints = newdoc.createElement('constraints')
890            batch_combo.appendChild(constraints)
891            for constraint in batch_fit_state.constraints_list:
892                if constraint.model_cbox.GetValue() != "":
893                    # model_cbox, param_cbox, egal_txt, constraint,
894                    # btRemove, sizer
895                    doc_cons = newdoc.createElement('constraint')
896                    doc_cons.setAttribute('model_cbox',
897                                          str(constraint.model_cbox.GetValue()))
898                    doc_cons.setAttribute('param_cbox',
899                                          str(constraint.param_cbox.GetValue()))
900                    doc_cons.setAttribute('egal_txt',
901                                          str(constraint.egal_txt.GetLabel()))
902                    doc_cons.setAttribute('constraint',
903                                          str(constraint.constraint.GetValue()))
904                    constraints.appendChild(doc_cons)
905
906            # Save all models
907            models = newdoc.createElement('model_list')
908            batch_combo.appendChild(models)
909            for model in batch_fit_state.model_list:
910                doc_model = newdoc.createElement('model_list_item')
911                doc_model.setAttribute('checked', str(model[0].GetValue()))
912                keys = model[1].keys()
913                doc_model.setAttribute('name', str(keys[0]))
914                values = model[1].get(keys[0])
915                doc_model.setAttribute('fit_number', str(model[2]))
916                doc_model.setAttribute('fit_page_source', str(model[3]))
917                doc_model.setAttribute('model_name', str(values.model.id))
918                models.appendChild(doc_model)
919
920            # Select All Checkbox
921            element = newdoc.createElement('select_all')
922            if batch_fit_state.select_all:
923                element.setAttribute('checked', 'True')
924            else:
925                element.setAttribute('checked', 'False')
926            batch_combo.appendChild(element)
927
928        # Save the file
929        if doc is None:
930            fd = open(file, 'w')
931            fd.write(newdoc.toprettyxml())
932            fd.close()
933            return None
934        else:
935            return newdoc
936
937    def _from_xml_helper(self, node, list):
938        """
939        Helper function to write state to xml
940        """
941        for item in node:
942            try:
943                name = item.get('name')
944            except:
945                name = None
946            try:
947                value = item.get('value')
948            except:
949                value = None
950            try:
951                selected_to_fit = (item.get('selected_to_fit') == "True")
952            except:
953                selected_to_fit = None
954            try:
955                error_displayed = (item.get('error_displayed') == "True")
956            except:
957                error_displayed = None
958            try:
959                error_value = item.get('error_value')
960            except:
961                error_value = None
962            try:
963                minimum_displayed = (item.get('minimum_displayed') == "True")
964            except:
965                minimum_displayed = None
966            try:
967                minimum_value = item.get('minimum_value')
968            except:
969                minimum_value = None
970            try:
971                maximum_displayed = (item.get('maximum_displayed') == "True")
972            except:
973                maximum_displayed = None
974            try:
975                maximum_value = item.get('maximum_value')
976            except:
977                maximum_value = None
978            try:
979                unit = item.get('unit')
980            except:
981                unit = None
982            list.append([selected_to_fit, name, value, "+/-",
983                         [error_displayed, error_value],
984                         [minimum_displayed, minimum_value],
985                         [maximum_displayed, maximum_value], unit])
986
987    def from_xml(self, file=None, node=None):
988        """
989        Load fitting state from a file
990
991        :param file: .fitv file
992        :param node: node of a XML document to read from
993        """
994        if file is not None:
995            msg = "PageState no longer supports non-CanSAS"
996            msg += " format for fitting files"
997            raise RuntimeError, msg
998
999        if node.get('version'):
1000            # Get the version for model conversion purposes
1001            self.version = tuple(int(e) for e in
1002                                 str.split(node.get('version'), "."))
1003            # The tuple must be at least 3 items long
1004            while len(self.version) < 3:
1005                ver_list = list(self.version)
1006                ver_list.append(0)
1007                self.version = tuple(ver_list)
1008
1009            # Get file name
1010            entry = get_content('ns:filename', node)
1011            if entry is not None:
1012                self.file = entry.text.strip()
1013
1014            # Get time stamp
1015            entry = get_content('ns:timestamp', node)
1016            if entry is not None and entry.get('epoch'):
1017                try:
1018                    self.timestamp = float(entry.get('epoch'))
1019                except Exception:
1020                    msg = "PageState.fromXML: Could not"
1021                    msg += " read timestamp\n %s" % sys.exc_value
1022                    logger.error(msg)
1023
1024            if entry is not None:
1025                # Parse fitting attributes
1026                entry = get_content('ns:Attributes', node)
1027                for item in LIST_OF_DATA_ATTRIBUTES:
1028                    node = get_content('ns:%s' % item[0], entry)
1029                    setattr(self, item[0], parse_entry_helper(node, item))
1030
1031                dx_old_node = get_content('ns:%s' % 'dx_min', entry)
1032                for item in LIST_OF_STATE_ATTRIBUTES:
1033                    if item[0] == "dx_percent" and dx_old_node is not None:
1034                        dxmin = ["dx_min", "dx_min", "float"]
1035                        setattr(self, item[0], parse_entry_helper(dx_old_node,
1036                                                                  dxmin))
1037                        self.dx_old = True
1038                    else:
1039                        node = get_content('ns:%s' % item[0], entry)
1040                        setattr(self, item[0], parse_entry_helper(node, item))
1041
1042                for item in LIST_OF_STATE_PARAMETERS:
1043                    node = get_content("ns:%s" % item[0], entry)
1044                    self._from_xml_helper(node=node,
1045                                          list=getattr(self, item[1]))
1046
1047                # Recover _disp_obj_dict from xml file
1048                self._disp_obj_dict = {}
1049                for tagname, varname, tagtype in DISPERSION_LIST:
1050                    node = get_content("ns:%s" % tagname, entry)
1051                    for attr in node:
1052                        parameter = str(attr.get('name'))
1053                        value = attr.get('value')
1054                        if value.startswith("<"):
1055                            try:
1056                                # <path.to.NamedDistribution object/instance...>
1057                                cls_name = value[1:].split()[0].split('.')[-1]
1058                                cls = getattr(sasmodels.weights, cls_name)
1059                                value = cls.type
1060                            except Exception:
1061                                base = "unable to load distribution %r for %s"
1062                                logger.error(base % (value, parameter))
1063                                continue
1064                        _disp_obj_dict = getattr(self, varname)
1065                        _disp_obj_dict[parameter] = value
1066
1067                # get self.values and self.weights dic. if exists
1068                for tagname, varname in LIST_OF_MODEL_ATTRIBUTES:
1069                    node = get_content("ns:%s" % tagname, entry)
1070                    dic = {}
1071                    value_list = []
1072                    for par in node:
1073                        name = par.get('name')
1074                        values = par.text.split()
1075                        # Get lines only with numbers
1076                        for line in values:
1077                            try:
1078                                val = float(line)
1079                                value_list.append(val)
1080                            except Exception:
1081                                # pass if line is empty (it happens)
1082                                msg = ("Error reading %r from %s %s\n"
1083                                       % (line, tagname, name))
1084                                logger.error(msg + traceback.format_exc())
1085                        dic[name] = np.array(value_list)
1086                    setattr(self, varname, dic)
1087
1088
1089class Reader(CansasReader):
1090    """
1091    Class to load a .fitv fitting file
1092    """
1093    # File type
1094    type_name = "Fitting"
1095
1096    # Wildcards
1097    type = ["Fitting files (*.fitv)|*.fitv"
1098            "SASView file (*.svs)|*.svs"]
1099    # List of allowed extensions
1100    ext = ['.fitv', '.FITV', '.svs', 'SVS']
1101
1102    def __init__(self, call_back=None, cansas=True):
1103        CansasReader.__init__(self)
1104        """
1105        Initialize the call-back method to be called
1106        after we load a file
1107
1108        :param call_back: call-back method
1109        :param cansas:  True = files will be written/read in CanSAS format
1110                        False = write CanSAS format
1111
1112        """
1113        # Call back method to be executed after a file is read
1114        self.call_back = call_back
1115        # CanSAS format flag
1116        self.cansas = cansas
1117        self.state = None
1118        # batch fitting params for saving
1119        self.batchfit_params = []
1120
1121    def get_state(self):
1122        return self.state
1123
1124    def read(self, path):
1125        """
1126        Load a new P(r) inversion state from file
1127
1128        :param path: file path
1129
1130        """
1131        if self.cansas:
1132            return self._read_cansas(path)
1133
1134    def _parse_state(self, entry):
1135        """
1136        Read a fit result from an XML node
1137
1138        :param entry: XML node to read from
1139        :return: PageState object
1140        """
1141        # Create an empty state
1142        state = None
1143        # Locate the P(r) node
1144        try:
1145            nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME,
1146                                namespaces={'ns': CANSAS_NS})
1147            if nodes:
1148                # Create an empty state
1149                state = PageState()
1150                state.from_xml(node=nodes[0])
1151
1152        except:
1153            logger.info("XML document does not contain fitting information.\n"
1154                         + traceback.format_exc())
1155
1156        return state
1157
1158    def _parse_simfit_state(self, entry):
1159        """
1160        Parses the saved data for a simultaneous fit
1161        :param entry: XML object to read from
1162        :return: XML object for a simultaneous fit or None
1163        """
1164        nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME,
1165                            namespaces={'ns': CANSAS_NS})
1166        if nodes:
1167            simfitstate = nodes[0].xpath('ns:simultaneous_fit',
1168                                         namespaces={'ns': CANSAS_NS})
1169            if simfitstate:
1170                from simfitpage import SimFitPageState
1171                sim_fit_state = SimFitPageState()
1172                simfitstate_0 = simfitstate[0]
1173                all = simfitstate_0.xpath('ns:select_all',
1174                                          namespaces={'ns': CANSAS_NS})
1175                atts = all[0].attrib
1176                checked = atts.get('checked')
1177                sim_fit_state.select_all = bool(checked)
1178                model_list = simfitstate_0.xpath('ns:model_list',
1179                                                 namespaces={'ns': CANSAS_NS})
1180                model_list_items = model_list[0].xpath('ns:model_list_item',
1181                                                       namespaces={'ns':
1182                                                                    CANSAS_NS})
1183                for model in model_list_items:
1184                    attrs = model.attrib
1185                    sim_fit_state.model_list.append(attrs)
1186
1187                constraints = simfitstate_0.xpath('ns:constraints',
1188                                                namespaces={'ns': CANSAS_NS})
1189                constraint_list = constraints[0].xpath('ns:constraint',
1190                                               namespaces={'ns': CANSAS_NS})
1191                for constraint in constraint_list:
1192                    attrs = constraint.attrib
1193                    sim_fit_state.constraints_list.append(attrs)
1194
1195                return sim_fit_state
1196            else:
1197                return None
1198
1199    def _parse_save_state_entry(self, dom):
1200        """
1201        Parse a SASentry
1202
1203        :param node: SASentry node
1204
1205        :return: Data1D/Data2D object
1206
1207        """
1208        node = dom.xpath('ns:data_class', namespaces={'ns': CANSAS_NS})
1209        return_value, _ = self._parse_entry(dom)
1210        return return_value, _
1211
1212    def _read_cansas(self, path):
1213        """
1214        Load data and fitting information from a CanSAS XML file.
1215
1216        :param path: file path
1217        :return: Data1D object if a single SASentry was found,
1218                    or a list of Data1D objects if multiple entries were found,
1219                    or None of nothing was found
1220        :raise RuntimeError: when the file can't be opened
1221        :raise ValueError: when the length of the data vectors are inconsistent
1222        """
1223        output = []
1224        simfitstate = None
1225        basename = os.path.basename(path)
1226        root, extension = os.path.splitext(basename)
1227        ext = extension.lower()
1228        try:
1229            if os.path.isfile(path):
1230                if ext in self.ext or ext == '.xml':
1231                    tree = etree.parse(path, parser=etree.ETCompatXMLParser())
1232                    # Check the format version number
1233                    # Specifying the namespace will take care of the file
1234                    # format version
1235                    root = tree.getroot()
1236                    entry_list = root.xpath('ns:SASentry',
1237                                            namespaces={'ns': CANSAS_NS})
1238                    for entry in entry_list:
1239                        try:
1240                            sas_entry, _ = self._parse_save_state_entry(entry)
1241                        except:
1242                            raise
1243                        fitstate = self._parse_state(entry)
1244
1245                        # state could be None when .svs file is loaded
1246                        # in this case, skip appending to output
1247                        if fitstate is not None:
1248                            sas_entry.meta_data['fitstate'] = fitstate
1249                            sas_entry.filename = fitstate.file
1250                            output.append(sas_entry)
1251
1252            else:
1253                self.call_back(format=ext)
1254                raise RuntimeError, "%s is not a file" % path
1255
1256            # Return output consistent with the loader's api
1257            if len(output) == 0:
1258                self.call_back(state=None, datainfo=None, format=ext)
1259                return None
1260            else:
1261                for ind in range(len(output)):
1262                    # Call back to post the new state
1263                    state = output[ind].meta_data['fitstate']
1264                    t = time.localtime(state.timestamp)
1265                    time_str = time.strftime("%b %d %H:%M", t)
1266                    # Check that no time stamp is already appended
1267                    max_char = state.file.find("[")
1268                    if max_char < 0:
1269                        max_char = len(state.file)
1270                    original_fname = state.file[0:max_char]
1271                    state.file = original_fname + ' [' + time_str + ']'
1272
1273                    if state is not None and state.is_data is not None:
1274                        output[ind].is_data = state.is_data
1275
1276                    output[ind].filename = state.file
1277                    state.data = output[ind]
1278                    state.data.name = output[ind].filename  # state.data_name
1279                    state.data.id = state.data_id
1280                    if state.is_data is not None:
1281                        state.data.is_data = state.is_data
1282                    if output[ind].run_name is not None\
1283                         and len(output[ind].run_name) != 0:
1284                        if isinstance(output[ind].run_name, dict):
1285                            name = output[ind].run_name.keys()[0]
1286                        else:
1287                            name = output[ind].run_name
1288                    else:
1289                        name = original_fname
1290                    state.data.group_id = name
1291                    state.version = fitstate.version
1292                    # store state in fitting
1293                    self.call_back(state=state,
1294                                   datainfo=output[ind], format=ext)
1295                    self.state = state
1296                simfitstate = self._parse_simfit_state(entry)
1297                if simfitstate is not None:
1298                    self.call_back(state=simfitstate)
1299
1300                return output
1301        except:
1302            self.call_back(format=ext)
1303            raise
1304
1305    def write(self, filename, datainfo=None, fitstate=None):
1306        """
1307        Write the content of a Data1D as a CanSAS XML file only for standalone
1308
1309        :param filename: name of the file to write
1310        :param datainfo: Data1D object
1311        :param fitstate: PageState object
1312
1313        """
1314        # Sanity check
1315        if self.cansas:
1316            # Add fitting information to the XML document
1317            doc = self.write_toXML(datainfo, fitstate)
1318            # Write the XML document
1319        else:
1320            doc = fitstate.to_xml(file=filename)
1321
1322        # Save the document no matter the type
1323        fd = open(filename, 'w')
1324        fd.write(doc.toprettyxml())
1325        fd.close()
1326
1327    def write_toXML(self, datainfo=None, state=None, batchfit=None):
1328        """
1329        Write toXML, a helper for write(),
1330        could be used by guimanager._on_save()
1331
1332        : return: xml doc
1333        """
1334
1335        self.batchfit_params = batchfit
1336        if state.data is None or not state.data.is_data:
1337            return None
1338        # make sure title and data run are filled.
1339        if state.data.title is None or state.data.title == '':
1340            state.data.title = state.data.name
1341        if state.data.run_name is None or state.data.run_name == {}:
1342            state.data.run = [str(state.data.name)]
1343            state.data.run_name[0] = state.data.name
1344
1345        data = state.data
1346        doc, sasentry = self._to_xml_doc(data)
1347
1348        if state is not None:
1349            doc = state.to_xml(doc=doc, file=data.filename, entry_node=sasentry,
1350                               batch_fit_state=self.batchfit_params)
1351
1352        return doc
1353
1354# Simple html report template
1355HEADER = "<html>\n"
1356HEADER += "<head>\n"
1357HEADER += "<meta http-equiv=Content-Type content='text/html; "
1358HEADER += "charset=windows-1252'> \n"
1359HEADER += "<meta name=Generator >\n"
1360HEADER += "</head>\n"
1361HEADER += "<body lang=EN-US>\n"
1362HEADER += "<div class=WordSection1>\n"
1363HEADER += "<p class=MsoNormal><b><span ><center><font size='4' >"
1364HEADER += "%s</font></center></span></center></b></p>"
1365HEADER += "<p class=MsoNormal>&nbsp;</p>"
1366PARA = "<p class=MsoNormal><font size='4' > %s \n"
1367PARA += "</font></p>"
1368CENTRE = "<p class=MsoNormal><center><font size='4' > %s \n"
1369CENTRE += "</font></center></p>"
1370FEET_1 = \
1371"""
1372<p class=MsoNormal>&nbsp;</p>
1373<br>
1374<p class=MsoNormal><b><span ><center> <font size='4' > Graph
1375</font></span></center></b></p>
1376<p class=MsoNormal>&nbsp;</p>
1377<center>
1378<br><font size='4' >Model Computation</font>
1379<br><font size='4' >Data: "%s"</font><br>
1380"""
1381FEET_2 = \
1382"""<img src="%s" ></img>
1383"""
1384FEET_3 = \
1385"""</center>
1386</div>
1387</body>
1388</html>
1389"""
1390ELINE = """<p class=MsoNormal>&nbsp;</p>
1391"""
Note: See TracBrowser for help on using the repository browser.