netrender:
[blender.git] / release / scripts / io / netrender / master_html.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 import os
20 import re
21 import shutil
22 from netrender.utils import *
23
24 src_folder = os.path.split(__file__)[0]
25
26 def get(handler):
27     def output(text):
28         handler.wfile.write(bytes(text, encoding='utf8'))
29
30     def head(title):
31         output("<html><head>")
32         output("<script src='/html/netrender.js' type='text/javascript'></script>")
33 #               output("<script src='/html/json2.js' type='text/javascript'></script>")
34         output("<title>")
35         output(title)
36         output("</title></head><body>")
37         output("<link rel='stylesheet' href='/html/netrender.css' type='text/css'>")
38
39
40     def link(text, url, script=""):
41         return "<a href='%s' %s>%s</a>" % (url, script, text)
42
43     def tag(name, text, attr=""):
44         return "<%s %s>%s</%s>" % (name, attr, text, name)
45
46     def startTable(border=1, class_style = None, caption = None):
47         output("<table border='%i'" % border)
48
49         if class_style:
50             output(" class='%s'" % class_style)
51
52         output(">")
53
54         if caption:
55             output("<caption>%s</caption>" % caption)
56
57     def headerTable(*headers):
58         output("<thead><tr>")
59
60         for c in headers:
61             output("<td>" + c + "</td>")
62
63         output("</tr></thead>")
64
65     def rowTable(*data, id = None, class_style = None, extra = None):
66         output("<tr")
67
68         if id:
69             output(" id='%s'" % id)
70
71         if class_style:
72             output(" class='%s'" % class_style)
73
74         if extra:
75             output(" %s" % extra)
76
77         output(">")
78
79         for c in data:
80             output("<td>" + str(c) + "</td>")
81
82         output("</tr>")
83
84     def endTable():
85         output("</table>")
86         
87     def checkbox(title, value, script=""):
88         return """<input type="checkbox" title="%s" %s %s>""" % (title, "checked" if value else "", ("onclick=\"%s\"" % script) if script else "")
89
90     if handler.path == "/html/netrender.js":
91         f = open(os.path.join(src_folder, "netrender.js"), 'rb')
92
93         handler.send_head(content = "text/javascript")
94         shutil.copyfileobj(f, handler.wfile)
95
96         f.close()
97     elif handler.path == "/html/netrender.css":
98         f = open(os.path.join(src_folder, "netrender.css"), 'rb')
99
100         handler.send_head(content = "text/css")
101         shutil.copyfileobj(f, handler.wfile)
102
103         f.close()
104     elif handler.path == "/html" or handler.path == "/":
105         handler.send_head(content = "text/html")
106         head("NetRender")
107
108         output("<h2>Master</h2>")
109
110         output("""<button title="remove all jobs" onclick="clear_jobs();">CLEAR JOB LIST</button>""")
111
112         startTable(caption = "Rules", class_style = "rules")
113
114         headerTable("type", "enabled", "description", "limit")
115
116         for rule in handler.server.balancer.rules:
117             rowTable(   
118                         "rating",
119                         checkbox("", rule.enabled, "balance_enable('%i', '%s')" % (id(rule), str(not rule.enabled))),
120                         rule,
121                         rule.str_limit() + 
122                         """<button title="edit limit" onclick="balance_edit('%i', '%s');">edit</button>""" % (id(rule), str(rule.limit)) if hasattr(rule, "limit") else "&nbsp;"
123                     )
124
125         for rule in handler.server.balancer.priorities:
126             rowTable(   
127                         "priority",
128                         checkbox("", rule.enabled, "balance_enable('%i', '%s')" % (id(rule), str(not rule.enabled))),
129                         rule,
130                         rule.str_limit() + 
131                         """<button title="edit limit" onclick="balance_edit('%i', '%s');">edit</button>""" % (id(rule), str(rule.limit)) if hasattr(rule, "limit") else "&nbsp;"
132                     )
133
134         for rule in handler.server.balancer.exceptions:
135             rowTable(   
136                         "exception",
137                         checkbox("", rule.enabled, "balance_enable('%i', '%s')" % (id(rule), str(not rule.enabled))),
138                         rule,
139                         rule.str_limit() + 
140                         """<button title="edit limit" onclick="balance_edit('%i', '%s');">edit</button>""" % (id(rule), str(rule.limit)) if hasattr(rule, "limit") else "&nbsp;"
141                     )
142
143         endTable()
144
145         output("<h2>Slaves</h2>")
146
147         startTable()
148         headerTable("name", "address", "last seen", "stats", "job")
149
150         for slave in handler.server.slaves:
151             rowTable(slave.name, slave.address[0], time.ctime(slave.last_seen), slave.stats, link(slave.job.name, "/html/job" + slave.job.id) if slave.job else "None")
152
153         endTable()
154
155         output("<h2>Jobs</h2>")
156
157         startTable()
158         headerTable(
159                         "&nbsp;",
160                         "id",
161                         "name",
162                         "category",
163                         "chunks",
164                         "priority",
165                         "usage",
166                         "wait",
167                         "status",
168                         "length",
169                         "done",
170                         "dispatched",
171                         "error",
172                         "priority",
173                         "exception"
174                     )
175
176         handler.server.balance()
177
178         for job in handler.server.jobs:
179             results = job.framesStatus()
180             rowTable(
181                         """<button title="cancel job" onclick="cancel_job('%s');">X</button>""" % job.id +
182                         """<button title="pause job" onclick="request('/pause_%s', null);">P</button>""" % job.id +
183                         """<button title="reset all frames" onclick="request('/resetall_%s_0', null);">R</button>""" % job.id,
184                         job.id,
185                         link(job.name, "/html/job" + job.id),
186                         job.category if job.category else "<i>None</i>",
187                         str(job.chunks) +
188                         """<button title="increase chunks size" onclick="request('/edit_%s', &quot;{'chunks': %i}&quot;);">+</button>""" % (job.id, job.chunks + 1) +
189                         """<button title="decrease chunks size" onclick="request('/edit_%s', &quot;{'chunks': %i}&quot;);" %s>-</button>""" % (job.id, job.chunks - 1, "disabled=True" if job.chunks == 1 else ""),
190                         str(job.priority) +
191                         """<button title="increase priority" onclick="request('/edit_%s', &quot;{'priority': %i}&quot;);">+</button>""" % (job.id, job.priority + 1) +
192                         """<button title="decrease priority" onclick="request('/edit_%s', &quot;{'priority': %i}&quot;);" %s>-</button>""" % (job.id, job.priority - 1, "disabled=True" if job.priority == 1 else ""),
193                         "%0.1f%%" % (job.usage * 100),
194                         "%is" % int(time.time() - job.last_dispatched),
195                         job.statusText(),
196                         len(job),
197                         results[DONE],
198                         results[DISPATCHED],
199                         str(results[ERROR]) +
200                         """<button title="reset error frames" onclick="request('/reset_%s_0', null);" %s>R</button>""" % (job.id, "disabled=True" if not results[ERROR] else ""),
201                         "yes" if handler.server.balancer.applyPriorities(job) else "no",
202                         "yes" if handler.server.balancer.applyExceptions(job) else "no"
203                     )
204
205         endTable()
206
207         output("</body></html>")
208
209     elif handler.path.startswith("/html/job"):
210         handler.send_head(content = "text/html")
211         job_id = handler.path[9:]
212
213         head("NetRender")
214
215         job = handler.server.getJobID(job_id)
216
217         if job:
218             output("<h2>Files</h2>")
219
220             startTable()
221             headerTable("path")
222
223             tot_cache = 0
224             tot_fluid = 0
225
226             for file in job.files:
227                 if file.filepath.endswith(".bphys"):
228                     tot_cache += 1
229                 elif file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
230                     tot_fluid += 1
231                 else:
232                     rowTable(file.filepath)
233
234             if tot_cache > 0:
235                 rowTable("%i physic cache files" % tot_cache, class_style = "toggle", extra = "onclick='toggleDisplay(&quot;.cache&quot;, &quot;none&quot;, &quot;table-row&quot;)'")
236                 for file in job.files:
237                     if file.filepath.endswith(".bphys"):
238                         rowTable(os.path.split(file.filepath)[1], class_style = "cache")
239
240             if tot_fluid > 0:
241                 rowTable("%i fluid bake files" % tot_fluid, class_style = "toggle", extra = "onclick='toggleDisplay(&quot;.fluid&quot;, &quot;none&quot;, &quot;table-row&quot;)'")
242                 for file in job.files:
243                     if file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
244                         rowTable(os.path.split(file.filepath)[1], class_style = "fluid")
245
246             endTable()
247
248             output("<h2>Blacklist</h2>")
249
250             if job.blacklist:
251                 startTable()
252                 headerTable("name", "address")
253
254                 for slave_id in job.blacklist:
255                     slave = handler.server.slaves_map[slave_id]
256                     rowTable(slave.name, slave.address[0])
257
258                 endTable()
259             else:
260                 output("<i>Empty</i>")
261
262             output("<h2>Frames</h2>")
263
264             startTable()
265             headerTable("no", "status", "render time", "slave", "log", "result", "")
266
267             for frame in job.frames:
268                 rowTable(
269                          frame.number,
270                          frame.statusText(),
271                          "%.1fs" % frame.time,
272                          frame.slave.name if frame.slave else "&nbsp;",
273                          link("view log", logURL(job_id, frame.number)) if frame.log_path else "&nbsp;",
274                          link("view result", renderURL(job_id, frame.number))  + " [" +
275                          tag("span", "show", attr="class='thumb' onclick='showThumb(%s, %i)'" % (job.id, frame.number)) + "]" if frame.status == DONE else "&nbsp;",
276                          "<img name='thumb%i' title='hide thumbnails' src='' class='thumb' onclick='showThumb(%s, %i)'>" % (frame.number, job.id, frame.number)
277                          )
278
279             endTable()
280         else:
281             output("no such job")
282
283         output("</body></html>")
284