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

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

#189: Can now open the simultaneous and constrained fit panel through a save state, but the constraint loading is still broken.

  • Property mode set to 100644
File size: 72.8 KB
RevLine 
[f32d144]1"""
2    Class that holds a fit page state
3"""
[a95ae9a]4# TODO: Refactor code so we don't need to use getattr/setattr
[5062bbf]5################################################################################
[a95ae9a]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.
[5062bbf]9#
[a95ae9a]10# See the license text in license.txt
[5062bbf]11#
[a95ae9a]12# copyright 2009, University of Tennessee
[5062bbf]13################################################################################
[11a7e11]14import time
15import os
16import sys
[abcbe09]17import wx
[6f023e8]18import copy
[11a7e11]19import logging
[df7ed14]20import numpy
[7673ecd]21import traceback
[c77d859]22
[35b556d]23import xml.dom.minidom
[ac5b69d]24from xml.dom.minidom import parseString
[11a7e11]25from lxml import etree
26
[6c382da]27import sasmodels.weights
28
[b699768]29import sas.sascalc.dataloader
30from sas.sascalc.dataloader.readers.cansas_reader import Reader as CansasReader
31from sas.sascalc.dataloader.readers.cansas_reader import get_content, write_node
[a95ae9a]32from sas.sascalc.dataloader.data_info import Data2D, Collimation, Detector
33from sas.sascalc.dataloader.data_info import Process, Aperture
34# Information to read/write state as xml
[61cada5]35FITTING_NODE_NAME = 'fitting_plug_in'
36CANSAS_NS = "cansas1d/1.0"
37
[b1e609c]38LIST_OF_DATA_ATTRIBUTES = [["is_data", "is_data", "bool"],
39                           ["group_id", "data_group_id", "string"],
40                           ["data_name", "data_name", "string"],
41                           ["data_id", "data_id", "string"],
42                           ["name", "name", "string"],
43                           ["data_name", "data_name", "string"]]
[acf8e4a5]44LIST_OF_STATE_ATTRIBUTES = [["qmin", "qmin", "float"],
[b1e609c]45                            ["qmax", "qmax", "float"],
46                            ["npts", "npts", "float"],
47                            ["categorycombobox", "categorycombobox", "string"],
48                            ["formfactorcombobox", "formfactorcombobox", "string"],
49                            ["structurecombobox", "structurecombobox", "string"],
50                            ["multi_factor", "multi_factor", "float"],
51                            ["magnetic_on", "magnetic_on", "bool"],
52                            ["enable_smearer", "enable_smearer", "bool"],
53                            ["disable_smearer", "disable_smearer", "bool"],
54                            ["pinhole_smearer", "pinhole_smearer", "bool"],
55                            ["slit_smearer", "slit_smearer", "bool"],
56                            ["enable_disp", "enable_disp", "bool"],
57                            ["disable_disp", "disable_disp", "bool"],
58                            ["dI_noweight", "dI_noweight", "bool"],
59                            ["dI_didata", "dI_didata", "bool"],
60                            ["dI_sqrdata", "dI_sqrdata", "bool"],
61                            ["dI_idata", "dI_idata", "bool"],
62                            ["enable2D", "enable2D", "bool"],
63                            ["cb1", "cb1", "bool"],
64                            ["tcChi", "tcChi", "float"],
65                            ["smearer", "smearer", "float"],
66                            ["smear_type", "smear_type", "string"],
67                            ["dq_l", "dq_l", "string"],
68                            ["dq_r", "dq_r", "string"],
69                            ["dx_max", "dx_max", "float"],
70                            ["dx_min", "dx_min", "float"],
71                            ["dxl", "dxl", "float"],
72                            ["dxw", "dxw", "float"]]
73
74LIST_OF_MODEL_ATTRIBUTES = [["values", "values"],
[11a7e11]75                            ["weights", "weights"]]
76
[b1e609c]77DISPERSION_LIST = [["disp_obj_dict", "_disp_obj_dict", "string"]]
[2296316]78
[b1e609c]79LIST_OF_STATE_PARAMETERS = [["parameters", "parameters"],
[f32d144]80                            ["str_parameters", "str_parameters"],
[61cada5]81                            ["orientation_parameters", "orientation_params"],
82                            ["dispersity_parameters", "orientation_params_disp"],
[f32d144]83                            ["fixed_param", "fixed_param"],
84                            ["fittable_param", "fittable_param"]]
[b1e609c]85LIST_OF_DATA_2D_ATTR = [["xmin", "xmin", "float"],
[f32d144]86                        ["xmax", "xmax", "float"],
87                        ["ymin", "ymin", "float"],
88                        ["ymax", "ymax", "float"],
89                        ["_xaxis", "_xaxis", "string"],
[df7ed14]90                        ["_xunit", "_xunit", "string"],
[f32d144]91                        ["_yaxis", "_yaxis", "string"],
92                        ["_yunit", "_yunit", "string"],
93                        ["_zaxis", "_zaxis", "string"],
94                        ["_zunit", "_zunit", "string"]]
[b1e609c]95LIST_OF_DATA_2D_VALUES = [["qx_data", "qx_data", "float"],
96                          ["qy_data", "qy_data", "float"],
97                          ["dqx_data", "dqx_data", "float"],
98                          ["dqy_data", "dqy_data", "float"],
99                          ["data", "data", "float"],
100                          ["q_data", "q_data", "float"],
101                          ["err_data", "err_data", "float"],
102                          ["mask", "mask", "bool"]]
[61cada5]103
[f32d144]104
105def parse_entry_helper(node, item):
[0b12abb5]106    """
107    Create a numpy list from value extrated from the node
[2f4b430]108
[0b12abb5]109    :param node: node from each the value is stored
110    :param item: list name of three strings.the two first are name of data
[f32d144]111        attribute and the third one is the type of the value of that
[0b12abb5]112        attribute. type can be string, float, bool, etc.
[2f4b430]113
[0b12abb5]114    : return: numpy array
115    """
116    if node is not None:
117        if item[2] == "string":
118            return str(node.get(item[0]).strip())
119        elif item[2] == "bool":
120            try:
[e3d1423]121                return node.get(item[0]).strip() == "True"
[0b12abb5]122            except:
123                return None
124        else:
125            try:
126                return float(node.get(item[0]))
127            except:
128                return None
[2f4b430]129
130
[cfc0913]131class PageState(object):
[c77d859]132    """
[5062bbf]133    Contains information to reconstruct a page of the fitpanel.
[c77d859]134    """
[61cada5]135    def __init__(self, parent=None, model=None, data=None):
[f32d144]136        """
[5062bbf]137        Initialize the current state
[2f4b430]138
[5062bbf]139        :param model: a selected model within a page
[f32d144]140        :param data:
[2f4b430]141
[c77d859]142        """
[61cada5]143        self.file = None
[a95ae9a]144        # Time of state creation
[11a7e11]145        self.timestamp = time.time()
[a95ae9a]146        # Data member to store the dispersion object created
[c77d859]147        self._disp_obj_dict = {}
[a95ae9a]148        # ------------------------
149        # Data used for fitting
[c77d859]150        self.data = data
[2296316]151        # model data
152        self.theory_data = None
[a95ae9a]153        # Is 2D
[2296316]154        self.is_2D = False
155        self.images = None
[2f4b430]156
[a95ae9a]157        # save additional information on data that dataloader.reader
158        # does not read
[61cada5]159        self.is_data = None
160        self.data_name = ""
[c0ff8cc]161
[61cada5]162        if self.data is not None:
163            self.data_name = self.data.name
164        self.data_id = None
165        if self.data is not None and hasattr(self.data, "id"):
166            self.data_id = self.data.id
167        self.data_group_id = None
168        if self.data is not None and hasattr(self.data, "group_id"):
169            self.data_group_id = self.data.group_id
[2f4b430]170
[a95ae9a]171        # reset True change the state of existing button
[61cada5]172        self.reset = False
[2f4b430]173
[c77d859]174        # flag to allow data2D plot
[cfc0913]175        self.enable2D = False
[c77d859]176        # model on which the fit would be performed
177        self.model = model
[93f0a862]178        self.m_name = None
[a95ae9a]179        # list of process done to model
[dce84c0]180        self.process = []
[a95ae9a]181        # fit page manager
[c77d859]182        self.manager = None
[a95ae9a]183        # Store the parent of this panel parent
[c77d859]184        # For this application fitpanel is the parent
[f32d144]185        self.parent = parent
[c77d859]186        # Event_owner is the owner of model event
187        self.event_owner = None
[a95ae9a]188        # page name
[cfc0913]189        self.page_name = ""
[eddb6ec]190        # Contains link between model, all its parameters, and panel organization
[61cada5]191        self.parameters = []
[fb59ed9]192        # String parameter list that can not be fitted
193        self.str_parameters = []
[f32d144]194        # Contains list of parameters that cannot be fitted and reference to
[a95ae9a]195        # panel objects
[61cada5]196        self.fixed_param = []
[f32d144]197        # Contains list of parameters with dispersity and reference to
[a95ae9a]198        # panel objects
[61cada5]199        self.fittable_param = []
[a95ae9a]200        # orientation parameters
[61cada5]201        self.orientation_params = []
[a95ae9a]202        # orientation parameters for gaussian dispersity
[61cada5]203        self.orientation_params_disp = []
[a95ae9a]204        # smearer info
[61cada5]205        self.smearer = None
[7609f1a]206        self.smear_type = None
207        self.dq_l = None
208        self.dq_r = None
[db8fd5b]209        self.dx_max = None
210        self.dx_min = None
211        self.dxl = None
212        self.dxw = None
[a95ae9a]213        # list of dispersion parameters
[f32d144]214        self.disp_list = []
[61cada5]215        if self.model is not None:
[71f0373]216            self.disp_list = self.model.getDispParamList()
[2296316]217
[61cada5]218        self.disp_cb_dict = {}
[2296316]219        self.values = {}
220        self.weights = {}
[2f4b430]221
[a95ae9a]222        # contains link between a model and selected parameters to fit
[61cada5]223        self.param_toFit = []
[a95ae9a]224        # dictionary of model type and model class
[cfc0913]225        self.model_list_box = None
[a95ae9a]226        # save the state of the context menu
[61cada5]227        self.saved_states = {}
[a95ae9a]228        # save selection of combobox
[3b9e023]229        self.formfactorcombobox = None
[ea5fa58]230        self.categorycombobox = None
[f32d144]231        self.structurecombobox = None
[c0ff8cc]232
[a95ae9a]233        # radio box to select type of model
234        # self.shape_rbutton = False
235        # self.shape_indep_rbutton = False
236        # self.struct_rbutton = False
237        # self.plugin_rbutton = False
238        # the indice of the current selection
[b787e68c]239        self.disp_box = 0
[a95ae9a]240        # Qrange
241        # Q range
[61cada5]242        self.qmin = 0.001
243        self.qmax = 0.1
[a95ae9a]244        # reset data range
[0b12abb5]245        self.qmax_x = None
246        self.qmin_x = None
[2f4b430]247
[cfc0913]248        self.npts = None
[61cada5]249        self.name = ""
[4523b68]250        self.multi_factor = None
[318b5bbb]251        self.magnetic_on = False
[a95ae9a]252        # enable smearering state
[cfc0913]253        self.enable_smearer = False
[fc6ea43]254        self.disable_smearer = True
[7609f1a]255        self.pinhole_smearer = False
[f32d144]256        self.slit_smearer = False
[55bb249c]257        # weighting options
258        self.dI_noweight = False
259        self.dI_didata = True
260        self.dI_sqrdata = False
[f32d144]261        self.dI_idata = False
[a95ae9a]262        # disperity selection
[61cada5]263        self.enable_disp = False
264        self.disable_disp = True
[2f4b430]265
[a95ae9a]266        # state of selected all check button
[cfc0913]267        self.cb1 = False
[a95ae9a]268        # store value of chisqr
[61cada5]269        self.tcChi = None
[2f4b430]270
[6f023e8]271    def clone(self):
[61cada5]272        """
[5062bbf]273        Create a new copy of the current object
[61cada5]274        """
275        model = None
276        if self.model is not None:
[6f023e8]277            model = self.model.clone()
[bb70474]278            model.name = self.model.name
[61cada5]279        obj = PageState(self.parent, model=model)
280        obj.file = copy.deepcopy(self.file)
[6f023e8]281        obj.data = copy.deepcopy(self.data)
[61cada5]282        if self.data is not None:
283            self.data_name = self.data.name
284        obj.data_name = self.data_name
285        obj.is_data = self.is_data
[dcf29d7]286        obj.model_list_box = copy.deepcopy(self.model_list_box)
[2f4b430]287
[ea5fa58]288        obj.categorycombobox = self.categorycombobox
[61cada5]289        obj.formfactorcombobox = self.formfactorcombobox
[f32d144]290        obj.structurecombobox = self.structurecombobox
[2f4b430]291
[a95ae9a]292        # obj.shape_rbutton = self.shape_rbutton
293        # obj.shape_indep_rbutton = self.shape_indep_rbutton
294        # obj.struct_rbutton = self.struct_rbutton
295        # obj.plugin_rbutton = self.plugin_rbutton
[2f4b430]296
[dcf29d7]297        obj.manager = self.manager
298        obj.event_owner = self.event_owner
[71f0373]299        obj.disp_list = copy.deepcopy(self.disp_list)
[2f4b430]300
[c477b31]301        obj.enable2D = copy.deepcopy(self.enable2D)
[cfc0913]302        obj.parameters = copy.deepcopy(self.parameters)
[fb59ed9]303        obj.str_parameters = copy.deepcopy(self.str_parameters)
[cfc0913]304        obj.fixed_param = copy.deepcopy(self.fixed_param)
305        obj.fittable_param = copy.deepcopy(self.fittable_param)
[f32d144]306        obj.orientation_params = copy.deepcopy(self.orientation_params)
307        obj.orientation_params_disp = copy.deepcopy(self.orientation_params_disp)
[b787e68c]308        obj.enable_disp = copy.deepcopy(self.enable_disp)
[fc6ea43]309        obj.disable_disp = copy.deepcopy(self.disable_disp)
[0aeabc6]310        obj.tcChi = self.tcChi
[2f4b430]311
[f32d144]312        if len(self._disp_obj_dict) > 0:
313            for k, v in self._disp_obj_dict.iteritems():
314                obj._disp_obj_dict[k] = v
315        if len(self.disp_cb_dict) > 0:
316            for k, v in self.disp_cb_dict.iteritems():
317                obj.disp_cb_dict[k] = v
318        if len(self.values) > 0:
319            for k, v in self.values.iteritems():
[2296316]320                obj.values[k] = v
[f32d144]321        if len(self.weights) > 0:
322            for k, v in self.weights.iteritems():
[2296316]323                obj.weights[k] = v
[b787e68c]324        obj.enable_smearer = copy.deepcopy(self.enable_smearer)
[fc6ea43]325        obj.disable_smearer = copy.deepcopy(self.disable_smearer)
[7609f1a]326        obj.pinhole_smearer = copy.deepcopy(self.pinhole_smearer)
327        obj.slit_smearer = copy.deepcopy(self.slit_smearer)
328        obj.smear_type = copy.deepcopy(self.smear_type)
[55bb249c]329        obj.dI_noweight = copy.deepcopy(self.dI_noweight)
330        obj.dI_didata = copy.deepcopy(self.dI_didata)
331        obj.dI_sqrdata = copy.deepcopy(self.dI_sqrdata)
332        obj.dI_idata = copy.deepcopy(self.dI_idata)
[7609f1a]333        obj.dq_l = copy.deepcopy(self.dq_l)
334        obj.dq_r = copy.deepcopy(self.dq_r)
[db8fd5b]335        obj.dx_max = copy.deepcopy(self.dx_max)
336        obj.dx_min = copy.deepcopy(self.dx_min)
337        obj.dxl = copy.deepcopy(self.dxl)
[2f4b430]338        obj.dxw = copy.deepcopy(self.dxw)
[b787e68c]339        obj.disp_box = copy.deepcopy(self.disp_box)
340        obj.qmin = copy.deepcopy(self.qmin)
341        obj.qmax = copy.deepcopy(self.qmax)
[c0ff8cc]342        obj.multi_factor = self.multi_factor
[318b5bbb]343        obj.magnetic_on = self.magnetic_on
[f32d144]344        obj.npts = copy.deepcopy(self.npts)
[b787e68c]345        obj.cb1 = copy.deepcopy(self.cb1)
[3370922]346        obj.smearer = copy.deepcopy(self.smearer)
[2f4b430]347
[a074145]348        for name, state in self.saved_states.iteritems():
349            copy_name = copy.deepcopy(name)
350            copy_state = state.clone()
[f32d144]351            obj.saved_states[copy_name] = copy_state
[6f023e8]352        return obj
[2f4b430]353
[61cada5]354    def _repr_helper(self, list, rep):
355        """
[5062bbf]356        Helper method to print a state
[61cada5]357        """
358        for item in list:
[f32d144]359            rep += "parameter name: %s \n" % str(item[1])
360            rep += "value: %s\n" % str(item[2])
361            rep += "selected: %s\n" % str(item[0])
362            rep += "error displayed : %s \n" % str(item[4][0])
363            rep += "error value:%s \n" % str(item[4][1])
364            rep += "minimum displayed : %s \n" % str(item[5][0])
365            rep += "minimum value : %s \n" % str(item[5][1])
366            rep += "maximum displayed : %s \n" % str(item[6][0])
367            rep += "maximum value : %s \n" % str(item[6][1])
368            rep += "parameter unit: %s\n\n" % str(item[7])
[240b9966]369        return rep
[2f4b430]370
[61cada5]371    def __repr__(self):
[f32d144]372        """
[5062bbf]373        output string for printing
[11a7e11]374        """
[f32d144]375        rep = "\nState name: %s\n" % self.file
[11a7e11]376        t = time.localtime(self.timestamp)
[deff488]377        time_str = time.strftime("%b %d %Y %H;%M;%S ", t)
[c0ff8cc]378
[f32d144]379        rep += "State created: %s\n" % time_str
380        rep += "State form factor combobox selection: %s\n" % self.formfactorcombobox
381        rep += "State structure factor combobox selection: %s\n" % self.structurecombobox
382        rep += "is data : %s\n" % self.is_data
383        rep += "data's name : %s\n" % self.data_name
384        rep += "data's id : %s\n" % self.data_id
[a95ae9a]385        if self.model is not None:
[93f0a862]386            m_name = self.model.__class__.__name__
387            if m_name == 'Model':
388                m_name = self.m_name
[f32d144]389            rep += "model name : %s\n" % m_name
[74dc0a4]390        else:
391            rep += "model name : None\n"
[f32d144]392        rep += "multi_factor : %s\n" % str(self.multi_factor)
[2f4b430]393        rep += "magnetic_on : %s\n" % str(self.magnetic_on)
[ea5fa58]394        rep += "model type (Category) selected: %s\n" % self.categorycombobox
[f32d144]395        rep += "data : %s\n" % str(self.data)
[a95ae9a]396        rep += "Plotting Range: min: %s, max: %s, steps: %s\n" % \
397               (str(self.qmin),str(self.qmax), str(self.npts))
[f32d144]398        rep += "Dispersion selection : %s\n" % str(self.disp_box)
399        rep += "Smearing enable : %s\n" % str(self.enable_smearer)
400        rep += "Smearing disable : %s\n" % str(self.disable_smearer)
401        rep += "Pinhole smearer enable : %s\n" % str(self.pinhole_smearer)
402        rep += "Slit smearer enable : %s\n" % str(self.slit_smearer)
403        rep += "Dispersity enable : %s\n" % str(self.enable_disp)
404        rep += "Dispersity disable : %s\n" % str(self.disable_disp)
405        rep += "Slit smearer enable: %s\n" % str(self.slit_smearer)
[2f4b430]406
[f32d144]407        rep += "dI_noweight : %s\n" % str(self.dI_noweight)
408        rep += "dI_didata : %s\n" % str(self.dI_didata)
409        rep += "dI_sqrdata : %s\n" % str(self.dI_sqrdata)
410        rep += "dI_idata : %s\n" % str(self.dI_idata)
[2f4b430]411
[f32d144]412        rep += "2D enable : %s\n" % str(self.enable2D)
[a95ae9a]413        rep += "All parameters checkbox selected: %s\n" % str(self.cb1)
[f32d144]414        rep += "Value of Chisqr : %s\n" % str(self.tcChi)
415        rep += "Smear object : %s\n" % str(self.smearer)
[a95ae9a]416        rep += "Smear type : %s\n" % str(self.smear_type)
[f32d144]417        rep += "dq_l  : %s\n" % self.dq_l
418        rep += "dq_r  : %s\n" % self.dq_r
[db8fd5b]419        rep += "dx_max  : %s\n" % str(self.dx_max)
[2f4b430]420        rep += "dx_min : %s\n" % str(self.dx_min)
[db8fd5b]421        rep += "dxl  : %s\n" % str(self.dxl)
[2f4b430]422        rep += "dxw : %s\n" % str(self.dxw)
[f32d144]423        rep += "model  : %s\n\n" % str(self.model)
[2296316]424        temp_parameters = []
425        temp_fittable_param = []
426        if self.data.__class__.__name__ == "Data2D":
427            self.is_2D = True
428        else:
429            self.is_2D = False
430        if self.data is not None:
431            if not self.is_2D:
432                for item in self.parameters:
[a95ae9a]433                    if item not in self.orientation_params:
[2296316]434                        temp_parameters.append(item)
435                for item in self.fittable_param:
[a95ae9a]436                    if item not in self.orientation_params_disp:
[2296316]437                        temp_fittable_param.append(item)
438            else:
439                temp_parameters = self.parameters
440                temp_fittable_param = self.fittable_param
[2f4b430]441
[a95ae9a]442            rep += "number parameters(self.parameters): %s\n" % \
443                   len(temp_parameters)
[f32d144]444            rep = self._repr_helper(list=temp_parameters, rep=rep)
[a95ae9a]445            rep += "number str_parameters(self.str_parameters): %s\n" % \
446                   len(self.str_parameters)
[f32d144]447            rep = self._repr_helper(list=self.str_parameters, rep=rep)
[a95ae9a]448            rep += "number fittable_param(self.fittable_param): %s\n" % \
449                   len(temp_fittable_param)
[f32d144]450            rep = self._repr_helper(list=temp_fittable_param, rep=rep)
[61cada5]451        return rep
[2296316]452
453    def set_report_string(self):
454        """
[f32d144]455        Get the values (strings) from __str__ for report
[2296316]456        """
[d06ae30]457        # Dictionary of the report strings
[2296316]458        repo_time = ""
459        model_name = ""
460        title = ""
461        title_name = ""
462        file_name = ""
463        param_string = ""
464        paramval_string = ""
465        chi2_string = ""
466        q_range = ""
467        strings = self.__repr__()
468        lines = strings.split('\n')
469
470        # get all string values from __str__()
471        for line in lines:
472            value = ""
473            content = line.split(":")
474            name = content[0]
475            try:
476                value = content[1]
[7673ecd]477            except Exception:
[6c382da]478                msg = "Report string expected 'name: value' but got %r"%line
479                logging.error(msg)
[b7c6a4a]480            if name.count("State created"):
481                repo_time = "" + value
[2296316]482            if name.count("parameter name"):
483                val_name = value.split(".")
484                if len(val_name) > 1:
485                    if val_name[1].count("width"):
486                        param_string += value + ','
487                    else:
488                        continue
489                else:
490                    param_string += value + ','
491            if name == "value":
492                param_string += value + ','
[b1e609c]493            if name == "selected":
494                if value == u' False':
495                    fixed_parameter = True
496                else:
497                    fixed_parameter = False
[2296316]498            if name == "error value":
[b1e609c]499                if fixed_parameter:
500                    param_string += '(fixed),'
501                else:
[d06ae30]502                    param_string += value + ','
[2296316]503            if name == "parameter unit":
[f32d144]504                param_string += value + ':'
[2296316]505            if name == "Value of Chisqr ":
506                chi2 = ("Chi2/Npts = " + value)
507                chi2_string = CENTRE % chi2
508            if name == "Title":
509                if len(value.strip()) == 0:
510                    continue
511                title = value + " [" + repo_time + "]"
512                title_name = HEADER % title
513            if name == "data ":
514                try:
[b1e609c]515                    file_value = ("File name:" + content[2])
516                    file_name = CENTRE % file_value
[2296316]517                    if len(title) == 0:
518                        title = content[2] + " [" + repo_time + "]"
519                        title_name = HEADER % title
[7673ecd]520                except Exception:
[6c382da]521                    msg = "While parsing 'data: ...'\n"
522                    logging.error(msg + traceback.format_exc())
[74dc0a4]523            if name == "model name ":
524                try:
525                    modelname = "Model name:" + content[1]
526                except:
527                    modelname = "Model name:" + " NAN"
[f32d144]528                model_name = CENTRE % modelname
[2f4b430]529
[2296316]530            if name == "Plotting Range":
531                try:
532                    q_range = content[1] + " = " + content[2] \
533                            + " = " + content[3].split(",")[0]
534                    q_name = ("Q Range:    " + q_range)
535                    q_range = CENTRE % q_name
[7673ecd]536                except Exception:
[6c382da]537                    msg = "While parsing 'Plotting Range: ...'\n"
538                    logging.error(msg + traceback.format_exc())
[2296316]539        paramval = ""
[f32d144]540        for lines in param_string.split(":"):
[2296316]541            line = lines.split(",")
[f32d144]542            if len(lines) > 0:
543                param = line[0]
[2296316]544                param += " = " + line[1]
545                if len(line[2].split()) > 0 and not line[2].count("None"):
546                    param += " +- " + line[2]
547                if len(line[3].split()) > 0 and not line[3].count("None"):
548                    param += " " + line[3]
549                if not paramval.count(param):
[f32d144]550                    paramval += param + "\n"
[2296316]551                    paramval_string += CENTRE % param + "\n"
[2f4b430]552
[a95ae9a]553        text_string = "\n\n\n%s\n\n%s\n%s\n%s\n\n%s" % \
554                      (title, file, q_name, chi2, paramval)
[2f4b430]555
[f5bdb4a]556        title_name = self._check_html_format(title_name)
557        file_name = self._check_html_format(file_name)
558        title = self._check_html_format(title)
[2f4b430]559
[2296316]560        html_string = title_name + "\n" + file_name + \
[f32d144]561                                   "\n" + model_name + \
[2296316]562                                   "\n" + q_range + \
563                                   "\n" + chi2_string + \
564                                   "\n" + ELINE + \
565                                   "\n" + paramval_string + \
566                                   "\n" + ELINE + \
567                                   "\n" + FEET_1 % title + \
[f32d144]568                                   "\n" + FEET_2
[2f4b430]569
[2296316]570        return html_string, text_string, title
[2f4b430]571
[f5bdb4a]572    def _check_html_format(self, name):
573        """
574        Check string '%' for html format
575        """
576        if name.count('%'):
577            name = name.replace('%', '&#37')
[2f4b430]578
[f5bdb4a]579        return name
[2f4b430]580
[2296316]581    def report(self, figs=None, canvases=None):
582        """
583        Invoke report dialog panel
[2f4b430]584
[2296316]585        : param figs: list of pylab figures [list]
586        """
[d85c194]587        from sas.sasgui.perspectives.fitting.report_dialog import ReportDialog
[2296316]588        # get the strings for report
589        html_str, text_str, title = self.set_report_string()
590        # Allow 2 figures to append
591        if len(figs) == 1:
[f32d144]592            add_str = FEET_3
[2296316]593        elif len(figs) == 2:
[f32d144]594            add_str = ELINE
595            add_str += FEET_2 % ("%s")
596            add_str += ELINE
597            add_str += FEET_3
[2296316]598        elif len(figs) > 2:
[f32d144]599            add_str = ELINE
600            add_str += FEET_2 % ("%s")
601            add_str += ELINE
602            add_str += FEET_2 % ("%s")
603            add_str += ELINE
604            add_str += FEET_3
[2296316]605        else:
606            add_str = ""
[c0ff8cc]607
[2296316]608        # final report html strings
609        report_str = html_str % ("%s") + add_str
610
611        # make plot image
612        images = self.set_plot_state(figs, canvases)
[f32d144]613        report_list = [report_str, text_str, images]
[6f16e25]614        dialog = ReportDialog(report_list, None, wx.ID_ANY, "")
[d838715]615        dialog.Show()
[2f4b430]616
[eddb6ec]617    def _toXML_helper(self, thelist, element, newdoc):
[61cada5]618        """
[5062bbf]619        Helper method to create xml file for saving state
[61cada5]620        """
[eddb6ec]621        for item in thelist:
[61cada5]622            sub_element = newdoc.createElement('parameter')
623            sub_element.setAttribute('name', str(item[1]))
624            sub_element.setAttribute('value', str(item[2]))
625            sub_element.setAttribute('selected_to_fit', str(item[0]))
626            sub_element.setAttribute('error_displayed', str(item[4][0]))
627            sub_element.setAttribute('error_value', str(item[4][1]))
628            sub_element.setAttribute('minimum_displayed', str(item[5][0]))
629            sub_element.setAttribute('minimum_value', str(item[5][1]))
630            sub_element.setAttribute('maximum_displayed', str(item[6][0]))
631            sub_element.setAttribute('maximum_value', str(item[6][1]))
632            sub_element.setAttribute('unit', str(item[7]))
633            element.appendChild(sub_element)
[2f4b430]634
[a95ae9a]635    def toXML(self, file="fitting_state.fitv", doc=None,
636              entry_node=None, batch_fit_state=None):
[61cada5]637        """
[a95ae9a]638        Writes the state of the fit panel to file, as XML.
[2f4b430]639
[5062bbf]640        Compatible with standalone writing, or appending to an
641        already existing XML document. In that case, the XML document
642        is required. An optional entry node in the XML document may also be given.
[2f4b430]643
[5062bbf]644        :param file: file to write to
645        :param doc: XML document object [optional]
646        :param entry_node: XML node within the XML document at which we will append the data [optional]
[2f4b430]647
[61cada5]648        """
649        from xml.dom.minidom import getDOMImplementation
[71f0373]650
[61cada5]651        # Check whether we have to write a standalone XML file
652        if doc is None:
653            impl = getDOMImplementation()
[f32d144]654            doc_type = impl.createDocumentType(FITTING_NODE_NAME, "1.0", "1.0")
[61cada5]655            newdoc = impl.createDocument(None, FITTING_NODE_NAME, doc_type)
656            top_element = newdoc.documentElement
657        else:
658            # We are appending to an existing document
659            newdoc = doc
[ac5b69d]660            try:
661                top_element = newdoc.createElement(FITTING_NODE_NAME)
662            except:
663                string = etree.tostring(doc, pretty_print=True)
664                newdoc = parseString(string)
665                top_element = newdoc.createElement(FITTING_NODE_NAME)
[61cada5]666            if entry_node is None:
667                newdoc.documentElement.appendChild(top_element)
668            else:
[ac5b69d]669                try:
670                    entry_node.appendChild(top_element)
671                except:
672                    node_name = entry_node.tag
673                    node_list = newdoc.getElementsByTagName(node_name)
674                    entry_node = node_list.item(0)
675                    entry_node.appendChild(top_element)
[2f4b430]676
[61cada5]677        attr = newdoc.createAttribute("version")
678        attr.nodeValue = '1.0'
679        top_element.setAttributeNode(attr)
[2f4b430]680
[61cada5]681        # File name
682        element = newdoc.createElement("filename")
683        if self.file is not None:
684            element.appendChild(newdoc.createTextNode(str(self.file)))
685        else:
686            element.appendChild(newdoc.createTextNode(str(file)))
687        top_element.appendChild(element)
[2f4b430]688
[11a7e11]689        element = newdoc.createElement("timestamp")
690        element.appendChild(newdoc.createTextNode(time.ctime(self.timestamp)))
691        attr = newdoc.createAttribute("epoch")
692        attr.nodeValue = str(self.timestamp)
693        element.setAttributeNode(attr)
694        top_element.appendChild(element)
[a95ae9a]695
[61cada5]696        # Inputs
697        inputs = newdoc.createElement("Attributes")
698        top_element.appendChild(inputs)
[2f4b430]699
[61cada5]700        if self.data is not None and hasattr(self.data, "group_id"):
701            self.data_group_id = self.data.group_id
702        if self.data is not None and hasattr(self.data, "is_data"):
703            self.is_data = self.data.is_data
704        if self.data is not None:
705            self.data_name = self.data.name
706        if self.data is not None and hasattr(self.data, "id"):
707            self.data_id = self.data.id
[2f4b430]708
[b1e609c]709        for item in LIST_OF_DATA_ATTRIBUTES:
[0b12abb5]710            element = newdoc.createElement(item[0])
[b1e609c]711            element.setAttribute(item[0], str(getattr(self, item[1])))
[f32d144]712            inputs.appendChild(element)
[2f4b430]713
[b1e609c]714        for item in LIST_OF_STATE_ATTRIBUTES:
[26f3dd5]715            element = newdoc.createElement(item[0])
[b1e609c]716            element.setAttribute(item[0], str(getattr(self, item[1])))
[26f3dd5]717            inputs.appendChild(element)
[2f4b430]718
[f32d144]719        # For self.values ={ disp_param_name: [vals,...],...}
720        # and for self.weights ={ disp_param_name: [weights,...],...}
[b1e609c]721        for item in LIST_OF_MODEL_ATTRIBUTES:
[11a7e11]722            element = newdoc.createElement(item[0])
[b1e609c]723            value_list = getattr(self, item[1])
724            for key, value in value_list.iteritems():
[2296316]725                sub_element = newdoc.createElement(key)
726                sub_element.setAttribute('name', str(key))
727                for val in value:
[b1e609c]728                    sub_element.appendChild(newdoc.createTextNode(str(val)))
[2f4b430]729
[f32d144]730                element.appendChild(sub_element)
[11a7e11]731            inputs.appendChild(element)
[2f4b430]732
[2296316]733        # Create doc for the dictionary of self._disp_obj_dic
[6c382da]734        for tagname, varname, tagtype in DISPERSION_LIST:
735            element = newdoc.createElement(tagname)
736            value_list = getattr(self, varname)
737            for key, value in value_list.iteritems():
[f32d144]738                sub_element = newdoc.createElement(key)
739                sub_element.setAttribute('name', str(key))
740                sub_element.setAttribute('value', str(value))
741                element.appendChild(sub_element)
742            inputs.appendChild(element)
[2f4b430]743
[b1e609c]744        for item in LIST_OF_STATE_PARAMETERS:
[61cada5]745            element = newdoc.createElement(item[0])
[b1e609c]746            self._toXML_helper(thelist=getattr(self, item[1]), element=element, newdoc=newdoc)
[61cada5]747            inputs.appendChild(element)
[2f4b430]748
[a95ae9a]749        # Combined and Simultaneous Fit Parameters
750        if batch_fit_state is not None:
751            batch_combo = newdoc.createElement('simultaneous_fit')
752            top_element.appendChild(batch_combo)
753
754            # Simultaneous Fit Number For Linking Later
755            element = newdoc.createElement('sim_fit_number')
756            element.setAttribute('fit_number', str(batch_fit_state.fit_page_no))
757            batch_combo.appendChild(element)
758
759            # Save constraints
760            constraints = newdoc.createElement('constraints')
761            batch_combo.appendChild(constraints)
762            for constraint in batch_fit_state.constraints_list:
763                # model_cbox, param_cbox, egal_txt, constraint, btRemove, sizer
764                doc_cons = newdoc.createElement('constraint')
765                doc_cons.setAttribute('model_cbox',
766                                      str(constraint.model_cbox.GetValue()))
767                doc_cons.setAttribute('param_cbox',
768                                      str(constraint.param_cbox.GetValue()))
769                doc_cons.setAttribute('egal_txt',
770                                      str(constraint.egal_txt.GetLabel()))
771                doc_cons.setAttribute('constraint',
772                                      str(constraint.constraint.GetValue()))
773                constraints.appendChild(doc_cons)
774
775            # Save all models
776            models = newdoc.createElement('model_list')
777            batch_combo.appendChild(models)
778            for model in batch_fit_state.model_list:
779                doc_model = newdoc.createElement('model_list_item')
780                doc_model.setAttribute('checked', str(model[0].GetValue()))
781                keys = model[1].keys()
782                doc_model.setAttribute('name', str(keys[0]))
783                values = model[1].get(keys[0])
784                doc_model.setAttribute('fit_number', str(model[2]))
785                doc_model.setAttribute('fit_page_source', str(model[3]))
786                doc_model.setAttribute('model_name', str(values.model.id))
787                models.appendChild(doc_model)
788
789            # Select All Checkbox
790            element = newdoc.createElement('select_all')
791            if batch_fit_state.select_all:
792                element.setAttribute('checked', 'True')
793            else:
794                element.setAttribute('checked', 'False')
795            batch_combo.appendChild(element)
796
[61cada5]797        # Save the file
798        if doc is None:
799            fd = open(file, 'w')
800            fd.write(newdoc.toprettyxml())
801            fd.close()
802            return None
803        else:
[ac5b69d]804            return newdoc
[2f4b430]805
[61cada5]806    def _fromXML_helper(self, node, list):
807        """
[5062bbf]808        Helper function to write state to xml
[61cada5]809        """
810        for item in node:
[0b12abb5]811            try:
812                name = item.get('name')
813            except:
814                name = None
815            try:
816                value = item.get('value')
817            except:
818                value = None
819            try:
[3ad91de]820                selected_to_fit = (item.get('selected_to_fit') == "True")
[0b12abb5]821            except:
822                selected_to_fit = None
823            try:
[3ad91de]824                error_displayed = (item.get('error_displayed') == "True")
[0b12abb5]825            except:
826                error_displayed = None
[f32d144]827            try:
[0b12abb5]828                error_value = item.get('error_value')
829            except:
830                error_value = None
831            try:
[f32d144]832                minimum_displayed = (item.get('minimum_displayed') == "True")
[0b12abb5]833            except:
834                minimum_displayed = None
835            try:
836                minimum_value = item.get('minimum_value')
837            except:
838                minimum_value = None
839            try:
[3ad91de]840                maximum_displayed = (item.get('maximum_displayed') == "True")
[0b12abb5]841            except:
842                maximum_displayed = None
843            try:
844                maximum_value = item.get('maximum_value')
845            except:
846                maximum_value = None
847            try:
848                unit = item.get('unit')
849            except:
850                unit = None
[2296316]851            list.append([selected_to_fit, name, value, "+/-",
852                         [error_displayed, error_value],
[f32d144]853                         [minimum_displayed, minimum_value],
854                         [maximum_displayed, maximum_value], unit])
[2f4b430]855
[61cada5]856    def fromXML(self, file=None, node=None):
857        """
[5062bbf]858        Load fitting state from a file
[2f4b430]859
[5062bbf]860        :param file: .fitv file
861        :param node: node of a XML document to read from
[61cada5]862        """
863        if file is not None:
[11a7e11]864            msg = "PageState no longer supports non-CanSAS"
865            msg += " format for fitting files"
866            raise RuntimeError, msg
[2f4b430]867
[e89aed5]868        if node.get('version') and node.get('version') == '1.0':
[2f4b430]869
[61cada5]870            # Get file name
871            entry = get_content('ns:filename', node)
872            if entry is not None:
873                self.file = entry.text.strip()
[2f4b430]874
[11a7e11]875            # Get time stamp
876            entry = get_content('ns:timestamp', node)
877            if entry is not None and entry.get('epoch'):
878                try:
879                    self.timestamp = float(entry.get('epoch'))
880                except:
881                    msg = "PageState.fromXML: Could not"
882                    msg += " read timestamp\n %s" % sys.exc_value
883                    logging.error(msg)
[2f4b430]884
[61cada5]885            # Parse fitting attributes
886            entry = get_content('ns:Attributes', node)
[b1e609c]887            for item in LIST_OF_DATA_ATTRIBUTES:
[f32d144]888                node = get_content('ns:%s' % item[0], entry)
[b1e609c]889                setattr(self, item[0], parse_entry_helper(node, item))
[2296316]890
[61cada5]891            if entry is not None:
[b1e609c]892                for item in LIST_OF_STATE_ATTRIBUTES:
[2296316]893                    node = get_content('ns:%s' % item[0], entry)
[b1e609c]894                    setattr(self, item[0], parse_entry_helper(node, item))
[2f4b430]895
[b1e609c]896                for item in LIST_OF_STATE_PARAMETERS:
[2296316]897                    node = get_content("ns:%s" % item[0], entry)
[b1e609c]898                    self._fromXML_helper(node=node, list=getattr(self, item[1]))
[2f4b430]899
[f32d144]900                # Recover _disp_obj_dict from xml file
901                self._disp_obj_dict = {}
[6c382da]902                for tagname, varname, tagtype in DISPERSION_LIST:
903                    node = get_content("ns:%s" % tagname, entry)
[2296316]904                    for attr in node:
[6c382da]905                        parameter = str(attr.get('name'))
906                        value = attr.get('value')
907                        if value.startswith("<"):
908                            try:
909                                # <path.to.NamedDistribution object/instance...>
910                                cls_name = value[1:].split()[0].split('.')[-1]
911                                cls = getattr(sasmodels.weights, cls_name)
912                                value = cls.type
913                            except Exception:
914                                logging.error("unable to load distribution %r for %s"
915                                              % (value, parameter))
916                                continue
917                        _disp_obj_dict = getattr(self, varname)
918                        _disp_obj_dict[parameter] = value
[2f4b430]919
[f32d144]920                # get self.values and self.weights dic. if exists
[6c382da]921                for tagname, varname in LIST_OF_MODEL_ATTRIBUTES:
922                    node = get_content("ns:%s" % tagname, entry)
[2296316]923                    dic = {}
[b1e609c]924                    value_list = []
[2296316]925                    for par in node:
926                        name = par.get('name')
[6c382da]927                        values = par.text.split()
[2296316]928                        # Get lines only with numbers
929                        for line in values:
930                            try:
[f32d144]931                                val = float(line)
[b1e609c]932                                value_list.append(val)
[7673ecd]933                            except Exception:
[2296316]934                                # pass if line is empty (it happens)
[6c382da]935                                msg = ("Error reading %r from %s %s\n"
936                                       % (line, tagname, name))
937                                logging.error(msg + traceback.format_exc())
[0e33a8d]938                        dic[name] = numpy.array(value_list)
[6c382da]939                    setattr(self, varname, dic)
[2f4b430]940
[2296316]941    def set_plot_state(self, figs, canvases):
942        """
943        Build image state that wx.html understand
944        by plotting, putting it into wx.FileSystem image object
945
946        """
947        images = []
948        # some imports
949        import wx
950
951        # Reset memory
952        self.imgRAM = None
953        wx.MemoryFSHandler()
[2f4b430]954
[2296316]955        # For no figures in the list, prepare empty plot
956        if figs == None or len(figs) == 0:
957            figs = [None]
[2f4b430]958
[f32d144]959        # Loop over the list of figures
[2296316]960        # use wx.MemoryFSHandler
[f32d144]961        self.imgRAM = wx.MemoryFSHandler()
[2296316]962        for fig in figs:
963            if figs != None:
964                ind = figs.index(fig)
965                canvas = canvases[ind]
[2f4b430]966
[b1e609c]967            #store the image in wx.FileSystem Object
[2296316]968            wx.FileSystem.AddHandler(wx.MemoryFSHandler())
[2f4b430]969
[2296316]970            # index of the fig
971            ind = figs.index(fig)
[2f4b430]972
[2296316]973            #AddFile, image can be retrieved with 'memory:filename'
[f32d144]974            self.imgRAM.AddFile('img_fit%s.png' % ind,
[2296316]975                                canvas.bitmap, wx.BITMAP_TYPE_PNG)
[2f4b430]976
[2296316]977            #append figs
978            images.append(fig)
[2f4b430]979
[c0ff8cc]980        return images
[61cada5]981
[f32d144]982
[61cada5]983class Reader(CansasReader):
984    """
[5062bbf]985    Class to load a .fitv fitting file
[61cada5]986    """
987    ## File type
988    type_name = "Fitting"
[2f4b430]989
[61cada5]990    ## Wildcards
[b35d3d1]991    type = ["Fitting files (*.fitv)|*.fitv"
[b9a5f0e]992            "SASView file (*.svs)|*.svs"]
[61cada5]993    ## List of allowed extensions
[f32d144]994    ext = ['.fitv', '.FITV', '.svs', 'SVS']
[2f4b430]995
[61cada5]996    def __init__(self, call_back=None, cansas=True):
[df7ed14]997        CansasReader.__init__(self)
[61cada5]998        """
[5062bbf]999        Initialize the call-back method to be called
1000        after we load a file
[2f4b430]1001
[5062bbf]1002        :param call_back: call-back method
1003        :param cansas:  True = files will be written/read in CanSAS format
1004                        False = write CanSAS format
[2f4b430]1005
[61cada5]1006        """
1007        ## Call back method to be executed after a file is read
1008        self.call_back = call_back
1009        ## CanSAS format flag
1010        self.cansas = cansas
[75fbd17]1011        self.state = None
[a95ae9a]1012        # batch fitting params for saving
1013        self.batchfit_params = []
[2f4b430]1014
[75fbd17]1015    def get_state(self):
1016        return self.state
[2f4b430]1017
[61cada5]1018    def read(self, path):
[f32d144]1019        """
[5062bbf]1020        Load a new P(r) inversion state from file
[2f4b430]1021
[5062bbf]1022        :param path: file path
[2f4b430]1023
[61cada5]1024        """
[e9b12eaf]1025        if self.cansas == True:
[61cada5]1026            return self._read_cansas(path)
[2f4b430]1027
[e9b12eaf]1028    def _data2d_to_xml_doc(self, datainfo):
[61cada5]1029        """
[5062bbf]1030        Create an XML document to contain the content of a Data2D
[2f4b430]1031
[5062bbf]1032        :param datainfo: Data2D object
[2f4b430]1033
[61cada5]1034        """
1035        if not issubclass(datainfo.__class__, Data2D):
1036            raise RuntimeError, "The cansas writer expects a Data2D instance"
[2f4b430]1037
[61cada5]1038        doc = xml.dom.minidom.Document()
1039        main_node = doc.createElement("SASroot")
1040        main_node.setAttribute("version", self.version)
[df7ed14]1041        main_node.setAttribute("xmlns", "cansas1d/%s" % self.version)
1042        main_node.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
[b1e609c]1043        main_node.setAttribute("xsi:schemaLocation",
1044                               "cansas1d/%s http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd" % self.version)
[2f4b430]1045
[61cada5]1046        doc.appendChild(main_node)
[2f4b430]1047
[61cada5]1048        entry_node = doc.createElement("SASentry")
1049        main_node.appendChild(entry_node)
[2f4b430]1050
[61cada5]1051        write_node(doc, entry_node, "Title", datainfo.title)
[df7ed14]1052        if datainfo is not None:
1053            write_node(doc, entry_node, "data_class", datainfo.__class__.__name__)
[61cada5]1054        for item in datainfo.run:
1055            runname = {}
[2f4b430]1056            if datainfo.run_name.has_key(item) and len(str(datainfo.run_name[item])) > 1:
[b1e609c]1057                runname = {'name': datainfo.run_name[item]}
[61cada5]1058            write_node(doc, entry_node, "Run", item, runname)
1059        # Data info
[df7ed14]1060        new_node = doc.createElement("SASdata")
1061        entry_node.appendChild(new_node)
[b1e609c]1062        for item in LIST_OF_DATA_2D_ATTR:
[e9b12eaf]1063            element = doc.createElement(item[0])
[b1e609c]1064            element.setAttribute(item[0], str(getattr(datainfo, item[1])))
[df7ed14]1065            new_node.appendChild(element)
[2f4b430]1066
[b1e609c]1067        for item in LIST_OF_DATA_2D_VALUES:
[df7ed14]1068            root_node = doc.createElement(item[0])
1069            new_node.appendChild(root_node)
[38226d26]1070            temp_list = None
[b1e609c]1071            temp_list = getattr(datainfo, item[1])
[df7ed14]1072
[2f4b430]1073            if temp_list is None or len(temp_list) == 0:
[df7ed14]1074                element = doc.createElement(item[0])
[b1e609c]1075                element.appendChild(doc.createTextNode(str(temp_list)))
[df7ed14]1076                root_node.appendChild(element)
[35b556d]1077            else:
1078                for value in temp_list:
[df7ed14]1079                    element = doc.createElement(item[0])
[b1e609c]1080                    element.setAttribute(item[0], str(value))
[df7ed14]1081                    root_node.appendChild(element)
[2f4b430]1082
[61cada5]1083        # Sample info
1084        sample = doc.createElement("SASsample")
1085        if datainfo.sample.name is not None:
1086            sample.setAttribute("name", str(datainfo.sample.name))
1087        entry_node.appendChild(sample)
1088        write_node(doc, sample, "ID", str(datainfo.sample.ID))
[f32d144]1089        write_node(doc, sample, "thickness", datainfo.sample.thickness,
1090                   {"unit": datainfo.sample.thickness_unit})
[61cada5]1091        write_node(doc, sample, "transmission", datainfo.sample.transmission)
[f32d144]1092        write_node(doc, sample, "temperature", datainfo.sample.temperature,
1093                   {"unit": datainfo.sample.temperature_unit})
[2f4b430]1094
[61cada5]1095        for item in datainfo.sample.details:
1096            write_node(doc, sample, "details", item)
[2f4b430]1097
[61cada5]1098        pos = doc.createElement("position")
[f32d144]1099        written = write_node(doc, pos, "x", datainfo.sample.position.x,
1100                             {"unit": datainfo.sample.position_unit})
1101        written = written | write_node(doc, pos, "y",
1102                                       datainfo.sample.position.y,
1103                                       {"unit": datainfo.sample.position_unit})
1104        written = written | write_node(doc, pos, "z",
1105                                       datainfo.sample.position.z,
1106                                       {"unit": datainfo.sample.position_unit})
[61cada5]1107        if written == True:
1108            sample.appendChild(pos)
[2f4b430]1109
[61cada5]1110        ori = doc.createElement("orientation")
[f32d144]1111        written = write_node(doc, ori, "roll", datainfo.sample.orientation.x,
1112                             {"unit": datainfo.sample.orientation_unit})
1113        written = written | write_node(doc, ori, "pitch",
1114                                       datainfo.sample.orientation.y,
1115                                       {"unit": datainfo.sample.orientation_unit})
1116        written = written | write_node(doc, ori, "yaw",
1117                                       datainfo.sample.orientation.z,
1118                                       {"unit": datainfo.sample.orientation_unit})
[61cada5]1119        if written == True:
1120            sample.appendChild(ori)
[2f4b430]1121
[61cada5]1122        # Instrument info
1123        instr = doc.createElement("SASinstrument")
1124        entry_node.appendChild(instr)
[2f4b430]1125
[61cada5]1126        write_node(doc, instr, "name", datainfo.instrument)
[2f4b430]1127
[61cada5]1128        #   Source
1129        source = doc.createElement("SASsource")
1130        if datainfo.source.name is not None:
1131            source.setAttribute("name", str(datainfo.source.name))
1132        instr.appendChild(source)
[2f4b430]1133
[61cada5]1134        write_node(doc, source, "radiation", datainfo.source.radiation)
1135        write_node(doc, source, "beam_shape", datainfo.source.beam_shape)
1136        size = doc.createElement("beam_size")
1137        if datainfo.source.beam_size_name is not None:
1138            size.setAttribute("name", str(datainfo.source.beam_size_name))
[f32d144]1139        written = write_node(doc, size, "x", datainfo.source.beam_size.x,
1140                             {"unit": datainfo.source.beam_size_unit})
1141        written = written | write_node(doc, size, "y",
1142                                       datainfo.source.beam_size.y,
1143                                       {"unit": datainfo.source.beam_size_unit})
1144        written = written | write_node(doc, size, "z",
1145                                       datainfo.source.beam_size.z,
1146                                       {"unit": datainfo.source.beam_size_unit})
[61cada5]1147        if written == True:
1148            source.appendChild(size)
[2f4b430]1149
[f32d144]1150        write_node(doc, source, "wavelength", datainfo.source.wavelength,
1151                   {"unit": datainfo.source.wavelength_unit})
1152        write_node(doc, source, "wavelength_min",
1153                   datainfo.source.wavelength_min,
1154                   {"unit": datainfo.source.wavelength_min_unit})
1155        write_node(doc, source, "wavelength_max",
1156                   datainfo.source.wavelength_max,
1157                   {"unit": datainfo.source.wavelength_max_unit})
1158        write_node(doc, source, "wavelength_spread",
1159                   datainfo.source.wavelength_spread,
1160                   {"unit": datainfo.source.wavelength_spread_unit})
[2f4b430]1161
[61cada5]1162        #   Collimation
1163        for item in datainfo.collimation:
1164            coll = doc.createElement("SAScollimation")
1165            if item.name is not None:
1166                coll.setAttribute("name", str(item.name))
1167            instr.appendChild(coll)
[2f4b430]1168
[f32d144]1169            write_node(doc, coll, "length", item.length,
1170                       {"unit": item.length_unit})
[2f4b430]1171
[61cada5]1172            for apert in item.aperture:
1173                ap = doc.createElement("aperture")
1174                if apert.name is not None:
1175                    ap.setAttribute("name", str(apert.name))
1176                if apert.type is not None:
1177                    ap.setAttribute("type", str(apert.type))
1178                coll.appendChild(ap)
[2f4b430]1179
[f32d144]1180                write_node(doc, ap, "distance", apert.distance,
1181                           {"unit": apert.distance_unit})
[2f4b430]1182
[61cada5]1183                size = doc.createElement("size")
1184                if apert.size_name is not None:
1185                    size.setAttribute("name", str(apert.size_name))
[f32d144]1186                written = write_node(doc, size, "x", apert.size.x,
1187                                     {"unit": apert.size_unit})
1188                written = written | write_node(doc, size, "y", apert.size.y,
1189                                               {"unit": apert.size_unit})
1190                written = written | write_node(doc, size, "z", apert.size.z,
1191                                               {"unit": apert.size_unit})
[61cada5]1192                if written == True:
1193                    ap.appendChild(size)
1194
1195        #   Detectors
1196        for item in datainfo.detector:
1197            det = doc.createElement("SASdetector")
1198            written = write_node(doc, det, "name", item.name)
[f32d144]1199            written = written | write_node(doc, det, "SDD", item.distance,
1200                                           {"unit": item.distance_unit})
1201            written = written | write_node(doc, det, "slit_length",
1202                                           item.slit_length,
1203                                           {"unit": item.slit_length_unit})
[61cada5]1204            if written == True:
1205                instr.appendChild(det)
[2f4b430]1206
[61cada5]1207            off = doc.createElement("offset")
[f32d144]1208            written = write_node(doc, off, "x", item.offset.x,
1209                                 {"unit": item.offset_unit})
1210            written = written | write_node(doc, off, "y", item.offset.y,
1211                                           {"unit": item.offset_unit})
1212            written = written | write_node(doc, off, "z", item.offset.z,
1213                                           {"unit": item.offset_unit})
[61cada5]1214            if written == True:
1215                det.appendChild(off)
[2f4b430]1216
[61cada5]1217            center = doc.createElement("beam_center")
[f32d144]1218            written = write_node(doc, center, "x", item.beam_center.x,
1219                                 {"unit": item.beam_center_unit})
1220            written = written | write_node(doc, center, "y",
1221                                           item.beam_center.y,
1222                                           {"unit": item.beam_center_unit})
1223            written = written | write_node(doc, center, "z",
1224                                           item.beam_center.z,
1225                                           {"unit": item.beam_center_unit})
[61cada5]1226            if written == True:
1227                det.appendChild(center)
[2f4b430]1228
[61cada5]1229            pix = doc.createElement("pixel_size")
[f32d144]1230            written = write_node(doc, pix, "x", item.pixel_size.x,
1231                                 {"unit": item.pixel_size_unit})
1232            written = written | write_node(doc, pix, "y", item.pixel_size.y,
1233                                           {"unit": item.pixel_size_unit})
1234            written = written | write_node(doc, pix, "z", item.pixel_size.z,
1235                                           {"unit": item.pixel_size_unit})
[61cada5]1236            if written == True:
1237                det.appendChild(pix)
[2f4b430]1238
[61cada5]1239            ori = doc.createElement("orientation")
[f32d144]1240            written = write_node(doc, ori, "roll", item.orientation.x,
1241                                 {"unit": item.orientation_unit})
1242            written = written | write_node(doc, ori, "pitch",
1243                                           item.orientation.y,
1244                                           {"unit": item.orientation_unit})
1245            written = written | write_node(doc, ori, "yaw", item.orientation.z,
1246                                           {"unit": item.orientation_unit})
[61cada5]1247            if written == True:
1248                det.appendChild(ori)
[2f4b430]1249
[61cada5]1250        # Processes info
1251        for item in datainfo.process:
1252            node = doc.createElement("SASprocess")
1253            entry_node.appendChild(node)
1254
1255            write_node(doc, node, "name", item.name)
1256            write_node(doc, node, "date", item.date)
1257            write_node(doc, node, "description", item.description)
1258            for term in item.term:
1259                value = term['value']
1260                del term['value']
1261                write_node(doc, node, "term", value, term)
1262            for note in item.notes:
1263                write_node(doc, node, "SASprocessnote", note)
1264        # Return the document, and the SASentry node associated with
1265        # the data we just wrote
1266        return doc, entry_node
[2f4b430]1267
[61cada5]1268    def _parse_state(self, entry):
1269        """
[5062bbf]1270        Read a fit result from an XML node
[2f4b430]1271
[f32d144]1272        :param entry: XML node to read from
[5062bbf]1273        :return: PageState object
[61cada5]1274        """
1275        # Create an empty state
[2f4b430]1276        state = None
[61cada5]1277        # Locate the P(r) node
1278        try:
[f32d144]1279            nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME,
1280                                namespaces={'ns': CANSAS_NS})
[e89aed5]1281            if nodes:
[b35d3d1]1282                # Create an empty state
[f32d144]1283                state = PageState()
[b35d3d1]1284                state.fromXML(node=nodes[0])
[2f4b430]1285
[61cada5]1286        except:
[6c382da]1287            logging.info("XML document does not contain fitting information.\n"
1288                         + traceback.format_exc())
[2f4b430]1289
[61cada5]1290        return state
[2f4b430]1291
[e89aed5]1292    def _parse_simfit_state(self, entry):
1293        """
1294        Parses the saved data for a simultaneous fit
1295        :param entry: XML object to read from
1296        :return: XML object for a simultaneous fit or None
1297        """
1298        nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME,
1299                            namespaces={'ns': CANSAS_NS})
1300        if nodes:
1301            simfitstate = nodes[0].xpath('ns:simultaneous_fit',
1302                                         namespaces={'ns': CANSAS_NS})
1303            if simfitstate:
1304                from simfitpage import SimFitPageState
1305                sim_fit_state = SimFitPageState()
1306                simfitstate_0 = simfitstate[0]
1307                all = simfitstate_0.xpath('ns:select_all',
1308                                        namespaces={'ns': CANSAS_NS})
1309                atts = all[0].attrib
1310                checked = atts.get('checked')
1311                sim_fit_state.select_all = bool(checked)
1312                model_list = simfitstate_0.xpath('ns:model_list',
1313                                               namespaces={'ns': CANSAS_NS})
1314                model_list_items = model_list[0].xpath('ns:model_list_item',
1315                                                       namespaces={'ns': CANSAS_NS})
1316                for model in model_list_items:
1317                    attrs = model.attrib
1318                    sim_fit_state.model_list.append(attrs)
1319                constraints = simfitstate_0.xpath('ns:constraints',
1320                                                namespaces={'ns': CANSAS_NS})
1321                constraint_list = constraints[0].xpath('ns:constraint',
1322                                                       namespaces={'ns': CANSAS_NS})
1323                for constraint in constraint_list:
1324                    attrs = constraint.attrib
1325                    sim_fit_state.constraints_list.append(attrs)
1326
1327                return sim_fit_state
1328            else:
1329                return None
1330
[ac5b69d]1331    def _parse_save_state_entry(self, dom):
[e9b12eaf]1332        """
[5062bbf]1333        Parse a SASentry
[2f4b430]1334
[5062bbf]1335        :param node: SASentry node
[2f4b430]1336
[df7ed14]1337        :return: Data1D/Data2D object
[2f4b430]1338
[e9b12eaf]1339        """
[df7ed14]1340        node = dom.xpath('ns:data_class', namespaces={'ns': CANSAS_NS})
1341        if not node or node[0].text.lstrip().rstrip() != "Data2D":
[ac5b69d]1342            return_value, _ = self._parse_entry(dom)
1343            numpy.trim_zeros(return_value.x)
1344            numpy.trim_zeros(return_value.y)
1345            numpy.trim_zeros(return_value.dy)
1346            size_dx = return_value.dx.size
1347            size_dxl = return_value.dxl.size
1348            size_dxw = return_value.dxw.size
1349            if size_dxl == 0 and size_dxw == 0:
1350                return_value.dxl = None
1351                return_value.dxw = None
1352                numpy.trim_zeros(return_value.dx)
1353            elif size_dx == 0:
1354                return_value.dx = None
1355                size_dx = size_dxl
1356                numpy.trim_zeros(return_value.dxl)
1357                numpy.trim_zeros(return_value.dxw)
[2f4b430]1358
[ac5b69d]1359            return return_value, _
[2f4b430]1360
[df7ed14]1361        #Parse 2D
[e9b12eaf]1362        data_info = Data2D()
[2f4b430]1363
[f32d144]1364        # Look up title
[e9b12eaf]1365        self._store_content('ns:Title', dom, 'title', data_info)
[2f4b430]1366
[f32d144]1367        # Look up run number
[e9b12eaf]1368        nodes = dom.xpath('ns:Run', namespaces={'ns': CANSAS_NS})
[f32d144]1369        for item in nodes:
[e9b12eaf]1370            if item.text is not None:
1371                value = item.text.strip()
1372                if len(value) > 0:
1373                    data_info.run.append(value)
1374                    if item.get('name') is not None:
1375                        data_info.run_name[value] = item.get('name')
[2f4b430]1376
[f32d144]1377        # Look up instrument name
1378        self._store_content('ns:SASinstrument/ns:name', dom,
1379                            'instrument', data_info)
[e9b12eaf]1380
1381        # Notes
1382        note_list = dom.xpath('ns:SASnote', namespaces={'ns': CANSAS_NS})
1383        for note in note_list:
1384            try:
1385                if note.text is not None:
1386                    note_value = note.text.strip()
1387                    if len(note_value) > 0:
1388                        data_info.notes.append(note_value)
[7673ecd]1389            except Exception:
[e9b12eaf]1390                err_mess = "cansas_reader.read: error processing entry notes\n  %s" % sys.exc_value
1391                self.errors.append(err_mess)
1392                logging.error(err_mess)
[2f4b430]1393
[e9b12eaf]1394        # Sample info ###################
1395        entry = get_content('ns:SASsample', dom)
1396        if entry is not None:
1397            data_info.sample.name = entry.get('name')
[2f4b430]1398
[b1e609c]1399        self._store_content('ns:SASsample/ns:ID', dom, 'ID', data_info.sample)
1400        self._store_float('ns:SASsample/ns:thickness', dom, 'thickness', data_info.sample)
1401        self._store_float('ns:SASsample/ns:transmission', dom, 'transmission', data_info.sample)
1402        self._store_float('ns:SASsample/ns:temperature', dom, 'temperature', data_info.sample)
[2f4b430]1403
[b1e609c]1404        nodes = dom.xpath('ns:SASsample/ns:details', namespaces={'ns': CANSAS_NS})
[e9b12eaf]1405        for item in nodes:
1406            try:
1407                if item.text is not None:
1408                    detail_value = item.text.strip()
1409                    if len(detail_value) > 0:
1410                        data_info.sample.details.append(detail_value)
[7673ecd]1411            except Exception:
[e9b12eaf]1412                err_mess = "cansas_reader.read: error processing sample details\n  %s" % sys.exc_value
1413                self.errors.append(err_mess)
1414                logging.error(err_mess)
[2f4b430]1415
[e9b12eaf]1416        # Position (as a vector)
[b1e609c]1417        self._store_float('ns:SASsample/ns:position/ns:x', dom, 'position.x', data_info.sample)
1418        self._store_float('ns:SASsample/ns:position/ns:y', dom, 'position.y', data_info.sample)
1419        self._store_float('ns:SASsample/ns:position/ns:z', dom, 'position.z', data_info.sample)
[2f4b430]1420
[e9b12eaf]1421        # Orientation (as a vector)
[f32d144]1422        self._store_float('ns:SASsample/ns:orientation/ns:roll',
[b1e609c]1423                          dom, 'orientation.x', data_info.sample)
[f32d144]1424        self._store_float('ns:SASsample/ns:orientation/ns:pitch',
[b1e609c]1425                          dom, 'orientation.y', data_info.sample)
[f32d144]1426        self._store_float('ns:SASsample/ns:orientation/ns:yaw',
[b1e609c]1427                          dom, 'orientation.z', data_info.sample)
[2f4b430]1428
[e9b12eaf]1429        # Source info ###################
1430        entry = get_content('ns:SASinstrument/ns:SASsource', dom)
1431        if entry is not None:
1432            data_info.source.name = entry.get('name')
[2f4b430]1433
[f32d144]1434        self._store_content('ns:SASinstrument/ns:SASsource/ns:radiation',
[b1e609c]1435                            dom, 'radiation', data_info.source)
[f32d144]1436        self._store_content('ns:SASinstrument/ns:SASsource/ns:beam_shape',
[b1e609c]1437                            dom, 'beam_shape', data_info.source)
[f32d144]1438        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength',
[b1e609c]1439                          dom, 'wavelength', data_info.source)
[f32d144]1440        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_min',
[b1e609c]1441                          dom, 'wavelength_min', data_info.source)
[f32d144]1442        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_max',
[b1e609c]1443                          dom, 'wavelength_max', data_info.source)
[f32d144]1444        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_spread',
[b1e609c]1445                          dom, 'wavelength_spread', data_info.source)
[2f4b430]1446
[f32d144]1447        # Beam size (as a vector)
[e9b12eaf]1448        entry = get_content('ns:SASinstrument/ns:SASsource/ns:beam_size', dom)
1449        if entry is not None:
1450            data_info.source.beam_size_name = entry.get('name')
[2f4b430]1451
[f32d144]1452        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:x',
[b1e609c]1453                          dom, 'beam_size.x', data_info.source)
[f32d144]1454        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:y',
[b1e609c]1455                          dom, 'beam_size.y', data_info.source)
[f32d144]1456        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:z',
[b1e609c]1457                          dom, 'beam_size.z', data_info.source)
[2f4b430]1458
[e9b12eaf]1459        # Collimation info ###################
[f32d144]1460        nodes = dom.xpath('ns:SASinstrument/ns:SAScollimation',
1461                          namespaces={'ns': CANSAS_NS})
[e9b12eaf]1462        for item in nodes:
1463            collim = Collimation()
1464            if item.get('name') is not None:
1465                collim.name = item.get('name')
[f32d144]1466            self._store_float('ns:length', item, 'length', collim)
[2f4b430]1467
[e9b12eaf]1468            # Look for apertures
[f32d144]1469            apert_list = item.xpath('ns:aperture',
1470                                    namespaces={'ns': CANSAS_NS})
[e9b12eaf]1471            for apert in apert_list:
[f32d144]1472                aperture = Aperture()
[2f4b430]1473
[e9b12eaf]1474                # Get the name and type of the aperture
1475                aperture.name = apert.get('name')
1476                aperture.type = apert.get('type')
[2f4b430]1477
[f32d144]1478                self._store_float('ns:distance', apert, 'distance', aperture)
[2f4b430]1479
[e9b12eaf]1480                entry = get_content('ns:size', apert)
1481                if entry is not None:
1482                    aperture.size_name = entry.get('name')
[2f4b430]1483
[f32d144]1484                self._store_float('ns:size/ns:x', apert, 'size.x', aperture)
1485                self._store_float('ns:size/ns:y', apert, 'size.y', aperture)
[e9b12eaf]1486                self._store_float('ns:size/ns:z', apert, 'size.z', aperture)
[2f4b430]1487
[e9b12eaf]1488                collim.aperture.append(aperture)
[2f4b430]1489
[e9b12eaf]1490            data_info.collimation.append(collim)
[2f4b430]1491
[e9b12eaf]1492        # Detector info ######################
[f32d144]1493        nodes = dom.xpath('ns:SASinstrument/ns:SASdetector',
1494                          namespaces={'ns': CANSAS_NS})
[e9b12eaf]1495        for item in nodes:
[2f4b430]1496
[e9b12eaf]1497            detector = Detector()
[2f4b430]1498
[e9b12eaf]1499            self._store_content('ns:name', item, 'name', detector)
[f32d144]1500            self._store_float('ns:SDD', item, 'distance', detector)
[2f4b430]1501
[e9b12eaf]1502            # Detector offset (as a vector)
[f32d144]1503            self._store_float('ns:offset/ns:x', item, 'offset.x', detector)
1504            self._store_float('ns:offset/ns:y', item, 'offset.y', detector)
1505            self._store_float('ns:offset/ns:z', item, 'offset.z', detector)
[2f4b430]1506
[e9b12eaf]1507            # Detector orientation (as a vector)
[f32d144]1508            self._store_float('ns:orientation/ns:roll', item,
1509                              'orientation.x', detector)
1510            self._store_float('ns:orientation/ns:pitch', item,
1511                              'orientation.y', detector)
1512            self._store_float('ns:orientation/ns:yaw', item,
1513                              'orientation.z', detector)
[2f4b430]1514
[e9b12eaf]1515            # Beam center (as a vector)
[f32d144]1516            self._store_float('ns:beam_center/ns:x', item,
1517                              'beam_center.x', detector)
1518            self._store_float('ns:beam_center/ns:y', item,
1519                              'beam_center.y', detector)
1520            self._store_float('ns:beam_center/ns:z', item,
1521                              'beam_center.z', detector)
[2f4b430]1522
[e9b12eaf]1523            # Pixel size (as a vector)
[f32d144]1524            self._store_float('ns:pixel_size/ns:x', item,
1525                              'pixel_size.x', detector)
1526            self._store_float('ns:pixel_size/ns:y', item,
1527                              'pixel_size.y', detector)
1528            self._store_float('ns:pixel_size/ns:z', item,
1529                              'pixel_size.z', detector)
[2f4b430]1530
[e9b12eaf]1531            self._store_float('ns:slit_length', item, 'slit_length', detector)
[2f4b430]1532
[f32d144]1533            data_info.detector.append(detector)
[e9b12eaf]1534
1535        # Processes info ######################
1536        nodes = dom.xpath('ns:SASprocess', namespaces={'ns': CANSAS_NS})
1537        for item in nodes:
1538            process = Process()
1539            self._store_content('ns:name', item, 'name', process)
1540            self._store_content('ns:date', item, 'date', process)
1541            self._store_content('ns:description', item, 'description', process)
[2f4b430]1542
[e9b12eaf]1543            term_list = item.xpath('ns:term', namespaces={'ns': CANSAS_NS})
1544            for term in term_list:
1545                try:
1546                    term_attr = {}
1547                    for attr in term.keys():
1548                        term_attr[attr] = term.get(attr).strip()
1549                    if term.text is not None:
1550                        term_attr['value'] = term.text.strip()
1551                        process.term.append(term_attr)
1552                except:
1553                    err_mess = "cansas_reader.read: error processing process term\n  %s" % sys.exc_value
1554                    self.errors.append(err_mess)
1555                    logging.error(err_mess)
[2f4b430]1556
[f32d144]1557            note_list = item.xpath('ns:SASprocessnote',
1558                                   namespaces={'ns': CANSAS_NS})
[e9b12eaf]1559            for note in note_list:
1560                if note.text is not None:
1561                    process.notes.append(note.text.strip())
[2f4b430]1562
[e9b12eaf]1563            data_info.process.append(process)
[2f4b430]1564
[e9b12eaf]1565        # Data info ######################
1566        nodes = dom.xpath('ns:SASdata', namespaces={'ns': CANSAS_NS})
[f32d144]1567        if len(nodes) > 1:
[a95ae9a]1568            raise RuntimeError, "CanSAS reader is not compatible with" + \
1569                                " multiple SASdata entries"
[2f4b430]1570
[df7ed14]1571        for entry in nodes:
[b1e609c]1572            for item in LIST_OF_DATA_2D_ATTR:
[df7ed14]1573                #get node
[f32d144]1574                node = get_content('ns:%s' % item[0], entry)
[b1e609c]1575                setattr(data_info, item[1], parse_entry_helper(node, item))
[2f4b430]1576
[b1e609c]1577            for item in LIST_OF_DATA_2D_VALUES:
[f32d144]1578                field = get_content('ns:%s' % item[0], entry)
[b1e609c]1579                value_list = []
[df7ed14]1580                if field is not None:
[b1e609c]1581                    value_list = [parse_entry_helper(node, item) for node in field]
1582                if len(value_list) < 2:
1583                    setattr(data_info, item[0], None)
[44f7c1b]1584                else:
[b1e609c]1585                    setattr(data_info, item[0], numpy.array(value_list))
[2f4b430]1586
[e9b12eaf]1587        return data_info
1588
[61cada5]1589    def _read_cansas(self, path):
[f32d144]1590        """
[e89aed5]1591        Load data and fitting information from a CanSAS XML file.
[2f4b430]1592
[5062bbf]1593        :param path: file path
[f32d144]1594        :return: Data1D object if a single SASentry was found,
[5062bbf]1595                    or a list of Data1D objects if multiple entries were found,
1596                    or None of nothing was found
1597        :raise RuntimeError: when the file can't be opened
1598        :raise ValueError: when the length of the data vectors are inconsistent
[61cada5]1599        """
1600        output = []
[e89aed5]1601        simfitstate = None
[f32d144]1602        basename = os.path.basename(path)
[b63dc6e]1603        root, extension = os.path.splitext(basename)
1604        ext = extension.lower()
[61cada5]1605        try:
1606            if os.path.isfile(path):
[a95ae9a]1607                if ext in self.ext or ext == '.xml':
[61cada5]1608                    tree = etree.parse(path, parser=etree.ETCompatXMLParser())
1609                    # Check the format version number
[a95ae9a]1610                    # Specifying the namespace will take care of the file
1611                    # format version
[61cada5]1612                    root = tree.getroot()
[f32d144]1613                    entry_list = root.xpath('ns:SASentry',
1614                                            namespaces={'ns': CANSAS_NS})
1615                    for entry in entry_list:
[e9b12eaf]1616                        try:
[ac5b69d]1617                            sas_entry, _ = self._parse_save_state_entry(entry)
[e9b12eaf]1618                        except:
[df7ed14]1619                            raise
[61cada5]1620                        fitstate = self._parse_state(entry)
[e89aed5]1621                        simfitstate = self._parse_simfit_state(entry)
[2f4b430]1622
[a95ae9a]1623                        # state could be None when .svs file is loaded
1624                        # in this case, skip appending to output
1625                        if fitstate is not None:
[b35d3d1]1626                            sas_entry.meta_data['fitstate'] = fitstate
1627                            sas_entry.filename = fitstate.file
1628                            output.append(sas_entry)
[e89aed5]1629
[61cada5]1630            else:
[b63dc6e]1631                self.call_back(format=ext)
[61cada5]1632                raise RuntimeError, "%s is not a file" % path
[b35d3d1]1633
[61cada5]1634            # Return output consistent with the loader's api
[f32d144]1635            if len(output) == 0:
1636                self.call_back(state=None, datainfo=None, format=ext)
[61cada5]1637                return None
1638            else:
[b35d3d1]1639                for ind in range(len(output)):
1640                    # Call back to post the new state
1641                    state = output[ind].meta_data['fitstate']
1642                    t = time.localtime(state.timestamp)
1643                    time_str = time.strftime("%b %d %H:%M", t)
1644                    # Check that no time stamp is already appended
1645                    max_char = state.file.find("[")
1646                    if max_char < 0:
1647                        max_char = len(state.file)
[ef16f59]1648                    original_fname = state.file[0:max_char]
[f32d144]1649                    state.file = original_fname + ' [' + time_str + ']'
[2f4b430]1650
[b35d3d1]1651                    if state is not None and state.is_data is not None:
[b1e609c]1652                        output[ind].is_data = state.is_data
[2f4b430]1653
[b35d3d1]1654                    output[ind].filename = state.file
1655                    state.data = output[ind]
[f32d144]1656                    state.data.name = output[ind].filename  # state.data_name
[b35d3d1]1657                    state.data.id = state.data_id
1658                    if state.is_data is not None:
1659                        state.data.is_data = state.is_data
[f32d144]1660                    if output[ind].run_name is not None\
1661                        and len(output[ind].run_name) != 0:
[654e8e0]1662                        if isinstance(output[ind].run_name, dict):
1663                            name = output[ind].run_name.keys()[0]
1664                        else:
1665                            name = output[ind].run_name
[f32d144]1666                    else:
1667                        name = original_fname
[ef16f59]1668                    state.data.group_id = name
[a95ae9a]1669                    # store state in fitting
[f32d144]1670                    self.call_back(state=state,
1671                                   datainfo=output[ind], format=ext)
1672                    self.state = state
[e89aed5]1673                if simfitstate is not None:
1674                    self.call_back(state=simfitstate)
1675
[ef16f59]1676                return output
[61cada5]1677        except:
[4bee68d]1678            self.call_back(format=ext)
[61cada5]1679            raise
[2f4b430]1680
[61cada5]1681    def write(self, filename, datainfo=None, fitstate=None):
1682        """
[b35d3d1]1683        Write the content of a Data1D as a CanSAS XML file only for standalone
[2f4b430]1684
[5062bbf]1685        :param filename: name of the file to write
1686        :param datainfo: Data1D object
1687        :param fitstate: PageState object
[2f4b430]1688
[61cada5]1689        """
1690        # Sanity check
[a95ae9a]1691        if self.cansas:
[61cada5]1692            # Add fitting information to the XML document
[ef16f59]1693            doc = self.write_toXML(datainfo, fitstate)
[61cada5]1694            # Write the XML document
1695        else:
[ac5b69d]1696            doc = fitstate.toXML(file=filename)
[2f4b430]1697
[ac5b69d]1698        # Save the document no matter the type
1699        fd = open(filename, 'w')
1700        fd.write(doc.toprettyxml())
1701        fd.close()
[2f4b430]1702
[a95ae9a]1703    def write_toXML(self, datainfo=None, state=None, batchfit=None):
[b35d3d1]1704        """
[f32d144]1705        Write toXML, a helper for write(),
1706        could be used by guimanager._on_save()
[2f4b430]1707
[b35d3d1]1708        : return: xml doc
1709        """
[3c44c66]1710
[a95ae9a]1711        self.batchfit_params = batchfit
1712        if state.data is None or not state.data.is_data:
[3b148c3]1713            return None
[a95ae9a]1714        # make sure title and data run are filled.
1715        if state.data.title is None or state.data.title == '':
1716            state.data.title = state.data.name
1717        if state.data.run_name is None or state.data.run_name == {}:
1718            state.data.run = [str(state.data.name)]
1719            state.data.run_name[0] = state.data.name
1720
1721        if issubclass(state.data.__class__,
1722                      sas.sascalc.dataloader.data_info.Data1D):
1723            data = state.data
1724            doc, sasentry = self._to_xml_doc(data)
[f32d144]1725        else:
[a95ae9a]1726            data = state.data
1727            doc, sasentry = self._data2d_to_xml_doc(data)
[2f4b430]1728
[b35d3d1]1729        if state is not None:
[a95ae9a]1730            doc = state.toXML(doc=doc, file=data.filename, entry_node=sasentry,
1731                              batch_fit_state=self.batchfit_params)
[2f4b430]1732
[f32d144]1733        return doc
Note: See TracBrowser for help on using the repository browser.