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

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

First changes (in standard wsgi branch) for supporting shopping cart
functionality etc.

Line 
1import DiscoveryTemplate
2from paste.request import parse_querystring
3import ndgSearch as NS
4from DIF import DIF
5from renderDiscoverySet import renderDiscoverySet
6from DiscoveryState import DiscoveryState
7from Utilities import myConfig,mailError
8from ndgObject import ndgObject
9from Date import *
10debug=0
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   
20    def __init__(self,environ,config,logger):
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       
25        inputs=dict(parse_querystring(environ))
26        self.inputs=inputs
27        self.environ=environ
28        self.config=config
29        self.logger=environ['wsgilog.logger']
30        self.mailServer=self.config.get('DEFAULT','mailServer','outbox.rl.ac.uk')
31        self.message=''
32        self.html=''
33       
34        #is this simply a blank request for an interface? In this version,
35        if self.inputs.keys()==[]:
36            self.advancedInterface2()
37            return
38        elif self.inputs.keys()==['advanced']:
39            self.advancedInterface2()
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'])
59        self.advanced=advanced
60       
61        self.html=''
62       
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
68            expected=['startDateDay','startDateMon','startDateYear',
69                        'endDateDay','endDateMon','endDateYear',
70                        'bboxN','bboxE','bboxS','bboxW']
71            self.__checkform(expected)
72            if self.message!='': return self.error()
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
79            else:
80                scope=None
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()
87               
88            # ------------- Handle Date Ranges ---------------
89            try:
90                dateRange=[(self.inputs['startDateDay'],iMonth(self.inputs['startDateMon']),self.inputs['startDateYear']),
91                           (self.inputs['endDateDay'],iMonth(self.inputs['endDateMon']),self.inputs['endDateYear'])]
92                #default form has blanks, in which case we don't want to check for date range
93                if dateRange<>[("","",""),("","","")]:
94                   self.__checkdates(dateRange)
95                else: dateRange=None           
96            except:
97                self.message='Invalid date provided'
98            if self.message!='': return self.error()
99                       
100            # ------------- ok, now go do the search -----------
101            self.doText(self.inputs['searchString'],self.inputs['textTarget'],
102                 self.inputs['start'],self.inputs['howmany'],scope=scope,dateRange=dateRange,bbox=bbox)
103           
104        return
105           
106           
107    def __checkbox(self,bbox):
108        if float(bbox[0])>90.0 or float(bbox[1])<-90. or float(bbox[2])<-180. or float(bbox[3])>180.:
109            self.message='Invalid bounding box dimensions entered (limits 90,-90,-180,180)'
110           
111    def __checkform(self,expected):
112        ''' Simply checks the inputs to make sure the elements in expected are present '''
113        message="An incomplete NDG search form was received: "
114        for i in expected:
115            if i not in self.inputs: 
116                self.message=message+i
117                self.html='<p>%s</p>'%self.message
118               
119    def __checkdates(self,dateRange):
120        ''' Check input dates for sanity '''
121        print dateRange,ValidDate(dateRange[0]),ValidDate(dateRange[1])
122        if not ValidDate(dateRange[0])*ValidDate(dateRange[1]):
123            self.message='Input dates are not valid'
124        elif JulDay(dateRange[0])>=JulDay(dateRange[1]):
125            self.message='Second date must be after first date'
126               
127    def __setState(self,id,searchString,hits,offset,stride,constraints):
128        ''' Sets the discovery state to be used by external routines '''
129        if hits<stride:stride=hits
130        return DiscoveryState(id,searchString,self.environ,hits,constraints,offset,stride)
131       
132   
133    def error(self):
134        ''' Return a useful error message '''
135        self.html='<div class="error"><p>%s</p></div>'%self.message
136        self.advancedInterface2()
137
138           
139    def message(self,message):
140        ''' Layout a message to the user '''
141        self.html='<div class="message"><p>%s</p></div>'%message
142       
143       
144    def __buildconstraints(self,dateRange,bbox,scope):
145        ''' Just build a constraint string '''
146        constraints=' ('
147        if dateRange is not None: 
148            constraints+='Including&nbsp;%s&nbsp;to&nbsp;%s; '%('%s,%s,%s'%dateRange[0],'%s,%s,%s'%dateRange[1])
149        if bbox is None or bbox==['+90.0','-90.0','-180.0','+180.0']:
150            constraints+='Global; '
151        else: constraints+='Bounding&nbsp;Box:&nbsp;%s,%s,%s,%s; '%tuple(bbox)
152        if scope is not None:
153            constraints+='Restricted to %s; '%scope[0]
154        constraints=constraints[:-2]+')'
155        self.logger.info(constraints)
156        return constraints
157   
158    def doText(self,searchString,textTarget,start,howmany,scope=None,dateRange=None,bbox=None):
159        ''' Carry out a text search for <searchString>
160        in the <textTarget> where the accepted text target values are controlled
161        by the DiscoveryTemplate GUI, and are: All, Authors, Parameters '''
162       
163        self.oneLineSearch()
164        start,howmany=int(start),int(howmany)  # url arguments need conversion ...
165       
166        ws=NS.ndgSearch(logger=self.logger)
167        documents=ws.search(searchString,start=start,howmany=howmany,target=textTarget,
168                            scope=scope,dateRange=dateRange,bbox=bbox)
169        if ws.error !=None:
170            for i in ws.error:
171                self.html+='<p>%s</p>'%i
172            return
173       
174        #build constraints info for report
175        constraints=self.__buildconstraints(dateRange,bbox,scope)
176       
177        hits=ws.hits
178        id=ws.serverSessionID
179        state=self.__setState(id,searchString,hits,start,howmany,constraints)
180        if hits==0: 
181            self.html+='<div class="message"><table><tbody><tr><td>No records found %s.</td></tr></tbody></table></div>'%state.constraints
182        else:
183            try:
184                results=ws.getLabelledDocs(format='DIF')
185                difs=[]
186                errors=[]
187                for result in results: 
188                    obj=ndgObject(result[0])
189                    obj.setConfig(self.config)
190                    try:
191                        difs.append(DIF(result[1],ndgObj=obj))
192                    except ValueError,e:
193                        errors.append((result[0],str(e)))
194                if results==[]:
195                    self.html+=self.message('No results for "%s"!</p>'%searchString)
196                elif difs==[]:
197                    self.html+=self.message('No usable results for "%s"!'%searchString) 
198                else:
199                    if errors<>[]:
200                        self.html+='<p>Search results for "%s"'%searchString
201                        dp=[]
202                        for e in errors:
203                            n=ndgObject(e[0])
204                            if n.repository not in dp: dp.append(n.repository)
205                        if len(dp)<>1: 
206                            dp='[Various Data Providers]'
207                        else:
208                            dp='[%s]'%dp[0] 
209                        self.html+=' (unfortunately %s hits matched unformattable documents from %s, an internal error has been logged):</p>'%(len(errors),dp)
210                        mailError('b.n.lawrence@rl.ac.uk','DIF errors',str(errors),server=self.mailServer)
211                    self.html+=renderDiscoverySet(difs,state,self.config,selector=self.environ['ndgSelector'])
212            except ValueError,e:
213                if debug: 
214                    raise ValueError,str(e)
215                else:
216                    self.html='<p> Error retrieving documents for %s hits is [%s]</p>'%(hits,e)
217                               
218        #self.standardInterface()
219        return
220               
221
222    def standardInterface(self):
223       
224        try:
225            helpAddress=self.config.get('SEARCH','helpURL')
226            discoveryURL=self.config.get('SEARCH','discoveryURL')
227            advancedURL=self.config.get('SEARCH','advancedURL')
228        except:
229            self.message='Error, invalid configuration for search interface'
230            self.html+=''
231            return
232       
233        self.html+=DiscoveryTemplate.main%locals()
234        return
235   
236    def advancedInterface(self):
237       
238        try:
239            helpAddress=self.config.get('SEARCH','helpURL')
240            discoveryURL=self.config.get('SEARCH','discoveryURL')
241            advancedURL=self.config.get('SEARCH','advancedURL')
242        except:
243            self.message='Error, invalid configuration for search interface'
244            self.html=''
245            return
246        advancedSubmit=DiscoveryTemplate.advancedSubmit%locals()
247        searchText=DiscoveryTemplate.searchText
248        searchTime=DiscoveryTemplate.searchTime
249        searchArea=DiscoveryTemplate.searchArea
250        searchSource=DiscoveryTemplate.searchSource
251        self.html+=DiscoveryTemplate.advanced%locals()
252        return
253   
254    def advancedInterface2(self):
255        try:
256            discoveryURL=self.config.get('SEARCH','discoveryURL')
257            advancedURL=self.config.get('SEARCH','advancedURL')
258            oneLiner=DiscoveryTemplate.oneLiner
259        except:
260            self.message='Error, invalid configuration for search interface'
261            self.html=''
262            return
263        bboxN,bboxW,bboxE,bboxS='+90.0','-180.0','+180.0','-90.0'
264        startDateDay,startDateMon,startDateYear='','',''
265        endDateDay,endDateMon,endDateYear='','',''
266        self.html+=DiscoveryTemplate.revisedAdvancedSearch%locals()
267       
268   
269    def helpInterface(self,advanced):
270        if advanced:
271            self.advancedInterface()
272        else:
273            self.standardInterface()
274        helpFile=self.config.get('HELP','helpFile')
275        f=file(helpFile,'r')
276        self.html+=f.read()
277        return
278       
279    def oneLineSearch(self):
280        try:
281            discoveryURL=self.config.get('SEARCH','discoveryURL')
282            oneLiner=DiscoveryTemplate.oneLiner
283        except:
284            self.message='Error, invalid configuration for search interface'
285            self.html=''
286            return
287        self.html+=DiscoveryTemplate.searchTextOneLine%locals()
288       
289       
Note: See TracBrowser for help on using the repository browser.