Changeset 3241dd2 in sasview for src/sans/dataloader/readers/cansas_reader.py
- Timestamp:
- Sep 25, 2014 10:33:41 AM (10 years ago)
- Branches:
- master, ESS_GUI, ESS_GUI_Docs, ESS_GUI_batch_fitting, ESS_GUI_bumps_abstraction, ESS_GUI_iss1116, ESS_GUI_iss879, ESS_GUI_iss959, ESS_GUI_opencl, ESS_GUI_ordering, ESS_GUI_sync_sascalc, costrafo411, magnetic_scatt, release-4.1.1, release-4.1.2, release-4.2.2, release_4.0.1, ticket-1009, ticket-1094-headless, ticket-1242-2d-resolution, ticket-1243, ticket-1249, ticket885, unittest-saveload
- Children:
- a91bc5d
- Parents:
- 72cdbec
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/sans/dataloader/readers/cansas_reader.py
r51af54b r3241dd2 35 35 PREPROCESS = "xmlpreprocess" 36 36 ENCODING = "encoding" 37 RUN_NAME_DEFAULT = "None" 37 38 HAS_CONVERTER = True 38 39 try: 39 40 from sans.data_util.nxsunit import Converter 40 except :41 except ImportError: 41 42 HAS_CONVERTER = False 42 43 … … 74 75 # Called by outside packages: 75 76 # sans.perspectives.fitting.pagestate 76 def write_node(doc, parent, name, value, attr= {}):77 def write_node(doc, parent, name, value, attr=None): 77 78 """ 78 79 :param doc: document DOM … … 84 85 :return: True if something was appended, otherwise False 85 86 """ 87 if attr is None: 88 attr = {} 86 89 if value is not None: 87 90 node = doc.createElement(name) … … 103 106 ##CanSAS version - defaults to version 1.0 104 107 cansas_version = "1.0" 108 base_ns = "{cansas1d/1.0}" 105 109 106 110 logging = [] … … 116 120 allow_all = True 117 121 122 118 123 def __init__(self): 119 124 ## List of errors 120 125 self.errors = [] 126 self.encoding = None 127 121 128 122 129 def is_cansas(self, ext="xml"): 123 130 """ 124 131 Checks to see if the xml file is a CanSAS file 132 133 :param ext: The file extension of the data file 125 134 """ 126 135 if self.validate_xml(): … … 134 143 return False 135 144 136 def read(self, xml): 137 """ 138 Validate and read in an xml file in the canSAS format. 139 140 :param xml: A canSAS file path in proper XML format 141 """ 142 # X - Q value; Y - Intensity (Abs) 143 x_vals = numpy.empty(0) 144 y_vals = numpy.empty(0) 145 dx_vals = numpy.empty(0) 146 dy_vals = numpy.empty(0) 147 dxl = numpy.empty(0) 148 dxw = numpy.empty(0) 145 146 def load_file_and_schema(self, xml_file): 147 """ 148 Loads the file and associates a schema, if a known schema exists 149 150 :param xml_file: The xml file path sent to Reader.read 151 """ 152 base_name = xml_reader.__file__ 153 base_name = base_name.replace("\\","/") 154 base = base_name.split("/sans/")[0] 155 156 # Load in xml file and get the cansas version from the header 157 self.set_xml_file(xml_file) 158 self.cansas_version = self.xmlroot.get("version", "1.0") 159 160 # Generic values for the cansas file based on the version 161 cansas_defaults = CANSAS_NS.get(self.cansas_version, "1.0") 162 schema_path = "{0}/sans/dataloader/readers/schema/{1}".format\ 163 (base, cansas_defaults.get("schema")).replace("\\", "/") 164 165 # Link a schema to the XML file. 166 self.set_schema(schema_path) 167 return cansas_defaults 168 169 170 def read(self, xml_file): 171 """ 172 Validate and read in an xml_file file in the canSAS format. 173 174 :param xml_file: A canSAS file path in proper XML format 175 """ 149 176 150 177 # output - Final list of Data1D objects 151 178 output = [] 152 # ns - Namespace hierarchy for current xml object179 # ns - Namespace hierarchy for current xml_file object 153 180 ns_list = [] 154 181 155 182 # Check that the file exists 156 if os.path.isfile(xml ):157 basename = os.path.basename(xml )183 if os.path.isfile(xml_file): 184 basename = os.path.basename(xml_file) 158 185 _, extension = os.path.splitext(basename) 159 186 # If the file type is not allowed, return nothing 160 187 if extension in self.ext or self.allow_all: 161 188 # Get the file location of 162 base_name = xml_reader.__file__ 163 base_name = base_name.replace("\\","/") 164 base = base_name.split("/sans/")[0] 165 166 # Load in xml file and get the cansas version from the header 167 self.set_xml_file(xml) 168 root = self.xmlroot 169 if root is None: 170 root = {} 171 self.cansas_version = root.get("version", "1.0") 172 173 # Generic values for the cansas file based on the version 174 cansas_defaults = CANSAS_NS.get(self.cansas_version, "1.0") 175 schema_path = "{0}/sans/dataloader/readers/schema/{1}".format\ 176 (base, cansas_defaults.get("schema")).replace("\\", "/") 177 178 # Link a schema to the XML file. 179 self.set_schema(schema_path) 189 cansas_defaults = self.load_file_and_schema(xml_file) 180 190 181 191 # Try to load the file, but raise an error if unable to. … … 184 194 if self.is_cansas(extension): 185 195 # Get each SASentry from XML file and add it to a list. 186 entry_list = root.xpath('/ns:SASroot/ns:SASentry', 187 namespaces={'ns': cansas_defaults.get("ns")}) 196 entry_list = self.xmlroot.xpath( 197 '/ns:SASroot/ns:SASentry', 198 namespaces = {'ns': cansas_defaults.get("ns")}) 188 199 ns_list.append("SASentry") 189 200 190 201 # If multiple files, modify the name for each is unique 191 multiple_files = len(entry_list) - 1192 202 increment = 0 193 name = basename194 203 # Parse each SASentry item 195 204 for entry in entry_list: 196 205 # Define a new Data1D object with zeroes for 197 206 # x_vals and y_vals 198 data1d = Data1D(x_vals, y_vals, dx_vals, dy_vals) 199 data1d.dxl = dxl 200 data1d.dxw = dxw 207 data1d = Data1D(numpy.empty(0), numpy.empty(0), \ 208 numpy.empty(0), numpy.empty(0)) 209 data1d.dxl = numpy.empty(0) 210 data1d.dxw = numpy.empty(0) 201 211 202 212 # If more than one SASentry, increment each in order 203 if multiple_files: 213 name = basename 214 if len(entry_list) - 1 > 0: 204 215 name += "_{0}".format(increment) 205 216 increment += 1 … … 220 231 del extras[:] 221 232 222 # Final cleanup 223 # Remove empty nodes, verify array sizes are correct 224 for error in self.errors: 225 return_value.errors.append(error) 226 del self.errors[:] 227 numpy.trim_zeros(return_value.x) 228 numpy.trim_zeros(return_value.y) 229 numpy.trim_zeros(return_value.dy) 230 size_dx = return_value.dx.size 231 size_dxl = return_value.dxl.size 232 size_dxw = return_value.dxw.size 233 if size_dxl == 0 and size_dxw == 0: 234 return_value.dxl = None 235 return_value.dxw = None 236 numpy.trim_zeros(return_value.dx) 237 elif size_dx == 0: 238 return_value.dx = None 239 size_dx = size_dxl 240 numpy.trim_zeros(return_value.dxl) 241 numpy.trim_zeros(return_value.dxw) 233 return_value = self._final_cleanup(return_value) 242 234 output.append(return_value) 243 235 else: 244 value = self.find_invalid_xml()245 output.append("Invalid XML at: {0}".format(value))236 output.append("Invalid XML at: {0}".format(\ 237 self.find_invalid_xml())) 246 238 except: 247 239 # If the file does not match the schema, raise this error 248 raise RuntimeError, "%s cannot be read" % xml 240 raise RuntimeError, "%s cannot be read" % xml_file 249 241 return output 250 242 # Return a list of parsed entries that dataloader can manage 251 243 return None 244 245 246 def _final_cleanup(self, data1d): 247 """ 248 Final cleanup of the Data1D object to be sure it has all the 249 appropriate information needed for perspectives 250 251 :param data1d: Data1D object that has been populated 252 """ 253 # Final cleanup 254 # Remove empty nodes, verify array sizes are correct 255 for error in self.errors: 256 data1d.errors.append(error) 257 del self.errors[:] 258 numpy.trim_zeros(data1d.x) 259 numpy.trim_zeros(data1d.y) 260 numpy.trim_zeros(data1d.dy) 261 size_dx = data1d.dx.size 262 size_dxl = data1d.dxl.size 263 size_dxw = data1d.dxw.size 264 if size_dxl == 0 and size_dxw == 0: 265 data1d.dxl = None 266 data1d.dxw = None 267 numpy.trim_zeros(data1d.dx) 268 elif size_dx == 0: 269 data1d.dx = None 270 size_dx = size_dxl 271 numpy.trim_zeros(data1d.dxl) 272 numpy.trim_zeros(data1d.dxw) 273 return data1d 274 252 275 253 276 def _create_unique_key(self, dictionary, name, numb = 0): … … 268 291 269 292 270 def _unit_conversion(self, n ew_current_level, attr, data1d, \271 tagname, node_value, optional = True):293 def _unit_conversion(self, node, new_current_level, data1d, \ 294 tagname, node_value): 272 295 """ 273 296 A unit converter method used to convert the data included in the file … … 279 302 :param data1d: Where the values will be saved 280 303 :param node_value: The value of the current dom node 281 :param optional: Boolean that says if the units are required282 """304 """ 305 attr = node.attrib 283 306 value_unit = '' 284 307 if 'unit' in attr and new_current_level.get('unit') is not None: … … 291 314 local_unit = attr['unit'] 292 315 if local_unit.lower() != default_unit.lower() and \ 293 local_unit is not None and local_unit.lower() != "none" and\294 default_unit is not None:316 local_unit is not None and local_unit.lower() != "none" \ 317 and default_unit is not None: 295 318 if HAS_CONVERTER == True: 296 319 try: … … 299 322 data_conv_q = Converter(attr['unit']) 300 323 value_unit = default_unit 301 exec "node_value = data_conv_q(node_value, units=data1d.{0})".format(unitname) 302 except KeyError as e: 324 i_string = "node_value = data_conv_q" 325 i_string += "(node_value, units=data1d.{0})" 326 exec i_string.format(unitname) 327 except KeyError: 303 328 err_msg = "CanSAS reader: could not convert " 304 err_msg += "{0} unit {1}; ".format(tagname, local_unit) 305 intermediate = "err_msg += \"expecting [{1}] {2}\".format(data1d.{0}, sys.exc_info()[1])".format(unitname, "{0}", "{1}") 306 exec intermediate 329 err_msg += "{0} unit {1}; " 330 err_msg = err_msg.format(tagname, local_unit) 331 intermediate = "err_msg += " + \ 332 "\"expecting [{1}] {2}\"" + \ 333 ".format(data1d.{0}, " + \ 334 "sys.exc_info()[1])" 335 exec intermediate.format(unitname, "{0}", "{1}") 307 336 self.errors.append(err_msg) 308 337 raise ValueError(err_msg) 309 return310 338 except: 311 339 err_msg = \ … … 320 348 self.errors.append(err_msg) 321 349 raise ValueError, err_msg 322 return323 350 else: 324 351 value_unit = local_unit 325 352 except: 326 353 err_msg = "CanSAS reader: could not convert " 327 err_msg += "Q unit [%s]; " % attr['unit'], 328 exec "err_msg += \"expecting [%s]\n %s\" % (data1d.{0}, sys.exc_info()[1])".format(unitname) 354 err_msg += "Q unit [%s]; " % attr['unit'] 355 intermediate = "err_msg += \"expecting [%s]\n %s\" % " + \ 356 "(data1d.{0}, sys.exc_info()[1])" 357 exec intermediate.format(unitname) 329 358 self.errors.append(err_msg) 330 359 raise ValueError, err_msg 331 return332 360 elif 'unit' in attr: 333 361 value_unit = attr['unit'] … … 335 363 return node_value, value_unit 336 364 337 def _parse_entry(self, dom, names=["SASentry"], data1d=None, extras=[]): 338 """ 339 Parse a SASEntry - new recursive method for parsing the dom of 340 the CanSAS data format. This will allow multiple data files 341 and extra nodes to be read in simultaneously. 342 343 :param dom: dom object with a namespace base of names 344 :param names: A list of element names that lead up to the dom object 345 :param data1d: The data1d object that will be modified 346 :param extras: Any values that should go into meta_data when data1d 347 is not a Data1D object 348 """ 349 350 # A portion of every namespace entry 365 366 def _check_for_empty_data(self, data1d): 367 """ 368 Creates an empty data set if no data is passed to the reader 369 370 :param data1d: presumably a Data1D object 371 """ 351 372 if data1d == None: 373 self.errors = [] 352 374 x_vals = numpy.empty(0) 353 375 y_vals = numpy.empty(0) … … 359 381 data1d.dxl = dxl 360 382 data1d.dxw = dxw 383 return data1d 384 385 386 def _handle_special_cases(self, tagname, data1d, children): 387 """ 388 Handle cases where the data type in Data1D is a dictionary or list 389 390 :param tagname: XML tagname in use 391 :param data1d: The original Data1D object 392 :param children: Child nodes of node 393 :param node: existing node with tag name 'tagname' 394 """ 395 if tagname == "SASdetector": 396 data1d = Detector() 397 elif tagname == "SAScollimation": 398 data1d = Collimation() 399 elif tagname == "SAStransmission_spectrum": 400 data1d = TransmissionSpectrum() 401 elif tagname == "SASprocess": 402 data1d = Process() 403 for child in children: 404 if child.tag.replace(self.base_ns, "") == "term": 405 term_attr = {} 406 for attr in child.keys(): 407 term_attr[attr] = \ 408 ' '.join(child.get(attr).split()) 409 if child.text is not None: 410 term_attr['value'] = \ 411 ' '.join(child.text.split()) 412 data1d.term.append(term_attr) 413 elif tagname == "aperture": 414 data1d = Aperture() 415 if tagname == "Idata" and children is not None: 416 data1d = self._check_for_empty_resolution(data1d, children) 417 return data1d 418 419 420 def _check_for_empty_resolution(self, data1d, children): 421 """ 422 A method to check all resolution data sets are the same size as I and Q 423 """ 424 dql_exists = False 425 dqw_exists = False 426 dq_exists = False 427 di_exists = False 428 for child in children: 429 tag = child.tag.replace(self.base_ns, "") 430 if tag == "dQl": 431 dql_exists = True 432 if tag == "dQw": 433 dqw_exists = True 434 if tag == "Qdev": 435 dq_exists = True 436 if tag == "Idev": 437 di_exists = True 438 if dqw_exists and dql_exists == False: 439 data1d.dxl = numpy.append(data1d.dxl, 0.0) 440 elif dql_exists and dqw_exists == False: 441 data1d.dxw = numpy.append(data1d.dxw, 0.0) 442 elif dql_exists == False and dqw_exists == False \ 443 and dq_exists == False: 444 data1d.dx = numpy.append(data1d.dx, 0.0) 445 if di_exists == False: 446 data1d.dy = numpy.append(data1d.dy, 0.0) 447 return data1d 448 449 450 def _restore_original_case(self, 451 tagname_original, 452 tagname, 453 save_data1d, 454 data1d): 455 """ 456 Save the special case data to the appropriate location and restore 457 the original Data1D object 458 459 :param tagname_original: Unmodified tagname for the node 460 :param tagname: modified tagname for the node 461 :param save_data1d: The original Data1D object 462 :param data1d: If a special case was handled, an object of that type 463 """ 464 if tagname_original == "SASdetector": 465 save_data1d.detector.append(data1d) 466 elif tagname_original == "SAScollimation": 467 save_data1d.collimation.append(data1d) 468 elif tagname == "SAStransmission_spectrum": 469 save_data1d.trans_spectrum.append(data1d) 470 elif tagname_original == "SASprocess": 471 save_data1d.process.append(data1d) 472 elif tagname_original == "aperture": 473 save_data1d.aperture.append(data1d) 474 else: 475 save_data1d = data1d 476 return save_data1d 477 478 479 def _handle_attributes(self, node, data1d, cs_values, tagname): 480 """ 481 Process all of the attributes for a node 482 """ 483 attr = node.attrib 484 if attr is not None: 485 for key in node.keys(): 486 try: 487 node_value, unit = self._get_node_value(node, cs_values, \ 488 data1d, tagname) 489 cansas_attrib = \ 490 cs_values.current_level.get("attributes").get(key) 491 attrib_variable = cansas_attrib.get("variable") 492 if key == 'unit' and unit != '': 493 attrib_value = unit 494 else: 495 attrib_value = node.attrib[key] 496 store_attr = attrib_variable.format("data1d", \ 497 attrib_value, key) 498 exec store_attr 499 except AttributeError: 500 pass 501 return data1d 502 503 504 def _get_node_value(self, node, cs_values, data1d, tagname): 505 """ 506 Get the value of a node and any applicable units 507 508 :param node: The XML node to get the value of 509 :param cs_values: A CansasConstants.CurrentLevel object 510 :param attr: The node attributes 511 :param dataid: The working object to be modified 512 :param tagname: The tagname of the node 513 """ 514 #Get the text from the node and convert all whitespace to spaces 515 units = '' 516 node_value = node.text 517 if node_value == "": 518 node_value = None 519 if node_value is not None: 520 node_value = ' '.join(node_value.split()) 521 522 # If the value is a float, compile with units. 523 if cs_values.ns_datatype == "float": 524 # If an empty value is given, set as zero. 525 if node_value is None or node_value.isspace() \ 526 or node_value.lower() == "nan": 527 node_value = "0.0" 528 #Convert the value to the base units 529 node_value, units = self._unit_conversion(node, \ 530 cs_values.current_level, data1d, tagname, node_value) 531 532 # If the value is a timestamp, convert to a datetime object 533 elif cs_values.ns_datatype == "timestamp": 534 if node_value is None or node_value.isspace(): 535 pass 536 else: 537 try: 538 node_value = \ 539 datetime.datetime.fromtimestamp(node_value) 540 except ValueError: 541 node_value = None 542 return node_value, units 543 544 545 def _parse_entry(self, dom, names=None, data1d=None, extras=None): 546 """ 547 Parse a SASEntry - new recursive method for parsing the dom of 548 the CanSAS data format. This will allow multiple data files 549 and extra nodes to be read in simultaneously. 550 551 :param dom: dom object with a namespace base of names 552 :param names: A list of element names that lead up to the dom object 553 :param data1d: The data1d object that will be modified 554 :param extras: Any values that should go into meta_data when data1d 555 is not a Data1D object 556 """ 557 558 if extras is None: 559 extras = [] 560 if names is None or names == []: 561 names = ["SASentry"] 562 563 data1d = self._check_for_empty_data(data1d) 361 564 362 base_ns = "{0}{1}{2}".format("{", \565 self.base_ns = "{0}{1}{2}".format("{", \ 363 566 CANSAS_NS.get(self.cansas_version).get("ns"), "}") 364 unit = ''365 567 tagname = '' 366 568 tagname_original = '' … … 370 572 try: 371 573 # Get the element name and set the current names level 372 tagname = node.tag.replace( base_ns, "")574 tagname = node.tag.replace(self.base_ns, "") 373 575 tagname_original = tagname 374 576 if tagname == "fitting_plug_in" or tagname == "pr_inversion" or\ … … 376 578 continue 377 579 names.append(tagname) 378 attr = node.attrib379 580 children = node.getchildren() 380 581 if len(children) == 0: … … 383 584 384 585 # Look for special cases 385 if tagname == "SASdetector": 386 data1d = Detector() 387 elif tagname == "SAScollimation": 388 data1d = Collimation() 389 elif tagname == "SAStransmission_spectrum": 390 data1d = TransmissionSpectrum() 391 elif tagname == "SASprocess": 392 data1d = Process() 393 for child in node: 394 if child.tag.replace(base_ns, "") == "term": 395 term_attr = {} 396 for attr in child.keys(): 397 term_attr[attr] = \ 398 ' '.join(child.get(attr).split()) 399 if child.text is not None: 400 term_attr['value'] = \ 401 ' '.join(child.text.split()) 402 data1d.term.append(term_attr) 403 elif tagname == "aperture": 404 data1d = Aperture() 405 if tagname == "Idata" and children is not None: 406 dql = 0 407 dqw = 0 408 for child in children: 409 tag = child.tag.replace(base_ns, "") 410 if tag == "dQl": 411 dql = 1 412 if tag == "dQw": 413 dqw = 1 414 if dqw == 1 and dql == 0: 415 data1d.dxl = numpy.append(data1d.dxl, 0.0) 416 elif dql == 1 and dqw == 0: 417 data1d.dxw = numpy.append(data1d.dxw, 0.0) 418 586 data1d = self._handle_special_cases(tagname, data1d, children) 587 419 588 # Get where to store content 420 589 cs_values = CONSTANTS.iterate_namespace(names) … … 427 596 428 597 #Get the information from the node 429 node_value = node.text 430 if node_value == "": 431 node_value = None 432 if node_value is not None: 433 node_value = ' '.join(node_value.split()) 434 435 # If the value is a float, compile with units. 436 if cs_values.ns_datatype == "float": 437 # If an empty value is given, store as zero. 438 if node_value is None or node_value.isspace() \ 439 or node_value.lower() == "nan": 440 node_value = "0.0" 441 node_value, unit = self._unit_conversion(\ 442 cs_values.current_level, attr, data1d, \ 443 tagname, node_value, cs_values.ns_optional) 444 # If the value is a timestamp, convert to a datetime object 445 elif cs_values.ns_datatype == "timestamp": 446 if node_value is None or node_value.isspace(): 447 pass 448 else: 449 try: 450 node_value = \ 451 datetime.datetime.fromtimestamp(node_value) 452 except ValueError: 453 node_value = None 598 node_value, _ = self._get_node_value(node, cs_values, \ 599 data1d, tagname) 600 454 601 # If appending to a dictionary (meta_data | run_name) 455 602 # make sure the key is unique … … 481 628 exec store_me 482 629 # Get attributes and process them 483 if attr is not None: 484 for key in node.keys(): 485 try: 486 cansas_attrib = \ 487 cs_values.current_level.get("attributes").get(key) 488 attrib_variable = cansas_attrib.get("variable") 489 if key == 'unit' and unit != '': 490 attrib_value = unit 491 else: 492 attrib_value = node.attrib[key] 493 store_attr = attrib_variable.format("data1d", \ 494 attrib_value, key) 495 exec store_attr 496 except AttributeError as e: 497 pass 498 499 except TypeError as e: 630 data1d = self._handle_attributes(node, data1d, cs_values, \ 631 tagname) 632 633 except TypeError: 500 634 pass 501 except Exception as e :635 except Exception as excep: 502 636 exc_type, exc_obj, exc_tb = sys.exc_info() 503 637 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 504 print(e, exc_type, fname, exc_tb.tb_lineno, tagname, exc_obj) 638 print(excep, exc_type, fname, exc_tb.tb_lineno, \ 639 tagname, exc_obj) 505 640 finally: 506 641 # Save special cases in original data1d object 507 642 # then restore the data1d 508 if tagname_original == "SASdetector": 509 save_data1d.detector.append(data1d) 510 elif tagname_original == "SAScollimation": 511 save_data1d.collimation.append(data1d) 512 elif tagname == "SAStransmission_spectrum": 513 save_data1d.trans_spectrum.append(data1d) 514 elif tagname_original == "SASprocess": 515 save_data1d.process.append(data1d) 516 elif tagname_original == "aperture": 517 save_data1d.aperture.append(data1d) 518 else: 519 save_data1d = data1d 643 save_data1d = self._restore_original_case(tagname_original, \ 644 tagname, save_data1d, data1d) 520 645 if tagname_original == "fitting_plug_in" or \ 521 646 tagname_original == "invariant" or \ … … 529 654 530 655 531 def _to_xml_doc(self, datainfo): 532 """ 533 Create an XML document to contain the content of a Data1D 534 535 :param datainfo: Data1D object 536 """ 537 if not issubclass(datainfo.__class__, Data1D): 538 raise RuntimeError, "The cansas writer expects a Data1D instance" 539 540 # Get PIs and create root element 656 def _get_pi_string(self): 657 """ 658 Creates the processing instructions header for writing to file 659 """ 541 660 pis = self.return_processing_instructions() 542 661 if len(pis) > 0: 543 662 pi_tree = self.create_tree(pis[0]) 544 663 i = 1 545 for i in range(1, len(pis) - 1):664 for i in range(1, len(pis) - 1): 546 665 pi_tree = self.append(pis[i], pi_tree) 547 666 pi_string = self.to_string(pi_tree) 548 667 else: 549 668 pi_string = "" 550 551 # Define namespaces and create SASroot object 669 return pi_string 670 671 672 def _create_main_node(self): 673 """ 674 Creates the primary xml header used when writing to file 675 """ 552 676 xsi = "http://www.w3.org/2001/XMLSchema-instance" 553 677 version = self.cansas_version 554 n s = CANSAS_NS.get(version).get("ns")678 n_s = CANSAS_NS.get(version).get("ns") 555 679 if version == "1.1": 556 680 url = "http://www.cansas.org/formats/1.1/" 557 681 else: 558 682 url = "http://svn.smallangles.net/svn/canSAS/1dwg/trunk/" 559 schema_location = "{0} {1}cansas1d.xsd".format(n s, url)683 schema_location = "{0} {1}cansas1d.xsd".format(n_s, url) 560 684 attrib = {"{" + xsi + "}schemaLocation" : schema_location, 561 685 "version" : version} 562 nsmap = {'xsi' : xsi, None: n s}563 564 main_node = self.create_element("{" + n s + "}SASroot", \686 nsmap = {'xsi' : xsi, None: n_s} 687 688 main_node = self.create_element("{" + n_s + "}SASroot", \ 565 689 attrib = attrib, \ 566 690 nsmap = nsmap) 567 568 # Create ElementTree, append SASroot and apply processing instructions 569 base_string = pi_string + self.to_string(main_node) 570 base_element = self.create_element_from_string(base_string) 571 doc = self.create_tree(base_element) 572 573 # Create SASentry Element 574 entry_node = self.create_element("SASentry") 575 root = doc.getroot() 576 root.append(entry_node) 577 578 # Add Title to SASentry 579 self.write_node(entry_node, "Title", datainfo.title) 580 581 # Add Run to SASentry 691 return main_node 692 693 694 def _write_run_names(self, datainfo, entry_node): 695 """ 696 Writes the run names to the XML file 697 698 :param datainfo: The Data1D object the information is coming from 699 :param entry_node: lxml node ElementTree object to be appended to 700 """ 582 701 if datainfo.run == None or datainfo.run == []: 583 RUN_NAME_DEFAULT = "None"584 702 datainfo.run.append(RUN_NAME_DEFAULT) 585 703 datainfo.run_name[RUN_NAME_DEFAULT] = RUN_NAME_DEFAULT … … 590 708 runname = {'name': datainfo.run_name[item]} 591 709 self.write_node(entry_node, "Run", item, runname) 592 593 # Data info 710 711 712 def _write_data(self, datainfo, entry_node): 713 """ 714 Writes the I and Q data to the XML file 715 716 :param datainfo: The Data1D object the information is coming from 717 :param entry_node: lxml node ElementTree object to be appended to 718 """ 594 719 node = self.create_element("SASdata") 595 720 self.append(node, entry_node) 596 721 597 722 for i in range(len(datainfo.x)): 598 pt = self.create_element("Idata") 599 node.append(pt) 600 self.write_node(pt, "Q", datainfo.x[i], {'unit': datainfo.x_unit}) 723 point = self.create_element("Idata") 724 node.append(point) 725 self.write_node(point, "Q", datainfo.x[i], \ 726 {'unit': datainfo.x_unit}) 601 727 if len(datainfo.y) >= i: 602 self.write_node(p t, "I", datainfo.y[i],728 self.write_node(point, "I", datainfo.y[i], 603 729 {'unit': datainfo.y_unit}) 604 730 if datainfo.dy != None and len(datainfo.dy) > i: 605 self.write_node(p t, "Idev", datainfo.dy[i],731 self.write_node(point, "Idev", datainfo.dy[i], 606 732 {'unit': datainfo.y_unit}) 607 733 if datainfo.dx != None and len(datainfo.dx) > i: 608 self.write_node(p t, "Qdev", datainfo.dx[i],734 self.write_node(point, "Qdev", datainfo.dx[i], 609 735 {'unit': datainfo.x_unit}) 610 736 if datainfo.dxw != None and len(datainfo.dxw) > i: 611 self.write_node(p t, "dQw", datainfo.dxw[i],737 self.write_node(point, "dQw", datainfo.dxw[i], 612 738 {'unit': datainfo.x_unit}) 613 739 if datainfo.dxl != None and len(datainfo.dxl) > i: 614 self.write_node(p t, "dQl", datainfo.dxl[i],740 self.write_node(point, "dQl", datainfo.dxl[i], 615 741 {'unit': datainfo.x_unit}) 616 742 617 # Transmission Spectrum Info 743 744 745 def _write_trans_spectrum(self, datainfo, entry_node): 746 """ 747 Writes the transmission spectrum data to the XML file 748 749 :param datainfo: The Data1D object the information is coming from 750 :param entry_node: lxml node ElementTree object to be appended to 751 """ 618 752 for i in range(len(datainfo.trans_spectrum)): 619 753 spectrum = datainfo.trans_spectrum[i] … … 624 758 node.setAttribute("timestamp", spectrum.timestamp) 625 759 for i in range(len(spectrum.wavelength)): 626 p t = self.create_element("Tdata")627 node.append(p t)628 self.write_node(p t, "Lambda", spectrum.wavelength[i],760 point = self.create_element("Tdata") 761 node.append(point) 762 self.write_node(point, "Lambda", spectrum.wavelength[i], 629 763 {'unit': spectrum.wavelength_unit}) 630 self.write_node(p t, "T", spectrum.transmission[i],764 self.write_node(point, "T", spectrum.transmission[i], 631 765 {'unit': spectrum.transmission_unit}) 632 766 if spectrum.transmission_deviation != None \ 633 767 and len(spectrum.transmission_deviation) >= i: 634 self.write_node(p t, "Tdev", \768 self.write_node(point, "Tdev", \ 635 769 spectrum.transmission_deviation[i], \ 636 770 {'unit': spectrum.transmission_deviation_unit}) 637 638 # Sample info 771 772 773 def _write_sample_info(self, datainfo, entry_node): 774 """ 775 Writes the sample information to the XML file 776 777 :param datainfo: The Data1D object the information is coming from 778 :param entry_node: lxml node ElementTree object to be appended to 779 """ 639 780 sample = self.create_element("SASsample") 640 781 if datainfo.sample.name is not None: … … 681 822 for item in datainfo.sample.details: 682 823 self.write_node(sample, "details", item) 683 684 # Instrument info 824 825 826 def _write_instrument(self, datainfo, entry_node): 827 """ 828 Writes the instrumental information to the XML file 829 830 :param datainfo: The Data1D object the information is coming from 831 :param entry_node: lxml node ElementTree object to be appended to 832 """ 685 833 instr = self.create_element("SASinstrument") 686 834 self.append(instr, entry_node) 687 688 835 self.write_node(instr, "name", datainfo.instrument) 689 690 # Source 836 return instr 837 838 839 def _write_source(self, datainfo, instr): 840 """ 841 Writes the source information to the XML file 842 843 :param datainfo: The Data1D object the information is coming from 844 :param instr: instrument node to be appended to 845 """ 691 846 source = self.create_element("SASsource") 692 847 if datainfo.source.name is not None: … … 728 883 datainfo.source.wavelength_spread, 729 884 {"unit": datainfo.source.wavelength_spread_unit}) 730 731 # Collimation 885 886 887 def _write_collimation(self, datainfo, instr): 888 """ 889 Writes the collimation information to the XML file 890 891 :param datainfo: The Data1D object the information is coming from 892 :param instr: lxml node ElementTree object to be appended to 893 """ 732 894 if datainfo.collimation == [] or datainfo.collimation == None: 733 895 coll = Collimation() … … 742 904 {"unit": item.length_unit}) 743 905 744 for apert in item.aperture:745 ap = self.create_element("aperture")746 if apert .name is not None:747 self.write_attribute(ap , "name", str(apert.name))748 if apert .type is not None:749 self.write_attribute(ap , "type", str(apert.type))750 self.append(ap , coll)906 for aperture in item.aperture: 907 apert = self.create_element("aperture") 908 if aperture.name is not None: 909 self.write_attribute(apert, "name", str(aperture.name)) 910 if aperture.type is not None: 911 self.write_attribute(apert, "type", str(aperture.type)) 912 self.append(apert, coll) 751 913 752 914 size = self.create_element("size") 753 if apert .size_name is not None:915 if aperture.size_name is not None: 754 916 self.write_attribute(size, 755 917 "name", 756 str(apert .size_name))757 written = self.write_node(size, "x", apert .size.x,758 {"unit": apert .size_unit})759 written = written | self.write_node(size, "y", apert .size.y,760 {"unit": apert .size_unit})761 written = written | self.write_node(size, "z", apert .size.z,762 {"unit": apert .size_unit})918 str(aperture.size_name)) 919 written = self.write_node(size, "x", aperture.size.x, 920 {"unit": aperture.size_unit}) 921 written = written | self.write_node(size, "y", aperture.size.y, 922 {"unit": aperture.size_unit}) 923 written = written | self.write_node(size, "z", aperture.size.z, 924 {"unit": aperture.size_unit}) 763 925 if written == True: 764 self.append(size, ap) 765 766 self.write_node(ap, "distance", apert.distance, 767 {"unit": apert.distance_unit}) 768 769 # Detectors 926 self.append(size, apert) 927 928 self.write_node(apert, "distance", aperture.distance, 929 {"unit": aperture.distance_unit}) 930 931 932 def _write_detectors(self, datainfo, instr): 933 """ 934 Writes the detector information to the XML file 935 936 :param datainfo: The Data1D object the information is coming from 937 :param inst: lxml instrument node to be appended to 938 """ 770 939 if datainfo.detector == None or datainfo.detector == []: 771 940 det = Detector() … … 827 996 if written == True: 828 997 self.append(pix, det) 829 830 # Processes info 998 999 1000 def _write_process_notes(self, datainfo, entry_node): 1001 """ 1002 Writes the process notes to the XML file 1003 1004 :param datainfo: The Data1D object the information is coming from 1005 :param entry_node: lxml node ElementTree object to be appended to 1006 1007 """ 831 1008 for item in datainfo.process: 832 1009 node = self.create_element("SASprocess") … … 844 1021 if len(item.notes) == 0: 845 1022 self.write_node(node, "SASprocessnote", "") 846 847 # Note info 1023 1024 1025 def _write_notes(self, datainfo, entry_node): 1026 """ 1027 Writes the notes to the XML file and creates an empty note if none 1028 exist 1029 1030 :param datainfo: The Data1D object the information is coming from 1031 :param entry_node: lxml node ElementTree object to be appended to 1032 1033 """ 848 1034 if len(datainfo.notes) == 0: 849 1035 node = self.create_element("SASnote") … … 854 1040 self.write_text(node, item) 855 1041 self.append(node, entry_node) 856 857 858 # Return the document, and the SASentry node associated with 859 # the data we just wrote 860 # If the calling function was not the cansas reader, return a minidom 861 # object rather than an lxml object. 1042 1043 1044 def _check_origin(self, entry_node, doc): 1045 """ 1046 Return the document, and the SASentry node associated with 1047 the data we just wrote. 1048 If the calling function was not the cansas reader, return a minidom 1049 object rather than an lxml object. 1050 1051 :param entry_node: lxml node ElementTree object to be appended to 1052 :param doc: entire xml tree 1053 """ 862 1054 frm = inspect.stack()[1] 863 1055 mod_name = frm[1].replace("\\", "/").replace(".pyc", "") … … 866 1058 mod_name = mod[1] 867 1059 if mod_name != "dataloader/readers/cansas_reader": 868 string = self.to_string(doc, p p=False)1060 string = self.to_string(doc, pretty_print=False) 869 1061 doc = parseString(string) 870 1062 node_name = entry_node.tag 871 1063 node_list = doc.getElementsByTagName(node_name) 872 1064 entry_node = node_list.item(0) 1065 return entry_node 1066 1067 1068 def _to_xml_doc(self, datainfo): 1069 """ 1070 Create an XML document to contain the content of a Data1D 1071 1072 :param datainfo: Data1D object 1073 """ 1074 if not issubclass(datainfo.__class__, Data1D): 1075 raise RuntimeError, "The cansas writer expects a Data1D instance" 1076 1077 # Get PIs and create root element 1078 pi_string = self._get_pi_string() 1079 1080 # Define namespaces and create SASroot object 1081 main_node = self._create_main_node() 1082 1083 # Create ElementTree, append SASroot and apply processing instructions 1084 base_string = pi_string + self.to_string(main_node) 1085 base_element = self.create_element_from_string(base_string) 1086 doc = self.create_tree(base_element) 1087 1088 # Create SASentry Element 1089 entry_node = self.create_element("SASentry") 1090 root = doc.getroot() 1091 root.append(entry_node) 1092 1093 # Add Title to SASentry 1094 self.write_node(entry_node, "Title", datainfo.title) 1095 1096 # Add Run to SASentry 1097 self._write_run_names(datainfo, entry_node) 1098 1099 # Add Data info to SASEntry 1100 self._write_data(datainfo, entry_node) 1101 1102 # Transmission Spectrum Info 1103 self._write_trans_spectrum(datainfo, entry_node) 1104 1105 # Sample info 1106 self._write_sample_info(datainfo, entry_node) 1107 1108 # Instrument info 1109 instr = self._write_instrument(datainfo, entry_node) 1110 1111 # Source 1112 self._write_source(datainfo, instr) 1113 1114 # Collimation 1115 self._write_collimation(datainfo, instr) 1116 1117 # Detectors 1118 self._write_detectors(datainfo, instr) 873 1119 1120 # Processes info 1121 self._write_process_notes(datainfo, entry_node) 1122 1123 # Note info 1124 self._write_notes(datainfo, entry_node) 1125 1126 # Return the document, and the SASentry node associated with 1127 # the data we just wrote 1128 # If the calling function was not the cansas reader, return a minidom 1129 # object rather than an lxml object. 1130 entry_node = self._check_origin(entry_node, doc) 1131 874 1132 return doc, entry_node 875 1133 … … 901 1159 doc, _ = self._to_xml_doc(datainfo) 902 1160 # Write the file 903 f d= open(filename, 'w')1161 file_ref = open(filename, 'w') 904 1162 if self.encoding == None: 905 1163 self.encoding = "UTF-8" 906 doc.write(f d, encoding=self.encoding,1164 doc.write(file_ref, encoding=self.encoding, 907 1165 pretty_print=True, xml_declaration=True) 908 f d.close()1166 file_ref.close() 909 1167 910 1168 … … 950 1208 conv(value, units=local_unit)) 951 1209 except: 952 exc_type, exc_value, exc_traceback= sys.exc_info()1210 _, exc_value, _ = sys.exc_info() 953 1211 err_mess = "CanSAS reader: could not convert" 954 1212 err_mess += " %s unit [%s]; expecting [%s]\n %s" \
Note: See TracChangeset
for help on using the changeset viewer.