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

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

Complete - # 22528: Migration of FAtCat Open Search link for HPFeld
 http://team.ceda.ac.uk/trac/ceda/ticket/22528
Adds a ceda_timestamp column to the tm_instant table to simplify the search-by-time in cedamoles.
The code has been changed consequently to use the new parameter.

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