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

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

more fixes and improvements

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 + ".default"):
102                    value = fixTrueFalse(outputInputDict[param + ".default"].strip()) 
103                    if value == "now":
104                        value = apply(datetime.datetime, time.localtime(time.time())[:6])
105
106                    item_dict["default"] = value
107
108                if outputInputDict.has_key(param + ".optional"):
109                    item_dict["optional"] = fixTrueFalse(outputInputDict[param+".optional"].strip())
110
111                if outputInputDict.has_key(param + ".length"):
112                    if isValidLengthDescription(outputInputDict[param+".length"].strip()):
113                        item_dict["allowed_length"] = outputInputDict[param+".length"].strip().replace(" ","-")
114                    else:
115                        raise Exception("varilable length %s not recognised" % outputInputDict[param+".length"].strip())
116                else:
117                    item_dict["allowed_length"] = None
118
119                if outputInputDict.has_key(param + ".schema"):
120                    item_dict["schema"] = outputInputDict[param+".schema"].strip()
121                else:
122                    item_dict["schema"] = None
123
124                if outputInputDict.has_key(param + ".encoding"):
125                    item_dict["encoding"] = outputInputDict[param+".encoding"].strip()
126
127                if outputInputDict.has_key(param + ".mime_type"):
128                    item_dict["mime_type"] = outputInputDict[param+".mime_type"].strip()
129                else:
130                    item_dict["mime_type"] = None
131
132                for add_option in additional_param_options:
133                    key_name = param + "." + add_option
134
135                    if outputInputDict.has_key(key_name):
136                        value = outputInputDict[key_name].strip()
137                        item_dict[add_option] = value
138
139                outin_dict[param.strip()] = item_dict
140
141    return outin_dict
142
143def parseWpsInterface(wps_interface):
144    wps_dict = {}
145    for item in wps_interface:
146        value = wps_interface[item].strip()
147        if item == "response_types":
148                mappings = value.split(" ")
149                map_dict = {}
150                for i in mappings:
151                   map = i.split(":")
152                   if len (map) ==2 :
153                       map_dict[map[0]] = map[1]
154                   else:
155                       raise Exception ('Invalid response types values')
156                wps_dict[item] = map_dict
157        else:
158                wps_dict[item]= fixTrueFalse(value)
159
160
161    if wps_dict.has_key('response_types') == False:
162        wps_dict['response_types'] = {}
163
164#    log.debug('wps_dict["process_type"] = %s' % wps_dict.has_key('process_type'))
165
166    if not wps_dict.has_key('process_type'):
167        raise Exception ('A correct process type must be specified')
168    else:
169        if wps_dict.has_key('status'):
170                if wps_dict['status'] and wps_dict['process_type'] == 'sync':
171                        raise Exception ('Status cannot supported by a synchronous process')
172    return wps_dict
173   
174def parseGlobals(globals):
175    identifier= ""
176    global_dict={}
177    for item in globals:
178        if item == "Identifier":
179                identifier = globals[item].strip()
180        else:
181                global_dict[item] = fixTrueFalse(globals[item].strip())
182    return identifier, global_dict
183               
184def makeCapabilitiesConfigDict(config_file_list=None): #fname=capabilities_file):
185    '''Parses capabilities config file to return dictionary of:
186    {"identifiers" = [id1, id2...]
187    id1 = {"wps_interface":{"call_locally": "package.module#callable",....},
188           "globals" : {"Title" : "jsdkfjdslfj", "Abstract" : "lllll"...},
189           "DataInputs" : {"plotType" : {"item_type": "array",
190                                         "type": "string"
191                                         "allowed_length": "1-5",
192                                         "optional": True,
193                                         "default": True,
194                                         "enumeration" : ["yvsx", "....]},
195           "OrderedDataInputs": [key1, key2,....],
196           "ProcessOutputs" : {"title": {"item_type" : "item",
197                                         "type" : "string",
198                                         "allowed_length" : "-",
199                                         "enumeration": "-" } }
200    '''
201    global caps_config_dict
202   
203    if config_file_list == None:
204        try:
205            config_dir = wps_config_dict['proc_config_dir']
206        except KeyError:
207            log.warn('No process_config_dir option in application configuration')
208            base_dir = os.environ.get("WPS_BASE", ".")
209            config_dir = os.path.join(base_dir, "configs")
210           
211        config_file_list = glob.glob("%s/*.ini" % config_dir)
212   
213    caps_config_dict={}
214    ids = []
215    caps_config_dict["Identifiers"] = ids
216    for confFile in config_file_list:
217#        log.debug('Reading process config file %s' % confFile)
218       
219        try:
220            process_config = {}
221            config =  cp.CaseSensitiveOrderedConfigParser()
222            config.read([confFile])
223
224            identifier, process_config["globals"] = parseGlobals(dict(config.items("globals")))
225                                                         
226            process_config["wps_interface"] = parseWpsInterface(dict(config.items("wps_interface")))
227           
228            process_config["DataInputs"] = parseDataOutputInputs(dict(config.items("DataInputs")))
229            process_config["ProcessOutputs"] = parseDataOutputInputs(dict(config.items("ProcessOutputs")))
230           
231            # Correct code for parsing in options ordered within each section
232            all_data_input_options_ordered = config._option_orders["DataInputs"]
233            process_config["OrderedDataInputs"] = [di_opt for di_opt in all_data_input_options_ordered if di_opt.find(".") < 0]
234
235
236            ids.append(identifier)
237            caps_config_dict[identifier] = process_config
238        except:
239            print >> sys.stderr, "Exception occurred while reading config file %s" % (confFile)
240            raise
241
242
243caps_config_dict = None
244makeCapabilitiesConfigDict()
245
246if __name__=="__main__":
247    #setup the test wps dict and re-import
248    import cows_wps.tests.setup_test_wps_config
249    from cows_wps.utils.parse_wps_config import wps_config_dict
250   
251    makeCapabilitiesConfigDict()
252   
253   
254    def printDict(d, indent=None):
255        if indent == None: indent = ''
256       
257        for k in sorted(d):
258           
259            if d[k].__class__ == dict:
260                print indent + k, ':'
261                printDict(d[k], indent + ' ')
262            else:
263                print indent + k,'=',d[k], d[k].__class__
264   
265    printDict(caps_config_dict['GetData'])
266    print caps_config_dict['GetData'].keys()
267    print caps_config_dict['GetData']['DataInputs']
268
269
Note: See TracBrowser for help on using the repository browser.