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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 71601312 was 71601312, checked in by krzywon, 3 years ago

Put the category name change in the right place.

  • Property mode set to 100644
File size: 57.7 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 wx
18import copy
19import logging
20import numpy
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
36# Information to read/write state as xml
37FITTING_NODE_NAME = 'fitting_plug_in'
38CANSAS_NS = "cansas1d/1.0"
39
40CUSTOM_MODEL = 'Plugin Models'
41CUSTOM_MODEL_OLD = 'Customized Models'
42
43LIST_OF_DATA_ATTRIBUTES = [["is_data", "is_data", "bool"],
44                           ["group_id", "data_group_id", "string"],
45                           ["data_name", "data_name", "string"],
46                           ["data_id", "data_id", "string"],
47                           ["name", "name", "string"],
48                           ["data_name", "data_name", "string"]]
49LIST_OF_STATE_ATTRIBUTES = [["qmin", "qmin", "float"],
50                            ["qmax", "qmax", "float"],
51                            ["npts", "npts", "float"],
52                            ["categorycombobox", "categorycombobox", "string"],
53                            ["formfactorcombobox", "formfactorcombobox",
54                             "string"],
55                            ["structurecombobox", "structurecombobox",
56                             "string"],
57                            ["multi_factor", "multi_factor", "float"],
58                            ["magnetic_on", "magnetic_on", "bool"],
59                            ["enable_smearer", "enable_smearer", "bool"],
60                            ["disable_smearer", "disable_smearer", "bool"],
61                            ["pinhole_smearer", "pinhole_smearer", "bool"],
62                            ["slit_smearer", "slit_smearer", "bool"],
63                            ["enable_disp", "enable_disp", "bool"],
64                            ["disable_disp", "disable_disp", "bool"],
65                            ["dI_noweight", "dI_noweight", "bool"],
66                            ["dI_didata", "dI_didata", "bool"],
67                            ["dI_sqrdata", "dI_sqrdata", "bool"],
68                            ["dI_idata", "dI_idata", "bool"],
69                            ["enable2D", "enable2D", "bool"],
70                            ["cb1", "cb1", "bool"],
71                            ["tcChi", "tcChi", "float"],
72                            ["smearer", "smearer", "float"],
73                            ["smear_type", "smear_type", "string"],
74                            ["dq_l", "dq_l", "float"],
75                            ["dq_r", "dq_r", "float"],
76                            ["dx_max", "dx_max", "float"],
77                            ["dx_min", "dx_min", "float"],
78                            ["dxl", "dxl", "float"],
79                            ["dxw", "dxw", "float"]]
80
81LIST_OF_MODEL_ATTRIBUTES = [["values", "values"],
82                            ["weights", "weights"]]
83
84DISPERSION_LIST = [["disp_obj_dict", "_disp_obj_dict", "string"]]
85
86LIST_OF_STATE_PARAMETERS = [["parameters", "parameters"],
87                            ["str_parameters", "str_parameters"],
88                            ["orientation_parameters", "orientation_params"],
89                            ["dispersity_parameters",
90                             "orientation_params_disp"],
91                            ["fixed_param", "fixed_param"],
92                            ["fittable_param", "fittable_param"]]
93LIST_OF_DATA_2D_ATTR = [["xmin", "xmin", "float"],
94                        ["xmax", "xmax", "float"],
95                        ["ymin", "ymin", "float"],
96                        ["ymax", "ymax", "float"],
97                        ["_xaxis", "_xaxis", "string"],
98                        ["_xunit", "_xunit", "string"],
99                        ["_yaxis", "_yaxis", "string"],
100                        ["_yunit", "_yunit", "string"],
101                        ["_zaxis", "_zaxis", "string"],
102                        ["_zunit", "_zunit", "string"]]
103LIST_OF_DATA_2D_VALUES = [["qx_data", "qx_data", "float"],
104                          ["qy_data", "qy_data", "float"],
105                          ["dqx_data", "dqx_data", "float"],
106                          ["dqy_data", "dqy_data", "float"],
107                          ["data", "data", "float"],
108                          ["q_data", "q_data", "float"],
109                          ["err_data", "err_data", "float"],
110                          ["mask", "mask", "bool"]]
111
112
113def parse_entry_helper(node, item):
114    """
115    Create a numpy list from value extrated from the node
116
117    :param node: node from each the value is stored
118    :param item: list name of three strings.the two first are name of data
119        attribute and the third one is the type of the value of that
120        attribute. type can be string, float, bool, etc.
121
122    : return: numpy array
123    """
124    if node is not None:
125        if item[2] == "string":
126            return str(node.get(item[0]).strip())
127        elif item[2] == "bool":
128            try:
129                return node.get(item[0]).strip() == "True"
130            except Exception:
131                return None
132        else:
133            try:
134                return float(node.get(item[0]))
135            except Exception:
136                return None
137
138
139class PageState(object):
140    """
141    Contains information to reconstruct a page of the fitpanel.
142    """
143    def __init__(self, parent=None, model=None, data=None):
144        """
145        Initialize the current state
146
147        :param model: a selected model within a page
148        :param data:
149
150        """
151        self.file = None
152        # Time of state creation
153        self.timestamp = time.time()
154        # Data member to store the dispersion object created
155        self._disp_obj_dict = {}
156        # ------------------------
157        # Data used for fitting
158        self.data = data
159        # model data
160        self.theory_data = None
161        # Is 2D
162        self.is_2D = False
163        self.images = None
164
165        # save additional information on data that dataloader.reader
166        # does not read
167        self.is_data = None
168        self.data_name = ""
169
170        if self.data is not None:
171            self.data_name = self.data.name
172        self.data_id = None
173        if self.data is not None and hasattr(self.data, "id"):
174            self.data_id = self.data.id
175        self.data_group_id = None
176        if self.data is not None and hasattr(self.data, "group_id"):
177            self.data_group_id = self.data.group_id
178
179        # reset True change the state of existing button
180        self.reset = False
181
182        # flag to allow data2D plot
183        self.enable2D = False
184        # model on which the fit would be performed
185        self.model = model
186        self.m_name = None
187        # list of process done to model
188        self.process = []
189        # fit page manager
190        self.manager = None
191        # Store the parent of this panel parent
192        # For this application fitpanel is the parent
193        self.parent = parent
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_max = None
218        self.dx_min = None
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(self.parent, 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_max = copy.deepcopy(self.dx_max)
346        obj.dx_min = copy.deepcopy(self.dx_min)
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                    logging.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 = numpy.nan
414            if not uncert or uncert[1] == '' or uncert[1] == 'None':
415                uncert[0] = False
416                uncert[1] = numpy.nan
417            if not upper or upper[1] == '' or upper[1] == 'None':
418                upper[0] = False
419                upper[1] = numpy.nan
420            if not lower or lower[1] == '' or lower[1] == 'None':
421                lower[0] = False
422                lower[1] = numpy.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 numpy.nan:
453                    std = [True, str(std)]
454                else:
455                    std = [False, '']
456                if lower is not None and lower is not numpy.nan:
457                    lower = [True, str(lower)]
458                else:
459                    lower = [True, '-inf']
460                if upper is not None and upper is not numpy.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_max  : %s\n" % str(self.dx_max)
565        rep += "dx_min : %s\n" % str(self.dx_min)
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                logging.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                    logging.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                    logging.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        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                    logging.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                for item in LIST_OF_STATE_ATTRIBUTES:
1051                    node = get_content('ns:%s' % item[0], entry)
1052                    setattr(self, item[0], parse_entry_helper(node, item))
1053
1054                for item in LIST_OF_STATE_PARAMETERS:
1055                    node = get_content("ns:%s" % item[0], entry)
1056                    self._from_xml_helper(node=node,
1057                                          list=getattr(self, item[1]))
1058
1059                # Recover _disp_obj_dict from xml file
1060                self._disp_obj_dict = {}
1061                for tagname, varname, tagtype in DISPERSION_LIST:
1062                    node = get_content("ns:%s" % tagname, entry)
1063                    for attr in node:
1064                        parameter = str(attr.get('name'))
1065                        value = attr.get('value')
1066                        if value.startswith("<"):
1067                            try:
1068                                # <path.to.NamedDistribution object/instance...>
1069                                cls_name = value[1:].split()[0].split('.')[-1]
1070                                cls = getattr(sasmodels.weights, cls_name)
1071                                value = cls.type
1072                            except Exception:
1073                                base = "unable to load distribution %r for %s"
1074                                logging.error(base % (value, parameter))
1075                                continue
1076                        _disp_obj_dict = getattr(self, varname)
1077                        _disp_obj_dict[parameter] = value
1078
1079                # get self.values and self.weights dic. if exists
1080                for tagname, varname in LIST_OF_MODEL_ATTRIBUTES:
1081                    node = get_content("ns:%s" % tagname, entry)
1082                    dic = {}
1083                    value_list = []
1084                    for par in node:
1085                        name = par.get('name')
1086                        values = par.text.split()
1087                        # Get lines only with numbers
1088                        for line in values:
1089                            try:
1090                                val = float(line)
1091                                value_list.append(val)
1092                            except Exception:
1093                                # pass if line is empty (it happens)
1094                                msg = ("Error reading %r from %s %s\n"
1095                                       % (line, tagname, name))
1096                                logging.error(msg + traceback.format_exc())
1097                        dic[name] = numpy.array(value_list)
1098                    setattr(self, varname, dic)
1099
1100    def set_plot_state(self, figs, canvases):
1101        """
1102        Build image state that wx.html understand
1103        by plotting, putting it into wx.FileSystem image object
1104
1105        """
1106        images = []
1107
1108        # Reset memory
1109        self.imgRAM = None
1110        wx.MemoryFSHandler()
1111
1112        # For no figures in the list, prepare empty plot
1113        if figs is None or len(figs) == 0:
1114            figs = [None]
1115
1116        # Loop over the list of figures
1117        # use wx.MemoryFSHandler
1118        self.imgRAM = wx.MemoryFSHandler()
1119        for fig in figs:
1120            if fig is not None:
1121                ind = figs.index(fig)
1122                canvas = canvases[ind]
1123
1124            # store the image in wx.FileSystem Object
1125            wx.FileSystem.AddHandler(wx.MemoryFSHandler())
1126
1127            # index of the fig
1128            ind = figs.index(fig)
1129
1130            # AddFile, image can be retrieved with 'memory:filename'
1131            self.imgRAM.AddFile('img_fit%s.png' % ind,
1132                                canvas.bitmap, wx.BITMAP_TYPE_PNG)
1133
1134            # append figs
1135            images.append(fig)
1136
1137        return images
1138
1139
1140class Reader(CansasReader):
1141    """
1142    Class to load a .fitv fitting file
1143    """
1144    # File type
1145    type_name = "Fitting"
1146
1147    # Wildcards
1148    type = ["Fitting files (*.fitv)|*.fitv"
1149            "SASView file (*.svs)|*.svs"]
1150    # List of allowed extensions
1151    ext = ['.fitv', '.FITV', '.svs', 'SVS']
1152
1153    def __init__(self, call_back=None, cansas=True):
1154        CansasReader.__init__(self)
1155        """
1156        Initialize the call-back method to be called
1157        after we load a file
1158
1159        :param call_back: call-back method
1160        :param cansas:  True = files will be written/read in CanSAS format
1161                        False = write CanSAS format
1162
1163        """
1164        # Call back method to be executed after a file is read
1165        self.call_back = call_back
1166        # CanSAS format flag
1167        self.cansas = cansas
1168        self.state = None
1169        # batch fitting params for saving
1170        self.batchfit_params = []
1171
1172    def get_state(self):
1173        return self.state
1174
1175    def read(self, path):
1176        """
1177        Load a new P(r) inversion state from file
1178
1179        :param path: file path
1180
1181        """
1182        if self.cansas:
1183            return self._read_cansas(path)
1184
1185    def _parse_state(self, entry):
1186        """
1187        Read a fit result from an XML node
1188
1189        :param entry: XML node to read from
1190        :return: PageState object
1191        """
1192        # Create an empty state
1193        state = None
1194        # Locate the P(r) node
1195        try:
1196            nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME,
1197                                namespaces={'ns': CANSAS_NS})
1198            if nodes:
1199                # Create an empty state
1200                state = PageState()
1201                state.from_xml(node=nodes[0])
1202
1203        except:
1204            logging.info("XML document does not contain fitting information.\n"
1205                         + traceback.format_exc())
1206
1207        return state
1208
1209    def _parse_simfit_state(self, entry):
1210        """
1211        Parses the saved data for a simultaneous fit
1212        :param entry: XML object to read from
1213        :return: XML object for a simultaneous fit or None
1214        """
1215        nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME,
1216                            namespaces={'ns': CANSAS_NS})
1217        if nodes:
1218            simfitstate = nodes[0].xpath('ns:simultaneous_fit',
1219                                         namespaces={'ns': CANSAS_NS})
1220            if simfitstate:
1221                from simfitpage import SimFitPageState
1222                sim_fit_state = SimFitPageState()
1223                simfitstate_0 = simfitstate[0]
1224                all = simfitstate_0.xpath('ns:select_all',
1225                                          namespaces={'ns': CANSAS_NS})
1226                atts = all[0].attrib
1227                checked = atts.get('checked')
1228                sim_fit_state.select_all = bool(checked)
1229                model_list = simfitstate_0.xpath('ns:model_list',
1230                                                 namespaces={'ns': CANSAS_NS})
1231                model_list_items = model_list[0].xpath('ns:model_list_item',
1232                                                       namespaces={'ns':
1233                                                                    CANSAS_NS})
1234                for model in model_list_items:
1235                    attrs = model.attrib
1236                    sim_fit_state.model_list.append(attrs)
1237
1238                constraints = simfitstate_0.xpath('ns:constraints',
1239                                                namespaces={'ns': CANSAS_NS})
1240                constraint_list = constraints[0].xpath('ns:constraint',
1241                                               namespaces={'ns': CANSAS_NS})
1242                for constraint in constraint_list:
1243                    attrs = constraint.attrib
1244                    sim_fit_state.constraints_list.append(attrs)
1245
1246                return sim_fit_state
1247            else:
1248                return None
1249
1250    def _parse_save_state_entry(self, dom):
1251        """
1252        Parse a SASentry
1253
1254        :param node: SASentry node
1255
1256        :return: Data1D/Data2D object
1257
1258        """
1259        node = dom.xpath('ns:data_class', namespaces={'ns': CANSAS_NS})
1260        return_value, _ = self._parse_entry(dom)
1261        return return_value, _
1262
1263    def _read_cansas(self, path):
1264        """
1265        Load data and fitting information from a CanSAS XML file.
1266
1267        :param path: file path
1268        :return: Data1D object if a single SASentry was found,
1269                    or a list of Data1D objects if multiple entries were found,
1270                    or None of nothing was found
1271        :raise RuntimeError: when the file can't be opened
1272        :raise ValueError: when the length of the data vectors are inconsistent
1273        """
1274        output = []
1275        simfitstate = None
1276        basename = os.path.basename(path)
1277        root, extension = os.path.splitext(basename)
1278        ext = extension.lower()
1279        try:
1280            if os.path.isfile(path):
1281                if ext in self.ext or ext == '.xml':
1282                    tree = etree.parse(path, parser=etree.ETCompatXMLParser())
1283                    # Check the format version number
1284                    # Specifying the namespace will take care of the file
1285                    # format version
1286                    root = tree.getroot()
1287                    entry_list = root.xpath('ns:SASentry',
1288                                            namespaces={'ns': CANSAS_NS})
1289                    for entry in entry_list:
1290                        try:
1291                            sas_entry, _ = self._parse_save_state_entry(entry)
1292                        except:
1293                            raise
1294                        fitstate = self._parse_state(entry)
1295
1296                        # state could be None when .svs file is loaded
1297                        # in this case, skip appending to output
1298                        if fitstate is not None:
1299                            sas_entry.meta_data['fitstate'] = fitstate
1300                            sas_entry.filename = fitstate.file
1301                            output.append(sas_entry)
1302
1303            else:
1304                self.call_back(format=ext)
1305                raise RuntimeError, "%s is not a file" % path
1306
1307            # Return output consistent with the loader's api
1308            if len(output) == 0:
1309                self.call_back(state=None, datainfo=None, format=ext)
1310                return None
1311            else:
1312                for ind in range(len(output)):
1313                    # Call back to post the new state
1314                    state = output[ind].meta_data['fitstate']
1315                    t = time.localtime(state.timestamp)
1316                    time_str = time.strftime("%b %d %H:%M", t)
1317                    # Check that no time stamp is already appended
1318                    max_char = state.file.find("[")
1319                    if max_char < 0:
1320                        max_char = len(state.file)
1321                    original_fname = state.file[0:max_char]
1322                    state.file = original_fname + ' [' + time_str + ']'
1323
1324                    if state is not None and state.is_data is not None:
1325                        output[ind].is_data = state.is_data
1326
1327                    output[ind].filename = state.file
1328                    state.data = output[ind]
1329                    state.data.name = output[ind].filename  # state.data_name
1330                    state.data.id = state.data_id
1331                    if state.is_data is not None:
1332                        state.data.is_data = state.is_data
1333                    if output[ind].run_name is not None\
1334                         and len(output[ind].run_name) != 0:
1335                        if isinstance(output[ind].run_name, dict):
1336                            name = output[ind].run_name.keys()[0]
1337                        else:
1338                            name = output[ind].run_name
1339                    else:
1340                        name = original_fname
1341                    state.data.group_id = name
1342                    state.version = fitstate.version
1343                    # store state in fitting
1344                    self.call_back(state=state,
1345                                   datainfo=output[ind], format=ext)
1346                    self.state = state
1347                simfitstate = self._parse_simfit_state(entry)
1348                if simfitstate is not None:
1349                    self.call_back(state=simfitstate)
1350
1351                return output
1352        except:
1353            self.call_back(format=ext)
1354            raise
1355
1356    def write(self, filename, datainfo=None, fitstate=None):
1357        """
1358        Write the content of a Data1D as a CanSAS XML file only for standalone
1359
1360        :param filename: name of the file to write
1361        :param datainfo: Data1D object
1362        :param fitstate: PageState object
1363
1364        """
1365        # Sanity check
1366        if self.cansas:
1367            # Add fitting information to the XML document
1368            doc = self.write_toXML(datainfo, fitstate)
1369            # Write the XML document
1370        else:
1371            doc = fitstate.to_xml(file=filename)
1372
1373        # Save the document no matter the type
1374        fd = open(filename, 'w')
1375        fd.write(doc.toprettyxml())
1376        fd.close()
1377
1378    def write_toXML(self, datainfo=None, state=None, batchfit=None):
1379        """
1380        Write toXML, a helper for write(),
1381        could be used by guimanager._on_save()
1382
1383        : return: xml doc
1384        """
1385
1386        self.batchfit_params = batchfit
1387        if state.data is None or not state.data.is_data:
1388            return None
1389        # make sure title and data run are filled.
1390        if state.data.title is None or state.data.title == '':
1391            state.data.title = state.data.name
1392        if state.data.run_name is None or state.data.run_name == {}:
1393            state.data.run = [str(state.data.name)]
1394            state.data.run_name[0] = state.data.name
1395
1396        data = state.data
1397        doc, sasentry = self._to_xml_doc(data)
1398
1399        if state is not None:
1400            doc = state.to_xml(doc=doc, file=data.filename, entry_node=sasentry,
1401                               batch_fit_state=self.batchfit_params)
1402
1403        return doc
1404
1405# Simple html report templet
1406HEADER = "<html>\n"
1407HEADER += "<head>\n"
1408HEADER += "<meta http-equiv=Content-Type content='text/html; "
1409HEADER += "charset=windows-1252'> \n"
1410HEADER += "<meta name=Generator >\n"
1411HEADER += "</head>\n"
1412HEADER += "<body lang=EN-US>\n"
1413HEADER += "<div class=WordSection1>\n"
1414HEADER += "<p class=MsoNormal><b><span ><center><font size='4' >"
1415HEADER += "%s</font></center></span></center></b></p>"
1416HEADER += "<p class=MsoNormal>&nbsp;</p>"
1417PARA = "<p class=MsoNormal><font size='4' > %s \n"
1418PARA += "</font></p>"
1419CENTRE = "<p class=MsoNormal><center><font size='4' > %s \n"
1420CENTRE += "</font></center></p>"
1421FEET_1 = \
1422"""
1423<p class=MsoNormal>&nbsp;</p>
1424<br>
1425<p class=MsoNormal><b><span ><center> <font size='4' > Graph
1426</font></span></center></b></p>
1427<p class=MsoNormal>&nbsp;</p>
1428<center>
1429<br><font size='4' >Model Computation</font>
1430<br><font size='4' >Data: "%s"</font><br>
1431"""
1432FEET_2 = \
1433"""
1434<img src="%s" >
1435</img>
1436"""
1437FEET_3 = \
1438"""
1439</center>
1440</div>
1441</body>
1442</html>
1443"""
1444ELINE = "<p class=MsoNormal>&nbsp;</p>"
Note: See TracBrowser for help on using the repository browser.