source: mauRepo/HPFos/trunk/src/HPFos/HPFos/moles3epb.py @ 8347

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/mauRepo/HPFos/trunk/src/HPFos/HPFos/moles3epb.py@8347
Revision 8347, 15.3 KB checked in by mnagni, 7 years ago (diff)
  • Property svn:mime-type set to text/plain
Line 
1'''
2Created on 10 Jan 2012
3
4@author: mnagni
5'''
6from libs.epb import EPB
7from libs.migration.exception.exceptions import NoDBManager
8from ea_model.ceda_metadatamodel.ceda_observationcollection.ceda_observationcollection import CEDA_ObservationCollection
9from ea_model.ceda_metadatamodel.ceda_observation.ceda_observation import CEDA_Observation
10from ea_model.iso_19115_2006_metadata_corrigendum.reference_system_information.md_identifier import MD_Identifier
11from ea_model.iso_19115_2006_metadata_corrigendum.citation_and_responsible_party_information.ci_citation import CI_Citation
12from ea_model.moles3_4.observationcollection.mo_observationcollection import MO_ObservationCollection
13from ea_model.moles3_4.observation.mo_observation import MO_Observation
14from ea_model.ceda_metadatamodel.ceda_project.ceda_project import CEDA_Project
15from sqlalchemy import Table, Column, ForeignKey, Integer, String
16from sqlalchemy.orm import mapper
17from ea_model.moles3_4.utilities.mo_responsiblepartyinfo import MO_ResponsiblePartyInfo
18from ea_model.moles3_4.utilities.mo_rolevalue import MO_RoleValue
19from sqlalchemy.orm.collections import InstrumentedList
20from ceda_guid import CedaGUID
21from sqlalchemy.orm.util import identity_key
22
23
24class Moles3EPBFactory(EPB):
25   
26    def __init__(self, dbManager):
27        self._dbManager = dbManager
28        self._initCEDA_Customization()   
29       
30
31    def _initCEDA_Customization(self):
32        self._associateCEDA_GUID()
33        #self._initSearchIndexes()       
34       
35    def _associateCEDA_GUID(self):
36        guid_table = Table('ceda_guid', self._dbManager.metadata, \
37                           Column('id', String, primary_key=True), \
38                           Column('ceda_observationcollection', Integer, ForeignKey('ceda_observationcollection.id')),
39                           Column('ceda_observation', Integer, ForeignKey('ceda_observation.id')))
40        mapper(CedaGUID, guid_table)
41        self._dbManager.metadata.create_all()
42
43    def _initSearchIndexes(self):
44        #To Be Done - CHECK IF THE COLUMN ALREADY EXISTS!
45        # We don't want sqlalchemy to know about this column so we add it externally.
46        try:
47            self._dbManager.engine.execute("alter table md_identifier add column code_search_vector tsvector")                 
48
49            # This indexes the tsvector column
50
51            self._dbManager.engine.execute("create index md_identifier_code_search_index on md_identifier using gin(code_search_vector)")
52
53            # This sets up the trigger that keeps the tsvector column up to date.
54            self._dbManager.engine.execute("create trigger md_identifier_code_search_update before update or insert on md_identifier \
55                for each row execute procedure tsvector_update_trigger('code_search_vector', 'pg_catalog.english', code)")                       
56        except Exception as e:
57            pass
58
59    def _getSession(self):
60        if self._dbManager is not None:
61            return self._dbManager.createDbSession()               
62        return None
63       
64    def createEPB(self):
65        return Moles3EPB(self._getSession())
66
67class Moles3EPB(object):
68
69    def __init__(self, session):
70        self._session = session
71       
72    def close(self):
73        return self._session.close()       
74       
75    def getObservationCollections(self):
76        """
77            Checks if a CEDA_Collection contains a given CEDA_Observation.
78            @param obs_coll_id: the CEDA_ObservationColleciton id
79            @param obs_id: the CEDA_Observation id
80            @return: True if the collection contains the given observation, False otherwise 
81        """
82        return self._session.query(CEDA_ObservationCollection).all()               
83       
84    def searchEager(self, clazz, inst_id):
85        return EPB.searchEager(clazz, inst_id)     
86     
87    def persistInstance(self, instance):
88        """
89            Adds a new migration object.
90            @param migrationObj: the migration object to add
91            @param session: an SQLAlchemy Session object. If not None the session IS NOT closed at the exit,
92            If None (default) a new Session is created from the underlying EPB and closed at the exit.
93            @return an updated, session independent, object instance reflecting the new persisted object
94        """     
95        EPB.persistInstance(instance, self._session)
96       
97     
98    def updateCedaObject(self, ceda_object, cols_to_update):
99        """
100            Update and eventually commit a CEDA Object in MOLES3 db.
101            NOTE: only the returned instance will reflect the update!
102            @param ceda_object: the CEDA object to persist
103            @param dict: a dictionary containing the columns to update for the given ceda_object
104            @param session: the external session to use. If None a new session will be open to add and commit the object and then closed at the exit. The object is committed
105            @return: the given instance with the updated attributes.
106        """
107        coll = None
108        try:
109            coll = self._session.merge(ceda_object)
110        except Exception as e:
111            print e
112        if coll != None:       
113            for k,v in cols_to_update.items():
114                if hasattr(coll, k):
115                    val = None
116                    try:
117                        val = self._session.merge(v)
118                    except Exception:
119                        val = v
120                    coll_k = getattr(coll, k)                       
121                    if type(coll_k) == list or type(coll_k) == InstrumentedList:
122                        if  type(val) == list or type(val) == InstrumentedList:
123                            coll_k.extend(val)
124                        else:
125                            if val in coll_k:
126                                break
127                            coll_k.append(val)
128                    else:
129                        setattr(coll, k, val)
130        EPB.persistInstance(coll, self._session)                                 
131
132    def getInstanceFromGUID(self, guid):
133        """
134            Returns a CEDA_Observation or a CEDA_ObservationCollection from their GUID
135            @param guid: the object guid
136            @return: the associated instance or None
137        """
138        ceda_guid = self.search(CedaGUID, guid)
139        if ceda_guid:
140            if ceda_guid.ceda_observation:
141                return self.search(CEDA_Observation, ceda_guid.ceda_observation)
142            elif ceda_guid.ceda_observationcollection:
143                return self.search(CEDA_ObservationCollection, ceda_guid.ceda_observationcollection)
144        return None
145
146    def retrieveGUIDFromInstance(self, instance):
147        """
148            Returns the CedaGUID object associated with the given instance.
149            @param instance: an instance of CEDA_Observation os CEDA_ObservationCollection 
150        """
151        if instance is None:
152            return None
153        if type(instance) == CEDA_ObservationCollection:
154            return self._session.query(CedaGUID).filter(CedaGUID.ceda_observationcollection==instance.id).first()
155        elif type(instance) == CEDA_Observation:
156            return self._session.query(CedaGUID).filter(CedaGUID.ceda_observation==instance.id).first()       
157   
158    def getObservationFromObservationCollection(self, obs_coll_id):
159        """
160            Checks if a CEDA_Collection contains a given CEDA_Observation.
161            @param obs_coll_id: the CEDA_ObservationColleciton id
162            @return: the associated CEDA_Observation 
163        """
164        return self._session.query(CEDA_ObservationCollection, CEDA_Observation).filter(CEDA_ObservationCollection.id==obs_coll_id).all()
165
166    def observationAuthor(self, obs_id):
167        """
168            Lists the CEDA_Observation author.
169            @param obs_id: the CEDA_Observation id           
170            @return: True if the collection contains the given observation, False otherwise 
171        """
172        ret = self._session.query(MO_ResponsiblePartyInfo).join(MO_Observation). \
173            filter(MO_ResponsiblePartyInfo.role == MO_RoleValue.cl_author). \
174            filter(MO_Observation.id == obs_id)       
175        return ret
176
177    def extractObservationByTitleKeywords(self, keywords):
178        """
179            Loooks for CEDA_Observation containing a specific title (observation.identifier.code)
180            @param keywords: a space separated terms string
181            @return: a tuple containing a CEDA_Observation satisfying the queryllection.idenfitier element having the title 
182        """               
183        # search_vector is a ts_vector column. To search for terms, you use the
184        # @@ operator. plainto_tsquery turns a string into a query that can be
185        # used with @@. So this adds a where clause like "WHERE search_vector
186        # @@ plaint_tsquery(<search string>)"
187        q = self._session.query(CEDA_Observation). \
188            join(MO_Observation).join(MO_ObservationCollection.identifier). \
189            filter('md_identifier.code_search_vector @@ to_tsquery(:terms)')
190        # This binds the :terms placeholder to the searchterms string. User input
191        # should always be put into queries this way to prevent SQL injection.
192        q = q.params(terms=keywords)
193        return q
194
195
196    def extractCollectionIdentifierByTitle(self, i_title):
197        """
198            Searches for an MD_Identifier from a CEDA_ObservationCollection contains a specific title (observation.identifier.code)
199            @param i_title: the CEDA_ObservationCollection.identifier.title value to search for
200            @return: a tuple containing a CEDA_ObservationCollection and the CEDA_ObservationCollection.idenfitier element having the title 
201        """
202        return self._session.query(CEDA_ObservationCollection, MD_Identifier). \
203            join(MO_ObservationCollection).join(MO_ObservationCollection.identifier). \
204            join(MD_Identifier.authority).filter(CI_Citation.title.like('%' + i_title + '%'))
205
206    def extractObservationsForProject(self, project):
207        """
208            Searches for the CEDA_Observation associated with a CEDA_Project
209            @param project: a CEDA_Project instance
210            @return: a tuple containing the associated CEDA_Observation 
211        """
212        return self._session.query(CEDA_Observation). \
213            join(CEDA_Observation, MO_Observation.inSupportOf).filter(CEDA_Project.id == project.id)
214
215    def extractProjectObservationCollections(self, project):
216        """
217            Searches for the Observation_Collections associated with a CEDA_Project
218            @param project: a CEDA_Project instance
219            @return: a tuple containing the associated CEDA_ObservationCollection 
220        """
221        mo_obs = self._session.query(MO_Observation).join(CEDA_Project).filter(CEDA_Project.id == project.id).subquery()     
222        obsers = self._session.query(CEDA_Observation).join(mo_obs, CEDA_Observation.id == mo_obs.c.id).one()
223        #print "obsers: " + str(intSession.query(CEDA_Observation).join(mo_obs, CEDA_Observation.id == mo_obs.c.id).count())
224       
225        cos = self._session.query(CEDA_ObservationCollection).all()
226        co = self._session.query(MO_ObservationCollection).join(MO_ObservationCollection.member).filter(MO_ObservationCollection.member.contains(obsers))
227       
228        observations = self._session.query(MO_ObservationCollection).join(CEDA_Observation). \
229            filter(obsers.any(CEDA_Observation.id==obsers.c.id))
230        print "observation:" + str(observations.count())
231        return observations
232
233    def search(self, clazz, inst_key):
234        """
235            Searches a required instance by id
236            @param clazz: the class type to search for
237            @param inst_key: the instance id for which the search is done. If None return all the instances of the type
238            @return the required instance(s)
239        """       
240        if inst_key:
241            ret = EPB.search(clazz, inst_key, self._session)
242        else:
243            ret = EPB.search(clazz, None, self._session).all()
244        return ret
245     
246    def _search(self, clazz, inst_id):
247        return EPB.search(clazz, inst_id, self._session)     
248     
249    def searchSelectiveLoadByInstance(self, instance, attributes):
250        id_key = identity_key(instance=instance)
251        if id_key:
252            return self.searchSelectiveLoad(id_key[0], id_key[1], attributes)
253        return None
254     
255    def searchSelectiveLoad(self, clazz, inst_id, attributes):
256        """
257            Searches a required instance by id loading selectively \
258            the specified fields. The parameter "attributes" is a single string or a list of attributes
259            owned by the instance of "clazz". Furthermore such list may contain
260            also the children of the main attributes. For example "attrs" may look
261            like
262            ['resultAccumulation', 'identifier.authority', 'resultTime.position.dateTime8601.month', \
263                      'relatedParty.party', 'result.source.function', 'permission', \
264                      'geographicExtent', 'phenomenonTime', 'keywords', 'description', \
265                      'inSupportOf.abstract', 'dataLineage']
266            the first parameter refers to the main class so is equivalent to
267            clazz.resultAccumulation
268            the second parameter is equivalent to invoke
269            clazz.identifier.authority
270            As single string "attributes" could be as well just 'identifier.authority'
271            @param clazz: the class type to search for
272            @param inst_id: the instance id for which the search is done
273            @param attributes: a single string or a list of attributes to load
274            @param session: a session to use for the query. By default a new one is created automatically at start and closed at the end
275            @return the required instance             
276        """               
277        ret = EPB.searchSelectiveLoad(clazz, inst_id, attributes, self._session)
278        return ret   
279   
280    def loadAttributes(self, instance, attributes):
281        """
282            Returns the attribute of an instance. The parameter "attributes" is a single string or a list of attributes
283            owned by the instance of "clazz". Furthermore such list may contain
284            also the children of the main attributes. For example "attrs" may look
285            like
286            ['resultAccumulation', 'identifier.authority', 'resultTime.position.dateTime8601.month', \
287                      'relatedParty.party', 'result.source.function', 'permission', \
288                      'geographicExtent', 'phenomenonTime', 'keywords', 'description', \
289                      'inSupportOf.abstract', 'dataLineage']
290            the first parameter refers to the main class so is equivalent to
291            clazz.resultAccumulation
292            the second parameter is equivalent to invoke
293            clazz.identifier.authority
294            As single string "attributes" could be as well just 'identifier.authority'
295            @param instance: an instance containing the appropriate id
296            @param attributes: the attribute value required
297            @param session: the session to use for the operation
298            @return: the given instance filled with the required attributes.                     
299        """
300        instance = self._session.merge(instance)
301        EPB.loadAttributes(instance, attributes, self._session)                 
302        return instance
303
304    def executeNative(self, sqlNative):
305        return EPB.executeNative(sqlNative, self._session) 
Note: See TracBrowser for help on using the repository browser.