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

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

Implemented the core cedaObject_to_adapter.
At this moment only MO_ResponsiblePartyInfo and MO_party are fully implemented.
Many unittest have been added to the implementation, however it should still be tested against the database

Added a Sphinx documentation folder

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