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

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.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since c80e0ac was a15e754, checked in by Doucet, Mathieu <doucetm@…>, 9 years ago

Fix report generation

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