source: mauRepo/HPFos/trunk/hpfos/HPFos/osImpl/myimpl.py @ 8557

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/mauRepo/HPFos/trunk/hpfos/HPFos/osImpl/myimpl.py@8557
Revision 8557, 21.7 KB checked in by mnagni, 7 years ago (diff)

Incomplete - # 22551: List resources / display single file
 http://team.ceda.ac.uk/trac/ceda/ticket/22551

  • 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 5 May 2012
30
31@author: Maurizio Nagni
32'''
33from datetime import datetime, date
34from hpfos.libs.postgisutil import create_st_setSRID
35from ceda_markup.opensearch.osquery import OSQuery
36from ceda_markup.atom.atom import createID, createUpdated, \
37    createPublished, createEntry
38from ceda_markup.atom.info import createTitle, HTML_TYPE, createContent
39from ceda_markup.dc.dc import createDate
40from ceda_markup.gml.gml import createBeginPosition, createEndPosition, \
41    createTimePeriod, createValidTime, createEnvelope, createLowerCorner, \
42    createUpperCorner, createPosList, createLinearRing, createExterior, createPolygon, \
43    GML_NAMESPACE
44from ceda_markup.georss.georss import createWhere
45from ceda_markup.atom.link import REL_SEARCH, REL_ALTERNATE, createLink
46from ceda_markup.opensearch import filter_results, COUNT_DEFAULT, \
47    START_INDEX_DEFAULT, START_PAGE_DEFAULT, create_autodiscovery_link
48from ceda_markup.opensearch.template.osresponse import Result, Subresult
49from ceda_markup.opensearch.template.atom import OSAtomResponse
50from ceda_markup.opensearch.template.html import OSHTMLResponse
51from ea_model.iso_19108_2006_temporal_schema.temporal_objects.tm_instant import TM_Instant
52from ceda_markup.georss import create_where_from_postgis
53from ea_model.ceda_metadatamodel.ceda_observationcollection.ceda_observationcollection \
54    import CEDA_ObservationCollection
55from ea_model.ceda_metadatamodel.ceda_observation.ceda_observation \
56    import CEDA_Observation
57from ea_model.ceda_metadatamodel.ceda_result.ceda_result import CEDA_Result
58from ceda_markup.opensearch.os_param import OSParam
59from hpfos import __version__, __revision__
60from ceda_markup.atom.atom import ATOM_NAMESPACE
61from hpfos.HPFos.osImpl.commons import get_document, get_xml_document, \
62    from_pt_to_string, tm_InstantToDatetime
63from ceda_markup.opensearch.os_request import OS_NAMESPACE
64from xml.etree.ElementTree import _ElementInterface
65from ea_model.iso_19115_2006_metadata_corrigendum.\
66    extent_information.ex_geographicboundingbox import EX_GeographicBoundingBox
67
68GUID = 'guid'
69FILE_ID = 'guid'
70COLLECTION = 'collection'
71OBSERVATION = 'observation'
72RESULT = 'result'
73BBOX = 'bbox'
74DUMMY_GUID = 'dummy_guid'
75
76FATCAT_HOST = 'cidev1.jc.rl.ac.uk'
77FATCAT_ROOT_PATH = 'fatcatOS'
78PROXY_URL = 'http://wwwcache.rl.ac.uk:8080'
79
80CEDA_TITLE = 'ceda_title'
81HPFOS_VERSION = __version__
82HPFOS_REVISION = __revision__
83
84HPFOS_ID = ''
85if HPFOS_REVISION != 'REVISION':
86    HPFOS_ID = '(v. %s rev. %s)' % (HPFOS_VERSION, HPFOS_REVISION)
87else:
88    HPFOS_ID = '(v. %s rev. %s)' % (HPFOS_VERSION, 'unknown')
89   
90HPFOS_TITLE = 'Discovery feed for Search Services %s' % (HPFOS_ID)
91
92
93
94class MyOSAtomResponse(OSAtomResponse):
95    '''
96    classdocs
97    '''
98
99    def __init__(self):
100        #query_params = {"q": "searchTerms", "pw":"startPage"}
101        super(MyOSAtomResponse, self).__init__()
102       
103        '''
104        Constructor
105        '''
106   
107    def _digest_fatcat_atom(self, context, results):
108        count, start_index, start_page = self._importCountAndPage(context)
109        entries = results.findall('{%s}entry' % (ATOM_NAMESPACE))
110        subresults = []
111        for entry in entries:
112            iid = entry.find('{%s}id' % (ATOM_NAMESPACE)).text.strip()
113            ititle = entry.find('{%s}title' % (ATOM_NAMESPACE)).text.strip()
114            kwargs = {}
115                         
116            element = entry.find('.//{%s}beginPosition' % (GML_NAMESPACE))
117            if element is not None:
118                kwargs['beginPosition'] = element.text.strip()
119                element = None
120           
121            element = entry.find('.//{%s}endPosition' % (GML_NAMESPACE))
122            if element is not None:
123                kwargs['endPosition'] = element.text.strip()
124                element = None           
125           
126            geometry = entry.find('.//{%s}posList' % (GML_NAMESPACE))
127            if geometry is not None:
128                kwargs['geometry'] = 'POLYGON((%s))' % geometry.text.strip()
129            else:
130                geometry = entry.find('.//{%s}Envelope' % (GML_NAMESPACE))
131                if geometry is not None:
132                    lc = geometry.find('./{%s}lowerCorner' % (GML_NAMESPACE)).text.strip()
133                    uc = geometry.find('./{%s}upperCorner' % (GML_NAMESPACE)).text.strip()
134                    kwargs['geometry'] = _create_box2d(lc, uc)
135
136
137            kwargs['enclosure'] = entry.findall("./{%s}link" % (ATOM_NAMESPACE))
138            #This can be activated on python >= 2.7
139            #kwargs['enclosure'] = entry.findall("./{%s}link[@rel='enclosure']" % (ATOM_NAMESPACE))                         
140            kwargs['description'] = 'no description'
141            subresults.append(Subresult(iid, ititle,
142                                        datetime.now().isoformat(), **kwargs))
143       
144        tot_results = int(results.find('{%s}totalResults'
145                                       % (OS_NAMESPACE)).text.replace('\n','').strip())
146        return Result(count, start_index, start_page, tot_results,
147                      subresult = subresults, title=HPFOS_TITLE)       
148       
149    def _check_text_filter(self, text, title, description):
150        words = text.split()
151        for word in words:
152            if word in title \
153                or word in description:
154                    return True
155        return False
156
157    def _check_start_filter(self, startTime, phenomenonTime):
158        pt = phenomenonTime
159        if isinstance(phenomenonTime, list):
160            if len(phenomenonTime) == 0:
161                return True
162            pt = phenomenonTime[0]
163         
164        if isinstance(pt, TM_Instant):
165            pt = tm_InstantToDatetime(pt)   
166        elif hasattr(pt, 'begin'):
167            pt = tm_InstantToDatetime(pt.begin)
168           
169        if isinstance(pt, datetime) and isinstance(startTime, date):
170            return pt.date() > startTime
171        if isinstance(pt, date) and isinstance(startTime, datetime):
172            return pt > startTime.date()   
173        return pt > startTime     
174       
175    def _check_end_filter(self, endTime, phenomenonTime):
176        pt = phenomenonTime
177        if isinstance(phenomenonTime, list):
178            if len(phenomenonTime) == 0:
179                return True
180            pt = phenomenonTime[0]
181
182        if isinstance(pt, TM_Instant):
183            pt = tm_InstantToDatetime(pt)   
184        elif hasattr(pt, 'end'):
185            pt = tm_InstantToDatetime(pt.end)
186       
187        if isinstance(pt, datetime) and isinstance(endTime, date):
188            return endTime < pt.date()
189        if isinstance(pt, date) and isinstance(endTime, datetime):
190            return endTime.date() < pt             
191        return endTime < pt
192
193    def apply_query_params(self, context, results):
194        subresults = []
195        if results is None:
196            return subresults
197        for result in results:         
198            result_guid = context['moles3EPB'].retrieveGUIDFromInstance(result)
199            if result_guid is None:
200                continue
201            ititle = self._extractTitle(result)
202
203            kwargs = {}               
204            #------------------------
205            phenomenonTime = result.phenomenonTime
206            if isinstance(result, list) and len(result.phenomenonTime) > 0:
207                phenomenonTime = result.phenomenonTime[0]                   
208            kwargs['beginPosition'], kwargs['endPosition'] = \
209                from_pt_to_string(phenomenonTime)
210            #------------------------                                                               
211            kwargs['geometry'] = self._new_extract_geographic_extent(result)
212            kwargs['description'] = result.description           
213
214            item = Subresult(result_guid.id, ititle, datetime.now().isoformat(),
215                             **kwargs)               
216            subresults.append(item)
217        return subresults
218
219    def digest_search_results(self, results, context):
220        if type(results) == CEDA_ObservationCollection:
221            results = results.member
222        elif type(results) == CEDA_Observation:
223            results = results.result
224        elif type(results) == CEDA_Result:
225            results = results.result
226        elif isinstance(results, _ElementInterface):
227            return self._digest_fatcat_atom(context, results)                   
228
229        count, start_index, start_page = self._importCountAndPage(context)
230        tot_results = len(results)       
231        # This check is a trick to speed up the response when no query parameter
232        # is required (all parameters are None). In this case the results are paged first
233        # then the subresults are generated.
234        # The opposite, filter first then the subresults are generated,
235        # if any query parameter is not None
236        if context['start'] is None \
237            or context['stop'] is None \
238            or context[BBOX] is None \
239            or context['q'] is None:
240            filtered = filter_results(results, count, start_index, start_page)
241            subresults = self.apply_query_params(context, filtered)
242        else:
243            filtered = self.apply_query_params(context, results)
244            subresults = filter_results(filtered, count, start_index, start_page)
245            tot_results = len(subresults)                                             
246               
247        return Result(count, start_index, start_page, tot_results, \
248                      subresult = subresults, title=HPFOS_TITLE)
249
250    def _new_extract_geographic_extent(self, result):       
251        if not hasattr(result, 'geographicExtent') \
252            or result.geographicExtent is None \
253            or len(result.geographicExtent) == 0:
254            return None
255       
256        ge = result.geographicExtent[0]
257        if not isinstance(ge, EX_GeographicBoundingBox):
258            return
259
260        return _create_box2d('%s %s' % (ge.southBoundLatitude, ge.westBoundLongitude),
261                             '%s %s' % (ge.northBoundLatitude, ge.eastBoundLongitude))
262                                   
263
264
265
266    def _extract_geographic_extent(self, result):       
267        if not hasattr(result, 'geographicExtent') \
268            or result.geographicExtent is None \
269            or len(result.geographicExtent) == 0:
270            return None, None
271       
272        ge = result.geographicExtent[0]
273        if not isinstance(ge, EX_GeographicBoundingBox):
274            return
275       
276        #returns lowerCorner, upperCorner
277        return [ge.southBoundLatitude, ge.westBoundLongitude], \
278                                    [ge.northBoundLatitude, ge.eastBoundLongitude]
279       
280       
281    def generateEntryLinks(self, entry, atomroot, path, linkid = None):
282        entry.append(create_autodiscovery_link(atomroot, path, self.extension, \
283                                               linkid, None, rel = REL_ALTERNATE))
284        entry.append(create_autodiscovery_link(atomroot, path, self.extension, \
285                                               linkid, None, rel = REL_SEARCH))               
286
287    def generate_entries(self, atomroot, subresults, path):
288        if isinstance(subresults, list) \
289                and len(subresults) > 0 \
290                and isinstance(subresults[0], _ElementInterface):
291            for entry in subresults:
292                atomroot.append(entry)
293            return
294       
295        entries = []
296       
297        for subresult in subresults:
298            #Here could loop over results
299            atomID = createID(path + subresult.id + '/' + self.extension, root = atomroot)
300            ititle = createTitle(root = atomroot,
301                                 body = subresult.title,
302                                 itype = HTML_TYPE)
303            atomContent = createContent(root = atomroot,
304                                        body = subresult.description,
305                                        itype = HTML_TYPE)
306            atomUpdated = createUpdated(subresult.updated, root = atomroot)
307            atomPublished = createPublished('TO_BE_DONE_2011-01-21T11:05:29.511Z',
308                                            root = atomroot)           
309            entry = createEntry(atomID, ititle, atomUpdated,
310                                published=atomPublished,
311                                content=atomContent, root = atomroot)
312            #xmlentry = entry.buildElement()
313           
314           
315            beginPosition = None
316            endPosition = None
317            if hasattr(subresult, 'beginPosition') \
318                    and subresult.beginPosition is not None:
319                beginPosition = subresult.beginPosition
320            if hasattr(subresult, 'endPosition') and subresult.endPosition is not None:               
321                endPosition = subresult.endPosition
322            self._append_valid_time(subresult, entry, atomroot, beginPosition, endPosition)           
323           
324            idate = createDate(root = atomroot,
325                               body = 'TO_BE_DONE_2002-10-18T08:07:37.387Z/2012-03-29T07:12:20.735Z')       
326            entry.append(idate)
327           
328            if hasattr(subresult, 'geometry') and subresult.geometry is not None:
329                where = create_where_from_postgis(subresult.geometry, atomroot)
330                entry.append(where)
331           
332            self.generateEntryLinks(entry, atomroot, path, subresult.id)
333            if hasattr(subresult, 'enclosure') and subresult.enclosure is not None:
334                for enclosure in subresult.enclosure:
335                    if enclosure.get('rel', None) == 'enclosure':
336                        entry.append(createLink(enclosure.get('href'),
337                                                rel = 'enclosure',
338                                                root = atomroot,
339                                                itype = enclosure.get('type'),
340                                                length = enclosure.get('length')))               
341                           
342            entries.append(entry)
343
344        for entry in entries:
345            atomroot.append(entry)
346
347    def _append_where(self, geometry, entry, atomroot):
348        if geometry.startswith('BOX2D('):
349            lc, uc = geometry[6:-1].split(',')
350            lowerCorner = createLowerCorner(atomroot, values = lc)
351            upperCorner = createUpperCorner(atomroot, values = uc)
352            where_body = createEnvelope(lowerCorner, upperCorner, atomroot)
353           
354        elif geometry.startswith('POLYGON(('):
355                posList = createPosList(root = atomroot,
356                                        values = [float(val) for val
357                                                  in geometry[9:-2].
358                                                        replace(',', ' ').split()],
359                                        srsDimension = '2')
360                linearRing = createLinearRing(root = atomroot, body = posList)
361                exterior = createExterior(root = atomroot, body = linearRing)
362                where_body = createPolygon(root = atomroot, body = exterior)       
363
364        where = createWhere(root = atomroot, body = where_body)
365        entry.append(where)
366
367    def _append_valid_time(self, subresult, entry, atomroot,
368                           beginPosition, endPosition):
369        #xmlentry = entry.buildElement()
370        if beginPosition is not None:
371            beginPosition = createBeginPosition(root = atomroot,
372                                                body = subresult.beginPosition)
373        if endPosition is not None:               
374            endPosition = createEndPosition(root = atomroot,
375                                            body = subresult.endPosition)               
376        timePeriod = createTimePeriod(root = atomroot,
377                                      begin = beginPosition, end = endPosition)       
378        validTime = createValidTime(root = atomroot, body = timePeriod)
379        if beginPosition is not None or endPosition is not None:
380            entry.append(validTime)
381
382    def _importCountAndPage(self, context):       
383        count = COUNT_DEFAULT
384        start_index = START_INDEX_DEFAULT
385        start_page = START_PAGE_DEFAULT
386       
387        try:
388            count = int(context['count'])
389        except:
390            pass       
391       
392        try:
393            start_index = int(context['startIndex'])
394        except:
395            pass
396       
397        try:
398            start_page = int(context['startPage'])
399        except:
400            pass
401       
402        return count, start_index, start_page
403
404    def _extractTitle(self, cedaObj):
405        if hasattr(cedaObj, 'identifier'):
406            for ident in cedaObj.identifier:
407                if ident.authority.title == CEDA_TITLE:
408                    return ident.code 
409
410class MyOSHTMLResponse(OSHTMLResponse):
411    '''
412    classdocs
413    '''
414
415    def __init__(self):
416        '''
417        Constructor
418        '''
419        super(MyOSHTMLResponse, self).__init__()
420       
421    def generateResponse(self, result, queries, ospath, **kwargs):
422        return result + " HTML!"
423       
424class MyOSQuery(OSQuery):
425    '''
426    classdocs
427    '''
428
429    def __init__(self):
430        '''
431            Constructor
432        '''       
433        param_1 = OSParam("count", "count")
434        param_2 = OSParam("startPage", "startPage")
435        param_3 = OSParam("startIndex", "startIndex")               
436        param_4 = OSParam("q", "searchTerms")           
437        param_5 = OSParam("uid", "uid",
438                          namespace = "http://a9.com/-/opensearch/extensions/geo/1.0/")       
439        param_6 = OSParam(BBOX, BBOX,
440                          namespace = "http://a9.com/-/opensearch/extensions/geo/1.0/")       
441        param_7 = OSParam("start", "start",
442                          namespace = "http://a9.com/-/opensearch/extensions/geo/1.0/")       
443        param_8 = OSParam("stop", "stop",
444                          namespace = "http://a9.com/-/opensearch/extensions/time/1.0/")       
445        params = [param_1, param_2, param_3, param_4, param_5, param_6, param_7, param_8]
446        super(MyOSQuery, self).__init__(params)
447       
448    def do_search(self, context):
449        ibbox = None
450        if context.has_key(BBOX) and context[BBOX] is not None:
451            coords = context[BBOX].split(',')
452            try:
453                if len(coords) == 4:
454                    ibbox = create_st_setSRID(int(coords[0]),int(coords[1]),
455                                              int(coords[2]),int(coords[3]))
456            except:
457                pass
458       
459        if context.has_key(GUID) and 'FID' in context[GUID]:
460            return self._extractFatcatEntities(context[GUID])
461       
462        if not context.has_key(GUID) or context[GUID] is None:
463            return context['moles3EPB'].getObservationCollections(bbox = ibbox,
464                                                                keywords = context['q'],
465                                                                start = context['start'],
466                                                                end = context['stop'])       
467             
468        obj = context['moles3EPB'].getInstanceFromGUID(context[GUID])   
469        if obj is None:
470            return None
471        if type(obj) == CEDA_ObservationCollection:
472            return context['moles3EPB'].searchSelectiveLoadByInstance(obj, 'member')
473        elif type(obj) == CEDA_Observation:
474            for source in obj.result.source:
475                if source.function == 'search':
476                    return self._extractFatcatEntities(source.description)
477
478       
479    def _extractFatcatEntities(self, fc_resource_id):
480        path = '/%s/search/%s/atom/' % (FATCAT_ROOT_PATH, str(fc_resource_id))
481        return find_fatcat_atom_entity(host = FATCAT_HOST, path = path)
482   
483def find_fatcat_atom_entity(host = 'localhost', path = '', port = 80):   
484    source = get_document(host, path, port, proxy = PROXY_URL)
485    return get_xml_document(source)
486
487def _create_box2d(lc, uc):
488    '''
489        Creates a postgis-like BOX2D string.
490        **Parameters**
491            * lc: the box lower corner as a two floats-as-string space separated pair
492            * uc: the box upper corner as a two floats-as-string space separated pair
493        **Returns**
494            A string formatted as 'BOX2D(lc1 lc2, uc1 uc2)'
495    '''   
496    return 'BOX2D(%s, %s)' % (lc, uc)
Note: See TracBrowser for help on using the repository browser.