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

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 ea67541 was ea67541, checked in by Jeff Krzywon <jeffery.krzywon@…>, 10 years ago

Fix for ticket #204 - processing instructions were not being set properly when saving as cansas data file.

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