source: sasview/src/sas/sascalc/fit/pagestate.py @ 81b35396

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 81b35396 was 81b35396, checked in by Paul Kienzle <pkienzle@…>, 7 years ago

remove unused smearer/smear_type from saved state

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