source: mauRepo/MolesManager/trunk/cedaMoles/libs/epb.py @ 8599

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/mauRepo/MolesManager/trunk/cedaMoles/libs/epb.py@8599
Revision 8599, 11.6 KB checked in by mnagni, 7 years ago (diff)

Complete - # 22554: "Remove the ""FollowSymLinks"""
 http://team.ceda.ac.uk/trac/ceda/ticket/22554
Incomplete - # 22563: Circular reference detected in citest dataset
 http://team.ceda.ac.uk/trac/ceda/ticket/22563

Line 
1'''
2BSD Licence
3Copyright (c) 2012, Science & Technology Facilities Council (STFC)
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without modification,
7are permitted provided that the following conditions are met:
8
9    * Redistributions of source code must retain the above copyright notice,
10        this list of conditions and the following disclaimer.
11    * Redistributions in binary form must reproduce the above copyright notice,
12        this list of conditions and the following disclaimer in the documentation
13        and/or other materials provided with the distribution.
14    * Neither the name of the Science & Technology Facilities Council (STFC)
15        nor the names of its contributors may be used to endorse or promote
16        products derived from this software without specific prior written permission.
17
18THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
22BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29Created on 10 Jan 2012
30
31@author: Maurizio Nagni
32'''
33from sqlalchemy.orm import subqueryload
34from sqlalchemy.sql.expression import text
35from sqlalchemy.orm.util import identity_key
36from cedaMoles.MolesManager.djencoder import methodsWithDecorator
37from decimal import Decimal
38
39class EPB(object):
40
41    def __init__(self, db_manager):
42        self._db_manager = db_manager 
43
44    @classmethod
45    def buildFilter(cls, key, keyValue):
46        try:
47            return '%s = \'%s\'' % (key, keyValue)
48        except RuntimeError as er:
49            print er
50
51    @classmethod
52    def search(cls, clazz, inst_key, session):
53        """
54            Searches a required instance by id
55            @param clazz: the class type to search for
56            @param inst_key: the instance id for which the search is done. If None return a query object
57            @param session: a session to use for the query
58            @return the required instance
59        """
60        if inst_key:
61            res = session.query(clazz).get(inst_key)
62        else:
63            res = session.query(clazz)
64           
65        if res is None:
66            return None
67        return res           
68       
69
70
71    @classmethod
72    def searchEager(cls, clazz, inst_id, session):
73        """
74            Searches a required instance by id loading eagerly ALL its field. Please use carefully because \
75            it could impact the performance
76            @param clazz: the class type to search for
77            @param inst_id: the instance id for which the search is done
78            @param session: a session to use for the query   
79            @return the required instance                   
80        """
81        res = session.query(clazz).options(subqueryload('*')).get(inst_id)
82        if res is None:
83            return None
84           
85        return res
86
87    @classmethod
88    def searchSelectiveLoad(cls, clazz, inst_id, attrs, session):
89        """
90            Searches a required instance by id loading \
91            the specified fields. The parameter "attrs" is a single string or a list of attributes
92            owned by the instance of "clazz". Furthermore such list may contain
93            also the children of the main attributes. For example "attrs" may look
94            like
95            ['resultAccumulation', 'identifier.authority', 'resultTime.position.dateTime8601.month', \
96                      'relatedParty.party', 'result.source.function', 'permission', \
97                      'geographicExtent', 'phenomenonTime', 'keywords', 'description', \
98                      'inSupportOf.abstract', 'dataLineage']
99            the first parameter refers to the main class so is equivalent to
100            clazz.resultAccumulation
101            the second parameter is equivalent to invoke
102            clazz.identifier.authority
103            As single string "attrs" could be as well just 'identifier.authority'
104            @param clazz: the class type to search for
105            @param inst_id: the instance id for which the search is done
106            @param attrs: a single string or a list of attributes to load
107            @param session: a session to use for the query
108            @return the required instance             
109        """         
110        if session is None:
111            raise Exception("Session is None!")       
112        res = EPB.search(clazz, inst_id, session)
113        if res is None:
114            return None
115        EPB._drillData(res, attrs)
116        return res
117
118    @classmethod
119    def loadAttributes(cls, instance, attributes, session):
120        """
121            Loads the given instance with the required attributes.
122            The parameter "attributes" is a single string or a list of attributes
123            owned by the instance of "clazz". Furthermore such list may contain
124            also the children of the main attributes. For example "attrs" may look
125            like
126            ['resultAccumulation', 'identifier.authority', 'resultTime.position.dateTime8601.month', \
127                      'relatedParty.party', 'result.source.function', 'permission', \
128                      'geographicExtent', 'phenomenonTime.*', 'keywords', 'description', \
129                      'inSupportOf.abstract', 'dataLineage']
130            the first parameter refers to the main class so is equivalent to
131            instance.resultAccumulation
132            the second parameter is equivalent to invoke
133            instance.identifier.authority
134            As single string "attributes" could be as well just 'identifier.authority'
135            An universal marker '*' can be used to request a full loading from the attribute downward
136            It does not return anything because it does not close the session
137            @param instance: an instance containing the appropriate id
138            @param attributes: a single string or a list of attributes to load
139            @param session: the session to use for the operation                             
140        """
141        if instance is None:
142            raise Exception("Instance is None!")
143        if session is None:
144            raise Exception("Session is None!")
145        session.merge(instance)
146        EPB._drillData(instance, attributes)                   
147
148
149    @classmethod   
150    def searchOrCreate(cls, clazz, session, clazz_id = None):
151        if clazz_id is not None:
152            return EPB.search(clazz, clazz_id, session)
153        else:                       
154            return clazz()
155       
156
157    @classmethod
158    def getAllObjects(cls, clazz, session):     
159        res = session.query(clazz)
160        if res is None:
161            return None
162        return res
163
164    @classmethod
165    def rollback(cls, session):
166        """
167        Rolls back the session and one a new transaction
168        """
169        session.rollback() 
170
171    @classmethod
172    def mergeInstance(cls, instance, session):
173        """
174            Copy the state an instance onto the persistent instance with the same identifier.
175            @param instance: the migration object to add
176            @param session: an sqlalchemy Session object. If None (default) the method creates
177            @return an updated, session independant, object instance reflecting the new persisted object
178        """       
179        return session.merge(instance)
180
181    @classmethod
182    def refresh(cls, instance, session):
183        """
184            Expire and refresh the attributes on the given instance.
185            @param instance: the migration object to add
186            @param session: an sqlalchemy Session object. If None (default) the method creates
187            @return an updated, session independant, object instance reflecting the new persisted object
188        """       
189        session.refresh(instance)
190
191    @classmethod
192    def persistInstance(cls, instance, session):
193        """
194            Adds a new object.
195            @param instance: the migration object to add
196            @param session: an sqlalchemy Session object. If None (default) the method creates
197            @return an updated, session independant, object instance reflecting the new persisted object
198        """       
199        session.add(instance)
200        session.commit()
201        id_key = identity_key(instance=instance)
202        instance = EPB.search(id_key[0], id_key[1], session)
203
204    @classmethod
205    def deleteInstance(cls, instance, session, commit = False):
206        """
207            Deletes an object.
208           
209            **Parameters**
210            * `object` **instance**
211                the object to delete           
212            * `SQLAlchemy.Session` **session**
213                the session associated with the object to delete               
214            * `bool` **commit**
215                Defines if the delete operation has to be immediately committed.
216                    Default is `False`
217        """       
218        session.delete(instance)
219        if commit:
220            session.commit()
221
222    @classmethod
223    def expunge(cls, instance, session):
224        """
225            Expunges an object from the session.
226            @param instance: the migration object to expunge
227            @param session: an sqlalchemy Session object. If None (default) the method creates
228        """       
229        session.expunge(instance)
230
231    @classmethod
232    def executeNative(self, sqlNative, session):
233        return session.execute(text(sqlNative))     
234
235    def _get_session(self):     
236        return self._db_manager.createDbSession()
237
238    @classmethod
239    def _drillData(cls, obj, attrs):
240        """
241            @param obj: its an instance already living inside an SQLAlchemy session
242            @param attrs: a list of attributes owned by the obj parameter. It accepts dot separated attributes as childern of obj attributes.
243        """
244        #if is a single field wrap it in a list and recalls itself
245        if not isinstance(attrs, list):
246            EPB._drillData(obj, [attrs])
247        for item in attrs:
248            attr = item.split('.')[0] 
249            if attr == '*':
250                EPB._drillALLData(obj)         
251            elif isinstance(obj, list):
252                for element in obj:
253                    EPB._drillData(element, [item])
254            else:
255                if hasattr(obj, attr):
256                    nobj = getattr(obj, attr) 
257                    if len(attr) != len(item):
258                        EPB._drillData(nobj, [item[len(attr) + 1:]])
259
260    @classmethod
261    def _drillALLData(cls, obj):
262        if obj is None or isPrimitive(obj):
263            return
264       
265        if isinstance(obj, list):
266            for element in obj:
267                EPB._drillALLData(element)
268        else:
269            keys = dir(obj)
270            keys.extend(list(methodsWithDecorator(type(obj), "property")))
271            for key in keys:
272                try:
273                    if '_' not in key:
274                        nobj = getattr(obj, key)
275                        EPB._drillALLData(nobj)
276                except Exception as e:
277                    pass
278       
279def isPrimitive(obj):
280    # Is a simple primitive?
281    return obj is None \
282            or isinstance(obj, str) \
283            or isinstance(obj, int) \
284            or isinstance(obj, unicode) \
285            or isinstance(obj, Decimal)
Note: See TracBrowser for help on using the repository browser.