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

Last change on this file since d32a594 was 345b3b3, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 6 years ago

Save status of data explorer

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