source: TI03-DataExtractor/branches/titania_install/cgi/dxui.py @ 1520

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI03-DataExtractor/branches/titania_install/cgi/dxui.py@1520
Revision 1520, 22.4 KB checked in by astephen, 14 years ago (diff)

This is the live version on titania - changes have been made so safest to SVN it.

  • Property svn:executable set to *
Line 
1#!/usr/local/badc/linux/suse10/cdat/bin/python
2#   Copyright (C) 2004 CCLRC & NERC( Natural Environment Research Council ).
3#   This software may be distributed under the terms of the
4#   Q Public License, version 1.0 or later. http://ndg.nerc.ac.uk/public_docs/QPublic_license.txt
5
6"""
7dxui
8====
9
10Holds the CGIClient class the users interact with if they
11are calling the DX web service via a CGI interface through a
12web browser. Typically this script will be called "dxui".
13
14"""
15
16# Import standard library modules
17import cgi, random, time, re, os, sys
18
19# Import SOAP library
20from ZSI.client import Binding
21from ZSI.version import Version as zsiv
22zsiv=float("%s.%s" % zsiv[:2])
23       
24# Get configuration file for this dx client
25configFile="/usr/local/badc/linux/suse10/dxc/configs/clientConfig.py"
26configPath, configFilename=os.path.split(configFile)
27sys.path.insert(0, configPath)
28exec ("from %s import *" % configFilename[:-3]) 
29         
30# Import package modules
31from pydxc import *
32
33# Import BADC security
34sys.path.append("/home/badc/software/pythonlib/badc")
35from BADCSecurityViaCGI import *
36
37# Set up CGI error reporting if DEBUG is set to 1 in clientConfig.py module
38if DEBUG==1:
39    import cgitb 
40    cgitb.enable() 
41
42
43class DXCGIClient:
44    """
45    The controlling class for interacting with the dx via the web.
46    """
47   
48    def __init__(self):
49        """
50        Initiates the instance setting up the appropriate
51        internal objects and calls to the relevant classes depending
52        on the configuration and arguments provided.
53        """
54        self.username="undefined"
55        self.password="undefined"
56        self.secureToken="undefined"
57        self.userRoles=[]
58        self.loginStatus="out"
59        self.loginMessage=""
60        self.sessionObject={}
61       
62        self.displayer=DisplayManager()
63
64        #self.displayer._displayHTTPHeader()
65
66        # Parse the arguments
67        try:
68            self._parseArgs()
69        except:
70            if DEBUG==1:
71                raise
72            else:
73                CGIErrorHandler("Error parsing arguments: "+str(sys.exc_type), noheader=0)
74
75        # Destroy any arguments not intended to pass any further
76        try:
77            self._destroyUnnecessaryArgs()
78        except:
79            if DEBUG==1:
80                raise
81            else:
82                CGIErrorHandler("Error destroying arguments: "+str(sys.exc_type), noheader=0)
83
84        # If switched on check security       
85        if RESTRICTED_DATA==1:
86            try:
87                self._checkSecurity()
88            except:
89                if DEBUG==1:
90                    raise
91                else:
92                    CGIErrorHandler("Security error: "+str(sys.exc_type), noheader=0)
93        else:
94            self.secureToken="undefined"
95
96        #print "Content-type: text/html\n\n"
97           
98        # Call the dx Web Service with the arguments provided       
99        try:
100            self._callDXWebService(self.args)   
101        except SystemExit:
102            pass         
103        except:
104            if DEBUG==1:
105                raise
106            else:
107                CGIErrorHandler("Error calling dx Web Service: "+str(sys.exc_type), noheader=0)       
108
109        try:   
110            self.displayer._displayHTMLFooter() 
111        except:
112            if DEBUG==1:
113                raise
114            else:
115                CGIErrorHandler("Error generating HTML footer: "+str(sys.exc_type), noheader=0)
116       
117       
118    def _parseArgs(self):
119        """
120        Parses the arguments sent, if any, ready for sending to the
121        web service.
122        """
123        # Get arguments
124        self.args={}                           
125        args=cgi.FieldStorage() 
126        self.fieldStorage=args
127       
128        # Get upload file link
129        if args.has_key("uploadedFile"):
130            self.tempFileLink=args["uploadedFile"].file
131            self.args["fileName"]=args["uploadedFile"].filename
132
133        # Populate the argument dictionary
134        for key in args.keys(): 
135            if key=="uploadedFile": continue
136            self.args[key]=args.getvalue(key) 
137                   
138        if self.args.has_key("sessionID") and self.args["sessionID"]=="undefined":
139            self.args["sessionID"]="undefined"     
140         
141        # Check for secure items, destroy if necessary and assign as instance variables   
142        if self.args.has_key("yousirnaim"):
143            self.username=self.args["yousirnaim"]
144            self.args["username"]=self.username
145            del self.args["yousirnaim"]
146           
147        if self.args.has_key("parcewerd"):
148            self.password=self.args["parcewerd"] 
149            del self.args["parcewerd"] 
150               
151        if self.args.has_key("secureToken"):
152            self.secureToken=self.args["secureToken"]
153            del self.args["secureToken"]   
154       
155        if not self.args.has_key("username"):
156            self.args["username"]="undefined"
157           
158        #self._determineNumberOfDatasets()
159        #self._parsePartialDimensionArgs()
160       
161        self._compileAxisSelections()
162       
163
164    def _compileAxisSelections(self):
165        """
166        Compiles partial selections from the last form selections
167        so that axis start and end components are linked into a
168        list of arguments and string components become whole date/time
169        strings.
170        """
171        #print "Content-Type: text/html\n\n"
172        #print "<P>".join(["%s:%s" % (n,v) for (n,v) in self.args.items()])     
173        dateTimeTemplate="%.4d-%.2d-%.2dT%.2d:%.2d:%f"
174        timeAxisItems=getDictSubsetMatching(self.args, "axis_.*_low\.time\.year", "regex")
175       
176        # Must have come from dxui so if year is there all others will be
177        for name, value in timeAxisItems.items():
178            axisIndex=re.match(r"axis_(.*)_low\.time\.year", name).groups()[0]
179            tcList=[]
180            for lh in ("low", "high"):
181                for t in "year month day hour minute second".split():
182                    argName="axis_%s_%s.time.%s" % (axisIndex, lh, t)
183                    tcList.append(self.args[argName])
184                    del self.args[argName]
185       
186            # Now compile into start and end date time
187            floatList=[float(i) for i in tcList]
188            self.args["axis_%s" % axisIndex]=[dateTimeTemplate % tuple(floatList[:6]),
189                                              dateTimeTemplate % tuple(floatList[6:])]
190
191        otherAxisItems=getDictSubsetMatching(self.args, "axis_.*_low$", "regex")
192        for name, value in otherAxisItems.items():
193            axisIndex=re.match(r"axis_(.*)_low$", name).groups()[0]
194            iList=[]
195            for lh in ("low", "high"):
196                argName="axis_%s_%s" % (axisIndex, lh)
197                iList.append(self.args[argName])
198                del self.args[argName]
199       
200            try:
201                if zsiv>=2.0:
202                    iList=[float(i) for i in iList]
203                else:
204                    iList=[int(i) for i in iList]
205            except:
206                try:
207                    iList=[int(i) for i in iList]
208                except:
209                    pass
210           
211            self.args["axis_%s" % axisIndex]=iList
212       
213        #print "Content-Type: text/html\n\n"
214        #print "<P>".join(["%s:%s" % (n,v) for (n,v) in self.args.items()])
215       
216
217    def _destroyUnnecessaryArgs(self):
218        """
219        Destroy the arguments that should not be passed to the main dx Web Service.
220        """
221        if self.args.has_key("password"):
222            del self.args["password"]   
223       
224       
225    def _checkSecurity(self):
226        """
227        If security is switched on with RESTRICTED_DATA=1 then this will
228        call the local implementation of the security.
229        """
230        if SECURITY_MODEL=="basic" or SECURITY_MODEL=="badc":
231            if SECURITY_MODEL=="basic":
232                secClass=SecurityViaCGI
233            elif SECURITY_MODEL=="badc":
234                secClass=BADCSecurityViaCGI
235
236            secChecker=apply(secClass, [self.username, self.password, self.secureToken])
237       
238            # Deal with logout
239            ############# NOTE - doesn't destroy session server side (yet)
240            if self.args.has_key("logout") and self.args["logout"]=="Logout":
241                secChecker.logout()
242                secCheck="You have been logged out."
243                self.username="undefined"
244                self.loginStatus="out"
245            else:
246                secCheck=secChecker.validate()     
247           
248            if type(secCheck)==type(""):
249                # Returned string means error in log in or logged out
250                self.loginMessage=secCheck
251            else:
252                self.loginStatus="in"
253                self.loginMessage=""
254
255                (self.secureToken, self.username, self.userRoles)=secCheck
256               
257        elif SECURITY_MODEL=="ndg":
258            from pydxc.NDGSecurityViaCGI import *
259            secChecker=NDGSecurityViaCGI(cookie=self.secureToken, urlArgs=self.fieldStorage)
260            self.loginlist=secChecker.getTrustedHostList()
261           
262            #(self.secureToken, self.username, self.userRoles)=
263            secCheck=secChecker.validate()
264            if type(secCheck)==type(""):
265                self.loginMessage=secCheck
266            else:
267                self.loginStatus="in"
268                self.loginMessage=""
269                (self.secureToken, self.username, self.userRoles)=secCheck
270
271            #o=open("/tmp/t", "w"); o.write("%s" % repr(secCheck)); o.close()
272           
273            # Delete field storage so not visible anywhere
274            #del self.fieldStorage
275
276               
277    def _callDXWebService(self, args):
278        """
279        According to the arguments given this binds to an appropriate
280        Web Service and calls it with the relevant arguments.
281        """
282        #print "Content-Type: text/html\n\n" ; print self.userRoles
283        # Just print the login page if not logged in and login required
284        if RESTRICTED_DATA==1 and self.loginStatus=="out":
285            self.displayer._displayHTTPHeader()
286            self.displayer._displayHTMLHeader()
287            #print "FIELD STORAGE:", self.fieldStorage
288            self.displayer._displayIntroduction()
289            #print self.loginlist           
290            if SECURITY_MODEL in ("basic", "badc"):
291                self.displayer._displayLoginBar(self.username, self.loginStatus, self.loginMessage)
292            elif SECURITY_MODEL=="ndg":
293                argString="?"
294                for key in self.fieldStorage.keys():
295                  if key.find("datasetURI_")>-1:
296                    if argString!="?": 
297                        gapper="&"
298                    else:
299                        gapper=""
300                    argString=argString+("%s%s=%s" % (gapper, key, self.fieldStorage.getvalue(key)))
301                    if argString=="?": argString=""
302                self.displayer._displayNDGLoginBar(self.loginlist, argString)
303                # Delete field storage so not visible anywhere
304                del self.fieldStorage
305            return     
306                             
307        # Set up SOAP bindings 
308        if CALL_METHOD.upper()=="WS":
309            self.server=WSCaller()
310        elif CALL_METHOD.upper()=="LOCAL":
311            serverLocation=LOCAL_SERVER_PACKAGE
312            sys.path.append(serverLocation)
313            print sys.path
314            self.server=LocalCaller()
315
316        # Get session ID
317        self._getSession()
318       
319        # Check if complete
320        isCompleteStatus=self._checkIfComplete()
321       
322
323        # Perform actions as requested by user
324        if self.args.has_key("action"):
325            actionReturn=self._performActions()
326            if actionReturn=="Display footer then complete":
327                return
328       
329        # Clear request if required
330        if self.args.has_key("clearRequest") or self.args.has_key("newRequest"):
331            if self.args.has_key("clearRequest"):
332                del self.args["clearRequest"]
333            else:
334                del self.args["newRequest"] 
335            self.sessionID, self.secureToken=self.server.callServerMethod("newRequest", 
336                                                  [self.sessionID, self.secureToken])                                           
337
338        # Make sure instance and args sessionID are the same
339        self.args["sessionID"]=self.sessionID
340       
341        # Ensure args contains secureToken object
342        if not self.args.has_key("secureToken"): 
343            self.args["secureToken"]=self.secureToken
344
345        # Get the options (and make selections if appropriate)
346        (optionCategories, options, optionStrings, summaryString, secureToken)=self._getLatestOptions() 
347       
348        # Get summary of request and determine number of datasets   
349        #self.summary=self.server.callServerMethod("summariseRequest", [self.sessionID, self.secureToken])[0]
350           
351        # Analyse the request
352        self._respondToOptionCategories(optionCategories, options, optionStrings, summaryString)       
353   
354       
355    def _getSession(self):
356        """
357        Checks if a session is already underway and starts one if not.
358        """           
359        # If no session then start a session and get a sessionID
360        if not self.args.has_key("sessionID") or self.args["sessionID"] in (None, "None", "undefined"):
361            # Start session if not known about
362            #print "Content-type: text/html\n\n", self.server.callServerMethod("startSession", [self.username, self.password, self.secureToken])
363            if zsiv>=2.0:
364                self.sessionID, self.secureToken=self.server.callServerMethod("startSession", [self.username, self.password, self.secureToken])[0]
365            else:
366                self.sessionID, self.secureToken=self.server.callServerMethod("startSession", [self.username, self.password, self.secureToken])
367        else:
368            self.sessionID=self.args["sessionID"] 
369           
370           
371    def _checkIfComplete(self):
372        """
373        Checks if complete, returns 1 (yes) or 0 (no).
374        """
375        isCompleteStatus=0       
376        if self.args.has_key("isComplete"):     
377            isCompleteStatus, self.secureToken=self.server.callServerMethod("isComplete", 
378                                                      [self.sessionID, self.secureToken])     
379        return isCompleteStatus
380       
381       
382    def _performActions(self):
383        """
384        If "action" argument received then do the appropriate action.
385        """     
386        if 1:  # Move all back <-- one tab and delete this line
387            action=self.args["action"]
388            if action=="viewRequestSummary":
389                summaryString=self.server.callServerMethod("summariseRequest", [self.sessionID, self.secureToken])[0]
390                if zsiv>=2.0:
391                    summaryString=summaryString[0][0]
392
393                self.displayer._displayHTMLHeader()
394                if RESTRICTED_DATA==1: 
395                    roleString=",".join(self.userRoles)
396                    self.displayer._displayLoginBar(self.username, loginStatus="in", loginMessage="with roles: "+roleString)
397                self.displayer._displayRequestSummaryTable(createSummaryDict(summaryString))
398                self.displayer._displayReturnLine(self.sessionID)
399                return "Display footer then complete"
400
401            elif action=="saveRequest":
402                x=self.server.callServerMethod("getDataSubsetSpecifier",
403                                                           [self.sessionID, self.secureToken])
404
405                #dataSubsetSpecifier, self.secureToken=x                                           
406                self.displayer._displayHTTPHeader() 
407                self.displayer._displayHTMLHeader()     
408                if RESTRICTED_DATA==1: self.displayer._displayLoginBar(self.username, loginStatus="in", loginMessage="with roles: "+str(self.userRoles))       
409                self.displayer._displaySaveRequestOptions(dataSubsetSpecifier, self.sessionID)
410                self.displayer._displayReturnLine(self.sessionID)
411                return "Display footer then complete"
412               
413            elif action=="uploadRequest":
414                self.displayer._displayHTTPHeader() 
415                self.displayer._displayHTMLHeader()
416                if RESTRICTED_DATA==1: self.displayer._displayLoginBar(self.username, loginStatus="in", loginMessage="with roles: "+str(self.userRoles))
417                self.displayer._displayUploadRequestOptions(self.sessionID)
418                self.displayer._displayReturnLine(self.sessionID)               
419                return "Display footer then complete"           
420               
421            elif action=="parseUploadedFile":
422                xmlFileString=self.tempFileLink.read()
423                status, self.secureToken=self.server.callServerMethod("uploadRequest", 
424                                              [self.sessionID, xmlFileString, self.secureToken])
425
426            elif action=="getStatus":
427                response=self.server.callServerMethod("getStatus", [self.sessionID, self.secureToken])
428                if zsiv>=2.0:
429                    response=response[0]
430               
431                (status, outputFilePaths, self.secureToken)=response
432                status=status[0]
433                outputFilePaths=outputFilePaths[0]
434               
435                self.displayer._displayHTTPHeader()
436                #print outputFilePaths
437                self.displayer._displayHTMLHeader()
438                if RESTRICTED_DATA==1: self.displayer._displayLoginBar(self.username, loginStatus="in", loginMessage="with roles: "+str(self.userRoles))
439                self.displayer._displayStatus(status, self.sessionID)
440                if status=="complete":
441                    self.displayer._displayOutputFileList(outputFilePaths)
442                return "Display footer then complete"
443       
444
445
446         
447    def _getLatestOptions(self):
448        """
449        If target page provided then get the options for that page,
450        otherwise get whatever options are presented by the dx and
451        send latest selections to update request.
452        """
453        if self.args.has_key("targetPage"):
454            tp=self.args["targetPage"]
455            tp2catMap={"DatasetGroupPage":"datasetGroup", "DatasetPage":"dataset",
456                       "VariablesPage":"variable", "DomainAndFormatPage":"domainAndFormat"}
457            optionCategory=tp2catMap[tp]
458            self.args["optionCategoryRequested"]=optionCategory
459            # Need to delete targetPage otherwise it will stay forever
460            del self.args["targetPage"]
461
462        response=self.server.callServerMethod("selectOptions", [self.sessionID, self._packArgsAsList(self.args)])
463        #self.displayer._displayHTTPHeader()
464        #print response, type(response)
465        (optionCategories, options, optionStrings, summaryString, secureToken)=self._translateResponse(response)       
466        #print (optionCategories, options, optionStrings, summaryString, secureToken)   
467        return (optionCategories, options, optionStrings, summaryString, secureToken)
468
469   
470    def _translateResponse(self, response):
471        """
472        Takes an object received from the WS and translates to:
473        (optionCategories, options, optionStrings, secureToken)
474        """
475        response=response[0]       
476        if zsiv>=2.0:
477            response=response[0][0]
478        optionCategories=[item[0] for item in response[:-2]]
479        options=[item[1] for item in response[:-2]]
480        optionStrings=options
481        summaryString=response[-2]
482        secureToken=response[-1]               
483        return (optionCategories, options, optionStrings, summaryString, secureToken)
484       
485
486    def _respondToOptionCategories(self, optionCategories, options, optionStrings, summaryString):
487        """
488        Work out what the option category is and respond by displaying
489        the appropriate user interface.
490        """     
491        summaryDict=createSummaryDict(summaryString)
492        # Analyse the option categories now
493        if optionCategories=="No category" or optionCategories==[]:
494            optcat=None
495        else:
496            optcat=optionCategories[0].split("_")[0]
497       
498        if optcat==None and (RESTRICTED_DATA==1 and summaryString.find("dataset")<0):
499            self.displayer._displayHTTPHeader()
500            self.displayer._displayHTMLHeader() 
501            print "<P><B>You do not have the credentials to view any datasets - Sorry!</B><P>" 
502       
503        # Can display simple form for these categories 
504        elif optcat in ("datasetGroup", "dataset", "variable"):         
505            self.displayer._displayHTTPHeader()
506            self.displayer._displayHTMLHeader()     
507            if RESTRICTED_DATA==1: self.displayer._displayLoginBar(self.username, loginStatus="in", loginMessage="with roles: "+str(self.userRoles))
508            self.displayer._displayDatasetSummaryLine(summaryString, optionCategories[0], self.sessionID)
509            self.displayer._displayMainTableHeader(self.sessionID)
510            self.displayer._displayOptionsTable(summaryDict, optionCategories, options, optionStrings, self.sessionID, self.sessionObject)
511            self.displayer._displayMainTableFooter()
512       
513        # Need to make a number of web service calls to get information
514        # for each domain as well as output format which are all
515        # displayed on the same page.   
516        elif optcat=="axis":
517            self.displayer._displayHTTPHeader() 
518            self.displayer._displayHTMLHeader()
519            if RESTRICTED_DATA==1: self.displayer._displayLoginBar(self.username, loginStatus="in", loginMessage="with roles: "+str(self.userRoles))
520            self.displayer._displayDatasetSummaryLine(summaryString, optionCategories[0], self.sessionID)
521            self.displayer._displayMainTableHeader(self.sessionID, onSubmit=""" onSubmit='return validateAllTimeSelections()'""")
522            self.displayer._displayDomainOptions(summaryDict, optionCategories, options, optionStrings, self.sessionID)
523            self.displayer._displayMainTableFooter()
524               
525        elif optcat==None: 
526            # If there are no option categories then the request is likely to be complete
527            # Display confirmation page if needed
528            if CONFIRMATION_PAGE==1 and not self.args.has_key("confirm"):
529                self.displayer._displayHTTPHeader() 
530                self.displayer._displayHTMLHeader()
531                if RESTRICTED_DATA==1: self.displayer._displayLoginBar(self.username, loginStatus="in", loginMessage="with roles: "+str(self.userRoles))
532                self.displayer._displayDatasetSummaryLine(summaryString, optionCategories, self.sessionID)         
533                self.displayer._displayMainTableHeader(self.sessionID)   
534
535                # Get request costs here...
536                response=self.server.callServerMethod("getExtractionCosts", [self.sessionID, self.secureToken])
537                if zsiv>=2.0:
538                    response=response[0]
539                (estimatedDuration, estimatedVolume)=response[0:2]
540                summaryDict["globals"]["estimatedDuration"]=estimatedDuration
541                summaryDict["globals"]["estimatedVolume"]=estimatedVolume
542                #print '<INPUT NAME="action" TYPE="hidden" VALUE="requestCosts">'
543                self.displayer._displayConfirmationSection(summaryDict)
544               
545            # Or display final job processing page     
546            elif self.args.has_key("action") and self.args["action"]=="requestCosts":   
547                response=self.server.callServerMethod("getExtractionCosts", 
548                                                          [self.sessionID, self.secureToken])  #[0][0:2]
549                if zsiv>=2.0:
550                    response=response[0]
551
552                (estimatedDuration, estimatedVolume)=response[0:2]
553                self.displayer._displayHTTPHeader() 
554                self.displayer._displayHTMLHeader()
555                if RESTRICTED_DATA==1: self.displayer._displayLoginBar(self.username, loginStatus="in", loginMessage="with roles: "+str(self.userRoles))
556                self.displayer._displayProcessingSection(estimatedDuration, estimatedVolume, self.sessionID)
557                response=self.server.callServerMethod("createOutput", [self.sessionID, self.secureToken])  #[0]
558               
559                if zsiv>=2.0:
560                    response=response[0]
561                pathList, self.secureToken=response
562                #print "<P>", pathList, len(pathList)
563                self.displayer._displayOutputFileList(pathList) 
564
565            elif self.args.has_key("action") and self.args["action"]=="getOutput":
566                self.displayer._displayHTTPHeader()
567                self.displayer._displayHTMLHeader()
568                if RESTRICTED_DATA==1: self.displayer._displayLoginBar(self.username, loginStatus="in", loginMessage="with roles: "+str(self.userRoles))
569                estimatedDuration=float(summaryDict["globals"]["estimatedDuration"])
570                estimatedVolume=float(summaryDict["globals"]["estimatedVolume"])
571                self.displayer._displayProcessingSection(estimatedDuration, estimatedVolume, self.sessionID)
572                response=self.server.callServerMethod("createOutput", [self.sessionID, self.secureToken])  #[0]
573
574                if zsiv>=2.0:
575                    response=response[0]
576                pathList, self.secureToken=response
577                #print "<P>", pathList, len(pathList)
578                #self.displayer._displayOutputFileList(pathList)
579
580
581
582    def _packArgsAsList(self, args):
583        """
584        In order to work with ZSI SOAP library need to pack up arguments as a list
585        of [keyword, value] pairs rather than a dictionary.
586        """
587        newList=[]
588        for key, value in args.items():
589            newList.append([key, value])
590        return newList
591       
592
593if __name__=="__main__":
594
595    DXCGIClient()       
Note: See TracBrowser for help on using the repository browser.