source: sasview/src/sans/dataloader/readers/cansas_reader.py @ f44b076

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 f44b076 was 9e2bc6c, checked in by Jeff Krzywon <jeffery.krzywon@…>, 11 years ago

Fixing tickets #204 and #233.

  • Property mode set to 100644
File size: 38.0 KB
Line 
1"""
2    CanSAS data reader - new recursive cansas_version.
3"""
4############################################################################
5#This software was developed by the University of Tennessee as part of the
6#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
7#project funded by the US National Science Foundation.
8#If you use DANSE applications to do scientific research that leads to
9#publication, we ask that you acknowledge the use of the software with the
10#following sentence:
11#This work benefited from DANSE software developed under NSF award DMR-0520547.
12#copyright 2008,2009 University of Tennessee
13#############################################################################
14
15import logging
16import numpy
17import os
18import sys
19import datetime
20from sans.dataloader.data_info import Data1D
21from sans.dataloader.data_info import Collimation
22from sans.dataloader.data_info import TransmissionSpectrum
23from sans.dataloader.data_info import Detector
24from sans.dataloader.data_info import Process
25from sans.dataloader.data_info import Aperture
26import sans.dataloader.readers.xml_reader as xml_reader
27import xml.dom.minidom
28from sans.dataloader.readers.cansas_constants import cansasConstants
29
30_ZERO = 1e-16
31PREPROCESS = "xmlpreprocess"
32HAS_CONVERTER = True
33try:
34    from sans.data_util.nxsunit import Converter
35except:
36    HAS_CONVERTER = False
37
38constants = cansasConstants()   
39CANSAS_FORMAT = constants.format
40CANSAS_NS = constants.ns
41ALLOW_ALL = True
42
43
44def get_content(location, node):
45    """
46    Get the first instance of the content of a xpath location.
47   
48    :param location: xpath location
49    :param node: node to start at
50   
51    :return: Element, or None
52    """
53    nodes = node.xpath(location, namespaces={'ns': CANSAS_NS})
54   
55    if len(nodes) > 0:
56        return nodes[0]
57    else:
58        return None
59
60
61def get_float(location, node):
62    """
63    Get the content of a node as a float
64   
65    :param location: xpath location
66    :param node: node to start at
67    """
68    nodes = node.xpath(location, namespaces={'ns': CANSAS_NS})
69   
70    value = None
71    attr = {}
72    if len(nodes) > 0:
73        try:
74            value = float(nodes[0].text)
75        except:
76            # Could not pass, skip and return None
77            msg = "cansas_reader.get_float: could not "
78            msg += " convert '%s' to float" % nodes[0].text
79            logging.error(msg)
80        if nodes[0].get('unit') is not None:
81            attr['unit'] = nodes[0].get('unit')
82    return value, attr
83
84
85def write_node(doc, parent, name, value, attr={}):
86    """
87    :param doc: document DOM
88    :param parent: parent node
89    :param name: tag of the element
90    :param value: value of the child text node
91    :param attr: attribute dictionary
92   
93    :return: True if something was appended, otherwise False
94    """
95    if value is not None:
96        node = doc.createElement(name)
97        node.appendChild(doc.createTextNode(str(value)))
98        for item in attr:
99            node.setAttribute(item, attr[item])
100        parent.appendChild(node)
101        return True
102    return False
103               
104
105class Reader():
106    """
107    Class to load cansas 1D XML files
108   
109    :Dependencies:
110        The CanSAS reader requires PyXML 0.8.4 or later.
111    """
112    ##CanSAS version - defaults to version 1.0
113    cansas_version = "1.0"
114    ##Data reader
115    reader = xml_reader.XMLreader()
116    errors = []
117   
118    type_name = "canSAS"
119   
120    ## Wildcards
121    type = ["XML files (*.xml)|*.xml"]
122    ## List of allowed extensions
123    ext = ['.xml', '.XML']
124   
125    ## Flag to bypass extension check
126    allow_all = True
127   
128    def __init__(self):
129        ## List of errors
130        self.errors = []
131       
132    def is_cansas(self):
133        """
134        Checks to see if the xml file is a CanSAS file
135        """
136        if self.reader.validateXML():
137            xmlns = self.reader.xmlroot.keys()
138            if (CANSAS_NS.get(self.cansas_version).get("ns") == \
139                    self.reader.xmlroot.get(xmlns[1]).rsplit(" ")[0]):
140                return True
141        return False
142   
143    def read(self, xml):
144        """
145        Validate and read in an xml file in the canSAS format.
146       
147        :param xml: A canSAS file path in proper XML format
148        """
149        # X - Q value; Y - Intensity (Abs)
150        x = numpy.empty(0)
151        y = numpy.empty(0)
152        dx = numpy.empty(0)
153        dy = numpy.empty(0)
154        dxl = numpy.empty(0)
155        dxw = numpy.empty(0)
156       
157        # output - Final list of Data1D objects
158        output = []
159        # ns - Namespace hierarchy for current xml object
160        ns = []
161       
162        # Check that the file exists
163        if os.path.isfile(xml):
164            basename = os.path.basename(xml)
165            _, extension = os.path.splitext(basename)
166            # If the fiel type is not allowed, return nothing
167            if extension in self.ext or self.allow_all:
168                base_name = xml_reader.__file__
169                base_name = base_name.replace("\\","/")
170                base = base_name.split("/sans/")[0]
171               
172                # Load in xml file and get the cansas version from the header
173                self.reader.setXMLFile(xml)
174                root = self.reader.xmlroot
175                if root is None:
176                    root = {}
177                self.cansas_version = root.get("version", "1.0")
178               
179                # Generic values for the cansas file based on the version
180                cansas_defaults = CANSAS_NS.get(self.cansas_version, "1.0")
181                schema_path = "{0}/sans/dataloader/readers/schema/{1}".format\
182                        (base, cansas_defaults.get("schema")).replace("\\", "/")
183               
184                # Link a schema to the XML file.
185                self.reader.setSchema(schema_path)
186           
187                # Try to load the file, but raise an error if unable to.
188                # Check the file matches the XML schema
189                try:
190                    if self.is_cansas():
191                        # Get each SASentry from XML file and add it to a list.
192                        entry_list = root.xpath('/ns:SASroot/ns:SASentry',
193                                namespaces={'ns': cansas_defaults.get("ns")})
194                        ns.append("SASentry")
195                       
196                        # If multiple files, modify the name for each is unique
197                        multiple_files = len(entry_list) - 1
198                        increment = 0
199                        name = basename
200                        # Parse each SASentry item
201                        for entry in entry_list:
202                            # Define a new Data1D object with zeroes for x and y
203                            data1d = Data1D(x,y,dx,dy)
204                            data1d.dxl = dxl
205                            data1d.dxw = dxw
206                           
207                            # If more than one SASentry, increment each in order
208                            if multiple_files:
209                                name += "_{0}".format(increment)
210                                increment += 1
211                           
212                            # Set the Data1D name and then parse the entry.
213                            # The entry is appended to a list of entry values
214                            data1d.filename = name
215                            data1d.meta_data["loader"] = "CanSAS 1D"
216                           
217                            # Get all preprocessing events
218                            self.reader.setProcessingInstructions()
219                            data1d.meta_data[PREPROCESS] = \
220                                    self.reader.processingInstructions
221                           
222                            # Parse the XML file
223                            return_value, extras = \
224                                self._parse_entry(entry, ns, data1d)
225                            del extras[:]
226                           
227                            # Final cleanup
228                            # Remove empty nodes, verify array sizes are correct
229                            for error in self.errors:
230                                return_value.errors.append(error)
231                            del self.errors[:]
232                            numpy.trim_zeros(return_value.x)
233                            numpy.trim_zeros(return_value.y)
234                            numpy.trim_zeros(return_value.dy)
235                            size_dx = return_value.dx.size
236                            size_dxl = return_value.dxl.size
237                            size_dxw = return_value.dxw.size
238                            if size_dxl == 0 and size_dxw == 0:
239                                return_value.dxl = None
240                                return_value.dxw = None
241                                numpy.trim_zeros(return_value.dx)
242                            elif size_dx == 0:
243                                return_value.dx = None
244                                size_dx = size_dxl
245                                numpy.trim_zeros(return_value.dxl)
246                                numpy.trim_zeros(return_value.dxw)
247                            output.append(return_value)
248                    else:
249                        value = self.reader.findInvalidXML()
250                        output.append("Invalid XML at: {0}".format(value))
251                except:
252                    # If the file does not match the schema, raise this error
253                    raise RuntimeError, "%s cannot be read \increment" % xml
254                return output
255        # Return a list of parsed entries that dataloader can manage
256        return None
257   
258    def _create_unique_key(self, dictionary, name, i = 0):
259        """
260        Create a unique key value for any dictionary to prevent overwriting
261        Recurses until a unique key value is found.
262       
263        :param dictionary: A dictionary with any number of entries
264        :param name: The index of the item to be added to dictionary
265        :param i: The number to be appended to the name, starts at 0
266        """
267        if dictionary.get(name) is not None:
268            i += 1
269            name = name.split("_")[0]
270            name += "_{0}".format(i)
271            name = self._create_unique_key(dictionary, name, i)
272        return name
273   
274   
275    def _unit_conversion(self, new_current_level, attr, data1d, \
276                                    tagname, node_value, optional = True):
277        """
278        A unit converter method used to convert the data included in the file
279        to the default units listed in data_info
280       
281        :param new_current_level: cansas_constants level as returned by
282            _iterate_namespace
283        :param attr: The attributes of the node
284        :param data1d: Where the values will be saved
285        :param node_value: The value of the current dom node
286        :param optional: Boolean that says if the units are required
287        """
288        value_unit = ''
289        if 'unit' in attr and new_current_level.get('unit') is not None:
290            try:
291                if isinstance(node_value, float) is False:
292                    exec("node_value = float({0})".format(node_value))
293                default_unit = None
294                unitname = new_current_level.get("unit")
295                exec "default_unit = data1d.{0}".format(unitname)
296                local_unit = attr['unit']
297                if local_unit.lower() != default_unit.lower() and \
298                    local_unit is not None and local_unit.lower() != "none" and\
299                     default_unit is not None:
300                    if HAS_CONVERTER == True:
301                        try:
302                            ## Check local units - bad units raise KeyError
303                            Converter(local_unit)
304                            data_conv_q = Converter(attr['unit'])
305                            value_unit = default_unit
306                            exec "node_value = data_conv_q(node_value, units=data1d.{0})".format(unitname)
307                        except KeyError as e:
308                            err_msg = "CanSAS reader: could not convert "
309                            err_msg += "{0} unit {1}; ".format(tagname, local_unit)
310                            intermediate = "err_msg += \"expecting [{1}]  {2}\".format(data1d.{0}, sys.exc_info()[1])".format(unitname, "{0}", "{1}")
311                            exec intermediate
312                            self.errors.append(err_msg)
313                            raise ValueError(err_msg)
314                            return
315                        except:
316                            err_msg = "CanSAS reader: could not convert the units"
317                            self.errors.append(err_msg)
318                            return
319                    else:
320                        value_unit = local_unit
321                        err_msg = "CanSAS reader: unrecognized %s unit [%s];"\
322                        % (node_value, default_unit)
323                        err_msg += " expecting [%s]" % local_unit
324                        self.errors.append(err_msg)
325                        raise ValueError, err_msg
326                        return
327                else:
328                    value_unit = local_unit
329            except:
330                err_msg = "CanSAS reader: could not convert "
331                err_msg += "Q unit [%s]; " % attr['unit'],
332                exec "err_msg += \"expecting [%s]\n  %s\" % (data1d.{0}, sys.exc_info()[1])".format(unitname)
333                self.errors.append(err_msg)
334                raise ValueError, err_msg
335                return
336        elif 'unit' in attr:
337            value_unit = attr['unit']
338        node_value = "float({0})".format(node_value)
339        return node_value, value_unit
340   
341    def _parse_entry(self, dom, ns, data1d, extras = []):
342        """
343        Parse a SASEntry - new recursive method for parsing the dom of
344            the CanSAS data format. This will allow multiple data files
345            and extra nodes to be read in simultaneously.
346       
347        :param dom: dom object with a namespace base of ns
348        :param ns: A list of element names that lead up to the dom object
349        :param data1d: The data1d object that will be modified
350        :param extras: Any values that should go into meta_data when data1d
351            is not a Data1D object
352        """
353         
354        # A portion of every namespace entry
355        base_ns = "{0}{1}{2}".format("{", \
356                            CANSAS_NS.get(self.cansas_version).get("ns"), "}")
357        unit = ''
358        tagname = ''
359        tagname_original = ''
360       
361        # Go through each child in the parent element
362        for node in dom:
363            try:
364                # Get the element name and set the current ns level
365                tagname = node.tag.replace(base_ns, "")
366                tagname_original = tagname
367                ns.append(tagname)
368                attr = node.attrib
369                children = node.getchildren()
370                save_data1d = data1d
371               
372                # Look for special cases
373                if tagname == "SASdetector":
374                    data1d = Detector()
375                elif tagname == "SAScollimation":
376                    data1d = Collimation()
377                elif tagname == "SAStransmission_spectrum":
378                    data1d = TransmissionSpectrum()
379                elif tagname == "SASprocess":
380                    data1d = Process()
381                    for child in node:
382                        if child.tag.replace(base_ns, "") == "term":
383                            term_attr = {}
384                            for attr in child.keys():
385                                term_attr[attr] = \
386                                    ' '.join(child.get(attr).split())
387                            if child.text is not None:
388                                term_attr['value'] = \
389                                    ' '.join(child.text.split())
390                            data1d.term.append(term_attr)
391                elif tagname == "aperture":
392                    data1d = Aperture()
393                if tagname == "Idata" and children is not None:
394                    dql = 0
395                    dqw = 0
396                    for child in children:
397                        tag = child.tag.replace(base_ns, "")
398                        if tag == "dQl":
399                            dql = 1
400                        if tag == "dQw":
401                            dqw = 1
402                    if dqw == 1 and dql == 0:
403                        data1d.dxl = numpy.append(data1d.dxl, 0.0)
404                    elif dql == 1 and dqw == 0:
405                        data1d.dxw = numpy.append(data1d.dxw, 0.0)
406                               
407                # Get where to store content
408                cs_values = constants._iterate_namespace(ns)
409                # If the element is a child element, recurse
410                if children is not None:
411                    # Returned value is new Data1D object with all previous and
412                    # new values in it.
413                    data1d, extras = self._parse_entry(node, ns, data1d, extras)
414                   
415                #Get the information from the node
416                node_value = node.text
417                if node_value == "":
418                    node_value = None
419                if node_value is not None:
420                    node_value = ' '.join(node_value.split())
421               
422                # If the value is a float, compile with units.
423                if cs_values.ns_datatype == "float":
424                    # If an empty value is given, store as zero.
425                    if node_value is None or node_value.isspace() \
426                                            or node_value.lower() == "nan":
427                        node_value = "0.0"
428                    node_value, unit = self._unit_conversion(\
429                                cs_values.current_level, attr, data1d, \
430                                tagname, node_value, cs_values.ns_optional)
431                # If the value is a timestamp, convert to a datetime object
432                elif cs_values.ns_datatype == "timestamp":
433                    if node_value is None or node_value.isspace():
434                        pass
435                    else:
436                        try:
437                            node_value = \
438                                datetime.datetime.fromtimestamp(node_value)
439                        except ValueError:
440                            node_value = None
441                # If appending to a dictionary (meta_data | run_name)
442                # make sure the key is unique
443                if cs_values.ns_variable == "{0}.meta_data[\"{2}\"] = \"{1}\"":
444                    # If we are within a Process, Detector, Collimation or
445                    # Aperture instance, pull out old data1d
446                    tagname = self._create_unique_key(data1d.meta_data, \
447                                                      tagname, 0)
448                    if isinstance(data1d, Data1D) == False:
449                        store_me = cs_values.ns_variable.format("data1d", \
450                                                            node_value, tagname)
451                        extras.append(store_me)
452                        cs_values.ns_variable = None
453                if cs_values.ns_variable == "{0}.run_name[\"{2}\"] = \"{1}\"":
454                    tagname = self._create_unique_key(data1d.run_name, \
455                                                      tagname, 0)
456               
457                # Check for Data1D object and any extra commands to save
458                if isinstance(data1d, Data1D):
459                    for item in extras:
460                        exec item
461                # Don't bother saving empty information unless it is a float
462                if cs_values.ns_variable is not None and \
463                            node_value is not None and \
464                            node_value.isspace() == False:
465                    # Format a string and then execute it.
466                    store_me = cs_values.ns_variable.format("data1d", \
467                                                            node_value, tagname)
468                    exec store_me
469                # Get attributes and process them
470                if attr is not None:
471                    for key in node.keys():
472                        try:
473                            cansas_attrib = \
474                            cs_values.current_level.get("attributes").get(key)
475                            attrib_variable = cansas_attrib.get("variable")
476                            if key == 'unit' and unit != '':
477                                attrib_value = unit
478                            else:
479                                attrib_value = node.attrib[key]
480                            store_attr = attrib_variable.format("data1d", \
481                                                            attrib_value, key)
482                            exec store_attr
483                        except AttributeError as e:
484                            pass
485           
486            except TypeError:
487                pass
488            except Exception as e:
489                exc_type, exc_obj, exc_tb = sys.exc_info()
490                fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
491                print(e, exc_type, fname, exc_tb.tb_lineno, tagname, exc_obj)
492            finally:
493                # Save special cases in original data1d object
494                # then restore the data1d
495                if tagname_original == "SASdetector":
496                    save_data1d.detector.append(data1d)
497                elif tagname_original == "SAScollimation":
498                    save_data1d.collimation.append(data1d)
499                elif tagname == "SAStransmission_spectrum":
500                    save_data1d.trans_spectrum.append(data1d)
501                elif tagname_original == "SASprocess":
502                    save_data1d.process.append(data1d)
503                elif tagname_original == "aperture":
504                    save_data1d.aperture.append(data1d)
505                else:
506                    save_data1d = data1d
507                data1d = save_data1d
508                # Remove tagname from ns to restore original base
509                ns.remove(tagname_original)
510       
511        return data1d, extras
512       
513    def _to_xml_doc(self, datainfo):
514        """
515        Create an XML document to contain the content of a Data1D
516       
517        :param datainfo: Data1D object
518        """
519       
520        if not issubclass(datainfo.__class__, Data1D):
521            raise RuntimeError, "The cansas writer expects a Data1D instance"
522       
523        ns = CANSAS_NS.get(self.cansas_version).get("ns")
524        doc = xml.dom.minidom.Document()
525       
526        main_node = doc.createElement("SASroot")
527       
528        doc = self.setProcessingInstructions(doc, \
529                                        datainfo.meta_data.get(PREPROCESS, {}))
530        main_node.setAttribute("version", self.cansas_version)
531        main_node.setAttribute("xmlns", ns)
532        main_node.setAttribute("xmlns:xsi",
533                               "http://www.w3.org/2001/XMLSchema-instance")
534        if self.cansas_version == "1.0":
535            main_node.setAttribute("xsi:schemaLocation", "cansas1d/1.0 http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd")
536        elif self.cansas_version == "1.1":
537            main_node.setAttribute("xsi:schemaLocation", "urn:cansas1d:1.1 http://www.cansas.org/formats/1.1/cansas1d.xsd")
538       
539        doc.appendChild(main_node)
540       
541        entry_node = doc.createElement("SASentry")
542        main_node.appendChild(entry_node)
543       
544        write_node(doc, entry_node, "Title", datainfo.title)
545        if datainfo.run == None or datainfo.run == []:
546            RUN_NAME_DEFAULT = "None"
547            datainfo.run.append(RUN_NAME_DEFAULT)
548            datainfo.run_name[RUN_NAME_DEFAULT] = RUN_NAME_DEFAULT
549        for item in datainfo.run:
550            runname = {}
551            if item in datainfo.run_name and \
552            len(str(datainfo.run_name[item])) > 1:
553                runname = {'name': datainfo.run_name[item]}
554            write_node(doc, entry_node, "Run", item, runname)
555       
556        # Data info
557        node = doc.createElement("SASdata")
558        entry_node.appendChild(node)
559       
560        for i in range(len(datainfo.x)):
561            pt = doc.createElement("Idata")
562            node.appendChild(pt)
563            write_node(doc, pt, "Q", datainfo.x[i], {'unit': datainfo.x_unit})
564            if len(datainfo.y) >= i:
565                write_node(doc, pt, "I", datainfo.y[i],
566                            {'unit': datainfo.y_unit})
567            if datainfo.dy != None and len(datainfo.dy) > i:
568                write_node(doc, pt, "Idev", datainfo.dy[i],
569                            {'unit': datainfo.y_unit})
570            if datainfo.dx != None and len(datainfo.dx) > i:
571                write_node(doc, pt, "Qdev", datainfo.dx[i],
572                            {'unit': datainfo.x_unit})
573            if datainfo.dxw != None and len(datainfo.dxw) > i:
574                write_node(doc, pt, "dQw", datainfo.dxw[i],
575                            {'unit': datainfo.x_unit})
576            if datainfo.dxl != None and len(datainfo.dxl) > i:
577                write_node(doc, pt, "dQl", datainfo.dxl[i],
578                            {'unit': datainfo.x_unit})
579
580        # Transmission Spectrum Info
581        for i in range(len(datainfo.trans_spectrum)):
582            spectrum = datainfo.trans_spectrum[i]
583            node = doc.createElement("SAStransmission_spectrum")
584            node.setAttribute("name", spectrum.name)
585            if isinstance(spectrum.timestamp, datetime.datetime):
586                node.setAttribute("timestamp", spectrum.timestamp)
587            entry_node.appendChild(node)
588            for i in range(len(spectrum.wavelength)):
589                pt = doc.createElement("Tdata")
590                node.appendChild(pt)
591                write_node(doc, pt, "Lambda", spectrum.wavelength[i], 
592                           {'unit': spectrum.wavelength_unit})
593                write_node(doc, pt, "T", spectrum.transmission[i], 
594                           {'unit': spectrum.transmission_unit})
595                if spectrum.transmission_deviation != None \
596                and len(spectrum.transmission_deviation) >= i:
597                    write_node(doc, pt, "Tdev", \
598                               spectrum.transmission_deviation[i], \
599                               {'unit': spectrum.transmission_deviation_unit})
600
601        # Sample info
602        sample = doc.createElement("SASsample")
603        if datainfo.sample.name is not None:
604            sample.setAttribute("name", str(datainfo.sample.name))
605        entry_node.appendChild(sample)
606        write_node(doc, sample, "ID", str(datainfo.sample.ID))
607        write_node(doc, sample, "thickness", datainfo.sample.thickness,
608                   {"unit": datainfo.sample.thickness_unit})
609        write_node(doc, sample, "transmission", datainfo.sample.transmission)
610        write_node(doc, sample, "temperature", datainfo.sample.temperature,
611                   {"unit": datainfo.sample.temperature_unit})
612       
613        pos = doc.createElement("position")
614        written = write_node(doc, pos, "x", datainfo.sample.position.x,
615                             {"unit": datainfo.sample.position_unit})
616        written = written | write_node(doc, pos, "y",
617                                       datainfo.sample.position.y,
618                                       {"unit": datainfo.sample.position_unit})
619        written = written | write_node(doc, pos, "z",
620                                       datainfo.sample.position.z,
621                                       {"unit": datainfo.sample.position_unit})
622        if written == True:
623            sample.appendChild(pos)
624       
625        ori = doc.createElement("orientation")
626        written = write_node(doc, ori, "roll",
627                             datainfo.sample.orientation.x,
628                             {"unit": datainfo.sample.orientation_unit})
629        written = written | write_node(doc, ori, "pitch",
630                                       datainfo.sample.orientation.y,
631                                    {"unit": datainfo.sample.orientation_unit})
632        written = written | write_node(doc, ori, "yaw",
633                                       datainfo.sample.orientation.z,
634                                    {"unit": datainfo.sample.orientation_unit})
635        if written == True:
636            sample.appendChild(ori)
637       
638        for item in datainfo.sample.details:
639            write_node(doc, sample, "details", item)
640       
641        # Instrument info
642        instr = doc.createElement("SASinstrument")
643        entry_node.appendChild(instr)
644       
645        write_node(doc, instr, "name", datainfo.instrument)
646       
647        #   Source
648        source = doc.createElement("SASsource")
649        if datainfo.source.name is not None:
650            source.setAttribute("name", str(datainfo.source.name))
651        instr.appendChild(source)
652        if datainfo.source.radiation == None or datainfo.source.radiation == '':
653            datainfo.source.radiation = "neutron"
654        write_node(doc, source, "radiation", datainfo.source.radiation)
655       
656        size = doc.createElement("beam_size")
657        if datainfo.source.beam_size_name is not None:
658            size.setAttribute("name", str(datainfo.source.beam_size_name))
659        written = write_node(doc, size, "x", datainfo.source.beam_size.x,
660                             {"unit": datainfo.source.beam_size_unit})
661        written = written | write_node(doc, size, "y",
662                                       datainfo.source.beam_size.y,
663                                       {"unit": datainfo.source.beam_size_unit})
664        written = written | write_node(doc, size, "z",
665                                       datainfo.source.beam_size.z,
666                                       {"unit": datainfo.source.beam_size_unit})
667        if written == True:
668            source.appendChild(size)
669           
670        write_node(doc, source, "beam_shape", datainfo.source.beam_shape)
671        write_node(doc, source, "wavelength",
672                   datainfo.source.wavelength,
673                   {"unit": datainfo.source.wavelength_unit})
674        write_node(doc, source, "wavelength_min",
675                   datainfo.source.wavelength_min,
676                   {"unit": datainfo.source.wavelength_min_unit})
677        write_node(doc, source, "wavelength_max",
678                   datainfo.source.wavelength_max,
679                   {"unit": datainfo.source.wavelength_max_unit})
680        write_node(doc, source, "wavelength_spread",
681                   datainfo.source.wavelength_spread,
682                   {"unit": datainfo.source.wavelength_spread_unit})
683       
684        #   Collimation
685        if datainfo.collimation == [] or datainfo.collimation == None:
686            coll = Collimation()
687            datainfo.collimation.append(coll)
688        for item in datainfo.collimation:
689            coll = doc.createElement("SAScollimation")
690            if item.name is not None:
691                coll.setAttribute("name", str(item.name))
692            instr.appendChild(coll)
693           
694            write_node(doc, coll, "length", item.length,
695                       {"unit": item.length_unit})
696           
697            for apert in item.aperture:
698                ap = doc.createElement("aperture")
699                if apert.name is not None:
700                    ap.setAttribute("name", str(apert.name))
701                if apert.type is not None:
702                    ap.setAttribute("type", str(apert.type))
703                coll.appendChild(ap)
704               
705                size = doc.createElement("size")
706                if apert.size_name is not None:
707                    size.setAttribute("name", str(apert.size_name))
708                written = write_node(doc, size, "x", apert.size.x,
709                                     {"unit": apert.size_unit})
710                written = written | write_node(doc, size, "y", apert.size.y,
711                                               {"unit": apert.size_unit})
712                written = written | write_node(doc, size, "z", apert.size.z,
713                                               {"unit": apert.size_unit})
714                if written == True:
715                    ap.appendChild(size)
716               
717                write_node(doc, ap, "distance", apert.distance,
718                           {"unit": apert.distance_unit})
719
720        #   Detectors
721        if datainfo.detector == None or datainfo.detector == []:
722            det = Detector()
723            det.name = ""
724            datainfo.detector.append(det)
725               
726        for item in datainfo.detector:
727            det = doc.createElement("SASdetector")
728            written = write_node(doc, det, "name", item.name)
729            written = written | write_node(doc, det, "SDD", item.distance,
730                                           {"unit": item.distance_unit})
731            if written == True:
732                instr.appendChild(det)
733           
734            off = doc.createElement("offset")
735            written = write_node(doc, off, "x", item.offset.x,
736                                 {"unit": item.offset_unit})
737            written = written | write_node(doc, off, "y", item.offset.y,
738                                           {"unit": item.offset_unit})
739            written = written | write_node(doc, off, "z", item.offset.z,
740                                           {"unit": item.offset_unit})
741            if written == True:
742                det.appendChild(off)
743               
744            ori = doc.createElement("orientation")
745            written = write_node(doc, ori, "roll", item.orientation.x,
746                                 {"unit": item.orientation_unit})
747            written = written | write_node(doc, ori, "pitch",
748                                           item.orientation.y,
749                                           {"unit": item.orientation_unit})
750            written = written | write_node(doc, ori, "yaw",
751                                           item.orientation.z,
752                                           {"unit": item.orientation_unit})
753            if written == True:
754                det.appendChild(ori)
755           
756            center = doc.createElement("beam_center")
757            written = write_node(doc, center, "x", item.beam_center.x,
758                                 {"unit": item.beam_center_unit})
759            written = written | write_node(doc, center, "y",
760                                           item.beam_center.y,
761                                           {"unit": item.beam_center_unit})
762            written = written | write_node(doc, center, "z",
763                                           item.beam_center.z,
764                                           {"unit": item.beam_center_unit})
765            if written == True:
766                det.appendChild(center)
767               
768            pix = doc.createElement("pixel_size")
769            written = write_node(doc, pix, "x", item.pixel_size.x,
770                                 {"unit": item.pixel_size_unit})
771            written = written | write_node(doc, pix, "y", item.pixel_size.y,
772                                           {"unit": item.pixel_size_unit})
773            written = written | write_node(doc, pix, "z", item.pixel_size.z,
774                                           {"unit": item.pixel_size_unit})
775            if written == True:
776                det.appendChild(pix)
777            written = written | write_node(doc, det, "slit_length",
778                                           item.slit_length,
779                                           {"unit": item.slit_length_unit})
780           
781        # Processes info
782        for item in datainfo.process:
783            node = doc.createElement("SASprocess")
784            entry_node.appendChild(node)
785
786            write_node(doc, node, "name", item.name)
787            write_node(doc, node, "date", item.date)
788            write_node(doc, node, "description", item.description)
789            for term in item.term:
790                value = term['value']
791                del term['value']
792                write_node(doc, node, "term", value, term)
793            for note in item.notes:
794                write_node(doc, node, "SASprocessnote", note)
795            if len(item.notes) == 0:
796                write_node(doc, node, "SASprocessnote", "")
797               
798        # Note info
799        if len(datainfo.notes) == 0:
800            node = doc.createElement("SASnote")
801            entry_node.appendChild(node)
802            if node.hasChildNodes():
803                for child in node.childNodes:
804                    node.removeChild(child)
805        else:
806            for item in datainfo.notes:
807                node = doc.createElement("SASnote")
808                entry_node.appendChild(node)
809                node.appendChild(doc.createTextNode(item))
810               
811        # Return the document, and the SASentry node associated with
812        # the data we just wrote
813        return doc, entry_node
814           
815    def write(self, filename, datainfo):
816        """
817        Write the content of a Data1D as a CanSAS XML file
818       
819        :param filename: name of the file to write
820        :param datainfo: Data1D object
821        """
822        # Create XML document
823        doc, _ = self._to_xml_doc(datainfo)
824        # Write the file
825        fd = open(filename, 'w')
826        fd.write(doc.toprettyxml())
827        fd.close()
828   
829    ## Once I convert the writer to lxml from minidom
830    ## This can be moved into xml_reader
831    def setProcessingInstructions(self, minidomObject, dic):
832        xmlroot = minidomObject.firstChild
833        for item in dic:
834            pi = minidomObject.createProcessingInstruction(item, dic[item])
835            minidomObject.insertBefore(pi, xmlroot)
836        return minidomObject
837   
Note: See TracBrowser for help on using the repository browser.