source: mauRepo/MolesManager/trunk/cedaMoles/MolesManager/views/moles2gui/__init__.py @ 8591

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

Corrects the GeographicExtent? (previosulsly the python's Decimal type was wrongly managed)

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