source: cows_wps/trunk/cows_wps/utils/parse_capabilities_config.py @ 7387

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/cows_wps/trunk/cows_wps/utils/parse_capabilities_config.py@7387
Revision 7387, 10.8 KB checked in by astephen, 10 years ago (diff)

Added various additions to allow dynamic generation of arguments based
on WPS calls.

Line 
1"""
2parseCapabilitiesConfig.py
3==========================
4
5Code to parse the capabilities.ini config file for WPS in Python Paste.
6
7Follows rules set out in:
8
9http://proj.badc.rl.ac.uk/dcip/wiki/UkcipDdp/Tasks/BADCDevtTeam/WPSCode/DataTypeDescriptions
10
11Name of entry    Values (or patterns)    Comments
12-------------------------------------------------
13Sequence or not         "array" OR "item"       Only one of these used for every single entry
14Type (or python type)   "string", "float", "int", OR "bool"     Booleans must be expressed as "True" or "False"
15Length restrictions     <low>-<high>    Only relevant to "array" type. <low> and/or <high> can be omitted. Use just "-" for non-array types.
16Keywords        "default:<default_value>" AND/OR "optional"     None, one or both, comma-separated. For neither just use "-"
17Enumerated Values       <s1>,<s2>,<s3>  Comma-separated list of values that all entries must be one of. Use "-" for none.
18"""
19
20# Import standard library modules
21import ConfigParser
22from cStringIO import StringIO
23import time
24import datetime
25import os
26import sys
27import re
28import glob 
29
30# Local modules
31import cows_wps.utils.case_sensitive_ordered_config_parser as cp
32
33import logging
34log = logging.getLogger(__name__)
35
36from cows_wps.utils.parse_wps_config import wps_config_dict
37
38def fixTrueFalse(value):
39    "Make true or false value a lower title cased version."
40    if value.lower() in ("true", "false"):
41        value = eval(value.title())
42    return value
43 
44def isValidLengthDescription(item):
45    "Returns False if length description doesn't match patterns allowed and True if it does."
46    # Check "<low>-<high>" pattern first
47    low_high_match = re.match("(\d+)*-(\d+)*$", item)
48    if low_high_match:
49        # Now check low is not bigger than high (only if both provided)
50       
51        (low, high) = [int(x) for x in low_high_match.groups()]
52        if low > high:
53            raise Exception("Minimum array length cannot be greater than maximum: " + item)
54        return True
55
56    # Now check other patterns
57    for pattern in ("\d+$", "(\d+)(,\d+)*$"):
58        if re.match(pattern, item): return True
59   
60    return False
61
62
63def parseDataOutputInputs(outputInputDict):
64    outin_dict = {}
65
66    allowed_types = ("xml", 'complex','xml_complex_value',"string", "float", "int", "bool", "filepath", "bbox", "datetime")
67    additional_param_options = ("extent", "basedir")
68
69    for item in outputInputDict:
70        item_with_params= item.split(".")
71        param = item_with_params[0]
72
73        if not outin_dict.has_key(param):
74            item_dict = {}
75            item_type = outputInputDict[param].strip().split(".")
76
77            if item_type[0] not in allowed_types:
78                raise Exception("Type must be %s, cannot recognise: %s" % (allowed_types, item_type[0]))
79            else:
80                item_dict["type"] = item_type[0]
81
82                if item_type[0] == "xml_complex_value":
83                    try:
84                        item_dict["template"] = outputInputDict[param+".template"]
85                    except:
86                        raise Exception("xml_complex_value type must have a template")
87                   
88                if len(item_type) > 1:
89                    if item_type[1] == "list":
90                        item_dict["item_type"] = item_type[1]
91                    else:
92                        raise Exception("Type must be 'list', cannot recognise: " + item_type[1])
93                else:
94                    item_dict["item_type"] = "item"
95
96                if outputInputDict.has_key(param + ".possible_values"):
97                    item_dict["possible_values"] = outputInputDict[param+".possible_values"].strip().split(",")
98                else:
99                    item_dict["possible_values"]=None
100
101                if outputInputDict.has_key(param + ".argument_trigger"):
102                    item_dict["argument_trigger"] = outputInputDict[param + ".argument_trigger"].strip()
103                else:
104                    item_dict["argument_trigger"] = None
105
106                if outputInputDict.has_key(param + ".default"):
107                    value = fixTrueFalse(outputInputDict[param + ".default"].strip()) 
108                    if value == "now":
109                        value = apply(datetime.datetime, time.localtime(time.time())[:6])
110
111                    item_dict["default"] = value
112
113                if outputInputDict.has_key(param + ".optional"):
114                    item_dict["optional"] = fixTrueFalse(outputInputDict[param+".optional"].strip())
115
116                if outputInputDict.has_key(param + ".length"):
117                    if isValidLengthDescription(outputInputDict[param+".length"].strip()):
118                        item_dict["allowed_length"] = outputInputDict[param+".length"].strip().replace(" ","-")
119                    else:
120                        raise Exception("varilable length %s not recognised" % outputInputDict[param+".length"].strip())
121                else:
122                    item_dict["allowed_length"] = None
123
124               
125                if outputInputDict.has_key(param + ".schema"):
126                    item_dict["schema"] = outputInputDict[param+".schema"].strip()
127                else:
128                    item_dict["schema"] = None
129
130                if outputInputDict.has_key(param + ".encoding"):
131                    item_dict["encoding"] = outputInputDict[param+".encoding"].strip()
132
133                if outputInputDict.has_key(param + ".mime_type"):
134                    item_dict["mime_type"] = outputInputDict[param+".mime_type"].strip()
135                else:
136                    item_dict["mime_type"] = None
137
138                for add_option in additional_param_options:
139                    key_name = param + "." + add_option
140
141                    if outputInputDict.has_key(key_name):
142                        value = outputInputDict[key_name].strip()
143                        item_dict[add_option] = value
144
145                outin_dict[param.strip()] = item_dict
146
147    return outin_dict
148
149def parseWpsInterface(wps_interface):
150    wps_dict = {}
151    for item in wps_interface:
152        value = wps_interface[item].strip()
153        if item == "response_types":
154                mappings = value.split(" ")
155                map_dict = {}
156                for i in mappings:
157                   map = i.split(":")
158                   if len (map) ==2 :
159                       map_dict[map[0]] = map[1]
160                   else:
161                       raise Exception ('Invalid response types values')
162                wps_dict[item] = map_dict
163        else:
164                wps_dict[item]= fixTrueFalse(value)
165
166
167    if wps_dict.has_key('response_types') == False:
168        wps_dict['response_types'] = {}
169
170#    log.debug('wps_dict["process_type"] = %s' % wps_dict.has_key('process_type'))
171
172    if not wps_dict.has_key('process_type'):
173        raise Exception ('A correct process type must be specified')
174    else:
175        if wps_dict.has_key('status'):
176                if wps_dict['status'] and wps_dict['process_type'] == 'sync':
177                        raise Exception ('Status cannot supported by a synchronous process')
178    return wps_dict
179   
180def parseGlobals(globals):
181    identifier= ""
182    global_dict={}
183    for item in globals:
184        if item == "Identifier":
185                identifier = globals[item].strip()
186        else:
187                global_dict[item] = fixTrueFalse(globals[item].strip())
188    return identifier, global_dict
189               
190def makeCapabilitiesConfigDict(config_file_list=None): #fname=capabilities_file):
191    '''Parses capabilities config file to return dictionary of:
192    {"identifiers" = [id1, id2...]
193    id1 = {"wps_interface":{"call_locally": "package.module#callable",....},
194           "globals" : {"Title" : "jsdkfjdslfj", "Abstract" : "lllll"...},
195           "DataInputs" : {"plotType" : {"item_type": "array",
196                                         "type": "string"
197                                         "allowed_length": "1-5",
198                                         "optional": True,
199                                         "default": True,
200                                         "enumeration" : ["yvsx", "....]},
201           "OrderedDataInputs": [key1, key2,....],
202           "ProcessOutputs" : {"title": {"item_type" : "item",
203                                         "type" : "string",
204                                         "allowed_length" : "-",
205                                         "enumeration": "-" } }
206    '''
207    global caps_config_dict
208   
209    if config_file_list == None:
210        try:
211            config_dir = wps_config_dict['proc_config_dir']
212        except KeyError:
213            log.warn('No process_config_dir option in application configuration')
214            base_dir = os.environ.get("WPS_BASE", ".")
215            config_dir = os.path.join(base_dir, "configs")
216           
217        config_file_list = glob.glob("%s/*.ini" % config_dir)
218   
219    caps_config_dict={}
220    ids = []
221    caps_config_dict["Identifiers"] = ids
222    for confFile in config_file_list:
223#        log.debug('Reading process config file %s' % confFile)
224       
225        try:
226            process_config = {}
227            config =  cp.CaseSensitiveOrderedConfigParser()
228            config.read([confFile])
229
230            identifier, process_config["globals"] = parseGlobals(dict(config.items("globals")))
231                                                         
232            process_config["wps_interface"] = parseWpsInterface(dict(config.items("wps_interface")))
233           
234            process_config["DataInputs"] = parseDataOutputInputs(dict(config.items("DataInputs")))
235            process_config["ProcessOutputs"] = parseDataOutputInputs(dict(config.items("ProcessOutputs")))
236           
237            # Correct code for parsing in options ordered within each section
238            all_data_input_options_ordered = config._option_orders["DataInputs"]
239            process_config["OrderedDataInputs"] = [di_opt for di_opt in all_data_input_options_ordered if di_opt.find(".") < 0]
240
241
242            ids.append(identifier)
243            caps_config_dict[identifier] = process_config
244        except:
245            print >> sys.stderr, "Exception occurred while reading config file %s" % (confFile)
246            raise
247
248
249caps_config_dict = None
250makeCapabilitiesConfigDict()
251
252if __name__=="__main__":
253    #setup the test wps dict and re-import
254    import cows_wps.tests.setup_test_wps_config
255    from cows_wps.utils.parse_wps_config import wps_config_dict
256   
257    makeCapabilitiesConfigDict()
258   
259   
260    def printDict(d, indent=None):
261        if indent == None: indent = ''
262       
263        for k in sorted(d):
264           
265            if d[k].__class__ == dict:
266                print indent + k, ':'
267                printDict(d[k], indent + ' ')
268            else:
269                print indent + k,'=',d[k], d[k].__class__
270   
271    printDict(caps_config_dict['GetData'])
272    print caps_config_dict['GetData'].keys()
273    print caps_config_dict['GetData']['DataInputs']
274
275
Note: See TracBrowser for help on using the repository browser.