source: MILK/trunk/milk_server/milk_server/controllers/browse/discovery.py @ 5730

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/MILK/trunk/milk_server/milk_server/controllers/browse/discovery.py@5730
Revision 5730, 28.2 KB checked in by aharwood, 11 years ago (diff)
Line 
1'''
2Controller for the discovery search functionality
3'''
4import socket, logging
5from paste.request import parse_querystring
6from ndg.common.src.clients.ws.discovery.discoveryserviceclient import DiscoveryServiceClient
7from ndg.common.src.clients.xmldb.eXist.searchclient import SearchClient
8from ndg.common.src.clients.http.vocabserverclient import VocabServerClient as VS
9from ndg.common.src.models.vocabtermdata import VocabTermData as VTD, VocabTermItem as VTI
10from ndg.common.src.models.ndgObject import ndgObject
11from ndg.common.src.models.DIF import DIF
12from ndg.common.src.lib.mailer import mailHandler
13import ndg.common.src.lib.htmlUtilities as utils
14from milk_server.lib.base import *
15from milk_server.lib.Date import *
16from milk_server.models.DiscoveryState import DiscoveryState, constraints
17from milk_server.controllers.home import HomeController
18import browserconstants as bc
19from milk_server.lib.Utilities import getURLConstraints
20import milk_server.lib.constants as constants
21
22
23class DiscoveryController(HomeController):
24    '''
25    Provides the pylons controller for NDG discovery
26    '''
27
28    def __setup(self):
29        '''
30        Common setup for controller methods
31
32        '''
33       
34        self.cf=request.environ['ndgConfig']
35        self.inputs=dict(parse_querystring(request.environ))
36       
37        self.message=''
38        c.inputErrors = {}    # dict to store error messages
39       
40        c.discoveryUrl = h.url_for('discovery')
41   
42    def index(self):
43        '''
44        Main entry point for doing discovery searches
45        '''
46       
47        self.__setup()
48       
49        # if inputs are not set: if the discovery mode is enabled, display
50        # the search screen, otherwise redirect to the default home page according
51        # to what milk mode is set (i.e. editor/browse)
52        if not self.inputs or 'ClearForm' in self.inputs:
53           
54            if g.discoveryEnabled:
55                return self.__advancedPrompt()
56            else:
57                logging.info("Discovery mode not enabled - redirect to default")
58                return h.redirect_to(h.url_for('default'))
59       
60        self.__getInputs()
61
62        if 'orderBy' not in self.inputs:
63           self.inputs['orderBy'] = 'title'
64           
65        c.orderByList = h.options_for_select(g.orderByList, self.inputs['orderBy'])
66       
67        if 'orderDirection' not in self.inputs:
68            self.inputs['orderDirection'] = constants.ORDER_BY_DIRECTION[0]
69        c.orderDirection = h.options_for_select(constants.ORDER_BY_DIRECTION,
70                                                self.inputs['orderDirection']) 
71       
72        # if any errors are found, return user to search page
73        if c.inputErrors:
74            return self.__advancedPrompt()
75
76        searchString = self.inputs['searchString']
77                   
78        if 'vocabTerm' in self.inputs:
79            searchString += " %s" %self.inputs['vocabTerm']
80           
81        # users can return to search page to refine the search inputs; in this case
82        # they will have a 'constrained' input
83        self.constraints = self.__buildconstraints(self.dateRange, self.bbox, self.scope,
84                                                   searchString, self.inputs['geoSearchType'])
85        if 'constrained' in self.inputs: 
86            return self.__advancedPrompt(searchConstraints = self.constraints)
87
88        # ok, now go do the search
89        try:
90
91            return self.__runSearch(searchString, self.inputs['textTarget'],
92                                    self.inputs['start'], self.inputs['howmany'], 
93                                    orderBy = self.inputs['orderBy'], 
94                                    orderDirection = self.inputs['orderDirection'],
95                                    scope = self.scope, dateRange = self.dateRange, 
96                                    bbox = self.bbox, geoSearch=self.inputs['geoSearchType'])
97        except Exception, e:
98            if g.debugModeOn == 'True':
99                raise e
100            else:
101                c.xml='Unexpected error: %s'%(str(e))
102                return render('error')
103
104
105    def __getInputs(self):
106        '''
107        Retrieve the user inputs and set defaults.  Values are stored in the
108        self.inputs dict
109        '''
110        logging.debug("Getting user inputs")
111       
112        # restore contraints from input, if set
113        if 'constraints' in self.inputs:
114            constraints = getURLConstraints(self.inputs['constraints'])
115            del self.inputs['constraints']
116            # NB, be careful not to overwrite newly passed in info
117            for key, val in constraints.items():
118                if key not in self.inputs:
119                    self.inputs[key] = val
120                   
121        if 'vocabTerm' in self.inputs and 'searchString' not in self.inputs:
122            self.inputs['searchString'] = ""
123           
124        # see if this is a discovery search or a more complicated search
125        if 'searchTarget' not in self.inputs: 
126            self.inputs['searchTarget']='Discovery'
127       
128        # set default for table paging, if not already set
129        # NB, url arguments need converting back to ints
130        if 'start' not in self.inputs:
131            self.inputs['start'] = 1
132        else:
133            self.inputs['start'] = int(self.inputs['start'])
134           
135        if 'howmany' not in self.inputs:
136            self.inputs['howmany'] = 10
137        else:
138            self.inputs['howmany'] = int(self.inputs['howmany'])
139           
140        # the simplest query we might get is a text search, in which case
141        # the inputs should be start, howmany and searchString (although
142        # maybe not in that order. The next simplest is one with
143        # a specified textTarget, after that we need all the inputs.
144        if 'searchString' in self.inputs and 'textTarget' not in self.inputs:
145            # it's a simple text search
146            self.inputs['textTarget']='All'
147
148        # the next simplest is one that includes texttarget as well ...
149        expected=['searchString','textTarget']
150        missingInputs = self.__checkform(expected)
151        if missingInputs:
152            if bc.INCOMPLETE_SEARCH_INPUT_MESSAGE not in c.inputErrors:
153                c.inputErrors[bc.INCOMPLETE_SEARCH_INPUT_MESSAGE] = []
154            c.inputErrors[bc.INCOMPLETE_SEARCH_INPUT_MESSAGE].extend(missingInputs)
155
156        self.__getSpatioTemporalInputs()
157       
158        logging.debug("User inputs retrieved")
159
160
161    def __getSpatioTemporalInputs(self):
162        '''
163        Get spatiotemporal input data - and set up any defaults, if required
164        '''
165        logging.debug("Getting spatiotemporal inputs")
166        if 'geoSearchType' not in self.inputs:
167            self.inputs['geoSearchType']='overlaps'
168
169        # now we add the defaults... this is kind of historical - NOT SURE THIS IS STILL NEEDED
170        if len(self.inputs)==6:
171            self.bbox=None
172            self.dateRange=None
173            self.scope=None
174            return
175       
176        if 'source' in self.inputs and self.inputs['source'] != 'All':
177            # NB, the WSDL expects a list
178            self.scope = [self.inputs['source']]
179        else:
180            self.scope = None
181           
182        missingInputs = self.__checkform(['bboxN','bboxE','bboxS','bboxW','geoSearchType'])
183        if missingInputs: 
184            self.bbox = None
185        else:
186            # default form has a global bounding box, NB, internal to this routine we use bbox=[N,W,E,S], not [W,S,E,N]!
187            self.bbox = [self.inputs['bboxN'], self.inputs['bboxW'],
188                         self.inputs['bboxE'], self.inputs['bboxS']]
189           
190            errors = self.__checkBBoxValidity(self.bbox)
191            if errors:
192                if bc.INVALID_BBOX_MESSAGE not in c.inputErrors:
193                    c.inputErrors[bc.INVALID_BBOX_MESSAGE] = []
194                c.inputErrors[bc.INVALID_BBOX_MESSAGE].extend(errors)
195
196        #self.bbox = None
197       
198               
199        missingInputs = self.__checkform(['startDate', 'endDate'])
200        self.dateRange = None
201        if missingInputs:
202            self.dateRange = None
203        elif self.inputs['startDate'] and not self.inputs['endDate']:
204            c.inputErrors[bc.INCOMPLETE_DATERANGE_MESSAGE] = ['End date missing']
205        elif not self.inputs['startDate'] and self.inputs['endDate']:
206            c.inputErrors[bc.INCOMPLETE_DATERANGE_MESSAGE] = ['Start date missing']
207        elif self.inputs['startDate'] and self.inputs['endDate']:
208            dateError = None
209            try:
210                year, month, day = self.inputs['startDate'].split('/')
211                self.dateRange = [(day, month, year)]
212                year, month, day = self.inputs['endDate'].split('/')
213                self.dateRange.append((day, month, year))
214
215                if self.dateRange <> [("","",""),("","","")]:
216                    dateError = self.__checkdates(self.dateRange)
217                   
218                else: 
219                    self.dateRange = None
220                               
221            except:
222                dateError = 'Invalid date provided'
223
224            if dateError:
225                if bc.INVALID_DATERANGE_MESSAGE not in c.inputErrors:
226                    c.inputErrors[bc.INVALID_DATERANGE_MESSAGE] = []
227                c.inputErrors[bc.INVALID_DATERANGE_MESSAGE].append(dateError)
228       
229        logging.debug("Spatiotemporal inputs retrieved")
230
231
232    def __getSearchClient(self, clientType):
233        '''
234        Retrieve the appropriate client to complete the search
235        - currently supported are browse and discovery clients
236        @param clientType: type of search client to use.  Currently accepts,
237        'Discovery', 'Browse' and 'NumSim'
238        @raise ValueError if unrecognised search client entered
239        @return search client adhering to the ndg.common.clients.interfacesearchclient
240        interface
241        '''
242        logging.debug("Getting %s type search client" %clientType)
243        searchClient = None
244        if clientType =='Discovery':
245            logging.info(" - use Discovery service to complete search")
246            if g.discoveryServiceURL:
247                searchClient = DiscoveryServiceClient(HostAndPort = g.discoveryServiceURL)
248            else:
249                searchClient = DiscoveryServiceClient()
250               
251        elif clientType in ['Browse','NumSim']:
252            logging.info(" - use Browse service to complete search")
253            searchClient = SearchClient(dbHostName = g.localEXist,
254                                        configFileName = g.pwFile)
255        else:
256            raise ValueError("Unrecognised search type, '%s'" %clientType)
257       
258        logging.debug("- returning search client")
259        return searchClient
260       
261
262    def __runSearch(self, searchString, textTarget, start,
263                    howmany, orderBy = None, orderDirection = None, scope = None, 
264                    dateRange = None, bbox = None, geoSearch = 'overlaps'):
265        '''
266        Carry out a text search for <searchString>
267        in the <textTarget> where the accepted text target values are controlled
268        by the DiscoveryTemplate GUI, and are: All, Authors, Parameters
269        @param searchString: string to search for
270        @param textTarget: target of the search - either, 'All', 'Authors' or 'Parameters'
271        @param start: starting record to return
272        @param howmany: number of records to return
273        @keyword orderBy: Field to order results by - NB, must be one of the
274        getList('orderByFieldList') values
275        @keyword orderDirection: Direction the 'orderBy' results should be returned in
276        - NB, this is currently 'ascending' or 'descending'
277        @keyword scope: scope of search - either NERC, NERC_DDC, MDIP or DPPP. Default = None
278        @keyword dateRange: date range in format [startDate, endDate] where the
279        date objects are tuples with format (day, month, year). Default = None
280        @keyword bbox: Bounding box in format [N,W,E,S]. Default = None
281        @keyword geoSearch: type of spatial search. Defaults to 'overlaps'.
282        '''
283        logging.debug("Running text search with string, '%s'" %searchString)
284       
285        searchClient = self.__getSearchClient(self.inputs['searchTarget'])
286       
287        if self.inputs['searchTarget'] in ['Browse','NumSim']:
288            textTarget = self.inputs['searchTarget']
289            if textTarget == 'Browse':
290                # NB, this switches the searching to be done on atom format
291                # rather than moles1.3 format docs
292                textTarget = SearchClient.ATOM_TARGET#'ndg_B_metadata'
293           
294        # PJK 04/09/08 Handle errors more gracefully
295        #
296        # http://proj.badc.rl.ac.uk/ndg/ticket/984
297        try:
298           
299            publicOnly = not g.atomEditorEnabled
300           
301            searchClient.search(searchString,
302                                start = start,
303                                howmany = howmany,
304                                target = textTarget,
305                                scope = scope,
306                                dateRange = dateRange,
307                                bbox = bbox,
308                                geoSearchType = geoSearch,
309                                orderBy = orderBy,
310                                publicOnly = publicOnly)
311                               
312        except socket.error, e:
313            logging.error("Socket error for discovery service search: %s" % e)
314            c.xml='The Discovery Service is unavailable.  Please check with '+\
315                    'your system administrator'
316            return render('error')
317        except Exception, e:
318            logging.error("Calling discovery service search: %s" % e)
319            c.xml='An internal error occured.  Please check with ' + \
320                    'your system administrator'
321            return render('error')
322           
323        logging.debug("Search returned - now processing results")
324        # DiscoveryState object is a wrapper to the various search config
325        # variables
326        c.state = DiscoveryState(searchClient.serverSessionID, searchString,
327                                 request.environ, searchClient.hits, self.constraints,
328                                 start, howmany)
329
330        return self.__processSearchResults(searchClient, c.state)
331
332
333    def __processSearchResults(self, searchClient, ds):
334        '''
335        Process the results from a search - as ran by the input search client object
336        @param searchClient: search client adhering to the ndg.common.clients.interfacesearchclient
337        interface - which has just ran a search
338        @param ds: DiscoveryState object with info on the search
339        '''
340       
341        if searchClient.error:
342            logging.error("Error encountered whilst running search: %s" %searchClient.error)
343            m=''
344            for i in searchClient.error:
345                m+='<p>%s</p>'%i
346            c.xml = m
347            return render('error')
348       
349        hits = searchClient.hits
350        # NB, this is used in the semantic search function of results.kid and short_results.kid
351        c.querystring = request.environ['QUERY_STRING']
352        c.altSearchURL = '%s?%s'%(h.url_for(action='semantic'),
353                                  request.environ['QUERY_STRING'])
354
355        difs = []
356        errors = []
357
358        logging.debug ("atom target %s" % SearchClient.ATOM_TARGET)
359        ds.constraintsInstance['textTarget'] = 'Atom'
360        logging.debug ("atom target %s" % ds.constraintsInstance)
361       
362        if hits == 0 and ds.constraintsInstance['textTarget'] != SearchClient.ATOM_TARGET:
363            outMessage = 'No records found for "%s"[constraints: %s]' \
364                %(ds.searchString, ds.constraints)
365            logging.info(outMessage) 
366            c.xml='<p>' + outMessage + '</p>'
367
368        else:
369            try:
370                # display browse search results differently
371                if self.inputs['searchTarget'] != 'Discovery':
372                    return self.__displayBrowseSearchResults(searchClient)
373   
374                # now actually retrieve the search records
375                results = searchClient.getLabelledDocs(format='DIF')
376   
377                if not results:
378                    c.xml='<p>No results for "%s"!</p>'%ds.searchString
379                else:
380                    for result in results: 
381                        obj=ndgObject(result[0], config = self.cf)
382                        try:
383                            difs.append(DIF(result[1],ndgObj=obj))
384                        except ValueError,e:
385                            errors.append((result[0], str(e)))
386       
387                    if not difs:
388                        c.xml='<p>No usable results for "%s"!</p>'%ds.searchString
389                   
390                    elif errors:
391                        c.xml='<p>Search results for "%s"'%ds.searchString
392                        dp=[]
393                        for e in errors:
394                            n=ndgObject(e[0])
395                            if n.repository not in dp: 
396                                dp.append(n.repository)
397                        if len(dp)<>1: 
398                            dp='[Various Data Providers]'
399                        else:
400                            dp='[%s]'%dp[0] 
401                           
402                        c.xml+=' (unfortunately %s hits matched unformattable documents from %s, an internal error has been logged):</p>'%(len(errors),dp)
403                        status, message=mailHandler([g.metadataMaintainer],'DIF errors',
404                                                    str(errors), server = g.mailServer)
405                        if not status:
406                            c.xml+='<p> Actually, not even an internal error has been logged. <br/>'
407                            c.xml+='Internal sending of mail failed with error [%s]</p>'%message
408                   
409                # if we're here, we're ready to display the dif records
410                c.difs = difs
411                session['results'] = h.current_url()
412                session.save()
413               
414                # set up the displayed tabs
415                if len(c.pageTabs)==1: 
416                    c.pageTabs.append(('Results', session['results']))
417                    c.pageTabs.append(('Selections',
418                                       h.url_for(controller='visualise/selectedItems',
419                                                 action='index')))
420                elif c.pageTabs[1][0]!='Results':
421                        c.pageTabs.insert(1,('Results',session['results']))
422                        selectionsNeeded=1
423                        for tab in c.pageTabs[0]:
424                            if tab == 'Selections':
425                                selectionsNeeded=0
426                        if selectionsNeeded:
427                            c.pageTabs.append(('Selections',
428                                       h.url_for(controller='visualise/selectedItems',
429                                                 action='index')))
430                           
431                   
432            except ValueError,e:
433                if g.debugModeOn == 'True':
434                    raise ValueError,str(e)
435
436                c.xml='<p> Error retrieving documents for %s hits is [%s]</p>'%(hits,e)
437
438        return render('browse/results')
439       
440    def __advancedPrompt(self, searchConstraints = None):
441        '''
442        This provides the advanced search input page
443        @keyword searchConstraints: a DiscoveryState.constraints object with the
444        search filter details in
445        '''
446        #defaults
447        c.title = bc.DISCOVERY_HOME_TITLE
448        c.bbox='90.0','-180.0','180.0','-90.0'
449        #c.bbox='','','',''
450        c.startDate = ''
451        c.endDate = ''
452        c.textTarget='All'
453        c.searchString=''
454        c.source=['All']
455        c.geoSearchType='overlaps'
456
457        # apply any available constraints
458        if searchConstraints:
459            if searchConstraints['dateRange']:
460                c.startDate = '%s/%s/%s' %searchConstraints['dateRange'][0]
461                c.endDate = '%s/%s/%s' %searchConstraints['dateRange'][1]
462            if searchConstraints['bbox']:
463                c.bbox=searchConstraints['bbox']
464            if searchConstraints['textTarget']:
465                c.textTarget=searchConstraints['textTarget']
466            if searchConstraints['searchString']:
467                c.searchString=searchConstraints['searchString']
468            if searchConstraints['scope']:
469                c.source=searchConstraints['scope']
470            if searchConstraints['geoSearchType']:
471                c.geoSearchType = searchConstraints['geoSearchType']
472               
473        # NB, htmlfill doesn't handle the checked property correctly, so need
474        # to add this explicitly here
475        if 'source' not in self.inputs:
476            self.inputs['source'] = 'NERC_DDC'
477       
478        return self.savePageAndRender("browse/discovery_search", **self.inputs)
479
480       
481    def __checkBBoxValidity(self, bbox):
482        '''
483        Check the integrity of the bounding box; return any errors found as list
484        @return: list of errors
485        '''
486       
487        errors = []
488       
489        for name, val in [('North', float(bbox[0])), ('South', float(bbox[3]))]:
490            if val > 90.0 or val < -90.:
491                errors.append("%s latitude exceeds valid range - -90 <= x <= 90" %name)
492               
493        for name, val in [('West', float(bbox[1])), ('East', float(bbox[2]))]:
494            if val > 180.0 or val < -180.:
495                errors.append("%s longitude exceeds valid range - -180 <= x <= 180" %name)
496        return errors
497
498           
499    def __checkform(self,expected):
500        '''
501        Simply checks the inputs to make sure the elements in expected are present
502        - NB, this isn't actually checking that a value for these inputs are set, it
503        is just checking the fields are there
504        @return array of missing inputs
505        '''
506        logging.debug("Checking for missing inputs")
507        missingInputs = []
508        for i in expected:
509            if i not in self.inputs:
510                logging.debug(" - found missing input: %s" %i)
511                missingInputs.append(i)
512        logging.debug("Finished checking for missing inputs")
513        return missingInputs
514       
515               
516    def __checkdates(self,dateRange):
517        '''
518        Check input dates for sanity
519        @return: error message, if invalid, None otherwise
520        '''
521        if not ValidDate(dateRange[0])*ValidDate(dateRange[1]):
522            return str(dateRange)
523        elif JulDay(dateRange[0]) >= JulDay(dateRange[1]):
524            return 'second date must be after first date'
525     
526        return None
527
528       
529    def __buildconstraints(self, dateRange, bbox, scope, searchString, geoSearch):
530        '''
531        Build and return a DiscoveryState.constraints object
532        '''
533        return constraints(dateRange=dateRange, bbox=bbox,
534                           scope=scope, searchString=searchString, 
535                           geoSearchType=geoSearch)
536       
537
538    def semantic(self):
539        self.__setup()
540        vs = VS(proxyServer = g.proxyServer)
541        if 'searchString' in self.inputs:
542            try:
543                # NB, the search doesn't work with aggregated info so just
544                # look up both in turn
545                broader = []
546                narrower = []
547                synonyms = []
548                if 'searchString' in self.inputs and self.inputs['searchString']:
549                    [broader, narrower, synonyms] = \
550                        vs.getRelated(self.inputs['searchString'])
551
552                if 'vocabTerm' in self.inputs and self.inputs['vocabTerm']:
553                    [broader1, narrower1, synonyms1] = \
554                        vs.getRelated(self.inputs['vocabTerm'])
555                   
556                    broader = self.__addNewTerms(broader, broader1)
557                    narrower = self.__addNewTerms(narrower, narrower1)
558                    synonyms = self.__addNewTerms(synonyms, synonyms1)
559                   
560                   
561                #get a base string for the links to new searches
562                if 'start' in self.inputs: 
563                    del self.inputs['start']
564                if 'howmany' in self.inputs: 
565                    del self.inputs['howmany']
566                self.inputs['searchString']='###SEARCHSSTRING###'
567                q='%s/discovery?'%g.server
568                for i in self.inputs: q+='%s=%s&'%(i,self.inputs[i])
569                url=q[0:-1]
570                # and now build the links
571                c.narrower=[]
572                c.broader=[]
573                c.synonyms=[]
574                for i in narrower:
575                    c.narrower.append((i,url.replace('###SEARCHSSTRING###',i)))
576                for i in broader:
577                    c.broader.append((i,url.replace('###SEARCHSSTRING###',i)))
578                for i in synonyms:
579                    c.synonyms.append((i,url.replace('###SEARCHSSTRING###',i)))
580                if c.narrower!=[] or c.broader!=[] or c.synonyms!=[]: c.semAvailable=1
581            except IOError,e:
582                c.semAvailable=0
583                c.semError=' (No valid reply from vocabulary service)'
584                #This should go in a log file ...
585                print 'ERROR: Vocabulary Service: %s (for search [%s])'%(str(e),self.inputs['searchString'])
586        else:
587            broader,narrower,synonyms=[],[],[]
588            c.semAvailable=0
589            c.semError='.'
590       
591        return render('browse/semantic',fragment=True)
592
593
594    def __addNewTerms(self, toList, fromList):
595        '''
596        Add any terms in list2 not in list1 to list1 - and then return this list
597        @param toList: list to add terms to
598        @param fromList: list to add terms from
599        '''
600        for data in fromList:
601            if data not in toList:
602                toList.append(data)
603               
604        return toList
605   
606
607   
608    def __displayBrowseSearchResults(self, searchClient):
609        '''
610        Provides the search results for Browse and NumSim content
611        @param searchClient: search client adhering to the ndg.common.clients.interfacesearchclient
612        interface - which has just ran a search
613        '''     
614        c.results = searchClient.results
615        c.searchTarget = self.inputs['searchTarget']
616       
617        textTarget = self.inputs['textTarget']
618
619        # check if we've done a search against atoms - NB, this should be the
620        # default eventually - so we can remove all the alternative options
621        isAtom = True
622
623#        if textTarget == SearchClient.ATOM_TARGET:
624#           isAtom = True
625       
626        for r in c.results:
627
628            id = r.id
629                # cope with atom docs
630            if isAtom:
631                r.link = r.href
632            else:
633                n=ndgObject(id,config=self.cf)
634                r.link={'Browse':n.BURL,'NumSim':n.URL}[c.searchTarget]
635
636        # filter atom docs according to publication state
637        if isAtom:
638            c.searchTerm = " - for search term, '%s'" % self.inputs['searchString']
639            c.searchString = self.inputs['searchString']
640                   
641            if not g.atomEditorEnabled:
642                c.results = self.__filterAtomResults(c.results)
643
644            if c.results:
645                c.searchTerm += ' [%s results found]' %len(c.results)
646               
647               
648            html = render('genshi', 'browse/short_atom_results')
649            # make sure the edit links point to the editor, not the browse service
650            html = html.replace(VTD.BROWSE_SERVER_URL + '/editAtom', g.server + '/editAtom')
651            return html
652        else:
653            return render('browse/short_results')
654
655
656    def __filterAtomResults(self, results):
657        '''
658        Given a set of atom docs search results, filter these to only return docs in the
659        'published' or 'Published' state
660        @param results: list of results as returned by SearchClient
661        @return filteredResults: list of results with only published data included
662        '''             
663        logging.debug("Filtering results to remove non-published data")
664        filteredResults = []
665        for result in results:
666            if result.collection.find('ublished') == -1:
667                logging.debug("- found non-published doc - ignoring id: %s" % result.id)
668                continue
669            filteredResults.append(result)
670        logging.debug("- returning filtered results")
671        return filteredResults
672
673           
674    def clearSession(self):
675        '''
676        Clear out all session variables - to help when these change in development
677        '''
678        session.clear()
679        session.save()           
680     
Note: See TracBrowser for help on using the repository browser.