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

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

Add SasView? version to report files. refs #1179

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