source: TI03-DataExtractor/trunk/pydxs/DXController.py @ 794

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI03-DataExtractor/trunk/pydxs/DXController.py@794
Revision 794, 12.5 KB checked in by astephen, 13 years ago (diff)

Unstable but latest version with multi-variable support and split hooks
for CDML and CSML.

Line 
1#   Copyright (C) 2004 CCLRC & NERC( Natural Environment Research Council ).
2#   This software may be distributed under the terms of the
3#   Q Public License, version 1.0 or later. http://ndg.nerc.ac.uk/public_docs/QPublic_license.txt
4
5"""
6DXController.py
7================
8
9The core class for controlling the package.
10
11"""
12
13# Import required modules
14import sys
15import os 
16import time 
17
18# Bring package into local scope
19from serverConfig import BASEDIR
20sys.path.insert(0, os.path.split(BASEDIR)[0])
21from pydxs import *
22
23 
24# Add any other locally required directories in which you have modules.
25for path in LOCAL_PYTHONPATH:
26   sys.path.insert(0, path) 
27
28
29class DXController:
30    """
31    Controls the overall process flow of the a session.
32    This class is called through each stage of the extraction process
33    and responds according to the environment and arguments provided.
34    Note that this class is called by the WSInterface
35    class which then reads various instance variables. As a result, very
36    little is returned from the methods below. Instead the data is stored
37    in the instance object.
38    """
39   
40    def __init__(self, args):
41        """
42        Takes in a group of arguments and then calls the appropriate
43        methods to process the user request.
44        """
45        self.username=None 
46        self.password=None
47        self.userRoles=[]
48        self.sessionID=None
49        self.sessionObj=None
50        self.bag=self.sessionObj # alias               
51        self.secureToken=None
52        self.error=None
53   
54        # Parse the arguments
55        try:
56            self._parseArgs(args)
57        except Exception, error:
58            raise DXArgumentError, error
59           
60        # If switched on check security       
61        if self.error==None:
62            if RESTRICTED_DATA==1:
63                try:
64                    self._checkSecurity()
65                except Exception, error:
66                    raise DXSecurityError, error   
67            else:
68                self.secureToken=None
69                   
70        # Construct the session object
71        if self.error==None:   
72            try:   
73                self._constructSessionObject()   
74            except Exception, error:
75                print error
76                raise DXSessionObjectError, error   
77
78        # Generate the options object
79        if self.error==None:   
80            try:   
81                self._generateOptions() 
82            except Exception, error:
83                raise DXOptionHandlingError, error
84
85
86        # Write request to a file if it has some content
87        if self.error==None:
88            if overlap(["datasetGroup_1", "datasetURI_1", "sessionID", "numberOfDatasets"], 
89                      self.bag.keys()):         
90                try:     
91                    self.bag["accessTime"]=time.time() 
92                    self.sessionObjectManager.writeSessionObject(self.bag) 
93                except Exception, error:
94                    raise DXSessionObjectError, error
95                   
96                # Validate the selection against known information
97                try:       
98                    self._validate()               
99                except Exception, error:
100                    raise DXValidationError, error
101           
102        # Estimate the cost of the extraction
103        creditChecked=None
104        if self.error==None:   
105            if self.bag.has_key("action") and self.bag["action"]=="checkCredit":           
106                # Check user has sufficient credit
107                try:       
108                    self._checkCredit()   
109                    creditChecked="yes"             
110                except Exception, error:
111                    raise DXCreditError, error
112                   
113
114        # Estimate the cost of the extraction
115        if self.error==None:   
116            if (self.bag.has_key("action") and self.bag["action"]=="requestCosts") and self.options=={}:
117           
118                try:
119                    (self.estimatedDuration, self.estimatedVolume)=self._createOutput(costOnly=1)
120                    sizeLimitInBytes=REQUEST_SIZE_LIMIT*2L**20
121                    # If size is over limit then send a tidy error to the user
122                    if self.estimatedVolume>sizeLimitInBytes:
123                        sizeInMB=self.estimatedVolume/(2L**20.)
124                        err="""Your request of %.1f MB is over the current %s MB size limit.   
125The Data Extractor cannot yet deal with such large requests.
126Consider mailing <A HREF="mailto:%s">%s</A> for advice or submit multiple smaller requests.""" % (sizeInMB, 
127                               REQUEST_SIZE_LIMIT, ADMIN_MAIL_ADDRESS, ADMIN_MAIL_ADDRESS)     
128                        raise DXSizeLimitError, err
129                except Exception, error:
130                    raise Exception, error
131                     
132        # Process the selections to generate some data
133        if self.error==None:   
134            if (self.bag.has_key("getOutput") and creditChecked==None) and self.options=={}:
135                # Check user has sufficient credit
136                try:       
137                    self._checkCredit()                     
138                except Exception, error:
139                    raise Exception, error
140                           
141                try:       
142                    self._createOutput() 
143                    self.logger=LogManager(self.bag)       
144                    self.bag["status"]="complete"       
145                    self.logger.logCompletedRequest(self.bag["outputFilePaths"])               
146                except Exception, error:
147                    raise DXProcessingError, error
148       
149        if self.error==None:
150            print "Saving session details:\n", self.bag, "\n\n" 
151            if self.bag.has_key("action") and self.bag["action"]=="clearRequest":
152                del self.bag["action"]
153            self.sessionObjectManager.writeSessionObject(self.bag)
154        else:
155            self.logger=LogManager(self.bag) 
156            self.logger.logError(self.error)
157         
158           
159    def _parseArgs(self, args):
160        """
161        Parses the argument dictionary that are sent.
162        """                     
163        self.args={}
164        for key, value in args.items():
165            if type(key)==type(u""):
166                newkey=str(key)
167            else:
168                newkey=key
169                   
170            if type(value)==type(u""):
171                newvalue=str(value)
172            else:
173                newvalue=value
174               
175            self.args[newkey]=newvalue
176           
177   
178        for item in ("username", "password", "secureToken"):
179            if self.args.has_key(item):             
180                value=self.args[item]               
181                setattr(self, item, value)
182                del self.args[item]
183               
184        print "ARGS and SecureToken:", self.args, self.secureToken
185                             
186
187    def _checkSecurity(self):
188        """
189        Checks security by getting username and
190        allowed groups from whatever implementation you have put in place.
191        """
192        secChecker=SecurityManager(self.username, self.password, self.secureToken) 
193        # Do something about logout here as well
194     
195        secCheck=secChecker.validateUser() 
196        self.username=secChecker.username
197
198        if type(secCheck)==type(""):
199            raise DXSecurityError, secCheck
200        elif type(secCheck)==type([]):
201            (self.secureToken, self.userRoles)=secCheck 
202        else: 
203            raise DXSecurityError, str(secCheck)+str(type(secCheck))#"No response from Security class!"
204           
205
206    def _constructSessionObject(self):
207        """
208        Ensures that all appropriate arguments are being written
209        to the session object in the correct manner. Note that the
210        session object "sessionObj" is just a dictionary but is read
211        in from a shelve object (if it exists already).
212        """     
213        # self.args now holds the input arguments
214        if not self.args.has_key("sessionID"): 
215            self.sessionObjectManager=SessionObject()
216            self.bag=self.sessionObjectManager.dict
217            #self.sessionObj["targetPage"]=STAGES=[0]
218        else: 
219            self.sessionObjectManager=SessionObject(self.args["sessionID"]) 
220            self.bag=self.sessionObjectManager.readSessionObject() 
221            # Clear the session object if requested
222            if self.args.has_key("clearRequest") or self.args.has_key("newRequest"):
223                self.sessionObjectManager.clearSessionObject(self.bag) 
224               
225        if type(self.sessionID)==type(u""):
226            self.sessionID=str(self.sessionID) 
227               
228        # Update session object with allowed roles at each stage (this is checked every reload)
229        self.bag["userRoles"]=self.userRoles
230
231        # Say it is under construction at present
232        self.bag["status"]="constructing"
233       
234        # Add the rest of the arguments to the session object
235        for key in self.args.keys(): 
236            self.bag[key]=self.args[key] 
237
238        # Might need username later
239        if self.bag.has_key("username"):
240            self.username=self.bag["username"]
241        else:
242            self.bag["username"]=self.username
243                               
244        #if hasattr(self, "numberOfDatasets"):
245        #    self.request["numberOfDatasets"]=self.numberOfDatasets
246        if not self.bag.has_key("numberOfDatasets"):
247            self.bag["numberOfDatasets"]=1
248        else:
249            if type(self.bag["numberOfDatasets"]) in (type(""), type(u"")):
250                self.bag["numberOfDatasets"]=int(self.bag["numberOfDatasets"])
251             
252        # Now check through if we have datasetURIs to see if they relate to our known
253        # datasetGroups and datasets   
254        self._populateRequestFromURI()
255
256
257    def _populateRequestFromURI(self):
258        """
259        Takes the request and adds datasetGroup and/or dataset information if
260        a datasetURI was provided rather than a datasetGroup/dataset pairing.
261        It then does a security check on the resulting data.
262        """
263        for n in range(1, self.bag["numberOfDatasets"]+1): 
264            if self.bag.has_key("datasetURI_%s" % n) and not self.bag.has_key("datasetGroup_%s" % n): 
265                DXDML=DXDMLHandler() 
266                URIList=DXDML.getDatasetURIList() 
267
268                for i in URIList: 
269                    i[0]=i[0].replace("file:", "")
270                    if self.bag["datasetURI_%s" % n]==i[0]: # if it is a known dataset
271                        self.bag["datasetGroup_%s" % n]=i[2] 
272                        self.bag["dataset_%s" %n]=i[1] 
273                       
274                if not self.bag.has_key("datasetGroup_%s" % n): 
275                    #self.bag["datasetGroup_%s" % n]="User dataset group"
276                    #self.bag["dataset_%s" % n]="User dataset"
277                    # Note if it is not known then not allowed to see it!
278                    raise "You do not have permission to access the requested data!"
279                   
280                self.bag["targetPage"]="VariablesPage" 
281       
282        # Tidy up request based on number of datasets requested
283        if self.bag.has_key("numberOfDatasets") and self.bag["numberOfDatasets"]==1: 
284            for n in range(2, MAX_NUM_DATASETS+1):
285                for key in ("datasetGroup_%s", "dataset_%s", "variable_%s", "datasetURI_%s"):
286                    item=key % n
287                    if self.bag.has_key(item):  del self.bag[item] 
288         
289        if self.bag.has_key("targetPage"): 
290            self.targetPage=self.bag["targetPage"] 
291        else: 
292            self.targetPage="DatasetGroupPage" 
293       
294
295    def _generateOptions(self):
296        """
297        Method that examines the current status of the request to create an appropriate
298        list of options for the user. Made up of many if clauses to control a logical
299        response to the current request.
300        """
301        optHandler=OptionHandler(self.bag)
302        self.options=optHandler.options
303       
304       
305    def _validate(self):
306        """
307        Validates the selections made by the user. Returns 1 if successful
308        and a string if failure.
309        """
310        try:
311            ValidateSelection(self.bag)
312        except Exception, error:
313            raise DXValidationError, error
314           
315     
316    def _checkCredit(self):
317        """
318        Checks if the user has the available credit to the selection task. Returns 1 if successful
319        and a string if failure.
320        """
321        creditChecker=CreditManager(self.username, self.bag)
322        creditResponse=creditChecker.creditCheck()
323        if creditResponse!=1:
324            raise creditResponse
325           
326           
327    def _createOutput(self, costOnly=None):
328        """
329        Creates either data files or a dataSubsetSpecifier xml file.
330        """   
331        # Roundabout method of getting dataset xml file out of list of lists.
332        sourceXMLS=[] 
333        for n in range(1, self.bag["numberOfDatasets"]+1): 
334            print self.bag
335            if self.bag.has_key("datasetGroup_%s" % n):
336                if self.bag["datasetGroup_%s" % n]=="User dataset group": 
337                    sourceXMLS.append(self.bag["datasetURI_%s" % n]) 
338                else: 
339                    DXDML=DXDMLHandler()
340                    dsg=self.bag["datasetGroup_%s" % n]
341                    ds=self.bag["dataset_%s" % n]
342                    dsURI=DXDML.getDatasetURI(dsg, ds)
343                    sourceXMLS.append(dsURI)
344                               
345        if costOnly==1:
346            requestCoster=RequestCost(sourceXMLS, self.bag) 
347            return requestCoster.getDurationAndVolume(self.bag["outputFormat"])   
348                 
349        formatClassDict={"NetCDF":NetCDFOutputManager, "NASA Ames":NASAAmesOutputManager}     
350
351        # Now really get data...
352        outputManager=apply(formatClassDict[self.bag["outputFormat"]], 
353            (sourceXMLS, self.bag),)           
354        outputManager.getParam()
355        outputFilePaths=outputManager.generateOutputFilePaths()         
356        outputFilePathsURLs=[]
357        for outputFile in outputFilePaths:
358            newpath=outputFile.replace(OUTPUT_DIR, OUTPUT_DIR_URL)
359            outputFilePathsURLs.append(newpath)     
360        self.bag["outputFilePaths"]=outputFilePathsURLs
361       
362        print """Should really fork this process at this point so that we can return
363              something if likely to be large job."""
364        outputManager.createOutputs() 
365
366
367
368   
Note: See TracBrowser for help on using the repository browser.