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

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

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

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        resp = """Please complete the form below to submit a request to the CEDA Web Processing Service.
134                Note that some processes are restricted to registered users only.
135                Click the 'Submit' button to submit your request.<br><br>"""
136
137        triggers_present = [i[1] for i in inputs if i[1]["argument_trigger"] != None]
138        if triggers_present != []:
139            resp += """\n\nThis process includes arguments for which the possible values
140                       are dependent on calls to an external process. Please click the
141                       'Update form' button at any time to update the argument options
142                       based on the selections you have made.<br><br>"""
143
144        resp += """\n\n <form action="/submit" onSubmit="return validateInputs();">
145                <input type="hidden" name="proc_id" id="proc_id" value="%s" />
146                <input type="hidden" id="_textarea_ids" name="_textarea_ids" value="" />
147                <table border="0">\n""" % proc
148
149        # Now render the inputs dict as a form
150        count = 0 
151        for (name, input) in inputs:
152
153            # Set colour style for row
154            count += 1
155            row_style = ("even_row", "odd_row")[count % 2]
156
157            # Start HTML for row
158            resp += """<tr class="%s"><td width="25%%"><b>%s</b></td><td width="30%%">\n""" % (row_style, name)
159
160            # Parse arguments from config
161            al = input["allowed_length"]
162            pv = input["possible_values"]
163   
164            if pv != None and len(pv) == 1 and pv[0][0] == "$":
165                pv = self._resolveWebServiceCallForPossibleValues(pv[0])
166
167            default = input.get("default", None)
168            opt = input.get("optional", False)
169            array_or_item = input["item_type"]
170
171            multiple = False
172            if array_or_item == "list":
173                multiple = True
174
175            tp = input["type"]
176
177            # Add a default instruction for this input type
178            instruction = ""
179            if opt == True:
180                instruction = "This input is optional."
181            if multiple == True:
182                instruction += " Multiple inputs are allowed."
183
184            # Now render them according to data type etc
185            if pv != None:
186                resp += fm.renderSelectList(name, values=pv, optional=opt, multiple=multiple)
187                n_items = "an item"
188                if multiple == True: n_items = "one or more items"
189                instruction += " Please select %s from the list." % n_items
190
191            elif tp == "bool":
192                resp += fm.renderRadioButton(name, is_boolean=True)
193                instruction += " Please select either true or false."
194           
195            elif tp in ("float", "int", "string", "datetime"):
196                if tp == "datetime":
197                    if args.has_key(name):
198                        # Use input arg sent in to form if received
199                        default = args[name]
200                    elif default:
201                        # Ensure time formatted correctly
202                        default = str(default).replace(" ", "T")
203       
204                    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>."
205                else:
206                    # Use CGI arg if received
207                    if args.has_key(name):
208                        default = args[name]
209
210                    instruction += " Please insert a value of type: %s." % tp
211
212                resp += fm.renderTextInput(name, dtype = tp, optional = opt, default = default, multiple = multiple)
213           
214            elif tp == "filepath":
215                base_dir = input["basedir"] 
216                resp += fm.renderTypeAheadDirList(name, base_dir)
217                instruction += " Please type a file path on the CEDA file system. Click down to auto-fill with one of the options on the drop-down list."
218
219            elif tp == "bbox":
220                self.bbox_arg_found = True
221                extent = input.get("extent", False)
222                resp += fm.renderBBox(name, extent)
223                csv_extent = extent.replace("|", ", ")
224                instruction += " Please select a valid bounding box with the following geographical extent: %s" % csv_extent
225
226            resp += "</td><td>%s</td></tr>\n" % instruction
227     
228        count += 1
229        row_style = ("even_row", "odd_row")[count % 2] 
230        resp += '<tr class="%s"><td></td><td colspan="2">' % row_style
231
232        if triggers_present != []:
233            resp += '<input type="button" id="update" name="update" value="Update form" onClick="updateForm();" />'
234
235        resp += '<input type="submit" value="Submit" /> Click this button when you are happy with your selections.</td></table>\n</form>\n' 
236        return  fm.htmlify(resp)
237
238    def _resolveWebServiceCallForPossibleValues(self, pv_descriptor):
239        """
240        Calls a web service to populate possible values.
241        """
242        pv_pattn = re.compile("^\$(\w+):((\w+)=(\w+))*/(.*)$")
243
244        match = pv_pattn.match(pv_descriptor)
245
246        if not match:
247            return ["ERROR - Arguments cannot be determined"]
248
249        items = match.groups()
250        log.warn("Items: %s" % str(items))
251
252        process = items[0]
253        path = items[-1]
254        arg_list = items[2:-1]
255        args = []
256
257        while len(arg_list) > 0:
258            (k, v) = arg_list[:2]
259            args.append((k, v))
260            arg_list = arg_list[2:]
261
262        possible_values = []
263        for (k, v) in args:
264            possible_values.append("%s = %s" % (k, v)) 
265
266        return possible_values
Note: See TracBrowser for help on using the repository browser.