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

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

Save states with 'Customized Models' category now load as 'Plugin Models' and created constants for both strings.

  • 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.formfactorcombobox == '':
373            FIRST_FORM = {
374                'Shapes' : 'BCCrystalModel',
375                'Uncategorized' : 'LineModel',
376                'StructureFactor' : 'HardsphereStructure',
377                'Ellipsoid' : 'core_shell_ellipsoid',
378                'Lamellae' : 'lamellar',
379                'Paracrystal' : 'bcc_paracrystal',
380                'Parallelepiped' : 'core_shell_parallelepiped',
381                'Shape Independent' : 'be_polyelectrolyte',
382                'Sphere' : 'adsorbed_layer',
383                'Structure Factor' : 'hardsphere',
384                CUSTOM_MODEL_OLD : '',
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_max  : %s\n" % str(self.dx_max)
564        rep += "dx_min : %s\n" % str(self.dx_min)
565        rep += "dxl  : %s\n" % str(self.dxl)
566        rep += "dxw : %s\n" % str(self.dxw)
567        rep += "model  : %s\n\n" % str(self.model)
568        temp_parameters = []
569        temp_fittable_param = []
570        if self.data.__class__.__name__ == "Data2D":
571            self.is_2D = True
572        else:
573            self.is_2D = False
574        if self.data is not None:
575            if not self.is_2D:
576                for item in self.parameters:
577                    if item not in self.orientation_params:
578                        temp_parameters.append(item)
579                for item in self.fittable_param:
580                    if item not in self.orientation_params_disp:
581                        temp_fittable_param.append(item)
582            else:
583                temp_parameters = self.parameters
584                temp_fittable_param = self.fittable_param
585
586            rep += "number parameters(self.parameters): %s\n" % \
587                   len(temp_parameters)
588            rep = self._repr_helper(list=temp_parameters, rep=rep)
589            rep += "number str_parameters(self.str_parameters): %s\n" % \
590                   len(self.str_parameters)
591            rep = self._repr_helper(list=self.str_parameters, rep=rep)
592            rep += "number fittable_param(self.fittable_param): %s\n" % \
593                   len(temp_fittable_param)
594            rep = self._repr_helper(list=temp_fittable_param, rep=rep)
595        return rep
596
597    def set_report_string(self):
598        """
599        Get the values (strings) from __str__ for report
600        """
601        # Dictionary of the report strings
602        repo_time = ""
603        model_name = ""
604        title = ""
605        title_name = ""
606        file_name = ""
607        param_string = ""
608        paramval_string = ""
609        chi2_string = ""
610        q_range = ""
611        strings = self.__repr__()
612        lines = strings.split('\n')
613
614        # get all string values from __str__()
615        for line in lines:
616            value = ""
617            content = line.split(":")
618            name = content[0]
619            try:
620                value = content[1]
621            except Exception:
622                msg = "Report string expected 'name: value' but got %r" % line
623                logging.error(msg)
624            if name.count("State created"):
625                repo_time = "" + value
626            if name.count("parameter name"):
627                val_name = value.split(".")
628                if len(val_name) > 1:
629                    if val_name[1].count("width"):
630                        param_string += value + ','
631                    else:
632                        continue
633                else:
634                    param_string += value + ','
635            if name == "value":
636                param_string += value + ','
637            fixed_parameter = False
638            if name == "selected":
639                if value == u' False':
640                    fixed_parameter = True
641            if name == "error value":
642                if fixed_parameter:
643                    param_string += '(fixed),'
644                else:
645                    param_string += value + ','
646            if name == "parameter unit":
647                param_string += value + ':'
648            if name == "Value of Chisqr ":
649                chi2 = ("Chi2/Npts = " + value)
650                chi2_string = CENTRE % chi2
651            if name == "Title":
652                if len(value.strip()) == 0:
653                    continue
654                title = value + " [" + repo_time + "]"
655                title_name = HEADER % title
656            if name == "data ":
657                try:
658                    file_value = ("File name:" + content[2])
659                    file_name = CENTRE % file_value
660                    if len(title) == 0:
661                        title = content[2] + " [" + repo_time + "]"
662                        title_name = HEADER % title
663                except Exception:
664                    msg = "While parsing 'data: ...'\n"
665                    logging.error(msg + traceback.format_exc())
666            if name == "model name ":
667                try:
668                    modelname = "Model name:" + content[1]
669                except:
670                    modelname = "Model name:" + " NAN"
671                model_name = CENTRE % modelname
672
673            if name == "Plotting Range":
674                try:
675                    q_range = content[1] + " = " + content[2] \
676                            + " = " + content[3].split(",")[0]
677                    q_name = ("Q Range:    " + q_range)
678                    q_range = CENTRE % q_name
679                except Exception:
680                    msg = "While parsing 'Plotting Range: ...'\n"
681                    logging.error(msg + traceback.format_exc())
682        paramval = ""
683        for lines in param_string.split(":"):
684            line = lines.split(",")
685            if len(lines) > 0:
686                param = line[0]
687                param += " = " + line[1]
688                if len(line[2].split()) > 0 and not line[2].count("None"):
689                    param += " +- " + line[2]
690                if len(line[3].split()) > 0 and not line[3].count("None"):
691                    param += " " + line[3]
692                if not paramval.count(param):
693                    paramval += param + "\n"
694                    paramval_string += CENTRE % param + "\n"
695
696        text_string = "\n\n\n%s\n\n%s\n%s\n%s\n\n%s" % \
697                      (title, file, q_name, chi2, paramval)
698
699        title_name = self._check_html_format(title_name)
700        file_name = self._check_html_format(file_name)
701        title = self._check_html_format(title)
702
703        html_string = title_name + "\n" + file_name + \
704                                   "\n" + model_name + \
705                                   "\n" + q_range + \
706                                   "\n" + chi2_string + \
707                                   "\n" + ELINE + \
708                                   "\n" + paramval_string + \
709                                   "\n" + ELINE + \
710                                   "\n" + FEET_1 % title + \
711                                   "\n" + FEET_2
712
713        return html_string, text_string, title
714
715    def _check_html_format(self, name):
716        """
717        Check string '%' for html format
718        """
719        if name.count('%'):
720            name = name.replace('%', '&#37')
721
722        return name
723
724    def report(self, figs=None, canvases=None):
725        """
726        Invoke report dialog panel
727
728        : param figs: list of pylab figures [list]
729        """
730        from sas.sasgui.perspectives.fitting.report_dialog import ReportDialog
731        # get the strings for report
732        html_str, text_str, title = self.set_report_string()
733        # Allow 2 figures to append
734        if len(figs) == 1:
735            add_str = FEET_3
736        elif len(figs) == 2:
737            add_str = ELINE
738            add_str += FEET_2 % ("%s")
739            add_str += ELINE
740            add_str += FEET_3
741        elif len(figs) > 2:
742            add_str = ELINE
743            add_str += FEET_2 % ("%s")
744            add_str += ELINE
745            add_str += FEET_2 % ("%s")
746            add_str += ELINE
747            add_str += FEET_3
748        else:
749            add_str = ""
750
751        # final report html strings
752        report_str = html_str % ("%s") + add_str
753
754        # make plot image
755        images = self.set_plot_state(figs, canvases)
756        report_list = [report_str, text_str, images]
757        dialog = ReportDialog(report_list, None, wx.ID_ANY, "")
758        dialog.Show()
759
760    def _to_xml_helper(self, thelist, element, newdoc):
761        """
762        Helper method to create xml file for saving state
763        """
764        for item in thelist:
765            sub_element = newdoc.createElement('parameter')
766            sub_element.setAttribute('name', str(item[1]))
767            sub_element.setAttribute('value', str(item[2]))
768            sub_element.setAttribute('selected_to_fit', str(item[0]))
769            sub_element.setAttribute('error_displayed', str(item[4][0]))
770            sub_element.setAttribute('error_value', str(item[4][1]))
771            sub_element.setAttribute('minimum_displayed', str(item[5][0]))
772            sub_element.setAttribute('minimum_value', str(item[5][1]))
773            sub_element.setAttribute('maximum_displayed', str(item[6][0]))
774            sub_element.setAttribute('maximum_value', str(item[6][1]))
775            sub_element.setAttribute('unit', str(item[7]))
776            element.appendChild(sub_element)
777
778    def to_xml(self, file="fitting_state.fitv", doc=None,
779               entry_node=None, batch_fit_state=None):
780        """
781        Writes the state of the fit panel to file, as XML.
782
783        Compatible with standalone writing, or appending to an
784        already existing XML document. In that case, the XML document is
785        required. An optional entry node in the XML document may also be given.
786
787        :param file: file to write to
788        :param doc: XML document object [optional]
789        :param entry_node: XML node within the XML document at which we
790                           will append the data [optional]
791        :param batch_fit_state: simultaneous fit state
792        """
793        from xml.dom.minidom import getDOMImplementation
794
795        # Check whether we have to write a standalone XML file
796        if doc is None:
797            impl = getDOMImplementation()
798            doc_type = impl.createDocumentType(FITTING_NODE_NAME, "1.0", "1.0")
799            newdoc = impl.createDocument(None, FITTING_NODE_NAME, doc_type)
800            top_element = newdoc.documentElement
801        else:
802            # We are appending to an existing document
803            newdoc = doc
804            try:
805                top_element = newdoc.createElement(FITTING_NODE_NAME)
806            except:
807                string = etree.tostring(doc, pretty_print=True)
808                newdoc = parseString(string)
809                top_element = newdoc.createElement(FITTING_NODE_NAME)
810            if entry_node is None:
811                newdoc.documentElement.appendChild(top_element)
812            else:
813                try:
814                    entry_node.appendChild(top_element)
815                except:
816                    node_name = entry_node.tag
817                    node_list = newdoc.getElementsByTagName(node_name)
818                    entry_node = node_list.item(0)
819                    entry_node.appendChild(top_element)
820
821        attr = newdoc.createAttribute("version")
822        import sasview
823        attr.nodeValue = sasview.__version__
824        # attr.nodeValue = '1.0'
825        top_element.setAttributeNode(attr)
826
827        # File name
828        element = newdoc.createElement("filename")
829        if self.file is not None:
830            element.appendChild(newdoc.createTextNode(str(self.file)))
831        else:
832            element.appendChild(newdoc.createTextNode(str(file)))
833        top_element.appendChild(element)
834
835        element = newdoc.createElement("timestamp")
836        element.appendChild(newdoc.createTextNode(time.ctime(self.timestamp)))
837        attr = newdoc.createAttribute("epoch")
838        attr.nodeValue = str(self.timestamp)
839        element.setAttributeNode(attr)
840        top_element.appendChild(element)
841
842        # Inputs
843        inputs = newdoc.createElement("Attributes")
844        top_element.appendChild(inputs)
845
846        if self.data is not None and hasattr(self.data, "group_id"):
847            self.data_group_id = self.data.group_id
848        if self.data is not None and hasattr(self.data, "is_data"):
849            self.is_data = self.data.is_data
850        if self.data is not None:
851            self.data_name = self.data.name
852        if self.data is not None and hasattr(self.data, "id"):
853            self.data_id = self.data.id
854
855        for item in LIST_OF_DATA_ATTRIBUTES:
856            element = newdoc.createElement(item[0])
857            element.setAttribute(item[0], str(getattr(self, item[1])))
858            inputs.appendChild(element)
859
860        for item in LIST_OF_STATE_ATTRIBUTES:
861            element = newdoc.createElement(item[0])
862            element.setAttribute(item[0], str(getattr(self, item[1])))
863            inputs.appendChild(element)
864
865        # For self.values ={ disp_param_name: [vals,...],...}
866        # and for self.weights ={ disp_param_name: [weights,...],...}
867        for item in LIST_OF_MODEL_ATTRIBUTES:
868            element = newdoc.createElement(item[0])
869            value_list = getattr(self, item[1])
870            for key, value in value_list.iteritems():
871                sub_element = newdoc.createElement(key)
872                sub_element.setAttribute('name', str(key))
873                for val in value:
874                    sub_element.appendChild(newdoc.createTextNode(str(val)))
875
876                element.appendChild(sub_element)
877            inputs.appendChild(element)
878
879        # Create doc for the dictionary of self._disp_obj_dic
880        for tagname, varname, tagtype in DISPERSION_LIST:
881            element = newdoc.createElement(tagname)
882            value_list = getattr(self, varname)
883            for key, value in value_list.iteritems():
884                sub_element = newdoc.createElement(key)
885                sub_element.setAttribute('name', str(key))
886                sub_element.setAttribute('value', str(value))
887                element.appendChild(sub_element)
888            inputs.appendChild(element)
889
890        for item in LIST_OF_STATE_PARAMETERS:
891            element = newdoc.createElement(item[0])
892            self._to_xml_helper(thelist=getattr(self, item[1]),
893                                element=element, newdoc=newdoc)
894            inputs.appendChild(element)
895
896        # Combined and Simultaneous Fit Parameters
897        if batch_fit_state is not None:
898            batch_combo = newdoc.createElement('simultaneous_fit')
899            top_element.appendChild(batch_combo)
900
901            # Simultaneous Fit Number For Linking Later
902            element = newdoc.createElement('sim_fit_number')
903            element.setAttribute('fit_number', str(batch_fit_state.fit_page_no))
904            batch_combo.appendChild(element)
905
906            # Save constraints
907            constraints = newdoc.createElement('constraints')
908            batch_combo.appendChild(constraints)
909            for constraint in batch_fit_state.constraints_list:
910                if constraint.model_cbox.GetValue() != "":
911                    # model_cbox, param_cbox, egal_txt, constraint,
912                    # btRemove, sizer
913                    doc_cons = newdoc.createElement('constraint')
914                    doc_cons.setAttribute('model_cbox',
915                                          str(constraint.model_cbox.GetValue()))
916                    doc_cons.setAttribute('param_cbox',
917                                          str(constraint.param_cbox.GetValue()))
918                    doc_cons.setAttribute('egal_txt',
919                                          str(constraint.egal_txt.GetLabel()))
920                    doc_cons.setAttribute('constraint',
921                                          str(constraint.constraint.GetValue()))
922                    constraints.appendChild(doc_cons)
923
924            # Save all models
925            models = newdoc.createElement('model_list')
926            batch_combo.appendChild(models)
927            for model in batch_fit_state.model_list:
928                doc_model = newdoc.createElement('model_list_item')
929                doc_model.setAttribute('checked', str(model[0].GetValue()))
930                keys = model[1].keys()
931                doc_model.setAttribute('name', str(keys[0]))
932                values = model[1].get(keys[0])
933                doc_model.setAttribute('fit_number', str(model[2]))
934                doc_model.setAttribute('fit_page_source', str(model[3]))
935                doc_model.setAttribute('model_name', str(values.model.id))
936                models.appendChild(doc_model)
937
938            # Select All Checkbox
939            element = newdoc.createElement('select_all')
940            if batch_fit_state.select_all:
941                element.setAttribute('checked', 'True')
942            else:
943                element.setAttribute('checked', 'False')
944            batch_combo.appendChild(element)
945
946        # Save the file
947        if doc is None:
948            fd = open(file, 'w')
949            fd.write(newdoc.toprettyxml())
950            fd.close()
951            return None
952        else:
953            return newdoc
954
955    def _from_xml_helper(self, node, list):
956        """
957        Helper function to write state to xml
958        """
959        for item in node:
960            try:
961                name = item.get('name')
962            except:
963                name = None
964            try:
965                value = item.get('value')
966            except:
967                value = None
968            try:
969                selected_to_fit = (item.get('selected_to_fit') == "True")
970            except:
971                selected_to_fit = None
972            try:
973                error_displayed = (item.get('error_displayed') == "True")
974            except:
975                error_displayed = None
976            try:
977                error_value = item.get('error_value')
978            except:
979                error_value = None
980            try:
981                minimum_displayed = (item.get('minimum_displayed') == "True")
982            except:
983                minimum_displayed = None
984            try:
985                minimum_value = item.get('minimum_value')
986            except:
987                minimum_value = None
988            try:
989                maximum_displayed = (item.get('maximum_displayed') == "True")
990            except:
991                maximum_displayed = None
992            try:
993                maximum_value = item.get('maximum_value')
994            except:
995                maximum_value = None
996            try:
997                unit = item.get('unit')
998            except:
999                unit = None
1000            list.append([selected_to_fit, name, value, "+/-",
1001                         [error_displayed, error_value],
1002                         [minimum_displayed, minimum_value],
1003                         [maximum_displayed, maximum_value], unit])
1004
1005    def from_xml(self, file=None, node=None):
1006        """
1007        Load fitting state from a file
1008
1009        :param file: .fitv file
1010        :param node: node of a XML document to read from
1011        """
1012        if file is not None:
1013            msg = "PageState no longer supports non-CanSAS"
1014            msg += " format for fitting files"
1015            raise RuntimeError, msg
1016
1017        if node.get('version'):
1018            # Get the version for model conversion purposes
1019            self.version = tuple(int(e) for e in
1020                                 str.split(node.get('version'), "."))
1021            # The tuple must be at least 3 items long
1022            while len(self.version) < 3:
1023                ver_list = list(self.version)
1024                ver_list.append(0)
1025                self.version = tuple(ver_list)
1026
1027            # Get file name
1028            entry = get_content('ns:filename', node)
1029            if entry is not None:
1030                self.file = entry.text.strip()
1031
1032            # Get time stamp
1033            entry = get_content('ns:timestamp', node)
1034            if entry is not None and entry.get('epoch'):
1035                try:
1036                    self.timestamp = float(entry.get('epoch'))
1037                except:
1038                    msg = "PageState.fromXML: Could not"
1039                    msg += " read timestamp\n %s" % sys.exc_value
1040                    logging.error(msg)
1041
1042            if entry is not None:
1043                # Parse fitting attributes
1044                entry = get_content('ns:Attributes', node)
1045                for item in LIST_OF_DATA_ATTRIBUTES:
1046                    node = get_content('ns:%s' % item[0], entry)
1047                    setattr(self, item[0], parse_entry_helper(node, item))
1048
1049                for item in LIST_OF_STATE_ATTRIBUTES:
1050                    node = get_content('ns:%s' % item[0], entry)
1051                    setattr(self, item[0], parse_entry_helper(node, item))
1052
1053                for item in LIST_OF_STATE_PARAMETERS:
1054                    node = get_content("ns:%s" % item[0], entry)
1055                    self._from_xml_helper(node=node,
1056                                          list=getattr(self, item[1]))
1057
1058                # Recover _disp_obj_dict from xml file
1059                self._disp_obj_dict = {}
1060                for tagname, varname, tagtype in DISPERSION_LIST:
1061                    node = get_content("ns:%s" % tagname, entry)
1062                    for attr in node:
1063                        parameter = str(attr.get('name'))
1064                        value = attr.get('value')
1065                        if value.startswith("<"):
1066                            try:
1067                                # <path.to.NamedDistribution object/instance...>
1068                                cls_name = value[1:].split()[0].split('.')[-1]
1069                                cls = getattr(sasmodels.weights, cls_name)
1070                                value = cls.type
1071                            except Exception:
1072                                base = "unable to load distribution %r for %s"
1073                                logging.error(base % (value, parameter))
1074                                continue
1075                        _disp_obj_dict = getattr(self, varname)
1076                        _disp_obj_dict[parameter] = value
1077
1078                # get self.values and self.weights dic. if exists
1079                for tagname, varname in LIST_OF_MODEL_ATTRIBUTES:
1080                    node = get_content("ns:%s" % tagname, entry)
1081                    dic = {}
1082                    value_list = []
1083                    for par in node:
1084                        name = par.get('name')
1085                        values = par.text.split()
1086                        # Get lines only with numbers
1087                        for line in values:
1088                            try:
1089                                val = float(line)
1090                                value_list.append(val)
1091                            except Exception:
1092                                # pass if line is empty (it happens)
1093                                msg = ("Error reading %r from %s %s\n"
1094                                       % (line, tagname, name))
1095                                logging.error(msg + traceback.format_exc())
1096                        dic[name] = numpy.array(value_list)
1097                    setattr(self, varname, dic)
1098
1099    def set_plot_state(self, figs, canvases):
1100        """
1101        Build image state that wx.html understand
1102        by plotting, putting it into wx.FileSystem image object
1103
1104        """
1105        images = []
1106
1107        # Reset memory
1108        self.imgRAM = None
1109        wx.MemoryFSHandler()
1110
1111        # For no figures in the list, prepare empty plot
1112        if figs is None or len(figs) == 0:
1113            figs = [None]
1114
1115        # Loop over the list of figures
1116        # use wx.MemoryFSHandler
1117        self.imgRAM = wx.MemoryFSHandler()
1118        for fig in figs:
1119            if fig is not None:
1120                ind = figs.index(fig)
1121                canvas = canvases[ind]
1122
1123            # store the image in wx.FileSystem Object
1124            wx.FileSystem.AddHandler(wx.MemoryFSHandler())
1125
1126            # index of the fig
1127            ind = figs.index(fig)
1128
1129            # AddFile, image can be retrieved with 'memory:filename'
1130            self.imgRAM.AddFile('img_fit%s.png' % ind,
1131                                canvas.bitmap, wx.BITMAP_TYPE_PNG)
1132
1133            # append figs
1134            images.append(fig)
1135
1136        return images
1137
1138
1139class Reader(CansasReader):
1140    """
1141    Class to load a .fitv fitting file
1142    """
1143    # File type
1144    type_name = "Fitting"
1145
1146    # Wildcards
1147    type = ["Fitting files (*.fitv)|*.fitv"
1148            "SASView file (*.svs)|*.svs"]
1149    # List of allowed extensions
1150    ext = ['.fitv', '.FITV', '.svs', 'SVS']
1151
1152    def __init__(self, call_back=None, cansas=True):
1153        CansasReader.__init__(self)
1154        """
1155        Initialize the call-back method to be called
1156        after we load a file
1157
1158        :param call_back: call-back method
1159        :param cansas:  True = files will be written/read in CanSAS format
1160                        False = write CanSAS format
1161
1162        """
1163        # Call back method to be executed after a file is read
1164        self.call_back = call_back
1165        # CanSAS format flag
1166        self.cansas = cansas
1167        self.state = None
1168        # batch fitting params for saving
1169        self.batchfit_params = []
1170
1171    def get_state(self):
1172        return self.state
1173
1174    def read(self, path):
1175        """
1176        Load a new P(r) inversion state from file
1177
1178        :param path: file path
1179
1180        """
1181        if self.cansas:
1182            return self._read_cansas(path)
1183
1184    def _parse_state(self, entry):
1185        """
1186        Read a fit result from an XML node
1187
1188        :param entry: XML node to read from
1189        :return: PageState object
1190        """
1191        # Create an empty state
1192        state = None
1193        # Locate the P(r) node
1194        try:
1195            nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME,
1196                                namespaces={'ns': CANSAS_NS})
1197            if nodes:
1198                # Create an empty state
1199                state = PageState()
1200                state.from_xml(node=nodes[0])
1201
1202        except:
1203            logging.info("XML document does not contain fitting information.\n"
1204                         + traceback.format_exc())
1205
1206        return state
1207
1208    def _parse_simfit_state(self, entry):
1209        """
1210        Parses the saved data for a simultaneous fit
1211        :param entry: XML object to read from
1212        :return: XML object for a simultaneous fit or None
1213        """
1214        nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME,
1215                            namespaces={'ns': CANSAS_NS})
1216        if nodes:
1217            simfitstate = nodes[0].xpath('ns:simultaneous_fit',
1218                                         namespaces={'ns': CANSAS_NS})
1219            if simfitstate:
1220                from simfitpage import SimFitPageState
1221                sim_fit_state = SimFitPageState()
1222                simfitstate_0 = simfitstate[0]
1223                all = simfitstate_0.xpath('ns:select_all',
1224                                          namespaces={'ns': CANSAS_NS})
1225                atts = all[0].attrib
1226                checked = atts.get('checked')
1227                sim_fit_state.select_all = bool(checked)
1228                model_list = simfitstate_0.xpath('ns:model_list',
1229                                                 namespaces={'ns': CANSAS_NS})
1230                model_list_items = model_list[0].xpath('ns:model_list_item',
1231                                                       namespaces={'ns':
1232                                                                    CANSAS_NS})
1233                for model in model_list_items:
1234                    attrs = model.attrib
1235                    sim_fit_state.model_list.append(attrs)
1236
1237                constraints = simfitstate_0.xpath('ns:constraints',
1238                                                namespaces={'ns': CANSAS_NS})
1239                constraint_list = constraints[0].xpath('ns:constraint',
1240                                               namespaces={'ns': CANSAS_NS})
1241                for constraint in constraint_list:
1242                    attrs = constraint.attrib
1243                    sim_fit_state.constraints_list.append(attrs)
1244
1245                return sim_fit_state
1246            else:
1247                return None
1248
1249    def _parse_save_state_entry(self, dom):
1250        """
1251        Parse a SASentry
1252
1253        :param node: SASentry node
1254
1255        :return: Data1D/Data2D object
1256
1257        """
1258        node = dom.xpath('ns:data_class', namespaces={'ns': CANSAS_NS})
1259        return_value, _ = self._parse_entry(dom)
1260        return return_value, _
1261
1262    def _read_cansas(self, path):
1263        """
1264        Load data and fitting information from a CanSAS XML file.
1265
1266        :param path: file path
1267        :return: Data1D object if a single SASentry was found,
1268                    or a list of Data1D objects if multiple entries were found,
1269                    or None of nothing was found
1270        :raise RuntimeError: when the file can't be opened
1271        :raise ValueError: when the length of the data vectors are inconsistent
1272        """
1273        output = []
1274        simfitstate = None
1275        basename = os.path.basename(path)
1276        root, extension = os.path.splitext(basename)
1277        ext = extension.lower()
1278        try:
1279            if os.path.isfile(path):
1280                if ext in self.ext or ext == '.xml':
1281                    tree = etree.parse(path, parser=etree.ETCompatXMLParser())
1282                    # Check the format version number
1283                    # Specifying the namespace will take care of the file
1284                    # format version
1285                    root = tree.getroot()
1286                    entry_list = root.xpath('ns:SASentry',
1287                                            namespaces={'ns': CANSAS_NS})
1288                    for entry in entry_list:
1289                        try:
1290                            sas_entry, _ = self._parse_save_state_entry(entry)
1291                        except:
1292                            raise
1293                        fitstate = self._parse_state(entry)
1294
1295                        # state could be None when .svs file is loaded
1296                        # in this case, skip appending to output
1297                        if fitstate is not None:
1298                            sas_entry.meta_data['fitstate'] = fitstate
1299                            sas_entry.filename = fitstate.file
1300                            output.append(sas_entry)
1301
1302            else:
1303                self.call_back(format=ext)
1304                raise RuntimeError, "%s is not a file" % path
1305
1306            # Return output consistent with the loader's api
1307            if len(output) == 0:
1308                self.call_back(state=None, datainfo=None, format=ext)
1309                return None
1310            else:
1311                for ind in range(len(output)):
1312                    # Call back to post the new state
1313                    state = output[ind].meta_data['fitstate']
1314                    t = time.localtime(state.timestamp)
1315                    time_str = time.strftime("%b %d %H:%M", t)
1316                    # Check that no time stamp is already appended
1317                    max_char = state.file.find("[")
1318                    if max_char < 0:
1319                        max_char = len(state.file)
1320                    original_fname = state.file[0:max_char]
1321                    state.file = original_fname + ' [' + time_str + ']'
1322
1323                    if state is not None and state.is_data is not None:
1324                        output[ind].is_data = state.is_data
1325
1326                    output[ind].filename = state.file
1327                    state.data = output[ind]
1328                    state.data.name = output[ind].filename  # state.data_name
1329                    state.data.id = state.data_id
1330                    if state.is_data is not None:
1331                        state.data.is_data = state.is_data
1332                    if output[ind].run_name is not None\
1333                         and len(output[ind].run_name) != 0:
1334                        if isinstance(output[ind].run_name, dict):
1335                            name = output[ind].run_name.keys()[0]
1336                        else:
1337                            name = output[ind].run_name
1338                    else:
1339                        name = original_fname
1340                    state.data.group_id = name
1341                    state.version = fitstate.version
1342                    # store state in fitting
1343                    self.call_back(state=state,
1344                                   datainfo=output[ind], format=ext)
1345                    self.state = state
1346                simfitstate = self._parse_simfit_state(entry)
1347                if simfitstate is not None:
1348                    self.call_back(state=simfitstate)
1349
1350                return output
1351        except:
1352            self.call_back(format=ext)
1353            raise
1354
1355    def write(self, filename, datainfo=None, fitstate=None):
1356        """
1357        Write the content of a Data1D as a CanSAS XML file only for standalone
1358
1359        :param filename: name of the file to write
1360        :param datainfo: Data1D object
1361        :param fitstate: PageState object
1362
1363        """
1364        # Sanity check
1365        if self.cansas:
1366            # Add fitting information to the XML document
1367            doc = self.write_toXML(datainfo, fitstate)
1368            # Write the XML document
1369        else:
1370            doc = fitstate.to_xml(file=filename)
1371
1372        # Save the document no matter the type
1373        fd = open(filename, 'w')
1374        fd.write(doc.toprettyxml())
1375        fd.close()
1376
1377    def write_toXML(self, datainfo=None, state=None, batchfit=None):
1378        """
1379        Write toXML, a helper for write(),
1380        could be used by guimanager._on_save()
1381
1382        : return: xml doc
1383        """
1384
1385        self.batchfit_params = batchfit
1386        if state.data is None or not state.data.is_data:
1387            return None
1388        # make sure title and data run are filled.
1389        if state.data.title is None or state.data.title == '':
1390            state.data.title = state.data.name
1391        if state.data.run_name is None or state.data.run_name == {}:
1392            state.data.run = [str(state.data.name)]
1393            state.data.run_name[0] = state.data.name
1394
1395        data = state.data
1396        doc, sasentry = self._to_xml_doc(data)
1397
1398        if state is not None:
1399            doc = state.to_xml(doc=doc, file=data.filename, entry_node=sasentry,
1400                               batch_fit_state=self.batchfit_params)
1401
1402        return doc
1403
1404# Simple html report templet
1405HEADER = "<html>\n"
1406HEADER += "<head>\n"
1407HEADER += "<meta http-equiv=Content-Type content='text/html; "
1408HEADER += "charset=windows-1252'> \n"
1409HEADER += "<meta name=Generator >\n"
1410HEADER += "</head>\n"
1411HEADER += "<body lang=EN-US>\n"
1412HEADER += "<div class=WordSection1>\n"
1413HEADER += "<p class=MsoNormal><b><span ><center><font size='4' >"
1414HEADER += "%s</font></center></span></center></b></p>"
1415HEADER += "<p class=MsoNormal>&nbsp;</p>"
1416PARA = "<p class=MsoNormal><font size='4' > %s \n"
1417PARA += "</font></p>"
1418CENTRE = "<p class=MsoNormal><center><font size='4' > %s \n"
1419CENTRE += "</font></center></p>"
1420FEET_1 = \
1421"""
1422<p class=MsoNormal>&nbsp;</p>
1423<br>
1424<p class=MsoNormal><b><span ><center> <font size='4' > Graph
1425</font></span></center></b></p>
1426<p class=MsoNormal>&nbsp;</p>
1427<center>
1428<br><font size='4' >Model Computation</font>
1429<br><font size='4' >Data: "%s"</font><br>
1430"""
1431FEET_2 = \
1432"""
1433<img src="%s" >
1434</img>
1435"""
1436FEET_3 = \
1437"""
1438</center>
1439</div>
1440</body>
1441</html>
1442"""
1443ELINE = "<p class=MsoNormal>&nbsp;</p>"
Note: See TracBrowser for help on using the repository browser.