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

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

Merge branch 'master' into ESS_GUI

  • Property mode set to 100644
File size: 15.6 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 os.path.basename(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        stripped_filename = new_plot.filename.strip()
103        if not stripped_filename:
104            new_plot.filename = file_name
105        if stripped_filename != os.path.basename(stripped_filename):
106            new_plot.filename = file_name
107
108        new_plot.name = name
109        new_plot.title = title
110        ## allow to highlight data when plotted
111        new_plot.interactive = True
112        ## when 2 data have the same id override the 1 st plotted
113        self.time_stamp += 1
114        new_plot.id = str(name) + str(self.time_stamp)
115        ##group_id specify on which panel to plot this data
116        new_plot.group_id = str(name) + str(self.time_stamp)
117        new_plot.is_data = True
118        new_plot.path = path
119        new_plot.list_group_id = []
120        ##post data to plot
121        # plot data
122        return new_plot
123
124    def rename(self, name):
125        """
126        rename data
127        """
128        ## name of the data allow to differentiate data when plotted
129        name = GuiUtils.parseName(name=name, expression="_")
130
131        max_char = name.find("[")
132        if max_char < 0:
133            max_char = len(name)
134        name = name[0:max_char]
135
136        if name not in self.data_name_dict:
137            self.data_name_dict[name] = 0
138        else:
139            self.data_name_dict[name] += 1
140            name = name + " [" + str(self.data_name_dict[name]) + "]"
141        return name
142
143
144    def add_data(self, data_list):
145        """
146        receive a list of
147        """
148        for id, data in data_list.iteritems():
149            if id  in self.stored_data:
150                msg = "Data manager already stores %s" % str(data.name)
151                msg += ""
152                logging.info(msg)
153                data_state = self.stored_data[id]
154                data_state.data = data
155            else:
156                data_state = DataState(data)
157                data_state.id = id
158                data_state.path = data.path
159                self.stored_data[id] = data_state
160
161    def update_data(self, prev_data, new_data):
162        """
163        """
164        if prev_data.id not in self.stored_data.keys():
165            return None, {}
166        data_state = self.stored_data[prev_data.id]
167        self.stored_data[new_data.id]  = data_state.clone()
168        self.stored_data[new_data.id].data = new_data
169        if prev_data.id in self.stored_data.keys():
170            del self.stored_data[prev_data.id]
171        return prev_data.id, {new_data.id: self.stored_data[new_data.id]}
172
173    def update_theory(self, theory, data_id=None, state=None):
174        """
175        """
176        uid = data_id
177        if data_id is None and theory is not None:
178            uid = theory.id
179        if uid in self.stored_data.keys():
180             data_state = self.stored_data[uid]
181        else:
182            data_state = DataState()
183        data_state.uid = uid
184        data_state.set_theory(theory_data=theory, theory_state=state)
185        self.stored_data[uid] = data_state
186        return {uid: self.stored_data[uid]}
187
188
189    def get_message(self):
190        """
191        return message
192        """
193        return self.message
194
195    def get_by_id(self, id_list=None):
196        """
197        """
198        _selected_data = {}
199        _selected_theory_list = {}
200        if id_list is None:
201            return
202        for d_id in self.stored_data:
203            for search_id in id_list:
204                data_state = self.stored_data[d_id]
205                data = data_state.data
206                theory_list = data_state.get_theory()
207                if search_id == d_id:
208                    _selected_data[search_id] = data
209                if search_id in theory_list.keys():
210                     _selected_theory_list[search_id] = theory_list[search_id]
211
212        return _selected_data, _selected_theory_list
213
214
215    def freeze(self, theory_id):
216        """
217        """
218        return self.freeze_theory(self.stored_data.keys(), theory_id)
219
220    def freeze_theory(self, data_id, theory_id):
221        """
222        """
223        selected_theory = {}
224        for d_id in data_id:
225            if d_id in self.stored_data:
226                data_state = self.stored_data[d_id]
227                theory_list = data_state.get_theory()
228                for t_id in theory_id:
229                    if t_id in theory_list.keys():
230                        theory_data, theory_state = theory_list[t_id]
231                        new_theory = copy.deepcopy(theory_data)
232                        new_theory.id  = time.time()
233                        new_theory.is_data = True
234                        new_theory.name += '_@' + \
235                                    str(new_theory.id)[7:-1].replace('.', '')
236                        new_theory.title = new_theory.name
237                        new_theory.label = new_theory.name
238                        selected_theory[new_theory.id] = DataState(new_theory)
239                        self.stored_data[new_theory.id] = \
240                                    selected_theory[new_theory.id]
241
242        return selected_theory
243
244
245    def delete_data(self, data_id, theory_id=None, delete_all=False):
246        """
247        """
248        for d_id in data_id:
249            if d_id in self.stored_data.keys():
250                data_state = self.stored_data[d_id]
251                if data_state.data.name in self.data_name_dict:
252                    del self.data_name_dict[data_state.data.name]
253                del self.stored_data[d_id]
254
255        self.delete_theory(data_id, theory_id)
256        if delete_all:
257            self.stored_data = {}
258            self.data_name_dict = {}
259
260    def delete_theory(self, data_id, theory_id):
261        """
262        """
263        for d_id in data_id:
264            if d_id in self.stored_data:
265                data_state = self.stored_data[d_id]
266                theory_list = data_state.get_theory()
267                if theory_id in theory_list.keys():
268                    del theory_list[theory_id]
269        #del pure theory
270        self.delete_by_id(theory_id)
271
272    def delete_by_id(self, id_list=None):
273        """
274        save data and path
275        """
276        for id in id_list:
277            if id in self.stored_data:
278                del self.stored_data[id]
279
280    def get_by_name(self, name_list=None):
281        """
282        return a list of data given a list of data names
283        """
284        _selected_data = {}
285        for selected_name in name_list:
286            for id, data_state in self.stored_data.iteritems():
287                if data_state.data.name == selected_name:
288                    _selected_data[id] = data_state.data
289        return _selected_data
290
291    def delete_by_name(self, name_list=None):
292        """
293        save data and path
294        """
295        for selected_name in name_list:
296            for id, data_state in self.stored_data.iteritems():
297                if data_state.data.name == selected_name:
298                    del self.stored_data[id]
299
300    def update_stored_data(self, name_list=None):
301        """ update stored data after deleting files in Data Explorer """
302        for selected_name in name_list:
303            # Take the copy of current, possibly shorter stored_data dict
304            stored_data = copy.deepcopy(self.stored_data)
305            for idx in stored_data.keys():
306                if str(selected_name) in str(idx):
307                    del self.stored_data[idx]
308
309    def get_data_state(self, data_id):
310        """
311        Send list of selected data
312        """
313        _selected_data_state = {}
314        for id in data_id:
315            if id in self.stored_data.keys():
316                _selected_data_state[id] = self.stored_data[id]
317        return _selected_data_state
318
319    def get_all_data(self):
320        """
321        return list of all available data
322        """
323        return self.stored_data
324
325    def assign(self, other):
326        self.stored_data = other.stored_data
327        self.message = other.message
328        self.data_name_dict = other.data_name_dict
329        self.count = other.count
330        self.list_of_id = other.list_of_id
331        self.time_stamp = other.time_stamp
332
333    def save_to_writable(self, fp):
334        """
335        save content of stored_data to fp (a .write()-supporting file-like object)
336        """
337
338        def add_type(dict, type):
339            dict['__type__'] = type.__name__
340            return dict
341
342        def jdefault(o):
343            """
344            objects that can't otherwise be serialized need to be converted
345            """
346            # tuples and sets (TODO: default JSONEncoder converts tuples to lists, create custom Encoder that preserves tuples)
347            if isinstance(o, (tuple, set)):
348                content = { 'data': list(o) }
349                return add_type(content, type(o))
350
351            # "simple" types
352            if isinstance(o, (Sample, Source, Vector)):
353                return add_type(o.__dict__, type(o))
354            if isinstance(o, (Plottable, View)):
355                return add_type(o.__dict__, type(o))
356
357            # DataState
358            if isinstance(o, DataState):
359                # don't store parent
360                content = o.__dict__.copy()
361                content.pop('parent')
362                return add_type(content, type(o))
363
364            # ndarray
365            if isinstance(o, np.ndarray):
366                buffer = StringIO()
367                np.save(buffer, o)
368                buffer.seek(0)
369                content = { 'data': buffer.read().decode('latin-1') }
370                return add_type(content, type(o))
371
372            # not supported
373            logging.info("data cannot be serialized to json: %s" % type(o))
374            return None
375
376        json.dump(self.stored_data, fp, indent=2, sort_keys=True, default=jdefault)
377
378
379    def load_from_readable(self, fp):
380        """
381        load content from tp to stored_data (a .read()-supporting file-like object)
382        """
383
384        supported = [
385            tuple, set,
386            Sample, Source, Vector,
387            Plottable, Data1D, Data2D, PlottableTheory1D, PlottableFit1D, Text, Chisq, View,
388            DataState, np.ndarray]
389
390        lookup = dict((cls.__name__, cls) for cls in supported)
391
392        class TooComplexException(Exception):
393            pass
394
395        def simple_type(cls, data, level):
396            class Empty(object):
397                def __init__(self):
398                    for key, value in data.iteritems():
399                        setattr(self, key, generate(value, level))
400
401            # create target object
402            o = Empty()
403            o.__class__ = cls
404
405            return o
406
407        def construct(type, data, level):
408            try:
409                cls = lookup[type]
410            except KeyError:
411                logging.info('unknown type: %s' % type)
412                return None
413
414            # tuples and sets
415            if cls in (tuple, set):
416                # convert list to tuple/set
417                return cls(generate(data['data'], level))
418
419            # "simple" types
420            if cls in (Sample, Source, Vector):
421                return simple_type(cls, data, level)
422            if issubclass(cls, Plottable) or (cls == View):
423                return simple_type(cls, data, level)
424
425            # DataState
426            if cls == DataState:
427                o = simple_type(cls, data, level)
428                o.parent = None # TODO: set to ???
429                return o
430
431            # ndarray
432            if cls == np.ndarray:
433                buffer = StringIO()
434                buffer.write(data['data'].encode('latin-1'))
435                buffer.seek(0)
436                return np.load(buffer)
437
438            logging.info('not implemented: %s, %s' % (type, cls))
439            return None
440
441        def generate(data, level):
442            if level > 16: # recursion limit (arbitrary number)
443                raise TooComplexException()
444            else:
445                level += 1
446
447            if isinstance(data, dict):
448                try:
449                    type = data['__type__']
450                except KeyError:
451                    # if dictionary doesn't have __type__ then it is assumed to be just an ordinary dictionary
452                    o = {}
453                    for key, value in data.iteritems():
454                        o[key] = generate(value, level)
455                    return o
456
457                return construct(type, data, level)
458
459            if isinstance(data, list):
460                return [generate(item, level) for item in data]
461
462            return data
463
464        new_stored_data = {}
465        for id, data in json.load(fp).iteritems():
466            try:
467                new_stored_data[id] = generate(data, 0)
468            except TooComplexException:
469                logging.info('unable to load %s' % id)
470
471        self.stored_data = new_stored_data
472
Note: See TracBrowser for help on using the repository browser.