source: cows_wps/trunk/cows_wps/renderer/proc_config_renderer.py @ 7576

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/cows_wps/trunk/cows_wps/renderer/proc_config_renderer.py@7576
Revision 7576, 11.4 KB checked in by astephen, 10 years ago (diff)

Now we can have multiple basedirs set in the config file.

Line 
1import logging
2import re
3import copy
4
5from cows_wps.utils.create_process_config import createProcessConfig
6
7from cows_wps.lib.ui.proc_config import ProcConfig
8from cows_wps.lib.ui.proc_config_convertor import ProcConfigConvertor
9
10from cows_wps.renderer.form_renderer import FormRenderer
11
12log = logging.getLogger(__name__)
13
14
15class ProcConfigRenderer(object):
16
17    def __init__(self):
18        # Set arg to be picked up by calling code to determine whether a bbox is needed/rendered
19        self.bbox_arg_found = False
20        self.pc = ProcConfig()
21
22    def renderProcConfig(self, proc):
23        pcc = ProcConfigConvertor()
24        proc_config = pcc.convertConfig(proc)
25        return self.renderProcessPage(proc_config)
26
27    def renderProcessPage(self, pc):
28        """
29        Takes a dictionary representing the process config.
30        Returns a list of HTML components.
31        """
32        resp = ["<div>"]
33
34        resp.extend( self.renderBlock("Process Information", pc["Process Information"]) )
35        resp.extend( self.renderInputs(pc["Inputs"]) )
36        resp.extend( self.renderBlock("Process Outputs", pc["Outputs"]) ) 
37
38        resp.append( "</div>\n" )
39        return resp
40
41    def renderBlock(self, id, items):
42
43        resp = ["<div>"]
44        resp.append( "<H3>%s</H3>\n" % id )
45
46        for k, v in items:
47               
48            resp.append( '<div style="width:200px;"><b>%s:</b></div><div style="position: relative; left:210px; top: -20px;">%s</div>\n' % (k, v) )
49
50        resp.append("</div>\n")
51        return resp
52
53    def renderInputs(self, items):
54        resp = ["<div>\n<H3>Process Inputs</H3>"]
55        resp.append( '<table border="1" style="position: relative; left: 30px;">\n' )
56
57        keys = ["Parameter name", "type", "item_type", "allowed_length", "possible_values"]
58        skeys = ["Parameter name", "Data type", "Is array?", "Permitted lengths", "Allowed values"]
59
60        resp.append("<tr>\n")
61        for (i, key) in enumerate(keys):
62            resp.append( '<td style="background:#FF9900"><b>%s</b></td>\n' % skeys[i] ) 
63
64        for (param_name, dct) in items: 
65
66            resp.append( '<tr>\n<td style="background:#FFFFFF">%s</td>\n' % param_name )
67
68            for key in keys[1:]:
69               
70                value = dct[key]
71
72                if key == "possible_values" and value != None:
73                    if len(value) == 1 and value[0][0] == "$":
74                        # Indicates a reference to a separate Web Service call
75                        value = "Call to web service: %s" % value[0]
76                    else:
77                        value = ", ".join(value)
78
79                if value != None: 
80                    resp.append( '<td style="background:#00FF00;">%s</td>\n' % value ) 
81                else:
82                    resp.append( '<td style="background:red;">-</td>\n' )
83
84            resp.append("</tr>\n")
85
86        resp.append("</table>\n")
87        resp.append("</div>\n")
88        return resp
89
90    def renderProcsViewTable(self):
91        """
92        Renders the table of all procs on the view page.
93        """
94        resp = """<table border="1">\n<tr>"""
95        headings = ("Process name", "Short description", "Full description", "Comments", 
96                    "Options")
97        for heading in headings:
98            resp += """<td width="17%%" style="background:#FF9900"><b>%s</b></td>\n""" % heading 
99        resp += "</tr>\n"
100
101        procs = self.pc.getProcList()
102        for (proc, long_name) in procs.items():
103            # Get config dict for this proc
104            proc_dict = createProcessConfig(proc)["Capabilities"]["globals"]
105
106            required_items = ("Title", "Abstract", "ProcessDescription")     
107            resp += """<tr>\n<td style="background: #FFFFFF;">%s</td>\n""" % proc
108
109            for req_item in required_items:
110                resp += """<td style="background: #FFFFFF;">%s</td>\n""" % proc_dict[req_item]
111
112            # Now add link to details and submit pages
113            resp += """<td style="background: #FFFFFF;"><a href="/submit/form?proc_id=%s">Submit job</a><br />
114                           <a href="/view/proc?proc_id=%s">View full details</a></td>\n""" % (proc, proc)
115
116            resp += "</tr>\n"
117
118        resp += "</table>\n" 
119        return resp
120
121    def renderProcSubmissionForm(self, proc, args):
122        """       
123        Renders a submission form for a proc.
124        Populates items with args dictionary if they exist.
125        """
126        pcc = ProcConfigConvertor()
127        proc_config = pcc.convertConfig(proc)
128        inputs = proc_config["Inputs"]
129
130        # Get form renderer
131        fm = FormRenderer()
132
133        # Define default arg for whether dynamic form args exist
134        resp = """<script type="text/javascript">var dynamic_form_manager = null;</script>"""
135
136        resp += """Please complete the form below to submit a request to the CEDA Web Processing Service.
137                Note that some processes are restricted to registered users only.
138                Click the 'Submit' button to submit your request.<br><br>"""
139
140        dynamic_args_present = [i[1] for i in inputs if i[1]["dynamic"] != False]
141
142        if dynamic_args_present != []:
143            # Since args are present we need to create dynamic form manager in javascript
144            resp += """<script type="text/javascript">dynamic_form_manager = new DynamicFormManager();</script>"""
145
146            resp += """\n\nThis process includes arguments for which the possible values
147                       are dependent on calls to an external process. Please click the
148                       'Update form' button at any time to update the argument options
149                       based on the selections you have made.<br><br>"""
150
151        resp += """\n\n <form action="/submit" onSubmit="return validateInputs();">
152                <input type="hidden" name="proc_id" id="proc_id" value="%s" />
153                <input type="hidden" id="_textarea_ids" name="_textarea_ids" value="" />
154                <table border="0">\n""" % proc
155
156        # Now render the inputs dict as a form
157        count = 0 
158        for (name, input) in inputs:
159
160            # Set colour style for row
161            count += 1
162            row_style = ("even_row", "odd_row")[count % 2]
163
164            # Start HTML for row
165            resp += """<tr class="%s"><td width="25%%"><b>%s</b></td><td width="30%%">\n""" % (row_style, name)
166
167            # Parse arguments from config
168            al = input["allowed_length"]
169            pv = input["possible_values"]
170   
171            if pv != None and len(pv) == 1 and pv[0][0] == "$":
172                pv = self._resolveWebServiceCallForPossibleValues(pv[0])
173
174            is_dynamic = input.get("dynamic", False)
175
176            if is_dynamic:
177                # Add to dynamic form manager
178                resp += """\n<script type="text/javascript">dynamic_form_manager.addDynamicFormField("%s", "%s", "%s");</script>""" % \
179                         (name, input["possible_values_url_template"],
180                          input["possible_values_xml_path"])
181
182            default = input.get("default", None)
183            opt = input.get("optional", False)
184            array_or_item = input["item_type"]
185
186            multiple = False
187            if array_or_item == "list":
188                multiple = True
189
190            tp = input["type"]
191
192            # Add a default instruction for this input type
193            instruction = ""
194            if opt == True:
195                instruction = "This input is optional."
196            if multiple == True:
197                instruction += " Multiple inputs are allowed."
198
199            # Now render them according to data type etc
200            if pv != None:
201                resp += fm.renderSelectList(name, values=pv, optional=opt, multiple=multiple)
202                n_items = "an item"
203                if multiple == True: n_items = "one or more items"
204                instruction += " Please select %s from the list." % n_items
205
206            elif is_dynamic:
207                resp += fm.renderSelectList(name, values = ["-- Dynamic field (see instructions) --"], optional = opt, multiple = multiple)
208                n_items = "an item"
209                if multiple == True: n_items = "one or more items"
210                instruction += "This field is dynamic, please select other options then click 'Update form' to populate the form field. Then please select %s from the list." % n_items
211
212            elif tp == "bool":
213                resp += fm.renderRadioButton(name, is_boolean=True)
214                instruction += " Please select either true or false."
215           
216            elif tp in ("float", "int", "string", "datetime"):
217                if tp == "datetime":
218                    if args.has_key(name):
219                        # Use input arg sent in to form if received
220                        default = args[name]
221                    elif default:
222                        # Ensure time formatted correctly
223                        default = str(default).replace(" ", "T")
224       
225                    instruction += " Please insert a date/time field in the format <kbd><B>YYYY-MM-DDThh:mm:ss</B></kbd> such as <kbd>2009-01-01T00:00:00</kbd>."
226                else:
227                    # Use CGI arg if received
228                    if args.has_key(name):
229                        default = args[name]
230
231                    instruction += " Please insert a value of type: %s." % tp
232
233                resp += fm.renderTextInput(name, dtype = tp, optional = opt, default = default, multiple = multiple)
234           
235            elif tp == "filepath":
236                basedirs = input["basedir"] 
237                resp += fm.renderTypeAheadDirList(name, basedirs[0])
238                instruction += " Please type a file path on the file system. Click down to auto-fill with one of the options on the drop-down list. Possible valid options are: <kbd>%s</kbd>." % ", ".join(basedirs)
239
240            elif tp == "bbox":
241                self.bbox_arg_found = True
242                extent = input.get("extent", False)
243                resp += fm.renderBBox(name, extent)
244                csv_extent = extent.replace("|", ", ")
245                instruction += " Please select a valid bounding box with the following geographical extent: %s" % csv_extent
246
247            resp += "</td><td>%s</td></tr>\n" % instruction
248     
249        count += 1
250        row_style = ("even_row", "odd_row")[count % 2] 
251        resp += '<tr class="%s"><td></td><td colspan="2">' % row_style
252
253        if dynamic_args_present != []:
254            resp += '<input type="button" id="update" name="update" value="Update form" onClick="dynamic_form_manager.updateForm();" /> Click to update the options above based on the selections you have made.<br />\n'
255
256        resp += '<input type="submit" value="Submit" /> Click this button when you are happy with your selections.</td></table>\n</form>\n' 
257        return  fm.htmlify(resp)
258
259    def _resolveWebServiceCallForPossibleValues(self, pv_descriptor):
260        """
261        Calls a web service to populate possible values.
262        """
263        pv_pattn = re.compile("^\$(\w+):((\w+)=(\w+))*/(.*)$")
264
265        match = pv_pattn.match(pv_descriptor)
266
267        if not match:
268            return ["ERROR - Arguments cannot be determined"]
269
270        items = match.groups()
271        log.warn("Items: %s" % str(items))
272
273        process = items[0]
274        path = items[-1]
275        arg_list = items[2:-1]
276        args = []
277
278        while len(arg_list) > 0:
279            (k, v) = arg_list[:2]
280            args.append((k, v))
281            arg_list = arg_list[2:]
282
283        possible_values = []
284        for (k, v) in args:
285            possible_values.append("%s = %s" % (k, v)) 
286
287        return possible_values
Note: See TracBrowser for help on using the repository browser.