Пример #1
0
    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()
Пример #2
0
    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()
Пример #3
0
    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()
Пример #4
0
    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()
Пример #5
0
    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()