source: TI03-DataExtractor/branches/old_stuff/dxui @ 793

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI03-DataExtractor/branches/old_stuff/dxui@793
Revision 793, 22.9 KB checked in by astephen, 13 years ago (diff)

Put all the old code in the old_stuff branch.

  • Property svn:executable set to *
Line 
1#!/usr/local/badc/linux/redhat7.3/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# Set up cgi error reporting to screen if DEBUG is on
17BASEDIR="/usr/local/dxc"
18import sys, os
19sys.path.append(BASEDIR)
20sys.path.append(os.path.split(BASEDIR)[0])
21
22from clientConfig import DEBUG
23if DEBUG==1:
24    import cgitb 
25    cgitb.enable() 
26
27# Import standard library modules
28import cgi, random, time, re
29
30# Import SOAP library
31from ZSI.client import Binding
32       
33# Import package modules
34from dxc import *
35
36# Import package module
37from common import *
38import times
39
40 
41class CGIClient:
42    """
43    The controlling class for interacting with the dx via the web.
44    """
45   
46    def __init__(self):
47        """
48        Initiates the instance setting up the appropriate
49        internal objects and calls to the relevant classes depending
50        on the configuration and arguments provided.
51        """
52        self.username=None
53        self.password=None
54        self.secureToken=None
55        self.userRoles=[]
56        self.loginStatus="out"
57        self.loginMessage=""
58        self.sessionObject={}
59       
60        self.displayer=DisplayManager()
61
62        # Parse the arguments
63        try:
64            self._parseArgs()
65        except:
66            if DEBUG==1:
67                raise
68            else:
69                CGIErrorHandler("Error parsing arguments: "+str(sys.exc_type), noheader=0)
70
71        # Destroy any arguments not intended to pass any further
72        try:
73            self._destroyUnnecessaryArgs()
74        except:
75            if DEBUG==1:
76                raise
77            else:
78                CGIErrorHandler("Error destroying arguments: "+str(sys.exc_type), noheader=0)
79
80        # If switched on check security       
81        if RESTRICTED_DATA==1:
82            try:
83                self._checkSecurity()
84            except:
85                if DEBUG==1:
86                    raise
87                else:
88                    CGIErrorHandler("Security error: "+str(sys.exc_type), noheader=0)
89        else:
90            self.secureToken=None             
91           
92        # Call the dx Web Service with the arguments provided       
93        try:
94            self._callDXWebService(self.args)
95        except:
96            if DEBUG==1:
97                raise
98            else:
99                CGIErrorHandler("Error calling dx Web Service: "+str(sys.exc_type), noheader=0)       
100
101        try:   
102            self.displayer._displayHTMLFooter() 
103        except:
104            if DEBUG==1:
105                raise
106            else:
107                CGIErrorHandler("Error generating HTML footer: "+str(sys.exc_type), noheader=0)
108       
109       
110    def _parseArgs(self):
111        """
112        Parses the arguments sent, if any, ready for sending to the
113        web service.
114        """
115        # Get arguments
116        self.args={}                           
117        args=cgi.FieldStorage() 
118       
119        # Get upload file link
120        if args.has_key("uploadedFile"):
121            self.tempFileLink=args["uploadedFile"].file
122            self.args["fileName"]=args["uploadedFile"].filename
123
124        # Populate the argument dictionary
125        for key in args.keys(): 
126            if key=="uploadedFile": continue
127            self.args[key]=args.getvalue(key) 
128                   
129        if self.args.has_key("sessionID") and self.args["sessionID"]=="None":
130            self.args["sessionID"]=None     
131         
132        # Check for secure items, destroy if necessary and assign as instance variables   
133        if self.args.has_key("yousirnaim"):
134            self.username=self.args["yousirnaim"]
135            self.args["username"]=self.username
136            del self.args["yousirnaim"]
137           
138        if self.args.has_key("parcewerd"):
139            self.password=self.args["parcewerd"] 
140            del self.args["parcewerd"] 
141               
142        if self.args.has_key("secureToken"):
143            self.secureToken=self.args["secureToken"]
144            del self.args["secureToken"]   
145       
146        if not self.args.has_key("username"):
147            self.args["username"]=None
148           
149        self._determineNumberOfDatasets()
150        self._parsePartialDimensionArgs()
151
152
153    def _determineNumberOfDatasets(self):
154        """
155        Returns the number of datasets the user is requesting.
156        This affects how the output is produced.
157        """
158        ndsetPattern=re.compile(r"^\w+_(\d+)$")
159        self.numberOfDatasets=1  # Dummy value for now
160       
161        for key in self.args.keys(): 
162            match=ndsetPattern.match(key)
163            if match:
164                dsetNumber=int(match.groups()[0])
165                if dsetNumber>self.numberOfDatasets:
166                    self.numberOfDatasets=dsetNumber
167
168        if self.args.has_key("numberOfDatasets"):
169            try:
170                self.numberOfDatasets=int(self.args["numberOfDatasets"])
171            except:
172                self.numberOfDatasets=int(self.args.getvalue("numberOfDatasets"))       
173
174
175    def _parsePartialDimensionArgs(self):
176        """
177        Parses dimension arguments so that they are of the
178        correct types to send to the server.
179        """                 
180        # Make a list of all the possible time keys to parse in time args
181        allTimeKeys=createTimeKeyList()
182                       
183        timeKeyCount=0   
184        for key in self.args.keys():
185            if key in allTimeKeys: 
186                # Make sure it is an integer
187                try: 
188                    self.args[key]=int(self.args[key]) 
189                    timeKeyCount=timeKeyCount+1
190                except: 
191                    pass                   
192            elif key in HORIZ_KEYS:
193                try:
194                    self.args[key]=float(self.args[key])
195                except:
196                    pass
197               
198        if timeKeyCount>0:
199            self._compileDateTimeArgs()             
200                   
201
202    def _compileDateTimeArgs(self):
203        """
204        Converts a list of arguments for each date and time component
205        to a start and end date/time following the xsd:dateTime description.
206        """
207        dateTimeString="%.4d-%.2d-%.2dT%.2d:%.2d:%.2d"
208        # Build item lists of year, month, day...second for each dataset
209        # both start and end date times.
210        # Also delete the original component items for clarity.
211        for n in range(1, self.numberOfDatasets+1):
212            startList=[]
213            endList=[]
214            for tk in TIME_KEYS:
215                startList.append(self.args["start%s_%s" % (tk, n)])
216                del self.args["start%s_%s" % (tk, n)]
217                endList.append(self.args["end%s_%s" % (tk, n)]) 
218                del self.args["end%s_%s" % (tk, n)]             
219           
220            self.args["startDateTime_%s" % n]=dateTimeString % tuple(startList)
221            self.args["endDateTime_%s" % n]=dateTimeString % tuple(endList)
222           
223
224    def _destroyUnnecessaryArgs(self):
225        """
226        Destroy the arguments that should not be passed to the main dx Web Service.
227        """
228        if self.args.has_key("password"):
229            del self.args["password"]   
230       
231       
232    def _checkSecurity(self):
233        """
234        If security is switched on with RESTRICTED_DATA=1 then this will
235        call the local implementation of the security.
236        """
237        secChecker=SecurityViaCGI(self.username, self.password, self.secureToken)
238       
239        # Deal with logout
240        ############# NOTE - doesn't destroy session server side (yet)
241        if self.args.has_key("logout") and self.args["logout"]=="Logout":
242            secChecker.logout()
243            secCheck="You have been logged out."
244        else:
245            secCheck=secChecker.validate()         
246           
247        if type(secCheck)==type(""):
248            # Returned string means error in log in or logged out
249            self.loginMessage=secCheck
250        else:
251            self.loginStatus="in"
252            self.loginMessage=""
253            print "Content-Type: text/html\n\n"
254            for i in secCheck:
255               print "<P>TEST:%s<P>" % i
256            print "token, username, roles"
257            (self.secureToken, self.username, self.userRoles)=secCheck   
258
259               
260    def _callDXWebService(self, args):
261        """
262        According to the arguments given this binds to an appropriate
263        Web Service and calls it with the relevant arguments.
264        """
265        # Just print the login page if not logged in and login required
266        if RESTRICTED_DATA==1 and self.loginStatus=="out":
267            self.displayer._displayHTTPHeader()
268            self.displayer._displayHTMLHeader()
269            self.displayer._displayIntroduction()           
270            self.displayer._displayLoginBar(self.username, self.loginStatus, self.loginMessage)
271            return     
272             
273        # Set up SOAP bindings
274        #trace=open('/tmp/tracefile.txt','w')
275        #self.server=Binding(url='', host=SOAP_SERVER_NAME, port=SOAP_SERVER_PORT, tracefile=trace)     
276        if CALL_METHOD.upper()=="WS":
277            self.server=WSCaller()
278        elif CALL_METHOD.upper()=="LOCAL":
279            serverLocation=LOCAL_SERVER_PACKAGE
280            sys.path.append(serverLocation)
281            print sys.path
282            self.server=LocalCaller()
283
284        # Get session ID
285        self._getSession()
286       
287        # Check if complete
288        isCompleteStatus=self._checkIfComplete()
289       
290        # If an argument received as "numberOfDatasets" then call WS to set that
291        if self.args.has_key("numberOfDatasets"):
292            #response=self._webServiceCallWrapper(self.server.setNumberOfDatasets(self.sessionID, self.args["numberOfDatasets"], self.secureToken))
293            #status, self.secureToken=response
294            status, self.secureToken=self.server.callServerMethod("setNumberOfDatasets", 
295                                          [self.sessionID, self.args["numberOfDatasets"], 
296                                           self.secureToken])
297       
298        # Perform actions as requested by user
299        if self.args.has_key("action"):
300            actionReturn=self._performActions()
301            if actionReturn=="Display footer then complete":
302                return
303       
304        # Clear request if required
305        if self.args.has_key("clearRequest") or self.args.has_key("newRequest"):
306            if self.args.has_key("clearRequest"):
307                del self.args["clearRequest"]
308            else:
309                del self.args["newRequest"]
310            #response=self._webServiceCallWrapper(self.server.newRequest(self.sessionID, self.secureToken))
311            #self.sessionID, self.secureToken=response     
312            self.sessionID, self.secureToken=self.server.callServerMethod("newRequest", 
313                                                  [self.sessionID, self.secureToken])                                           
314
315        # Make sure instance and args sessionID are the same
316        self.args["sessionID"]=self.sessionID
317       
318        #out=open("/tmp/tmp/st", "w"); out.write(self.secureToken+"\n"); out.close()
319        # ***Can delete?***
320        # Ensure args contains secureToken object
321        if not self.args.has_key("secureToken"): 
322            self.args["secureToken"]=self.secureToken
323
324        # Get the options (and make selections if appropriate)
325        (optionCategories, options, optionStrings, secureToken)=self._getLatestOptions()
326
327        # ***Can probably delete ***Update secure token after
328        #if RESTRICTED_DATA==1:
329        #    self._upDateSecureToken(secureToken)       
330       
331        # Get summary of request and determine number of datasets   
332        #response=self._webServiceCallWrapper(self.server.summariseRequest(self.sessionID, self.secureToken))
333        #self.summary=response[0]
334        self.summary=self.server.callServerMethod("summariseRequest", [self.sessionID, self.secureToken])[0]
335        numDatasetsMatch=re.search(r"numberOfDatasets:\t(\d+)", self.summary)
336        if numDatasetsMatch:
337            self.numberOfDatasets=int(numDatasetsMatch.groups()[0])
338           
339        # Analyse the request
340        self._respondToOptionCategories(optionCategories, options, optionStrings)       
341   
342       
343    def _getSession(self):
344        """
345        Checks if a session is already underway and starts one if not.
346        """           
347        # If no session then start a session and get a sessionID
348        if not self.args.has_key("sessionID") or self.args["sessionID"] in (None, "None"):
349            # Start session if not known about
350            #response=self._webServiceCallWrapper(self.server.startSession(self.username, self.password, self.secureToken))
351            #self.sessionID, self.secureToken=response
352            #self.sessionID=self._deUnicodeObject(self.sessionID)
353            self.sessionID, self.secureToken=self.server.callServerMethod("startSession",
354                                                 [self.username, self.password, self.secureToken])
355        else:
356            self.sessionID=self.args["sessionID"] 
357           
358           
359    def _checkIfComplete(self):
360        """
361        Checks if complete, returns 1 (yes) or 0 (no).
362        """
363        isCompleteStatus=0       
364        if self.args.has_key("isComplete"):
365            #response=self._webServiceCallWrapper(self.server.isComplete(self.sessionID, self.secureToken))
366            #isCompleteStatus, self.secureToken=response       
367            isCompleteStatus, self.secureToken=self.server.callServerMethod("isComplete", 
368                                                      [self.sessionID, self.secureToken])     
369        return isCompleteStatus
370       
371       
372    def _performActions(self):
373        """
374        If "action" argument received then do the appropriate action.
375        """     
376        if 1:  # Move all back <-- one tab and delete this line
377            action=self.args["action"]
378            if action=="viewRequestSummary":
379                #response=self._webServiceCallWrapper(self.server.summariseRequest(self.sessionID, self.secureToken))       
380                #summary=response[0]
381                summary=self.server.callServerMethod("summariseRequest", [self.sessionID, self.secureToken])[0]
382                self.displayer._displayHTTPHeader() 
383                self.displayer._displayHTMLHeader()
384                self.displayer._displayRequestSummaryTable(summary)
385                self.displayer._displayReturnLine(self.sessionID)
386                return "Display footer then complete"
387               
388            elif action=="saveRequest":
389                #response=self._webServiceCallWrapper(self.server.getDataSubsetSpecifier(self.sessionID, self.secureToken))
390                #dataSubsetSpecifier, self.secureToken=response
391                dataSubsetSpecifier, self.secureToken=self.server.callServerMethod("getDataSubsetSpecifier",
392                                                           [self.sessionID, self.secureToken])
393                self.displayer._displayHTTPHeader() 
394                self.displayer._displayHTMLHeader()             
395                self.displayer._displaySaveRequestOptions(dataSubsetSpecifier, self.sessionID)
396                self.displayer._displayReturnLine(self.sessionID)
397                return "Display footer then complete"
398               
399            elif action=="uploadRequest":
400                self.displayer._displayHTTPHeader() 
401                self.displayer._displayHTMLHeader()
402                self.displayer._displayUploadRequestOptions(self.sessionID)
403                self.displayer._displayReturnLine(self.sessionID)               
404                return "Display footer then complete"           
405               
406            elif action=="parseUploadedFile":
407                xmlFileString=self.tempFileLink.read()
408                #del self.args["uploadedFile"]
409                #response=self._webServiceCallWrapper(self.server.uploadRequest(self.sessionID, xmlFileString, self.secureToken))
410                #status, self.secureToken=response         
411                status, self.secureToken=self.server.callServerMethod("uploadRequest", 
412                                              [self.sessionID, xmlFileString, self.secureToken])
413       
414         
415    def _getLatestOptions(self):
416        """
417        If target page provided then get the options for that page,
418        otherwise get whatever options are presented by the dx and
419        send latest selections to update request.
420        """
421        if self.args.has_key("targetPage"):
422            tp=self.args["targetPage"]
423            tp2catMap={"DatasetGroupPage":"datasetGroup", "DatasetPage":"dataset",
424                       "VariablesPage":"variable", "DomainPage":"horizontalDomain"}
425            optionCategory=tp2catMap[tp]
426            #response=self._webServiceCallWrapper(self.server.getOptions(self.sessionID, self.secureToken, optionCategory))
427            #(optionCategories, options, optionStrings, secureToken)=response
428            (optionCategories, options, optionStrings, secureToken)=self.server.callServerMethod("getOptions", 
429                                                                         [self.sessionID, self.secureToken, 
430                                                                         optionCategory])
431        else: 
432            #print "Content-Type: text/html\n\n"
433            #args=self._packArgsAsList(self.args)
434            #print str(args)
435            #r=self.server.selectOptions(self.sessionID, [])
436            #raise str(r)
437            #r=apply(self.server.selectOptions, [self.sessionID, args])
438            #response=self._webServiceCallWrapper(apply(self.server.selectOptions, [self.sessionID, args]))
439            #response=self._deUnicodeObject(response)                       
440            #(optionCategories, options, optionStrings, secureToken)=response
441            (optionCategories, options, optionStrings, secureToken)=self.server.callServerMethod("selectOptions",
442                                                                       [self.sessionID, self._packArgsAsList(self.args)])
443       
444        return (optionCategories, options, optionStrings, secureToken)
445
446
447    def _respondToOptionCategories(self, optionCategories, options, optionStrings):
448        """
449        Work out what the option category is and respond by displaying
450        the appropriate user interface.
451        """     
452        # Analyse the option categories now
453        if optionCategories=="No category":
454            optcat=None
455        else:
456            optcat=optionCategories[0].split("_")[0]
457       
458        # Can display simple form for these categories 
459        if optcat in ("datasetGroup", "dataset", "variable"):           
460            self.displayer._displayHTTPHeader()
461            self.displayer._displayHTMLHeader()     
462            if RESTRICTED_DATA==1: self.displayer._displayLoginBar(self.username, loginStatus="in", loginMessage="with roles: "+str(self.userRoles))
463            self.displayer._displayDatasetSummaryLine(self.summary, optionCategories[0], self.sessionID)
464            self.displayer._displayMainTableHeader(self.sessionID)
465            self.displayer._displayOptionsTable(optionCategories, options, optionStrings, self.sessionID, self.sessionObject)
466            self.displayer._displayMainTableFooter()
467       
468        # Need to make a number of web service calls to get information
469        # for each domain as well as output format which are all
470        # displayed on the same page.   
471        elif optcat=="horizontalDomain":
472            self.displayer._displayHTTPHeader() 
473            self.displayer._displayHTMLHeader()
474            self.displayer._displayDatasetSummaryLine(self.summary, optionCategories[0], self.sessionID)
475            self.displayer._displayMainTableHeader(self.sessionID)         
476            print "<P><B>SPATIAL AND TEMPORAL SELECTION</B><P>"     
477            self.displayer._displayHorizontalDomainOptions(options)
478           
479            # Now get the vertical domain options
480            self.args["optionCategoryRequested"]="verticalDomain"
481            #response=self._webServiceCallWrapper(apply(self.server.selectOptions, [self.sessionID, self._packArgsAsList(self.args)]))
482            #response=self._deUnicodeObject(response)       
483            #(optionCategories, options, optionStrings, secureToken)=response   
484            (optionCategories, options, optionStrings, secureToken)=self.server.callServerMethod("selectOptions", 
485                                                                         [self.sessionID, self._packArgsAsList(self.args)])
486            self.displayer._displayVerticalSpatialDomainOptions(options)
487
488            # Now get the temporal domain options
489            self.args["optionCategoryRequested"]="temporalDomain"           
490            #response=self._webServiceCallWrapper(apply(self.server.selectOptions, [self.sessionID, self._packArgsAsList(self.args)]))
491            #response=self._deUnicodeObject(response)               
492            #(optionCategories, options, optionStrings, secureToken)=response   
493            (optionCategories, options, optionStrings, secureToken)=self.server.callServerMethod("selectOptions", 
494                                                                         [self.sessionID, self._packArgsAsList(self.args)])
495            self.displayer._displayTemporalDomainOptions(options)
496
497            # Now get the output format options
498            self.args["optionCategoryRequested"]="outputFormat"     
499            #response=self._webServiceCallWrapper(apply(self.server.selectOptions, [self.sessionID, self._packArgsAsList(self.args)]))
500            #response=self._deUnicodeObject(response)               
501            #(optionCategories, options, optionStrings, secureToken)=response       
502            (optionCategories, options, optionStrings, secureToken)=self.server.callServerMethod("selectOptions", 
503                                                                         [self.sessionID, self._packArgsAsList(self.args)])         
504            self.displayer._displayOutputFormatOptions(options, self.sessionID)   
505       
506        # If there are no option categories then the request is likely to be complete
507        elif optcat==None:
508       
509            # Display confirmation page if needed
510            if CONFIRMATION_PAGE==1 and not self.args.has_key("confirm"):
511                self.displayer._displayHTTPHeader() 
512                self.displayer._displayHTMLHeader()
513                self.displayer._displayDatasetSummaryLine(self.summary, optionCategories[0], self.sessionID)       
514                self.displayer._displayMainTableHeader(self.sessionID)   
515                print '<INPUT NAME="action" TYPE="hidden" VALUE="requestCosts">' 
516                self.displayer._displayConfirmationSection(self.summary)
517               
518            # Or display final job processing page     
519            elif self.args.has_key("action") and self.args["action"]=="requestCosts": 
520                #response=self._webServiceCallWrapper(self.server.getExtractionCosts(self.sessionID, self.secureToken))
521                #response=self._deUnicodeObject(response)
522                #(estimatedDuration, estimatedVolume)=response[0:2]                 
523                (estimatedDuration, estimatedVolume)=self.server.callServerMethod("getExtractionCosts", 
524                                                          [self.sessionID, self.secureToken])[0:2]
525                self.displayer._displayHTTPHeader() 
526                self.displayer._displayHTMLHeader()
527                self.displayer._displayProcessingSection(estimatedDuration, estimatedVolume, self.sessionID)       
528                #response=self._webServiceCallWrapper(self.server.createOutput(self.sessionID, self.secureToken))
529                #response=self._deUnicodeObject(response)               
530                #pathList, self.secureToken=response
531                pathList, self.secureToken=self.server.callServerMethod("createOutput", [self.sessionID, self.secureToken])
532                # Update user on screen
533                print "<P><B>Your request has been processed.</B><HR><P>"               
534                self.displayer._displayOutputFileList(pathList[0])
535                               
536
537    def _webServiceCallWrapper(self, response):
538        """
539        Analyses the response from a Web Service call to check if an error has
540        occurred. If so it parses and reports the error. Otherwise it returns 
541        the object returned from the Web Service call.
542        """
543        if type(response)==type(""):
544            # String returns are errors
545            self.displayer._displayErrorPage(response)
546            self.displayer._displayHTMLFooter()
547            sys.exit()
548        else:
549            return response                     
550
551
552    def _packArgsAsList(self, args):
553        """
554        In order to work with ZSI SOAP library need to pack up arguments as a list
555        of [keyword, value] pairs rather than a dictionary.
556        """
557        newList=[]
558        for key, value in args.items():
559            newList.append([key, value])
560        return newList
561       
562
563    def _deUnicodeObject(self, obj):
564        """
565        Returns an identical object with all unicode strings returned as normal strings.
566        """
567        return deUnicodeObject(obj)
568        """if type(obj)==type(u""):
569            return str(obj)
570        elif type(obj)==type([]):
571            newList=[]
572            for item in obj:
573                if type(item)==type(u""):
574                    newList.append(str(item))
575                elif type(item)==type([]):
576                    extraList=[]
577                    for i in item:
578                        if type(i)==type(u""):
579                            extraList.append(str(i))
580                        elif type(i)==type([]):
581                            anotherList=[]
582                            for a in i:
583                                if type(a)==type(u""):
584                                    anotherList.append(str(a))
585                                elif type(a)==type([]):
586                                    wowList=[]
587                                    for x in a:
588                                        if type(x)==type(u""):
589                                            wowList.append(str(x))
590                                        else:
591                                            wowList.append(x)
592                                    anotherList.append(wowList[:])
593                                else:
594                                    anotherList.append(a)
595                            extraList.append(anotherList[:])
596                        else:
597                            extraList.append(i)
598                    newList.append(extraList[:])
599                else:
600                    newList.append(item)
601            return newList
602        else:
603            return obj"""
604
605       
606    def _validateRequest(self):
607        """
608        Calls the validation Web Service to check that the current selection is
609        within the available options. This will be needed as users can write their
610        own XML files and upload them.
611        """
612        pass
613       
614
615if __name__=="__main__":
616
617    CGIClient() 
Note: See TracBrowser for help on using the repository browser.