Merge with trunk, revision 28528 - 28976.
[blender-staging.git] / release / scripts / io / netrender / model.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 import sys, os
20 import http, http.client, http.server, urllib
21 import subprocess, shutil, time, hashlib
22
23 from netrender.utils import *
24
25 class LogFile:
26     def __init__(self, job_id = 0, slave_id = 0, frames = []):
27         self.job_id = job_id
28         self.slave_id = slave_id
29         self.frames = frames
30
31     def serialize(self):
32         return  {
33                             "job_id": self.job_id,
34                             "slave_id": self.slave_id,
35                             "frames": self.frames
36                         }
37
38     @staticmethod
39     def materialize(data):
40         if not data:
41             return None
42
43         logfile = LogFile()
44         logfile.job_id = data["job_id"]
45         logfile.slave_id = data["slave_id"]
46         logfile.frames = data["frames"]
47
48         return logfile
49
50 class RenderSlave:
51     _slave_map = {}
52
53     def __init__(self):
54         self.id = ""
55         self.name = ""
56         self.address = ("",0)
57         self.stats = ""
58         self.total_done = 0
59         self.total_error = 0
60         self.last_seen = 0.0
61
62     def serialize(self):
63         return  {
64                             "id": self.id,
65                             "name": self.name,
66                             "address": self.address,
67                             "stats": self.stats,
68                             "total_done": self.total_done,
69                             "total_error": self.total_error,
70                             "last_seen": self.last_seen
71                         }
72
73     @staticmethod
74     def materialize(data, cache = True):
75         if not data:
76             return None
77
78         slave_id = data["id"]
79
80         if cache and slave_id in RenderSlave._slave_map:
81             return RenderSlave._slave_map[slave_id]
82
83         slave = RenderSlave()
84         slave.id = slave_id
85         slave.name = data["name"]
86         slave.address = data["address"]
87         slave.stats = data["stats"]
88         slave.total_done = data["total_done"]
89         slave.total_error = data["total_error"]
90         slave.last_seen = data["last_seen"]
91
92         if cache:
93             RenderSlave._slave_map[slave_id] = slave
94
95         return slave
96
97 JOB_BLENDER = 1
98 JOB_PROCESS = 2
99
100 JOB_TYPES = {
101                             JOB_BLENDER: "Blender",
102                             JOB_PROCESS: "Process"
103                         }
104
105 class RenderFile:
106     def __init__(self, filepath = "", index = 0, start = -1, end = -1, signature=0):
107         self.filepath = filepath
108         self.original_path = filepath
109         self.signature = signature
110         self.index = index
111         self.start = start
112         self.end = end
113
114     def serialize(self):
115         return  {
116                     "filepath": self.filepath,
117                     "original_path": self.original_path,
118                     "index": self.index,
119                     "start": self.start,
120                     "end": self.end,
121                     "signature": self.signature
122                 }
123
124     @staticmethod
125     def materialize(data):
126         if not data:
127             return None
128
129         rfile = RenderFile(data["filepath"], data["index"], data["start"], data["end"], data["signature"])
130         rfile.original_path = data["original_path"]
131
132         return rfile
133
134 class RenderJob:
135     def __init__(self, job_info = None):
136         self.id = ""
137         self.type = JOB_BLENDER
138         self.name = ""
139         self.category = "None"
140         self.status = JOB_WAITING
141         self.files = []
142         self.chunks = 0
143         self.priority = 0
144         self.blacklist = []
145
146         self.usage = 0.0
147         self.last_dispatched = 0.0
148         self.frames = []
149
150         if job_info:
151             self.type = job_info.type
152             self.name = job_info.name
153             self.category = job_info.category
154             self.status = job_info.status
155             self.files = job_info.files
156             self.chunks = job_info.chunks
157             self.priority = job_info.priority
158             self.blacklist = job_info.blacklist
159
160     def addFile(self, file_path, start=-1, end=-1):
161         signature = hashFile(file_path)
162         self.files.append(RenderFile(file_path, len(self.files), start, end, signature))
163
164     def addFrame(self, frame_number, command = ""):
165         frame = RenderFrame(frame_number, command)
166         self.frames.append(frame)
167         return frame
168
169     def __len__(self):
170         return len(self.frames)
171
172     def countFrames(self, status=QUEUED):
173         total = 0
174         for f in self.frames:
175             if f.status == status:
176                 total += 1
177
178         return total
179
180     def countSlaves(self):
181         return len(set((frame.slave for frame in self.frames if frame.status == DISPATCHED)))
182
183     def statusText(self):
184         return JOB_STATUS_TEXT[self.status]
185
186     def framesStatus(self):
187         results = {
188                                 QUEUED: 0,
189                                 DISPATCHED: 0,
190                                 DONE: 0,
191                                 ERROR: 0
192                             }
193
194         for frame in self.frames:
195             results[frame.status] += 1
196
197         return results
198
199     def __contains__(self, frame_number):
200         for f in self.frames:
201             if f.number == frame_number:
202                 return True
203         else:
204             return False
205
206     def __getitem__(self, frame_number):
207         for f in self.frames:
208             if f.number == frame_number:
209                 return f
210         else:
211             return None
212
213     def serialize(self, frames = None):
214         min_frame = min((f.number for f in frames)) if frames else -1
215         max_frame = max((f.number for f in frames)) if frames else -1
216         return  {
217                             "id": self.id,
218                             "type": self.type,
219                             "name": self.name,
220                             "category": self.category,
221                             "status": self.status,
222                             "files": [f.serialize() for f in self.files if f.start == -1 or not frames or (f.start <= max_frame and f.end >= min_frame)],
223                             "frames": [f.serialize() for f in self.frames if not frames or f in frames],
224                             "chunks": self.chunks,
225                             "priority": self.priority,
226                             "usage": self.usage,
227                             "blacklist": self.blacklist,
228                             "last_dispatched": self.last_dispatched
229                         }
230
231     @staticmethod
232     def materialize(data):
233         if not data:
234             return None
235
236         job = RenderJob()
237         job.id = data["id"]
238         job.type = data["type"]
239         job.name = data["name"]
240         job.category = data["category"]
241         job.status = data["status"]
242         job.files = [RenderFile.materialize(f) for f in data["files"]]
243         job.frames = [RenderFrame.materialize(f) for f in data["frames"]]
244         job.chunks = data["chunks"]
245         job.priority = data["priority"]
246         job.usage = data["usage"]
247         job.blacklist = data["blacklist"]
248         job.last_dispatched = data["last_dispatched"]
249
250         return job
251
252 class RenderFrame:
253     def __init__(self, number = 0, command = ""):
254         self.number = number
255         self.time = 0
256         self.status = QUEUED
257         self.slave = None
258         self.command = command
259
260     def statusText(self):
261         return FRAME_STATUS_TEXT[self.status]
262
263     def serialize(self):
264         return  {
265                             "number": self.number,
266                             "time": self.time,
267                             "status": self.status,
268                             "slave": None if not self.slave else self.slave.serialize(),
269                             "command": self.command
270                         }
271
272     @staticmethod
273     def materialize(data):
274         if not data:
275             return None
276
277         frame = RenderFrame()
278         frame.number = data["number"]
279         frame.time = data["time"]
280         frame.status = data["status"]
281         frame.slave = RenderSlave.materialize(data["slave"])
282         frame.command = data["command"]
283
284         return frame