source: sasview/src/sas/qtgui/MainWindow/DataManager.py @ e958531

Last change on this file since e958531 was 7d9c83c, checked in by Piotr Rozyczko <rozyczko@…>, 7 years ago

Minor changes to logging and looping in DataOperation? calculator

  • Property mode set to 100644
File size: 15.5 KB
Line 
1################################################################################
2#This software was developed by the University of Tennessee as part of the
3#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
4#project funded by the US National Science Foundation.
5#
6#See the license text in license.txt
7#
8#copyright 2010, University of Tennessee
9################################################################################
10"""
11This module manages all data loaded into the application. Data_manager makes
12available all data loaded  for the current perspective.
13
14All modules "creating Data" posts their data to data_manager .
15Data_manager  make these new data available for all other perspectives.
16"""
17#
18# REDUNDANT CLASS!!!
19# ALL THE FUNCTIONALITY IS BEING MOVED TO GuiManager.py
20#
21import os
22import copy
23import logging
24import json
25import time
26from StringIO import StringIO
27import numpy as np
28
29from sas.qtgui.Plotting.PlotterData import Data1D
30from sas.qtgui.Plotting.PlotterData import Data2D
31from sas.qtgui.Plotting.Plottables import Plottable
32from sas.qtgui.Plotting.Plottables import PlottableTheory1D
33from sas.qtgui.Plotting.Plottables import PlottableFit1D
34from sas.qtgui.Plotting.Plottables import Text
35from sas.qtgui.Plotting.Plottables import Chisq
36from sas.qtgui.Plotting.Plottables import View
37
38from sas.qtgui.Utilities import GuiUtils
39from sas.qtgui.MainWindow.DataState import DataState
40import sas.sascalc.dataloader.data_info as DataInfo
41
42# used for import/export
43from sas.sascalc.dataloader.data_info import Sample, Source, Vector
44
45class DataManager(object):
46    """
47    Manage a list of data
48    """
49    def __init__(self):
50        """
51        Store opened path and data object created at the loading time
52        :param auto_plot: if True the datamanager sends data to plotting
53                            plugin.
54        :param auto_set_data: if True the datamanager sends to the current
55        perspective
56        """
57        self.stored_data = {}
58        self.message = ""
59        self.data_name_dict = {}
60        self.count = 0
61        self.list_of_id = []
62        self.time_stamp = time.time()
63
64    def __str__(self):
65        _str  = ""
66        _str += "No of states  is %s \n" % str(len(self.stored_data))
67        n_count = 0
68        for  value in self.stored_data.values():
69            n_count += 1
70            _str += "State No %s \n"  % str(n_count)
71            _str += str(value) + "\n"
72        return _str
73
74    def create_gui_data(self, data, path=None):
75        """
76        Receive data from loader and create a data to use for guiframe
77        """
78
79        if issubclass(Data2D, data.__class__):
80            new_plot = Data2D(image=None, err_image=None)
81        else:
82            new_plot = Data1D(x=[], y=[], dx=None, dy=None)
83
84        new_plot.copy_from_datainfo(data)
85        data.clone_without_data(clone=new_plot)
86        #creating a name for data
87        title = ""
88        file_name = os.path.basename(path) if path is not None else data.filename
89        if file_name:
90            name = file_name
91        elif data.run:
92            name = data.run[0]
93        else:
94            name = "data"
95        name = self.rename(name)
96        #find title
97        if data.title.strip():
98            title = data.title
99        if title.strip() == "":
100            title = file_name
101
102        if new_plot.filename.strip() == "":
103            new_plot.filename = file_name
104
105        new_plot.name = name
106        new_plot.title = title
107        ## allow to highlight data when plotted
108        new_plot.interactive = True
109        ## when 2 data have the same id override the 1 st plotted
110        self.time_stamp += 1
111        new_plot.id = str(name) + str(self.time_stamp)
112        ##group_id specify on which panel to plot this data
113        new_plot.group_id = str(name) + str(self.time_stamp)
114        new_plot.is_data = True
115        new_plot.path = path
116        new_plot.list_group_id = []
117        ##post data to plot
118        # plot data
119        return new_plot
120
121    def rename(self, name):
122        """
123        rename data
124        """
125        ## name of the data allow to differentiate data when plotted
126        name = GuiUtils.parseName(name=name, expression="_")
127
128        max_char = name.find("[")
129        if max_char < 0:
130            max_char = len(name)
131        name = name[0:max_char]
132
133        if name not in self.data_name_dict:
134            self.data_name_dict[name] = 0
135        else:
136            self.data_name_dict[name] += 1
137            name = name + " [" + str(self.data_name_dict[name]) + "]"
138        return name
139
140
141    def add_data(self, data_list):
142        """
143        receive a list of
144        """
145        for id, data in data_list.iteritems():
146            if id  in self.stored_data:
147                msg = "Data manager already stores %s" % str(data.name)
148                msg += ""
149                logging.info(msg)
150                data_state = self.stored_data[id]
151                data_state.data = data
152            else:
153                data_state = DataState(data)
154                data_state.id = id
155                data_state.path = data.path
156                self.stored_data[id] = data_state
157
158    def update_data(self, prev_data, new_data):
159        """
160        """
161        if prev_data.id not in self.stored_data.keys():
162            return None, {}
163        data_state = self.stored_data[prev_data.id]
164        self.stored_data[new_data.id]  = data_state.clone()
165        self.stored_data[new_data.id].data = new_data
166        if prev_data.id in self.stored_data.keys():
167            del self.stored_data[prev_data.id]
168        return prev_data.id, {new_data.id: self.stored_data[new_data.id]}
169
170    def update_theory(self, theory, data_id=None, state=None):
171        """
172        """
173        uid = data_id
174        if data_id is None and theory is not None:
175            uid = theory.id
176        if uid in self.stored_data.keys():
177             data_state = self.stored_data[uid]
178        else:
179            data_state = DataState()
180        data_state.uid = uid
181        data_state.set_theory(theory_data=theory, theory_state=state)
182        self.stored_data[uid] = data_state
183        return {uid: self.stored_data[uid]}
184
185
186    def get_message(self):
187        """
188        return message
189        """
190        return self.message
191
192    def get_by_id(self, id_list=None):
193        """
194        """
195        _selected_data = {}
196        _selected_theory_list = {}
197        if id_list is None:
198            return
199        for d_id in self.stored_data:
200            for search_id in id_list:
201                data_state = self.stored_data[d_id]
202                data = data_state.data
203                theory_list = data_state.get_theory()
204                if search_id == d_id:
205                    _selected_data[search_id] = data
206                if search_id in theory_list.keys():
207                     _selected_theory_list[search_id] = theory_list[search_id]
208
209        return _selected_data, _selected_theory_list
210
211
212    def freeze(self, theory_id):
213        """
214        """
215        return self.freeze_theory(self.stored_data.keys(), theory_id)
216
217    def freeze_theory(self, data_id, theory_id):
218        """
219        """
220        selected_theory = {}
221        for d_id in data_id:
222            if d_id in self.stored_data:
223                data_state = self.stored_data[d_id]
224                theory_list = data_state.get_theory()
225                for t_id in theory_id:
226                    if t_id in theory_list.keys():
227                        theory_data, theory_state = theory_list[t_id]
228                        new_theory = copy.deepcopy(theory_data)
229                        new_theory.id  = time.time()
230                        new_theory.is_data = True
231                        new_theory.name += '_@' + \
232                                    str(new_theory.id)[7:-1].replace('.', '')
233                        new_theory.title = new_theory.name
234                        new_theory.label = new_theory.name
235                        selected_theory[new_theory.id] = DataState(new_theory)
236                        self.stored_data[new_theory.id] = \
237                                    selected_theory[new_theory.id]
238
239        return selected_theory
240
241
242    def delete_data(self, data_id, theory_id=None, delete_all=False):
243        """
244        """
245        for d_id in data_id:
246            if d_id in self.stored_data.keys():
247                data_state = self.stored_data[d_id]
248                if data_state.data.name in self.data_name_dict:
249                    del self.data_name_dict[data_state.data.name]
250                del self.stored_data[d_id]
251
252        self.delete_theory(data_id, theory_id)
253        if delete_all:
254            self.stored_data = {}
255            self.data_name_dict = {}
256
257    def delete_theory(self, data_id, theory_id):
258        """
259        """
260        for d_id in data_id:
261            if d_id in self.stored_data:
262                data_state = self.stored_data[d_id]
263                theory_list = data_state.get_theory()
264                if theory_id in theory_list.keys():
265                    del theory_list[theory_id]
266        #del pure theory
267        self.delete_by_id(theory_id)
268
269    def delete_by_id(self, id_list=None):
270        """
271        save data and path
272        """
273        for id in id_list:
274            if id in self.stored_data:
275                del self.stored_data[id]
276
277    def get_by_name(self, name_list=None):
278        """
279        return a list of data given a list of data names
280        """
281        _selected_data = {}
282        for selected_name in name_list:
283            for id, data_state in self.stored_data.iteritems():
284                if data_state.data.name == selected_name:
285                    _selected_data[id] = data_state.data
286        return _selected_data
287
288    def delete_by_name(self, name_list=None):
289        """
290        save data and path
291        """
292        for selected_name in name_list:
293            for id, data_state in self.stored_data.iteritems():
294                if data_state.data.name == selected_name:
295                    del self.stored_data[id]
296
297    def update_stored_data(self, name_list=None):
298        """ update stored data after deleting files in Data Explorer """
299        for selected_name in name_list:
300            # Take the copy of current, possibly shorter stored_data dict
301            stored_data = copy.deepcopy(self.stored_data)
302            for idx in stored_data.keys():
303                if str(selected_name) in str(idx):
304                    del self.stored_data[idx]
305
306    def get_data_state(self, data_id):
307        """
308        Send list of selected data
309        """
310        _selected_data_state = {}
311        for id in data_id:
312            if id in self.stored_data.keys():
313                _selected_data_state[id] = self.stored_data[id]
314        return _selected_data_state
315
316    def get_all_data(self):
317        """
318        return list of all available data
319        """
320        return self.stored_data
321
322    def assign(self, other):
323        self.stored_data = other.stored_data
324        self.message = other.message
325        self.data_name_dict = other.data_name_dict
326        self.count = other.count
327        self.list_of_id = other.list_of_id
328        self.time_stamp = other.time_stamp
329
330    def save_to_writable(self, fp):
331        """
332        save content of stored_data to fp (a .write()-supporting file-like object)
333        """
334
335        def add_type(dict, type):
336            dict['__type__'] = type.__name__
337            return dict
338
339        def jdefault(o):
340            """
341            objects that can't otherwise be serialized need to be converted
342            """
343            # tuples and sets (TODO: default JSONEncoder converts tuples to lists, create custom Encoder that preserves tuples)
344            if isinstance(o, (tuple, set)):
345                content = { 'data': list(o) }
346                return add_type(content, type(o))
347
348            # "simple" types
349            if isinstance(o, (Sample, Source, Vector)):
350                return add_type(o.__dict__, type(o))
351            if isinstance(o, (Plottable, View)):
352                return add_type(o.__dict__, type(o))
353
354            # DataState
355            if isinstance(o, DataState):
356                # don't store parent
357                content = o.__dict__.copy()
358                content.pop('parent')
359                return add_type(content, type(o))
360
361            # ndarray
362            if isinstance(o, np.ndarray):
363                buffer = StringIO()
364                np.save(buffer, o)
365                buffer.seek(0)
366                content = { 'data': buffer.read().decode('latin-1') }
367                return add_type(content, type(o))
368
369            # not supported
370            logging.info("data cannot be serialized to json: %s" % type(o))
371            return None
372
373        json.dump(self.stored_data, fp, indent=2, sort_keys=True, default=jdefault)
374
375
376    def load_from_readable(self, fp):
377        """
378        load content from tp to stored_data (a .read()-supporting file-like object)
379        """
380
381        supported = [
382            tuple, set,
383            Sample, Source, Vector,
384            Plottable, Data1D, Data2D, PlottableTheory1D, PlottableFit1D, Text, Chisq, View,
385            DataState, np.ndarray]
386
387        lookup = dict((cls.__name__, cls) for cls in supported)
388
389        class TooComplexException(Exception):
390            pass
391
392        def simple_type(cls, data, level):
393            class Empty(object):
394                def __init__(self):
395                    for key, value in data.iteritems():
396                        setattr(self, key, generate(value, level))
397
398            # create target object
399            o = Empty()
400            o.__class__ = cls
401
402            return o
403
404        def construct(type, data, level):
405            try:
406                cls = lookup[type]
407            except KeyError:
408                logging.info('unknown type: %s' % type)
409                return None
410
411            # tuples and sets
412            if cls in (tuple, set):
413                # convert list to tuple/set
414                return cls(generate(data['data'], level))
415
416            # "simple" types
417            if cls in (Sample, Source, Vector):
418                return simple_type(cls, data, level)
419            if issubclass(cls, Plottable) or (cls == View):
420                return simple_type(cls, data, level)
421
422            # DataState
423            if cls == DataState:
424                o = simple_type(cls, data, level)
425                o.parent = None # TODO: set to ???
426                return o
427
428            # ndarray
429            if cls == np.ndarray:
430                buffer = StringIO()
431                buffer.write(data['data'].encode('latin-1'))
432                buffer.seek(0)
433                return np.load(buffer)
434
435            logging.info('not implemented: %s, %s' % (type, cls))
436            return None
437
438        def generate(data, level):
439            if level > 16: # recursion limit (arbitrary number)
440                raise TooComplexException()
441            else:
442                level += 1
443
444            if isinstance(data, dict):
445                try:
446                    type = data['__type__']
447                except KeyError:
448                    # if dictionary doesn't have __type__ then it is assumed to be just an ordinary dictionary
449                    o = {}
450                    for key, value in data.iteritems():
451                        o[key] = generate(value, level)
452                    return o
453
454                return construct(type, data, level)
455
456            if isinstance(data, list):
457                return [generate(item, level) for item in data]
458
459            return data
460
461        new_stored_data = {}
462        for id, data in json.load(fp).iteritems():
463            try:
464                new_stored_data[id] = generate(data, 0)
465            except TooComplexException:
466                logging.info('unable to load %s' % id)
467
468        self.stored_data = new_stored_data
469
Note: See TracBrowser for help on using the repository browser.