source: mauRepo/HPFos/trunk/hpfos/HPFos/moles3epb.py @ 8515

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/mauRepo/HPFos/trunk/hpfos/HPFos/moles3epb.py@8515
Revision 8515, 11.5 KB checked in by mnagni, 9 years ago (diff)

Incomplete - # 22534: Add versiojn number to the gui page
 http://team.ceda.ac.uk/trac/ceda/ticket/22534
Corrects import mistakes

  • Property svn:mime-type set to text/plain
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 hpfos.libs.epb import EPB
34from ea_model.ceda_metadatamodel.ceda_observationcollection.ceda_observationcollection import CEDA_ObservationCollection
35from ea_model.ceda_metadatamodel.ceda_observation.ceda_observation import CEDA_Observation
36from ea_model.moles3_4.observationcollection.mo_observationcollection import MO_ObservationCollection
37from ea_model.moles3_4.observation.mo_observation import MO_Observation
38from sqlalchemy import Table, Column, ForeignKey, Integer, String
39from sqlalchemy.orm import mapper
40from hpfos.HPFos.ceda_guid import CedaGUID
41from sqlalchemy.orm.util import identity_key
42from ea_model.iso_19115_2006_metadata_corrigendum.extent_information.ex_geographicboundingbox import EX_GeographicBoundingBox
43
44import logging
45from logging import StreamHandler
46from hpfos.libs.postgisutil import create_st_setSRID, unifyGeometries,\
47    intersectGeometries
48
49class Moles3EPBFactory(EPB):
50   
51    def __init__(self, dbManager):
52        self._dbManager = dbManager
53        self._initCEDA_Customization()   
54       
55
56    def _initCEDA_Customization(self):
57        self._associateCEDA_GUID()
58        #self._initSearchIndexes()       
59       
60    def _associateCEDA_GUID(self):
61        guid_table = Table('ceda_guid', self._dbManager.metadata, \
62                           Column('id', String, primary_key=True), \
63                           Column('ceda_observationcollection', Integer, ForeignKey('ceda_observationcollection.id')),
64                           Column('ceda_observation', Integer, ForeignKey('ceda_observation.id')))
65        mapper(CedaGUID, guid_table)
66        self._dbManager.metadata.create_all()
67
68    def _getSession(self):
69        if self._dbManager is not None:
70            return self._dbManager.createDbSession()               
71        return None
72       
73    def createEPB(self):
74        return Moles3EPB(self._getSession())
75
76class Moles3EPB(object):
77
78    def __init__(self, session):
79        self._session = session
80        self.logging = logging.getLogger('Moles3EPB')
81        self.logging.addHandler(StreamHandler())
82        self.logging.setLevel(logging.INFO)
83       
84    def close(self):
85        return self._session.close()                         
86       
87    def getObservationCollections(self, bbox = None, keywords = '*'):
88        """
89            Returns the stored CEDA_ObservationCollection eventually filtering them against a postgis goemetry
90            @param bbox: a postgis geometry
91            @return: a list of CEDA_ObservationCollections or None if empty 
92        """
93       
94        collections = self._session.query(CEDA_ObservationCollection)
95        res = []
96        #collections = self._extract_observation_collection_by_title_keywords(keywords)
97        if collections is None:
98            return res   
99        if bbox == None:
100            return collections.all()
101       
102
103        for collection in collections:
104            if len(collection.geographicExtent) > 0:
105                collection_geometry = getGeograpicExtentGeometry(collection.geographicExtent[0])
106                if collection_geometry is not None \
107                        and intersectGeometries(bbox, collection_geometry, self):
108                    res.append(self.searchSelectiveLoad(CEDA_ObservationCollection, \
109                            collection.id, ['geographicExtent.*']))
110        return res
111
112    def retrieveGUIDFromInstance(self, instance):
113        """
114            Returns the CedaGUID object associated with the given instance.
115            @param instance: an instance of CEDA_Observation os CEDA_ObservationCollection 
116        """
117        if instance is None:
118            return None
119        if type(instance) == CEDA_ObservationCollection:
120            return self._session.query(CedaGUID).filter(CedaGUID.ceda_observationcollection==instance.id).first()
121        elif type(instance) == CEDA_Observation:
122            return self._session.query(CedaGUID).filter(CedaGUID.ceda_observation==instance.id).first()           
123
124    def getInstanceFromGUID(self, guid):
125        """
126            Returns a CEDA_Observation or a CEDA_ObservationCollection from their GUID
127            @param guid: the object guid
128            @return: the associated instance or None
129        """
130        ceda_guid = self.search(CedaGUID, guid)
131        if ceda_guid:
132            if ceda_guid.ceda_observation:
133                return self.search(CEDA_Observation, ceda_guid.ceda_observation)
134            elif ceda_guid.ceda_observationcollection:
135                return self.search(CEDA_ObservationCollection, ceda_guid.ceda_observationcollection)
136        return None
137
138    def _extract_observation_collection_by_title_keywords(self, keywords):
139        """
140            Loooks for CEDA_ObservationCollection containing a specific title (observationCollection.identifier.code)
141            @param keywords: a space separated terms string 
142        """               
143        # search_vector is a ts_vector column. To search for terms, you use the
144        # @@ operator. plainto_tsquery turns a string into a query that can be
145        # used with @@. So this adds a where clause like "WHERE search_vector
146        # @@ plaint_tsquery(<search string>)"
147        q = self._session.query(CEDA_ObservationCollection). \
148            join(MO_ObservationCollection).join(MO_Observation.identifier). \
149            filter('md_identifier.code_search_vector @@ to_tsquery(:terms)')
150        # This binds the :terms placeholder to the searchterms string. User input
151        # should always be put into queries this way to prevent SQL injection.
152        q = q.params(terms=keywords)
153
154    def search(self, clazz, inst_key):
155        """
156            Searches a required instance by id
157            @param clazz: the class type to search for
158            @param inst_key: the instance id for which the search is done. If None return all the instances of the type
159            @return the required instance(s)
160        """       
161        if inst_key:
162            ret = EPB.search(clazz, inst_key, self._session)
163        else:
164            ret = EPB.search(clazz, None, self._session).all()
165        return ret
166     
167    def _search(self, clazz, inst_id):
168        return EPB.search(clazz, inst_id, self._session)     
169     
170    def searchSelectiveLoadByInstance(self, instance, attributes):
171        id_key = identity_key(instance=instance)
172        if id_key:
173            return self.searchSelectiveLoad(id_key[0], id_key[1], attributes)
174        return None
175     
176    def searchSelectiveLoad(self, clazz, inst_id, attributes):
177        """
178            Searches a required instance by id loading selectively \
179            the specified fields. The parameter "attributes" is a single string or a list of attributes
180            owned by the instance of "clazz". Furthermore such list may contain
181            also the children of the main attributes. For example "attrs" may look
182            like
183            ['resultAccumulation', 'identifier.authority', 'resultTime.position.dateTime8601.month', \
184                      'relatedParty.party', 'result.source.function', 'permission', \
185                      'geographicExtent', 'phenomenonTime', 'keywords', 'description', \
186                      'inSupportOf.abstract', 'dataLineage']
187            the first parameter refers to the main class so is equivalent to
188            clazz.resultAccumulation
189            the second parameter is equivalent to invoke
190            clazz.identifier.authority
191            As single string "attributes" could be as well just 'identifier.authority'
192            @param clazz: the class type to search for
193            @param inst_id: the instance id for which the search is done
194            @param attributes: a single string or a list of attributes to load
195            @param session: a session to use for the query. By default a new one is created automatically at start and closed at the end
196            @return the required instance             
197        """               
198        ret = EPB.searchSelectiveLoad(clazz, inst_id, attributes, self._session)
199        return ret   
200   
201    def loadAttributes(self, instance, attributes):
202        """
203            Returns the attribute of an instance. The parameter "attributes" is a single string or a list of attributes
204            owned by the instance of "clazz". Furthermore such list may contain
205            also the children of the main attributes. For example "attrs" may look
206            like
207            ['resultAccumulation', 'identifier.authority', 'resultTime.position.dateTime8601.month', \
208                      'relatedParty.party', 'result.source.function', 'permission', \
209                      'geographicExtent', 'phenomenonTime', 'keywords', 'description', \
210                      'inSupportOf.abstract', 'dataLineage']
211            the first parameter refers to the main class so is equivalent to
212            clazz.resultAccumulation
213            the second parameter is equivalent to invoke
214            clazz.identifier.authority
215            As single string "attributes" could be as well just 'identifier.authority'
216            @param instance: an instance containing the appropriate id
217            @param attributes: the attribute value required
218            @param session: the session to use for the operation
219            @return: the given instance filled with the required attributes.                     
220        """
221        instance = self._session.merge(instance)
222        EPB.loadAttributes(instance, attributes, self._session)                 
223        return instance
224
225    def executeNative(self, sqlNative):
226        return EPB.executeNative(sqlNative, self._session) 
227   
228def getGeograpicExtentGeometry(ge):
229    '''
230        Creates the appropriate postgis geometry from a EX_GeographicExtent
231        @param ge: an EX_GeographicExtent instance
232        @return: a postgix text geometry
233    '''
234    if isinstance(ge, EX_GeographicBoundingBox):
235        return create_st_setSRID(ge.westBoundLongitude, ge.southBoundLatitude, \
236                       ge.eastBoundLongitude, ge.northBoundLatitude)
237    return None
Note: See TracBrowser for help on using the repository browser.