source: cows_wps/trunk/cows_wps/controllers/jobviewer.py @ 7538

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/cows_wps/trunk/cows_wps/controllers/jobviewer.py@7538
Revision 7538, 11.6 KB checked in by astephen, 10 years ago (diff)

Introduced use of pygments for XML rendering.

Line 
1import os
2import re
3import glob
4import logging
5import urllib
6import xml.etree.ElementTree as ET
7from pygments import highlight
8from pygments.lexers import XmlLexer
9from pygments.formatters import HtmlFormatter
10
11from pylons import request, response, session, tmpl_context as c
12from pylons.controllers.util import abort, redirect_to
13from routes import url_for
14
15
16from cows.exceptions import *
17from cows.pylons.ows_controller import render_ows_exception
18
19from cows_wps.controllers import *
20from cows_wps import utils
21from cows_wps.utils.common import *
22from cows_wps.renderer.ui_renderer import *
23from cows_wps.model.managers import requestManager
24from cows_wps.process_handler.context.process_context import ProcessContext
25from cows_wps.renderer import xml_renderer
26
27from cows_wps.utils.parse_wps_config import wps_config_dict
28from cows_wps.utils.parse_capabilities_config import caps_config_dict
29
30
31from cows_wps.model.managers import requestManager
32
33log = logging.getLogger(__name__)
34
35
36class JobviewerController(BaseController):
37
38
39    def index(self):
40        status_url = str(request.params.getone("status_url"))
41        getter = urllib.urlopen(status_url)
42        job_xml = getter.read()
43        getter.close()
44 
45        job_as_table = self._xmlToTable(job_xml)
46        job_xml_encoded = self._htmlifyXML(job_xml)
47        job_as_plots = "There are no plots available in the outputs of this job."
48
49        resp = """<div id="view_options" style="background: #EEF4FF; border: 2px outset blue; padding: 5px;">
50                <a href="javascript:switchViewTo('table_view');">Normal View</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"""
51
52        poll_info_div = ""
53        if self.status == "complete":
54            resp += """<a href="javascript:switchViewTo('xml_view');">View as XML</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
55                <a href="javascript:switchViewTo('plots_view');">View Plots (if available)</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"""
56        elif self.status == "running":
57            poll_info_div = '<div id="poll_info" class="poller"></div>'
58
59        resp  += """
60</div>
61<div id="view_container" style="background: white; border: 2px outset blue; padding: 5px;">
62        <div id="view_contents">
63                <div id="table_view" style="visibility: visible; height: auto;">%s%s</div>""" % (poll_info_div, job_as_table)
64
65        if self.status == "complete":
66            resp += """
67                <div id="xml_view" style="visibility: hidden; height: 0px;">%s</div>
68                <div id="plots_view" style="visibility: hidden; height: 0px;">%s</div>
69</div>""" % (job_xml_encoded, job_as_plots)
70
71        elif self.status == "running":
72            jobviewer_url = url_for(controller='jobviewer', status_url = status_url)
73            base_url = "http://" + request.url[7:].split("/")[0]
74            full_url = base_url + jobviewer_url
75
76            resp += """<input type="hidden" id="poll_url" value="%s" />""" % status_url
77            resp += """<input type="hidden" id="jobviewer_url" value="%s" />""" % jobviewer_url
78            resp += """<script type="text/javascript">
79        function init() {
80                pollWPS();
81        }
82
83</script>"""
84
85        renderer = UIPageRenderer()
86        resp = renderer.render("WPS Job Information",
87                              [("Job: %s" % self.job_id, resp)])
88        return resp
89
90    def _resolveStatus(self, status_node):
91        snode = status_node.getchildren()[0].tag
92        mdict = {"Succeeded": "complete",
93                 "Failed": "failed",
94                 "Accepted": "running",
95                 "Started": "running"}
96
97        for k,v in mdict.items():
98            if snode.find(k) > -1:
99                return v
100        else:
101            raise Exception("Could not match status tag.")
102
103    def _xmlToTable(self, xml):
104        "Chops up xml to get table of outputs."
105        self.node = ET.fromstring(xml)
106        self.namespace_ows = "http://www.opengeospatial.net/ows"
107        self.namespace = "http://www.opengeospatial.net/wps"
108
109        self.status = self._resolveStatus(self.node.find("{" + self.namespace + "}Status")) 
110
111        job_details = self.node.find("{" + self.namespace +  "}ProcessOutputs").find("{" + self.namespace +  "}Output").find("{" + self.namespace +  "}ComplexValue").find("{" + self.namespace +  "}WPSResponseDetails").find("{" + self.namespace +  "}JobDetails")
112
113        file_set_list = job_details.find("{" + self.namespace +  "}FileSet").getchildren()
114
115        items = ["JobID", "JobCompletionTimeDate", "RequestDescription",
116                 "RequestType", "JobCapabilities", "JobDuration", "JobVolume"]
117        mapped_names = ["Job ID", "Completion Time", 
118                        "Request Description", "Request Type", "Job Capabilities"]
119
120        if self.status == "complete":
121            mapped_names.extend(["Job Duration", "Output Size"])
122        else:
123            mapped_names.extend(["Estimated Job Duration", "Estimated Output Size"])
124
125        resp = "<h2>DETAILS</h2>"
126
127        self.job_id = job_details.find("{" + self.namespace + "}JobID").text
128
129        for (i, item) in enumerate(items):
130            x = job_details.find("{" + self.namespace +  "}" + item).text
131            if type(x) != str: 
132                x = "Undefined"
133
134            x = x.strip()
135
136            if item == "JobCapabilities" and x.find("send_to_extract_weather_data") > -1:
137                file_url = file_set_list[0].find("{" + self.namespace +  "}FileURL").text
138                file_path = mapDownloadURLToFilePath(file_url)
139                if self.status == "complete":
140                    x = x.replace("send_to_extract_weather_data",
141                            '<a href="/submit/form?proc_id=ExtractUKStationData&StationsFile=%s">Use stations to extract UK weather data</a>' % file_path) 
142
143            elif item == "JobVolume":
144                x = self._toMB(x)
145
146            elif item == "JobDuration":
147                secs = int(float(x))
148                secs = secs or 1
149                ext = ""
150                if secs > 1: ext = "s"
151                x = "%d second%s" % (secs, ext)
152
153            elif item == "JobCompletionTimeDate":
154                x = x.split(".")[0]
155 
156            resp += "<b>%s</b> = %s<br>" % (mapped_names[i], x)
157
158        # Render output files if completed
159        if self.status == "complete":
160
161            resp += "<h2>OUTPUT FILES</h2>"
162
163            if len(file_set_list) > 0:
164                resp += "The following file outputs are available from your job.<br>"
165            else:
166                resp += 'There are no output files associated with your job. Please click the "View as XML" link above to view your output.<br>'
167
168            for fnode in file_set_list:
169
170                furl = fnode.find("{" + self.namespace +  "}FileURL").text
171                fname_html = self._prettyFileNameHTML(furl)
172 
173                resp += fname_html
174
175                size = fnode.find("{" + self.namespace +  "}FileSize").text
176                size = self._toMB(size) 
177                resp += size
178
179                # Check for nested file set
180                fcontents = fnode.find("{" + self.namespace + "}FileContents")
181
182                for nested_fnode in fcontents.getchildren():
183                    nfurl = nested_fnode.find("{" + self.namespace +  "}FileURL").text
184                    nfname_html = self._prettyFileNameHTML(nfurl, nested = True) 
185                    resp += nfname_html
186
187                    nsize = nested_fnode.find("{" + self.namespace +  "}FileSize").text
188                    nsize = self._toMB(nsize)
189                    resp += nsize
190
191        return resp
192
193
194    def _prettyFileNameHTML(self, furl, nested = False):
195        """
196        Returns pretty displayable HTML filename string. If nested is True then
197        don't display download option and nest slightly.
198        """
199        fname = furl.split("/")[-1]
200        len_fname = len(fname)
201
202        p = 70
203        prefix = ""
204        download = '[<a href="%s">Download</a>]' % furl
205        fname_html = "<b>%s</b>" % fname
206
207        if nested:
208            p = 90 
209            prefix = "&nbsp;*&nbsp;" 
210            download = ""
211            fname_html = fname
212
213        padding = p - len_fname
214        if padding < 0: padding = 0
215
216        return ('<br><kbd>%s%s %s</kbd>' + (padding * "&nbsp;")) % (prefix, fname_html, download)
217 
218   
219    def _toMB(self, bytes, float_format = "%.2f"):
220        "Returns MB string from string or int or float."
221        mb = float_format % (int(bytes) / (2**20))
222        if mb == "0.00": mb = "0.01"
223        mb += " MB"
224        return mb
225       
226
227    def _htmlifyXML(self, xml):
228        "Returns html string that will make XML look ok on HTML page."
229
230        return highlight(xml, XmlLexer(), HtmlFormatter())
231        xml1 = xml.replace("<", "&lt;").replace(">", "&gt;").replace("\n", "<br>")
232
233        def retag(x): return x.replace("<", "&lt;").replace(">", "&gt;")
234
235        open_and_close = re.compile(r"^<([^/]+?)>(.*?)</\1>$")
236        open_and_open = re.compile(r"^<([^/]+?)>\s*<([^/]+?)>$")
237        close_and_close = re.compile(r"^<(/.+?)>\s*<(/.+?)>$")
238        open = re.compile(r"^<([^/]+)>$")
239        close = re.compile(r"^</(.+?)>$")
240        empty = re.compile(r"^<(.+?)/>$")
241        bad = re.compile(r"^</(.+?)>\s*<([^/]+)>$")
242
243        lines = [i.strip() for i in xml.split("\n")]
244
245        spant = '%s<span style="color: #%s;">%s</span><br>'
246        span2 = '%s<span style="color: #%s;">&lt;%s&gt;</span><span style="color: #%s;">%s</span><span style="color: #%s;">&lt;/%s&gt;</span><br>'
247
248        x = ""
249        pad = 0
250        pc = "&nbsp;" * 4
251
252        for line in lines:
253
254            colour = pad * 50000 
255            col = ("000000" + hex(colour).split("x")[-1])[-6:]
256            line2 = retag(line)
257
258            open_and_close_m = open_and_close.match(line)
259            open_and_open_m = False#open_and_open.match(line)
260            close_and_close_m = False#close_and_close.match(line)
261            open_m = open.match(line) 
262            close_m = close.match(line)
263            empty_m = empty.match(line)
264            bad_m = bad.match(line)
265
266            if line == lines[0]:
267                line = spant % (pc * pad, "0000ff", line2)
268                pad += 1
269            elif open_and_close_m:
270                groups = open_and_close_m.groups()
271                (tag, content) = groups[:2]
272                line = span2 % (pc * pad, "%s" % col, tag, "000000", content, "%s" % col, tag)
273            elif open_and_open_m:
274                groups = open_and_open_m.groups()
275                (tag1, tag2) = groups[:2]
276                pad += 1
277                line1 = spant % (pc * pad, "%s" % col, "&lt;%s&gt;" % tag1)
278                pad += 1
279                line2 = spant % (pc * pad, "%s" % col, "&lt;%s&gt;" % tag2)
280                line = line1 + line2
281            elif close_and_close_m:
282                groups = close_and_close_m.groups()
283                (tag1, tag2) = groups[:2]
284                pad -= 1
285                line1 = spant % (pc * pad, "%s" % col, "&lt;%s&gt;" % tag1)
286                pad -= 1
287                line2 = spant % (pc * pad, "%s" % col, "&lt;%s&gt;" % tag2)
288                line = line1 + line2
289            elif bad_m:
290                (first, second) = bad_m.groups()[:2]
291                pad -= 1
292                line1 = spant % (pc * pad, "%s" % col, "&lt;/%s&gt;" % first)
293                line2 = spant % (pc * pad, "%s" % col, "&lt;%s&gt;" % second)
294                line = line1 + line2
295                pad += 1
296            elif open_m:
297                line = spant % (pc * pad, "%s" % col, line2) 
298                pad += 1
299            elif close_m:
300                pad -= 1
301                line = spant % (pc * pad, "%s" % col, line2)
302            elif empty_m:
303                line = spant % (pc * pad, "%s" % col, line2)
304            else:
305                line = spant % (pc * pad, "0000ff", line2)
306
307            x += line
308
309        return x
310
311
Note: See TracBrowser for help on using the repository browser.