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

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

Incomplete - # 22528: Migration of FAtCat Open Search link for HPFeld
 http://team.ceda.ac.uk/trac/ceda/ticket/22528
Incomplete - # 22534: Add versiojn number to the gui page
 http://team.ceda.ac.uk/trac/ceda/ticket/22534

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
37
38class EPB(object):
39
40    def __init__(self, db_manager):
41        self._db_manager = db_manager 
42
43    @classmethod
44    def buildFilter(cls, key, keyValue):
45        try:
46            return '%s = \'%s\'' % (key, keyValue)
47        except RuntimeError as er:
48            print er
49
50    @classmethod
51    def search(cls, clazz, inst_key, session):
52        """
53            Searches a required instance by id
54            @param clazz: the class type to search for
55            @param inst_key: the instance id for which the search is done. If None return a query object
56            @param session: a session to use for the query
57            @return the required instance
58        """
59        if inst_key:
60            res = session.query(clazz).get(inst_key)
61        else:
62            res = session.query(clazz)
63           
64        if res is None:
65            return None
66        return res           
67       
68
69
70    @classmethod
71    def searchEager(cls, clazz, inst_id, session):
72        """
73            Searches a required instance by id loading eagerly ALL its field. Please use carefully because \
74            it could impact the performance
75            @param clazz: the class type to search for
76            @param inst_id: the instance id for which the search is done
77            @param session: a session to use for the query   
78            @return the required instance                   
79        """
80        res = session.query(clazz).options(subqueryload('*')).get(inst_id)
81        if res is None:
82            return None
83           
84        return res
85
86    @classmethod
87    def searchSelectiveLoad(cls, clazz, inst_id, attrs, session):
88        """
89            Searches a required instance by id loading \
90            the specified fields. The parameter "attrs" is a single string or a list of attributes
91            owned by the instance of "clazz". Furthermore such list may contain
92            also the children of the main attributes. For example "attrs" may look
93            like
94            ['resultAccumulation', 'identifier.authority', 'resultTime.position.dateTime8601.month', \
95                      'relatedParty.party', 'result.source.function', 'permission', \
96                      'geographicExtent', 'phenomenonTime', 'keywords', 'description', \
97                      'inSupportOf.abstract', 'dataLineage']
98            the first parameter refers to the main class so is equivalent to
99            clazz.resultAccumulation
100            the second parameter is equivalent to invoke
101            clazz.identifier.authority
102            As single string "attrs" could be as well just 'identifier.authority'
103            @param clazz: the class type to search for
104            @param inst_id: the instance id for which the search is done
105            @param attrs: a single string or a list of attributes to load
106            @param session: a session to use for the query
107            @return the required instance             
108        """         
109        if session is None:
110            raise Exception("Session is None!")       
111        res = EPB.search(clazz, inst_id, session)
112        if res is None:
113            return None
114        EPB._drillData(res, attrs)
115        return res
116
117    @classmethod
118    def loadAttributes(cls, instance, attributes, session):
119        """
120            Loads the given instance with the required attributes.
121            The parameter "attributes" is a single string or a list of attributes
122            owned by the instance of "clazz". Furthermore such list may contain
123            also the children of the main attributes. For example "attrs" may look
124            like
125            ['resultAccumulation', 'identifier.authority', 'resultTime.position.dateTime8601.month', \
126                      'relatedParty.party', 'result.source.function', 'permission', \
127                      'geographicExtent', 'phenomenonTime.*', 'keywords', 'description', \
128                      'inSupportOf.abstract', 'dataLineage']
129            the first parameter refers to the main class so is equivalent to
130            instance.resultAccumulation
131            the second parameter is equivalent to invoke
132            instance.identifier.authority
133            As single string "attributes" could be as well just 'identifier.authority'
134            An universal marker '*' can be used to request a full loading from the attribute downward
135            It does not return anything because it does not close the session
136            @param instance: an instance containing the appropriate id
137            @param attributes: a single string or a list of attributes to load
138            @param session: the session to use for the operation                             
139        """
140        if instance is None:
141            raise Exception("Instance is None!")
142        if session is None:
143            raise Exception("Session is None!")
144        session.merge(instance)
145        EPB._drillData(instance, attributes)                   
146
147
148    @classmethod   
149    def searchOrCreate(cls, clazz, session, clazz_id = None):
150        if clazz_id is not None:
151            return EPB.search(clazz, clazz_id, session)
152        else:                       
153            return clazz()
154       
155
156    @classmethod
157    def getAllObjects(cls, clazz, session):     
158        res = session.query(clazz)
159        if res is None:
160            return None
161        return res
162
163    @classmethod
164    def rollback(cls, session):
165        """
166        Rolls back the session and one a new transaction
167        """
168        session.rollback() 
169
170    @classmethod
171    def mergeInstance(cls, instance, session):
172        """
173            Copy the state an instance onto the persistent instance with the same identifier.
174            @param instance: the migration object to add
175            @param session: an sqlalchemy Session object. If None (default) the method creates
176            @return an updated, session independant, object instance reflecting the new persisted object
177        """       
178        return session.merge(instance)
179
180    @classmethod
181    def refresh(cls, instance, session):
182        """
183            Expire and refresh the attributes on the given instance.
184            @param instance: the migration object to add
185            @param session: an sqlalchemy Session object. If None (default) the method creates
186            @return an updated, session independant, object instance reflecting the new persisted object
187        """       
188        session.refresh(instance)
189
190    @classmethod
191    def persistInstance(cls, instance, session):
192        """
193            Adds a new object.
194            @param instance: the migration object to add
195            @param session: an sqlalchemy Session object. If None (default) the method creates
196            @return an updated, session independant, object instance reflecting the new persisted object
197        """       
198        session.add(instance)
199        session.commit()
200        id_key = identity_key(instance=instance)
201        instance = EPB.search(id_key[0], id_key[1], session)
202
203    @classmethod
204    def deleteInstance(cls, instance, session, commit = False):
205        """
206            Deletes an object.
207           
208            **Parameters**
209            * `object` **instance**
210                the object to delete           
211            * `SQLAlchemy.Session` **session**
212                the session associated with the object to delete               
213            * `bool` **commit**
214                Defines if the delete operation has to be immediately committed.
215                    Default is `False`
216        """       
217        session.delete(instance)
218        if commit:
219            session.commit()
220
221    @classmethod
222    def expunge(cls, instance, session):
223        """
224            Expunges an object from the session.
225            @param instance: the migration object to expunge
226            @param session: an sqlalchemy Session object. If None (default) the method creates
227        """       
228        session.expunge(instance)
229
230    @classmethod
231    def executeNative(self, sqlNative, session):
232        return session.execute(text(sqlNative))     
233
234    def _get_session(self):     
235        return self._db_manager.createDbSession()
236
237    @classmethod
238    def _drillData(cls, obj, attrs):
239        """
240            @param obj: its an instance already living inside an SQLAlchemy session
241            @param attrs: a list of attributes owned by the obj parameter. It accepts dot separated attributes as childern of obj attributes.
242        """
243        #if is a single field wrap it in a list and recalls itself
244        if not isinstance(attrs, list):
245            EPB._drillData(obj, [attrs])
246        for item in attrs:
247            attr = item.split('.')[0] 
248            if attr == '*':
249                EPB._drillALLData(obj)         
250            elif isinstance(obj, list):
251                for element in obj:
252                    EPB._drillData(element, [item])
253            else:
254                if hasattr(obj, attr):
255                    nobj = getattr(obj, attr) 
256                    if len(attr) != len(item):
257                        EPB._drillData(nobj, [item[len(attr) + 1:]])
258
259    @classmethod
260    def _drillALLData(cls, obj):
261        if obj is None or isPrimitive(obj):
262            return
263       
264        if isinstance(obj, list):
265            for element in obj:
266                EPB._drillALLData(element)
267        else:
268            keys = dir(obj)
269            keys.extend(list(methodsWithDecorator(type(obj), "property")))
270            for key in keys:
271                try:
272                    if '_' not in key:
273                        nobj = getattr(obj, key)
274                        EPB._drillALLData(nobj)
275                except Exception as e:
276                    pass
277       
278def isPrimitive(obj):
279    # Is a simple primitive?
280    return obj is None \
281            or isinstance(obj, str) \
282            or isinstance(obj, int) \
283            or isinstance(obj, unicode)
Note: See TracBrowser for help on using the repository browser.