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

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

Incomplete - # 22534: Add versiojn number to the gui page
 http://team.ceda.ac.uk/trac/ceda/ticket/22534
New classes for the moles2gui library

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