source: mauRepo/MolesManager/trunk/src/MolesManager/moles3epb.py @ 8339

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/mauRepo/MolesManager/trunk/src/MolesManager/moles3epb.py@8339
Revision 8339, 13.7 KB checked in by mnagni, 7 years ago (diff)

Fixed the /search

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