def validate(self): if not self._message: raise ActionError("Message can not be empty.") try: self._sender = User.current() except UserError: raise ActionError("Could not identify current user.") # get ooto notification recipients from the configs ptask_area = PTaskArea.current() ooto_config = ptask_area.config( OOTO_CONFIG_PATH, composite_ancestors=True, composite_method="append", ) ooto_notify = ooto_config.get('notify', []) self._to.extend(ooto_notify) # for all usernames specified, make sure they're a valid user, # get their email addresses. recipients = set() for recipient in self._to: # assume already a valid email address if "@" in recipient: recipients.add(recipient) else: try: recipient = User.get(recipient) except UserError: raise ActionError( "Could not identify user: "******"\n\nCurrent ptask: {p}".format(p=ptask_area.spec) self._message += "\n\n- {s}".format(s=self._sender.full_name) self._subject = "OOTO: {fn} ({un})".format( fn=self.sender.full_name, un=self.sender.username, )
def notify(self): # XXX db intensive. revisit at some point # for now, only alert on publish/official/deprecate if not self.publish and not self.official and not self.deprecate: return ptasks_to_notify = [] msg = "A product you may be using has been updated:\n\n" msg += "PRODUCT: " + self.product.spec + "\n\n" if self.official: product = self.official.product ptasks_to_notify.extend(product.dependent_ptasks) msg += "NOW OFFICIAL: " + self.official.number_padded + " - " + \ self.official.release_note + "\n" if self.publish: for ver in self.publish: product = ver.product ptasks_to_notify.extend(product.dependent_ptasks) msg += "NOW PUBLISHED: " + ver.number_padded + " - " + \ ver.release_note + "\n" if self.deprecate: for ver in self.deprecate: product = ver.product ptasks_to_notify.extend(product.dependent_ptasks) msg += "NOW DEPRECATED: " + ver.number_padded + " - " + \ ver.release_note + "\n" msg += "\nYou should update your subscriptions accordingly." subject = "Product Update: " + self.product.spec sender = User.current().email # TODO: the recipients should be creators of versions subscribed recipients = set([p.creator.email for p in ptasks_to_notify]) # no need to send if there are no ptask creators to notify. if recipients: notification = Notification(subject, msg, list(recipients), sender=sender) notification.send_email()
def _product_render_arnold(self): # get timestamp for all the tasks being submitted now = datetime.datetime.now() render_layers = self._get_render_layers() # figure out the total number of operations to perform for the progress num_ops = 1 + len(render_layers) * len(self._frame_list) # layer > frame num_ops += len(self._frame_list) # frame submission if self._remove_scenes: num_ops += 1 if self._generate_scenes: num_ops += 1 cur_op = 0 progress_dialog = QtGui.QProgressDialog( "Product render...", "", cur_op, num_ops, self) progress_dialog.setWindowTitle("Dark Knight is busy...") progress_dialog.setAutoReset(False) progress_dialog.setLabelText("Preparing maya file for rendering...") progress_dialog.show() ptask = self._cur_ptask ptask_version = self._cur_ptask.version(self._version) ptask_dir = self._cur_ptask.area.dir() ver_dir = ptask.area.dir(version=self._version) # need to get the maya file in the version directory maya_file = self.session.cmds.file(q=True, sceneName=True) maya_file = maya_file.replace(ptask_dir, ver_dir) file_base = os.path.splitext(os.path.split(maya_file)[1])[0] self.session.cmds.setAttr('defaultResolution.width', self._resolution.width) self.session.cmds.setAttr('defaultResolution.height', self._resolution.height) # set the output file naming convention to name.#.ext self.session.cmds.setAttr("defaultRenderGlobals.animation", True) self.session.cmds.setAttr("defaultRenderGlobals.putFrameBeforeExt", True) self.session.cmds.setAttr("defaultRenderGlobals.outFormatControl", False) # set all other cameras to not be renderable (this seems to work) cam_shape_list = self.session.cmds.ls(cameras=True) for cam_shape in cam_shape_list: cam_name = str( self.session.cmds.listRelatives(cam_shape, parent=True)[0]) if cam_name == self._camera: self.session.cmds.setAttr(cam_shape + ".renderable", 1) else: self.session.cmds.setAttr(cam_shape + ".renderable", 0) # ---- sync current work area to version snapshot to render from cur_project = self.session.cmds.workspace(query=True, rootDirectory=True) ver_project = cur_project.replace(ptask_dir, ver_dir) progress_dialog.setLabelText("Sync'ing work to current version...") try: self.session.save() self._sync_latest() except Exception as e: self._show_error("Unable to save & sync the latest work: " + str(e)) self.setEnabled(True) progress_dialog.close() return cur_op += 1 progress_dialog.setValue(cur_op) create_action_cls = ActionRegistry().get_action('create', 'product') if not create_action_cls: progress_dialog.close() raise DarkKnightError("Unable to find product creation action.") # ---- clean up ASSs scene_dir = os.path.join(ver_project, 'arnold', file_base, 'ass') if self._remove_scenes: progress_dialog.setLabelText("Removing ass files...") if os.path.isdir(scene_dir): try: shutil.rmtree(scene_dir) except Exception as e: progress_dialog.close() raise DarkKnightError("Unable to clean up ass files: " + str(e)) cur_op += 1 progress_dialog.setValue(cur_op) # ---- get a list of warnings to ignore #prman_config = ptask.area.config(PRMAN_CONFIG_PATH, # composite_ancestors=True, composite_method="override") #prman_warnings = " ".join( # ["-woff " + w for w in prman_config.get('woff', [])]) # ---- construct scripts for the queue render_summary = [] for render_layer in render_layers: progress_dialog.setLabelText( "Creating product for layer: {rl}...".format(rl=render_layer)) # ensure product exists for each render layer create_action = create_action_cls( product=render_layer, ptask=ptask_version.ptask_spec, version=ptask_version.number, category='imgseq', description=render_layer + " render layer", file_type=self._file_type, resolution=self._res_str, note=self._version_note, ) try: create_action() except ActionError as e: progress_dialog.close() raise DarkKnightError("Unable to create product: " + str(e)) product_repr = create_action.product_repr product_repr_area = product_repr.area progress_dialog.setLabelText( "Provisioning 'queue' directory in product...") # make sure queue directory exists try: product_repr_area.provision('queue') except Exception as e: progress_dialog.close() raise DarkKnightError( "Unable to create queue scripts directory: " + str(e)) queue_dir = product_repr_area.dir(dir_name='queue') tasks_info_file = os.path.join(queue_dir, 'tasks_info.cfg') tasks_info_config = Config() # dpaset command to run dpaset_cmd = 'eval "`dpa env ptask {pt}@{vn}`"'.format( pt=ptask.spec, vn=ptask_version.number) # set group permissions on project dir, recursively os.system("chmod g+rw {pd} -R".format(pd=ver_project)) # figure out the render layer if render_layer == 'masterLayer': layer_index = self.session.cmds.getAttr("defaultRenderLayer.rlid") else: layer_index = self.session.cmds.getAttr(render_layer + ".rlid") frame_scripts = [] for frame in self._frame_list: frame_padded = str(frame).zfill(4) progress_dialog.setLabelText( "Building render shell script for {rl} frame {f}".format( rl=render_layer, f=frame_padded)) script_path = os.path.join(queue_dir, "{rl}.{fn}.sh".format(rl=render_layer, fn=frame_padded)) out_dir = product_repr_area.dir() out_file = os.path.join(out_dir, "{rl}.{fn}.{ft}".\ format(rl=render_layer, fn=frame_padded, ft=self._file_type)) simple_scene = "{proj}arnold/{fb}/ass/{fb}.{fn}.ass".format( proj=ver_project, fb=file_base, fn=frame_padded) layer_scene = "{proj}arnold/{fb}/ass/{fb}_{rl}.{fn}.ass".\ format(proj=ver_project, fb=file_base, fn=frame_padded, rl=render_layer) render_cmd = "/opt/solidangle/arnold-maya2016/bin/kick -dw -v 6 -i $ASS_PATH " render_cmd += "-l /opt/solidangle/arnold-maya2016/shaders " render_cmd += "-l /opt/solidangle/arnold-maya2016/procedurals " render_cmd += "-o {od} ".format(od=out_file) #render_cmd += "-f {rl} ".format(rl=render_layer) #render_cmd += "-p {proj} ".format(proj=ver_project) #render_cmd += "--prman '-t:0 -cwd \"{proj}\" {warn}' ".\dd # format(proj=ver_project, warn=prman_warnings) with open(script_path, "w") as script_file: script_file.write("#!/bin/bash\n\n") # XXX these should happen automatically in the queue... script_file.write("source /DPA/wookie/dpa/bash/startup.bash\n") script_file.write("pipeup\n") # 'kick' command has to be added to $PATH # Create env variable for Arnold License server script_file.write("export [email protected]\n\n") script_file.write("# set the ptask version to render\n") script_file.write(dpaset_cmd + "\n") script_file.write("cd " + ver_project + "\n\n") # Add necessary paths to the environment for XGen script_file.write("export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/autodesk/maya2016/plug-ins/xgen/lib:/usr/autodesk/maya2016/lib:/opt/solidangle/arnold-maya2016/bin\n\n") # the logic for determining which ass will be generated is # unclear at this point. So we'll build a conditional script_file.write("if [[ -f {lr} ]]; then\n".format(lr=layer_scene)) script_file.write(" export ASS_PATH={lr}\n".format(lr=layer_scene)) script_file.write("else\n") script_file.write(" export ASS_PATH={sr}\n".format(sr=simple_scene)) script_file.write("fi\n") script_file.write("# render!\n") script_file.write(render_cmd + "\n\n") script_file.write("chmod 660 {of}\n\n".format( of=os.path.join(out_dir, render_layer + "*." + self._file_type))) os.chmod(script_path, 0770) frame_scripts.append((frame_padded, script_path, out_file)) cur_op += 1 progress_dialog.setValue(cur_op) frame_tasks = [] task_id_base = get_unique_id(product_repr_area.spec, dt=now) tasks_info_config.add('base_id', task_id_base) if self._generate_scenes: frame_queue = 'hold' else: frame_queue = self._render_queue # create frame tasks for (frame, frame_script, out_file) in frame_scripts: progress_dialog.setLabelText( "Submitting frame: " + frame_script) task_id = task_id_base + "_" + frame if not self._debug_mode: # create tasks, don't actually submit yet create_queue_task(frame_queue, frame_script, task_id, output_file=out_file, submit=False, log_path=frame_script + '.log') frame_tasks.append((frame, task_id)) # # resubmit frame-by-frame because # group submit seems to be occasionally # having problems. os.system("cqresubmittask {qn} {tid}".format( qn=frame_queue, tid=task_id)) cur_op += 1 progress_dialog.setValue(cur_op) frame_info = Config() for (frame, task_id) in frame_tasks: frame_info.add(str(frame), task_id) tasks_info_config.add('frame_ids', frame_info) # resubmit all at once (workaround for slow individual submissions) # # This approach seems to be having problems with the server # communications. Switch to frame-by-frame resubmit because # that has worked where this fails #os.system("cqresubmittask {qn} {tid}".format( # qn=frame_queue, tid=task_id_base)) if self._generate_scenes: progress_dialog.setLabelText("Creating ass generation script...") script_path = os.path.join(queue_dir, "{rl}_assgen.sh".format(rl=render_layer)) with open(script_path, "w") as script_file: script_file.write("#!/bin/bash\n\n") # XXX these should happen automatically in the queue... script_file.write("source /DPA/wookie/dpa/bash/startup.bash\n") script_file.write("pipeup\n\n") script_file.write("# set the ptask version to render\n") script_file.write(dpaset_cmd + "\n") script_file.write("cd " + ver_project + "\n\n") script_file.write("# generate the ass files...\n") current_render_layer = render_layer if render_layer == 'masterLayer': current_render_layer = "defaultRenderLayer" switch_render_layer_cmd = "editRenderLayerGlobals " switch_render_layer_cmd += "-currentRenderLayer \"{rl}\"".\ format(rl=current_render_layer) arnold_export_cmd = "arnoldExportAss -f \"{ad}/{fb}_{rl}.ass\" ".\ format(ad=scene_dir, fb=file_base, rl=render_layer) arnold_export_cmd += "-startFrame {sf} -endFrame {ef} -frameStep 1 ".\ format(li=layer_index, sf=self._frange.start, ef=self._frange.end) arnold_export_cmd += "-mask 255 -lightLinks 1 -shadowLinks 1 -cam {cam} ".\ format(cam=self._camera) arnold_export_cmd += "-expandProcedurals " maya_batch_cmd = 'maya2016 -batch -proj "{proj}" '.format( proj=ver_project) maya_batch_cmd += '-command \'{srlc}; {ar}\' '.\ format(srlc=switch_render_layer_cmd, ar=arnold_export_cmd) maya_batch_cmd += '-file "{mf}"'.format(mf=maya_file) script_file.write(maya_batch_cmd + "\n") script_file.write( "\n# make sure project dir has group permissions\n") script_file.write( "chmod g+rw {pd} -R\n\n".format(pd=ver_project)) # submit the frames to render script_file.write("# Submit frames after ass gen \n") for (frame, frame_task) in frame_tasks: script_file.write("cqmovetask {qn} {tid}\n".format( qn=self._render_queue, tid=frame_task)) # changed to move group #script_file.write("cqmovetask {qn} {tid}\n".format( #qn=self._render_queue, tid=task_id_base)) os.chmod(script_path, 0770) # submit the scenegen script progress_dialog.setLabelText( "Submitting ass gen: " + script_path) task_id = task_id_base + "_asses" tasks_info_config.add('assgen_id', task_id) if not self._debug_mode: create_queue_task(self._scenegen_queue, script_path, task_id, output_file=scene_dir, submit=True, log_path=script_path + '.log') cur_op += 1 progress_dialog.setValue(cur_op) cur_op += 1 progress_dialog.setValue(cur_op) progress_dialog.close() render_summary.append( (render_layer, task_id_base, product_repr, queue_dir)) # For now, disable wrangling tickets. bsddb is causing problems # - zshore, 2015-10-23 # if not self._debug_mode: # # ---- dpa specific queue stuff # from cheesyq import DPAWrangler # # create wrangling ticket # wrangle = DPAWrangler.WrangleRecord(task_id_base) # wrangle.frames = self._frame_list # db = DPAWrangler.GetWranglingDB() # db.set(wrangle.baseId, wrangle) # DPAWrangler.AssignWranglerTask("none", task_id_base) wranglecmd = 'cqcreatewrangleitem ' + task_id_base + ' ' for f in self._frame_list: wranglecmd = wranglecmd + str(f) + ' ' print wranglecmd os.system(wranglecmd) tasks_info_config.write(tasks_info_file) os.chmod(tasks_info_file, 0660) if not self._debug_mode: # send msg... msg_title = "Queue submission report: " + \ now.strftime("%Y/%m/%d %H:%M:%S") msg_body = "Submitted the following tasks for " + \ ptask.spec + ":\n\n" msg_body += " Description: " + self._version_note + "\n" msg_body += " Resolution: " + self._res_str + "\n" msg_body += " File type: " + self._file_type + "\n" msg_body += " Camera: " + self._camera + "\n" if self._generate_scenes: msg_body += " Ass gen queue: " + self._scenegen_queue + "\n" msg_body += " Render queue: " + self._render_queue + "\n" msg_body += " Frames: " + str(self._frange) + "\n" msg_body += " Ass directory: " + scene_dir + "\n" msg_body += "\n" for (layer, task_id_base, product_repr, queue_dir) in render_summary: msg_body += " Render layer: " + layer + "\n" msg_body += " Base task ID: " + task_id_base + "\n" msg_body += " Product representation: " + \ product_repr.spec + "\n" msg_body += " Scripts directory: " + queue_dir + "\n" msg_body += "\n" dk_config = ptask.area.config(DK_CONFIG_PATH, composite_ancestors=True, composite_method="append") recipients = dk_config.get('notify', []) recipients.append(current_username()) recipients = emails_from_unames(recipients) notification = Notification(msg_title, msg_body, recipients, sender=User.current().email) notification.send_email()
def _render_to_product(self): # add the version note for the product render_node = self.session.nuke.toNode(self._node_to_render) render_node['product_ver_note'].setValue(self._version_note) # ---- progress dialog num_ops = 6 cur_op = 0 progress_dialog = QtGui.QProgressDialog( "Product render...", "", cur_op, num_ops, self) progress_dialog.setWindowTitle("Dark Knight is busy...") progress_dialog.setAutoReset(False) progress_dialog.setLabelText("Preparing nuke file for rendering...") progress_dialog.show() # ensure the product has been created progress_dialog.setLabelText("Creating product...") product_repr = create_product_before_render(node=render_node) product_repr_area = product_repr.area cur_op += 1 progress_dialog.setValue(cur_op) # get timestamp for all the tasks being submitted now = datetime.datetime.now() ptask_area = PTaskArea.current() ptask = PTask.get(ptask_area.spec) if ptask_area.version: ptask_version = ptask.version(ptask_area.version) else: ptask_version = ptask.latest_version ptask_dir = ptask_area.dir() ver_dir = ptask_area.dir(version=ptask_version.number) nuke_file = self.session.nuke.root().name() nuke_file = nuke_file.replace(ptask_dir, ver_dir) file_base = os.path.splitext(os.path.split(nuke_file)[1])[0] # ---- sync current work area to version snapshot to render from progress_dialog.setLabelText("Sync'ing the latest work...") try: self.session.save() self._sync_latest() except Exception as e: self._show_error("Unable to save & sync the latest work: " + str(e)) self.setEnabled(True) progress_dialog.close() return cur_op += 1 progress_dialog.setValue(cur_op) # make sure queue directory exists progress_dialog.setLabelText("Provisioning the queue directory...") try: product_repr_area.provision('queue') except Exception as e: raise DarkKnightError( "Unable to create queue scripts directory: " + str(e)) cur_op += 1 progress_dialog.setValue(cur_op) queue_dir = product_repr_area.dir(dir_name='queue') tasks_info_file = os.path.join(queue_dir, 'tasks_info.cfg') tasks_info_config = Config() progress_dialog.setLabelText("Building the queue script...") # dpaset command to run dpaset_cmd = 'eval "`dpa env ptask {pt}@{vn}`"'.format( pt=ptask.spec, vn=ptask_version.number) frange_str = str(self._frange).replace("-", "_").replace(":", "_") script_path = os.path.join(queue_dir, "{pn}.{fr}.sh".format(pn=render_node['product_name'].value(), fr=frange_str)) render_cmd = "nuke --cont -f -F {fs}-{fe}x{step} -X {rn} -V 2 -x {nf}".\ format( fs=self._frange.start, fe=self._frange.end, step=self._frange.step, rn=self._node_to_render, nf=nuke_file, ) with open(script_path, "w") as script_file: script_file.write("#!/bin/bash\n\n") # XXX these should happen automatically in the queue... script_file.write("source /DPA/wookie/dpa/bash/startup.bash\n") script_file.write("pipeup\n\n") script_file.write("# set the ptask version to render\n") script_file.write(dpaset_cmd + "\n\n") script_file.write("# render!\n") script_file.write(render_cmd + "\n\n") os.chmod(script_path, 0770) cur_op += 1 progress_dialog.setValue(cur_op) task_id = get_unique_id(product_repr_area.spec, dt=now) task_id += "_" + frange_str tasks_info_config.add('task_id', task_id) out_file = self.session.nuke.filename(render_node, self.session.nuke.REPLACE) if not self._debug_mode: progress_dialog.setLabelText("Submitting to the queue...") create_queue_task(self._render_queue, script_path, task_id, output_file=out_file, submit=True, log_path=script_path + '.log') tasks_info_config.write(tasks_info_file) os.chmod(tasks_info_file, 0660) cur_op += 1 progress_dialog.setValue(cur_op) if not self._debug_mode: progress_dialog.setLabelText("Sending submission report...") # send msg... msg_title = "Queue submission report: " + \ now.strftime("%Y/%m/%d %H:%M:%S") msg_body = "Submitted the following task for " + \ ptask.spec + ":\n\n" msg_body += " Product representation: " + product_repr.spec + "\n" msg_body += " Description: " + self._version_note + "\n" msg_body += " Render queue: " + self._render_queue + "\n" msg_body += " Frames: " + str(self._frange) + "\n" msg_body += " Task ID: " + task_id + "\n" msg_body += " Scripts directory: " + queue_dir + "\n" msg_body += "\n" dk_config = ptask.area.config(DK_CONFIG_PATH, composite_ancestors=True, composite_method="append") recipients = dk_config.get('notify', []) recipients.append(current_username()) recipients = emails_from_unames(recipients) notification = Notification(msg_title, msg_body, recipients, sender=User.current().email) notification.send_email() cur_op += 1 progress_dialog.setValue(cur_op) progress_dialog.close()
def _product_render_arnold(self): # get timestamp for all the tasks being submitted now = datetime.datetime.now() render_layers = self._get_render_layers() # figure out the total number of operations to perform for the progress num_ops = 1 + len(render_layers) * len(self._frame_list) # layer > frame num_ops += len(self._frame_list) # frame submission if self._remove_scenes: num_ops += 1 if self._generate_scenes: num_ops += 1 cur_op = 0 progress_dialog = QtGui.QProgressDialog( "Product render...", "", cur_op, num_ops, self) progress_dialog.setWindowTitle("Dark Knight is busy...") progress_dialog.setAutoReset(False) progress_dialog.setLabelText("Preparing maya file for rendering...") progress_dialog.show() ptask = self._cur_ptask ptask_version = self._cur_ptask.version(self._version) ptask_dir = self._cur_ptask.area.dir() ver_dir = ptask.area.dir(version=self._version) # need to get the maya file in the version directory maya_file = self.session.cmds.file(q=True, sceneName=True) maya_file = maya_file.replace(ptask_dir, ver_dir) file_base = os.path.splitext(os.path.split(maya_file)[1])[0] self.session.cmds.setAttr('defaultResolution.width', self._resolution.width) self.session.cmds.setAttr('defaultResolution.height', self._resolution.height) # set the output file naming convention to name.#.ext self.session.cmds.setAttr("defaultRenderGlobals.animation", True) self.session.cmds.setAttr("defaultRenderGlobals.putFrameBeforeExt", True) self.session.cmds.setAttr("defaultRenderGlobals.outFormatControl", False) # set all other cameras to not be renderable (this seems to work) cam_shape_list = self.session.cmds.ls(cameras=True) for cam_shape in cam_shape_list: cam_name = str( self.session.cmds.listRelatives(cam_shape, parent=True)[0]) if cam_name == self._camera: self.session.cmds.setAttr(cam_shape + ".renderable", 1) else: self.session.cmds.setAttr(cam_shape + ".renderable", 0) # ---- sync current work area to version snapshot to render from cur_project = self.session.cmds.workspace(query=True, rootDirectory=True) ver_project = cur_project.replace(ptask_dir, ver_dir) progress_dialog.setLabelText("Sync'ing work to current version...") try: self.session.save() self._sync_latest() except Exception as e: self._show_error("Unable to save & sync the latest work: " + str(e)) self.setEnabled(True) progress_dialog.close() return cur_op += 1 progress_dialog.setValue(cur_op) create_action_cls = ActionRegistry().get_action('create', 'product') if not create_action_cls: progress_dialog.close() raise DarkKnightError("Unable to find product creation action.") # ---- clean up ASSs scene_dir = os.path.join(ver_project, 'arnold', file_base, 'ass') if self._remove_scenes: progress_dialog.setLabelText("Removing ass files...") if os.path.isdir(scene_dir): try: shutil.rmtree(scene_dir) except Exception as e: progress_dialog.close() raise DarkKnightError("Unable to clean up ass files: " + str(e)) cur_op += 1 progress_dialog.setValue(cur_op) # ---- get a list of warnings to ignore #prman_config = ptask.area.config(PRMAN_CONFIG_PATH, # composite_ancestors=True, composite_method="override") #prman_warnings = " ".join( # ["-woff " + w for w in prman_config.get('woff', [])]) # ---- construct scripts for the queue render_summary = [] for render_layer in render_layers: progress_dialog.setLabelText( "Creating product for layer: {rl}...".format(rl=render_layer)) # ensure product exists for each render layer create_action = create_action_cls( product=render_layer, ptask=ptask_version.ptask_spec, version=ptask_version.number, category='imgseq', description=render_layer + " render layer", file_type=self._file_type, resolution=self._res_str, note=self._version_note, ) try: create_action() except ActionError as e: progress_dialog.close() raise DarkKnightError("Unable to create product: " + str(e)) product_repr = create_action.product_repr product_repr_area = product_repr.area progress_dialog.setLabelText( "Provisioning 'queue' directory in product...") # make sure queue directory exists try: product_repr_area.provision('queue') except Exception as e: progress_dialog.close() raise DarkKnightError( "Unable to create queue scripts directory: " + str(e)) queue_dir = product_repr_area.dir(dir_name='queue') tasks_info_file = os.path.join(queue_dir, 'tasks_info.cfg') tasks_info_config = Config() # dpaset command to run dpaset_cmd = 'eval "`dpa env ptask {pt}@{vn}`"'.format( pt=ptask.spec, vn=ptask_version.number) # set group permissions on project dir, recursively os.system("chmod g+rw {pd} -R".format(pd=ver_project)) # figure out the render layer if render_layer == 'masterLayer': layer_index = self.session.cmds.getAttr("defaultRenderLayer.rlid") else: layer_index = self.session.cmds.getAttr(render_layer + ".rlid") frame_scripts = [] for frame in self._frame_list: frame_padded = str(frame).zfill(4) progress_dialog.setLabelText( "Building render shell script for {rl} frame {f}".format( rl=render_layer, f=frame_padded)) script_path = os.path.join(queue_dir, "{rl}.{fn}.sh".format(rl=render_layer, fn=frame_padded)) out_dir = product_repr_area.dir() out_file = os.path.join(out_dir, "{rl}.{fn}.{ft}".\ format(rl=render_layer, fn=frame_padded, ft=self._file_type)) simple_scene = "{proj}arnold/{fb}/ass/{fb}.{fn}.ass".format( proj=ver_project, fb=file_base, fn=frame_padded) layer_scene = "{proj}arnold/{fb}/ass/{fb}_{rl}.{fn}.ass".\ format(proj=ver_project, fb=file_base, fn=frame_padded, rl=render_layer) render_cmd = "/opt/solidangle/arnold-maya2014/bin/kick -dw -v 0 -i $ASS_PATH " render_cmd += "-l /opt/solidangle/arnold-maya2014/shaders " render_cmd += "-o {od} ".format(od=out_file) #render_cmd += "-f {rl} ".format(rl=render_layer) #render_cmd += "-p {proj} ".format(proj=ver_project) #render_cmd += "--prman '-t:0 -cwd \"{proj}\" {warn}' ".\dd # format(proj=ver_project, warn=prman_warnings) with open(script_path, "w") as script_file: script_file.write("#!/bin/bash\n\n") # XXX these should happen automatically in the queue... script_file.write("source /DPA/wookie/dpa/bash/startup.bash\n") script_file.write("pipeup\n") # 'kick' command has to be added to $PATH # Create env variable for Arnold License server script_file.write("export [email protected]\n\n") script_file.write("# set the ptask version to render\n") script_file.write(dpaset_cmd + "\n") script_file.write("cd " + ver_project + "\n\n") # the logic for determining which ass will be generated is # unclear at this point. So we'll build a conditional script_file.write("if [[ -f {lr} ]]; then\n".format(lr=layer_scene)) script_file.write(" export ASS_PATH={lr}\n".format(lr=layer_scene)) script_file.write("else\n") script_file.write(" export ASS_PATH={sr}\n".format(sr=simple_scene)) script_file.write("fi\n") script_file.write("# render!\n") script_file.write(render_cmd + "\n\n") script_file.write("chmod 660 {of}\n\n".format( of=os.path.join(out_dir, render_layer + "*." + self._file_type))) os.chmod(script_path, 0770) frame_scripts.append((frame_padded, script_path, out_file)) cur_op += 1 progress_dialog.setValue(cur_op) frame_tasks = [] task_id_base = get_unique_id(product_repr_area.spec, dt=now) tasks_info_config.add('base_id', task_id_base) if self._generate_scenes: frame_queue = 'hold' else: frame_queue = self._render_queue # create frame tasks for (frame, frame_script, out_file) in frame_scripts: progress_dialog.setLabelText( "Submitting frame: " + frame_script) task_id = task_id_base + "_" + frame if not self._debug_mode: # create tasks, don't actually submit yet create_queue_task(frame_queue, frame_script, task_id, output_file=out_file, submit=False, log_path=frame_script + '.log') frame_tasks.append((frame, task_id)) # # resubmit frame-by-frame because # group submit seems to be occasionally # having problems. os.system("cqresubmittask {qn} {tid}".format( qn=frame_queue, tid=task_id)) cur_op += 1 progress_dialog.setValue(cur_op) frame_info = Config() for (frame, task_id) in frame_tasks: frame_info.add(str(frame), task_id) tasks_info_config.add('frame_ids', frame_info) # resubmit all at once (workaround for slow individual submissions) # # This approach seems to be having problems with the server # communications. Switch to frame-by-frame resubmit because # that has worked where this fails #os.system("cqresubmittask {qn} {tid}".format( # qn=frame_queue, tid=task_id_base)) if self._generate_scenes: progress_dialog.setLabelText("Creating ass generation script...") script_path = os.path.join(queue_dir, "{rl}_assgen.sh".format(rl=render_layer)) with open(script_path, "w") as script_file: script_file.write("#!/bin/bash\n\n") # XXX these should happen automatically in the queue... script_file.write("source /DPA/wookie/dpa/bash/startup.bash\n") script_file.write("pipeup\n\n") script_file.write("# set the ptask version to render\n") script_file.write(dpaset_cmd + "\n") script_file.write("cd " + ver_project + "\n\n") script_file.write("# generate the ass files...\n") current_render_layer = render_layer if render_layer == 'masterLayer': current_render_layer = "defaultRenderLayer" switch_render_layer_cmd = "editRenderLayerGlobals " switch_render_layer_cmd += "-currentRenderLayer \"{rl}\"".\ format(rl=current_render_layer) arnold_export_cmd = "arnoldExportAss -f \"{ad}/{fb}_{rl}.ass\" ".\ format(ad=scene_dir, fb=file_base, rl=render_layer) arnold_export_cmd += "-startFrame {sf} -endFrame {ef} -frameStep 1 ".\ format(li=layer_index, sf=self._frange.start, ef=self._frange.end) arnold_export_cmd += "-mask 255 -lightLinks 1 -shadowLinks 1 -cam {cam}".\ format(cam=self._camera) maya_batch_cmd = 'maya -batch -proj "{proj}" '.format( proj=ver_project) maya_batch_cmd += '-command \'{srlc}; {ar}\' '.\ format(srlc=switch_render_layer_cmd, ar=arnold_export_cmd) maya_batch_cmd += '-file "{mf}"'.format(mf=maya_file) script_file.write(maya_batch_cmd + "\n") script_file.write( "\n# make sure project dir has group permissions\n") script_file.write( "chmod g+rw {pd} -R\n\n".format(pd=ver_project)) # submit the frames to render script_file.write("# Submit frames after ass gen \n") for (frame, frame_task) in frame_tasks: script_file.write("cqmovetask {qn} {tid}\n".format( qn=self._render_queue, tid=frame_task)) # changed to move group #script_file.write("cqmovetask {qn} {tid}\n".format( #qn=self._render_queue, tid=task_id_base)) os.chmod(script_path, 0770) # submit the scenegen script progress_dialog.setLabelText( "Submitting ass gen: " + script_path) task_id = task_id_base + "_asses" tasks_info_config.add('assgen_id', task_id) if not self._debug_mode: create_queue_task(self._scenegen_queue, script_path, task_id, output_file=scene_dir, submit=True, log_path=script_path + '.log') cur_op += 1 progress_dialog.setValue(cur_op) cur_op += 1 progress_dialog.setValue(cur_op) progress_dialog.close() render_summary.append( (render_layer, task_id_base, product_repr, queue_dir)) # For now, disable wrangling tickets. bsddb is causing problems # - zshore, 2015-10-23 # if not self._debug_mode: # # ---- dpa specific queue stuff # from cheesyq import DPAWrangler # # create wrangling ticket # wrangle = DPAWrangler.WrangleRecord(task_id_base) # wrangle.frames = self._frame_list # db = DPAWrangler.GetWranglingDB() # db.set(wrangle.baseId, wrangle) # DPAWrangler.AssignWranglerTask("none", task_id_base) tasks_info_config.write(tasks_info_file) os.chmod(tasks_info_file, 0660) if not self._debug_mode: # send msg... msg_title = "Queue submission report: " + \ now.strftime("%Y/%m/%d %H:%M:%S") msg_body = "Submitted the following tasks for " + \ ptask.spec + ":\n\n" msg_body += " Description: " + self._version_note + "\n" msg_body += " Resolution: " + self._res_str + "\n" msg_body += " File type: " + self._file_type + "\n" msg_body += " Camera: " + self._camera + "\n" if self._generate_scenes: msg_body += " Ass gen queue: " + self._scenegen_queue + "\n" msg_body += " Render queue: " + self._render_queue + "\n" msg_body += " Frames: " + str(self._frange) + "\n" msg_body += " Ass directory: " + scene_dir + "\n" msg_body += "\n" for (layer, task_id_base, product_repr, queue_dir) in render_summary: msg_body += " Render layer: " + layer + "\n" msg_body += " Base task ID: " + task_id_base + "\n" msg_body += " Product representation: " + \ product_repr.spec + "\n" msg_body += " Scripts directory: " + queue_dir + "\n" msg_body += "\n" dk_config = ptask.area.config(DK_CONFIG_PATH, composite_ancestors=True, composite_method="append") recipients = dk_config.get('notify', []) recipients.append(current_username()) recipients = emails_from_unames(recipients) notification = Notification(msg_title, msg_body, recipients, sender=User.current().email) notification.send_email()
def _render_to_product(self): # get render node reference render_node = self.session.hou.node(self._node_to_render) # ---- progress dialog num_ops = 8 cur_op = 0 progress_dialog = QtGui.QProgressDialog( "Product render...", "", cur_op, num_ops, self) progress_dialog.setWindowTitle("Dark Knight is busy...") progress_dialog.setAutoReset(False) progress_dialog.setLabelText("Preparing nuke file for rendering...") progress_dialog.show() ######################################### # ensure the product has been created ######################################### progress_dialog.setLabelText("Creating product...") if not render_node.type().name()=='ifd' or not self._version_note: raise Exception("The supplied node is not a WriteProduct node.") print "Creating product for node... " + str(render_node) ptask_area = PTaskArea.current() ptask = PTask.get(ptask_area.spec) if ptask_area.version: ptask_version = ptask.version(ptask_area.version) else: ptask_version = ptask.latest_version category = 'imgseq' file_type = 'exr' product_name = render_node.name() product_desc = render_node.name() + " mantra render" product_ver_note = self._version_note camera_node = self.session.hou.node(render_node.evalParm('camera')) if not camera_node: raise Exception("Camera specified is not valid.") width = camera_node.evalParm("resx") height = camera_node.evalParm("resy") resolution = "%sx%s" % (width, height) create_action_cls = ActionRegistry().get_action('create', 'product') if not create_action_cls: raise Exception("Unable to find product creation action.") create_action = create_action_cls( product=product_name, ptask=ptask.spec, version=ptask_version.number, category=category, description=product_desc, file_type=file_type, resolution=resolution, note=product_ver_note, ) try: create_action() except ActionError as e: raise Exception("Unable to create product: " + str(e)) # provision the ifd directory try: create_action.product_repr.area.provision('ifd') except Exception as e: raise Exception( "Unable to create ifd file directory: " + str(e)) ifd_dir = os.path.join(create_action.product_repr.area.path, 'ifd', product_name + '.$F4.ifd') out_path = os.path.join(create_action.product_repr.area.path, product_name + '.$F4.' + file_type) # by default, the mantra frame range has an expression on frame numbers render_node.parm('f1').deleteAllKeyframes() render_node.parm('f2').deleteAllKeyframes() # set frange render_node.parm('trange').set(1) render_node.parm('f1').set(self._frange.start) render_node.parm('f2').set(self._frange.end) render_node.parm('f3').set(self._frange.step) # set output render_node.parm('soho_outputmode').set(1) render_node.parm('soho_diskfile').set(ifd_dir) render_node.parm('soho_diskfile').disable(0) render_node.parm('vm_picture').set(out_path) render_node.parm('soho_mkpath').set(1) product_repr = create_action.product_repr product_repr_area = product_repr.area cur_op += 1 progress_dialog.setValue(cur_op) ######################################### # create ifd files ######################################### progress_dialog.setLabelText("Generating ifd files...") render_node.parm('execute').pressButton() ifd_file_list = glob.glob( os.path.join( create_action.product_repr.area.path, 'ifd', '*.ifd') ) for ifd_file in ifd_file_list: os.chmod(ifd_file, 0770) cur_op += 1 progress_dialog.setValue(cur_op) ######################################### # sync current work area to version snapshot to render from ######################################### progress_dialog.setLabelText("Sync'ing the latest work...") try: self.session.save() self._sync_latest() except Exception as e: self._show_error("Unable to save & sync the latest work: " + str(e)) self.setEnabled(True) progress_dialog.close() return cur_op += 1 progress_dialog.setValue(cur_op) ######################################### # ensure queue directory exists ######################################### progress_dialog.setLabelText("Provisioning the queue directory...") try: product_repr_area.provision('queue') except Exception as e: raise DarkKnightError( "Unable to create queue scripts directory: " + str(e)) cur_op += 1 progress_dialog.setValue(cur_op) out_dir = product_repr_area.path ifd_dir = product_repr_area.dir(dir_name='ifd') queue_dir = product_repr_area.dir(dir_name='queue') tasks_info_file = os.path.join(queue_dir, 'tasks_info.cfg') tasks_info_config = Config() cur_op += 1 progress_dialog.setValue(cur_op) ######################################### # buidling queue scripts ######################################### progress_dialog.setLabelText("Building the queue script...") # # dpaset command to run dpaset_cmd = 'eval "`dpa env ptask {pt}@{vn}`"'.format( pt=ptask.spec, vn=ptask_version.number) # write out queue shell scripts frame_scripts = [] for frame in self._frame_list: frame_padded = str(frame).zfill(4) ifd_file = os.path.join(ifd_dir, "{pn}.{fn}.ifd".format(pn=product_name, fn=frame_padded)) script_path = os.path.join(queue_dir, "{pn}.{fn}.sh".format(pn=product_name, fn=frame_padded)) out_file = os.path.join(out_dir, "{pn}.{fn}.{ft}".format(pn=product_name, fn=frame_padded, ft=file_type) ) render_cmd = "/opt/hfs14/bin/mantra -f {ifd} -V 2a".\ format( ifd=ifd_file ) with open(script_path, "w") as script_file: script_file.write("#!/bin/bash\n\n") # XXX these should happen automatically in the queue... script_file.write("source /DPA/wookie/dpa/bash/startup.bash\n") script_file.write("pipeup\n\n") script_file.write("# set the ptask version to render\n") script_file.write(dpaset_cmd + "\n\n") script_file.write("# render!\n") script_file.write(render_cmd + "\n\n") frame_scripts.append((frame_padded, script_path, out_file)) os.chmod(script_path, 0770) cur_op += 1 progress_dialog.setValue(cur_op) ################################################ # submit to the queue ################################################ now = datetime.datetime.now() task_id_base = get_unique_id(product_repr_area.spec, dt=now) frame_tasks = [] # create frame tasks for (frame, frame_script, out_file) in frame_scripts: progress_dialog.setLabelText( "Submitting frame: " + frame_script) task_id = task_id_base + "_" + frame if not self._debug_mode: # create tasks, don't actually submit yet create_queue_task(self._render_queue, frame_script, task_id, output_file=out_file, submit=False, log_path=frame_script + '.log') frame_tasks.append((frame, task_id)) # # resubmit frame-by-frame because # group submit seems to be occasionally # having problems. os.system("cqresubmittask {qn} {tid}".format( qn=self._render_queue, tid=task_id)) cur_op += 1 progress_dialog.setValue(cur_op) ################################################ # task info stuff, allows task ids to # be retrieved with product spec ################################################ progress_dialog.setLabelText("Creating task info file...") tasks_info_file = os.path.join(queue_dir, 'tasks_info.cfg') tasks_info_config = Config() tasks_info_config.add('base_id', task_id_base) frame_info = Config() for (frame, task_id) in frame_tasks: frame_info.add(str(frame), task_id) tasks_info_config.add('frame_ids', frame_info) tasks_info_config.write(tasks_info_file) os.chmod(tasks_info_file, 0660) cur_op += 1 progress_dialog.setValue(cur_op) ################################################ # email report ################################################ if not self._debug_mode: # send msg... msg_title = "Queue submission report: " + \ now.strftime("%Y/%m/%d %H:%M:%S") msg_body = "Submitted the following tasks for " + \ ptask.spec + ":\n\n" msg_body += " Description: " + self._version_note + "\n" msg_body += " Resolution: " + resolution + "\n" msg_body += " Render queue: " + self._render_queue + "\n" msg_body += " Frames: " + str(self._frange) + "\n" msg_body += " Ifd directory: " + ifd_dir + "\n" msg_body += "\n" msg_body += " Base task ID: " + task_id_base + "\n" msg_body += " Product representation: " + \ product_repr.spec + "\n" msg_body += " Scripts directory: " + queue_dir + "\n" msg_body += "\n" dk_config = ptask.area.config(DK_CONFIG_PATH, composite_ancestors=True, composite_method="append") recipients = dk_config.get('notify', []) recipients.append(current_username()) recipients = emails_from_unames(recipients) notification = Notification(msg_title, msg_body, recipients, sender=User.current().email) notification.send_email() print recipients cur_op += 1 progress_dialog.setValue(cur_op) progress_dialog.close()