Skip to content
Snippets Groups Projects
model.py 14.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • # ##### BEGIN GPL LICENSE BLOCK #####
    #
    #  This program is free software; you can redistribute it and/or
    #  modify it under the terms of the GNU General Public License
    #  as published by the Free Software Foundation; either version 2
    #  of the License, or (at your option) any later version.
    #
    #  This program is distributed in the hope that it will be useful,
    #  but WITHOUT ANY WARRANTY; without even the implied warranty of
    #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #  GNU General Public License for more details.
    #
    #  You should have received a copy of the GNU General Public License
    #  along with this program; if not, write to the Free Software Foundation,
    #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    #
    # ##### END GPL LICENSE BLOCK #####
    
    import netrender.versioning as versioning
    from netrender.utils import *
    
    
    Martin Poirier's avatar
    Martin Poirier committed
    import time
    
    # Jobs status
    JOB_WAITING = 0 # before all data has been entered
    JOB_PAUSED = 1 # paused by user
    JOB_FINISHED = 2 # finished rendering
    JOB_QUEUED = 3 # ready to be dispatched
    
    JOB_STATUS_TEXT = {
            JOB_WAITING: "Waiting",
            JOB_PAUSED: "Paused",
            JOB_FINISHED: "Finished",
            JOB_QUEUED: "Queued"
            }
    
    JOB_TRANSITION_STARTED = "Started"
    JOB_TRANSITION_PAUSED = "Paused"
    JOB_TRANSITION_RESUMED = "Resumed"
    JOB_TRANSITION_FINISHED = "Finished"
    JOB_TRANSITION_RESTARTED = "Restarted"
    
    JOB_TRANSITIONS = {
           (JOB_WAITING, JOB_QUEUED) : JOB_TRANSITION_STARTED,
           (JOB_QUEUED, JOB_PAUSED) : JOB_TRANSITION_PAUSED,
           (JOB_PAUSED, JOB_QUEUED) : JOB_TRANSITION_RESUMED,
           (JOB_QUEUED, JOB_FINISHED) : JOB_TRANSITION_FINISHED,
           (JOB_FINISHED, JOB_QUEUED) : JOB_TRANSITION_RESTARTED
           }
    
    # Job types (depends on the dependency type)
    JOB_BLENDER = 1
    JOB_PROCESS = 2
    JOB_VCS     = 3
    
    JOB_TYPES = {
                    JOB_BLENDER: "Blender",
                    JOB_PROCESS: "Process",
                    JOB_VCS:     "Versioned",
                }
    
    JOB_SUB_RENDER = 1
    JOB_SUB_BAKING = 2
    
    # Job subtypes
    JOB_SUBTYPES = {
                    JOB_SUB_RENDER: "Render",
                    JOB_SUB_BAKING: "Baking",
                }
    
    
    # Frames status
    FRAME_QUEUED = 0
    FRAME_DISPATCHED = 1
    FRAME_DONE = 2
    FRAME_ERROR = 3
    
    FRAME_STATUS_TEXT = {
            FRAME_QUEUED: "Queued",
            FRAME_DISPATCHED: "Dispatched",
            FRAME_DONE: "Done",
            FRAME_ERROR: "Error"
            }
    
    # Tags
    
    Martin Poirier's avatar
    Martin Poirier committed
    TAG_BAKING = "baking"
    TAG_RENDER = "render"
    
    TAG_ALL = set((TAG_BAKING, TAG_RENDER))
    
    
    class LogFile:
        def __init__(self, job_id = 0, slave_id = 0, frames = []):
            self.job_id = job_id
            self.slave_id = slave_id
            self.frames = frames
    
        def serialize(self):
            return 	{
                                "job_id": self.job_id,
                                "slave_id": self.slave_id,
                                "frames": self.frames
                            }
    
        @staticmethod
        def materialize(data):
            if not data:
                return None
    
            logfile = LogFile()
            logfile.job_id = data["job_id"]
            logfile.slave_id = data["slave_id"]
            logfile.frames = data["frames"]
    
            return logfile
    
    class RenderSlave:
        _slave_map = {}
    
    
    Martin Poirier's avatar
    Martin Poirier committed
        def __init__(self, info = None):
    
            self.id = ""
            self.total_done = 0
            self.total_error = 0
            self.last_seen = 0.0
    
    Martin Poirier's avatar
    Martin Poirier committed
            
            if info:
                self.name = info.name
                self.address = info.address
                self.stats = info.stats
                self.tags = info.tags
            else:
                self.name = ""
                self.address = ("",0)
                self.stats = ""
                self.tags = set()
    
    
        def serialize(self):
            return 	{
                                "id": self.id,
                                "name": self.name,
                                "address": self.address,
                                "stats": self.stats,
                                "total_done": self.total_done,
                                "total_error": self.total_error,
    
    Martin Poirier's avatar
    Martin Poirier committed
                                "last_seen": self.last_seen,
                                "tags": tuple(self.tags)
    
                            }
    
        @staticmethod
        def materialize(data, cache = True):
            if not data:
                return None
    
            slave_id = data["id"]
    
            if cache and slave_id in RenderSlave._slave_map:
                return RenderSlave._slave_map[slave_id]
    
            slave = RenderSlave()
            slave.id = slave_id
            slave.name = data["name"]
            slave.address = data["address"]
            slave.stats = data["stats"]
            slave.total_done = data["total_done"]
            slave.total_error = data["total_error"]
            slave.last_seen = data["last_seen"]
    
    Martin Poirier's avatar
    Martin Poirier committed
            slave.tags = set(data["tags"])
    
    
            if cache:
                RenderSlave._slave_map[slave_id] = slave
    
            return slave
    
    class VersioningInfo:
        def __init__(self, info = None):
            self._system = None
            self.wpath = ""
            self.rpath = ""
            self.revision = ""
            
        @property
        def system(self):
            return self._system
    
        @system.setter
        def system(self, value):
            self._system = versioning.SYSTEMS[value]
    
        def update(self):
            self.system.update(self)
        
        def serialize(self):
            return {
                    "wpath": self.wpath,
                    "rpath": self.rpath,
                    "revision": self.revision,
                    "system": self.system.name
                    }
            
        @staticmethod
        def generate(system, path):
            vs = VersioningInfo()
            vs.wpath = path
            vs.system = system
    
            vs.rpath = vs.system.path(path)
            vs.revision = vs.system.revision(path)
            
            return vs
            
            
        @staticmethod
        def materialize(data):
            if not data:
                return None
            
            vs = VersioningInfo()
            vs.wpath = data["wpath"]
            vs.rpath = data["rpath"]
            vs.revision = data["revision"]
            vs.system = data["system"]
            
            return vs
            
    
    class RenderFile:
    
    Martin Poirier's avatar
    Martin Poirier committed
        def __init__(self, filepath = "", index = 0, start = -1, end = -1, signature = 0):
    
            self.filepath = filepath
            self.original_path = filepath
            self.signature = signature
            self.index = index
            self.start = start
            self.end = end
    
    Martin Poirier's avatar
    Martin Poirier committed
            self.force = False
    
    Martin Poirier's avatar
    Martin Poirier committed
            
    
    
        def serialize(self):
            return 	{
                        "filepath": self.filepath,
                        "original_path": self.original_path,
                        "index": self.index,
                        "start": self.start,
                        "end": self.end,
    
    Martin Poirier's avatar
    Martin Poirier committed
                        "signature": self.signature,
                        "force": self.force
    
    Martin Poirier's avatar
    Martin Poirier committed
                        
    
                    }
    
        @staticmethod
        def materialize(data):
            if not data:
                return None
    
            rfile = RenderFile(data["filepath"], data["index"], data["start"], data["end"], data["signature"])
            rfile.original_path = data["original_path"]
    
    Martin Poirier's avatar
    Martin Poirier committed
            rfile.force = data["force"]
    
    
            return rfile
    
    class RenderJob:
    
    Martin Poirier's avatar
    Martin Poirier committed
        def __init__(self, info = None):
    
            self.id = ""
            
            self.resolution = None
    
            self.usage = 0.0
            self.last_dispatched = 0.0
            self.frames = []
    
    Martin Poirier's avatar
    Martin Poirier committed
            self.transitions = []
            
            self._status = None
            
    
    Martin Poirier's avatar
    Martin Poirier committed
            if info:
                self.type = info.type
                self.subtype = info.subtype
                self.name = info.name
                self.category = info.category
                self.tags = info.tags
                self.status = info.status
                self.files = info.files
                self.chunks = info.chunks
                self.priority = info.priority
                self.blacklist = info.blacklist
                self.version_info = info.version_info
                self.render = info.render
            else:
                self.type = JOB_BLENDER
                self.subtype = JOB_SUB_RENDER
                self.name = ""
                self.category = "None"
                self.tags = set()
                self.status = JOB_WAITING
                self.files = []
                self.chunks = 0
                self.priority = 0
                self.blacklist = []
                self.version_info = None
                self.render = "BLENDER_RENDER"
    
    Martin Poirier's avatar
    Martin Poirier committed
        @property
        def status(self):
            """Status of the job (waiting, paused, finished or queued)"""
            return self._status
        
        @status.setter
        def status(self, value):
            transition = JOB_TRANSITIONS.get((self.status, value), None)
            if transition:
                self.transitions.append((transition, time.time()))
                
            self._status = value
    
        @property
        def time_started(self):
            started_time = None
            for transition, time_value in self.transitions:
                if transition == JOB_TRANSITION_STARTED:
                    started_time = time_value
                    break
                
            return started_time
    
        @property
        def time_finished(self):
            finished_time = None
            if self.status == JOB_FINISHED:
                for transition, time_value in self.transitions:
                    if transition == JOB_TRANSITION_FINISHED:
                        finished_time = time_value
                
            return finished_time
    
    
        def hasRenderResult(self):
    
    Martin Poirier's avatar
    Martin Poirier committed
            return self.subtype == JOB_SUB_RENDER
    
    
        def rendersWithBlender(self):
    
    Martin Poirier's avatar
    Martin Poirier committed
            return self.subtype == JOB_SUB_RENDER
    
    
        def addFile(self, file_path, start=-1, end=-1, signed=True):
    
    Martin Poirier's avatar
    Martin Poirier committed
            def isFileInFrames():
                if start == end == -1:
                    return True
                
                for rframe in self.frames:
                    if start <= rframe.number<= end:
                        return True
                
                return False
                
                
            if isFileInFrames(): 
                if signed:
                    signature = hashFile(file_path)
                else:
                    signature = None
                self.files.append(RenderFile(file_path, len(self.files), start, end, signature))
    
    
        def addFrame(self, frame_number, command = ""):
            frame = RenderFrame(frame_number, command)
            self.frames.append(frame)
            return frame
    
        def __len__(self):
            return len(self.frames)
    
    
    Martin Poirier's avatar
    Martin Poirier committed
        def countFrames(self, status=FRAME_QUEUED):
    
            total = 0
            for f in self.frames:
                if f.status == status:
                    total += 1
    
            return total
    
        def countSlaves(self):
    
    Martin Poirier's avatar
    Martin Poirier committed
            return len(set((frame.slave for frame in self.frames if frame.status == FRAME_DISPATCHED)))
    
    
        def statusText(self):
            return JOB_STATUS_TEXT[self.status]
    
        def framesStatus(self):
            results = {
    
    Martin Poirier's avatar
    Martin Poirier committed
                                    FRAME_QUEUED: 0,
                                    FRAME_DISPATCHED: 0,
                                    FRAME_DONE: 0,
                                    FRAME_ERROR: 0
    
                                }
    
            for frame in self.frames:
                results[frame.status] += 1
    
            return results
    
        def __contains__(self, frame_number):
            for f in self.frames:
                if f.number == frame_number:
                    return True
            else:
                return False
    
        def __getitem__(self, frame_number):
            for f in self.frames:
                if f.number == frame_number:
                    return f
            else:
                return None
    
    
    Martin Poirier's avatar
    Martin Poirier committed
        def serialize(self, frames = None,withFiles=True,withFrames=True):
    
            min_frame = min((f.number for f in frames)) if frames else -1
            max_frame = max((f.number for f in frames)) if frames else -1
    
    Martin Poirier's avatar
    Martin Poirier committed
            data={
    
                                "id": self.id,
                                "type": self.type,
    
    Martin Poirier's avatar
    Martin Poirier committed
                                "subtype": self.subtype,
    
                                "name": self.name,
                                "category": self.category,
    
    Martin Poirier's avatar
    Martin Poirier committed
                                "tags": tuple(self.tags),
    
                                "status": self.status,
    
    Martin Poirier's avatar
    Martin Poirier committed
                                "transitions": self.transitions,
    
                                "chunks": self.chunks,
                                "priority": self.priority,
                                "usage": self.usage,
                                "blacklist": self.blacklist,
                                "last_dispatched": self.last_dispatched,
                                "version_info": self.version_info.serialize() if self.version_info else None,
    
                                "resolution": self.resolution,
                                "render": self.render
    
    Martin Poirier's avatar
    Martin Poirier committed
            if (withFiles):
               data["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)]
              
            if (withFrames):
               data["frames"]=[f.serialize() for f in self.frames if not frames or f in frames]
               
            return data
    
        @staticmethod
        def materialize(data):
            if not data:
                return None
    
            job = RenderJob()
            job.id = data["id"]
            job.type = data["type"]
    
    Martin Poirier's avatar
    Martin Poirier committed
            job.subtype = data["subtype"]
    
            job.name = data["name"]
            job.category = data["category"]
    
    Martin Poirier's avatar
    Martin Poirier committed
            job.tags = set(data["tags"])
    
            job.status = data["status"]
    
    Martin Poirier's avatar
    Martin Poirier committed
            job.transitions = data["transitions"]
    
            job.files = [RenderFile.materialize(f) for f in data["files"]]
            job.frames = [RenderFrame.materialize(f) for f in data["frames"]]
            job.chunks = data["chunks"]
            job.priority = data["priority"]
            job.usage = data["usage"]
            job.blacklist = data["blacklist"]
            job.last_dispatched = data["last_dispatched"]
            job.resolution = data["resolution"]
    
            
            version_info = data.get("version_info", None)
            if version_info:
                job.version_info = VersioningInfo.materialize(version_info)
    
            return job
    
    class RenderFrame:
        def __init__(self, number = 0, command = ""):
            self.number = number
            self.time = 0
    
    Martin Poirier's avatar
    Martin Poirier committed
            self.status = FRAME_QUEUED
    
            self.slave = None
            self.command = command
    
    Martin Poirier's avatar
    Martin Poirier committed
            self.results = []   # List of filename of result files associated with this frame
    
    
        def statusText(self):
            return FRAME_STATUS_TEXT[self.status]
    
        def serialize(self):
            return 	{
                                "number": self.number,
                                "time": self.time,
                                "status": self.status,
                                "slave": None if not self.slave else self.slave.serialize(),
    
    Martin Poirier's avatar
    Martin Poirier committed
                                "command": self.command,
                                "results": self.results
    
                            }
    
        @staticmethod
        def materialize(data):
            if not data:
                return None
    
            frame = RenderFrame()
            frame.number = data["number"]
            frame.time = data["time"]
            frame.status = data["status"]
            frame.slave = RenderSlave.materialize(data["slave"])
            frame.command = data["command"]
    
    Martin Poirier's avatar
    Martin Poirier committed
            frame.results = data["results"]
    
    
            return frame