source: TI03-DataExtractor/branches/old_stuff/latest_dx/dx/pydxs/DXController.py @ 793

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

Put all the old code in the old_stuff branch.

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.secureToken=None
51        self.error=None
52   
53        # Parse the arguments
54        try:
55            self._parseArgs(args)
56        except Exception, error:
57            raise DXArgumentError, error
58           
59        # If switched on check security       
60        if self.error==None:
61            if RESTRICTED_DATA==1:
62                try:
63                    self._checkSecurity()
64                except Exception, error:
65                    raise DXSecurityError, error   
66            else:
67                self.secureToken=None
68                   
69        # Construct the session object
70        if self.error==None:   
71            try:   
72                self._constructSessionObject()   
73            except Exception, error:
74                print error
75                raise DXSessionObjectError, error   
76
77        # Generate the options object
78        if self.error==None:   
79            try:   
80                self._generateOptions() 
81            except Exception, error:
82                raise DXOptionHandlingError, error
83
84
85        # Write request to a file if it has some content
86        if self.error==None:
87            if overlap(["datasetGroup_1", "datasetURI_1", "sessionID", "numberOfDatasets"], 
88                      self.sessionObj.keys()): 
89                try:     
90                    self.sessionObj["accessTime"]=time.time()   
91                    self.sessionObjectManager.writeSessionObject(self.sessionObj) 
92                except Exception, error:
93                    raise DXSessionObjectError, error
94                   
95                # Validate the selection against known information
96                try:       
97                    self._validate()               
98                except Exception, error:
99                    raise DXValidationError, error
100           
101        # Estimate the cost of the extraction
102        creditChecked=None
103        if self.error==None:   
104            if self.sessionObj.has_key("action") and self.sessionObj["action"]=="checkCredit":     
105                # Check user has sufficient credit
106                try:       
107                    self._checkCredit()   
108                    creditChecked="yes"             
109                except Exception, error:
110                    raise DXCreditError, error
111                   
112
113        # Estimate the cost of the extraction
114        if self.error==None:   
115            if (self.sessionObj.has_key("action") and self.sessionObj["action"]=="requestCosts") and self.options=={}:
116           
117                try:
118                    (self.estimatedDuration, self.estimatedVolume)=self._createOutput(costOnly=1)
119                    sizeLimitInBytes=REQUEST_SIZE_LIMIT*2L**20
120                    # If size is over limit then send a tidy error to the user
121                    if self.estimatedVolume>sizeLimitInBytes:
122                        sizeInMB=self.estimatedVolume/(2L**20.)
123                        err="""Your request of %.1f MB is over the current %s MB size limit.   
124The Data Extractor cannot yet deal with such large requests.
125Consider mailing <A HREF="mailto:%s">%s</A> for advice or submit multiple smaller requests.""" % (sizeInMB, 
126                               REQUEST_SIZE_LIMIT, ADMIN_MAIL_ADDRESS, ADMIN_MAIL_ADDRESS)     
127                        raise DXSizeLimitError, err
128                except Exception, error:
129                    raise Exception, error
130                     
131        # Process the selections to generate some data
132        if self.error==None:   
133            if (self.sessionObj.has_key("getOutput") and creditChecked==None) and self.options=={}:
134                # Check user has sufficient credit
135                try:       
136                    self._checkCredit()                     
137                except Exception, error:
138                    raise Exception, error
139                           
140                try:       
141                    self._createOutput() 
142                    self.logger=LogManager(self.sessionObj)         
143                    self.sessionObj["status"]="complete"       
144                    self.logger.logCompletedRequest(self.sessionObj["outputFilePaths"])         
145                except Exception, error:
146                    raise DXProcessingError, error
147           
148        if self.error==None:
149            print "Saving session details:\n", self.sessionObj, "\n\n" 
150            if self.sessionObj.has_key("action") and self.sessionObj["action"]=="clearRequest":
151                del self.sessionObj["action"]
152            self.sessionObjectManager.writeSessionObject(self.sessionObj)
153        else:
154            self.logger=LogManager(self.sessionObj) 
155            self.logger.logError(self.error)
156         
157           
158    def _parseArgs(self, args):
159        """
160        Parses the argument dictionary that are sent.
161        """                     
162        self.args={}
163        for key, value in args.items():
164            if type(key)==type(u""):
165                newkey=str(key)
166            else:
167                newkey=key
168                   
169            if type(value)==type(u""):
170                newvalue=str(value)
171            else:
172                newvalue=value
173               
174            self.args[newkey]=newvalue
175           
176   
177        for item in ("username", "password", "secureToken"):
178            if self.args.has_key(item):             
179                value=self.args[item]               
180                setattr(self, item, value)
181                del self.args[item]
182               
183        print "ARGS and SecureToken:", self.args, self.secureToken
184                             
185
186    def _checkSecurity(self):
187        """
188        Checks security by getting username and
189        allowed groups from whatever implementation you have put in place.
190        """
191        secChecker=SecurityManager(self.username, self.password, self.secureToken) 
192        # Do something about logout here as well
193     
194        secCheck=secChecker.validateUser() 
195        self.username=secChecker.username
196
197        if type(secCheck)==type(""):
198            raise DXSecurityError, secCheck
199        elif type(secCheck)==type([]):
200            (self.secureToken, self.userRoles)=secCheck 
201        else: 
202            raise DXSecurityError, str(secCheck)+str(type(secCheck))#"No response from Security class!"
203           
204
205    def _constructSessionObject(self):
206        """
207        Ensures that all appropriate arguments are being written
208        to the session object in the correct manner. Note that the
209        session object "sessionObj" is just a dictionary but is read
210        in from a shelve object (if it exists already).
211        """     
212        # self.args now holds the input arguments
213        if not self.args.has_key("sessionID"): 
214            self.sessionObjectManager=SessionObject()
215            self.sessionObj=self.sessionObjectManager.dict
216            #self.sessionObj["targetPage"]=STAGES=[0]
217        else: 
218            self.sessionObjectManager=SessionObject(self.args["sessionID"]) 
219            self.sessionObj=self.sessionObjectManager.readSessionObject() 
220            # Clear the session object if requested
221            if self.args.has_key("clearRequest") or self.args.has_key("newRequest"):
222                self.sessionObjectManager.clearSessionObject(self.sessionObj) 
223               
224        if type(self.sessionID)==type(u""):
225            self.sessionID=str(self.sessionID) 
226               
227        # Update session object with allowed roles at each stage (this is checked every reload)
228        self.sessionObj["userRoles"]=self.userRoles
229
230        # Say it is under construction at present
231        self.sessionObj["status"]="constructing"
232       
233        # Add the rest of the arguments to the session object
234        for key in self.args.keys(): 
235            self.sessionObj[key]=self.args[key] 
236
237        # Might need username later
238        if self.sessionObj.has_key("username"):
239            self.username=self.sessionObj["username"]
240        else:
241            self.sessionObj["username"]=self.username
242                               
243        #if hasattr(self, "numberOfDatasets"):
244        #    self.request["numberOfDatasets"]=self.numberOfDatasets
245        if not self.sessionObj.has_key("numberOfDatasets"):
246            self.sessionObj["numberOfDatasets"]=1
247        else:
248            if type(self.sessionObj["numberOfDatasets"]) in (type(""), type(u"")):
249                self.sessionObj["numberOfDatasets"]=int(self.sessionObj["numberOfDatasets"])
250             
251        # Now check through if we have datasetURIs to see if they relate to our known
252        # datasetGroups and datasets   
253        self._populateRequestFromURI()
254
255
256    def _populateRequestFromURI(self):
257        """
258        Takes the request and adds datasetGroup and/or dataset information if
259        a datasetURI was provided rather than a datasetGroup/dataset pairing.
260        It then does a security check on the resulting data.
261        """
262        for n in range(1, self.sessionObj["numberOfDatasets"]+1): 
263            if self.sessionObj.has_key("datasetURI_%s" % n) and not self.sessionObj.has_key("datasetGroup_%s" % n): 
264                dsetdb=Datasetdb() 
265                URIList=dsetdb.getDatasetURIList() 
266
267                for i in URIList: 
268                    i[0]=i[0].replace("file:", "")
269                    if self.sessionObj["datasetURI_%s" % n]==i[0]: # if it is a known dataset
270                        self.sessionObj["datasetGroup_%s" % n]=i[2] 
271                        self.sessionObj["dataset_%s" %n]=i[1] 
272                       
273                if not self.sessionObj.has_key("datasetGroup_%s" % n): 
274                    #self.sessionObj["datasetGroup_%s" % n]="User dataset group"
275                    #self.sessionObj["dataset_%s" % n]="User dataset"
276                    # Note if it is not known then not allowed to see it!
277                    raise "You do not have permission to access the requested data!"
278                   
279                self.sessionObj["targetPage"]="VariablesPage" 
280       
281        # Tidy up request based on number of datasets requested
282        if self.sessionObj.has_key("numberOfDatasets") and self.sessionObj["numberOfDatasets"]==1: 
283            for n in range(2, MAX_NUM_DATASETS+1):
284                for key in ("datasetGroup_%s", "dataset_%s", "variable_%s", "datasetURI_%s"):
285                    item=key % n
286                    if self.sessionObj.has_key(item):  del self.sessionObj[item] 
287         
288        if self.sessionObj.has_key("targetPage"): 
289            self.targetPage=self.sessionObj["targetPage"] 
290        else: 
291            self.targetPage="DatasetGroupPage" 
292   
293        # Extra security check to avoid loophole
294        ##############################
295        ########## CAN I DELETE THIS???################
296        ######################################
297        ##if RESTRICTED_DATA==1:
298        ##    for n in range(1, MAX_NUM_DATASETS+1):
299        ##        dsetgrp="datasetGroup_%s" % n
300        ##        if self.sessionObj.has_key(dsetgrp):
301        ##            if DATASET_GROUPS[self.sessionObj[dsetgrp]]==None:
302        ##                pass
303        ##            elif DATASET_GROUPS[self.sessionObj[dsetgrp]] not in self.sessionObj["userRoles"]:
304        ##                raise "You do not have permission to access the requested data!"
305       
306
307    def _generateOptions(self):
308        """
309        Method that examines the current status of the request to create an appropriate
310        list of options for the user. Made up of many if clauses to control a logical
311        response to the current request.
312        """
313        optHandler=OptionHandler(self.sessionObj["userRoles"], self.username)
314        self.options={}
315       
316        # Define a parameter to pick up whether user wants a specific set of options
317        optsReq=None
318        if self.sessionObj.has_key("optionCategoryRequested"):
319            optsReq=self.sessionObj["optionCategoryRequested"]
320
321        # Get the required option category (going to highest level for all datasets)
322        if optsReq==None or optsReq in ("datasetGroup", "dataset", "variable"): 
323          for n in range(1, self.sessionObj["numberOfDatasets"]+1):
324            if isUndefined(self.sessionObj, "datasetGroup_%s" % n) or optsReq=="datasetGroup":
325                if TOP_LEVEL=="dataset" and (isUndefined(self.sessionObj, "dataset_%s" % n)):
326                    optsReq="dataset"  # to ensure all options are at this level
327                elif TOP_LEVEL=="datasetGroup":
328                    optsReq="datasetGroup"  # to ensure all options are at this level
329               
330            elif isUndefined(self.sessionObj, "dataset_%s" % n):
331                optsReq="dataset"  # to ensure all options are at this level                   
332               
333            elif isUndefined(self.sessionObj, "variable_%s" % n):
334                optsReq="variable"  # to ensure all options are at this level
335
336          # Now populate the options object if required for datasetGroup, dataset or variable
337          for n in range(1, self.sessionObj["numberOfDatasets"]+1):
338       
339            choices=None
340            if optsReq=="datasetGroup":
341                    choices=("datasetGroup_%s" % n, optHandler.getDatasetGroupList())     
342            elif optsReq=="dataset":                 
343                    choices=("dataset_%s" % n, 
344                        optHandler.getDatasetList(self.sessionObj["datasetGroup_%s" % n]))
345            elif optsReq=="variable":
346                choices=("variable_%s" % n, optHandler.getVariableList(self.sessionObj["datasetGroup_%s" % n],
347                         self.sessionObj["dataset_%s" % n]))
348 
349            if choices:  self.options[str(n)]=choices
350       
351        if self.options!={}: 
352            # If datasetGroup, dataset or variable is the option then return
353            return     
354               
355        # Now move on to the horizontal domain (fixed for all)           
356        if not self._isHorizontalDomainDefined() and optsReq in (None, "horizontalDomain"):
357            choices=("horizontalDomain", optHandler.getHorizontalDomain(self.sessionObj["datasetGroup_%s" % 1],
358                     self.sessionObj["dataset_%s" % 1], self.sessionObj["variable_%s" % 1]))   
359            self.options["1"]=choices
360            return
361                     
362        # Now deal with the other axes (varying with each variable).
363        for n in range(1, self.sessionObj["numberOfDatasets"]+1):
364           
365            choices=None 
366            if isUndefined(self.sessionObj, "verticalDomain_%s" % n) and optsReq in (None, "verticalDomain"):
367                choices=("verticalDomain_%s" % n, optHandler.getVerticalSpatialDomain(self.sessionObj["datasetGroup_%s" % n],
368                         self.sessionObj["dataset_%s" % n], self.sessionObj["variable_%s" % n]))
369                optsReq="verticalDomain"  # to ensure all options are at this level
370                         
371            elif not self._isTemporalDomainDefined(n) and optsReq in (None, "temporalDomain"):
372                choices=("temporalDomain_%s" % n, optHandler.getTemporalDomain(self.sessionObj["datasetGroup_%s" % n],
373                         self.sessionObj["dataset_%s" % n], self.sessionObj["variable_%s" % n]))
374                optsReq="temporalDomain"  # to ensure all options are at this level
375                               
376            if choices:  self.options[str(n)]=choices
377           
378        if self.options!={}: 
379            # If vertical or temporal domain is the option then return
380            return     
381       
382        if isUndefined(self.sessionObj, "outputFormat") and optsReq in (None, "outputFormat"):
383            choices=("outputFormat", OUTPUT_FORMATS)
384            self.options["1"]=choices
385           
386        return
387       
388       
389    def _validate(self):
390        """
391        Validates the selections made by the user. Returns 1 if successful
392        and a string if failure.
393        """
394        try:
395            ValidateSelection(self.sessionObj)
396        except Exception, error:
397            raise DXValidationError, error
398           
399     
400    def _checkCredit(self):
401        """
402        Checks if the user has the available credit to the selection task. Returns 1 if successful
403        and a string if failure.
404        """
405        creditChecker=CreditManager(self.username, self.sessionObj)
406        creditResponse=creditChecker.creditCheck()
407        if creditResponse!=1:
408            raise creditResponse
409           
410           
411    def _createOutput(self, costOnly=None):
412        """
413        Creates either data files or a dataSubsetSpecifier xml file.
414        """   
415        # Roundabout method of getting dataset xml file out of list of lists.
416        sourceXMLS=[] 
417        for n in range(1, self.sessionObj["numberOfDatasets"]+1): 
418            print self.sessionObj
419            if self.sessionObj.has_key("datasetGroup_%s" % n):
420                if self.sessionObj["datasetGroup_%s" % n]=="User dataset group": 
421                    sourceXMLS.append(self.sessionObj["datasetURI_%s" % n]) 
422                else: 
423                    dsetdb=Datasetdb()
424                    dsg=self.sessionObj["datasetGroup_%s" % n]
425                    ds=self.sessionObj["dataset_%s" % n]
426                    dsURI=dsetdb.getDatasetURI(dsg, ds)
427                    sourceXMLS.append(dsURI)
428                               
429        if costOnly==1:
430            requestCoster=RequestCost(sourceXMLS, self.sessionObj) 
431            return requestCoster.getDurationAndVolume(self.sessionObj["outputFormat"])   
432                 
433        formatClassDict={"NetCDF":NetCDFOutputManager, "NASA Ames":NASAAmesOutputManager}     
434
435        # Now really get data...
436        outputManager=apply(formatClassDict[self.sessionObj["outputFormat"]], 
437            (sourceXMLS, self.sessionObj),)             
438        outputManager.getParam()
439        outputFilePaths=outputManager.generateOutputFilePaths()         
440        outputFilePathsURLs=[]
441        for outputFile in outputFilePaths:
442            newpath=outputFile.replace(OUTPUT_DIR, OUTPUT_DIR_URL)
443            outputFilePathsURLs.append(newpath)     
444        self.sessionObj["outputFilePaths"]=outputFilePathsURLs 
445       
446        print """Should really fork this process at this point so that we can return
447              something if likely to be large job."""
448        outputManager.createOutputs() 
449
450           
451    def _isTemporalDomainDefined(self, dset_num):
452        """
453        Method to check if all required temporal domain fields have been set.
454        Returns 1 if yes and None if no.
455        """
456        for n in range(1, self.sessionObj["numberOfDatasets"]+1):
457            for item in ["startDateTime_%s" % n, "endDateTime_%s" % n]:
458                if item not in self.sessionObj.keys():
459                    return
460        return 1
461   
462   
463    def _isHorizontalDomainDefined(self):
464        """
465        Method to check if all required horizontal domain fields have been set.
466        Returns 1 if yes and None if no.
467        """
468        for hk in HORIZ_KEYS:
469            if not self.sessionObj.has_key(hk):
470                return
471               
472        return 1
473   
Note: See TracBrowser for help on using the repository browser.