source: TI03-DataExtractor/trunk/pydxs/common.py @ 1715

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

Merged with titania version.

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"""
6common.py
7=========
8
9Holds common functions and classes used in the package.
10
11"""
12
13# Import python modules
14import os, re
15
16# Import package modules
17from serverConfig import *
18
19# Set up commmon variables. These are CAPITALISED to
20# improve visibility in other modules.
21
22STAGES=["DatasetGroupPage", "DatasetPage", "VariablesPage", "DomainPage", "ResultsPage"]
23
24DOMAIN_PARAMS=("start_time", "end_time", "time_interval", "horiz_domain", 
25                      "resolution", "vertical_units", "vertical_domain", 
26                       "outputFormat")
27
28TIME_KEYS=["Year", "Month", "Day", "Hour", "Minute", "Second"]
29
30HORIZ_KEYS=["northernExtent", "easternExtent", "southernExtent", "westernExtent"]
31
32CF_METADATA_GLOBAL_ATTRIBUTE_KEYS="Conventions title source institution history references comment".split()
33
34dateTimePattern=re.compile(r"^(\d{4}).(\d{1,2}).(\d{1,2})(\s+|T)(\d+):(\d+):(\d+\.?.*)$")
35
36
37# Set up common functions
38
39def getDateTimeComponents(dateTimeString):
40    """
41    Takes in a time string in standard DateTime format and returns the items in it.
42    """
43    match=dateTimePattern.match(dateTimeString)
44    if not match:
45        raise "Cannot match date time string: %s" % dateTimeString
46
47    items=match.groups()
48    (year, month, day, hour, minute)=[int(i) for i in items[:3]+items[4:6]]
49    second=float(items[6])
50    return (year, month, day, hour, minute, second)
51
52
53def mapFileFormatToExtension(format):
54    """
55    Returns a suitable file extension for a known file format.
56    """
57    extMap={"CSML/NetCDF":"nc", "NetCDF":"nc", "NASA Ames":"na", "ASCII Text":"txt"}
58    return extMap[format]
59   
60
61def createTimeKeyList():
62    """
63    Returns all the separate date and time component arguments required as a list.
64    """
65    allTimeKeys=[]
66    for ts in ["start", "end"]:
67        for i in range(1, MAX_NUM_DATASETS+1):
68            for key in TIME_KEYS:
69                allTimeKeys.append("%s%s_%s" % (ts, key, i))
70    return allTimeKeys
71
72
73def isUndefined(dict, item): 
74    """
75    Function that returns 1 if item is not a key in dict or
76    is defined as None in dict.
77    """
78    if dict.has_key(item):
79        if dict[item]!=None:
80            return 0
81    return 1
82
83
84def keyPatternMatch(dct, pattern, mode="string match"):
85    """
86    Returns 1 if one or more keys in the dictionary 'dct' match the
87    pattern provided using string.find(). Returns 0 otherwise.
88    """
89    for key in dct.keys():
90        if mode=="string match":
91            if key.find(pattern)>-1:
92                return 1
93        elif mode=="regex":
94            match=re.search(pattern, key)
95            if match:
96                return 1
97    return 0
98
99
100def getValuesInRange(start, end, array):
101    """
102    Takes a start and end value and returns the values in the array that are between them.
103    If not in range and are the same value then returns [start].
104    """
105    # check all are floats
106    array=map(lambda x: float(x), list(array))
107    if array[0]>array[-1]: array.reverse()
108    if start>end:  (start, end)=(end, start)
109    (start, end)=(float(start), float(end))
110    rtarray=[]
111    for i in array:
112        if i>=start and i<=end:
113            rtarray.append(i)
114    if rtarray==[] and start==end:
115        rtarray=[start]     
116    return rtarray
117
118
119def getSortedKeysLike(dct, pattern, mode="string match"):
120    """
121    Returns a list of all keys in the dictionary 'dct' that do a
122    string match on 'pattern'.
123    """
124    rtlist=[]
125    for key in dct.keys():
126        if mode=="string match":
127            if key.find(pattern)>-1:
128                rtlist.append(key)
129        elif mode=="regex":
130            match=re.search(pattern, key)
131            if match:
132                rtlist.append(key)
133    rtlist.sort()       
134    return rtlist
135   
136 
137def getDictSubsetMatching(dct, pattern, mode="string match"):
138    """
139    Returns a dictionary of all items in input dictionary 'dct'
140    where keys match 'pattern'.
141    """
142    rtdict={}
143    for key in dct.keys():
144        if mode=="string match":
145            if key.find(pattern)>-1:
146                rtdict[key]=dct[key]
147        elif mode=="regex":
148            match=re.search(pattern, key)
149            if match:
150                rtdict[key]=dct[key]       
151    return rtdict
152
153   
154def deleteDictSubsetMatching(dct, pattern, mode="string match"):
155    """
156    Deletes any items in the dictionary subset matched when calling
157    getDictSubsetMatching above. Returns the number of items deleted.
158    """
159    subdict=getDictSubsetMatching(dct, pattern, mode)
160    counter=0;
161    for key in subdict.keys():
162        del dct[key]
163        counter=counter+1
164    return counter
165   
166   
167def constructOutputInformation(pathDict, sizeDict, durationDict):
168    """
169    Constructs some output information to return to the client.
170    """
171    outputFilePaths=[]
172    outputSize=0
173    duration=0
174       
175    print "\n\nKEYS OH KEYS", pathDict.keys(), sizeDict.keys(), durationDict.keys()
176
177    for key in pathDict.keys():
178       
179        outputSize=outputSize+sizeDict[key]
180        duration=duration+durationDict[key]
181        outputFilePaths=outputFilePaths+pathDict[key]
182       
183    return (outputFilePaths, outputSize, duration)
184
185
186def intAll(stringList):
187    """
188    Takes a list of strings and returns them as ints.
189    """
190    return [int(i) for i in stringList]
191
192
193def convertDateTimeStringToYYYYMMDDHH(timeString):
194    """
195    Takes in a long CF-compliant time string and returns a shorter
196    YYYYMMDDHH string.
197    """
198    match=re.match(r"(\d{4}).(\d{1,2}).(\d{1,2})(\s+|T)(\d+):", timeString)
199    if match:
200        (y,m,d,blank,h)=match.groups()
201        timeString="%.4d%.2d%.2d%.2d" % (int(y), int(m), int(d), int(h))
202       
203    return timeString
204
205
206def deUnicodeObject(obj):
207    """
208    Returns the object identical except unicode strings are all
209    converted to normal strings.
210    """
211    tupleFound=None
212    if type(obj)==type((1,2)): 
213        tupleFound=1
214        obj=list(obj)
215
216    if type(obj)==type([]):
217        rtobj=[]
218        for i in obj:
219            if type(i) in (type([]), type((1,2))):
220                rtobj.append(deUnicodeObject(i)) 
221            elif type(i)==type(u""):
222                rtobj.append(str(i))
223            else:
224                rtobj.append(i)
225        if tupleFound==1: rtobj=tuple(rtobj)
226
227    elif type(obj)==type(u""):
228        rtobj=str(obj)
229    else: 
230        rtobj=obj
231       
232    return rtobj
233
234
235def translateURI(uri):
236    """
237    Takes a URL and returns the location of the file on the local network
238    or takes a local path and returns the URL of the file/directory.
239    """
240    if uri.find("http")>-1:
241        newpath=uri.replace(OUTPUT_DIR_URL, OUTPUT_DIR)
242    else:
243        newpath=uri.replace(OUTPUT_DIR, OUTPUT_DIR_URL)
244    print "\n\n", uri, "\n\n", newpath, "\n\n"
245    return newpath
246
247
248def sortUnique(list1):
249    """
250    Returns sorted list that removes any duplicates.
251    """
252    rtlist=[]
253    list1.sort()
254    for i in list1:
255        if i not in rtlist: rtlist.append(i)
256    return rtlist
257
258
259def overlap(list1, list2):
260    """
261    overlap function - returns a list of overlapping items in list1 and list2.
262    Otherwise returns None.
263    """ 
264    rtlist=[]
265    for i in list1:
266       if i in list2: rtlist.append(i)
267
268    if len(rtlist)>0:
269        return rtlist
270    else:
271        return None
272
273
274def compareAxes(ax1, ax2):
275    """Takes 2 cdms axis objects returning 1 if they are essentially
276    the same and 0 if not."""
277    import cdms
278    for axtype in ("time", "level", "latitude", "longitude"):
279        if cdms.axisMatches(ax1, axtype) and not cdms.axisMatches(ax2, axtype):
280            return 0
281
282    # Check ids
283    if ax1.id!=ax2.id: return 0
284    # Check lengths
285    if len(ax1)!=len(ax2): return 0
286    # Check values
287    if ax1._data_!=ax2._data_: return 0
288    # Check units
289    if ax1.units!=ax2.units: return 0
290    # OK, I think they are the same axis!
291    return 1
292
293
294def compareGrids(grid1, grid2):
295    """Takes 2 cdms grid objects returning 1 if they are essentially
296    the same and 0 if not."""
297    import cdms
298    if compareAxes(grid1.getLatitude(), grid2.getLatitude())==0: return 0
299    if compareAxes(grid1.getLongitude(), grid2.getLongitude())==0: return 0
300    return 1
301   
302
303def nudgeSingleValuesToAxisValues(value, axisValues, axisType):
304        """
305        Takes a value and checks if it is in the axisValues array. If not, it nudges the
306        value to the nearest neighbour in axis. It returns the new value twice along
307        with a message describing the change.
308        """
309        import cdms
310        rtMessage=""
311        newValue=None
312       
313        if value in axisValues:
314            newValue=value
315        else:
316            sortedAxis=[]
317            for i in axisValues:
318                sortedAxis.append(i)
319            sortedAxis.sort()
320           
321            if value<sortedAxis[0]:
322                newValue=sortedAxis[0]
323            elif value>sortedAxis[-1]:
324                newValue=sortedAxis[-1]
325            else:
326                for i in range(len(axisValues)):
327                    if i<(len(axisValues)-1):
328                        (current, nextone)=(axisValues[i], axisValues[i+1])
329                        if current>nextone:
330                            tempc=nextone
331                            nextone=current
332                            current=tempc
333                   
334                        if value>current and value<nextone:
335                            lowergap=value-current
336                            uppergap=nextone-value
337                            if uppergap==lowergap:
338                                newValue=nextone
339                            elif uppergap>lowergap:
340                                newValue=current
341                            elif uppergap<lowergap:
342                                newValue=nextone                       
343                            break
344            if newValue==None:
345                raise DXProcessingError, ("Could not nudge selected value '%s' into axis '%s'." % (value, axisType))
346            rtMessage="%s axis selected value '%s' nudged to nearest value in real axis '%s' ;" % (axisType, value, newValue)                   
347            print rtMessage
348           
349        return (newValue, newValue, rtMessage)   
350
351
352def createSummaryString(reqDict):
353    """
354    Returns a summary string of a request dictionary.
355    """
356    exclusions=("callMethod", "secureToken", "optionCategoryRequested", 
357                "action", "targetPage", "sessionID")
358       
359    summaryString=""   
360    for key in reqDict.keys():
361        if reqDict[key]!="" and key not in exclusions: 
362            summaryString=summaryString+("%s:\t%s\n" % (key, reqDict[key]))
363   
364    return summaryString
365
366
367def makeDirsAndPerms(basedir, dirs, permissions, owner, verbose="no"):
368    """
369    A function for making directories recursively and setting permissions/ownership.
370    """
371    if type(dirs)==str: dirs=[dirs]
372    dirs=[basedir]+list(dirs)
373    dir=None
374
375    while len(dirs)>0:
376
377        if dir:
378            dir=os.path.join(dir, dirs[0])
379        else:
380            dir=dirs[0]
381        dirs=dirs[1:]
382
383        if not os.path.exists(dir):
384            if verbose=="yes":   print "Making directory:", dir
385            os.mkdir(dir)
386            os.chmod(dir, permissions)
387            os.system('/bin/chown %s %s' % (owner, dir))
388
389    return
390   
391   
392def checkSubDirectory(user=None):
393    """
394    checkSubDirectory method - checks if the required sub-directory exists
395    to write the output and if not it creates it.
396    """ 
397    if SECURITY_MODEL=="badc":
398        reqDirScript="/home/badc/software/infrastructure/make_user_request_dir"
399        os.system("%s %s > /dev/null" % (reqDirScript, user))
400    else: 
401        makeDirsAndPerms(OUTPUT_DIR, user, OUTPUT_DIR_PERMISSION, "%s.%s" %
402                                               (OUTPUT_FILE_USER, OUTPUT_FILE_GROUP))
403                                               
404    if user==None or user=="None":   user="anonymous"
405    outputDir=os.path.join(OUTPUT_DIR, user, "dx_output")
406    if not os.path.isdir(outputDir):
407        makeDirsAndPerms(OUTPUT_DIR, (user, "dx_output"), OUTPUT_DIR_PERMISSION, "%s.%s" %
408                                           (OUTPUT_FILE_USER, OUTPUT_FILE_GROUP))
409    return outputDir
410
411
412def fixFilePermissions(path):
413    """
414    Fixes permissions on a file.
415    """
416    os.chmod(path, OUTPUT_FILE_PERMISSION)
417    os.system("chown %s.%s %s" % (OUTPUT_FILE_USER, OUTPUT_FILE_GROUP, path))   
418   
419   
420# Set up common classes
421class RedirectStdout:
422    """
423    RedirectStdout class - used to direct standard output away from
424    the screen in CGI scripts.
425    """
426
427    def write(self, item):
428        """
429        write method - allows dummy standard out to work.
430        """
431        pass
432
433    def flush(self):
434        """
435        Method to do nothing, again!
436        """
437        pass
Note: See TracBrowser for help on using the repository browser.