source: sasview/src/sas/sascalc/fit/pagestate.py @ 47c44f0

magnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249unittest-saveload
Last change on this file since 47c44f0 was 47c44f0, checked in by wojciech, 6 years ago

Reverting Windows page layout

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