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

magnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249unittest-saveload
Last change on this file since a8e368b was a8e368b, checked in by wojciech, 6 years ago

Fixing width for printing

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