source: sasview/invariantview/perspectives/invariant/invariant_state.py @ 66141a0

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 66141a0 was f5bdb4a, checked in by Jae Cho <jhjcho@…>, 14 years ago

fixed html string format problem because of '%" symbol

  • Property mode set to 100644
File size: 32.7 KB
Line 
1"""
2"""
3
4#import time
5import os
6import sys
7import logging
8import copy
9import DataLoader
10#from xml.dom.minidom import parse
11from lxml import etree
12from DataLoader.readers.cansas_reader import Reader as CansasReader
13from DataLoader.readers.cansas_reader import get_content
14from sans.guiframe.utils import format_number
15from sans.guiframe.dataFitting import Theory1D
16from sans.guiframe.dataFitting import Data1D
17
18INVNODE_NAME = 'invariant'
19CANSAS_NS = "cansas1d/1.0"
20
21# default state
22list = {'file': 'None',
23        'compute_num':0,
24        'state_num':0,
25        'is_time_machine':False,
26        'background_tcl':0.0,
27        'scale_tcl':1.0,
28        'contrast_tcl':1.0,
29        'porod_constant_tcl':'',
30        'npts_low_tcl':10,
31        'npts_high_tcl':10,
32        'power_high_tcl':4.0,
33        'power_low_tcl': 4.0,
34        'enable_high_cbox':False,
35        'enable_low_cbox':False,
36        'guinier': True,
37        'power_law_high': False,
38        'power_law_low': False,
39        'fit_enable_high': False,
40        'fit_enable_low': False,
41        'fix_enable_high':True,
42        'fix_enable_low':True,
43        'volume_tcl':'',
44        'volume_err_tcl':'',
45        'surface_tcl':'',
46        'surface_err_tcl':''}
47# list of states: This list will be filled as panel
48# init and the number of states increases
49state_list = {}
50bookmark_list = {}
51# list of input parameters (will be filled up on panel init) used by __str__
52input_list = {'background_tcl':0,
53        'scale_tcl':0,
54        'contrast_tcl':0,
55        'porod_constant_tcl':'',
56        'npts_low_tcl':0,
57        'npts_high_tcl':0,
58        'power_high_tcl':0,
59        'power_low_tcl': 0} 
60# list of output parameters (order sensitive) used by __str__   
61output_list = [["qstar_low",      "Q* from low Q extrapolation [1/(cm*A)]"],
62               ["qstar_low_err",             "dQ* from low Q extrapolation"],
63               ["qstar_low_percent",  "Q* percent from low Q extrapolation"],
64               ["qstar",              "Q* from data [1/(cm*A)]"],
65               ["qstar_err",                                "dQ* from data"],
66               ["qstar_percent",                     "Q* percent from data"],
67               ["qstar_high",    "Q* from high Q extrapolation [1/(cm*A)]"],
68               ["qstar_high_err",           "dQ* from high Q extrapolation"],
69               ["qstar_high_percent", "Q* percent from low Q extrapolation"],
70               ["qstar_total",         "total Q* [1/(cm*A)]"],
71               ["qstar_total_err",                              "total dQ*"],
72               ["volume",                                 "volume fraction"],
73               ["volume_err",                       "volume fraction error"],
74               ["surface",                               "specific surface"],
75               ["surface_err",                     "specific surface error"]]
76
77   
78
79class InvariantState(object):
80    """
81    Class to hold the state information of the InversionControl panel.
82    """
83    def __init__(self):
84        """
85        Default values
86        """
87        # Input
88        self.file  = None
89        self.data = Data1D(x=[], y=[], dx=None, dy=None)
90        self.theory_lowQ =  Theory1D(x=[], y=[], dy=None)
91        self.theory_highQ = Theory1D(x=[], y=[], dy=None)
92        #self.is_time_machine = False
93        self.saved_state = list
94        self.state_list = state_list
95        self.bookmark_list = bookmark_list
96        self.input_list = input_list
97        self.output_list = output_list
98       
99        self.compute_num = 0
100        self.state_num = 0
101        self.timestamp = ('00:00:00', '00/00/0000')
102        self.container = None
103        #plot image
104        self.wximbmp = None
105        # report_html strings
106        import sans.perspectives.invariant as invariant
107        path = invariant.get_data_path(media='media')
108        path_report_html = os.path.join(path,"report_template.html")
109        html_template = open(path_report_html,"r")
110        self.template_str = html_template.read()
111        self.report_str = self.template_str
112        #self.report_str_save = None
113        html_template.close()
114       
115    def __str__(self):
116        """
117        Pretty print
118           
119        : return: string representing the state
120        """
121        # Input string
122        compute_num = self.saved_state['compute_num']
123        compute_state = self.state_list[str(compute_num)]
124        my_time, date = self.timestamp
125        file_name = self.file
126
127        state_num = int(self.saved_state['state_num'])
128        state = "\n[Invariant computation for %s: " % file_name
129        state += "performed at %s on %s] \n" % (my_time, date)
130        state += "State No.: %d \n" % state_num
131        state += "\n=== Inputs ===\n"
132       
133        # text ctl general inputs ( excluding extrapolation text ctl)
134        for key, value in self.input_list.iteritems(): 
135            if value == '':
136                continue
137            key_split = key.split('_') 
138            max_ind = len(key_split)-1
139            if key_split[max_ind] == 'tcl': 
140                name = ""
141                if key_split[1] == 'low' or key_split[1] == 'high':
142                    continue
143                for ind in range(0, max_ind):
144                    name += " %s" % key_split[ind]
145                state += "%s:   %s\n" % (name.lstrip(" "), value)
146               
147        # other input parameters       
148        extra_lo = compute_state['enable_low_cbox']
149        if compute_state['enable_low_cbox']:
150            if compute_state['guinier']:
151                extra_lo = 'Guinier'
152            else:
153                extra_lo = 'Power law'
154        extra_hi = compute_state['enable_high_cbox']
155        if compute_state['enable_high_cbox']:
156            extra_hi = 'Power law'
157        state += "\nExtrapolation:  High=%s; Low=%s\n" %(extra_hi, extra_lo)   
158        low_off = False
159        high_off = False
160        for key, value in self.input_list.iteritems(): 
161            key_split = key.split('_') 
162            max_ind = len(key_split) - 1   
163            if key_split[max_ind] == 'tcl': 
164                name = "" 
165                # check each buttons whether or not ON or OFF
166                if key_split[1] == 'low' or key_split[1] == 'high':
167                    if not compute_state['enable_low_cbox'] and \
168                        key_split[max_ind-1] == 'low':
169                        low_off = True
170                        continue             
171                    elif not compute_state['enable_high_cbox'] and \
172                        key_split[max_ind-1] == 'high':
173                        high_off = True
174                        continue
175                    elif extra_lo == 'Guinier' and key_split[0] == 'power' and \
176                        key_split[max_ind-1] == 'low':
177                        continue
178                    for ind in range(0, max_ind):
179                        name += " %s" % key_split[ind]
180                    name = name.lstrip(" ")
181                    if name == "power low" :
182                        if compute_state['fix_enable_low']:
183                            name += ' (Fixed)'
184                        else:
185                            name += ' (Fitted)'
186                    if name == "power high" :
187                        if compute_state['fix_enable_high']:
188                            name += ' (Fixed)'
189                        else:
190                            name += ' (Fitted)'
191                    state += "%s:   %s\n" % (name, value)
192        # Outputs
193        state += "\n=== Outputs ==="
194        for item in output_list:
195            item_split = item[0].split('_') 
196            # Exclude the extrapolation that turned off
197            if len(item_split) > 1:
198                if low_off and item_split[1] == 'low':
199                    continue
200                if high_off and item_split[1] == 'high':
201                    continue
202            max_ind = len(item_split) - 1
203            value = None
204            try:
205                # Q* outputs
206                exec "value = self.container.%s\n" % item[0]
207            except:
208                # other outputs than Q*
209                name = item[0] + "_tcl"
210                if name in self.saved_state.keys():
211                    exec "value = self.saved_state['%s']" % name
212               
213            # Exclude the outputs w/''   
214            if value == '':
215                continue   
216            # Error outputs
217            if item_split[max_ind] == 'err':
218                state += "+- %s " % format_number(value)
219            # Percentage outputs
220            elif item_split[max_ind] == 'percent':
221                try:
222                    value = float(value) * 100
223                except:
224                    pass
225                state += "(%s %s)" % (format_number(value), '%')
226            # Outputs
227            else:
228                state += "\n%s:   %s " % (item[1], 
229                                        format_number(value, high=True))
230        # Include warning msg
231        if self.container is not None:
232            state += "\n\nNote:\n" + self.container.warning_msg
233        return state
234
235    def clone_state(self):
236        """
237        deepcopy the state
238        """
239        return copy.deepcopy(self.saved_state)
240
241    def toXML(self, file="inv_state.inv", doc=None, entry_node=None):
242        """
243        Writes the state of the InversionControl panel to file, as XML.
244       
245        Compatible with standalone writing, or appending to an
246        already existing XML document. In that case, the XML document
247        is required. An optional entry node in the XML document
248        may also be given.
249       
250        : param file: file to write to
251        : param doc: XML document object [optional]
252        : param entry_node: XML node within the XML document at which
253            we will append the data [optional]   
254        """
255        from xml.dom.minidom import getDOMImplementation
256        import time
257        timestamp = time.time()
258        # Check whether we have to write a standalone XML file
259        if doc is None:
260            impl = getDOMImplementation()
261       
262            doc_type = impl.createDocumentType(INVNODE_NAME, "1.0", "1.0")     
263       
264            newdoc = impl.createDocument(None, INVNODE_NAME, doc_type)
265            top_element = newdoc.documentElement
266        else:
267            # We are appending to an existing document
268            newdoc = doc
269            top_element = newdoc.createElement(INVNODE_NAME)
270            if entry_node is None:
271                newdoc.documentElement.appendChild(top_element)
272            else:
273                entry_node.appendChild(top_element)
274           
275        attr = newdoc.createAttribute("version")
276        attr.nodeValue = '1.0'
277        top_element.setAttributeNode(attr)
278       
279        # File name
280        element = newdoc.createElement("filename")
281        if self.file != None and self.file != '':
282            element.appendChild(newdoc.createTextNode(str(self.file)))
283        else:
284            element.appendChild(newdoc.createTextNode(str(file)))
285        top_element.appendChild(element)
286       
287        element = newdoc.createElement("timestamp")
288        element.appendChild(newdoc.createTextNode(time.ctime(timestamp)))
289        attr = newdoc.createAttribute("epoch")
290        attr.nodeValue = str(timestamp)
291        element.setAttributeNode(attr)
292        top_element.appendChild(element)
293       
294        # Current state
295        state = newdoc.createElement("state")
296        top_element.appendChild(state)
297       
298        for name,value in self.saved_state.iteritems():
299            element = newdoc.createElement(str(name))
300            element.appendChild(newdoc.createTextNode(str(value)))
301            state.appendChild(element)
302             
303        # State history list
304        history = newdoc.createElement("history")
305        top_element.appendChild(history)
306       
307        for name, value in self.state_list.iteritems():
308            history_element = newdoc.createElement('state_' + str(name))
309            for state_name,state_value in value.iteritems():
310                state_element = newdoc.createElement(str(state_name))
311                child = newdoc.createTextNode(str(state_value))
312                state_element.appendChild(child)
313                history_element.appendChild(state_element)
314            #history_element.appendChild(state_list_element)
315            history.appendChild(history_element)
316
317        # Bookmarks  bookmark_list[self.bookmark_num] = [\
318        #my_time,date,state,comp_state]
319        bookmark = newdoc.createElement("bookmark")
320        top_element.appendChild(bookmark)
321        item_list = ['time','date','state','comp_state']
322        for name, value_list in self.bookmark_list.iteritems():
323            element = newdoc.createElement('mark_'+ str(name))
324            _,date,state,comp_state = value_list
325            time_element = newdoc.createElement('time')
326            time_element.appendChild(newdoc.createTextNode(str(value_list[0])))
327            date_element = newdoc.createElement('date')
328            date_element.appendChild(newdoc.createTextNode(str(value_list[1])))
329            state_list_element = newdoc.createElement('state')
330            comp_state_list_element = newdoc.createElement('comp_state')
331            for state_name, state_value in value_list[2].iteritems():
332                state_element = newdoc.createElement(str(state_name))
333                child = newdoc.createTextNode(str(state_value))
334                state_element.appendChild(child)
335                state_list_element.appendChild(state_element)
336            for comp_name, comp_value in value_list[3].iteritems():
337                comp_element = newdoc.createElement(str(comp_name))
338                comp_element.appendChild(newdoc.createTextNode(str(comp_value)))
339                comp_state_list_element.appendChild(comp_element)
340            element.appendChild(time_element)
341            element.appendChild(date_element)
342            element.appendChild(state_list_element)
343            element.appendChild(comp_state_list_element)
344            bookmark.appendChild(element)
345
346        # Save the file
347        if doc is None:
348            fd = open('test000', 'w')
349            fd.write(newdoc.toprettyxml())
350            fd.close()
351            return None
352        else:
353            return newdoc.toprettyxml()
354       
355    def fromXML(self, file=None, node=None):
356        """
357        Load invariant states from a file
358           
359        : param file: .inv file
360        : param node: node of a XML document to read from       
361        """
362        if file is not None:
363            msg = "InvariantSate no longer supports non-CanSAS"
364            msg += " format for invariant files"
365            raise RuntimeError, msg
366       
367        if node.get('version')\
368            and node.get('version') == '1.0':
369
370            # Get file name
371            entry = get_content('ns:filename', node)
372            if entry is not None:
373                file_name = entry.text.strip()
374
375            # Get time stamp
376            entry = get_content('ns:timestamp', node)
377            if entry is not None and entry.get('epoch'):
378                try:
379                    timestamp = (entry.get('epoch'))
380                except:
381                    msg = "InvariantSate.fromXML: Could not read"
382                    msg += " timestamp\n %s" % sys.exc_value
383                    logging.error(msg)
384           
385            # Parse bookmarks
386            entry_bookmark = get_content('ns:bookmark', node)
387
388            for ind in range(1, len(entry_bookmark) + 1):
389                temp_state = {}
390                temp_bookmark = {}
391                entry = get_content('ns:mark_%s' % ind, entry_bookmark) 
392                               
393                if entry is not None:
394                    my_time = get_content('ns:time', entry)
395                    val_time = str(my_time.text.strip())
396                    date = get_content('ns:date', entry)
397                    val_date = str(date.text.strip())
398                    state_entry = get_content('ns:state', entry)
399                    for item in list:
400
401                        input_field = get_content('ns:%s' % item, state_entry)
402                        val = str(input_field.text.strip())
403                        if input_field is not None:
404                            try:
405                                exec "temp_state['%s'] = %s" % (item, val)     
406                            except:
407                                exec "temp_state['%s'] = '%s'" % (item, val)
408                    comp_entry = get_content('ns:comp_state', entry)
409                   
410                    for item in list:
411                        input_field = get_content('ns:%s' % item, comp_entry)
412                        val = str(input_field.text.strip())
413                        if input_field is not None:
414                            try:
415                                exec "temp_bookmark['%s'] = %s" % (item, val)     
416                            except:
417                                exec "temp_bookmark['%s'] = '%s'" % (item, val)
418                    try:
419                        cmd = "self.bookmark_list[%s] = [val_time,"
420                        cmd += "val_date,temp_state,temp_bookmark]"
421                        exec cmd % ind
422                    except:
423                        raise "missing components of bookmarks..."
424            # Parse histories
425            entry_history = get_content('ns:history', node)
426
427            for ind in range(0, len(entry_history)):
428                temp_state = {}
429                entry = get_content('ns:state_%s' % ind, entry_history) 
430
431                if entry is not None:
432                    for item in list:
433                        input_field = get_content('ns:%s' % item, entry )
434                        val = str(input_field.text.strip())
435
436                        if input_field is not None:
437                            try:
438                                exec "temp_state['%s'] = %s" % (item, val)         
439                            except:
440                                exec "temp_state['%s'] = '%s'" % (item, val)
441                            finally:
442                                exec "self.state_list['%s'] = temp_state" % ind
443            # Parse current state (ie, saved_state)
444            entry = get_content('ns:state', node)           
445            if entry is not None:
446                for item in list:
447                    input_field = get_content('ns:%s' % item, entry)
448                    val = str(input_field.text.strip())
449                    if input_field is not None:
450                        self.set_saved_state(name=item, value=val)
451            self.file = file_name
452           
453    def set_report_string(self):
454        """
455        Get the values (strings) from __str__ for report
456        """
457        strings = self.__str__()
458   
459        # default string values
460        for num in range (1, 19):
461            exec "s_%s = 'NA'" % str(num)
462        lines = strings.split('\n')
463        # get all string values from __str__()
464        for line in range(0, len(lines)):
465            if line == 1:
466                s_1 = lines[1]
467            elif line == 2:
468                s_2 = lines[2]
469            else:
470                item = lines[line].split(':')
471                item[0] = item[0].strip()
472                if item[0] == "scale":
473                    s_3 = item[1]
474                elif item[0] == "porod constant":
475                    s_4 = item[1]
476                elif item[0] == "background":
477                    s_5 = item[1]
478                elif item[0] == "contrast":
479                    s_6 = item[1]
480                elif item[0] == "Extrapolation":
481                    extra = item[1].split(";")
482                    bool_0 = extra[0].split("=")
483                    bool_1 = extra[1].split("=")
484                    s_8 = " " + bool_0[0] + "Q region = " + bool_0[1]
485                    s_7 = " " + bool_1[0] + "Q region = " + bool_1[1]
486                elif item[0] == "npts low":
487                    s_9 = item[1]
488                elif item[0] == "npts high":
489                    s_10 = item[1]
490                elif item[0] == "volume fraction":
491                    val=item[1].split("+-")[0].strip()
492                    error = item[1].split("+-")[1].strip()
493                    s_17 = val + " &plusmn; " + error
494                elif item[0] == "specific surface":
495                    val = item[1].split("+-")[0].strip()
496                    error = item[1].split("+-")[1].strip()
497                    s_18 = val + " &plusmn; " + error
498                elif item[0].split("(")[0].strip() == "power low":
499                    s_11 = item[0]+" ="+item[1]
500                elif item[0].split("(")[0].strip() == "power high":
501                    s_12 = item[0]+" ="+item[1]
502                elif item[0].split("[")[0].strip() == "Q* from low Q extrapolation":
503                    #looks messy but this way the symbols +_ and % work on html
504                    val = item[1].split("+-")[0].strip()
505                    error = item[1].split("+-")[1].strip()
506                    err = error.split("%")[0].strip()
507                    percent = error.split("%")[1].strip()
508                    s_13 = val + " &plusmn; " + err + "&#37" + percent
509                elif item[0].split("[")[0].strip() == "Q* from data":
510                    val = item[1].split("+-")[0].strip()
511                    error = item[1].split("+-")[1].strip()
512                    err = error.split("%")[0].strip()
513                    percent = error.split("%")[1].strip()
514                    s_14 = val + " &plusmn; " + err + "&#37" + percent
515                elif item[0].split("[")[0].strip() == "Q* from high Q extrapolation":
516                    val = item[1].split("+-")[0].strip()
517                    error = item[1].split("+-")[1].strip()
518                    err = error.split("%")[0].strip()
519                    percent = error.split("%")[1].strip()
520                    s_15 = val + " &plusmn; " + err + "&#37" + percent
521                elif item[0].split("[")[0].strip() == "total Q*":
522                    val = item[1].split("+-")[0].strip()
523                    error = item[1].split("+-")[1].strip()
524                    s_16 = val + " &plusmn; " + error
525                else:
526                    continue
527
528        s_1 = self._check_html_format(s_1)
529        file_name = self._check_html_format(self.file)
530
531        # make plot image
532        self.set_plot_state(extra_high=bool_0[1],extra_low=bool_1[1])
533        # get ready for report with setting all the html strings
534        self.report_str = str(self.template_str) % (s_1, s_2,
535                                        s_3, s_4, s_5, s_6, s_7, s_8,
536                                    s_9, s_10, s_11, s_12, s_13, s_14, s_15,
537                                        s_16, s_17, s_18, file_name, "%s")
538
539    def _check_html_format(self, name):
540        """
541        Check string '%' for html format
542        """
543        if name.count('%'):
544            name = name.replace('%', '&#37')
545       
546        return name
547       
548    def set_saved_state(self, name, value):
549        """
550        Set the state list
551                   
552        : param name: name of the state component
553        : param value: value of the state component
554        """
555        rb_list = [['power_law_low','guinier'],
556                   ['fit_enable_low','fix_enable_low'],
557                   ['fit_enable_high','fix_enable_high']]
558
559        try:
560            if value == None or value.lstrip().rstrip() == '':
561                exec "self.%s = '%s'" % (name, value)
562                exec "self.saved_state['%s'] = '%s'" % (name, value)
563            else:
564                exec 'self.%s = %s' % (name, value)
565                exec "self.saved_state['%s'] = %s" % (name, value)
566            # set the count part of radio button clicked
567            # False for the saved_state
568            for title, content in rb_list:
569                if name ==  title:
570                    name = content
571                    value = False     
572                elif name == content:
573                    name = title
574                    value = False 
575            exec "self.saved_state['%s'] = '%s'" % (name, value)     
576            self.state_num = self.saved_state['state_num']
577        except:           
578            pass
579
580    def set_plot_state(self, extra_high=False, extra_low=False):
581        """
582        Build image state that wx.html understand
583        by plotting, putting it into wx.FileSystem image object
584       
585        : extrap_high,extra_low: low/high extrapolations
586        are possible extra-plots
587        """
588        # some imports
589        import wx
590        import matplotlib.pyplot as plt
591        from matplotlib.backends.backend_agg import FigureCanvasAgg
592
593        #we use simple plot, not plotpanel
594        #make matlab figure
595        fig = plt.figure()
596        fig.set_facecolor('w')
597        graph = fig.add_subplot(111)
598
599        #data plot
600        graph.errorbar(self.data.x, self.data.y, yerr=self.data.dy, fmt='o')
601        #low Q extrapolation fit plot
602        if not extra_low == 'False':
603            graph.plot(self.theory_lowQ.x,self.theory_lowQ.y)
604        #high Q extrapolation fit plot
605        if not extra_high == 'False':
606            graph.plot(self.theory_highQ.x,self.theory_highQ.y)
607        graph.set_xscale("log", nonposx='clip')
608        graph.set_yscale("log", nonposy='clip')
609        graph.set_xlabel('$\\rm{Q}(\\AA^{-1})$', fontsize = 12)
610        graph.set_ylabel('$\\rm{Intensity}(cm^{-1})$', fontsize = 12)
611        canvas = FigureCanvasAgg(fig)
612        #actually make image
613        canvas.draw()
614       
615        #make python.Image object
616        #size
617        w, h = canvas.get_width_height()
618        #convert to wx.Image
619        wximg = wx.EmptyImage(w,h)
620        #wxim.SetData(img.convert('RGB').tostring() )
621        wximg.SetData(canvas.tostring_rgb()) 
622        #get the dynamic image for the htmlwindow
623        wximgbmp = wx.BitmapFromImage(wximg)
624        #store the image in wx.FileSystem Object
625        wx.FileSystem.AddHandler(wx.MemoryFSHandler())
626        # use wx.MemoryFSHandler
627        self.imgRAM = wx.MemoryFSHandler()
628        #AddFile, image can be retrieved with 'memory:filename'
629        self.imgRAM.AddFile('img_inv.png', wximgbmp, wx.BITMAP_TYPE_PNG)
630       
631        self.wximgbmp = 'memory:img_inv.png'
632        self.image = fig
633
634class Reader(CansasReader):
635    """
636    Class to load a .inv invariant file
637    """
638    ## File type
639    type_name = "Invariant"
640   
641    ## Wildcards
642    type = ["Invariant file (*.inv)|*.inv",
643            "SANSView file (*.svs)|*.svs"]
644    ## List of allowed extensions
645    ext = ['.inv', '.INV', '.svs', 'SVS'] 
646   
647    def __init__(self, call_back, cansas=True):
648        """
649        Initialize the call-back method to be called
650        after we load a file
651       
652        : param call_back: call-back method
653        : param cansas:  True = files will be written/read in CanSAS format
654                        False = write CanSAS format 
655        """
656        ## Call back method to be executed after a file is read
657        self.call_back = call_back
658        ## CanSAS format flag
659        self.cansas = cansas
660        self.state = None
661
662    def read(self, path):
663        """
664        Load a new invariant state from file
665       
666        : param path: file path
667        : return: None
668        """
669        if self.cansas == True:
670            return self._read_cansas(path)
671        else:
672            return self._read_standalone(path)
673       
674    def _read_standalone(self, path):
675        """
676        Load a new invariant state from file.
677        The invariant node is assumed to be the top element.
678       
679        : param path: file path
680        : return: None
681        """
682        # Read the new state from file
683        state = InvariantState()
684
685        state.fromXML(file=path)
686       
687        # Call back to post the new state
688        self.call_back(state)
689        return None
690   
691    def _parse_state(self, entry):
692        """
693        Read an invariant result from an XML node
694       
695        : param entry: XML node to read from
696        : return: InvariantState object
697        """
698        state = None
699        # Locate the invariant node
700        try:
701            nodes = entry.xpath('ns:%s' % INVNODE_NAME,
702                                namespaces={'ns': CANSAS_NS})
703            # Create an empty state
704            if nodes != []:
705                state = InvariantState()
706                state.fromXML(node=nodes[0])
707        except:
708            msg = "XML document does not contain invariant"
709            msg += " information.\n %s" % sys.exc_value
710            logging.info(msg) 
711        return state
712   
713    def _read_cansas(self, path):
714        """
715        Load data and invariant information from a CanSAS XML file.
716       
717        : param path: file path
718        : return: Data1D object if a single SASentry was found,
719                    or a list of Data1D objects if multiple entries were found,
720                    or None of nothing was found
721        : raise RuntimeError: when the file can't be opened
722        : raise ValueError: when the length of the data vectors are inconsistent
723        """
724        output = []
725        if os.path.isfile(path):
726            basename  = os.path.basename(path)
727            root, extension = os.path.splitext(basename)
728           
729            if  extension.lower() in self.ext or \
730                extension.lower() == '.xml':
731                tree = etree.parse(path, parser=etree.ETCompatXMLParser())
732       
733                # Check the format version number
734                # Specifying the namespace will take care of
735                # the file format version
736                root = tree.getroot()
737               
738                entry_list = root.xpath('/ns:SASroot/ns:SASentry',
739                                        namespaces={'ns': CANSAS_NS})
740               
741                for entry in entry_list:
742                   
743                    sas_entry = self._parse_entry(entry)
744                    invstate = self._parse_state(entry)
745                   
746                    #invstate could be None when .svs file is loaded
747                    #in this case, skip appending to output
748                    if invstate != None:
749                        sas_entry.meta_data['invstate'] = invstate
750                        sas_entry.filename = invstate.file
751                        output.append(sas_entry)
752        else:
753            raise RuntimeError, "%s is not a file" % path
754
755        # Return output consistent with the loader's api
756        if len(output) == 0:
757            return None
758        elif len(output) == 1:
759            # Call back to post the new state
760            self.state = output[0].meta_data['invstate']
761            self.call_back(state=output[0].meta_data['invstate'],
762                          datainfo = output[0])
763            return output[0]
764        else:
765            return output               
766   
767    def get_state(self):
768        return self.state
769   
770    def write(self, filename, datainfo=None, invstate=None):
771        """
772        Write the content of a Data1D as a CanSAS XML file
773       
774        : param filename: name of the file to write
775        : param datainfo: Data1D object
776        : param invstate: InvariantState object
777        """
778        # Sanity check
779        if self.cansas == True:
780            doc = self.write_toXML(datainfo,invstate)
781            # Write the XML document
782            fd = open(filename, 'w')
783            fd.write(doc.toprettyxml())
784            fd.close()
785        else:
786            invstate.toXML(file=filename)
787       
788    def write_toXML(self, datainfo=None, state=None):
789        """
790        Write toXML, a helper for write()
791       
792        : return: xml doc
793        """
794        if datainfo is None:
795            datainfo = DataLoader.data_info.Data1D(x=[], y=[])   
796        elif not issubclass(datainfo.__class__, DataLoader.data_info.Data1D):
797            msg = "The cansas writer expects a Data1D"
798            msg += " instance: %s" % str(datainfo.__class__.__name__)
799            raise RuntimeError, msg
800        #make sure title and data run is filled up.
801        if datainfo.title == None or datainfo.title == '':
802            datainfo.title = datainfo.name
803        if datainfo.run_name == None or datainfo.run_name == {}: 
804            datainfo.run = [str(datainfo.name)]
805            datainfo.run_name[0] = datainfo.name
806        # Create basic XML document
807        doc, sasentry = self._to_xml_doc(datainfo)
808        # Add the invariant information to the XML document
809        if state is not None:
810            state.toXML(datainfo.name,doc=doc, entry_node=sasentry)
811        return doc
812
Note: See TracBrowser for help on using the repository browser.