Files
Zordon/lib/scheduled_job.py
2023-05-24 09:49:25 -05:00

152 lines
5.1 KiB
Python

import glob
import hashlib
import json
import logging
import os
import threading
import uuid
from datetime import datetime
from sqlalchemy import Column, Integer, String, DateTime, JSON
from sqlalchemy.ext.declarative import declarative_base
from .render_workers.render_worker import RenderStatus, RenderWorkerFactory
logger = logging.getLogger()
Base = declarative_base()
class ScheduledJob(Base):
__tablename__ = 'scheduled_jobs'
id = Column(String, primary_key=True)
# Get file hash on bg thread
def __get_file_hash(self):
if os.path.exists(self.worker.input_path):
self.file_hash = hashlib.md5(open(self.worker.input_path, 'rb').read()).hexdigest()
def __init__(self, renderer, input_path, output_path, args, priority=2, owner=None, client=None, notify=None,
custom_id=None, name=None):
self.id = custom_id or self.generate_id()
self.renderer = renderer
self.input_path = input_path
self.output_path = output_path
self.priority = priority
self.owner = owner
self.client = client
self.notify = notify
self.date_created = datetime.now()
self.scheduled_start = None
self.name = name or os.path.basename(input_path) + '_' + self.date_created.strftime("%Y.%m.%d_%H.%M.%S")
self.worker_object = RenderWorkerFactory.create_worker(renderer, input_path, output_path, args)
self.worker_object.log_path = self.log_path()
self.worker_object.validate()
self.file_hash = None
threading.Thread(target=self.__get_file_hash).start() # get file hash on bg thread
def render_status(self):
if self.scheduled_start and self.worker.status == RenderStatus.NOT_STARTED:
return RenderStatus.SCHEDULED
else:
return self.worker.status
def file_hash(self):
if os.path.exists(self.worker.input_path):
return hashlib.md5(open(self.worker.input_path, 'rb').read()).hexdigest()
return None
def json(self):
"""Converts RenderJob into JSON-friendly dict"""
job_dict = None
try:
job_dict = {
'id': self.id,
'name': self.name,
'input_path': self.input_path,
'output_path': self.output_path,
'priority': self.priority,
'owner': self.owner,
'client': self.client,
'notify': self.notify,
'date_created': self.date_created,
'scheduled_start': self.scheduled_start,
'status': self.render_status().value,
'time_elapsed': self.worker().time_elapsed() if hasattr(self.worker(), 'time_elapsed') else None,
'file_hash': self.file_hash,
'percent_complete': self.percent_complete(),
'file_list': self.file_list(),
'renderer': self.renderer,
'worker': self.worker().json() if hasattr(self.worker(), 'json') else {},
}
# convert to json and back to auto-convert dates to iso format
def date_serializer(o):
if isinstance(o, datetime):
return o.isoformat()
json_convert = json.dumps(job_dict, default=date_serializer)
job_dict = json.loads(json_convert)
except Exception as e:
logger.exception(e)
logger.error("Error converting to JSON: {}".format(e))
return job_dict
def start(self):
self.worker.start()
def stop(self):
self.worker.stop()
def time_elapsed(self):
from string import Template
class DeltaTemplate(Template):
delimiter = "%"
def strfdelta(tdelta, fmt='%H:%M:%S'):
d = {"D": tdelta.days}
hours, rem = divmod(tdelta.seconds, 3600)
minutes, seconds = divmod(rem, 60)
d["H"] = '{:02d}'.format(hours)
d["M"] = '{:02d}'.format(minutes)
d["S"] = '{:02d}'.format(seconds)
t = DeltaTemplate(fmt)
return t.substitute(**d)
# calculate elapsed time
elapsed_time = None
start_time = self.worker.start_time
end_time = self.worker.end_time
if start_time:
if end_time:
elapsed_time = end_time - start_time
elif self.render_status() == RenderStatus.RUNNING:
elapsed_time = datetime.now() - start_time
elapsed_time_string = strfdelta(elapsed_time) if elapsed_time else "Unknown"
return elapsed_time_string
def frame_count(self):
return self.worker.total_frames
def work_path(self):
return os.path.dirname(self.output_path)
def file_list(self):
job_dir = os.path.dirname(self.output_path)
return glob.glob(os.path.join(job_dir, '*'))
def log_path(self):
return os.path.join(os.path.dirname(self.input_path), self.name + '.log')
def percent_complete(self):
return self.worker.percent_complete()
@classmethod
def generate_id(cls):
return str(uuid.uuid4()).split('-')[0]