source: TI07-MOLES/trunk/PythonCode/browse/portal/cgi/browse/browseCGI.py @ 1525

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI07-MOLES/trunk/PythonCode/browse/portal/cgi/browse/browseCGI.py@1525
Revision 1525, 14.1 KB checked in by lawrence, 15 years ago (diff)

Sundry modifications to browse to improve paging through
result sets and remove dependence on Marta's backend for
fulltext searching.

  • Property svn:executable set to *
Line 
1#!/usr/bin/env python
2# CGI Script to support prototype NDG MOLES_Portal (Browse) functionality
3# Bryan Lawrence, April, 2006
4
5import cgi
6#import cgitb;ctitb.enable()
7
8import os
9import ElementTree as ET
10
11from insecure import *
12from secure import *
13from stubB import *
14
15from renderEntity import renderEntity
16from renderPage import renderPage
17from renderDiscoverySet import renderDiscoverySet
18from htmlUtilities import selector,hyperlink
19from Utilities import *
20from ETxmlView import *
21from DiscoveryWS import *
22from DIF import DIF
23import base64
24from eXistInterface import *
25
26import Cookie
27
28class BrowseSession:
29       
30        ''' Holds the browse and select history '''
31       
32        def __init__(self,cookie,config):
33               
34                '''Instantiate with an unsecured browse session (security only
35                required if a secure resource is the target) '''
36                self.config=config
37                self.rawCookie=cookie
38                self.cookie=Cookie.SimpleCookie(cookie)
39                self.history=RingBuffer(10)
40                self.selected=RingBuffer(5)
41                self.historyNames=RingBuffer(10)
42                self.__load()
43                       
44        def __toXML(self):
45                ''' Used to serialise the session into a cookie string '''
46                xml='<ndgCookie><bh>'
47                for uri,name in self.getHistory():
48                        xml+='<i><u>%s</u><n>%s</n></i>'%(base64.urlsafe_b64encode(uri),name)
49                xml+='</bh><sh>'
50                for uri,name in self.getSelected():
51                    xml+='<i><u>%s</u><n>%s</n></i>'%(base64.urlsafe_b64encode(uri),name)
52                xml+='</sh></ndgCookie>'
53                return xml
54               
55        def __load(self):
56            ''' get lists of URI values out of a cookie '''
57            try:
58                e=ET.fromstring(self.cookie['history'].value)
59                for se in e:
60                    for item in se:
61                        uri,name=item.find('u'),item.find('n')
62                        self.__addTo(base64.urlsafe_b64decode(uri.text),name.text,se.tag)
63            except KeyError:
64                pass
65                       
66        def __addTo(self,uri,name,tag,ignore=False):
67            d={'bh':self.history,'sh':self.selected}
68            current=d[tag].tolist()
69            # key the uri's by name ... so we can deal with
70            # duplicate abbreviations.
71            names={}
72            for i in current: names[i[1]]=i[0]
73            if ignore: 
74                d[tag].append((uri,name))
75            else:
76                if name in names.keys(): # we might already have it
77                    if names[name]!=uri:
78                        #it's a duplicate name, but not a duplicate uri
79                        d[tag].append((uri,EnumerateString(name)))
80                else: #it's new ...
81                    d[tag].append((uri,name))
82                       
83        def addToHistory(self,uri,name,ignore=False):
84           ''' Add a URI to the session history'''
85           self.__addTo(uri,name,'bh',ignore)
86                       
87        def getHistory(self):
88           ''' Return a list of the items in the history '''
89           return self.history.tolist()
90       
91        def addToSelected(self,uri,name,ignore=False):
92            ''' Add a URI to the selected items '''
93            self.__addTo(uri,name,'sh',ignore)
94            self.__addTo(uri,name,'bh',ignore)
95                   
96        def getSelected(self):
97            ''' Return a list of selected items '''     
98            return self.selected.tolist()       
99       
100        def makeCookie(self,ndgSec=None):
101                ''' Create a local cookie '''
102                import md5,time,base64
103                # start by creating a unique session id
104                m=md5.new()
105                m.update('this is a seed string')
106                m.update(str(time.time()))
107                cookie=Cookie.SimpleCookie()
108                cookie['session']=base64.encodestring(m.digest())[:-3].replace("/", "$")
109                cookie['history']=self.__toXML()
110                if ndgSec is not None:
111                    cookie['NDG-ID1']=ndgSec[0]
112                    cookie['NDG-ID2']=ndgSec[1]
113                return cookie
114
115class CGIcontroller:
116        ''' Currently holds the cgi environment and controls '''
117       
118        def __init__(self,config):
119                ''' Instantiate the CGI environment'''
120                #
121                # Need to refactor all of this to use the request class and then move
122                # the request class out into the utilities ...
123                # (underway)
124               
125                self.env=os.environ
126                #self.path=self.env.get('PATH_INFO','/') #commented out because I don't think
127                #we use it ...
128                self.config=config
129                self.response=Response()
130                self.request=Request(self.env,cgi)
131                self.FieldStorage=self.request.variables
132                self.requestURL=self.request.URL
133                if 'SEARCHSTRING' in self.FieldStorage:
134                        url=self.request.baseURL+'?%s'%urllib.urlencode(
135                        {'SEARCHSTRING':self.FieldStorage['SEARCHSTRING']})
136                else: url=self.requestURL
137                self.selector=selector(url,'select',config.get('layout','selectI'))
138                self.serviceFile=self.config.get('services','serviceFile')
139               
140
141        def makeGateway(self,cookie=None):
142                ''' Make connection to NDG security and load what is necessary for
143                an NDG cookie to be written '''
144                aa=self.config.get('security','localAA',None)
145                if 'NDG-ID1' in self.FieldStorage and 'NDG-ID2' in self.FieldStorage:
146                    #this is a redirect following login ...
147                    cmdLine=(self.FieldStorage['NDG-ID1'],self.FieldStorage['NDG-ID2'])
148                else: cmdLine=None
149                self.ndgGate=gateway2NDGsession(self.requestURL,self.config,aa,cookie=self.cookie,cmdLine=cmdLine)
150                self.ndgSec=cmdLine
151
152        def goforit(self):
153                ''' This method actually responds to the user'''
154               
155                self.ViewTextOnly,self.ViewXML=0,0
156               
157                #Instantiate the Session Environment
158                cookie=self.env.get('HTTP_COOKIE',None)
159                self.session=BrowseSession(cookie,self.config)
160                self.cookie=self.session.cookie # now a cookie class object ...
161                if self.config.logfile is not None: self.config.log(self.cookie)
162               
163                #this is where we invoke NDG security setup, even if we don't
164                #need it ...
165                self.makeGateway()
166               
167                #this will do for the moment, although I'd rather the whole
168                #URI was self consistent using the request object.
169               
170                if 'Error' in self.FieldStorage:
171                    content,name=self.FieldStorage['Error'],'URL Error'
172                    self.response.cookie=self.session.makeCookie(ndgSec=self.ndgSec)
173                   
174                if 'select' in self.FieldStorage:
175                    n=self.FieldStorage['name']
176                    s=ServiceBinding(n=n,
177                                     entryID=self.FieldStorage['select'],
178                                     serviceFile=self.serviceFile)
179                    self.session.addToSelected(s.url,n)
180
181                    #we also need to trim the selector off the current requestURL
182                    self.requestURL=self.requestURL[0:self.requestURL.find('&select')]
183                    self.selector.baseURL=self.requestURL
184               
185                #use name as an error return as well in the following calls ...
186                if 'uri' in self.FieldStorage:
187                    self.uri=self.FieldStorage['uri']
188                    content,name=self.__browse()
189                elif 'SEARCHSTRING' in self.FieldStorage:
190                    # probably ought to watch out for search=text types ...
191                    self.searchType='text'#self.FieldStorage['search']
192                    content,name=self.__search()
193                elif 'Error' in self.FieldStorage:
194                    pass
195                else: 
196                    content,name=self.error('No URI or search string. Begin browsing via search box or pass a specific URI argument.'),'No URI'
197
198                if not self.ViewTextOnly:
199                    historyHTML='<p>'
200                    for item in self.session.getHistory():
201                        historyHTML+=hyperlink(item[1],item[0])+'<br/>'
202                    historyHTML+='</p>'
203                    selectHTML='<p>'
204                    for item in self.session.getSelected():
205                        selectHTML+=hyperlink(item[1],item[0])+'<br/>'
206                    selectHTML+='</p>'
207                    self.response.content=renderPage(
208                        content,historyHTML,selectHTML,name,self.config)
209                else:
210                    self.response.content=content
211                self.response.cookie=self.session.makeCookie(ndgSec=self.ndgSec)
212                return self.response
213               
214        def __browse(self):
215                ''' Handle orthodox browsing '''
216               
217                if self.FieldStorage.has_key('text'):
218                    self.ViewTextOnly=1
219                elif self.FieldStorage.has_key('xml'):
220                    self.ViewXML=1
221               
222                format='NDG-B0'
223                if self.FieldStorage.has_key('D'):format='DIF'
224               
225                #get the xml document
226                db=self.config.get('db','exist',None)
227                passwordFile=self.config.get('security','passwords','passwords.txt')
228                               
229                jar=self.config.get('services','jarLoc')
230                javabin=self.config.get('services','javaBinary')
231
232                xml=insecureGetDoc(self.uri,db=db,format=format,jar=jar,javaBin=javabin,
233                                   passwordFile=passwordFile)
234                # this isn't ideal but it'll do for now ...
235                # try again, directly to the exist database
236                if xml is None and format=='DIF':
237                    existDB=ndg_eXist(db=db,passwordFile=passwordFile)
238                    xml=existDB.getDIF(self.uri)
239               
240               
241                #create document instance
242                if format=='NDG-B0':
243                    self.b=stubB(xml,serviceFile=self.serviceFile)
244                elif format=='DIF':
245                    self.b=DIF(xml,serviceFile=self.serviceFile)
246               
247                if self.b.xml is None:
248                    content=self.error('Unable to obtain record [%s] from database'%self.uri)
249                    return content,0
250                else:
251                    self.session.addToHistory(self.b.binding.url,self.b.abbreviation)
252                    if  self.b.constraints.exist:
253                        # we need to evaluate them
254                        #result=self.ndgGate.check(self.b.constraints.SimpleCondition)
255                        try:
256                             result=self.ndgGate.check(self.b.constraints.SimpleCondition)
257                             if result=='AccessGranted': 
258                                access=1
259                             else:
260                                access=0
261                        except Exception,e:
262                             #unable to make ndggate check ... deny
263                             access=0
264                             result=self.error('Unable to connect to security gateway, access to secure resource denied')
265                             result+='<p>%s</p>'%str(self.b.constraints.SimpleCondition)
266                             result+='<p>%s</p>'%str(e)
267                    else:
268                        access=1
269                    if access:
270                        name=self.b.name
271                        if self.ViewTextOnly:
272                            self.response.contentType='text/plain'
273                            content=et2text(self.b.tree)
274                        elif self.ViewXML==1:
275                            content=et2html(self.b.tree)
276                        else:
277                            content=self.b.toHTML(self.config)
278                    else:
279                        name='NDG Browse'
280                        content=result
281
282                    return content,name
283               
284        def __search(self):
285            ''' Handle simple searching within the context of the browse '''
286            stride=20
287            if 'SEARCHSTRING' in self.FieldStorage:
288                searchString=self.FieldStorage['SEARCHSTRING']
289                if 'searchSession' in self.FieldStorage:
290                    searchSession=self.FieldStorage['searchSession']
291                    start=int(self.FieldStorage['start'])
292                    howmany=int(self.FieldStorage['howmany'])
293                else:
294                    searchSession=None
295                    start=1
296                    howmany=stride
297                title='Search for '+searchString
298                try:
299                    #ws=DiscoveryWS(wsdl=self.config.get('services','searchWSDL'),
300                    #                sessID=searchSession)
301       
302                    db=self.config.get('db','exist',None)
303                    passwordFile=self.config.get('security','passwords','passwords.txt')
304                    existDB=ndg_eXist(db=db,passwordFile=passwordFile)
305                except Exception, e:
306                    return self.error('<p>%s<br/>%s'%(e,'Unable to connect to Search BackEnd')),'Error'
307                try:
308                    #hits=ws.SearchFullText(searchString)
309                    #state=DiscoveryState(ws.sessID,self.requestURL,hits,stride=stride)
310                    #results=ws.GetResults(offset=state.offset,number=state.stride)
311                    hits=0
312                    if searchSession is not None:
313                        try:
314                            id=searchSession
315                            summary=existDB.querySummary(id)
316                            print '<p>%s</p>'%string(summary)
317                        except:
318                            id,summary=existDB.full_text(searchString)
319                    else:
320                        id,summary=existDB.full_text(searchString)
321                    hits=summary['hits']
322                    if hits==0: return self.error('No records found'),title
323                    r=1
324                    results=[]
325                    j=1
326                    while r is not None and j <= howmany:
327                        j+=1
328                        r=existDB.retrieveNext(id,pos=start+j-1)
329                        if r is not None: results.append(r)
330                    state=DiscoveryState(id,self.requestURL,hits,stride=stride,offset=start)
331                except TypeError:
332                    # this is not what these web services should do, they should
333                    # return an empty set or something which matches the argument
334                    # list, but at least I think this traps the empty set for
335                    # marta's code at least
336                    return self.error('No records found'),title
337                difs=[]
338               
339                for result in results: difs.append(DIF(result,serviceFile=self.serviceFile))
340       
341                html=renderDiscoverySet(difs,state,selector=self.selector,
342                               summary=1,spatial=1,temporal=1,services=1)
343               
344                return html,title
345            else:
346                return self.error('No valid search option'),'Error'
347             
348        def error(self,message):
349                ''' Construct a nice formal response, but don't throw a 404 ... '''
350                return '<p>Error: %s</p>'%message
351               
Note: See TracBrowser for help on using the repository browser.