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

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 984f3fc was d5aff7f, checked in by krzywon, 7 years ago

Small code cleanup to only fetch dx_min from save state a single time.

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