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

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

code cleanup

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