source: TI07-MOLES/trunk/PythonCode/wsgi/DiscoveryGUI.py @ 2533

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI07-MOLES/trunk/PythonCode/wsgi/DiscoveryGUI.py@2533
Revision 2533, 12.0 KB checked in by lawrence, 12 years ago (diff)

Discovery now defaults to an advanced interface which is getting close
to that required by ticket:731. The first point on ticket:733 is now done too.

RevLine 
[1905]1import DiscoveryTemplate
[1955]2from paste.request import parse_querystring
[1905]3import ndgSearch as NS
4from DIF import DIF
5from renderDiscoverySet import renderDiscoverySet
6from DiscoveryState import DiscoveryState
[2417]7from Utilities import myConfig,mailError
[2097]8from ndgObject import ndgObject
[2522]9from Date import *
[2417]10debug=0
[1905]11class DiscoveryGUI:
12    ''' This class handles the NDG gui search interface, and is intimately related to the
13    template.py code which provides the gui interface and the variables which need to be populated '''
14   
15    #The key options are either there is content in the inputdictionary, in which case we have a
16    #search to do, and then some results to present, or there is none, in which case we present
17    #an appropriate interface back to the user (remembering that we only produce part of a page,
18    #this can all be embedded in anything else.
19   
[1955]20    def __init__(self,environ,config,logger):
[1905]21        ''' Takes a dictionary of variables which have been populated by the cgi (or whatever)
22        handler, and tests them for sanity before handing queries off to the NDG backend xqquery
23        web service '''
24       
[1955]25        inputs=dict(parse_querystring(environ))
[1905]26        self.inputs=inputs
[1955]27        self.environ=environ
[1905]28        self.config=config
[2448]29        self.logger=environ['wsgilog.logger']
[2417]30        self.mailServer=self.config.get('DEFAULT','mailServer','outbox.rl.ac.uk')
[1905]31        self.message=''
32        self.html=''
33       
[2533]34        #is this simply a blank request for an interface? In this version,
[1905]35        if self.inputs.keys()==[]:
[2533]36            self.advancedInterface2()
[1905]37            return
38        elif self.inputs.keys()==['advanced']:
[2533]39            self.advancedInterface2()
[1905]40            return
41        elif self.inputs.keys()==['help']:
42            self.helpInterface(0)
43            return
44        elif self.inputs.keys()==['help','advanced']:
45            self.helpInterface(1)
46            return
47       
48        #check simplest text issues common to both simple and advanced search
49        expected=['advanced','textTarget','searchString']
50        self.__checkform(expected)
51        if self.message!='': return
52       
53        #the following may also be present
54        continuations={'start':1,'howmany':10}
55        for i in continuations:
56            if i not in self.inputs: self.inputs[i]=continuations[i]
57           
58        advanced=int(self.inputs['advanced'])
[2522]59        self.advanced=advanced
[1905]60       
61        self.html=''
[2533]62       
[1905]63        if not advanced:
64            self.doText(self.inputs['searchString'],self.inputs['textTarget'],
65                        self.inputs['start'],self.inputs['howmany'])
66        else:
67            #check content
[2533]68            expected=['startDateDay','startDateMon','startDateYear',
69                        'endDateDay','endDateMon','endDateYear',
70                        'bboxN','bboxE','bboxS','bboxW']
[1905]71            self.__checkform(expected)
[2522]72            if self.message!='': return self.error()
[2533]73           
74            # ------------- Handle scope from radio button on form -------
75            if 'source' in self.inputs:
76                # the WSDL expects a list, we're just providing one ... via a radio ...
77                scope=[self.inputs['source']]
78                if scope==['All']: scope=None
[1905]79            else:
80                scope=None
[2533]81           
82            # ------------- Handle Location Bounding --------
83            # default form has a global bounding box
84            bbox=[self.inputs['bboxN'],self.inputs['bboxS'],self.inputs['bboxW'],self.inputs['bboxE']]
85            self.__checkbox(bbox)
86            if self.message!='': return self.error()
[1905]87               
[2533]88            # ------------- Handle Date Ranges ---------------
89            dateRange=[(self.inputs['startDateDay'],iMonth(self.inputs['startDateMon']),self.inputs['startDateYear']),
90                           (self.inputs['endDateDay'],iMonth(self.inputs['endDateMon']),self.inputs['endDateYear'])]
91            #default form has blanks, in which case we don't want to check for date range
92            if dateRange<>[("","",""),("","","")]:
93                self.__checkdates(dateRange)
94            else: dateRange=None
95            if self.message!='': return self.error()
[2522]96                       
[2533]97            # ------------- ok, now go do the search -----------
[1905]98            self.doText(self.inputs['searchString'],self.inputs['textTarget'],
[2533]99                 self.inputs['start'],self.inputs['howmany'],scope=scope,dateRange=dateRange,bbox=bbox)
[1905]100           
101        return
102           
[2522]103           
104    def __checkbox(self,bbox):
105        if float(bbox[0])>90.0 or float(bbox[1])<-90. or float(bbox[2])<-180. or float(bbox[3])>180.:
106            self.message='Invalid bounding box dimensions entered (limits 90,-90,-180,180)'
107           
[1905]108    def __checkform(self,expected):
109        ''' Simply checks the inputs to make sure the elements in expected are present '''
110        message="An incomplete NDG search form was received: "
111        for i in expected:
112            if i not in self.inputs: 
113                self.message=message+i
114                self.html='<p>%s</p>'%self.message
115               
[2522]116    def __checkdates(self,dateRange):
117        ''' Check input dates for sanity '''
118        print dateRange,ValidDate(dateRange[0]),ValidDate(dateRange[1])
119        if not ValidDate(dateRange[0])*ValidDate(dateRange[1]):
120            self.message='Input dates are not valid'
121        elif JulDay(dateRange[0])>=JulDay(dateRange[1]):
122            self.message='Second date must be after first date'
123               
[2533]124    def __setState(self,id,searchString,hits,offset,stride,constraints):
[1905]125        ''' Sets the discovery state to be used by external routines '''
[2374]126        if hits<stride:stride=hits
[2533]127        return DiscoveryState(id,searchString,self.environ,hits,constraints,offset,stride)
[1905]128       
[2522]129   
130    def error(self):
131        ''' Return a useful error message '''
132        self.html='<div class="error"><p>%s</p></div>'%self.message
[2533]133        self.advancedInterface2()
134
[2528]135           
136    def message(self,message):
137        ''' Layout a message to the user '''
138        self.html='<div class="message"><p>%s</p></div>'%message
[2522]139       
[2533]140       
141    def __buildconstraints(self,dateRange,bbox,scope):
142        ''' Just build a constraint string '''
143        constraints=' ('
144        if dateRange is not None: 
145            constraints+='Including&nbsp;%s&nbsp;to&nbsp;%s; '%('%s,%s,%s'%dateRange[0],'%s,%s,%s'%dateRange[1])
146        if bbox is None or bbox==['+90.0','-90.0','-180.0','+180.0']:
147            constraints+='Global; '
148        else: constraints+='Bounding&nbsp;Box:&nbsp;%s,%s,%s,%s; '%tuple(bbox)
149        if scope is not None:
150            constraints+='Restricted to %s; '%scope[0]
151        constraints=constraints[:-2]+')'
152        self.logger.info(constraints)
153        return constraints
154       
[2522]155   
[1905]156    def doText(self,searchString,textTarget,start,howmany,scope=None,dateRange=None,bbox=None):
157        ''' Carry out a text search for <searchString>
158        in the <textTarget> where the accepted text target values are controlled
159        by the DiscoveryTemplate GUI, and are: All, Authors, Parameters '''
160       
[2528]161        self.oneLineSearch()
[1905]162        start,howmany=int(start),int(howmany)  # url arguments need conversion ...
163       
164        ws=NS.ndgSearch(logger=self.logger)
165        documents=ws.search(searchString,start=start,howmany=howmany,target=textTarget,
166                            scope=scope,dateRange=dateRange,bbox=bbox)
167        if ws.error !=None:
168            for i in ws.error:
169                self.html+='<p>%s</p>'%i
170            return
171       
[2533]172        #build constraints info for report
173        constraints=self.__buildconstraints(dateRange,bbox,scope)
174       
[1905]175        hits=ws.hits
176        id=ws.serverSessionID
[2533]177        state=self.__setState(id,searchString,hits,start,howmany,constraints)
[1905]178        if hits==0: 
[2533]179            self.html+='<div class="message"><table><tbody><tr><td>No records found %s.</td></tr></tbody></table></div>'%state.constraints
[1905]180        else:
[2319]181            try:
182                results=ws.getLabelledDocs(format='DIF')
183                difs=[]
[2417]184                errors=[]
[2319]185                for result in results: 
186                    obj=ndgObject(result[0])
187                    obj.setConfig(self.config)
[2417]188                    try:
[2422]189                        difs.append(DIF(result[1],ndgObj=obj))
[2417]190                    except ValueError,e:
[2422]191                        errors.append((result[0],str(e)))
[2319]192                if results==[]:
[2528]193                    self.html+=self.message('No results for "%s"!</p>'%searchString)
[2417]194                elif difs==[]:
[2528]195                    self.html+=self.message('No usable results for "%s"!'%searchString) 
[2319]196                else:
[2417]197                    if errors<>[]:
[2487]198                        self.html+='<p>Search results for "%s"'%searchString
[2417]199                        dp=[]
200                        for e in errors:
201                            n=ndgObject(e[0])
202                            if n.repository not in dp: dp.append(n.repository)
203                        if len(dp)<>1: 
204                            dp='[Various Data Providers]'
205                        else:
206                            dp='[%s]'%dp[0] 
207                        self.html+=' (unfortunately %s hits matched unformattable documents from %s, an internal error has been logged):</p>'%(len(errors),dp)
208                        mailError('b.n.lawrence@rl.ac.uk','DIF errors',str(errors),server=self.mailServer)
[2528]209                    self.html+=renderDiscoverySet(difs,state,self.config)
[2319]210            except ValueError,e:
[2330]211                if debug: 
212                    raise ValueError,str(e)
213                else:
214                    self.html='<p> Error retrieving documents for %s hits is [%s]</p>'%(hits,e)
[1905]215                               
[2528]216        #self.standardInterface()
[1905]217        return
[2528]218               
[1905]219
220    def standardInterface(self):
221       
222        try:
223            helpAddress=self.config.get('SEARCH','helpURL')
224            discoveryURL=self.config.get('SEARCH','discoveryURL')
225            advancedURL=self.config.get('SEARCH','advancedURL')
226        except:
227            self.message='Error, invalid configuration for search interface'
228            self.html+=''
229            return
230       
231        self.html+=DiscoveryTemplate.main%locals()
232        return
233   
234    def advancedInterface(self):
235       
236        try:
237            helpAddress=self.config.get('SEARCH','helpURL')
238            discoveryURL=self.config.get('SEARCH','discoveryURL')
239            advancedURL=self.config.get('SEARCH','advancedURL')
240        except:
241            self.message='Error, invalid configuration for search interface'
242            self.html=''
243            return
244        advancedSubmit=DiscoveryTemplate.advancedSubmit%locals()
245        searchText=DiscoveryTemplate.searchText
246        searchTime=DiscoveryTemplate.searchTime
247        searchArea=DiscoveryTemplate.searchArea
248        searchSource=DiscoveryTemplate.searchSource
249        self.html+=DiscoveryTemplate.advanced%locals()
250        return
251   
[2533]252    def advancedInterface2(self):
253        try:
254            discoveryURL=self.config.get('SEARCH','discoveryURL')
255            advancedURL=self.config.get('SEARCH','advancedURL')
256            oneLiner=DiscoveryTemplate.oneLiner
257        except:
258            self.message='Error, invalid configuration for search interface'
259            self.html=''
260            return
261        bboxN,bboxW,bboxE,bboxS='+90.0','-180.0','+180.0','-90.0'
262        startDateDay,startDateMon,startDateYear='','',''
263        endDateDay,endDateMon,endDateYear='','',''
264        self.html+=DiscoveryTemplate.revisedAdvancedSearch%locals()
265       
266   
[1905]267    def helpInterface(self,advanced):
268        if advanced:
269            self.advancedInterface()
270        else:
271            self.standardInterface()
272        helpFile=self.config.get('HELP','helpFile')
273        f=file(helpFile,'r')
274        self.html+=f.read()
275        return
276       
[2528]277    def oneLineSearch(self):
278        try:
279            discoveryURL=self.config.get('SEARCH','discoveryURL')
[2533]280            oneLiner=DiscoveryTemplate.oneLiner
[2528]281        except:
282            self.message='Error, invalid configuration for search interface'
283            self.html=''
284            return
285        self.html+=DiscoveryTemplate.searchTextOneLine%locals()
286       
[1905]287       
Note: See TracBrowser for help on using the repository browser.