source: sasview/src/sas/sascalc/fit/pagestate.py @ 0e0c645

ticket-1243
Last change on this file since 0e0c645 was 863ac2c, checked in by butler, 6 years ago

Update fix to pagestate.py for ticket-1179 branch as per P. Kienzle

Use global already defined in pagestate.py instead of recreating it as
suggested by Paul Kienzle.

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