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

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 9cc002d was 467202f, checked in by krzywon, 8 years ago

Fixes #737: Report results edit option working again.

  • Property mode set to 100644
File size: 74.5 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
[998ca90]380        rep += "State form factor combobox selection: %s\n" % \
381               self.formfactorcombobox
382        rep += "State structure factor combobox selection: %s\n" % \
383               self.structurecombobox
[f32d144]384        rep += "is data : %s\n" % self.is_data
385        rep += "data's name : %s\n" % self.data_name
386        rep += "data's id : %s\n" % self.data_id
[a95ae9a]387        if self.model is not None:
[93f0a862]388            m_name = self.model.__class__.__name__
389            if m_name == 'Model':
390                m_name = self.m_name
[f32d144]391            rep += "model name : %s\n" % m_name
[74dc0a4]392        else:
393            rep += "model name : None\n"
[f32d144]394        rep += "multi_factor : %s\n" % str(self.multi_factor)
[2f4b430]395        rep += "magnetic_on : %s\n" % str(self.magnetic_on)
[ea5fa58]396        rep += "model type (Category) selected: %s\n" % self.categorycombobox
[f32d144]397        rep += "data : %s\n" % str(self.data)
[a95ae9a]398        rep += "Plotting Range: min: %s, max: %s, steps: %s\n" % \
399               (str(self.qmin),str(self.qmax), str(self.npts))
[f32d144]400        rep += "Dispersion selection : %s\n" % str(self.disp_box)
401        rep += "Smearing enable : %s\n" % str(self.enable_smearer)
402        rep += "Smearing disable : %s\n" % str(self.disable_smearer)
403        rep += "Pinhole smearer enable : %s\n" % str(self.pinhole_smearer)
404        rep += "Slit smearer enable : %s\n" % str(self.slit_smearer)
405        rep += "Dispersity enable : %s\n" % str(self.enable_disp)
406        rep += "Dispersity disable : %s\n" % str(self.disable_disp)
407        rep += "Slit smearer enable: %s\n" % str(self.slit_smearer)
[2f4b430]408
[f32d144]409        rep += "dI_noweight : %s\n" % str(self.dI_noweight)
410        rep += "dI_didata : %s\n" % str(self.dI_didata)
411        rep += "dI_sqrdata : %s\n" % str(self.dI_sqrdata)
412        rep += "dI_idata : %s\n" % str(self.dI_idata)
[2f4b430]413
[f32d144]414        rep += "2D enable : %s\n" % str(self.enable2D)
[a95ae9a]415        rep += "All parameters checkbox selected: %s\n" % str(self.cb1)
[f32d144]416        rep += "Value of Chisqr : %s\n" % str(self.tcChi)
417        rep += "Smear object : %s\n" % str(self.smearer)
[a95ae9a]418        rep += "Smear type : %s\n" % str(self.smear_type)
[f32d144]419        rep += "dq_l  : %s\n" % self.dq_l
420        rep += "dq_r  : %s\n" % self.dq_r
[db8fd5b]421        rep += "dx_max  : %s\n" % str(self.dx_max)
[2f4b430]422        rep += "dx_min : %s\n" % str(self.dx_min)
[db8fd5b]423        rep += "dxl  : %s\n" % str(self.dxl)
[2f4b430]424        rep += "dxw : %s\n" % str(self.dxw)
[f32d144]425        rep += "model  : %s\n\n" % str(self.model)
[2296316]426        temp_parameters = []
427        temp_fittable_param = []
428        if self.data.__class__.__name__ == "Data2D":
429            self.is_2D = True
430        else:
431            self.is_2D = False
432        if self.data is not None:
433            if not self.is_2D:
434                for item in self.parameters:
[a95ae9a]435                    if item not in self.orientation_params:
[2296316]436                        temp_parameters.append(item)
437                for item in self.fittable_param:
[a95ae9a]438                    if item not in self.orientation_params_disp:
[2296316]439                        temp_fittable_param.append(item)
440            else:
441                temp_parameters = self.parameters
442                temp_fittable_param = self.fittable_param
[2f4b430]443
[a95ae9a]444            rep += "number parameters(self.parameters): %s\n" % \
445                   len(temp_parameters)
[f32d144]446            rep = self._repr_helper(list=temp_parameters, rep=rep)
[a95ae9a]447            rep += "number str_parameters(self.str_parameters): %s\n" % \
448                   len(self.str_parameters)
[f32d144]449            rep = self._repr_helper(list=self.str_parameters, rep=rep)
[a95ae9a]450            rep += "number fittable_param(self.fittable_param): %s\n" % \
451                   len(temp_fittable_param)
[f32d144]452            rep = self._repr_helper(list=temp_fittable_param, rep=rep)
[61cada5]453        return rep
[2296316]454
455    def set_report_string(self):
456        """
[f32d144]457        Get the values (strings) from __str__ for report
[2296316]458        """
[d06ae30]459        # Dictionary of the report strings
[2296316]460        repo_time = ""
461        model_name = ""
462        title = ""
463        title_name = ""
464        file_name = ""
465        param_string = ""
466        paramval_string = ""
467        chi2_string = ""
468        q_range = ""
469        strings = self.__repr__()
470        lines = strings.split('\n')
471
472        # get all string values from __str__()
473        for line in lines:
474            value = ""
475            content = line.split(":")
476            name = content[0]
477            try:
478                value = content[1]
[7673ecd]479            except Exception:
[6c382da]480                msg = "Report string expected 'name: value' but got %r"%line
481                logging.error(msg)
[b7c6a4a]482            if name.count("State created"):
483                repo_time = "" + value
[2296316]484            if name.count("parameter name"):
485                val_name = value.split(".")
486                if len(val_name) > 1:
487                    if val_name[1].count("width"):
488                        param_string += value + ','
489                    else:
490                        continue
491                else:
492                    param_string += value + ','
493            if name == "value":
494                param_string += value + ','
[b1e609c]495            if name == "selected":
496                if value == u' False':
497                    fixed_parameter = True
498                else:
499                    fixed_parameter = False
[2296316]500            if name == "error value":
[b1e609c]501                if fixed_parameter:
502                    param_string += '(fixed),'
503                else:
[d06ae30]504                    param_string += value + ','
[2296316]505            if name == "parameter unit":
[f32d144]506                param_string += value + ':'
[2296316]507            if name == "Value of Chisqr ":
508                chi2 = ("Chi2/Npts = " + value)
509                chi2_string = CENTRE % chi2
510            if name == "Title":
511                if len(value.strip()) == 0:
512                    continue
513                title = value + " [" + repo_time + "]"
514                title_name = HEADER % title
515            if name == "data ":
516                try:
[b1e609c]517                    file_value = ("File name:" + content[2])
518                    file_name = CENTRE % file_value
[2296316]519                    if len(title) == 0:
520                        title = content[2] + " [" + repo_time + "]"
521                        title_name = HEADER % title
[7673ecd]522                except Exception:
[6c382da]523                    msg = "While parsing 'data: ...'\n"
524                    logging.error(msg + traceback.format_exc())
[74dc0a4]525            if name == "model name ":
526                try:
527                    modelname = "Model name:" + content[1]
528                except:
529                    modelname = "Model name:" + " NAN"
[f32d144]530                model_name = CENTRE % modelname
[2f4b430]531
[2296316]532            if name == "Plotting Range":
533                try:
534                    q_range = content[1] + " = " + content[2] \
535                            + " = " + content[3].split(",")[0]
536                    q_name = ("Q Range:    " + q_range)
537                    q_range = CENTRE % q_name
[7673ecd]538                except Exception:
[6c382da]539                    msg = "While parsing 'Plotting Range: ...'\n"
540                    logging.error(msg + traceback.format_exc())
[2296316]541        paramval = ""
[f32d144]542        for lines in param_string.split(":"):
[2296316]543            line = lines.split(",")
[f32d144]544            if len(lines) > 0:
545                param = line[0]
[2296316]546                param += " = " + line[1]
547                if len(line[2].split()) > 0 and not line[2].count("None"):
548                    param += " +- " + line[2]
549                if len(line[3].split()) > 0 and not line[3].count("None"):
550                    param += " " + line[3]
551                if not paramval.count(param):
[f32d144]552                    paramval += param + "\n"
[2296316]553                    paramval_string += CENTRE % param + "\n"
[2f4b430]554
[a95ae9a]555        text_string = "\n\n\n%s\n\n%s\n%s\n%s\n\n%s" % \
556                      (title, file, q_name, chi2, paramval)
[2f4b430]557
[f5bdb4a]558        title_name = self._check_html_format(title_name)
559        file_name = self._check_html_format(file_name)
560        title = self._check_html_format(title)
[2f4b430]561
[2296316]562        html_string = title_name + "\n" + file_name + \
[f32d144]563                                   "\n" + model_name + \
[2296316]564                                   "\n" + q_range + \
565                                   "\n" + chi2_string + \
566                                   "\n" + ELINE + \
567                                   "\n" + paramval_string + \
568                                   "\n" + ELINE + \
569                                   "\n" + FEET_1 % title + \
[f32d144]570                                   "\n" + FEET_2
[2f4b430]571
[2296316]572        return html_string, text_string, title
[2f4b430]573
[f5bdb4a]574    def _check_html_format(self, name):
575        """
576        Check string '%' for html format
577        """
578        if name.count('%'):
579            name = name.replace('%', '&#37')
[2f4b430]580
[f5bdb4a]581        return name
[2f4b430]582
[2296316]583    def report(self, figs=None, canvases=None):
584        """
585        Invoke report dialog panel
[2f4b430]586
[2296316]587        : param figs: list of pylab figures [list]
588        """
[d85c194]589        from sas.sasgui.perspectives.fitting.report_dialog import ReportDialog
[2296316]590        # get the strings for report
591        html_str, text_str, title = self.set_report_string()
592        # Allow 2 figures to append
593        if len(figs) == 1:
[f32d144]594            add_str = FEET_3
[2296316]595        elif len(figs) == 2:
[f32d144]596            add_str = ELINE
597            add_str += FEET_2 % ("%s")
598            add_str += ELINE
599            add_str += FEET_3
[2296316]600        elif len(figs) > 2:
[f32d144]601            add_str = ELINE
602            add_str += FEET_2 % ("%s")
603            add_str += ELINE
604            add_str += FEET_2 % ("%s")
605            add_str += ELINE
606            add_str += FEET_3
[2296316]607        else:
608            add_str = ""
[c0ff8cc]609
[2296316]610        # final report html strings
611        report_str = html_str % ("%s") + add_str
612
613        # make plot image
614        images = self.set_plot_state(figs, canvases)
[f32d144]615        report_list = [report_str, text_str, images]
[6f16e25]616        dialog = ReportDialog(report_list, None, wx.ID_ANY, "")
[d838715]617        dialog.Show()
[2f4b430]618
[eddb6ec]619    def _toXML_helper(self, thelist, element, newdoc):
[61cada5]620        """
[5062bbf]621        Helper method to create xml file for saving state
[61cada5]622        """
[eddb6ec]623        for item in thelist:
[61cada5]624            sub_element = newdoc.createElement('parameter')
625            sub_element.setAttribute('name', str(item[1]))
626            sub_element.setAttribute('value', str(item[2]))
627            sub_element.setAttribute('selected_to_fit', str(item[0]))
628            sub_element.setAttribute('error_displayed', str(item[4][0]))
629            sub_element.setAttribute('error_value', str(item[4][1]))
630            sub_element.setAttribute('minimum_displayed', str(item[5][0]))
631            sub_element.setAttribute('minimum_value', str(item[5][1]))
632            sub_element.setAttribute('maximum_displayed', str(item[6][0]))
633            sub_element.setAttribute('maximum_value', str(item[6][1]))
634            sub_element.setAttribute('unit', str(item[7]))
635            element.appendChild(sub_element)
[2f4b430]636
[a95ae9a]637    def toXML(self, file="fitting_state.fitv", doc=None,
638              entry_node=None, batch_fit_state=None):
[61cada5]639        """
[a95ae9a]640        Writes the state of the fit panel to file, as XML.
[2f4b430]641
[5062bbf]642        Compatible with standalone writing, or appending to an
[998ca90]643        already existing XML document. In that case, the XML document is
644        required. An optional entry node in the XML document may also be given.
[2f4b430]645
[5062bbf]646        :param file: file to write to
647        :param doc: XML document object [optional]
[998ca90]648        :param entry_node: XML node within the XML document at which we
649                           will append the data [optional]
[61cada5]650        """
651        from xml.dom.minidom import getDOMImplementation
[71f0373]652
[61cada5]653        # Check whether we have to write a standalone XML file
654        if doc is None:
655            impl = getDOMImplementation()
[f32d144]656            doc_type = impl.createDocumentType(FITTING_NODE_NAME, "1.0", "1.0")
[61cada5]657            newdoc = impl.createDocument(None, FITTING_NODE_NAME, doc_type)
658            top_element = newdoc.documentElement
659        else:
660            # We are appending to an existing document
661            newdoc = doc
[ac5b69d]662            try:
663                top_element = newdoc.createElement(FITTING_NODE_NAME)
664            except:
665                string = etree.tostring(doc, pretty_print=True)
666                newdoc = parseString(string)
667                top_element = newdoc.createElement(FITTING_NODE_NAME)
[61cada5]668            if entry_node is None:
669                newdoc.documentElement.appendChild(top_element)
670            else:
[ac5b69d]671                try:
672                    entry_node.appendChild(top_element)
673                except:
674                    node_name = entry_node.tag
675                    node_list = newdoc.getElementsByTagName(node_name)
676                    entry_node = node_list.item(0)
677                    entry_node.appendChild(top_element)
[2f4b430]678
[61cada5]679        attr = newdoc.createAttribute("version")
680        attr.nodeValue = '1.0'
681        top_element.setAttributeNode(attr)
[2f4b430]682
[61cada5]683        # File name
684        element = newdoc.createElement("filename")
685        if self.file is not None:
686            element.appendChild(newdoc.createTextNode(str(self.file)))
687        else:
688            element.appendChild(newdoc.createTextNode(str(file)))
689        top_element.appendChild(element)
[2f4b430]690
[11a7e11]691        element = newdoc.createElement("timestamp")
692        element.appendChild(newdoc.createTextNode(time.ctime(self.timestamp)))
693        attr = newdoc.createAttribute("epoch")
694        attr.nodeValue = str(self.timestamp)
695        element.setAttributeNode(attr)
696        top_element.appendChild(element)
[a95ae9a]697
[61cada5]698        # Inputs
699        inputs = newdoc.createElement("Attributes")
700        top_element.appendChild(inputs)
[2f4b430]701
[61cada5]702        if self.data is not None and hasattr(self.data, "group_id"):
703            self.data_group_id = self.data.group_id
704        if self.data is not None and hasattr(self.data, "is_data"):
705            self.is_data = self.data.is_data
706        if self.data is not None:
707            self.data_name = self.data.name
708        if self.data is not None and hasattr(self.data, "id"):
709            self.data_id = self.data.id
[2f4b430]710
[b1e609c]711        for item in LIST_OF_DATA_ATTRIBUTES:
[0b12abb5]712            element = newdoc.createElement(item[0])
[b1e609c]713            element.setAttribute(item[0], str(getattr(self, item[1])))
[f32d144]714            inputs.appendChild(element)
[2f4b430]715
[b1e609c]716        for item in LIST_OF_STATE_ATTRIBUTES:
[26f3dd5]717            element = newdoc.createElement(item[0])
[b1e609c]718            element.setAttribute(item[0], str(getattr(self, item[1])))
[26f3dd5]719            inputs.appendChild(element)
[2f4b430]720
[f32d144]721        # For self.values ={ disp_param_name: [vals,...],...}
722        # and for self.weights ={ disp_param_name: [weights,...],...}
[b1e609c]723        for item in LIST_OF_MODEL_ATTRIBUTES:
[11a7e11]724            element = newdoc.createElement(item[0])
[b1e609c]725            value_list = getattr(self, item[1])
726            for key, value in value_list.iteritems():
[2296316]727                sub_element = newdoc.createElement(key)
728                sub_element.setAttribute('name', str(key))
729                for val in value:
[b1e609c]730                    sub_element.appendChild(newdoc.createTextNode(str(val)))
[2f4b430]731
[f32d144]732                element.appendChild(sub_element)
[11a7e11]733            inputs.appendChild(element)
[2f4b430]734
[2296316]735        # Create doc for the dictionary of self._disp_obj_dic
[6c382da]736        for tagname, varname, tagtype in DISPERSION_LIST:
737            element = newdoc.createElement(tagname)
738            value_list = getattr(self, varname)
739            for key, value in value_list.iteritems():
[f32d144]740                sub_element = newdoc.createElement(key)
741                sub_element.setAttribute('name', str(key))
742                sub_element.setAttribute('value', str(value))
743                element.appendChild(sub_element)
744            inputs.appendChild(element)
[2f4b430]745
[b1e609c]746        for item in LIST_OF_STATE_PARAMETERS:
[61cada5]747            element = newdoc.createElement(item[0])
[998ca90]748            self._toXML_helper(thelist=getattr(self, item[1]),
749                               element=element, newdoc=newdoc)
[61cada5]750            inputs.appendChild(element)
[2f4b430]751
[a95ae9a]752        # Combined and Simultaneous Fit Parameters
753        if batch_fit_state is not None:
754            batch_combo = newdoc.createElement('simultaneous_fit')
755            top_element.appendChild(batch_combo)
756
757            # Simultaneous Fit Number For Linking Later
758            element = newdoc.createElement('sim_fit_number')
759            element.setAttribute('fit_number', str(batch_fit_state.fit_page_no))
760            batch_combo.appendChild(element)
761
762            # Save constraints
763            constraints = newdoc.createElement('constraints')
764            batch_combo.appendChild(constraints)
765            for constraint in batch_fit_state.constraints_list:
[998ca90]766                if constraint.model_cbox.GetValue() != "":
767                    # model_cbox, param_cbox, egal_txt, constraint, btRemove, sizer
768                    doc_cons = newdoc.createElement('constraint')
769                    doc_cons.setAttribute('model_cbox',
770                                          str(constraint.model_cbox.GetValue()))
771                    doc_cons.setAttribute('param_cbox',
772                                          str(constraint.param_cbox.GetValue()))
773                    doc_cons.setAttribute('egal_txt',
774                                          str(constraint.egal_txt.GetLabel()))
775                    doc_cons.setAttribute('constraint',
776                                          str(constraint.constraint.GetValue()))
777                    constraints.appendChild(doc_cons)
[a95ae9a]778
779            # Save all models
780            models = newdoc.createElement('model_list')
781            batch_combo.appendChild(models)
782            for model in batch_fit_state.model_list:
783                doc_model = newdoc.createElement('model_list_item')
784                doc_model.setAttribute('checked', str(model[0].GetValue()))
785                keys = model[1].keys()
786                doc_model.setAttribute('name', str(keys[0]))
787                values = model[1].get(keys[0])
788                doc_model.setAttribute('fit_number', str(model[2]))
789                doc_model.setAttribute('fit_page_source', str(model[3]))
790                doc_model.setAttribute('model_name', str(values.model.id))
791                models.appendChild(doc_model)
792
793            # Select All Checkbox
794            element = newdoc.createElement('select_all')
795            if batch_fit_state.select_all:
796                element.setAttribute('checked', 'True')
797            else:
798                element.setAttribute('checked', 'False')
799            batch_combo.appendChild(element)
800
[61cada5]801        # Save the file
802        if doc is None:
803            fd = open(file, 'w')
804            fd.write(newdoc.toprettyxml())
805            fd.close()
806            return None
807        else:
[ac5b69d]808            return newdoc
[2f4b430]809
[61cada5]810    def _fromXML_helper(self, node, list):
811        """
[5062bbf]812        Helper function to write state to xml
[61cada5]813        """
814        for item in node:
[0b12abb5]815            try:
816                name = item.get('name')
817            except:
818                name = None
819            try:
820                value = item.get('value')
821            except:
822                value = None
823            try:
[3ad91de]824                selected_to_fit = (item.get('selected_to_fit') == "True")
[0b12abb5]825            except:
826                selected_to_fit = None
827            try:
[3ad91de]828                error_displayed = (item.get('error_displayed') == "True")
[0b12abb5]829            except:
830                error_displayed = None
[f32d144]831            try:
[0b12abb5]832                error_value = item.get('error_value')
833            except:
834                error_value = None
835            try:
[f32d144]836                minimum_displayed = (item.get('minimum_displayed') == "True")
[0b12abb5]837            except:
838                minimum_displayed = None
839            try:
840                minimum_value = item.get('minimum_value')
841            except:
842                minimum_value = None
843            try:
[3ad91de]844                maximum_displayed = (item.get('maximum_displayed') == "True")
[0b12abb5]845            except:
846                maximum_displayed = None
847            try:
848                maximum_value = item.get('maximum_value')
849            except:
850                maximum_value = None
851            try:
852                unit = item.get('unit')
853            except:
854                unit = None
[2296316]855            list.append([selected_to_fit, name, value, "+/-",
856                         [error_displayed, error_value],
[f32d144]857                         [minimum_displayed, minimum_value],
858                         [maximum_displayed, maximum_value], unit])
[2f4b430]859
[61cada5]860    def fromXML(self, file=None, node=None):
861        """
[5062bbf]862        Load fitting state from a file
[2f4b430]863
[5062bbf]864        :param file: .fitv file
865        :param node: node of a XML document to read from
[61cada5]866        """
867        if file is not None:
[11a7e11]868            msg = "PageState no longer supports non-CanSAS"
869            msg += " format for fitting files"
870            raise RuntimeError, msg
[2f4b430]871
[e89aed5]872        if node.get('version') and node.get('version') == '1.0':
[2f4b430]873
[61cada5]874            # Get file name
875            entry = get_content('ns:filename', node)
876            if entry is not None:
877                self.file = entry.text.strip()
[2f4b430]878
[11a7e11]879            # Get time stamp
880            entry = get_content('ns:timestamp', node)
881            if entry is not None and entry.get('epoch'):
882                try:
883                    self.timestamp = float(entry.get('epoch'))
884                except:
885                    msg = "PageState.fromXML: Could not"
886                    msg += " read timestamp\n %s" % sys.exc_value
887                    logging.error(msg)
[2f4b430]888
[61cada5]889            # Parse fitting attributes
890            entry = get_content('ns:Attributes', node)
[b1e609c]891            for item in LIST_OF_DATA_ATTRIBUTES:
[f32d144]892                node = get_content('ns:%s' % item[0], entry)
[b1e609c]893                setattr(self, item[0], parse_entry_helper(node, item))
[2296316]894
[61cada5]895            if entry is not None:
[b1e609c]896                for item in LIST_OF_STATE_ATTRIBUTES:
[2296316]897                    node = get_content('ns:%s' % item[0], entry)
[b1e609c]898                    setattr(self, item[0], parse_entry_helper(node, item))
[2f4b430]899
[b1e609c]900                for item in LIST_OF_STATE_PARAMETERS:
[2296316]901                    node = get_content("ns:%s" % item[0], entry)
[b1e609c]902                    self._fromXML_helper(node=node, list=getattr(self, item[1]))
[2f4b430]903
[f32d144]904                # Recover _disp_obj_dict from xml file
905                self._disp_obj_dict = {}
[6c382da]906                for tagname, varname, tagtype in DISPERSION_LIST:
907                    node = get_content("ns:%s" % tagname, entry)
[2296316]908                    for attr in node:
[6c382da]909                        parameter = str(attr.get('name'))
910                        value = attr.get('value')
911                        if value.startswith("<"):
912                            try:
913                                # <path.to.NamedDistribution object/instance...>
914                                cls_name = value[1:].split()[0].split('.')[-1]
915                                cls = getattr(sasmodels.weights, cls_name)
916                                value = cls.type
917                            except Exception:
[998ca90]918                                base = "unable to load distribution %r for %s"
919                                logging.error(base % (value, parameter))
[6c382da]920                                continue
921                        _disp_obj_dict = getattr(self, varname)
922                        _disp_obj_dict[parameter] = value
[2f4b430]923
[f32d144]924                # get self.values and self.weights dic. if exists
[6c382da]925                for tagname, varname in LIST_OF_MODEL_ATTRIBUTES:
926                    node = get_content("ns:%s" % tagname, entry)
[2296316]927                    dic = {}
[b1e609c]928                    value_list = []
[2296316]929                    for par in node:
930                        name = par.get('name')
[6c382da]931                        values = par.text.split()
[2296316]932                        # Get lines only with numbers
933                        for line in values:
934                            try:
[f32d144]935                                val = float(line)
[b1e609c]936                                value_list.append(val)
[7673ecd]937                            except Exception:
[2296316]938                                # pass if line is empty (it happens)
[6c382da]939                                msg = ("Error reading %r from %s %s\n"
940                                       % (line, tagname, name))
941                                logging.error(msg + traceback.format_exc())
[0e33a8d]942                        dic[name] = numpy.array(value_list)
[6c382da]943                    setattr(self, varname, dic)
[2f4b430]944
[2296316]945    def set_plot_state(self, figs, canvases):
946        """
947        Build image state that wx.html understand
948        by plotting, putting it into wx.FileSystem image object
949
950        """
951        images = []
952        # some imports
953        import wx
954
955        # Reset memory
956        self.imgRAM = None
957        wx.MemoryFSHandler()
[2f4b430]958
[2296316]959        # For no figures in the list, prepare empty plot
960        if figs == None or len(figs) == 0:
961            figs = [None]
[2f4b430]962
[f32d144]963        # Loop over the list of figures
[2296316]964        # use wx.MemoryFSHandler
[f32d144]965        self.imgRAM = wx.MemoryFSHandler()
[2296316]966        for fig in figs:
967            if figs != None:
968                ind = figs.index(fig)
969                canvas = canvases[ind]
[2f4b430]970
[b1e609c]971            #store the image in wx.FileSystem Object
[2296316]972            wx.FileSystem.AddHandler(wx.MemoryFSHandler())
[2f4b430]973
[2296316]974            # index of the fig
975            ind = figs.index(fig)
[2f4b430]976
[2296316]977            #AddFile, image can be retrieved with 'memory:filename'
[f32d144]978            self.imgRAM.AddFile('img_fit%s.png' % ind,
[2296316]979                                canvas.bitmap, wx.BITMAP_TYPE_PNG)
[2f4b430]980
[2296316]981            #append figs
982            images.append(fig)
[2f4b430]983
[c0ff8cc]984        return images
[61cada5]985
[f32d144]986
[61cada5]987class Reader(CansasReader):
988    """
[5062bbf]989    Class to load a .fitv fitting file
[61cada5]990    """
991    ## File type
992    type_name = "Fitting"
[2f4b430]993
[61cada5]994    ## Wildcards
[b35d3d1]995    type = ["Fitting files (*.fitv)|*.fitv"
[b9a5f0e]996            "SASView file (*.svs)|*.svs"]
[61cada5]997    ## List of allowed extensions
[f32d144]998    ext = ['.fitv', '.FITV', '.svs', 'SVS']
[2f4b430]999
[61cada5]1000    def __init__(self, call_back=None, cansas=True):
[df7ed14]1001        CansasReader.__init__(self)
[61cada5]1002        """
[5062bbf]1003        Initialize the call-back method to be called
1004        after we load a file
[2f4b430]1005
[5062bbf]1006        :param call_back: call-back method
1007        :param cansas:  True = files will be written/read in CanSAS format
1008                        False = write CanSAS format
[2f4b430]1009
[61cada5]1010        """
1011        ## Call back method to be executed after a file is read
1012        self.call_back = call_back
1013        ## CanSAS format flag
1014        self.cansas = cansas
[75fbd17]1015        self.state = None
[a95ae9a]1016        # batch fitting params for saving
1017        self.batchfit_params = []
[2f4b430]1018
[75fbd17]1019    def get_state(self):
1020        return self.state
[2f4b430]1021
[61cada5]1022    def read(self, path):
[f32d144]1023        """
[5062bbf]1024        Load a new P(r) inversion state from file
[2f4b430]1025
[5062bbf]1026        :param path: file path
[2f4b430]1027
[61cada5]1028        """
[e9b12eaf]1029        if self.cansas == True:
[61cada5]1030            return self._read_cansas(path)
[2f4b430]1031
[e9b12eaf]1032    def _data2d_to_xml_doc(self, datainfo):
[61cada5]1033        """
[5062bbf]1034        Create an XML document to contain the content of a Data2D
[2f4b430]1035
[5062bbf]1036        :param datainfo: Data2D object
[2f4b430]1037
[61cada5]1038        """
1039        if not issubclass(datainfo.__class__, Data2D):
1040            raise RuntimeError, "The cansas writer expects a Data2D instance"
[2f4b430]1041
[998ca90]1042        title = "cansas1d/%s" % self.version
1043        title += "http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd"
[61cada5]1044        doc = xml.dom.minidom.Document()
1045        main_node = doc.createElement("SASroot")
1046        main_node.setAttribute("version", self.version)
[df7ed14]1047        main_node.setAttribute("xmlns", "cansas1d/%s" % self.version)
[998ca90]1048        main_node.setAttribute("xmlns:xsi",
1049                               "http://www.w3.org/2001/XMLSchema-instance")
1050        main_node.setAttribute("xsi:schemaLocation", title)
[2f4b430]1051
[61cada5]1052        doc.appendChild(main_node)
[2f4b430]1053
[61cada5]1054        entry_node = doc.createElement("SASentry")
1055        main_node.appendChild(entry_node)
[2f4b430]1056
[61cada5]1057        write_node(doc, entry_node, "Title", datainfo.title)
[df7ed14]1058        if datainfo is not None:
[998ca90]1059            write_node(doc, entry_node, "data_class",
1060                       datainfo.__class__.__name__)
[61cada5]1061        for item in datainfo.run:
1062            runname = {}
[998ca90]1063            if datainfo.run_name.has_key(item) and \
1064                            len(str(datainfo.run_name[item])) > 1:
[b1e609c]1065                runname = {'name': datainfo.run_name[item]}
[61cada5]1066            write_node(doc, entry_node, "Run", item, runname)
1067        # Data info
[df7ed14]1068        new_node = doc.createElement("SASdata")
1069        entry_node.appendChild(new_node)
[b1e609c]1070        for item in LIST_OF_DATA_2D_ATTR:
[e9b12eaf]1071            element = doc.createElement(item[0])
[b1e609c]1072            element.setAttribute(item[0], str(getattr(datainfo, item[1])))
[df7ed14]1073            new_node.appendChild(element)
[2f4b430]1074
[b1e609c]1075        for item in LIST_OF_DATA_2D_VALUES:
[df7ed14]1076            root_node = doc.createElement(item[0])
1077            new_node.appendChild(root_node)
[38226d26]1078            temp_list = None
[b1e609c]1079            temp_list = getattr(datainfo, item[1])
[df7ed14]1080
[2f4b430]1081            if temp_list is None or len(temp_list) == 0:
[df7ed14]1082                element = doc.createElement(item[0])
[b1e609c]1083                element.appendChild(doc.createTextNode(str(temp_list)))
[df7ed14]1084                root_node.appendChild(element)
[35b556d]1085            else:
1086                for value in temp_list:
[df7ed14]1087                    element = doc.createElement(item[0])
[b1e609c]1088                    element.setAttribute(item[0], str(value))
[df7ed14]1089                    root_node.appendChild(element)
[2f4b430]1090
[61cada5]1091        # Sample info
1092        sample = doc.createElement("SASsample")
1093        if datainfo.sample.name is not None:
1094            sample.setAttribute("name", str(datainfo.sample.name))
1095        entry_node.appendChild(sample)
1096        write_node(doc, sample, "ID", str(datainfo.sample.ID))
[f32d144]1097        write_node(doc, sample, "thickness", datainfo.sample.thickness,
1098                   {"unit": datainfo.sample.thickness_unit})
[61cada5]1099        write_node(doc, sample, "transmission", datainfo.sample.transmission)
[f32d144]1100        write_node(doc, sample, "temperature", datainfo.sample.temperature,
1101                   {"unit": datainfo.sample.temperature_unit})
[2f4b430]1102
[61cada5]1103        for item in datainfo.sample.details:
1104            write_node(doc, sample, "details", item)
[2f4b430]1105
[61cada5]1106        pos = doc.createElement("position")
[f32d144]1107        written = write_node(doc, pos, "x", datainfo.sample.position.x,
1108                             {"unit": datainfo.sample.position_unit})
1109        written = written | write_node(doc, pos, "y",
1110                                       datainfo.sample.position.y,
1111                                       {"unit": datainfo.sample.position_unit})
1112        written = written | write_node(doc, pos, "z",
1113                                       datainfo.sample.position.z,
1114                                       {"unit": datainfo.sample.position_unit})
[61cada5]1115        if written == True:
1116            sample.appendChild(pos)
[2f4b430]1117
[61cada5]1118        ori = doc.createElement("orientation")
[f32d144]1119        written = write_node(doc, ori, "roll", datainfo.sample.orientation.x,
1120                             {"unit": datainfo.sample.orientation_unit})
1121        written = written | write_node(doc, ori, "pitch",
1122                                       datainfo.sample.orientation.y,
1123                                       {"unit": datainfo.sample.orientation_unit})
1124        written = written | write_node(doc, ori, "yaw",
1125                                       datainfo.sample.orientation.z,
1126                                       {"unit": datainfo.sample.orientation_unit})
[61cada5]1127        if written == True:
1128            sample.appendChild(ori)
[2f4b430]1129
[61cada5]1130        # Instrument info
1131        instr = doc.createElement("SASinstrument")
1132        entry_node.appendChild(instr)
[2f4b430]1133
[61cada5]1134        write_node(doc, instr, "name", datainfo.instrument)
[2f4b430]1135
[61cada5]1136        #   Source
1137        source = doc.createElement("SASsource")
1138        if datainfo.source.name is not None:
1139            source.setAttribute("name", str(datainfo.source.name))
1140        instr.appendChild(source)
[2f4b430]1141
[61cada5]1142        write_node(doc, source, "radiation", datainfo.source.radiation)
1143        write_node(doc, source, "beam_shape", datainfo.source.beam_shape)
1144        size = doc.createElement("beam_size")
1145        if datainfo.source.beam_size_name is not None:
1146            size.setAttribute("name", str(datainfo.source.beam_size_name))
[f32d144]1147        written = write_node(doc, size, "x", datainfo.source.beam_size.x,
1148                             {"unit": datainfo.source.beam_size_unit})
1149        written = written | write_node(doc, size, "y",
1150                                       datainfo.source.beam_size.y,
1151                                       {"unit": datainfo.source.beam_size_unit})
1152        written = written | write_node(doc, size, "z",
1153                                       datainfo.source.beam_size.z,
1154                                       {"unit": datainfo.source.beam_size_unit})
[61cada5]1155        if written == True:
1156            source.appendChild(size)
[2f4b430]1157
[f32d144]1158        write_node(doc, source, "wavelength", datainfo.source.wavelength,
1159                   {"unit": datainfo.source.wavelength_unit})
1160        write_node(doc, source, "wavelength_min",
1161                   datainfo.source.wavelength_min,
1162                   {"unit": datainfo.source.wavelength_min_unit})
1163        write_node(doc, source, "wavelength_max",
1164                   datainfo.source.wavelength_max,
1165                   {"unit": datainfo.source.wavelength_max_unit})
1166        write_node(doc, source, "wavelength_spread",
1167                   datainfo.source.wavelength_spread,
1168                   {"unit": datainfo.source.wavelength_spread_unit})
[2f4b430]1169
[61cada5]1170        #   Collimation
1171        for item in datainfo.collimation:
1172            coll = doc.createElement("SAScollimation")
1173            if item.name is not None:
1174                coll.setAttribute("name", str(item.name))
1175            instr.appendChild(coll)
[2f4b430]1176
[f32d144]1177            write_node(doc, coll, "length", item.length,
1178                       {"unit": item.length_unit})
[2f4b430]1179
[61cada5]1180            for apert in item.aperture:
1181                ap = doc.createElement("aperture")
1182                if apert.name is not None:
1183                    ap.setAttribute("name", str(apert.name))
1184                if apert.type is not None:
1185                    ap.setAttribute("type", str(apert.type))
1186                coll.appendChild(ap)
[2f4b430]1187
[f32d144]1188                write_node(doc, ap, "distance", apert.distance,
1189                           {"unit": apert.distance_unit})
[2f4b430]1190
[61cada5]1191                size = doc.createElement("size")
1192                if apert.size_name is not None:
1193                    size.setAttribute("name", str(apert.size_name))
[f32d144]1194                written = write_node(doc, size, "x", apert.size.x,
1195                                     {"unit": apert.size_unit})
1196                written = written | write_node(doc, size, "y", apert.size.y,
1197                                               {"unit": apert.size_unit})
1198                written = written | write_node(doc, size, "z", apert.size.z,
1199                                               {"unit": apert.size_unit})
[61cada5]1200                if written == True:
1201                    ap.appendChild(size)
1202
1203        #   Detectors
1204        for item in datainfo.detector:
1205            det = doc.createElement("SASdetector")
1206            written = write_node(doc, det, "name", item.name)
[f32d144]1207            written = written | write_node(doc, det, "SDD", item.distance,
1208                                           {"unit": item.distance_unit})
1209            written = written | write_node(doc, det, "slit_length",
1210                                           item.slit_length,
1211                                           {"unit": item.slit_length_unit})
[61cada5]1212            if written == True:
1213                instr.appendChild(det)
[2f4b430]1214
[61cada5]1215            off = doc.createElement("offset")
[f32d144]1216            written = write_node(doc, off, "x", item.offset.x,
1217                                 {"unit": item.offset_unit})
1218            written = written | write_node(doc, off, "y", item.offset.y,
1219                                           {"unit": item.offset_unit})
1220            written = written | write_node(doc, off, "z", item.offset.z,
1221                                           {"unit": item.offset_unit})
[61cada5]1222            if written == True:
1223                det.appendChild(off)
[2f4b430]1224
[61cada5]1225            center = doc.createElement("beam_center")
[f32d144]1226            written = write_node(doc, center, "x", item.beam_center.x,
1227                                 {"unit": item.beam_center_unit})
1228            written = written | write_node(doc, center, "y",
1229                                           item.beam_center.y,
1230                                           {"unit": item.beam_center_unit})
1231            written = written | write_node(doc, center, "z",
1232                                           item.beam_center.z,
1233                                           {"unit": item.beam_center_unit})
[61cada5]1234            if written == True:
1235                det.appendChild(center)
[2f4b430]1236
[61cada5]1237            pix = doc.createElement("pixel_size")
[f32d144]1238            written = write_node(doc, pix, "x", item.pixel_size.x,
1239                                 {"unit": item.pixel_size_unit})
1240            written = written | write_node(doc, pix, "y", item.pixel_size.y,
1241                                           {"unit": item.pixel_size_unit})
1242            written = written | write_node(doc, pix, "z", item.pixel_size.z,
1243                                           {"unit": item.pixel_size_unit})
[61cada5]1244            if written == True:
1245                det.appendChild(pix)
[2f4b430]1246
[61cada5]1247            ori = doc.createElement("orientation")
[f32d144]1248            written = write_node(doc, ori, "roll", item.orientation.x,
1249                                 {"unit": item.orientation_unit})
1250            written = written | write_node(doc, ori, "pitch",
1251                                           item.orientation.y,
1252                                           {"unit": item.orientation_unit})
1253            written = written | write_node(doc, ori, "yaw", item.orientation.z,
1254                                           {"unit": item.orientation_unit})
[61cada5]1255            if written == True:
1256                det.appendChild(ori)
[2f4b430]1257
[61cada5]1258        # Processes info
1259        for item in datainfo.process:
1260            node = doc.createElement("SASprocess")
1261            entry_node.appendChild(node)
1262
1263            write_node(doc, node, "name", item.name)
1264            write_node(doc, node, "date", item.date)
1265            write_node(doc, node, "description", item.description)
1266            for term in item.term:
1267                value = term['value']
1268                del term['value']
1269                write_node(doc, node, "term", value, term)
1270            for note in item.notes:
1271                write_node(doc, node, "SASprocessnote", note)
1272        # Return the document, and the SASentry node associated with
1273        # the data we just wrote
1274        return doc, entry_node
[2f4b430]1275
[61cada5]1276    def _parse_state(self, entry):
1277        """
[5062bbf]1278        Read a fit result from an XML node
[2f4b430]1279
[f32d144]1280        :param entry: XML node to read from
[5062bbf]1281        :return: PageState object
[61cada5]1282        """
1283        # Create an empty state
[2f4b430]1284        state = None
[61cada5]1285        # Locate the P(r) node
1286        try:
[f32d144]1287            nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME,
1288                                namespaces={'ns': CANSAS_NS})
[e89aed5]1289            if nodes:
[b35d3d1]1290                # Create an empty state
[f32d144]1291                state = PageState()
[b35d3d1]1292                state.fromXML(node=nodes[0])
[2f4b430]1293
[61cada5]1294        except:
[6c382da]1295            logging.info("XML document does not contain fitting information.\n"
1296                         + traceback.format_exc())
[2f4b430]1297
[61cada5]1298        return state
[2f4b430]1299
[e89aed5]1300    def _parse_simfit_state(self, entry):
1301        """
1302        Parses the saved data for a simultaneous fit
1303        :param entry: XML object to read from
1304        :return: XML object for a simultaneous fit or None
1305        """
1306        nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME,
1307                            namespaces={'ns': CANSAS_NS})
1308        if nodes:
1309            simfitstate = nodes[0].xpath('ns:simultaneous_fit',
1310                                         namespaces={'ns': CANSAS_NS})
1311            if simfitstate:
1312                from simfitpage import SimFitPageState
1313                sim_fit_state = SimFitPageState()
1314                simfitstate_0 = simfitstate[0]
1315                all = simfitstate_0.xpath('ns:select_all',
1316                                        namespaces={'ns': CANSAS_NS})
1317                atts = all[0].attrib
1318                checked = atts.get('checked')
1319                sim_fit_state.select_all = bool(checked)
1320                model_list = simfitstate_0.xpath('ns:model_list',
1321                                               namespaces={'ns': CANSAS_NS})
1322                model_list_items = model_list[0].xpath('ns:model_list_item',
[998ca90]1323                                                   namespaces={'ns': CANSAS_NS})
[e89aed5]1324                for model in model_list_items:
1325                    attrs = model.attrib
1326                    sim_fit_state.model_list.append(attrs)
[998ca90]1327
[e89aed5]1328                constraints = simfitstate_0.xpath('ns:constraints',
1329                                                namespaces={'ns': CANSAS_NS})
1330                constraint_list = constraints[0].xpath('ns:constraint',
[998ca90]1331                                               namespaces={'ns': CANSAS_NS})
[e89aed5]1332                for constraint in constraint_list:
1333                    attrs = constraint.attrib
1334                    sim_fit_state.constraints_list.append(attrs)
1335
1336                return sim_fit_state
1337            else:
1338                return None
1339
[ac5b69d]1340    def _parse_save_state_entry(self, dom):
[e9b12eaf]1341        """
[5062bbf]1342        Parse a SASentry
[2f4b430]1343
[5062bbf]1344        :param node: SASentry node
[2f4b430]1345
[df7ed14]1346        :return: Data1D/Data2D object
[2f4b430]1347
[e9b12eaf]1348        """
[df7ed14]1349        node = dom.xpath('ns:data_class', namespaces={'ns': CANSAS_NS})
1350        if not node or node[0].text.lstrip().rstrip() != "Data2D":
[ac5b69d]1351            return_value, _ = self._parse_entry(dom)
1352            numpy.trim_zeros(return_value.x)
1353            numpy.trim_zeros(return_value.y)
1354            numpy.trim_zeros(return_value.dy)
1355            size_dx = return_value.dx.size
1356            size_dxl = return_value.dxl.size
1357            size_dxw = return_value.dxw.size
1358            if size_dxl == 0 and size_dxw == 0:
1359                return_value.dxl = None
1360                return_value.dxw = None
1361                numpy.trim_zeros(return_value.dx)
1362            elif size_dx == 0:
1363                return_value.dx = None
1364                size_dx = size_dxl
1365                numpy.trim_zeros(return_value.dxl)
1366                numpy.trim_zeros(return_value.dxw)
[2f4b430]1367
[ac5b69d]1368            return return_value, _
[2f4b430]1369
[df7ed14]1370        #Parse 2D
[e9b12eaf]1371        data_info = Data2D()
[2f4b430]1372
[f32d144]1373        # Look up title
[e9b12eaf]1374        self._store_content('ns:Title', dom, 'title', data_info)
[2f4b430]1375
[f32d144]1376        # Look up run number
[e9b12eaf]1377        nodes = dom.xpath('ns:Run', namespaces={'ns': CANSAS_NS})
[f32d144]1378        for item in nodes:
[e9b12eaf]1379            if item.text is not None:
1380                value = item.text.strip()
1381                if len(value) > 0:
1382                    data_info.run.append(value)
1383                    if item.get('name') is not None:
1384                        data_info.run_name[value] = item.get('name')
[2f4b430]1385
[f32d144]1386        # Look up instrument name
1387        self._store_content('ns:SASinstrument/ns:name', dom,
1388                            'instrument', data_info)
[e9b12eaf]1389
1390        # Notes
1391        note_list = dom.xpath('ns:SASnote', namespaces={'ns': CANSAS_NS})
1392        for note in note_list:
1393            try:
1394                if note.text is not None:
1395                    note_value = note.text.strip()
1396                    if len(note_value) > 0:
1397                        data_info.notes.append(note_value)
[7673ecd]1398            except Exception:
[998ca90]1399                err_mess = "cansas_reader.read: error processing entry notes\n"
1400                err_mess += %s" % sys.exc_value
[e9b12eaf]1401                self.errors.append(err_mess)
1402                logging.error(err_mess)
[2f4b430]1403
[e9b12eaf]1404        # Sample info ###################
1405        entry = get_content('ns:SASsample', dom)
1406        if entry is not None:
1407            data_info.sample.name = entry.get('name')
[2f4b430]1408
[b1e609c]1409        self._store_content('ns:SASsample/ns:ID', dom, 'ID', data_info.sample)
[998ca90]1410        self._store_float('ns:SASsample/ns:thickness', dom, 'thickness',
1411                          data_info.sample)
1412        self._store_float('ns:SASsample/ns:transmission', dom, 'transmission',
1413                          data_info.sample)
1414        self._store_float('ns:SASsample/ns:temperature', dom, 'temperature',
1415                          data_info.sample)
1416
1417        nodes = dom.xpath('ns:SASsample/ns:details',
1418                          namespaces={'ns': CANSAS_NS})
[e9b12eaf]1419        for item in nodes:
1420            try:
1421                if item.text is not None:
1422                    detail_value = item.text.strip()
1423                    if len(detail_value) > 0:
1424                        data_info.sample.details.append(detail_value)
[7673ecd]1425            except Exception:
[998ca90]1426                err_mess = "cansas_reader.read: error processing entry notes\n"
1427                err_mess += %s" % sys.exc_value
[e9b12eaf]1428                self.errors.append(err_mess)
1429                logging.error(err_mess)
[2f4b430]1430
[e9b12eaf]1431        # Position (as a vector)
[998ca90]1432        self._store_float('ns:SASsample/ns:position/ns:x', dom, 'position.x',
1433                          data_info.sample)
1434        self._store_float('ns:SASsample/ns:position/ns:y', dom, 'position.y',
1435                          data_info.sample)
1436        self._store_float('ns:SASsample/ns:position/ns:z', dom, 'position.z',
1437                          data_info.sample)
[2f4b430]1438
[e9b12eaf]1439        # Orientation (as a vector)
[f32d144]1440        self._store_float('ns:SASsample/ns:orientation/ns:roll',
[b1e609c]1441                          dom, 'orientation.x', data_info.sample)
[f32d144]1442        self._store_float('ns:SASsample/ns:orientation/ns:pitch',
[b1e609c]1443                          dom, 'orientation.y', data_info.sample)
[f32d144]1444        self._store_float('ns:SASsample/ns:orientation/ns:yaw',
[b1e609c]1445                          dom, 'orientation.z', data_info.sample)
[2f4b430]1446
[e9b12eaf]1447        # Source info ###################
1448        entry = get_content('ns:SASinstrument/ns:SASsource', dom)
1449        if entry is not None:
1450            data_info.source.name = entry.get('name')
[2f4b430]1451
[f32d144]1452        self._store_content('ns:SASinstrument/ns:SASsource/ns:radiation',
[b1e609c]1453                            dom, 'radiation', data_info.source)
[f32d144]1454        self._store_content('ns:SASinstrument/ns:SASsource/ns:beam_shape',
[b1e609c]1455                            dom, 'beam_shape', data_info.source)
[f32d144]1456        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength',
[b1e609c]1457                          dom, 'wavelength', data_info.source)
[f32d144]1458        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_min',
[b1e609c]1459                          dom, 'wavelength_min', data_info.source)
[f32d144]1460        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_max',
[b1e609c]1461                          dom, 'wavelength_max', data_info.source)
[f32d144]1462        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_spread',
[b1e609c]1463                          dom, 'wavelength_spread', data_info.source)
[2f4b430]1464
[f32d144]1465        # Beam size (as a vector)
[e9b12eaf]1466        entry = get_content('ns:SASinstrument/ns:SASsource/ns:beam_size', dom)
1467        if entry is not None:
1468            data_info.source.beam_size_name = entry.get('name')
[2f4b430]1469
[f32d144]1470        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:x',
[b1e609c]1471                          dom, 'beam_size.x', data_info.source)
[f32d144]1472        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:y',
[b1e609c]1473                          dom, 'beam_size.y', data_info.source)
[f32d144]1474        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:z',
[b1e609c]1475                          dom, 'beam_size.z', data_info.source)
[2f4b430]1476
[e9b12eaf]1477        # Collimation info ###################
[f32d144]1478        nodes = dom.xpath('ns:SASinstrument/ns:SAScollimation',
1479                          namespaces={'ns': CANSAS_NS})
[e9b12eaf]1480        for item in nodes:
1481            collim = Collimation()
1482            if item.get('name') is not None:
1483                collim.name = item.get('name')
[f32d144]1484            self._store_float('ns:length', item, 'length', collim)
[2f4b430]1485
[e9b12eaf]1486            # Look for apertures
[f32d144]1487            apert_list = item.xpath('ns:aperture',
1488                                    namespaces={'ns': CANSAS_NS})
[e9b12eaf]1489            for apert in apert_list:
[f32d144]1490                aperture = Aperture()
[2f4b430]1491
[e9b12eaf]1492                # Get the name and type of the aperture
1493                aperture.name = apert.get('name')
1494                aperture.type = apert.get('type')
[2f4b430]1495
[f32d144]1496                self._store_float('ns:distance', apert, 'distance', aperture)
[2f4b430]1497
[e9b12eaf]1498                entry = get_content('ns:size', apert)
1499                if entry is not None:
1500                    aperture.size_name = entry.get('name')
[2f4b430]1501
[f32d144]1502                self._store_float('ns:size/ns:x', apert, 'size.x', aperture)
1503                self._store_float('ns:size/ns:y', apert, 'size.y', aperture)
[e9b12eaf]1504                self._store_float('ns:size/ns:z', apert, 'size.z', aperture)
[2f4b430]1505
[e9b12eaf]1506                collim.aperture.append(aperture)
[2f4b430]1507
[e9b12eaf]1508            data_info.collimation.append(collim)
[2f4b430]1509
[e9b12eaf]1510        # Detector info ######################
[f32d144]1511        nodes = dom.xpath('ns:SASinstrument/ns:SASdetector',
1512                          namespaces={'ns': CANSAS_NS})
[e9b12eaf]1513        for item in nodes:
[2f4b430]1514
[e9b12eaf]1515            detector = Detector()
[2f4b430]1516
[e9b12eaf]1517            self._store_content('ns:name', item, 'name', detector)
[f32d144]1518            self._store_float('ns:SDD', item, 'distance', detector)
[2f4b430]1519
[e9b12eaf]1520            # Detector offset (as a vector)
[f32d144]1521            self._store_float('ns:offset/ns:x', item, 'offset.x', detector)
1522            self._store_float('ns:offset/ns:y', item, 'offset.y', detector)
1523            self._store_float('ns:offset/ns:z', item, 'offset.z', detector)
[2f4b430]1524
[e9b12eaf]1525            # Detector orientation (as a vector)
[f32d144]1526            self._store_float('ns:orientation/ns:roll', item,
1527                              'orientation.x', detector)
1528            self._store_float('ns:orientation/ns:pitch', item,
1529                              'orientation.y', detector)
1530            self._store_float('ns:orientation/ns:yaw', item,
1531                              'orientation.z', detector)
[2f4b430]1532
[e9b12eaf]1533            # Beam center (as a vector)
[f32d144]1534            self._store_float('ns:beam_center/ns:x', item,
1535                              'beam_center.x', detector)
1536            self._store_float('ns:beam_center/ns:y', item,
1537                              'beam_center.y', detector)
1538            self._store_float('ns:beam_center/ns:z', item,
1539                              'beam_center.z', detector)
[2f4b430]1540
[e9b12eaf]1541            # Pixel size (as a vector)
[f32d144]1542            self._store_float('ns:pixel_size/ns:x', item,
1543                              'pixel_size.x', detector)
1544            self._store_float('ns:pixel_size/ns:y', item,
1545                              'pixel_size.y', detector)
1546            self._store_float('ns:pixel_size/ns:z', item,
1547                              'pixel_size.z', detector)
[2f4b430]1548
[e9b12eaf]1549            self._store_float('ns:slit_length', item, 'slit_length', detector)
[2f4b430]1550
[f32d144]1551            data_info.detector.append(detector)
[e9b12eaf]1552
1553        # Processes info ######################
1554        nodes = dom.xpath('ns:SASprocess', namespaces={'ns': CANSAS_NS})
1555        for item in nodes:
1556            process = Process()
1557            self._store_content('ns:name', item, 'name', process)
1558            self._store_content('ns:date', item, 'date', process)
1559            self._store_content('ns:description', item, 'description', process)
[2f4b430]1560
[e9b12eaf]1561            term_list = item.xpath('ns:term', namespaces={'ns': CANSAS_NS})
1562            for term in term_list:
1563                try:
1564                    term_attr = {}
1565                    for attr in term.keys():
1566                        term_attr[attr] = term.get(attr).strip()
1567                    if term.text is not None:
1568                        term_attr['value'] = term.text.strip()
1569                        process.term.append(term_attr)
1570                except:
[998ca90]1571                    err_mess = "cansas_reader.read: error processing "
1572                    err_mess += "entry notes\n  %s" % sys.exc_value
[e9b12eaf]1573                    self.errors.append(err_mess)
1574                    logging.error(err_mess)
[2f4b430]1575
[f32d144]1576            note_list = item.xpath('ns:SASprocessnote',
1577                                   namespaces={'ns': CANSAS_NS})
[e9b12eaf]1578            for note in note_list:
1579                if note.text is not None:
1580                    process.notes.append(note.text.strip())
[2f4b430]1581
[e9b12eaf]1582            data_info.process.append(process)
[2f4b430]1583
[e9b12eaf]1584        # Data info ######################
1585        nodes = dom.xpath('ns:SASdata', namespaces={'ns': CANSAS_NS})
[f32d144]1586        if len(nodes) > 1:
[a95ae9a]1587            raise RuntimeError, "CanSAS reader is not compatible with" + \
1588                                " multiple SASdata entries"
[2f4b430]1589
[df7ed14]1590        for entry in nodes:
[b1e609c]1591            for item in LIST_OF_DATA_2D_ATTR:
[df7ed14]1592                #get node
[f32d144]1593                node = get_content('ns:%s' % item[0], entry)
[b1e609c]1594                setattr(data_info, item[1], parse_entry_helper(node, item))
[2f4b430]1595
[b1e609c]1596            for item in LIST_OF_DATA_2D_VALUES:
[f32d144]1597                field = get_content('ns:%s' % item[0], entry)
[b1e609c]1598                value_list = []
[df7ed14]1599                if field is not None:
[998ca90]1600                    value_list = \
1601                        [parse_entry_helper(node, item) for node in field]
[b1e609c]1602                if len(value_list) < 2:
1603                    setattr(data_info, item[0], None)
[44f7c1b]1604                else:
[b1e609c]1605                    setattr(data_info, item[0], numpy.array(value_list))
[2f4b430]1606
[e9b12eaf]1607        return data_info
1608
[61cada5]1609    def _read_cansas(self, path):
[f32d144]1610        """
[e89aed5]1611        Load data and fitting information from a CanSAS XML file.
[2f4b430]1612
[5062bbf]1613        :param path: file path
[f32d144]1614        :return: Data1D object if a single SASentry was found,
[5062bbf]1615                    or a list of Data1D objects if multiple entries were found,
1616                    or None of nothing was found
1617        :raise RuntimeError: when the file can't be opened
1618        :raise ValueError: when the length of the data vectors are inconsistent
[61cada5]1619        """
1620        output = []
[e89aed5]1621        simfitstate = None
[f32d144]1622        basename = os.path.basename(path)
[b63dc6e]1623        root, extension = os.path.splitext(basename)
1624        ext = extension.lower()
[61cada5]1625        try:
1626            if os.path.isfile(path):
[a95ae9a]1627                if ext in self.ext or ext == '.xml':
[61cada5]1628                    tree = etree.parse(path, parser=etree.ETCompatXMLParser())
1629                    # Check the format version number
[a95ae9a]1630                    # Specifying the namespace will take care of the file
1631                    # format version
[61cada5]1632                    root = tree.getroot()
[f32d144]1633                    entry_list = root.xpath('ns:SASentry',
1634                                            namespaces={'ns': CANSAS_NS})
[998ca90]1635                    name_map = {}
[f32d144]1636                    for entry in entry_list:
[e9b12eaf]1637                        try:
[ac5b69d]1638                            sas_entry, _ = self._parse_save_state_entry(entry)
[e9b12eaf]1639                        except:
[df7ed14]1640                            raise
[61cada5]1641                        fitstate = self._parse_state(entry)
[2f4b430]1642
[a95ae9a]1643                        # state could be None when .svs file is loaded
1644                        # in this case, skip appending to output
1645                        if fitstate is not None:
[b35d3d1]1646                            sas_entry.meta_data['fitstate'] = fitstate
1647                            sas_entry.filename = fitstate.file
1648                            output.append(sas_entry)
[e89aed5]1649
[61cada5]1650            else:
[b63dc6e]1651                self.call_back(format=ext)
[61cada5]1652                raise RuntimeError, "%s is not a file" % path
[b35d3d1]1653
[61cada5]1654            # Return output consistent with the loader's api
[f32d144]1655            if len(output) == 0:
1656                self.call_back(state=None, datainfo=None, format=ext)
[61cada5]1657                return None
1658            else:
[b35d3d1]1659                for ind in range(len(output)):
1660                    # Call back to post the new state
1661                    state = output[ind].meta_data['fitstate']
1662                    t = time.localtime(state.timestamp)
1663                    time_str = time.strftime("%b %d %H:%M", t)
1664                    # Check that no time stamp is already appended
1665                    max_char = state.file.find("[")
1666                    if max_char < 0:
1667                        max_char = len(state.file)
[ef16f59]1668                    original_fname = state.file[0:max_char]
[f32d144]1669                    state.file = original_fname + ' [' + time_str + ']'
[2f4b430]1670
[b35d3d1]1671                    if state is not None and state.is_data is not None:
[b1e609c]1672                        output[ind].is_data = state.is_data
[2f4b430]1673
[b35d3d1]1674                    output[ind].filename = state.file
1675                    state.data = output[ind]
[f32d144]1676                    state.data.name = output[ind].filename  # state.data_name
[b35d3d1]1677                    state.data.id = state.data_id
1678                    if state.is_data is not None:
1679                        state.data.is_data = state.is_data
[f32d144]1680                    if output[ind].run_name is not None\
1681                        and len(output[ind].run_name) != 0:
[654e8e0]1682                        if isinstance(output[ind].run_name, dict):
1683                            name = output[ind].run_name.keys()[0]
1684                        else:
1685                            name = output[ind].run_name
[f32d144]1686                    else:
1687                        name = original_fname
[ef16f59]1688                    state.data.group_id = name
[a95ae9a]1689                    # store state in fitting
[f32d144]1690                    self.call_back(state=state,
1691                                   datainfo=output[ind], format=ext)
1692                    self.state = state
[e6de6b8]1693                simfitstate = self._parse_simfit_state(entry)
[e89aed5]1694                if simfitstate is not None:
1695                    self.call_back(state=simfitstate)
1696
[ef16f59]1697                return output
[61cada5]1698        except:
[4bee68d]1699            self.call_back(format=ext)
[61cada5]1700            raise
[2f4b430]1701
[61cada5]1702    def write(self, filename, datainfo=None, fitstate=None):
1703        """
[b35d3d1]1704        Write the content of a Data1D as a CanSAS XML file only for standalone
[2f4b430]1705
[5062bbf]1706        :param filename: name of the file to write
1707        :param datainfo: Data1D object
1708        :param fitstate: PageState object
[2f4b430]1709
[61cada5]1710        """
1711        # Sanity check
[a95ae9a]1712        if self.cansas:
[61cada5]1713            # Add fitting information to the XML document
[ef16f59]1714            doc = self.write_toXML(datainfo, fitstate)
[61cada5]1715            # Write the XML document
1716        else:
[ac5b69d]1717            doc = fitstate.toXML(file=filename)
[2f4b430]1718
[ac5b69d]1719        # Save the document no matter the type
1720        fd = open(filename, 'w')
1721        fd.write(doc.toprettyxml())
1722        fd.close()
[2f4b430]1723
[a95ae9a]1724    def write_toXML(self, datainfo=None, state=None, batchfit=None):
[b35d3d1]1725        """
[f32d144]1726        Write toXML, a helper for write(),
1727        could be used by guimanager._on_save()
[2f4b430]1728
[b35d3d1]1729        : return: xml doc
1730        """
[3c44c66]1731
[a95ae9a]1732        self.batchfit_params = batchfit
1733        if state.data is None or not state.data.is_data:
[3b148c3]1734            return None
[a95ae9a]1735        # make sure title and data run are filled.
1736        if state.data.title is None or state.data.title == '':
1737            state.data.title = state.data.name
1738        if state.data.run_name is None or state.data.run_name == {}:
1739            state.data.run = [str(state.data.name)]
1740            state.data.run_name[0] = state.data.name
1741
1742        if issubclass(state.data.__class__,
1743                      sas.sascalc.dataloader.data_info.Data1D):
1744            data = state.data
1745            doc, sasentry = self._to_xml_doc(data)
[f32d144]1746        else:
[a95ae9a]1747            data = state.data
1748            doc, sasentry = self._data2d_to_xml_doc(data)
[2f4b430]1749
[b35d3d1]1750        if state is not None:
[a95ae9a]1751            doc = state.toXML(doc=doc, file=data.filename, entry_node=sasentry,
1752                              batch_fit_state=self.batchfit_params)
[2f4b430]1753
[f32d144]1754        return doc
[467202f]1755
1756# Simple html report templet
1757HEADER = "<html>\n"
1758HEADER += "<head>\n"
1759HEADER += "<meta http-equiv=Content-Type content='text/html; "
1760HEADER += "charset=windows-1252'> \n"
1761HEADER += "<meta name=Generator >\n"
1762HEADER += "</head>\n"
1763HEADER += "<body lang=EN-US>\n"
1764HEADER += "<div class=WordSection1>\n"
1765HEADER += "<p class=MsoNormal><b><span ><center><font size='4' >"
1766HEADER += "%s</font></center></span></center></b></p>"
1767HEADER += "<p class=MsoNormal>&nbsp;</p>"
1768PARA = "<p class=MsoNormal><font size='4' > %s \n"
1769PARA += "</font></p>"
1770CENTRE = "<p class=MsoNormal><center><font size='4' > %s \n"
1771CENTRE += "</font></center></p>"
1772FEET_1 = \
1773"""
1774<p class=MsoNormal>&nbsp;</p>
1775<br>
1776<p class=MsoNormal><b><span ><center> <font size='4' > Graph
1777</font></span></center></b></p>
1778<p class=MsoNormal>&nbsp;</p>
1779<center>
1780<br><font size='4' >Model Computation</font>
1781<br><font size='4' >Data: "%s"</font><br>
1782"""
1783FEET_2 = \
1784"""
1785<img src="%s" >
1786</img>
1787"""
1788FEET_3 = \
1789"""
1790</center>
1791</div>
1792</body>
1793</html>
1794"""
1795ELINE = "<p class=MsoNormal>&nbsp;</p>"
Note: See TracBrowser for help on using the repository browser.