source: sasview/src/sas/sasgui/perspectives/fitting/pagestate.py @ 7cde638

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 7cde638 was 959eb01, checked in by ajj, 8 years ago

normalising line endings

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