source: TI03-DataExtractor/trunk/cgi/dxui @ 1184

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI03-DataExtractor/trunk/cgi/dxui@1184
Revision 1184, 17.3 KB checked in by astephen, 15 years ago (diff)

Safe version pre-security installation and zsi versioning.

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