source: cows_wps/trunk/cows_wps/process_handler/validate_arguments.py @ 6111

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/cows_wps/trunk/cows_wps/process_handler/validate_arguments.py@6111
Revision 6111, 8.6 KB checked in by astephen, 10 years ago (diff)

added date time validator and data type.

Line 
1"""
2validate_arguments.py
3=====================
4
5Takes an argument dictionary being sent to a WPS process (or optionally to any callable
6object) and checks contents against the argument signature defined in the parsed
7configuration file.
8
9Holds ValidateArguments class to do this.
10
11Requires third party typecheck package to be installed.
12
13"""
14# Import standard library modules
15import os
16import sys
17import re
18import dateutil.parser
19import datetime
20
21import logging
22log = logging.getLogger(__name__)
23
24# Import local modules
25import cows_wps.utils.parse_capabilities_config as parse_capabilities_config
26from cows_wps.utils.parse_capabilities_config import caps_config_dict
27
28
29def convertType(dtype, item):
30    "Returns item converted to dtype data type or raises error."
31   
32    if dtype == "bool":
33       
34        if item.__class__ == bool:
35            return item
36       
37        item = parse_capabilities_config.fixTrueFalse(item)
38        if item not in (True, False):
39            raise Exception("Data type of '" + str(item) + "' must be 'True' or 'False'.")
40        else:
41            return item
42
43    if dtype in ("xml", "filepath"): 
44        dtype = "string"
45    elif dtype == "datetime":
46        return parseDateTime(item)
47    elif dtype in ("bbox",):
48        dtype = "float"
49
50    transformer = dtype
51    if transformer == "string": transformer = "str"
52   
53    try:
54        item = eval("%s('%s')" % (transformer, item))
55    except:
56        raise Exception("Could not convert item '" + str(item) + "' to data type '" + `dtype` + ".")
57   
58    return item
59
60
61def checkArgInList(item, enumeration):
62    "Returns True if item is in enumeration and raises error if not."
63   
64    if item not in enumeration:
65       
66        #check that the enumerations are strings
67        if type(enumeration[0]) == str:
68            enumerationString = ",".join(enumeration)
69        else:
70            enumerationString = ",".join([str(x) for x in enumeration])
71
72        raise Exception("Item %s not allowed in enumerated list: %s" % (item, enumerationString))
73   
74    return True
75
76
77def parseSimpleItem(item, dtype, enumeration):
78    """
79    Returns a parsed and correctly typed item checking it is of type dtype
80    and it is in enumeration if provided.
81    """
82    item = convertType(dtype, item)
83
84    # Check if enumeration specified and if given values are allowed
85    if enumeration:
86        enum_typed = [convertType(dtype, x) for x in enumeration] 
87        checkArgInList(item, enum_typed)   
88   
89    return item
90
91
92def parseSimpleArray(item, dtype, allowed_length, enumeration):
93    """
94    Returns a list of items following pattern '<x>|<y>|<z>',
95    checking against the valid lengths in allowed_length.
96    """
97#    log.debug('parseSimpleArray: allowed_length = %s' % allowed_length)
98    if type(item) in (type("string"), type(u"hi")):
99        items = item.split("|")
100    else:
101        items = item
102   
103#    log.debug('parseSimpleArray: %s' % ((item, dtype, allowed_length, enumeration),))
104
105    # Generate array
106    array = [convertType(dtype, x) for x in items]
107
108    # Check length of array is allowed
109    # Simple list of lengths first
110    l = len(array)
111    if allowed_length != None:
112   
113        if allowed_length.find("-") < 0:
114            allowed_lengths = [int(x) for x in allowed_length.split(",")]
115            if l not in allowed_lengths:
116                raise Exception("Array length of %s is not in list of allowed lengths: %s" \
117                                % (l, allowed_lengths))
118
119        elif allowed_length.find("-") > -1:     
120            low_high_match = re.match("(\d+)*-(\d+)*$", allowed_length.strip())
121            (low, high) = low_high_match.groups()
122            if low != None:
123                if l < int(low):
124                    raise Exception("Array length of '" + str(l) + "' is less than minimum allowed: " + str(low))
125
126            if high != None:
127                    if l > int(high):
128                        raise Exception("Array length of '" + str(l) + "' is greater than maximum allowed: " + str(high))
129
130    # Check if enumeration specified and if given values are allowed
131    if enumeration:
132        enum_typed = [convertType(dtype, x) for x in enumeration] 
133        for i in array:
134            checkArgInList(i, enum_typed)   
135
136    return array
137
138
139def parseDateTime(item):
140    """
141    Checks that item is a valid date time string: 'YYYY-MM-DDThh:mm:ss'.
142    """
143    pattn = re.compile("^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$")
144    if not pattn.match(item):
145        raise Exception("Item does not match date/time pattern.")
146
147    date_time_value = dateutil.parser.parse("2008-02-23T12:12:12")
148    return date_time_value
149
150
151class ValidateArguments:
152
153    def __init__(self, process_name, arg_dict, config_file="capabilities.ini"):
154        "Initialises dictionaries of input and validation rules."
155        log.info("Validation Arguments object created")
156        self.process_name = process_name
157        self.args = arg_dict
158#        log.debug('Config file: %s' % config_file)
159 
160        try:
161            self.valids = caps_config_dict[self.process_name]
162        except:
163            raise Exception("Cannot find entry for process '" + self.process_name + "' in config file.")
164
165    def validate(self):
166        """
167        Performs validation.
168        Raises exception if bad arguments given.
169        Returns a new dictionary of valid arguments of the correct type if valid.
170        """
171        new_dict = {}
172
173#        log.info("running validation")
174
175        # Step through each arg testing for validity
176        arg_names = self.valids["DataInputs"].keys()
177        arg_names.sort()
178               
179        log.debug("Validating list of arguments:(%s)" % (self.args,))
180       
181        # NOTE:policy of this code is to ignore any unknown arguments but not to raise an exception
182        for name in arg_names:
183
184            try:
185                # define d as the entry for this particular argument
186                d = self.valids["DataInputs"][name]
187               
188                # If did not get this arg test whether allowed and insert default value if given
189                if name not in self.args.keys():
190                    log.debug ("DataInput %s not present in args" % (name,))
191
192                    # Check if it is optional or has a default value
193                    if d.has_key("default"):
194                        value = d["default"]
195                        new_dict[name] = parseValue(value, d)
196                        log.debug("Using default value of %s for %s" % (new_dict[name], name))
197                       
198                    elif d.has_key('optional'):
199   
200                        if not d['optional']:
201                            raise Exception("key %s does not exist in args (%s)" % (name, self.args.keys()))
202       
203                    else:
204                        raise Exception("key %s does not exist in args (%s)" % (name, self.args.keys()))
205                   
206                    continue
207   
208                # Test the value provided
209                value_given = self.args[name]
210             
211                new_dict[name] = parseValue(value_given, d)
212               
213            except:
214                log.warning("Error occurred while parsing the %s parameter." \
215                            % (name,))
216                raise
217       
218        #log a warning if there are parameters that arn't needed
219        for recievedParam in self.args.keys():
220            if recievedParam not in arg_names:
221                log.warning("Parameter %s was recieved but not needed for a %s process." \
222                            % (recievedParam, self.process_name))
223       
224        return new_dict
225
226def parseValue(value, d):
227    parsedValue = None
228   
229    if d["item_type"] == "list":
230        parsedValue = parseSimpleArray(value, d["type"], d["allowed_length"], d["possible_values"])
231    elif d["item_type"] == "item":
232        parsedValue = parseSimpleItem(value, d["type"], d["possible_values"])
233    else:
234        raise ValueError(d["item_type"])   
235
236    return parsedValue
237
238def makeDict(qs):
239        dct = {}
240        items = qs.split(",")
241        for c in range(0, len(items)-1, 2):
242            dct[items[c]] = items[(c + 1)]
243        return dct
244
245if __name__ == "__main__":
246
247
248    qs = ("Username,astephens,RequestID,session32432,Dataset,probdata,Variables,precip,EmissionsScenarios,a1b,",
249         "TimeSlices,2020-2049|2030-2059,TemporalAveragingPeriod,sep,LocationType,region,Location,south_west,",
250         "ProbabilityDataType,cdf,ImageOutputFormat,ps,ImageWidth,900,ImageHeight,600,",
251         "LegendPosition,bc,YLimits,0|100")
252
253    qs = "".join(qs)
254   
255    dct = makeDict(qs)
256    x = ValidateArguments("AsyncTest", dct, ["/home/ashaon/wps/wps-ng_new/wps-ng/configs/AsyncTest.ini"])
257    print x.validate()
Note: See TracBrowser for help on using the repository browser.