source: sasview/src/sas/sasgui/perspectives/corfunc/corfunc_state.py @ 463e7ffc

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.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 463e7ffc was 463e7ffc, checked in by Ricardo Ferraz Leal <ricleal@…>, 7 years ago

getLogger with module name

  • Property mode set to 100644
File size: 12.7 KB
Line 
1import time
2import sys
3import os
4import logging
5import sas.sascalc.dataloader
6from lxml import etree
7from sas.sascalc.dataloader.readers.cansas_reader import Reader as CansasReader
8from sas.sascalc.dataloader.readers.cansas_reader import get_content
9from sas.sasgui.guiframe.utils import format_number
10from sas.sasgui.guiframe.gui_style import GUIFRAME_ID
11from sas.sasgui.guiframe.dataFitting import Data1D
12from sas.sascalc.dataloader.data_info import Data1D as LoaderData1D
13from sas.sascalc.dataloader.loader import Loader
14
15logger = logging.getLogger(__name__)
16
17CORNODE_NAME = 'corfunc'
18CANSAS_NS = 'cansas1d/1.0'
19
20# The default state
21DEFAULT_STATE = {
22    'qmin_tcl': None,
23    'qmax1_tcl': None,
24    'qmax2_tcl': None,
25    'background_tcl': None
26}
27
28# List of output parameters, used by __str__
29output_list = [
30    ['max', "Long Period (A): "],
31    ['Lc', "Average Hard Block Thickness (A): "],
32    ['dtr', "Average Interface Thickness (A): "],
33    ['d0', "Average Core Thickness: "],
34    ['A', "PolyDispersity: "],
35    ['fill', "Filling Fraction: "]
36]
37
38class CorfuncState(object):
39    """
40    Stores information about the state of CorfuncPanel
41    """
42
43    def __init__(self):
44        # Inputs
45        self.file = None
46        self.data = None
47        self.qmin = None
48        self.qmax = [0, 0]
49        self.background = None
50        self.outputs = {}
51        self.is_extrapolated = False
52        self.transform_type = 'fourier'
53        self.is_transformed = False
54
55        self.saved_state = DEFAULT_STATE
56        self.timestamp = time.time()
57
58        # Raw Data
59        self.q = None
60        self.iq = None
61        # TODO: Add extrapolated data and transformed data (when implemented)
62
63    def __str__(self):
64        """
65        Pretty print the state
66
67        :return: A string representing the state
68        """
69        state = "File:         {}\n".format(self.file)
70        state += "Timestamp:    {}\n".format(self.timestamp)
71        state += "Qmin:         {}\n".format(str(self.qmin))
72        state += "Qmax:         {}\n".format(str(self.qmax))
73        state += "Background:   {}\n".format(str(self.background))
74
75        if self.outputs != {} and self.outputs is not None:
76            state += "\nOutputs:\n"
77            for key, value in self.outputs.iteritems():
78                name = output_list[key][1]
79                state += "{}: {}\n".format(name, str(value))
80
81        return state
82
83    def set_saved_state(self, name, value):
84        """
85        Set a value in the current state.
86
87        :param name: The name of the parameter to set
88        :param value: The value to set the parameter to
89        """
90        self.saved_state[name] = value
91        if name == 'qmin_tcl':
92            self.qmin = value
93        elif name == 'qmax1_tcl':
94            self.qmax[0] = value
95        elif name == 'qmax2_tcl':
96            self.qmax[1] = value
97        elif name == 'background_tcl':
98            self.background = value
99
100    def toXML(self, filename='corfunc_state.crf', doc=None, entry_node=None):
101        """
102        Writes the state of the CorfuncPanel panel to file, as XML.
103
104        Compatible with standalone writing, or appending to an
105        already existing XML document. In that case, the XML document
106        is required. An optional entry node in the XML document
107        may also be given.
108
109        :param file: file to write to
110        :param doc: XML document object [optional]
111        :param entry_node: XML node within the XML document at which
112            we will append the data [optional]
113
114        :return: None if no doc is provided, modified XML document if doc!=None
115        """
116        from xml.dom.minidom import getDOMImplementation
117
118        top_element = None
119        new_doc = None
120        if doc is None:
121            # Create a new XML document
122            impl = getDOMImplementation()
123            doc_type = impl.createDocumentType(CORNODE_NAME, "1.0", "1.0")
124            new_doc = impl.createDocument(None, CORNODE_NAME, doc_type)
125            top_element = new_doc.documentElement
126        else:
127            # Create a new element in the document provided
128            top_element = doc.createElement(CORNODE_NAME)
129            if entry_node is None:
130                doc.documentElement.appendChild(top_element)
131            else:
132                entry_node.appendChild(top_element)
133            new_doc = doc
134
135        # Version
136        attr = new_doc.createAttribute("version")
137        attr.nodeValue = '1.0'
138        top_element.setAttributeNode(attr)
139
140        # Filename
141        element = new_doc.createElement("filename")
142        if self.file is not None and self.file != '':
143            element.appendChild(new_doc.createTextNode(str(self.file)))
144        else:
145            element.appendChild(new_doc.createTextNode(str(filename)))
146        top_element.appendChild(element)
147
148        # Timestamp
149        element = new_doc.createElement("timestamp")
150        # Pretty printed format
151        element.appendChild(new_doc.createTextNode(time.ctime(self.timestamp)))
152        attr = new_doc.createAttribute("epoch")
153        # Epoch value (used in self.fromXML)
154        attr.nodeValue = str(self.timestamp)
155        element.setAttributeNode(attr)
156        top_element.appendChild(element)
157
158        # Current state
159        state = new_doc.createElement("state")
160        top_element.appendChild(state)
161        for name, value in self.saved_state.iteritems():
162            element = new_doc.createElement(name)
163            element.appendChild(new_doc.createTextNode(str(value)))
164            state.appendChild(element)
165
166        # Whether or not the extrapolate & transform buttons have been clicked
167        element = new_doc.createElement("is_extrapolated")
168        top_element.appendChild(element)
169        element.appendChild(new_doc.createTextNode(str(int(self.is_extrapolated))))
170
171        element = new_doc.createElement("is_transformed")
172        top_element.appendChild(element)
173        element.appendChild(new_doc.createTextNode(str(int(self.is_transformed))))
174
175        if self.is_transformed:
176            element = new_doc.createElement("transform_type")
177            top_element.appendChild(element)
178            element.appendChild(new_doc.createTextNode(self.transform_type))
179
180        # Output parameters
181        if self.outputs != {} and self.outputs is not None:
182            output = new_doc.createElement("output")
183            top_element.appendChild(output)
184            for key, value in self.outputs.iteritems():
185                element = new_doc.createElement(key)
186                element.appendChild(new_doc.createTextNode(str(value)))
187                output.appendChild(element)
188
189        # Save the file or return the original document with the state
190        # data appended
191        if doc is None:
192            fd = open(filename, 'w')
193            fd.write(new_doc.toprettyxml())
194            fd.close()
195            return None
196        else:
197            return new_doc
198
199
200    def fromXML(self, node):
201        """
202        Load corfunc states from a file
203
204        :param node: node of an XML document to read from (optional)
205        """
206        if node.get('version') and node.get('version') == '1.0':
207            # Parse filename
208            entry = get_content('ns:filename', node)
209            if entry is not None:
210                self.file = entry.text.strip()
211
212            # Parse timestamp
213            entry = get_content('ns:timestamp', node)
214            if entry is not None and entry.get('epoch'):
215                try:
216                    self.timestamp = (entry.get('epoch'))
217                except:
218                    msg = ("CorfuncState.fromXML: Could not read timestamp",
219                        "\n{}").format(sys.exc_value)
220                    logger.error(msg)
221
222            # Parse current state
223            entry = get_content('ns:state', node)
224            if entry is not None:
225                for item in DEFAULT_STATE.iterkeys():
226                    input_field = get_content("ns:{}".format(item), entry)
227                    if input_field is not None:
228                        try:
229                            value = float(input_field.text.strip())
230                        except:
231                            value = None
232                        self.set_saved_state(name=item, value=value)
233
234            # Parse is_extrapolated and is_transformed
235            entry = get_content('ns:is_extrapolated', node)
236            if entry is not None:
237                self.is_extrapolated = bool(int(entry.text.strip()))
238            entry = get_content('ns:is_transformed', node)
239            if entry is not None:
240                self.is_transformed = bool(int(entry.text.strip()))
241                entry = get_content('ns:transform_type', node)
242                self.transform_type = entry.text.strip()
243
244            # Parse outputs
245            entry = get_content('ns:output', node)
246            if entry is not None:
247                for item in output_list:
248                    parameter = get_content("ns:{}".format(item[0]), entry)
249                    if parameter is not None:
250                        self.outputs[item[0]] = float(parameter.text.strip())
251
252
253
254class Reader(CansasReader):
255    """
256    Reads a CanSAS file containing the state of a CorfuncPanel
257    """
258
259    type_name = "Corfunc"
260
261    type = ["Corfunc file (*.crf)|*.crf",
262            "SASView file (*.svs)|*.svs"]
263
264    ext = ['.crf', '.CRF', '.svs', '.SVS']
265
266    def __init__(self, callback):
267        self.callback = callback
268        self.state = None
269
270    def read(self, path):
271        """
272        Load data and corfunc information frmo a CanSAS file.
273
274        :param path: The file path to read from
275        :return: Data1D object, a list of Data1D objects, or None
276        :raise IOError: When the file can't be found
277        :raise IOError: When the file is an invalid file type
278        :raise ValueError: When the length of the data vectors are inconsistent
279        """
280        output = []
281        if os.path.isfile(path):
282            # Load file
283            basename = os.path.basename(path)
284            root, ext = os.path.splitext(basename)
285            if not ext.lower() in self.ext:
286                raise IOError, "{} is not a supported file type".format(ext)
287            tree = etree.parse(path, parser=etree.ETCompatXMLParser())
288            root = tree.getroot()
289            entry_list = root.xpath('/ns:SASroot/ns:SASentry',
290                namespaces={'ns': CANSAS_NS})
291            for entry in entry_list:
292                sas_entry, _ = self._parse_entry(entry)
293                corstate = self._parse_state(entry)
294
295                if corstate != None:
296                    sas_entry.meta_data['corstate'] = corstate
297                    sas_entry.filename = corstate.file
298                    output.append(sas_entry)
299        else:
300            # File not found
301            msg = "{} is not a valid file path or doesn't exist".format(path)
302            raise IOError, msg
303
304        if len(output) == 0:
305            return None
306        elif len(output) == 1:
307            self.callback(output[0].meta_data['corstate'], datainfo=output[0])
308            return output[0]
309        else:
310            return output
311
312    def write(self, filename, datainfo=None, state=None):
313        """
314        Write the content of a Data1D as a CanSAS file.
315
316        : param filename: Name of the file to write
317        : param datainfo: Data1D object
318        : param state: CorfuncState object
319        """
320        # Prepare datainfo
321        if datainfo is None:
322            datainfo = Data1D(x=[], y=[])
323        elif not (isinstance(datainfo, Data1D) or isinstance(datainfo, LoaderData1D)):
324            msg = ("The CanSAS writer expects a Data1D instance. {} was "
325                "provided").format(datainfo.__class__.__name__)
326            raise RuntimeError, msg
327        if datainfo.title is None or datainfo.title == '':
328            datainfo.title = datainfo.name
329        if datainfo.run_name == None or datainfo.run_name == '':
330            datainfo.run = [str(datainfo.name)]
331            datainfo.run_name[0] = datainfo.name
332
333        # Create the XMl doc
334        doc, sasentry = self._to_xml_doc(datainfo)
335        if state is not None:
336            doc = state.toXML(doc=doc, entry_node=sasentry)
337
338        # Write the XML doc to a file
339        fd = open(filename, 'w')
340        fd.write(doc.toprettyxml())
341        fd.close()
342
343    def get_state(self):
344        return self.state
345
346
347    def _parse_state(self, entry):
348        """
349        Read state data from an XML node
350
351        :param entry: The XML node to read from
352        :return: CorfuncState object
353        """
354        state = None
355        try:
356            nodes = entry.xpath('ns:{}'.format(CORNODE_NAME),
357                namespaces={'ns': CANSAS_NS})
358            if nodes != []:
359                state = CorfuncState()
360                state.fromXML(nodes[0])
361        except:
362            msg = "XML document does not contain CorfuncState information\n{}"
363            msg.format(sys.exc_value)
364            logger.info(msg)
365        return state
Note: See TracBrowser for help on using the repository browser.