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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since f0bb711 was f0bb711, checked in by celinedurniak <celine.durniak@…>, 7 years ago

Implemented comments from review for Data Operation Panel (ESS-GUI-SasView?-245)

  • Property mode set to 100755
File size: 15.0 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 get_data_state(self, data_id):
298        """
299        Send list of selected data
300        """
301        _selected_data_state = {}
302        for id in data_id:
303            if id in self.stored_data.keys():
304                _selected_data_state[id] = self.stored_data[id]
305        return _selected_data_state
306
307    def get_all_data(self):
308        """
309        return list of all available data
310        """
311        return self.stored_data
312
313    def assign(self, other):
314        self.stored_data = other.stored_data
315        self.message = other.message
316        self.data_name_dict = other.data_name_dict
317        self.count = other.count
318        self.list_of_id = other.list_of_id
319        self.time_stamp = other.time_stamp
320
321    def save_to_writable(self, fp):
322        """
323        save content of stored_data to fp (a .write()-supporting file-like object)
324        """
325
326        def add_type(dict, type):
327            dict['__type__'] = type.__name__
328            return dict
329
330        def jdefault(o):
331            """
332            objects that can't otherwise be serialized need to be converted
333            """
334            # tuples and sets (TODO: default JSONEncoder converts tuples to lists, create custom Encoder that preserves tuples)
335            if isinstance(o, (tuple, set)):
336                content = { 'data': list(o) }
337                return add_type(content, type(o))
338
339            # "simple" types
340            if isinstance(o, (Sample, Source, Vector)):
341                return add_type(o.__dict__, type(o))
342            if isinstance(o, (Plottable, View)):
343                return add_type(o.__dict__, type(o))
344
345            # DataState
346            if isinstance(o, DataState):
347                # don't store parent
348                content = o.__dict__.copy()
349                content.pop('parent')
350                return add_type(content, type(o))
351
352            # ndarray
353            if isinstance(o, np.ndarray):
354                buffer = StringIO()
355                np.save(buffer, o)
356                buffer.seek(0)
357                content = { 'data': buffer.read().decode('latin-1') }
358                return add_type(content, type(o))
359
360            # not supported
361            logging.info("data cannot be serialized to json: %s" % type(o))
362            return None
363
364        json.dump(self.stored_data, fp, indent=2, sort_keys=True, default=jdefault)
365
366
367    def load_from_readable(self, fp):
368        """
369        load content from tp to stored_data (a .read()-supporting file-like object)
370        """
371
372        supported = [
373            tuple, set,
374            Sample, Source, Vector,
375            Plottable, Data1D, Data2D, PlottableTheory1D, PlottableFit1D, Text, Chisq, View,
376            DataState, np.ndarray]
377
378        lookup = dict((cls.__name__, cls) for cls in supported)
379
380        class TooComplexException(Exception):
381            pass
382
383        def simple_type(cls, data, level):
384            class Empty(object):
385                def __init__(self):
386                    for key, value in data.iteritems():
387                        setattr(self, key, generate(value, level))
388
389            # create target object
390            o = Empty()
391            o.__class__ = cls
392
393            return o
394
395        def construct(type, data, level):
396            try:
397                cls = lookup[type]
398            except KeyError:
399                logging.info('unknown type: %s' % type)
400                return None
401
402            # tuples and sets
403            if cls in (tuple, set):
404                # convert list to tuple/set
405                return cls(generate(data['data'], level))
406
407            # "simple" types
408            if cls in (Sample, Source, Vector):
409                return simple_type(cls, data, level)
410            if issubclass(cls, Plottable) or (cls == View):
411                return simple_type(cls, data, level)
412
413            # DataState
414            if cls == DataState:
415                o = simple_type(cls, data, level)
416                o.parent = None # TODO: set to ???
417                return o
418
419            # ndarray
420            if cls == np.ndarray:
421                buffer = StringIO()
422                buffer.write(data['data'].encode('latin-1'))
423                buffer.seek(0)
424                return np.load(buffer)
425
426            logging.info('not implemented: %s, %s' % (type, cls))
427            return None
428
429        def generate(data, level):
430            if level > 16: # recursion limit (arbitrary number)
431                raise TooComplexException()
432            else:
433                level += 1
434
435            if isinstance(data, dict):
436                try:
437                    type = data['__type__']
438                except KeyError:
439                    # if dictionary doesn't have __type__ then it is assumed to be just an ordinary dictionary
440                    o = {}
441                    for key, value in data.iteritems():
442                        o[key] = generate(value, level)
443                    return o
444
445                return construct(type, data, level)
446
447            if isinstance(data, list):
448                return [generate(item, level) for item in data]
449
450            return data
451
452        new_stored_data = {}
453        for id, data in json.load(fp).iteritems():
454            try:
455                new_stored_data[id] = generate(data, 0)
456            except TooComplexException:
457                logging.info('unable to load %s' % id)
458
459        self.stored_data = new_stored_data
460
Note: See TracBrowser for help on using the repository browser.