source: sasview/guiframe/state_reader.py @ b2d9826

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 b2d9826 was 5c4b674, checked in by Gervaise Alina <gervyh@…>, 14 years ago

working on data panel

  • Property mode set to 100644
File size: 71.0 KB
Line 
1
2
3############################################################################
4#This software was developed by the University of Tennessee as part of the
5#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
6#project funded by the US National Science Foundation.
7#If you use DANSE applications to do scientific research that leads to
8#publication, we ask that you acknowledge the use of the software with the
9#following sentence:
10#This work benefited from DANSE software developed under NSF award DMR-0520547.
11#copyright 2008,2009 University of Tennessee
12#############################################################################
13
14# Known issue: reader not compatible with multiple SASdata entries
15# within a single SASentry. Will raise a runtime error.
16
17#TODO: check that all vectors are written only if they have at
18#    least one non-empty value
19#TODO: Writing only allows one SASentry per file.
20#     Would be best to allow multiple entries.
21#TODO: Store error list
22#TODO: Allow for additional meta data for each section
23#TODO: Notes need to be implemented. They can be any XML
24#    structure in version 1.0
25#      Process notes have the same problem.
26#TODO: Unit conversion is not complete (temperature units are missing)
27
28
29import logging
30import numpy
31import os
32import sys
33from sans.guiframe.dataFitting import Data1D
34from sans.guiframe.dataFitting import Data2D
35from DataLoader.data_info import Collimation
36from DataLoader.data_info import Detector
37from DataLoader.data_info import Process
38from DataLoader.data_info import Aperture
39from lxml import etree
40import xml.dom.minidom
41
42has_converter = True
43try:
44    from data_util.nxsunit import Converter
45except:
46    has_converter = False
47
48STATE_NS = "State/1.0"
49
50def write_node(doc, parent, name, value, attr={}):
51    """
52    :param doc: document DOM
53    :param parent: parent node
54    :param name: tag of the element
55    :param value: value of the child text node
56    :param attr: attribute dictionary
57   
58    :return: True if something was appended, otherwise False
59    """
60    if value is not None:
61        node = doc.createElement(name)
62        node.appendChild(doc.createTextNode(str(value)))
63        for item in attr:
64            node.setAttribute(item, attr[item])
65        parent.appendChild(node)
66        return True
67    return False
68
69def get_content(location, node):
70    """
71    Get the first instance of the content of a xpath location.
72   
73    :param location: xpath location
74    :param node: node to start at
75   
76    :return: Element, or None
77    """
78    nodes = node.xpath(location, namespaces={'ns': STATE_NODE})
79   
80    if len(nodes)>0:
81        return nodes[0]
82    else:
83        return None
84
85def get_float(location, node):
86    """
87    Get the content of a node as a float
88   
89    :param location: xpath location
90    :param node: node to start at
91    """
92    nodes = node.xpath(location, namespaces={'ns': STATE_NODE})
93   
94    value = None
95    attr = {}
96    if len(nodes) > 0:
97        try:
98            value = float(nodes[0].text)   
99        except:
100            # Could not pass, skip and return None
101            msg = "state_reader.get_float: could not "
102            msg += " convert '%s' to float" % nodes[0].text
103            logging.error(msg)
104        if nodes[0].get('unit') is not None:
105            attr['unit'] = nodes[0].get('unit')
106    return value, attr
107
108           
109class Reader1D:
110    """
111    read state of a plugin  and available data
112   
113    :Dependencies:
114        The CanSas reader requires PyXML 0.8.4 or later.
115    """
116    ## CanSAS version
117    version = '1.0'
118    ## File type
119    type_name = "CanSAS 1D"
120    ## Wildcards
121    type = []
122
123    ## List of allowed extensions
124    ext = [] 
125   
126    def __init__(self):
127        ## List of errors
128        self.errors = []
129   
130    def read(self, path):
131        """
132        Load data file
133       
134        :param path: file path
135       
136        :return: Data1D object if a single SASentry was found,
137                    or a list of Data1D objects if multiple entries were found,
138                    or None of nothing was found
139                   
140        :raise RuntimeError: when the file can't be opened
141        :raise ValueError: when the length of the data vectors are inconsistent
142        """
143        output = []
144        if os.path.isfile(path):
145            basename  = os.path.basename(path)
146            root, extension = os.path.splitext(basename)
147            if extension.lower() in self.ext:
148               
149                tree = etree.parse(path, parser=etree.ETCompatXMLParser())
150                # Check the format version number
151                # Specifying the namespace will take care of the file
152                # format version
153                root = tree.getroot()
154               
155                entry_list = root.xpath('/ns:SASroot/ns:SASentry',
156                                         namespaces={'ns': STATE_NODE})
157               
158                for entry in entry_list:
159                    self.errors = []
160                    sas_entry = self._parse_entry(entry)
161                    sas_entry.filename = basename
162                   
163                    # Store loading process information
164                    sas_entry.errors = self.errors
165                    sas_entry.meta_data['loader'] = self.type_name
166                    output.append(sas_entry)
167               
168        else:
169            raise RuntimeError, "%s is not a file" % path
170        # Return output consistent with the loader's api
171        if len(output) == 0:
172            #cannot return none when it cannot read
173            #return None
174            raise RuntimeError, "%s cannot be read \n" % path
175        elif len(output) == 1:
176            return output[0]
177        else:
178            return output               
179               
180    def _parse_entry(self, dom):
181        """
182        Parse a SASentry
183       
184        :param node: SASentry node
185       
186        :return: Data1D object
187        """
188        x = numpy.zeros(0)
189        y = numpy.zeros(0)
190       
191        data_info = Data1D(x, y)
192       
193        # Look up title     
194        self._store_content('ns:Title', dom, 'title', data_info)
195       
196        # Look up run number   
197        nodes = dom.xpath('ns:Run', namespaces={'ns': STATE_NODE})
198        for item in nodes:   
199            if item.text is not None:
200                value = item.text.strip()
201                if len(value) > 0:
202                    data_info.run.append(value)
203                    if item.get('name') is not None:
204                        data_info.run_name[value] = item.get('name')
205                           
206        # Look up instrument name             
207        self._store_content('ns:SASinstrument/ns:name', dom, 'instrument',
208                             data_info)
209
210        # Notes
211        note_list = dom.xpath('ns:SASnote', namespaces={'ns': STATE_NODE})
212        for note in note_list:
213            try:
214                if note.text is not None:
215                    note_value = note.text.strip()
216                    if len(note_value) > 0:
217                        data_info.notes.append(note_value)
218            except:
219                err_mess = "state_reader.read: error processing"
220                err_mess += " entry notes\n  %s" % sys.exc_value
221                self.errors.append(err_mess)
222                logging.error(err_mess)
223       
224        # Sample info ###################
225        entry = get_content('ns:SASsample', dom)
226        if entry is not None:
227            data_info.sample.name = entry.get('name')
228           
229        self._store_content('ns:SASsample/ns:ID', 
230                     dom, 'ID', data_info.sample)                   
231        self._store_float('ns:SASsample/ns:thickness', 
232                     dom, 'thickness', data_info.sample)
233        self._store_float('ns:SASsample/ns:transmission', 
234                     dom, 'transmission', data_info.sample)
235        self._store_float('ns:SASsample/ns:temperature', 
236                     dom, 'temperature', data_info.sample)
237       
238        nodes = dom.xpath('ns:SASsample/ns:details', 
239                          namespaces={'ns': STATE_NODE})
240        for item in nodes:
241            try:
242                if item.text is not None:
243                    detail_value = item.text.strip()
244                    if len(detail_value) > 0:
245                        data_info.sample.details.append(detail_value)
246            except:
247                err_mess = "state_reader.read: error processing "
248                err_mess += " sample details\n  %s" % sys.exc_value
249                self.errors.append(err_mess)
250                logging.error(err_mess)
251       
252        # Position (as a vector)
253        self._store_float('ns:SASsample/ns:position/ns:x', 
254                     dom, 'position.x', data_info.sample)         
255        self._store_float('ns:SASsample/ns:position/ns:y', 
256                     dom, 'position.y', data_info.sample)         
257        self._store_float('ns:SASsample/ns:position/ns:z', 
258                     dom, 'position.z', data_info.sample)         
259       
260        # Orientation (as a vector)
261        self._store_float('ns:SASsample/ns:orientation/ns:roll', 
262                     dom, 'orientation.x', data_info.sample)         
263        self._store_float('ns:SASsample/ns:orientation/ns:pitch', 
264                     dom, 'orientation.y', data_info.sample)         
265        self._store_float('ns:SASsample/ns:orientation/ns:yaw', 
266                     dom, 'orientation.z', data_info.sample)         
267       
268        # Source info ###################
269        entry = get_content('ns:SASinstrument/ns:SASsource', dom)
270        if entry is not None:
271            data_info.source.name = entry.get('name')
272       
273        self._store_content('ns:SASinstrument/ns:SASsource/ns:radiation', 
274                     dom, 'radiation', data_info.source)                   
275        self._store_content('ns:SASinstrument/ns:SASsource/ns:beam_shape', 
276                     dom, 'beam_shape', data_info.source)                   
277        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength', 
278                     dom, 'wavelength', data_info.source)         
279        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_min', 
280                     dom, 'wavelength_min', data_info.source)         
281        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_max', 
282                     dom, 'wavelength_max', data_info.source)         
283        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_spread', 
284                     dom, 'wavelength_spread', data_info.source)   
285       
286        # Beam size (as a vector)   
287        entry = get_content('ns:SASinstrument/ns:SASsource/ns:beam_size', dom)
288        if entry is not None:
289            data_info.source.beam_size_name = entry.get('name')
290           
291        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:x', 
292                     dom, 'beam_size.x', data_info.source)   
293        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:y', 
294                     dom, 'beam_size.y', data_info.source)   
295        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:z', 
296                     dom, 'beam_size.z', data_info.source)   
297       
298        # Collimation info ###################
299        nodes = dom.xpath('ns:SASinstrument/ns:SAScollimation', 
300                          namespaces={'ns': STATE_NODE})
301        for item in nodes:
302            collim = Collimation()
303            if item.get('name') is not None:
304                collim.name = item.get('name')
305            self._store_float('ns:length', item, 'length', collim) 
306           
307            # Look for apertures
308            apert_list = item.xpath('ns:aperture', namespaces={'ns': STATE_NODE})
309            for apert in apert_list:
310                aperture =  Aperture()
311               
312                # Get the name and type of the aperture
313                aperture.name = apert.get('name')
314                aperture.type = apert.get('type')
315                   
316                self._store_float('ns:distance', apert, 'distance', aperture)   
317               
318                entry = get_content('ns:size', apert)
319                if entry is not None:
320                    aperture.size_name = entry.get('name')
321               
322                self._store_float('ns:size/ns:x', apert, 'size.x', aperture)   
323                self._store_float('ns:size/ns:y', apert, 'size.y', aperture)   
324                self._store_float('ns:size/ns:z', apert, 'size.z', aperture)
325               
326                collim.aperture.append(aperture)
327               
328            data_info.collimation.append(collim)
329       
330        # Detector info ######################
331        nodes = dom.xpath('ns:SASinstrument/ns:SASdetector',
332                           namespaces={'ns': STATE_NODE})
333        for item in nodes:
334           
335            detector = Detector()
336           
337            self._store_content('ns:name', item, 'name', detector)
338            self._store_float('ns:SDD', item, 'distance', detector)   
339           
340            # Detector offset (as a vector)
341            self._store_float('ns:offset/ns:x', item, 'offset.x', detector)   
342            self._store_float('ns:offset/ns:y', item, 'offset.y', detector)   
343            self._store_float('ns:offset/ns:z', item, 'offset.z', detector)   
344           
345            # Detector orientation (as a vector)
346            self._store_float('ns:orientation/ns:roll',  item, 'orientation.x',
347                               detector)   
348            self._store_float('ns:orientation/ns:pitch', item, 'orientation.y',
349                               detector)   
350            self._store_float('ns:orientation/ns:yaw',   item, 'orientation.z',
351                               detector)   
352           
353            # Beam center (as a vector)
354            self._store_float('ns:beam_center/ns:x', item, 'beam_center.x',
355                               detector)   
356            self._store_float('ns:beam_center/ns:y', item, 'beam_center.y', 
357                              detector)   
358            self._store_float('ns:beam_center/ns:z', item, 'beam_center.z',
359                               detector)   
360           
361            # Pixel size (as a vector)
362            self._store_float('ns:pixel_size/ns:x', item, 'pixel_size.x',
363                               detector)   
364            self._store_float('ns:pixel_size/ns:y', item, 'pixel_size.y',
365                               detector)   
366            self._store_float('ns:pixel_size/ns:z', item, 'pixel_size.z',
367                               detector)   
368           
369            self._store_float('ns:slit_length', item, 'slit_length', detector)
370           
371            data_info.detector.append(detector)   
372
373        # Processes info ######################
374        nodes = dom.xpath('ns:SASprocess', namespaces={'ns': STATE_NODE})
375        for item in nodes:
376            process = Process()
377            self._store_content('ns:name', item, 'name', process)
378            self._store_content('ns:date', item, 'date', process)
379            self._store_content('ns:description', item, 'description', process)
380           
381            term_list = item.xpath('ns:term', namespaces={'ns': STATE_NODE})
382            for term in term_list:
383                try:
384                    term_attr = {}
385                    for attr in term.keys():
386                        term_attr[attr] = term.get(attr).strip()
387                    if term.text is not None:
388                        term_attr['value'] = term.text.strip()
389                        process.term.append(term_attr)
390                except:
391                    err_mess = "state_reader.read: error processing "
392                    err_mess += " process term\n  %s" % sys.exc_value
393                    self.errors.append(err_mess)
394                    logging.error(err_mess)
395           
396            note_list = item.xpath('ns:SASprocessnote', 
397                                   namespaces={'ns': STATE_NODE})
398            for note in note_list:
399                if note.text is not None:
400                    process.notes.append(note.text.strip())
401           
402            data_info.process.append(process)
403           
404           
405        # Data info ######################
406        nodes = dom.xpath('ns:SASdata', namespaces={'ns': STATE_NODE})
407        if len(nodes) > 1:
408            msg = "CanSAS reader is not compatible with multiple"
409            msg += " SASdata entries"
410            raise RuntimeError, msg
411       
412        nodes = dom.xpath('ns:SASdata/ns:Idata', namespaces={'ns': STATE_NODE})
413
414        x  = numpy.zeros(0)
415        y  = numpy.zeros(0)
416        dx = numpy.zeros(0)
417        dy = numpy.zeros(0)
418        dxw = numpy.zeros(0)
419        dxl = numpy.zeros(0)
420       
421        for item in nodes:
422            _x, attr = get_float('ns:Q', item)
423            _dx, attr_d = get_float('ns:Qdev', item)
424            _dxl, attr_l = get_float('ns:dQl', item)
425            _dxw, attr_w = get_float('ns:dQw', item)
426            if _dx == None:
427                _dx = 0.0
428            if _dxl == None:
429                _dxl = 0.0
430            if _dxw == None:
431                _dxw = 0.0
432               
433            if attr.has_key('unit') and \
434                attr['unit'].lower() != data_info.x_unit.lower():
435                if has_converter==True:
436                    try:
437                        data_conv_q = Converter(attr['unit'])
438                        _x = data_conv_q(_x, units=data_info.x_unit)
439                    except:
440                        msg =  "CanSAS reader: could not convert "
441                        msg += "Q unit [%s]; " 
442                        msg += "expecting [%s]\n  %s" % (attr['unit'], 
443                                  data_info.x_unit, sys.exc_value)
444                        raise ValueError, msg
445                       
446                else:
447                    msg = "CanSAS reader: unrecognized Q unit [%s]; "
448                    msg += "expecting [%s]" % (attr['unit'], data_info.x_unit)
449                    raise ValueError, msg
450                       
451            # Error in Q
452            if attr_d.has_key('unit') and \
453                attr_d['unit'].lower() != data_info.x_unit.lower():
454                if has_converter==True:
455                    try:
456                        data_conv_q = Converter(attr_d['unit'])
457                        _dx = data_conv_q(_dx, units=data_info.x_unit)
458                    except:
459                        msg = "CanSAS reader: could not convert dQ unit [%s];"
460                        msg += " expecting " 
461                        msg += "[%s]\n  %s" % (attr['unit'],
462                                                data_info.x_unit, sys.exc_value)
463                        raise ValueError, msg
464                       
465                else:
466                    msg = "CanSAS reader: unrecognized dQ unit [%s]; "
467                    msg += "expecting [%s]" % (attr['unit'], data_info.x_unit)
468                    raise ValueError,  msg
469                       
470            # Slit length
471            if attr_l.has_key('unit') and \
472                attr_l['unit'].lower() != data_info.x_unit.lower():
473                if has_converter == True:
474                    try:
475                        data_conv_q = Converter(attr_l['unit'])
476                        _dxl = data_conv_q(_dxl, units=data_info.x_unit)
477                    except:
478                        msg = "CanSAS reader: could not convert dQl unit [%s];"
479                        msg += " expecting [%s]\n  %s" % (attr['unit'],
480                                             data_info.x_unit, sys.exc_value)
481                        raise ValueError, msg
482                       
483                else:
484                    msg = "CanSAS reader: unrecognized dQl unit [%s];"
485                    msg += " expecting [%s]" % (attr['unit'], data_info.x_unit)
486                    raise ValueError, msg
487                       
488            # Slit width
489            if attr_w.has_key('unit') and \
490            attr_w['unit'].lower() != data_info.x_unit.lower():
491                if has_converter == True:
492                    try:
493                        data_conv_q = Converter(attr_w['unit'])
494                        _dxw = data_conv_q(_dxw, units=data_info.x_unit)
495                    except:
496                        msg = "CanSAS reader: could not convert dQw unit [%s];"
497                        msg += " expecting [%s]\n  %s" % (attr['unit'], 
498                                                data_info.x_unit, sys.exc_value)
499                        raise ValueError, msg
500                       
501                else:
502                    msg = "CanSAS reader: unrecognized dQw unit [%s];"
503                    msg += " expecting [%s]" % (attr['unit'], data_info.x_unit)
504                    raise ValueError, msg   
505            _y, attr = get_float('ns:I', item)
506            _dy, attr_d = get_float('ns:Idev', item)
507            if _dy == None:
508                _dy = 0.0
509            if attr.has_key('unit') and \
510            attr['unit'].lower() != data_info.y_unit.lower():
511                if has_converter==True:
512                    try:
513                        data_conv_i = Converter(attr['unit'])
514                        _y = data_conv_i(_y, units=data_info.y_unit)
515                    except:
516                        msg = "CanSAS reader: could not convert I(q) unit [%s];"
517                        msg += " expecting [%s]\n  %s" % (attr['unit'], 
518                                            data_info.y_unit, sys.exc_value)
519                        raise ValueError, msg
520                else:
521                    msg = "CanSAS reader: unrecognized I(q) unit [%s];"
522                    msg += " expecting [%s]" % (attr['unit'], data_info.y_unit)
523                    raise ValueError, msg
524                       
525            if attr_d.has_key('unit') and \
526            attr_d['unit'].lower() != data_info.y_unit.lower():
527                if has_converter==True:
528                    try:
529                        data_conv_i = Converter(attr_d['unit'])
530                        _dy = data_conv_i(_dy, units=data_info.y_unit)
531                    except:
532                        msg = "CanSAS reader: could not convert dI(q) unit "
533                        msg += "[%s]; expecting [%s]\n  %s"  % (attr_d['unit'],
534                                             data_info.y_unit, sys.exc_value)
535                        raise ValueError, msg
536                else:
537                    msg = "CanSAS reader: unrecognized dI(q) unit [%s]; "
538                    msg += "expecting [%s]" % (attr_d['unit'], data_info.y_unit)
539                    raise ValueError, msg
540               
541            if _x is not None and _y is not None:
542                x  = numpy.append(x, _x)
543                y  = numpy.append(y, _y)
544                dx = numpy.append(dx, _dx)
545                dy = numpy.append(dy, _dy)
546                dxl = numpy.append(dxl, _dxl)
547                dxw = numpy.append(dxw, _dxw)
548               
549        data_info.x = x
550        data_info.y = y
551        data_info.dx = dx
552        data_info.dy = dy
553        data_info.dxl = dxl
554        data_info.dxw = dxw
555       
556        data_conv_q = None
557        data_conv_i = None
558       
559        if has_converter == True and data_info.x_unit != '1/A':
560            data_conv_q = Converter('1/A')
561            # Test it
562            data_conv_q(1.0, output.Q_unit)
563           
564        if has_converter == True and data_info.y_unit != '1/cm':
565            data_conv_i = Converter('1/cm')
566            # Test it
567            data_conv_i(1.0, output.I_unit)                   
568               
569        if data_conv_q is not None:
570            data_info.xaxis("\\rm{Q}", data_info.x_unit)
571        else:
572            data_info.xaxis("\\rm{Q}", 'A^{-1}')
573        if data_conv_i is not None:
574            data_info.yaxis("\\rm{Intensity}", data_info.y_unit)
575        else:
576            data_info.yaxis("\\rm{Intensity}","cm^{-1}")
577       
578        return data_info
579
580    def _to_xml_doc(self, datainfo):
581        """
582        Create an XML document to contain the content of a Data1D
583       
584        :param datainfo: Data1D object
585        """
586       
587        if not issubclass(datainfo.__class__, Data1D):
588            raise RuntimeError, "The cansas writer expects a Data1D instance"
589       
590        doc = xml.dom.minidom.Document()
591        main_node = doc.createElement("SASroot")
592        main_node.setAttribute("version", self.version)
593        main_node.setAttribute("xmlns", "cansas1d/%s" % self.version)
594        main_node.setAttribute("xmlns:xsi",
595                               "http://www.w3.org/2001/XMLSchema-instance")
596        main_node.setAttribute("xsi:schemaLocation",
597                               "cansas1d/%s http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd" % self.version)
598       
599        doc.appendChild(main_node)
600       
601        entry_node = doc.createElement("SASentry")
602        main_node.appendChild(entry_node)
603       
604        write_node(doc, entry_node, "Title", datainfo.title)
605        for item in datainfo.run:
606            runname = {}
607            if datainfo.run_name.has_key(item) and \
608            len(str(datainfo.run_name[item]))>1:
609                runname = {'name': datainfo.run_name[item] }
610            write_node(doc, entry_node, "Run", item, runname)
611       
612        # Data info
613        node = doc.createElement("SASdata")
614        entry_node.appendChild(node)
615       
616        for i in range(len(datainfo.x)):
617            pt = doc.createElement("Idata")
618            node.appendChild(pt)
619            write_node(doc, pt, "Q", datainfo.x[i], {'unit':datainfo.x_unit})
620            if len(datainfo.y)>=i:
621                write_node(doc, pt, "I", datainfo.y[i],
622                            {'unit':datainfo.y_unit})
623            if datainfo.dx != None and len(datainfo.dx) >= i:
624                write_node(doc, pt, "Qdev", datainfo.dx[i],
625                            {'unit':datainfo.x_unit})
626            if datainfo.dxl != None and len(datainfo.dxl) >= i:
627                write_node(doc, pt, "dQl", datainfo.dxl[i],
628                            {'unit':datainfo.x_unit})   
629            if datainfo.dxw != None and len(datainfo.dxw) >= i:
630                write_node(doc, pt, "dQw", datainfo.dxw[i],
631                            {'unit':datainfo.x_unit})             
632            if datainfo.dy != None and len(datainfo.dy) >= i:
633                write_node(doc, pt, "Idev", datainfo.dy[i],
634                            {'unit':datainfo.y_unit})
635        #data gui info
636        gui_info = doc.createElement("DataInfoGui")
637       
638        write_node(doc, gui_info, "group_id", 'group_id')
639        for item in datainfo.group_id:
640            write_node(doc, gui_info, "group_id", str(item))
641        write_node(doc, gui_info, "name", datainfo.name)
642        write_node(doc, gui_info, "id", datainfo.id)
643        write_node(doc, gui_info, "group_id", datainfo.groud_id)
644        write_node(doc, gui_info, "name", datainfo.name)
645        write_node(doc, gui_info, "is_data", datainfo.is_data)
646        write_node(doc, gui_info, "xtransform", datainfo.xtransform)
647        write_node(doc, gui_info, "scale", datainfo.scale)
648        write_node(doc, gui_info, "ytransform", datainfo.ytransform)
649        write_node(doc, gui_info, "path", datainfo.path)
650        node.appendChild(gui_info)
651        # Sample info
652        sample = doc.createElement("SASsample")
653        if datainfo.sample.name is not None:
654            sample.setAttribute("name", str(datainfo.sample.name))
655        entry_node.appendChild(sample)
656        write_node(doc, sample, "ID", str(datainfo.sample.ID))
657        write_node(doc, sample, "thickness", datainfo.sample.thickness,
658                   {"unit":datainfo.sample.thickness_unit})
659        write_node(doc, sample, "transmission", datainfo.sample.transmission)
660        write_node(doc, sample, "temperature", datainfo.sample.temperature,
661                   {"unit":datainfo.sample.temperature_unit})
662       
663        for item in datainfo.sample.details:
664            write_node(doc, sample, "details", item)
665       
666        pos = doc.createElement("position")
667        written = write_node(doc, pos, "x", datainfo.sample.position.x,
668                             {"unit":datainfo.sample.position_unit})
669        written = written | write_node(doc, pos, "y",
670                                       datainfo.sample.position.y,
671                                       {"unit":datainfo.sample.position_unit})
672        written = written | write_node(doc, pos, "z",
673                                       datainfo.sample.position.z,
674                                       {"unit":datainfo.sample.position_unit})
675        if written == True:
676            sample.appendChild(pos)
677       
678        ori = doc.createElement("orientation")
679        written = write_node(doc, ori, "roll",
680                             datainfo.sample.orientation.x,
681                             {"unit":datainfo.sample.orientation_unit})
682        written = written | write_node(doc, ori, "pitch",
683                                       datainfo.sample.orientation.y,
684                                    {"unit":datainfo.sample.orientation_unit})
685        written = written | write_node(doc, ori, "yaw",
686                                       datainfo.sample.orientation.z,
687                                    {"unit":datainfo.sample.orientation_unit})
688        if written == True:
689            sample.appendChild(ori)
690       
691        # Instrument info
692        instr = doc.createElement("SASinstrument")
693        entry_node.appendChild(instr)
694       
695        write_node(doc, instr, "name", datainfo.instrument)
696       
697        #   Source
698        source = doc.createElement("SASsource")
699        if datainfo.source.name is not None:
700            source.setAttribute("name", str(datainfo.source.name))
701        instr.appendChild(source)
702       
703        write_node(doc, source, "radiation", datainfo.source.radiation)
704        write_node(doc, source, "beam_shape", datainfo.source.beam_shape)
705        size = doc.createElement("beam_size")
706        if datainfo.source.beam_size_name is not None:
707            size.setAttribute("name", str(datainfo.source.beam_size_name))
708        written = write_node(doc, size, "x", datainfo.source.beam_size.x,
709                             {"unit":datainfo.source.beam_size_unit})
710        written = written | write_node(doc, size, "y",
711                                       datainfo.source.beam_size.y,
712                                       {"unit":datainfo.source.beam_size_unit})
713        written = written | write_node(doc, size, "z",
714                                       datainfo.source.beam_size.z,
715                                       {"unit":datainfo.source.beam_size_unit})
716        if written == True:
717            source.appendChild(size)
718           
719        write_node(doc, source, "wavelength",
720                   datainfo.source.wavelength,
721                   {"unit":datainfo.source.wavelength_unit})
722        write_node(doc, source, "wavelength_min",
723                   datainfo.source.wavelength_min,
724                   {"unit":datainfo.source.wavelength_min_unit})
725        write_node(doc, source, "wavelength_max",
726                   datainfo.source.wavelength_max,
727                   {"unit":datainfo.source.wavelength_max_unit})
728        write_node(doc, source, "wavelength_spread",
729                   datainfo.source.wavelength_spread,
730                   {"unit":datainfo.source.wavelength_spread_unit})
731       
732        #   Collimation
733        for item in datainfo.collimation:
734            coll = doc.createElement("SAScollimation")
735            if item.name is not None:
736                coll.setAttribute("name", str(item.name))
737            instr.appendChild(coll)
738           
739            write_node(doc, coll, "length", item.length,
740                       {"unit":item.length_unit})
741           
742            for apert in item.aperture:
743                ap = doc.createElement("aperture")
744                if apert.name is not None:
745                    ap.setAttribute("name", str(apert.name))
746                if apert.type is not None:
747                    ap.setAttribute("type", str(apert.type))
748                coll.appendChild(ap)
749               
750                write_node(doc, ap, "distance", apert.distance,
751                           {"unit":apert.distance_unit})
752               
753                size = doc.createElement("size")
754                if apert.size_name is not None:
755                    size.setAttribute("name", str(apert.size_name))
756                written = write_node(doc, size, "x", apert.size.x,
757                                     {"unit":apert.size_unit})
758                written = written | write_node(doc, size, "y", apert.size.y,
759                                               {"unit":apert.size_unit})
760                written = written | write_node(doc, size, "z", apert.size.z,
761                                               {"unit":apert.size_unit})
762                if written == True:
763                    ap.appendChild(size)
764
765        #   Detectors
766        for item in datainfo.detector:
767            det = doc.createElement("SASdetector")
768            written = write_node(doc, det, "name", item.name)
769            written = written | write_node(doc, det, "SDD", item.distance,
770                                           {"unit":item.distance_unit})
771            written = written | write_node(doc, det, "slit_length",
772                                           item.slit_length,
773                                           {"unit":item.slit_length_unit})
774            if written == True:
775                instr.appendChild(det)
776           
777            off = doc.createElement("offset")
778            written = write_node(doc, off, "x", item.offset.x,
779                                 {"unit":item.offset_unit})
780            written = written | write_node(doc, off, "y", item.offset.y,
781                                           {"unit":item.offset_unit})
782            written = written | write_node(doc, off, "z", item.offset.z,
783                                           {"unit":item.offset_unit})
784            if written == True:
785                det.appendChild(off)
786           
787            center = doc.createElement("beam_center")
788            written = write_node(doc, center, "x", item.beam_center.x,
789                                 {"unit":item.beam_center_unit})
790            written = written | write_node(doc, center, "y",
791                                           item.beam_center.y,
792                                           {"unit":item.beam_center_unit})
793            written = written | write_node(doc, center, "z",
794                                           item.beam_center.z,
795                                           {"unit":item.beam_center_unit})
796            if written == True:
797                det.appendChild(center)
798               
799            pix = doc.createElement("pixel_size")
800            written = write_node(doc, pix, "x", item.pixel_size.x,
801                                 {"unit":item.pixel_size_unit})
802            written = written | write_node(doc, pix, "y", item.pixel_size.y,
803                                           {"unit":item.pixel_size_unit})
804            written = written | write_node(doc, pix, "z", item.pixel_size.z,
805                                           {"unit":item.pixel_size_unit})
806            if written == True:
807                det.appendChild(pix)
808               
809            ori = doc.createElement("orientation")
810            written = write_node(doc, ori, "roll",  item.orientation.x,
811                                 {"unit":item.orientation_unit})
812            written = written | write_node(doc, ori, "pitch",
813                                           item.orientation.y,
814                                           {"unit":item.orientation_unit})
815            written = written | write_node(doc, ori, "yaw",
816                                           item.orientation.z,
817                                           {"unit":item.orientation_unit})
818            if written == True:
819                det.appendChild(ori)
820               
821       
822        # Processes info
823        for item in datainfo.process:
824            node = doc.createElement("SASprocess")
825            entry_node.appendChild(node)
826
827            write_node(doc, node, "name", item.name)
828            write_node(doc, node, "date", item.date)
829            write_node(doc, node, "description", item.description)
830            for term in item.term:
831                value = term['value']
832                del term['value']
833                write_node(doc, node, "term", value, term)
834            for note in item.notes:
835                write_node(doc, node, "SASprocessnote", note)
836       
837        # Return the document, and the SASentry node associated with
838        # the data we just wrote
839        return doc, entry_node
840           
841    def write(self, filename, datainfo):
842        """
843        Write the content of a Data1D as a CanSAS XML file
844       
845        :param filename: name of the file to write
846        :param datainfo: Data1D object
847        """
848        # Create XML document
849        doc, sasentry = self._to_xml_doc(datainfo)
850        # Write the file
851        fd = open(filename, 'w')
852        fd.write(doc.toprettyxml())
853        fd.close()
854       
855    def _store_float(self, location, node, variable, storage, optional=True):
856        """
857        Get the content of a xpath location and store
858        the result. Check that the units are compatible
859        with the destination. The value is expected to
860        be a float.
861       
862        The xpath location might or might not exist.
863        If it does not exist, nothing is done
864       
865        :param location: xpath location to fetch
866        :param node: node to read the data from
867        :param variable: name of the data member to store it in [string]
868        :param storage: data object that has the 'variable' data member
869        :param optional: if True, no exception will be raised
870            if unit conversion can't be done
871
872        :raise ValueError: raised when the units are not recognized
873        """
874        entry = get_content(location, node)
875        try:
876            value = float(entry.text)
877        except:
878            value = None
879           
880        if value is not None:
881            # If the entry has units, check to see that they are
882            # compatible with what we currently have in the data object
883            units = entry.get('unit')
884            if units is not None:
885                toks = variable.split('.')
886                exec "local_unit = storage.%s_unit" % toks[0]
887                if units.lower()!=local_unit.lower():
888                    if has_converter==True:
889                        try:
890                            conv = Converter(units)
891                            exec "storage.%s = %g" % (variable,
892                                            conv(value, units=local_unit))
893                        except:
894                            err_mess = "CanSAS reader: could not convert"
895                            err_mess += " %s unit [%s]; expecting [%s]\n  %s" \
896                                % (variable, units, local_unit, sys.exc_value)
897                            self.errors.append(err_mess)
898                            if optional:
899                                logging.info(err_mess)
900                            else:
901                                raise ValueError, err_mess
902                    else:
903                        err_mess = "CanSAS reader: unrecognized %s unit [%s];"
904                        err_mess += " expecting [%s]" % (variable, 
905                                                         units, local_unit)
906                        self.errors.append(err_mess)
907                        if optional:
908                            logging.info(err_mess)
909                        else:
910                            raise ValueError, err_mess
911                else:
912                    exec "storage.%s = value" % variable
913            else:
914                exec "storage.%s = value" % variable
915               
916    def _store_content(self, location, node, variable, storage):
917        """
918        Get the content of a xpath location and store
919        the result. The value is treated as a string.
920       
921        The xpath location might or might not exist.
922        If it does not exist, nothing is done
923       
924        :param location: xpath location to fetch
925        :param node: node to read the data from
926        :param variable: name of the data member to store it in [string]
927        :param storage: data object that has the 'variable' data member
928       
929        :return: return a list of errors
930        """
931        entry = get_content(location, node)
932        if entry is not None and entry.text is not None:
933            exec "storage.%s = entry.text.strip()" % variable
934
935           
936
937class Reader2D:
938    """
939    Class to load a basic guiframe state
940    """
941    ## File type
942    type_name = "Fitting"
943   
944    ## Wildcards
945    type = ["Fitting files (*.fitv)|*.fitv"
946            "SANSView file (*.svs)|*.svs"]
947    ## List of allowed extensions
948    ext=['.fitv', '.FITV', '.svs', 'SVS']   
949   
950    def __init__(self):
951        CansasReader.__init__(self)
952        """
953        Initialize the call-back method to be called
954        after we load a file
955       
956        :param call_back: call-back method
957        :param cansas:  True = files will be written/read in CanSAS format
958                        False = write CanSAS format
959           
960        """
961        ## Call back method to be executed after a file is read
962        #self.call_back = call_back
963        ## CanSAS format flag
964        self.cansas = cansas
965        self.state = None
966       
967    def read(self, path):
968        """
969        Load a new P(r) inversion state from file
970       
971        :param path: file path
972       
973        """
974        if self.cansas == True:
975            return self._read_cansas(path)
976     
977    def _to_xml_doc(self, datainfo):
978        """
979        Create an XML document to contain the content of a Data2D
980       
981        :param datainfo: Data2D object
982       
983        """
984        if not issubclass(datainfo.__class__, Data2D):
985            raise RuntimeError, "The cansas writer expects a Data2D instance"
986       
987        doc = xml.dom.minidom.Document()
988        main_node = doc.createElement("SASroot")
989        main_node.setAttribute("version", self.version)
990        main_node.setAttribute("xmlns", "cansas1d/%s" % self.version)
991        main_node.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
992        main_node.setAttribute("xsi:schemaLocation", "cansas1d/%s http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd" % self.version)
993       
994        doc.appendChild(main_node)
995       
996        entry_node = doc.createElement("SASentry")
997        main_node.appendChild(entry_node)
998       
999        write_node(doc, entry_node, "Title", datainfo.title)
1000        if datainfo is not None:
1001            write_node(doc, entry_node, "data_class", datainfo.__class__.__name__)
1002        for item in datainfo.run:
1003            runname = {}
1004            if datainfo.run_name.has_key(item) and len(str(datainfo.run_name[item]))>1:
1005                runname = {'name': datainfo.run_name[item] }
1006            write_node(doc, entry_node, "Run", item, runname)
1007        # Data info
1008        new_node = doc.createElement("SASdata")
1009        entry_node.appendChild(new_node)
1010        for item in list_of_data_2d_attr:
1011            element = doc.createElement(item[0])
1012            exec "element.setAttribute(item[0], str(datainfo.%s))"%(item[1])
1013            new_node.appendChild(element)
1014           
1015        for item in list_of_data2d_values:
1016            root_node = doc.createElement(item[0])
1017            new_node.appendChild(root_node)
1018           
1019            exec "temp_list = datainfo.%s"%item[1]
1020
1021            if temp_list is None or len(temp_list)== 0:
1022                element = doc.createElement(item[0])
1023                exec "element.appendChild(doc.createTextNode(str(%s)))"%temp_list
1024                root_node.appendChild(element)
1025            else:
1026                for value in temp_list:
1027                    element = doc.createElement(item[0])
1028                    exec "element.setAttribute(item[0], str(%s))"%value
1029                    root_node.appendChild(element)
1030       
1031        # Sample info
1032        sample = doc.createElement("SASsample")
1033        if datainfo.sample.name is not None:
1034            sample.setAttribute("name", str(datainfo.sample.name))
1035        entry_node.appendChild(sample)
1036        write_node(doc, sample, "ID", str(datainfo.sample.ID))
1037        write_node(doc, sample, "thickness", datainfo.sample.thickness, {"unit":datainfo.sample.thickness_unit})
1038        write_node(doc, sample, "transmission", datainfo.sample.transmission)
1039        write_node(doc, sample, "temperature", datainfo.sample.temperature, {"unit":datainfo.sample.temperature_unit})
1040       
1041        for item in datainfo.sample.details:
1042            write_node(doc, sample, "details", item)
1043       
1044        pos = doc.createElement("position")
1045        written = write_node(doc, pos, "x", datainfo.sample.position.x, {"unit":datainfo.sample.position_unit})
1046        written = written | write_node(doc, pos, "y", datainfo.sample.position.y, {"unit":datainfo.sample.position_unit})
1047        written = written | write_node(doc, pos, "z", datainfo.sample.position.z, {"unit":datainfo.sample.position_unit})
1048        if written == True:
1049            sample.appendChild(pos)
1050       
1051        ori = doc.createElement("orientation")
1052        written = write_node(doc, ori, "roll",  datainfo.sample.orientation.x, {"unit":datainfo.sample.orientation_unit})
1053        written = written | write_node(doc, ori, "pitch", datainfo.sample.orientation.y, {"unit":datainfo.sample.orientation_unit})
1054        written = written | write_node(doc, ori, "yaw",   datainfo.sample.orientation.z, {"unit":datainfo.sample.orientation_unit})
1055        if written == True:
1056            sample.appendChild(ori)
1057       
1058        # Instrument info
1059        instr = doc.createElement("SASinstrument")
1060        entry_node.appendChild(instr)
1061       
1062        write_node(doc, instr, "name", datainfo.instrument)
1063       
1064        #   Source
1065        source = doc.createElement("SASsource")
1066        if datainfo.source.name is not None:
1067            source.setAttribute("name", str(datainfo.source.name))
1068        instr.appendChild(source)
1069       
1070        write_node(doc, source, "radiation", datainfo.source.radiation)
1071        write_node(doc, source, "beam_shape", datainfo.source.beam_shape)
1072        size = doc.createElement("beam_size")
1073        if datainfo.source.beam_size_name is not None:
1074            size.setAttribute("name", str(datainfo.source.beam_size_name))
1075        written = write_node(doc, size, "x", datainfo.source.beam_size.x, {"unit":datainfo.source.beam_size_unit})
1076        written = written | write_node(doc, size, "y", datainfo.source.beam_size.y, {"unit":datainfo.source.beam_size_unit})
1077        written = written | write_node(doc, size, "z", datainfo.source.beam_size.z, {"unit":datainfo.source.beam_size_unit})
1078        if written == True:
1079            source.appendChild(size)
1080           
1081        write_node(doc, source, "wavelength", datainfo.source.wavelength, {"unit":datainfo.source.wavelength_unit})
1082        write_node(doc, source, "wavelength_min", datainfo.source.wavelength_min, {"unit":datainfo.source.wavelength_min_unit})
1083        write_node(doc, source, "wavelength_max", datainfo.source.wavelength_max, {"unit":datainfo.source.wavelength_max_unit})
1084        write_node(doc, source, "wavelength_spread", datainfo.source.wavelength_spread, {"unit":datainfo.source.wavelength_spread_unit})
1085       
1086        #   Collimation
1087        for item in datainfo.collimation:
1088            coll = doc.createElement("SAScollimation")
1089            if item.name is not None:
1090                coll.setAttribute("name", str(item.name))
1091            instr.appendChild(coll)
1092           
1093            write_node(doc, coll, "length", item.length, {"unit":item.length_unit})
1094           
1095            for apert in item.aperture:
1096                ap = doc.createElement("aperture")
1097                if apert.name is not None:
1098                    ap.setAttribute("name", str(apert.name))
1099                if apert.type is not None:
1100                    ap.setAttribute("type", str(apert.type))
1101                coll.appendChild(ap)
1102               
1103                write_node(doc, ap, "distance", apert.distance, {"unit":apert.distance_unit})
1104               
1105                size = doc.createElement("size")
1106                if apert.size_name is not None:
1107                    size.setAttribute("name", str(apert.size_name))
1108                written = write_node(doc, size, "x", apert.size.x, {"unit":apert.size_unit})
1109                written = written | write_node(doc, size, "y", apert.size.y, {"unit":apert.size_unit})
1110                written = written | write_node(doc, size, "z", apert.size.z, {"unit":apert.size_unit})
1111                if written == True:
1112                    ap.appendChild(size)
1113
1114        #   Detectors
1115        for item in datainfo.detector:
1116            det = doc.createElement("SASdetector")
1117            written = write_node(doc, det, "name", item.name)
1118            written = written | write_node(doc, det, "SDD", item.distance, {"unit":item.distance_unit})
1119            written = written | write_node(doc, det, "slit_length", item.slit_length, {"unit":item.slit_length_unit})
1120            if written == True:
1121                instr.appendChild(det)
1122           
1123            off = doc.createElement("offset")
1124            written = write_node(doc, off, "x", item.offset.x, {"unit":item.offset_unit})
1125            written = written | write_node(doc, off, "y", item.offset.y, {"unit":item.offset_unit})
1126            written = written | write_node(doc, off, "z", item.offset.z, {"unit":item.offset_unit})
1127            if written == True:
1128                det.appendChild(off)
1129           
1130            center = doc.createElement("beam_center")
1131            written = write_node(doc, center, "x", item.beam_center.x, {"unit":item.beam_center_unit})
1132            written = written | write_node(doc, center, "y", item.beam_center.y, {"unit":item.beam_center_unit})
1133            written = written | write_node(doc, center, "z", item.beam_center.z, {"unit":item.beam_center_unit})
1134            if written == True:
1135                det.appendChild(center)
1136               
1137            pix = doc.createElement("pixel_size")
1138            written = write_node(doc, pix, "x", item.pixel_size.x, {"unit":item.pixel_size_unit})
1139            written = written | write_node(doc, pix, "y", item.pixel_size.y, {"unit":item.pixel_size_unit})
1140            written = written | write_node(doc, pix, "z", item.pixel_size.z, {"unit":item.pixel_size_unit})
1141            if written == True:
1142                det.appendChild(pix)
1143               
1144            ori = doc.createElement("orientation")
1145            written = write_node(doc, ori, "roll",  item.orientation.x, {"unit":item.orientation_unit})
1146            written = written | write_node(doc, ori, "pitch", item.orientation.y, {"unit":item.orientation_unit})
1147            written = written | write_node(doc, ori, "yaw",   item.orientation.z, {"unit":item.orientation_unit})
1148            if written == True:
1149                det.appendChild(ori)
1150               
1151        # Processes info
1152        for item in datainfo.process:
1153            node = doc.createElement("SASprocess")
1154            entry_node.appendChild(node)
1155
1156            write_node(doc, node, "name", item.name)
1157            write_node(doc, node, "date", item.date)
1158            write_node(doc, node, "description", item.description)
1159            for term in item.term:
1160                value = term['value']
1161                del term['value']
1162                write_node(doc, node, "term", value, term)
1163            for note in item.notes:
1164                write_node(doc, node, "SASprocessnote", note)
1165        # Return the document, and the SASentry node associated with
1166        # the data we just wrote
1167        return doc, entry_node
1168   
1169    def _parse_state(self, entry, NODE_NAME= 'state'):
1170        """
1171        Read a fit result from an XML node
1172       
1173        :param entry: XML node to read from
1174       
1175        :return: PageState object
1176        """
1177        # Create an empty state
1178        state = None   
1179        # Locate the P(r) node
1180        try:
1181            nodes = entry.xpath('ns:%s' % NODE_NAME, namespaces={'ns': STATE_NODE})
1182            if nodes !=[]:
1183                # Create an empty state
1184                state =  PageState()
1185                state.fromXML(node=nodes[0])
1186        except:
1187            logging.info("XML document does not contain fitting information.\n %s" % sys.exc_value)
1188           
1189        return state
1190   
1191   
1192                   
1193    def _parse_entry(self, dom):
1194        """
1195        Parse a SASentry
1196       
1197        :param node: SASentry node
1198       
1199        :return: Data1D/Data2D object
1200       
1201        """
1202        node = dom.xpath('ns:data_class', namespaces={'ns': STATE_NODE})
1203        if not node or node[0].text.lstrip().rstrip() != "Data2D":
1204            return CansasReader._parse_entry(self, dom)
1205       
1206        #Parse 2D
1207        data_info = Data2D()
1208       
1209        # Look up title     
1210        self._store_content('ns:Title', dom, 'title', data_info)
1211       
1212        # Look up run number   
1213        nodes = dom.xpath('ns:Run', namespaces={'ns': STATE_NODE})
1214        for item in nodes:   
1215            if item.text is not None:
1216                value = item.text.strip()
1217                if len(value) > 0:
1218                    data_info.run.append(value)
1219                    if item.get('name') is not None:
1220                        data_info.run_name[value] = item.get('name')
1221                           
1222        # Look up instrument name             
1223        self._store_content('ns:SASinstrument/ns:name', dom, 'instrument', data_info)
1224
1225        # Notes
1226        note_list = dom.xpath('ns:SASnote', namespaces={'ns': STATE_NODE})
1227        for note in note_list:
1228            try:
1229                if note.text is not None:
1230                    note_value = note.text.strip()
1231                    if len(note_value) > 0:
1232                        data_info.notes.append(note_value)
1233            except:
1234                err_mess = "state_reader.read: error processing entry notes\n  %s" % sys.exc_value
1235                self.errors.append(err_mess)
1236                logging.error(err_mess)
1237       
1238        # Sample info ###################
1239        entry = get_content('ns:SASsample', dom)
1240        if entry is not None:
1241            data_info.sample.name = entry.get('name')
1242           
1243        self._store_content('ns:SASsample/ns:ID', 
1244                     dom, 'ID', data_info.sample)                   
1245        self._store_float('ns:SASsample/ns:thickness', 
1246                     dom, 'thickness', data_info.sample)
1247        self._store_float('ns:SASsample/ns:transmission', 
1248                     dom, 'transmission', data_info.sample)
1249        self._store_float('ns:SASsample/ns:temperature', 
1250                     dom, 'temperature', data_info.sample)
1251       
1252        nodes = dom.xpath('ns:SASsample/ns:details', namespaces={'ns': STATE_NODE})
1253        for item in nodes:
1254            try:
1255                if item.text is not None:
1256                    detail_value = item.text.strip()
1257                    if len(detail_value) > 0:
1258                        data_info.sample.details.append(detail_value)
1259            except:
1260                err_mess = "state_reader.read: error processing sample details\n  %s" % sys.exc_value
1261                self.errors.append(err_mess)
1262                logging.error(err_mess)
1263       
1264        # Position (as a vector)
1265        self._store_float('ns:SASsample/ns:position/ns:x', 
1266                     dom, 'position.x', data_info.sample)         
1267        self._store_float('ns:SASsample/ns:position/ns:y', 
1268                     dom, 'position.y', data_info.sample)         
1269        self._store_float('ns:SASsample/ns:position/ns:z', 
1270                     dom, 'position.z', data_info.sample)         
1271       
1272        # Orientation (as a vector)
1273        self._store_float('ns:SASsample/ns:orientation/ns:roll', 
1274                     dom, 'orientation.x', data_info.sample)         
1275        self._store_float('ns:SASsample/ns:orientation/ns:pitch', 
1276                     dom, 'orientation.y', data_info.sample)         
1277        self._store_float('ns:SASsample/ns:orientation/ns:yaw', 
1278                     dom, 'orientation.z', data_info.sample)         
1279       
1280        # Source info ###################
1281        entry = get_content('ns:SASinstrument/ns:SASsource', dom)
1282        if entry is not None:
1283            data_info.source.name = entry.get('name')
1284       
1285        self._store_content('ns:SASinstrument/ns:SASsource/ns:radiation', 
1286                     dom, 'radiation', data_info.source)                   
1287        self._store_content('ns:SASinstrument/ns:SASsource/ns:beam_shape', 
1288                     dom, 'beam_shape', data_info.source)                   
1289        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength', 
1290                     dom, 'wavelength', data_info.source)         
1291        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_min', 
1292                     dom, 'wavelength_min', data_info.source)         
1293        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_max', 
1294                     dom, 'wavelength_max', data_info.source)         
1295        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_spread', 
1296                     dom, 'wavelength_spread', data_info.source)   
1297       
1298        # Beam size (as a vector)   
1299        entry = get_content('ns:SASinstrument/ns:SASsource/ns:beam_size', dom)
1300        if entry is not None:
1301            data_info.source.beam_size_name = entry.get('name')
1302           
1303        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:x', 
1304                     dom, 'beam_size.x', data_info.source)   
1305        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:y', 
1306                     dom, 'beam_size.y', data_info.source)   
1307        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:z', 
1308                     dom, 'beam_size.z', data_info.source)   
1309       
1310        # Collimation info ###################
1311        nodes = dom.xpath('ns:SASinstrument/ns:SAScollimation', namespaces={'ns': STATE_NODE})
1312        for item in nodes:
1313            collim = Collimation()
1314            if item.get('name') is not None:
1315                collim.name = item.get('name')
1316            self._store_float('ns:length', item, 'length', collim) 
1317           
1318            # Look for apertures
1319            apert_list = item.xpath('ns:aperture', namespaces={'ns': STATE_NODE})
1320            for apert in apert_list:
1321                aperture =  Aperture()
1322               
1323                # Get the name and type of the aperture
1324                aperture.name = apert.get('name')
1325                aperture.type = apert.get('type')
1326                   
1327                self._store_float('ns:distance', apert, 'distance', aperture)   
1328               
1329                entry = get_content('ns:size', apert)
1330                if entry is not None:
1331                    aperture.size_name = entry.get('name')
1332               
1333                self._store_float('ns:size/ns:x', apert, 'size.x', aperture)   
1334                self._store_float('ns:size/ns:y', apert, 'size.y', aperture)   
1335                self._store_float('ns:size/ns:z', apert, 'size.z', aperture)
1336               
1337                collim.aperture.append(aperture)
1338               
1339            data_info.collimation.append(collim)
1340       
1341        # Detector info ######################
1342        nodes = dom.xpath('ns:SASinstrument/ns:SASdetector', namespaces={'ns': STATE_NODE})
1343        for item in nodes:
1344           
1345            detector = Detector()
1346           
1347            self._store_content('ns:name', item, 'name', detector)
1348            self._store_float('ns:SDD', item, 'distance', detector)   
1349           
1350            # Detector offset (as a vector)
1351            self._store_float('ns:offset/ns:x', item, 'offset.x', detector)   
1352            self._store_float('ns:offset/ns:y', item, 'offset.y', detector)   
1353            self._store_float('ns:offset/ns:z', item, 'offset.z', detector)   
1354           
1355            # Detector orientation (as a vector)
1356            self._store_float('ns:orientation/ns:roll',  item, 'orientation.x', detector)   
1357            self._store_float('ns:orientation/ns:pitch', item, 'orientation.y', detector)   
1358            self._store_float('ns:orientation/ns:yaw',   item, 'orientation.z', detector)   
1359           
1360            # Beam center (as a vector)
1361            self._store_float('ns:beam_center/ns:x', item, 'beam_center.x', detector)   
1362            self._store_float('ns:beam_center/ns:y', item, 'beam_center.y', detector)   
1363            self._store_float('ns:beam_center/ns:z', item, 'beam_center.z', detector)   
1364           
1365            # Pixel size (as a vector)
1366            self._store_float('ns:pixel_size/ns:x', item, 'pixel_size.x', detector)   
1367            self._store_float('ns:pixel_size/ns:y', item, 'pixel_size.y', detector)   
1368            self._store_float('ns:pixel_size/ns:z', item, 'pixel_size.z', detector)   
1369           
1370            self._store_float('ns:slit_length', item, 'slit_length', detector)
1371           
1372            data_info.detector.append(detector)   
1373
1374        # Processes info ######################
1375        nodes = dom.xpath('ns:SASprocess', namespaces={'ns': STATE_NODE})
1376        for item in nodes:
1377            process = Process()
1378            self._store_content('ns:name', item, 'name', process)
1379            self._store_content('ns:date', item, 'date', process)
1380            self._store_content('ns:description', item, 'description', process)
1381           
1382            term_list = item.xpath('ns:term', namespaces={'ns': STATE_NODE})
1383            for term in term_list:
1384                try:
1385                    term_attr = {}
1386                    for attr in term.keys():
1387                        term_attr[attr] = term.get(attr).strip()
1388                    if term.text is not None:
1389                        term_attr['value'] = term.text.strip()
1390                        process.term.append(term_attr)
1391                except:
1392                    err_mess = "state_reader.read: error processing process term\n  %s" % sys.exc_value
1393                    self.errors.append(err_mess)
1394                    logging.error(err_mess)
1395           
1396            note_list = item.xpath('ns:SASprocessnote', namespaces={'ns': STATE_NODE})
1397            for note in note_list:
1398                if note.text is not None:
1399                    process.notes.append(note.text.strip())
1400           
1401            data_info.process.append(process)
1402           
1403           
1404        # Data info ######################
1405        nodes = dom.xpath('ns:SASdata', namespaces={'ns': STATE_NODE})
1406        if len(nodes)>1:
1407            raise RuntimeError, "CanSAS reader is not compatible with multiple SASdata entries"
1408       
1409        for entry in nodes:
1410            for item in list_of_data_2d_attr:
1411                #get node
1412                node = get_content('ns:%s'%item[0], entry)
1413                exec "data_info.%s = parse_entry_helper(node, item)"%(item[1])
1414                   
1415            for item in list_of_data2d_values:
1416                field = get_content('ns:%s'%item[0], entry)
1417                list = []
1418                if field is not None:
1419                    list = [parse_entry_helper(node, item) for node in field]
1420                exec "data_info.%s = numpy.array(list)"%item[0]
1421       
1422        return data_info
1423
1424    def _read_cansas(self, path):
1425        """
1426        Load data and P(r) information from a CanSAS XML file.
1427       
1428        :param path: file path
1429       
1430        :return: Data1D object if a single SASentry was found,
1431                    or a list of Data1D objects if multiple entries were found,
1432                    or None of nothing was found
1433                   
1434        :raise RuntimeError: when the file can't be opened
1435        :raise ValueError: when the length of the data vectors are inconsistent
1436       
1437        """
1438        output = []
1439        basename  = os.path.basename(path)
1440        root, extension = os.path.splitext(basename)
1441        ext = extension.lower()
1442        try:
1443            if os.path.isfile(path):
1444               
1445                #TODO: eventually remove the check for .xml once
1446                # the P(r) writer/reader is truly complete.
1447                if  ext in self.ext or \
1448                    ext == '.xml':
1449                   
1450                    tree = etree.parse(path, parser=etree.ETCompatXMLParser())
1451                    # Check the format version number
1452                    # Specifying the namespace will take care of the file format version
1453                    root = tree.getroot()
1454                    entry_list = root.xpath('ns:SASentry', namespaces={'ns': STATE_NODE})
1455                    for entry in entry_list:   
1456                        try:
1457                            sas_entry = self._parse_entry(entry)
1458                        except:
1459                            raise
1460                        fitstate = self._parse_state(entry)
1461                       
1462                        #state could be None when .svs file is loaded
1463                        #in this case, skip appending to output
1464                        if fitstate != None:
1465                            sas_entry.meta_data['fitstate'] = fitstate
1466                            sas_entry.filename = fitstate.file
1467                            output.append(sas_entry)
1468            else:
1469                self.call_back(format=ext)
1470                raise RuntimeError, "%s is not a file" % path
1471
1472            # Return output consistent with the loader's api
1473            if len(output)==0:
1474                self.call_back(state=None, datainfo=None,format=ext)
1475                return None
1476            else:
1477                for ind in range(len(output)):
1478                    # Call back to post the new state
1479                    state = output[ind].meta_data['fitstate']
1480                    t = time.localtime(state.timestamp)
1481                    time_str = time.strftime("%b %d %H:%M", t)
1482                    # Check that no time stamp is already appended
1483                    max_char = state.file.find("[")
1484                    if max_char < 0:
1485                        max_char = len(state.file)
1486                    original_fname = state.file[0:max_char]
1487                    state.file = original_fname +' [' + time_str + ']'
1488                   
1489                       
1490                    if state is not None and state.is_data is not None:
1491                        exec 'output[%d].is_data = state.is_data'% ind
1492                     
1493                    output[ind].filename = state.file
1494                    state.data = output[ind]
1495                    state.data.name = output[ind].filename #state.data_name
1496                    state.data.id = state.data_id
1497                    if state.is_data is not None:
1498                        state.data.is_data = state.is_data
1499                    if output[ind].run_name is not None and\
1500                        len(output[ind].run_name) != 0 :
1501                        name = output[ind].run_name
1502                    else: 
1503                        name=original_fname
1504                    state.data.group_id = name
1505                    #store state in fitting
1506                    self.call_back(state=state, datainfo=output[ind],format=ext)
1507                    self.state= state
1508                return output
1509             
1510        except:
1511            #self.call_back(format=ext)
1512            self.state= state
1513            raise
1514           
1515    def write(self, filename, datainfo=None, fitstate=None):
1516        """
1517        Write the content of a Data1D as a CanSAS XML file only for standalone
1518       
1519        :param filename: name of the file to write
1520        :param datainfo: Data1D object
1521        :param fitstate: PageState object
1522       
1523        """
1524        # Sanity check
1525        if self.cansas == True:
1526           
1527            # Add fitting information to the XML document
1528            doc = self.write_toXML(datainfo, fitstate)
1529            # Write the XML document
1530            fd = open(filename, 'w')
1531            fd.write(doc.toprettyxml())
1532            fd.close()
1533        else:
1534            fitstate.toXML(file=filename)
1535       
1536    def write_toXML(self, datainfo=None, state=None):
1537        """
1538        Write toXML, a helper for write() , could be used by guimanager._on_save()
1539       
1540        : return: xml doc
1541        """
1542
1543        if state.data is None:
1544            data = Data2D() 
1545        else: 
1546            #make sure title and data run is filled up.
1547            if state.data.title == None or state.data.title=='':
1548                 state.data.title = state.data.name
1549            if state.data.run_name == None or state.data.run_name=={}: 
1550                state.data.run = [str(state.data.name)]
1551                state.data.run_name[0] = state.data.name
1552            doc, sasentry = self._to_xml_doc(data)
1553           
1554
1555        if state is not None:
1556            state.toXML(doc=doc, file=data.name, entry_node=sasentry)
1557           
1558        return doc
1559 
1560if __name__ == "__main__":
1561    state = PageState(parent=None)
1562    #state.toXML()
1563    """
1564   
1565    file = open("test_state", "w")
1566    pickle.dump(state, file)
1567    print pickle.dumps(state)
1568    state.data_name = "hello---->"
1569    pickle.dump(state, file)
1570    file = open("test_state", "r")
1571    new_state= pickle.load(file)
1572    print "new state", new_state
1573    new_state= pickle.load(file)
1574    print "new state", new_state
1575    #print "state", state
1576    """
1577    import bsddb
1578    import pickle
1579    db= bsddb.btopen('file_state.db', 'c')
1580    val = (pickle.dumps(state), "hello", "hi")
1581    db['state1']= pickle.dumps(val)
1582    print pickle.loads(db['state1'])
1583    state.data_name = "hello---->22"
1584    db['state2']= pickle.dumps(state)
1585    state.data_name = "hello---->2"
1586    db['state3']= pickle.dumps(state)
1587    del db['state3']
1588    state.data_name = "hello---->3"
1589    db['state4']= pickle.dumps(state)
1590    new_state = pickle.loads(db['state1'])
1591    #print db.last()
1592    db.set_location('state2')
1593    state.data_name = "hello---->5"
1594    db['aastate5']= pickle.dumps(state)
1595    db.keys().sort()
1596    print pickle.loads(db['state2'])
1597 
1598    db.close() 
1599
1600    logging.basicConfig(level=logging.ERROR,
1601                        format='%(asctime)s %(levelname)s %(message)s',
1602                        filename='state_reader.log',
1603                        filemode='w')
1604    reader = Reader()
1605    print reader.read("../test/cansas1d.xml")
1606    #print reader.read("../test/latex_smeared.xml")
1607   
1608   
1609                       
Note: See TracBrowser for help on using the repository browser.