source: sasview/src/sas/sasgui/guiframe/data_manager.py @ 64f1e93

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 64f1e93 was 630155bd, checked in by davidm, 8 years ago

implementation of loadProject and saveProject

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