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

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

move sim fit state to sascalc pagestate

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