source: mauRepo/MolesManager/trunk/src/MolesManager/views/moles2gui/__init__.py @ 8480

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/mauRepo/MolesManager/trunk/src/MolesManager/views/moles2gui/__init__.py@8480
Revision 8480, 12.8 KB checked in by mnagni, 8 years ago (diff)

Fixed a bug in objectEncoding

Line 
1from json.encoder import JSONEncoder
2from json import loads
3import re
4import logging
5from abc import abstractmethod
6from ea_model.moles3_4.utilities.mo_organisation import MO_Organisation
7from ea_model.moles3_4.utilities.mo_individual import MO_Individual
8from MolesManager.djencoder import escapeForJSON
9import operator
10import inspect
11from libs.epb import isPrimitive
12
13"""
14    From now on we define:
15    - cedaObject any instances or class defined in from the CedaMoles3 model
16    - jsonObject any json representation generated by the CedaMolesGuiAdapter.encode method
17"""
18
19_encodeMapper = {'ea_model.iso_19108_2006_temporal_schema.temporal_objects.tm_instant.TM_Instant': 
20                    ('MolesManager.views.moles2gui.om.instant', 'Instant'),
21                 'ea_model.iso_19108_2006_temporal_schema.temporal_objects.tm_period.TM_Period': 
22                    ('MolesManager.views.moles2gui.om.period', 'Period'),                   
23                 'ea_model.moles3_4.utilities.mo_organisation.MO_Organisation': 
24                    ('MolesManager.views.moles2gui.mo.party', 'Party'),
25                 'ea_model.moles3_4.utilities.mo_individual.MO_Individual': 
26                    ('MolesManager.views.moles2gui.mo.party', 'Party'),
27                 'ea_model.moles3_4.utilities.mo_responsiblepartyinfo.MO_ResponsiblePartyInfo': 
28                    ('MolesManager.views.moles2gui.mo.responsiblePartyInfo', 'ResponsiblePartyInfo'),
29                 'ea_model.ceda_metadatamodel.ceda_observation.ceda_observation.CEDA_Observation': 
30                    ('MolesManager.views.moles2gui.ceda.observation', 'Observation')                   
31                 }
32
33def encodeCedaMoles2Json(cedaObj):
34    adapter = _encodeCedaMoles2Adapter(cedaObj);
35    if adapter is not None:
36        return _Moles2guiEncoder().encode(adapter)
37   
38    # Then is a non mapped class
39    return None
40
41def decodeJson2CedaMoles(json= None, moles3EPB = None, cedaMoleObj = None):
42    """
43        Updates the given a cedaObject using a jsonObject object.
44        If `moles3EPB` is given it overrride the value from `cedaMoleObj`
45       
46        **Parameters**
47            * json: a json string
48            * moles3EPB: an epb instance to retrieve the cedaObject by the json string
49            * cedaMoleObj: a cedaObject
50       
51        **Raise**
52            * Exception both `moles3EPB` and `cedaMoleObj` are `None`
53    """   
54    if (cedaMoleObj is None or moles3EPB is None) and json is None:
55        raise Exception("No sufficient parameters to exploit the request")
56       
57    adapter = _decodeJson2Adapter(json)       
58     
59    if adapter == json:
60        raise Exception("json string %s cannot be loaded") % json
61   
62    if cedaMoleObj is None:
63        cedaMoleObj = moles3EPB.search(dynamicallyLoadClass(adapter._module, adapter._clazz), adapter.id).first()
64    if cedaMoleObj is None:
65        return cedaMoleObj
66       
67    adapter.decode(cedaMoleObj)
68    return cedaMoleObj
69
70def dynamicallyLoadClass(modulePath, className):
71    mod = __import__(modulePath, fromlist=[className])
72    return getattr(mod, className)
73
74def dictByID(objList):
75    """
76        **Deprecated**
77        Returns a dictionary where the key is the object id
78    """   
79    ret = {}
80    for item in objList:
81        ret[item.id] = item
82    return ret
83
84class CedaMolesGuiAdapter(object):
85    internal = ['_module', '_clazz', '_adapter_module', '_adapter_clazz']
86    mapper = {}
87    acceptedTypes = []   
88
89    def __init__(self):
90        for item in self.mapper.items():
91            setattr(self, item[0], None)
92
93    @classmethod
94    def _cedaObjToAdapterMapping(cls, instance, cedaObject):
95        if type(cedaObject) in cls.acceptedTypes:       
96            for item in cls.mapper.items():
97                setattr(instance, item[0], getData(cedaObject, item[1]))
98
99    @classmethod
100    def _adapterToCedaObjMapping(cls, instance, cedaObj):
101        for item in cls.mapper.items():           
102            _setData(cedaObj, item[1], getattr(instance, item[0]))
103
104    def postCedaObjToAdapterMapping(self, cedaObject):
105        """
106        Refines the mapping done using the internal `mapper`.
107        If some mapping are possible only at a later moment override this method
108       
109        **Parameters**
110                * cedaObj: the Ceda object instance which has to be mapped to the adapter
111        """
112        pass
113   
114    def postAdapterToCedaObjMapping(self, cedaObject):
115        """
116        Refines the mapping done using the internal `mapper`.
117        If some mapping are possible only at a later moment override this method
118       
119        **Parameters**
120                * cedaObject: the Ceda object instance which has to be mapped from the adapter
121        """
122    pass
123
124    @classmethod
125    def cedaObjToAdapter(cls, cedaObject):
126        """
127        Injects a new attribute in a CedaMolesGuiAdapter. Using the 2 component `attributeMap` tuple,
128        the attribute has 
129        - name as attributeMap[0]
130        - attributeMap as the attribute in `cedaObject` speficied by attributeMap[1]
131           
132        **Parameters**
133            * cedaObject: a Ceda object instance
134        """
135        adapter = cls()
136        if type(cedaObject) not in cls.acceptedTypes:
137            raise Exception ("Not accepted type mapping")
138       
139        if cedaObject is not None:
140            setattr(adapter, 'id', None)
141            if hasattr(cedaObject, 'id'):
142                setattr(adapter, 'id', cedaObject.id)
143               
144            setattr(adapter, '_module', cedaObject.__module__)       
145            setattr(adapter, '_clazz', cedaObject.__class__.__name__)       
146        setattr(adapter, '_adapter_module', adapter.__module__)       
147        setattr(adapter, '_adapter_clazz', adapter.__class__.__name__)
148           
149        cls._cedaObjToAdapterMapping(adapter, cedaObject)
150        adapter.postCedaObjToAdapterMapping(cedaObject)
151       
152        return adapter
153
154    def decode(self, cedaObject):       
155        if type(cedaObject) not in self.acceptedTypes:
156            raise Exception ("Not accepted type mapping")
157
158        self._adapterToCedaObjMapping(self, cedaObject)
159        self.postAdapterToCedaObjMapping(cedaObject)
160
161class _Moles2guiEncoder(JSONEncoder):
162
163    log = logging.getLogger('DJEncoder')
164   
165    def __init__(self):
166        self.__markers = {}
167        super(_Moles2guiEncoder, self).__init__()
168        self.__pattern = re.compile('\D\D__*')
169        self.__pattern2 = re.compile('\A\w+__id\Z')
170       
171
172    def default(self, obj):       
173        # Convert objects to a dictionary of their representation
174        d = {}
175   
176        if not hasattr(obj, "__dict__"):
177            return d
178   
179        for key in obj.__dict__.keys():
180            if key in CedaMolesGuiAdapter.internal or not (key.startswith("_") or self.__pattern.match(key) or self.__pattern2.match(key)):
181                d.update({key: getattr(obj, key)})
182       
183        for key, value in d.items():
184            if value is not None and id(value) in self.__markers and not isinstance(value, str) and not isinstance(value, int):
185                continue
186            else:
187                self.__markers[id(value)] = value
188                if isinstance(value, str) or isinstance(value, unicode):                   
189                    self.__markers[id(value)] = escapeForJSON(value)
190                else:
191                    self.__markers[id(value)] = value
192        return d
193
194def getData(obj, attrString):
195    """
196    Returns an attribute value from an object. It is similar to getattr() function
197    but in this case the `attr` parameter express a deeper argument than `obj.paramName`.
198    For instance, call drillData(obj, 'a.b.c[1].d') is aquivalent to call
199    obj.a.b.c[1].d
200
201    **Parameters**         
202            * obj: its an the instance from which the attribute is required
203            * attrString: an attributes dot separated string expression
204    """
205    if attrString is not None and isinstance(attrString, str):
206        return _drillData(obj, attrString.split('.'))
207
208def _setDataToAdapter(obj, attrString, value):
209    if attrString is None or not isinstance(attrString, str) or value is None:
210        return
211
212    ret = None
213    if isinstance(value, list):
214        ret = []
215        for item in value:
216            ret.append(_decodeJson2Adapter(item))
217    else:
218        ret = _decodeJson2Adapter(value)       
219    setattr(obj, attrString, ret)
220
221def _setData(obj, attrString, value):
222    if attrString is None or not isinstance(attrString, str):
223        return
224   
225    attrOwner = obj
226    attrName =  attrString
227   
228    items = attrString.split('.')             
229    if len(items[:-1]) > 0:
230        attrOwner = _drillData(obj, items[:-1], toAdapter = False)
231        attrName =  items[-1:][0].partition('[')[0]             
232
233    if attrOwner is not None and hasattr(attrOwner, attrName):
234        attr = getattr(attrOwner, attrName)
235        if isinstance(attr, list):
236            if len(value) == 0:
237                del attr[:]
238           
239            if not isinstance(value[0], CedaMolesGuiAdapter):
240                del attr[:]
241                if isinstance(value, list):
242                    attr.extend(value)
243                else:
244                    attr.append(value)
245            else:   
246                sorted(attr, key = operator.attrgetter('id'))
247                sorted(value, key = operator.attrgetter('id'))
248                for item in attr:
249                    for valueItem in value:
250                        if item.id == valueItem.id:
251                            valueItem.decode(item)                       
252        else:
253            setattr(attrOwner, attrName, value)       
254   
255def _drillData(obj, attr, toAdapter = True):
256    """
257        When the method is used by the cedaObjToAdapter the `toAdapter=True` condition
258        guarantees that an immediate conversion of the mapped classes is done.
259        When the method is used by the adapterTocedaObj the `toAdapter=False` condition
260        guarantees that no 'cedaObjToAdapter' conversion is done. 
261    """
262    if attr is None or len(attr) == 0:
263        return None
264   
265    item = attr[0]
266    newobj = None
267           
268    # is an array?
269    if re.search("\[\d+\]", item) is not None:
270        index = int(re.search("\[\d+\]", item).group(0).replace('[', '').replace(']', ''))
271        parName = item.partition('[')[0]
272        if hasattr(obj, parName) and isinstance(getattr(obj, parName), list):
273            objattr = getattr(obj, parName)
274            if index < len(objattr):
275                newobj = objattr[index]
276            else:
277                return None
278        else:
279            raise Exception ("Parameter call exception. Param:%s" % item) 
280       
281    # is a dictionary?
282    elif re.search("\['\w+\'\]", item) is not None:           
283        key = re.search("\['\w+\'\]", item).group(0).replace('[\'', '').replace('\']', '')
284        parName = item.partition('[')[0]       
285        if hasattr(obj, parName) and isinstance(getattr(obj, parName), dict):
286            objattr = getattr(obj, parName)
287        if objattr.has_key(key):
288            newobj = objattr[key]
289       
290    # is just a simple attribute
291    else:
292        if hasattr(obj, item):
293            newobj = getattr(obj, item)
294   
295    #is the last attr's item?
296    if len(attr) > 1:       
297        return _drillData(newobj, attr[1:], toAdapter)
298   
299    if not toAdapter:
300        return newobj
301
302    if isinstance(newobj, list):
303        ret = []
304        for item in newobj:               
305            ret.append(_encodeCedaMoles2Adapter(item))
306        return ret
307    else:
308        return _encodeCedaMoles2Adapter(newobj)             
309   
310def _encodeCedaMoles2Adapter(cedaObj):
311    if isPrimitive(cedaObj):
312        return cedaObj
313   
314    # Is a mapped class?
315    key = "%s.%s" % (cedaObj.__module__, cedaObj.__class__.__name__)
316    if _encodeMapper.has_key(key):   
317        module, clazz = _encodeMapper[key]
318        encoder = dynamicallyLoadClass(module, clazz)
319        return encoder.cedaObjToAdapter(cedaObj)
320   
321    # Then is a non mapped class
322    return None
323
324def _decodeJson2Adapter(json):
325    decJson = json
326   
327    if isPrimitive(decJson) and not isinstance(decJson, str):
328        return decJson
329   
330    if not isinstance(json, dict):
331        try:
332            decJson = loads(json)
333        except:
334            return decJson
335   
336    adapter = dynamicallyLoadClass(decJson['_adapter_module'], decJson['_adapter_clazz'])()
337    setattr(adapter, '_module', decJson['_module'])
338    setattr(adapter, '_clazz', decJson['_clazz'])
339    setattr(adapter, '_adapter_module', decJson['_adapter_module'])
340    setattr(adapter, '_adapter_clazz', decJson['_adapter_clazz'])
341    setattr(adapter, 'id', decJson['id'])
342    for item in adapter.mapper.items():
343        _setDataToAdapter(adapter, item[0], decJson[item[0]])
344    return adapter
345
346def unicodeToString(text):
347    if isinstance(text, unicode):
348        return str(text)
349    return text
350
351def hasAttrNotNone(obj, name):
352    return hasattr(obj, name) and getattr(obj, name) is not None
Note: See TracBrowser for help on using the repository browser.