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

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

Incomplete - # 22534: Add versiojn number to the gui page
 http://team.ceda.ac.uk/trac/ceda/ticket/22534
Partially refactored the EPB structure

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):
205        """
206            Deletes a new object.
207            @param instance: the migration object to be deleted
208            @param session: an sqlalchemy Session object. If None (default) the method creates
209        """       
210        session.delete(instance)
211        session.commit()
212
213    @classmethod
214    def expunge(cls, instance, session):
215        """
216            Expunges an object from the session.
217            @param instance: the migration object to expunge
218            @param session: an sqlalchemy Session object. If None (default) the method creates
219        """       
220        session.expunge(instance)
221
222    @classmethod
223    def executeNative(self, sqlNative, session):
224        return session.execute(text(sqlNative))     
225
226    def _get_session(self):     
227        return self._db_manager.createDbSession()
228
229    @classmethod
230    def _drillData(cls, obj, attrs):
231        """
232            @param obj: its an instance already living inside an SQLAlchemy session
233            @param attrs: a list of attributes owned by the obj parameter. It accepts dot separated attributes as childern of obj attributes.
234        """
235        #if is a single field wrap it in a list and recalls itself
236        if not isinstance(attrs, list):
237            EPB._drillData(obj, [attrs])
238        for item in attrs:
239            attr = item.split('.')[0] 
240            if attr == '*':
241                EPB._drillALLData(obj)         
242            elif isinstance(obj, list):
243                for element in obj:
244                    EPB._drillData(element, [item])
245            else:
246                if hasattr(obj, attr):
247                    nobj = getattr(obj, attr) 
248                    if len(attr) != len(item):
249                        EPB._drillData(nobj, [item[len(attr) + 1:]])
250
251    @classmethod
252    def _drillALLData(cls, obj):
253        if obj is None or isPrimitive(obj):
254            return
255       
256        if isinstance(obj, list):
257            for element in obj:
258                EPB._drillALLData(element)
259        else:
260            keys = dir(obj)
261            keys.extend(list(methodsWithDecorator(type(obj), "property")))
262            for key in keys:
263                try:
264                    if '_' not in key:
265                        nobj = getattr(obj, key)
266                        EPB._drillALLData(nobj)
267                except Exception as e:
268                    pass
269       
270def isPrimitive(obj):
271    # Is a simple primitive?
272    return obj is None \
273            or isinstance(obj, str) \
274            or isinstance(obj, int) \
275            or isinstance(obj, unicode)
Note: See TracBrowser for help on using the repository browser.