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

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

Regression bug. Did not append Observation to ObservationCollection?.member

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            @param ceda_object: the CEDA object to update
92            @param cols_to_update: a dictionary containing the columns to update for the given ceda_object and the desired value.
93            If the attribute is a list of objects the new instances are appended only if do not exist in the actual list
94            @return: the given instance with the updated attributes.
95        """
96        coll = None
97        try:
98            coll = self._session.merge(ceda_object)
99        except Exception as e:
100            print e
101        if coll != None:       
102            for k,v in cols_to_update.items():
103                if hasattr(coll, k):
104                    val = None
105                    try:
106                        val = self._session.merge(v)
107                    except Exception:
108                        val = v
109                    coll_k = getattr(coll, k)                       
110                    if type(coll_k) == list or type(coll_k) == InstrumentedList:
111                        tmp_coll = []
112                        if type(val) == list or type(val) == InstrumentedList:
113                            tmp_coll.extend(val)
114                        else:
115                            tmp_coll.append(val)
116                        for item in tmp_coll:
117                            if item not in coll_k:
118                                coll_k.append(item)
119                    else:
120                        setattr(coll, k, val)
121        self._session.commit()
122        return coll                                     
123
124    def retrieveGUIDFromInstance(self, instance):
125        """
126            Returns the CedaGUID object associated with the given instance.
127            @param instance: an instance of CEDA_Observation os CEDA_ObservationCollection 
128        """
129        if instance is None:
130            return None
131        if type(instance) == CEDA_ObservationCollection:
132            return self._session.query(CedaGUID).filter(CedaGUID.ceda_observationcollection==instance.id).first()
133        elif type(instance) == CEDA_Observation:
134            return self._session.query(CedaGUID).filter(CedaGUID.ceda_observation==instance.id).first()       
135   
136    def observationCollectionHasObservation(self, obs_coll_id, obs_id):
137        """
138            Checks if a CEDA_Collection contains a given CEDA_Observation.
139            @param obs_coll_id: the CEDA_ObservationColleciton id
140            @param obs_id: the CEDA_Observation id
141            @return: True if the collection contains the given observation, False otherwise 
142        """
143        ret = self._session.query(CEDA_ObservationCollection, CEDA_Observation).filter(CEDA_ObservationCollection.id==obs_coll_id).filter(CEDA_Observation.id==obs_id).count() > 0
144        return ret
145
146    def observationAuthor(self, obs_id):
147        """
148            Lists the CEDA_Observation author.
149            @param obs_id: the CEDA_Observation id           
150            @return: True if the collection contains the given observation, False otherwise 
151        """
152        ret = self._session.query(MO_ResponsiblePartyInfo).join(MO_Observation). \
153            filter(MO_ResponsiblePartyInfo.role == MO_RoleValue.cl_author). \
154            filter(MO_Observation.id == obs_id)       
155        return ret
156
157    def extractObservationByTitleKeywords(self, keywords):
158        """
159            Loooks for CEDA_Observation containing a specific title (observation.identifier.code)
160            @param keywords: a space separated terms string
161            @return: a tuple containing a CEDA_Observation satisfying the queryllection.idenfitier element having the title 
162        """               
163        # search_vector is a ts_vector column. To search for terms, you use the
164        # @@ operator. plainto_tsquery turns a string into a query that can be
165        # used with @@. So this adds a where clause like "WHERE search_vector
166        # @@ plaint_tsquery(<search string>)"
167        q = self._session.query(CEDA_Observation). \
168            join(MO_Observation).join(MO_ObservationCollection.identifier). \
169            filter('md_identifier.code_search_vector @@ to_tsquery(:terms)')
170        # This binds the :terms placeholder to the searchterms string. User input
171        # should always be put into queries this way to prevent SQL injection.
172        q = q.params(terms=keywords)
173        return q.all()
174
175
176    def extractCollectionIdentifierByTitle(self, i_title):
177        """
178            Searches for an MD_Identifier from a CEDA_ObservationCollection contains a specific title (observation.identifier.code)
179            @param i_title: the CEDA_ObservationCollection.identifier.title value to search for
180            @return: a tuple containing a CEDA_ObservationCollection and the CEDA_ObservationCollection.idenfitier element having the title 
181        """
182        return self._session.query(CEDA_ObservationCollection, MD_Identifier). \
183            join(MO_ObservationCollection).join(MO_ObservationCollection.identifier). \
184            join(MD_Identifier.authority).filter(CI_Citation.title.like('%' + i_title + '%'))
185
186    def extractObservationsForProject(self, project):
187        """
188            Searches for the CEDA_Observation associated with a CEDA_Project
189            @param project: a CEDA_Project instance
190            @return: a tuple containing the associated CEDA_Observation 
191        """
192        return self._session.query(CEDA_Observation). \
193            join(CEDA_Observation, MO_Observation.inSupportOf).filter(CEDA_Project.id == project.id)
194
195    def extractProjectObservationCollections(self, project):
196        """
197            Searches for the Observation_Collections associated with a CEDA_Project
198            @param project: a CEDA_Project instance
199            @return: a tuple containing the associated CEDA_ObservationCollection 
200        """
201        mo_obs = self._session.query(MO_Observation).join(CEDA_Project).filter(CEDA_Project.id == project.id).subquery()     
202        obsers = self._session.query(CEDA_Observation).join(mo_obs, CEDA_Observation.id == mo_obs.c.id).one()
203        #print "obsers: " + str(intSession.query(CEDA_Observation).join(mo_obs, CEDA_Observation.id == mo_obs.c.id).count())
204       
205        cos = self._session.query(CEDA_ObservationCollection).all()
206        co = self._session.query(MO_ObservationCollection).join(MO_ObservationCollection.member).filter(MO_ObservationCollection.member.contains(obsers))
207       
208        observations = self._session.query(MO_ObservationCollection).join(CEDA_Observation). \
209            filter(obsers.any(CEDA_Observation.id==obsers.c.id))
210        print "observation:" + str(observations.count())
211        return observations
212
213    def search(self, clazz, inst_id):
214        ret = EPB.search(clazz, inst_id, self._session)
215        return ret
216     
217    def searchSelectiveLoad(self, clazz, inst_id, attributes): 
218        """
219            Searches a required instance by id loading selectively \
220            the specified fields. The parameter "attributes" is a single string or a list of attributes
221            owned by the instance of "clazz". Furthermore such list may contain
222            also the children of the main attributes. For example "attrs" may look
223            like
224            ['resultAccumulation', 'identifier.authority', 'resultTime.position.dateTime8601.month', \
225                      'relatedParty.party', 'result.source.function', 'permission', \
226                      'geographicExtent', 'phenomenonTime', 'keywords', 'description', \
227                      'inSupportOf.abstract', 'dataLineage']
228            the first parameter refers to the main class so is equivalent to
229            clazz.resultAccumulation
230            the second parameter is equivalent to invoke
231            clazz.identifier.authority
232            As single string "attributes" could be as well just 'identifier.authority'
233            @param clazz: the class type to search for
234            @param inst_id: the instance id for which the search is done
235            @param attributes: a single string or a list of attributes to load
236            @param session: a session to use for the query. By default a new one is created automatically at start and closed at the end
237            @return the required instance             
238        """               
239        ret = EPB.searchSelectiveLoad(clazz, inst_id, attributes, self._session)
240        return ret   
241   
242    def loadAttributes(self, instance, attributes):
243        """
244            Returns the attribute of an instance. The parameter "attributes" is a single string or a list of attributes
245            owned by the instance of "clazz". Furthermore such list may contain
246            also the children of the main attributes. For example "attrs" may look
247            like
248            ['resultAccumulation', 'identifier.authority', 'resultTime.position.dateTime8601.month', \
249                      'relatedParty.party', 'result.source.function', 'permission', \
250                      'geographicExtent', 'phenomenonTime', 'keywords', 'description', \
251                      'inSupportOf.abstract', 'dataLineage']
252            the first parameter refers to the main class so is equivalent to
253            clazz.resultAccumulation
254            the second parameter is equivalent to invoke
255            clazz.identifier.authority
256            As single string "attributes" could be as well just 'identifier.authority'
257            @param instance: an instance containing the appropriate id
258            @param attributes: the attribute value required
259            @param session: the session to use for the operation
260            @return: the given instance filled with the required attributes.                     
261        """
262        instance = self._session.merge(instance)
263        EPB.loadAttributes(instance, attributes, self._session)                 
264        return instance
265
266    def executeNative(self, sqlNative):
267        return EPB.executeNative(sqlNative, self._session) 
Note: See TracBrowser for help on using the repository browser.