source: TI03-DataExtractor/branches/old_stuff/dx-webservice/dxSoapServer.py @ 793

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

Put all the old code in the old_stuff branch.

Line 
1#!/usr/bin/env python
2"""
3dxSoapServer.py
4===============
5
6A simple web service implementation of the dx.
7
8"""
9
10import sys
11sys.path.append('/usr/local/src/soap/SOAPpy-0.11.6/build/lib')
12
13# Import required modules
14import string 
15import sys
16import os 
17import time 
18import cgi 
19import commands 
20
21# Add your location path for the dx package
22BASEDIR="/usr/local/dx_webservice"
23sys.path.append(os.path.split(BASEDIR)[0]) 
24 
25# Set home directory for use by CDAT
26USER_HOMEDIR="/var/lib/wwwrun"
27os.environ["HOME"]=USER_HOMEDIR 
28
29# Bring DataExtractor package into local scope
30from dx_webservice import * 
31 
32# Add any other locally required directories in which you have modules.
33for path in LOCAL_PYTHONPATH:
34   sys.path.insert(0, path) 
35   
36# Set up cgi error reporting to screen if DEBUG is on
37if DEBUG==1:
38    import cgitb 
39    cgitb.enable() 
40
41
42class ControlExtract:
43    """
44    ControlExtract class - controls the overall process flow of the dx.
45    This class is called through each stage of the extraction process
46    and responds according to the environment and arguments provided.
47    """
48 
49    def __init__(self, args, dummy="confusedaboutnumberofargs"):
50        """
51        __init__ method - takes in a group of arguments and then calls the appropriate
52        methods to process the request.
53        """
54        #print args
55        if args.has_key("callMethod"):  self.callMethod=args["callMethod"]
56        self.username=None 
57        self.allowed_groups=""
58        self.response=None 
59        self.request={}
60
61        # Parse the arguments
62        try:
63            self._parseArgs(args)
64        except:
65            if DEBUG==1:
66                raise
67            else:
68                ErrorHandler("Error parsing 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                    ErrorHandler("Security error: "+str(sys.exc_type), noheader=0)
79
80        # Construct the request object
81        try:   
82            self._constructRequest()   
83        except:
84            if DEBUG==1:
85                raise
86            else:
87                ErrorHandler("Error constructing request: "+str(sys.exc_type), noheader=0)
88
89        # Create the user interface and process data (if needed).
90        try:   
91            self._createUserInterface()
92        except:
93            if DEBUG==1:
94                raise
95            else:
96                ErrorHandler("Error generating user interface or output: "+str(sys.exc_type), noheader=0)
97           
98        # Write request to a file if it has some content
99        if overlap(["datasetGroup_1", "datasetURI_1", "req_id"], self.request.keys()): 
100            self.request["access_time"]=time.time() 
101            self.requester.writeRequest(self.request) 
102           
103        print self.request   
104           
105    def _parseArgs(self, args):
106        """
107        _parseArgs method - parses the argument dictionary that are sent.
108        """ 
109        if args==None: 
110            self.callMethod="CGI"
111            self.args={} 
112            form=cgi.FieldStorage() 
113
114            # Populate the argument dictionary
115            for key in form.keys(): 
116                self.args[key]=form.getvalue(key) 
117                       
118        if self.callMethod=="WebService":
119            self.args={}
120            for key in args.keys():
121                self.args[key]=args[key]
122             
123        # Make a list of all the possible time keys
124        allTimeKeys=[]
125        for ts in ["start", "end"]:
126            for i in range(1,3):
127                for key in TIME_KEYS:
128                    allTimeKeys.append("%s_%s_%s" % (ts, key, i))
129           
130        for key in self.args.keys():
131            if key in ["num_datasets"]+allTimeKeys: 
132                # Make sure it is an integer
133                try: 
134                    self.args[key]=int(self.args[key]) 
135                except: 
136                    pass
137            elif key in list(HORIZ_KEYS):
138                try:
139                    self.args[key]=float(self.args[key])
140                except:
141                    pass
142           
143    def _checkSecurity(self):
144        """
145        _checkSecurity method - checks security by getting username and
146        allowed groups from whatever implementation you have put in place.
147        """
148        sec=Security() 
149        if self.args.has_key("logout"): 
150            # logout
151            self.args={} 
152            sec.logout() 
153            allowed_groups=None 
154     
155        response=sec.getLoginStatus() 
156        if response: 
157            self.username=response[0] 
158            allowed_group_codes=response[1] 
159            self.allowed_groups=[]
160            for i in allowed_group_codes:
161                try:
162                    import grp
163                    group=grp.getgrgid(int(i))[0]
164                except:
165                    group=i
166                self.allowed_groups.append(group)
167
168                # Sort multiple group issue for ECMWFOP
169                if group[:7]=="ecmwfop": 
170                    for g in ("ecmwfop", "ecmwfop1", "ecmwfop2"): 
171                        if g not in self.allowed_groups:  self.allowed_groups.append(g)
172
173                # Sort the ERA40 and ERA15 merger groups
174                if group in ("era", "era4t", "ecmwfera"):
175                    for g in ("era", "era4t", "ecmwfera"):
176                        if g not in self.allowed_groups:  self.allowed_groups.append(g)
177                               
178                if type(self.allowed_groups)==str: 
179                    self.allowed_string=self.allowed_groups.replace(" ","") 
180                    allowed_as_list=allowed_string[1:-1].split(",") 
181                    cleaned_list=map(lambda x: x.replace("'", ""), allowed_as_list) 
182                    self.allowed_groups=cleaned_list
183                       
184                # local rule follows   
185                for key in ("_gohome",):   
186                    if self.args.has_key(key): del self.args[key]
187
188        else: 
189            raise "No response from Security class!"
190
191
192
193    def _constructRequest(self):
194        """
195        _constructRequest method - ensures that all appropriate arguments are being written
196        to the request object in the correct manner.
197        """   
198         
199        # self.args now holds the input arguments (from CGI or command line)
200     
201        if not self.args.has_key("req_id"): 
202            self.requester=Requestdb() 
203            self.request["req_id"]=self.requester.getRequestID() 
204            self.request["target_page"]=STAGES[0] 
205            self.request["num_datasets"]=1 
206        else: 
207            self.requester=Requestdb(self.args["req_id"]) 
208            self.request=self.requester.readRequest() 
209     
210        # Update request with allowed groups at each stage (this is checked every reload)
211        self.request["allowed_groups"]=self.allowed_groups
212     
213        if not self.request.has_key("user"): 
214            self.request["user"]=self.username
215     
216        # Add the rest of the arguments to the request object
217        for key in self.args.keys(): 
218            if key in ("dataset_group_1", "dataset_group_2"):
219                self.request["datasetGroup_%s" % key[-1]]=self.args[key]
220            else:
221                self.request[key]=self.args[key] 
222
223        # If more than one dataset then make sure num_datasets is set to 2
224        if self.request.has_key("datasetURI_2") or self.request.has_key("datasetGroup_2"):
225            self.request["num_datasets"]=2 
226         
227        # Now check through if we have datasetURIs to see if they relate to our known
228        # datasetGroups and datasets   
229        for dset_num in range(1, self.request["num_datasets"]+1): 
230            if self.request.has_key("datasetURI_%s" % dset_num) and not self.request.has_key("datasetGroup_%s" % dset_num): 
231                dsetdb=Datasetdb() 
232                URI_list=dsetdb.getDatasetURIList() 
233                for i in URI_list: 
234                    if self.request["datasetURI_%s" % dset_num]==i[0]: # if it is a known dataset
235                        self.request["datasetGroup_%s" % dset_num]=i[2] 
236                        self.request["dataset_%s" %dset_num]=i[1] 
237                if not self.request.has_key("datasetGroup_%s" % dset_num): 
238                    self.request["datasetGroup_%s" % dset_num]="User dataset group" 
239                    self.request["dataset_%s" % dset_num]="User dataset" 
240                    # Note if it is not known then no security check needed.         
241                self.request["target_page"]="VariablesPage" 
242     
243        if self.request.has_key("num_datasets") and self.request["num_datasets"]==1: 
244            for key in ("datasetGroup_2", "dataset_2", "variable_2"): 
245                if self.request.has_key(key):  del self.request[key] 
246         
247        if self.request.has_key("target_page"): 
248            self.target_page=self.request["target_page"] 
249        else: 
250            self.target_page="DatasetGroupPage" 
251     
252        # Extra security check to avoid loophole
253        if RESTRICTED_DATA==1:
254            for dsetgrp in ("datasetGroup_1", "datasetGroup_2"): 
255                if self.request.has_key(dsetgrp): 
256                    if self.request[dsetgrp]=="User dataset group" or                                                  DATASET_GROUPS[self.request[dsetgrp]]==None: 
257                        pass 
258                    elif DATASET_GROUPS[self.request[dsetgrp]] not in self.request["allowed_groups"] and                              DATASET_GROUPS[self.request[dsetgrp]]!=None: 
259                        raise "You do not have permission to access the requested data!"
260
261
262    def _createUserInterface(self):
263        """
264        _createUserInterface method - calls the UserInterface class.
265        """
266        if self.callMethod=="CGI":
267            ui=CGIInterface()   
268        elif self.callMethod=="WebService":
269            ui=WSInterface()
270        elif self.callMethod=="CommandLine":
271            raise "Command line calling not yet implemented."
272       
273        if self.target_page: 
274            stage=STAGES.index(self.target_page)
275        else: 
276            ErrorHandler("Target page not recognised; %s" % target_page, noheader=1,                           request_id=self.request["req_id"], user=self.request["user"]) 
277     
278        if self.target_page!=STAGES[4]: 
279            #ui.writePage(self.request, stage)
280            pass
281        else:                         #  if target_page==STAGES[4]:
282            ui.writeHeader() 
283            ui.writeRequestInfo(self.request, stage) 
284            self._createOutput() 
285            print "2222222222222"
286            ui.writeFooter() 
287     
288        if self.request.has_key("allowed_groups"):  del self.request["allowed_groups"]     
289           
290    def _createOutput(self):
291        """
292        _createOutput method - calls the classes needed to generate output and also writes
293        information to the screen to keep user updated.
294        All the printed stuff here needs to be moved into the user interface classes at some
295        point.
296        """   
297        print '<FONT FACE="Arial, Helvetica, sans-serif" SIZE="-1"><P><B>Executing request...</B><P>'
298        # Flush standard out to get everything displayed on screen to user
299        sys.stdout.flush()
300
301        # Redirect standard out to avoid splurging to screen
302        stdout=sys.stdout
303        sys.stdout=RedirectStdout()
304           
305        # Roundabout method of getting dataset xml file out of list of lists.
306        source_xmls=[] 
307        for dset_num in range(1, self.request["num_datasets"]+1): 
308            if self.request["datasetGroup_%s" % dset_num]=="User dataset group": 
309                source_xmls.append(self.request["datasetURI_%s" % dset_num]) 
310            else: 
311                for dset in DATASETS[self.request["datasetGroup_%s" % dset_num]]: 
312                    if dset[0]==self.request["dataset_%s" % dset_num]: 
313                        source_xmls.append(dset[1]) 
314     
315        if self.request["output_type"]=="NetCDF": 
316            requestCoster=RequestCost(source_xmls, self.request) 
317            estimatedDuration=requestCoster.getDurationEstimate() 
318
319            # Tell the user they will be e-mailed when the job is complete if large job
320            if estimatedDuration>60: 
321                try:
322                    emailAddr=getUserEmail(self.request["user"]) 
323                    shouldIMailUser="yes"
324                except:
325                    emailAddr=None
326                    shouldIMailUser="no"
327
328                # Get stdout back
329                sys.stdout=stdout
330                print "<P>Your extraction job has been submitted and is estimated to take about %s seconds." % int(estimatedDuration) 
331                if emailAddr:
332                    print "<P>You will be emailed at %s when the job has finished and the output is ready."
333                print "<P>Thank you for using the %s.""" % (int(estimatedDuration), emailAddr, PACKAGE_NAME) 
334                print "<P>If you remain on this page it should eventually produce your output but the server may time out if your request is large.<P>" 
335            else:
336                shouldIMailUser="no"
337
338            if sys.stdout==stdout: 
339                sys.stdout.flush()
340                stdout=sys.stdout
341                sys.stdout=RedirectStdout()
342
343            # Now really get data...
344            data_producer=DataFile(source_xmls, self.request)       
345            data_producer.getParam()
346            (outfile, outfile_actual)=data_producer.process() 
347               
348            # E-mail the user now the data has been produced
349            if shouldIMailUser=="yes": 
350                mail=MailUser(emailAddr, "Message from %s" % PACKAGE_NAME) 
351                mail.sendMail("""Your extraction job has completed, you may collect the data from:\n 
352    %s\n\nThank you for using this service.\n\nIf you have any comments or feedback please send them to %s.""" % (outfile, ADMIN_MAIL_ADDRESS))
353           
354            # Get stdout back
355            sys.stdout=stdout
356               
357            # Update user on screen
358            print "<P><B>Request processed...</B><P>" 
359            print '<A HREF="%s">Click here to download file</A>.<P>' % outfile
360            print '<A HREF="%s?fileURI=%s">Click here to visualise your NetCDF file</A>.<P>' % (VISUALISOR_NAME, outfile_actual) 
361            self.request["outfile"]=outfile
362   
363def callControlExtractClass(**args):
364    """
365    Function wrapper to ControlExtract class.
366    """
367    args["callMethod"]="WebService"
368    return ControlExtract(args)
369
370def setUpServer():
371    print "Importing SOAP library (SOAPpy)."
372    from SOAPpy import SOAPServer
373    print "Setting up server"
374    server = SOAPServer(("localhost", 8443)) # set up any port you want
375    server.registerFunction(callControlExtractClass)
376    print "Begin serving..."
377    server.serve_forever()
378
379if __name__=="__main__":
380    setUpServer()
Note: See TracBrowser for help on using the repository browser.