f55712caa0fc331ea040357306a843c45787a160
[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):
41         return "<a href='%s'>%s</a>" % (url, text)
42
43     def startTable(border=1, class_style = None, caption = None):
44         output("<table border='%i'" % border)
45
46         if class_style:
47             output(" class='%s'" % class_style)
48
49         output(">")
50
51         if caption:
52             output("<caption>%s</caption>" % caption)
53
54     def headerTable(*headers):
55         output("<thead><tr>")
56
57         for c in headers:
58             output("<td>" + c + "</td>")
59
60         output("</tr></thead>")
61
62     def rowTable(*data, id = None, class_style = None, extra = None):
63         output("<tr")
64
65         if id:
66             output(" id='%s'" % id)
67
68         if class_style:
69             output(" class='%s'" % class_style)
70
71         if extra:
72             output(" %s" % extra)
73
74         output(">")
75
76         for c in data:
77             output("<td>" + str(c) + "</td>")
78
79         output("</tr>")
80
81     def endTable():
82         output("</table>")
83         
84     def checkbox(title, value, script=""):
85         return """<input type="checkbox" title="%s" %s %s>""" % (title, "checked" if value else "", ("onclick=\"%s\"" % script) if script else "")
86
87     if handler.path == "/html/netrender.js":
88         f = open(os.path.join(src_folder, "netrender.js"), 'rb')
89
90         handler.send_head(content = "text/javascript")
91         shutil.copyfileobj(f, handler.wfile)
92
93         f.close()
94     elif handler.path == "/html/netrender.css":
95         f = open(os.path.join(src_folder, "netrender.css"), 'rb')
96
97         handler.send_head(content = "text/css")
98         shutil.copyfileobj(f, handler.wfile)
99
100         f.close()
101     elif handler.path == "/html" or handler.path == "/":
102         handler.send_head(content = "text/html")
103         head("NetRender")
104
105         output("<h2>Master</h2>")
106
107         output("""<button title="remove all jobs" onclick="clear_jobs();">CLEAR JOB LIST</button>""")
108
109         startTable(caption = "Rules", class_style = "rules")
110
111         headerTable("type", "enabled", "description", "limit")
112
113         for rule in handler.server.balancer.rules:
114             rowTable(   
115                         "rating",
116                         checkbox("", rule.enabled, "balance_enable('%i', '%s')" % (id(rule), str(not rule.enabled))),
117                         rule,
118                         rule.str_limit() + 
119                         """<button title="edit limit" onclick="balance_edit('%i', '%s');">edit</button>""" % (id(rule), str(rule.limit)) if hasattr(rule, "limit") else "&nbsp;"
120                     )
121
122         for rule in handler.server.balancer.priorities:
123             rowTable(   
124                         "priority",
125                         checkbox("", rule.enabled, "balance_enable('%i', '%s')" % (id(rule), str(not rule.enabled))),
126                         rule,
127                         rule.str_limit() + 
128                         """<button title="edit limit" onclick="balance_edit('%i', '%s');">edit</button>""" % (id(rule), str(rule.limit)) if hasattr(rule, "limit") else "&nbsp;"
129                     )
130
131         for rule in handler.server.balancer.exceptions:
132             rowTable(   
133                         "exception",
134                         checkbox("", rule.enabled, "balance_enable('%i', '%s')" % (id(rule), str(not rule.enabled))),
135                         rule,
136                         rule.str_limit() + 
137                         """<button title="edit limit" onclick="balance_edit('%i', '%s');">edit</button>""" % (id(rule), str(rule.limit)) if hasattr(rule, "limit") else "&nbsp;"
138                     )
139
140         endTable()
141
142         output("<h2>Slaves</h2>")
143
144         startTable()
145         headerTable("name", "address", "last seen", "stats", "job")
146
147         for slave in handler.server.slaves:
148             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")
149
150         endTable()
151
152         output("<h2>Jobs</h2>")
153
154         startTable()
155         headerTable(
156                         "&nbsp;",
157                         "id",
158                         "name",
159                         "category",
160                         "chunks",
161                         "priority",
162                         "usage",
163                         "wait",
164                         "status",
165                         "length",
166                         "done",
167                         "dispatched",
168                         "error",
169                         "priority",
170                         "exception"
171                     )
172
173         handler.server.balance()
174
175         for job in handler.server.jobs:
176             results = job.framesStatus()
177             rowTable(
178                         """<button title="cancel job" onclick="cancel_job('%s');">X</button>""" % job.id +
179                         """<button title="pause job" onclick="request('/pause_%s', null);">P</button>""" % job.id +
180                         """<button title="reset all frames" onclick="request('/resetall_%s_0', null);">R</button>""" % job.id,
181                         job.id,
182                         link(job.name, "/html/job" + job.id),
183                         job.category if job.category else "<i>None</i>",
184                         str(job.chunks) +
185                         """<button title="increase chunks size" onclick="request('/edit_%s', &quot;{'chunks': %i}&quot;);">+</button>""" % (job.id, job.chunks + 1) +
186                         """<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 ""),
187                         str(job.priority) +
188                         """<button title="increase priority" onclick="request('/edit_%s', &quot;{'priority': %i}&quot;);">+</button>""" % (job.id, job.priority + 1) +
189                         """<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 ""),
190                         "%0.1f%%" % (job.usage * 100),
191                         "%is" % int(time.time() - job.last_dispatched),
192                         job.statusText(),
193                         len(job),
194                         results[DONE],
195                         results[DISPATCHED],
196                         str(results[ERROR]) +
197                         """<button title="reset error frames" onclick="request('/reset_%s_0', null);" %s>R</button>""" % (job.id, "disabled=True" if not results[ERROR] else ""),
198                         "yes" if handler.server.balancer.applyPriorities(job) else "no",
199                         "yes" if handler.server.balancer.applyExceptions(job) else "no"
200                     )
201
202         endTable()
203
204         output("</body></html>")
205
206     elif handler.path.startswith("/html/job"):
207         handler.send_head(content = "text/html")
208         job_id = handler.path[9:]
209
210         head("NetRender")
211
212         job = handler.server.getJobID(job_id)
213
214         if job:
215             output("<h2>Files</h2>")
216
217             startTable()
218             headerTable("path")
219
220             tot_cache = 0
221             tot_fluid = 0
222
223             for file in job.files:
224                 if file.filepath.endswith(".bphys"):
225                     tot_cache += 1
226                 elif file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
227                     tot_fluid += 1
228                 else:
229                     rowTable(file.filepath)
230
231             if tot_cache > 0:
232                 rowTable("%i physic cache files" % tot_cache, class_style = "toggle", extra = "onclick='toggleDisplay(&quot;.cache&quot;, &quot;none&quot;, &quot;table-row&quot;)'")
233                 for file in job.files:
234                     if file.filepath.endswith(".bphys"):
235                         rowTable(os.path.split(file.filepath)[1], class_style = "cache")
236
237             if tot_fluid > 0:
238                 rowTable("%i fluid bake files" % tot_fluid, class_style = "toggle", extra = "onclick='toggleDisplay(&quot;.fluid&quot;, &quot;none&quot;, &quot;table-row&quot;)'")
239                 for file in job.files:
240                     if file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
241                         rowTable(os.path.split(file.filepath)[1], class_style = "fluid")
242
243             endTable()
244
245             output("<h2>Blacklist</h2>")
246
247             if job.blacklist:
248                 startTable()
249                 headerTable("name", "address")
250
251                 for slave_id in job.blacklist:
252                     slave = handler.server.slaves_map[slave_id]
253                     rowTable(slave.name, slave.address[0])
254
255                 endTable()
256             else:
257                 output("<i>Empty</i>")
258
259             output("<h2>Frames</h2>")
260
261             startTable()
262             headerTable("no", "status", "render time", "slave", "log", "result")
263
264             for frame in job.frames:
265                 rowTable(
266                          frame.number,
267                          frame.statusText(),
268                          "%.1fs" % frame.time,
269                          frame.slave.name if frame.slave else "&nbsp;",
270                          link("view log", logURL(job_id, frame.number)) if frame.log_path else "&nbsp;",
271                          link("view result", renderURL(job_id, frame.number))  + " [" +
272                          link("jpeg", renderURL(job_id, frame.number, exr = False)) + "]" if frame.status == DONE else "&nbsp;"
273                          )
274
275             endTable()
276         else:
277             output("no such job")
278
279         output("</body></html>")
280