def validate(self): # current ptask/version try: area = PTaskArea.current() self._current_ptask = PTask.get(area.spec) self._current_ptask_version = self._current_ptask.latest_version except PTaskError: raise ActionError("Unable to find ptask: " + str(self._ptask)) # source ptask if not isinstance(self._ptask, PTask): try: cur_spec = PTaskArea.current().spec full_spec = PTaskSpec.get(self._ptask, relative_to=cur_spec) self._ptask = PTask.get(full_spec) except PTaskError: raise ActionError("Unable to find ptask: " + str(self._ptask)) # source ptask version if isinstance(self._version, PTaskVersion): pass elif self._version: matches = PTaskVersion.list( ptask=self._ptask.spec, number=self._version ) if len(matches) != 1: raise ActionError( "Unable to find ptask '{p}' at version '{v}'".format( p=self._ptask.spec, v=self._version ) ) else: self._version = matches[0] else: self._version = self._ptask.latest_version # source subs self._match_str = self._match_str.replace("%", ".*") all_subs = self._version.subscriptions self._subs_to_source = [] for sub in all_subs: if re.search(self._match_str, sub.product_version_spec): self._subs_to_source.append(sub) if not self._subs_to_source: raise ActionAborted("No subscriptions to source.")
def validate(self): cur_spec = PTaskArea.current().spec full_spec = PTaskSpec.get(self.spec, relative_to=cur_spec) # if we're listing the current ptask's subs, and no versions specified if cur_spec == full_spec and not self._versions: ptask_ver = DpaVars.ptask_version().get() if ptask_ver: self._versions = [ptask_ver] if not self._versions: self._versions = ["latest"] # try to get a ptask instance from the db try: ptask = PTask.get(full_spec) except PTaskError as e: # fall back to the input spec try: ptask = PTask.get(self.spec) except PTaskError: raise ActionError( 'Could not determine ptask from: "{s}"'.format( s=self.spec) ) self._ptask = ptask if self._versions == ['latest']: versions = [self.ptask.latest_version] elif self._versions == ['all']: versions = self.ptask.versions else: self._versions = map(int, self._versions) versions = [v for v in self.ptask.versions if v.number in self._versions] if len(versions) == 0: raise ActionError( "No matches found for {p} version: {v}".format( p=ptask.spec, v=Style.bright + str(self._versions) + Style.normal, ) ) self._versions = versions
def ptask(self): """:returns: PTask object for this version.""" if not self.ptask_spec: return None # import here to avoid circular dependencies from dpa.ptask import PTask return PTask.get(self.ptask_spec)
def ptask(self): """:returns: PTask object for this assignment.""" if not self.ptask_spec: return None # import here to avoid circular dependencies from dpa.ptask import PTask return PTask.get(self.ptask_spec)
def validate(self): cur_spec = PTaskArea.current().spec full_spec = PTaskSpec.get(self.spec, relative_to=cur_spec) # if we're listing the current ptask's subs, and no versions specified if cur_spec == full_spec and not self._versions: ptask_ver = DpaVars.ptask_version().get() if ptask_ver: self._versions = [ptask_ver] if not self._versions: self._versions = ["latest"] # try to get a ptask instance from the db try: ptask = PTask.get(full_spec) except PTaskError as e: # fall back to the input spec try: ptask = PTask.get(self.spec) except PTaskError: raise ActionError( 'Could not determine ptask from: "{s}"'.format( s=self.spec)) self._ptask = ptask if self._versions == ['latest']: versions = [self.ptask.latest_version] elif self._versions == ['all']: versions = self.ptask.versions else: self._versions = map(int, self._versions) versions = [ v for v in self.ptask.versions if v.number in self._versions ] if len(versions) == 0: raise ActionError("No matches found for {p} version: {v}".format( p=ptask.spec, v=Style.bright + str(self._versions) + Style.normal, )) self._versions = versions
def validate(self): # current ptask/version try: area = PTaskArea.current() self._current_ptask = PTask.get(area.spec) self._current_ptask_version = self._current_ptask.latest_version except PTaskError: raise ActionError("Unable to find ptask: " + str(self._ptask)) # source ptask if not isinstance(self._ptask, PTask): try: cur_spec = PTaskArea.current().spec full_spec = PTaskSpec.get(self._ptask, relative_to=cur_spec) self._ptask = PTask.get(full_spec) except PTaskError: raise ActionError("Unable to find ptask: " + str(self._ptask)) # source ptask version if isinstance(self._version, PTaskVersion): pass elif self._version: matches = PTaskVersion.list(ptask=self._ptask.spec, number=self._version) if len(matches) != 1: raise ActionError( "Unable to find ptask '{p}' at version '{v}'".format( p=self._ptask.spec, v=self._version)) else: self._version = matches[0] else: self._version = self._ptask.latest_version # source subs self._match_str = self._match_str.replace("%", ".*") all_subs = self._version.subscriptions self._subs_to_source = [] for sub in all_subs: if re.search(self._match_str, sub.product_version_spec): self._subs_to_source.append(sub) if not self._subs_to_source: raise ActionAborted("No subscriptions to source.")
def __init__(self, spec, ptask_type=None, description=None, creator=None, start_date=None, due_date=None, source=None, force=True): super(PTaskCreateAction, self).__init__( spec, ptask_type=ptask_type, description=description, creator=creator, start_date=start_date, due_date=due_date, source=source, force=force, ) # allow calling code to override the target to specify the ptask type # to create if ptask_type is None: if self.__class__.target_type is not 'ptask': ptask_type = self.__class__.target_type else: raise ActionError("PTask type is required.") # do some initial validation on the supplied spec parent = PTaskSpec.parent(spec) if parent != "": try: parent = PTask.get(parent) except PTaskError: raise ActionError("Parent ptask does not exist: " + parent) name = PTaskSpec.name(spec) try: name = validate_ptask_name(name) except PTaskError as e: raise ActionError("Invalid ptask name: " + str(e)) # input self._spec = spec self._ptask_type = ptask_type self._description = description self._creator = creator self._start_date = start_date self._due_date = due_date self._source = source self._force = force # to create self._ptask = None self._ptask_area = None self._ptask_version = None
def validate(self): if self.source: try: self._source = PTask.get(self.source) except PTaskError: raise ActionError( "Unable to retrieve ptask from source argument: " + \ str(self.source), )
def validate(self): if not isinstance(self._ptask, PTask): try: cur_spec = PTaskArea.current().spec full_spec = PTaskSpec.get(self._ptask, relative_to=cur_spec) self._ptask = PTask.get(full_spec) except PTaskError: raise ActionError("Could not determine ptask from: {p}".format( p=self._ptask))
def validate(self): cur_spec = PTaskArea.current().spec full_spec = PTaskSpec.get(self.spec, relative_to=cur_spec) # try to get a ptask instance from the db if full_spec: try: ptask = PTask.get(full_spec) except PTaskError as e: # fall back to the input spec try: ptask = PTask.get(self.spec) except PTaskError: raise ActionError( 'Could not determine ptask from: "{s}"'.format( s=self.spec)) else: ptask = None self._ptask = ptask
def validate(self): cur_spec = PTaskArea.current().spec full_spec = PTaskSpec.get(self.spec, relative_to=cur_spec) # try to get a ptask instance from the db if full_spec: try: ptask = PTask.get(full_spec) except PTaskError as e: # fall back to the input spec try: ptask = PTask.get(self.spec) except PTaskError: raise ActionError( 'Could not determine ptask from: "{s}"'.format( s=self.spec) ) else: ptask = None self._ptask = ptask
def ptask(self): if not hasattr(self, '_ptask'): ptask_area = self.ptask_area if not ptask_area.spec: self._ptask = None else: try: self._ptask = PTask.get(ptask_area.spec) except PTaskError as e: raise SessionError("Unable to determine ptask.") return self._ptask
def validate(self): use_cur_version = False if not isinstance(self.ptask, PTask): if not self.ptask or self.ptask == '.': use_cur_version = True cur_spec = PTaskArea.current().spec full_spec = PTaskSpec.get(self.ptask, relative_to=cur_spec) # try to get a ptask instance from the db try: ptask = PTask.get(full_spec) except PTaskError as e: # fall back to the input spec try: ptask = PTask.get(self.spec) except PTaskError: raise ActionError( 'Could not determine ptask from: "{s}"'.format( s=self.spec) ) self._ptask = ptask latest_ver = self.ptask.latest_version if use_cur_version: cur_ptask_ver = DpaVars.ptask_version().get() if cur_ptask_ver and cur_ptask_ver != latest_ver.number: self._ptask_version = self.ptask.version(cur_ptask_ver) self._version = cur_ptask_ver else: self._ptask_version = self.ptask.latest_version else: self._ptask_version = self.ptask.latest_version
def validate(self): use_cur_version = False if not isinstance(self.ptask, PTask): if not self.ptask or self.ptask == '.': use_cur_version = True cur_spec = PTaskArea.current().spec full_spec = PTaskSpec.get(self.ptask, relative_to=cur_spec) # try to get a ptask instance from the db try: ptask = PTask.get(full_spec) except PTaskError as e: # fall back to the input spec try: ptask = PTask.get(self.spec) except PTaskError: raise ActionError( 'Could not determine ptask from: "{s}"'.format( s=self.spec)) self._ptask = ptask latest_ver = self.ptask.latest_version if use_cur_version: cur_ptask_ver = DpaVars.ptask_version().get() if cur_ptask_ver and cur_ptask_ver != latest_ver.number: self._ptask_version = self.ptask.version(cur_ptask_ver) self._version = cur_ptask_ver else: self._ptask_version = self.ptask.latest_version else: self._ptask_version = self.ptask.latest_version
def validate(self): if self.interactive: print "\nValidating product arguments ..." # should have a valid product name, self._name = validate_product_name(self._name) if self._category: if not self._category in Product.category_names(): raise ActionError("Unrecognized category.") else: raise ActionError("Category is required.") if not self._description: raise ActionError("Description is required.") # ptask if not isinstance(self._ptask, PTask): try: self._ptask = PTask.get(self._ptask) except PTaskError: raise ActionError("Could not determine ptask.") if self._version: self._ptask_version = self._ptask.version(self._version) else: self._ptask_version = self._ptask.latest_version if not self._ptask_version: raise ActionError("Could not determine ptask version.") if not self._note: self._note = "None" if self._path: if not os.path.exists(self._path): raise ActionError("Supplied path does not exist.") if (os.path.isdir(self._path) and not self._path.endswith(os.path.sep)): self._path += os.path.sep
def populate_sub_cache(ptask_version=None, refresh=False): if not ptask_version: 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 nuke_file = nuke.root().name() nuke_dir = os.path.dirname(nuke_file) if refresh or not SUBD_REPR_CACHE: for sub in ptask_version.subscriptions: for product_repr in sub.product_version.representations: product = product_repr.product_version.product if product.category != 'imgseq': continue product_repr_str = product.name_spec + ' @ ' + \ product_repr.type if product_repr.resolution != 'none': product_repr_str += PTaskSpec.SEPARATOR + \ product_repr.resolution sub_import_dir = get_import_dir(product_repr, product=product, area=ptask_area, relative_to=nuke_dir) # populate cache lookups SUBD_REPR_CACHE.append(product_repr) PRODUCT_REPR_STR_TO_PATH[product_repr_str] = \ sub_import_dir
def validate(self): # make sure the ptask evaluates to a real ptask try: self._ptask = PTask.get(self.ptask) except PTaskError: raise ActionError("Unable to determine ptask from: " + str(self.ptask)) # for efficiency self._ptask_latest_version = self.ptask.latest_version # make sure the latest version is not this location if self.ptask_latest_version.location_code == current_location_code(): raise ActionError( "Latest version of {b}{p}{r} already owned by this location.".\ format( b=Style.bright, p=self.ptask.spec, r=Style.reset, ) )
def validate(self): cur_loc_code = current_location_code() try: self._ptask = PTask.get(self.ptask) except PTaskError as e: raise ActionError( "Unable to determine ptask from spec: " + str(self.ptask) ) # just sync the supplied version if self.version: try: ptask_version = _get_ptask_version( self.ptask, self.version, ) except TypeError as e: raise ActionError("Unable to retrieve ptask version: " + str(e)) # make sure we got a version and it's not this location if ptask_version is None: raise ActionError( "No ptask version matching: " + str(self.version) ) # make sure the location is not this location elif ptask_version.location_code == cur_loc_code: raise ActionError("Specified version exists in this location.") versions = [ptask_version] # sync all ptask versions that are not in this location else: versions = [v for v in self.ptask.versions if v.location_code != cur_loc_code] self._versions = versions
def __init__(self): self._ptask_area = PTaskArea.current() options_config = self._ptask_area.config(VERSION_OPTIONS_CONFIG, composite_ancestors=True) try: self._ptask = PTask.get(self._ptask_area.spec) except PTaskError as e: error_dialog = QtGui.QErrorMessage(self) error_dialog.setWindowTitle('Version Failure') error_dialog.showMessage("Unable to determine current ptask.") return icon_path = IconFactory().disk_path(VERSION_ICON_URI) super(PTaskVersionDialog, self).__init__( title='Version up', options_config=options_config, icon_path=icon_path, action_button_text='Submit', modal=False, )
def validate(self): # make sure the ptask evaluates to a real ptask try: self._ptask = PTask.get(self.ptask) except PTaskError: raise ActionError( "Unable to determine ptask from: " + str(self.ptask) ) # for efficiency self._ptask_latest_version = self.ptask.latest_version # make sure the latest version is not this location if self.ptask_latest_version.location_code == current_location_code(): raise ActionError( "Latest version of {b}{p}{r} already owned by this location.".\ format( b=Style.bright, p=self.ptask.spec, r=Style.reset, ) )
def validate(self): try: self._ptask = PTask.get(self.spec) except PTaskError: raise ActionError( "Unable to determine ptask from: " + str(self.spec) ) self._latest_version = self.ptask.latest_version # determine the source version. also store a reference to the latest # version for efficiency if self.source_version: source_version = self.ptask.version(self.source_version) if source_version is None: raise ActionError( "Could not determine source version from: " + \ self.source_version ) self._source_version = source_version else: self._source_version = self.latest_version # latest version must exist in this location. if self.latest_version.location_code != current_location_code(): raise ActionError( "The latest version of this ptask is not owned by this " + "location.\nOwnership of this ptask must first be " + "transferred to this location." ) if self.source_version.ptask != self.ptask: raise ActionError( "Source version's ptask does not match the ptask being " + \ "versioned." )
def prompt(self): parent_spec = PTaskSpec.parent(self.spec) template_options = [] if parent_spec: par_ptask = PTask.get(parent_spec) par_ptask_type = par_ptask.ptask_type else: par_ptask_type = 'none' ptask_area = PTaskArea(parent_spec, validate=False) master_config = ptask_area.config( PROJECT_MASTER_CONFIG_PATH, composite_ancestors=True, ) if not master_config or not hasattr(master_config, 'hierarchy'): raise ActionError("Unable to find project master config.") if not self.ptask_type in master_config.hierarchy[par_ptask_type]: raise ActionError( "Cannot create '{t}' ptask inside '{p}' ptask".format( t=self.ptask_type, p=par_ptask_type, ) ) # ---- prompt for missing fields if not self.source and self.ptask_type in master_config.templates: for template_spec in master_config.templates[self.ptask_type]: trimmed_spec = re.sub( "^templates?=", "", template_spec, flags=re.IGNORECASE ) template_options.append( ( re.sub("[=_-]+", " ", trimmed_spec).title(), template_spec ) ) self._source = Output.prompt_menu( "Select a template to source", prompt_str="Selection", options=template_options, help_str="Please choose from the templates listed.", none_option=True, custom_prompt="Custom Source", custom_blank=False ) # see if the ptask already exists if not self.ptask: try: self._ptask = PTask.get(self.spec) except PTaskError: pass else: if not self.force: raise ActionAborted("PTask already exists.") else: if not self._description: self._description = self.ptask.description if not self._start_date: self._start_date = self.ptask.start_date if not self._due_date: self._due_date = self.ptask.due_date if (not self.description or not self.start_date or not self.due_date): if self.force: raise ActionError( "Cannot force creation without required fields." ) else: print "\nPlease enter information about this new {b}{t}{r}:".\ format( b=Style.bright, t=self.ptask_type, r=Style.reset, ) ptask_display = " [{pt}] {b}{s}{r}".format( pt=self.ptask_type, b=Style.bright, s=self.spec, r=Style.reset, ) if not self.description: self._description = Output.prompt( '{pd} description'.format(pd=ptask_display), blank=False, ) if not self.start_date: self._start_date = Output.prompt_date( '{pd} start date'.format(pd=ptask_display), blank=False, ) if not self.due_date: self._due_date = Output.prompt_date( '{pd} due date'.format(pd=ptask_display), blank=False, )
def _output_options(self): output_type_lbl = QtGui.QLabel("Output:") output_type = QtGui.QComboBox() output_type.addItems(['Automatic', 'Manual']) header_layout = QtGui.QHBoxLayout() header_layout.addStretch() header_layout.addWidget(output_type_lbl) header_layout.addWidget(output_type) header_layout.addStretch() # ---- auto cur_area = PTaskArea.current() self._cur_ptask = PTask.get(cur_area.spec) if self._cur_ptask: self._version = \ cur_area.version or self._cur_ptask.latest_version.number else: self._cur_ptask = None self._version = "None" ptask_lbl = QtGui.QLabel("PTask:") ptask_edit = QtGui.QLineEdit(str(self._cur_ptask)) ptask_edit.setReadOnly(True) version_num = QtGui.QLabel("<B>v" + str(self._version) + "</B>") auto_layout = QtGui.QGridLayout() auto_layout.addWidget(ptask_lbl, 0, 0, QtCore.Qt.AlignRight) auto_layout.addWidget(ptask_edit, 0, 1) auto_layout.addWidget(version_num, 0, 2, QtCore.Qt.AlignLeft) auto_layout.setColumnStretch(0, 0) auto_layout.setColumnStretch(1, 1000) auto_widgets = QtGui.QWidget() auto_widgets.setLayout(auto_layout) # ---- manual dir_lbl = QtGui.QLabel("Directory:") self._dir_edit = QtGui.QLineEdit(os.getcwd()) dir_btn = QtGui.QPushButton() dir_btn.setFlat(True) dir_btn_size = QtCore.QSize(22, 22) dir_btn.setFixedSize(dir_btn_size) dir_btn.setIcon(QtGui.QIcon(self.__class__._dir_path)) dir_btn.setIconSize(dir_btn_size) dir_dialog = QtGui.QFileDialog(self, 'Output directory', os.getcwd()) dir_dialog.setFileMode(QtGui.QFileDialog.Directory) dir_dialog.setOption(QtGui.QFileDialog.ShowDirsOnly, True) dir_dialog.setOption(QtGui.QFileDialog.DontResolveSymlinks, True) dir_dialog.setOption(QtGui.QFileDialog.HideNameFilterDetails, True) dir_dialog.fileSelected.connect(self._dir_edit.setText) dir_btn.clicked.connect(dir_dialog.show) manual_layout = QtGui.QGridLayout() manual_layout.setContentsMargins(0, 0, 0, 0) manual_layout.addWidget(dir_lbl, 0, 0, QtCore.Qt.AlignRight) manual_layout.addWidget(self._dir_edit, 0, 1) manual_layout.addWidget(dir_btn, 0, 2) manual_layout.setColumnStretch(0, 0) manual_layout.setColumnStretch(1, 1000) manual_layout.setColumnStretch(2, 0) manual_widgets = QtGui.QWidget() manual_widgets.setLayout(manual_layout) self._output_stack = QtGui.QStackedWidget() self._output_stack.addWidget(auto_widgets) self._output_stack.addWidget(manual_widgets) output_type.activated.connect(self._output_stack.setCurrentIndex) # ---- layout output_layout = QtGui.QVBoxLayout() output_layout.addLayout(header_layout) output_layout.addWidget(self._output_stack) return output_layout
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 execute(self): ptasks = [] # search the ptasks for specs matching the supplied string if PTaskSpec.WILDCARD in self.wild_spec: search_str = ",".join( filter(None, self.wild_spec.strip().split(PTaskSpec.WILDCARD))) if not search_str: raise ActionError( "Search is too broad. " + \ "Please supply a string to search against." ) try: # XXX this is inefficient. need better filtering on the backend ptasks = PTask.list(search=search_str) except PTaskError: pass else: try: ptasks.append(PTask.get(self.wild_spec)) except PTaskError: pass matching_ptasks = [] # the rest api's search filter isn't that great. it doesn't maintain any # knowledge of order for the supplied filters. So, it will return ptasks # that match all of the search terms, but not necessarily in the order # supplied. Do one more match against the returned ptasks specs keeping # the order of the supplied wildcard spec. regex_spec = "^" + \ self.wild_spec.replace(PTaskSpec.WILDCARD, "([\w=]+)?") + "$" regex_spec = re.compile(regex_spec) for ptask in ptasks: if regex_spec.match(ptask.spec): matching_ptasks.append(ptask) match_count = len(matching_ptasks) if match_count == 0: print '\nFound 0 ptasks matching: "{s}"\n'.format(s=self.wild_spec) return # define the fields names spec = "Spec" ptask_type = "Type" status = "Status" # define the look of the output output = Output() output.vertical_separator = None output.table_cell_separator = ' ' output.table_header_separator = '-' if match_count == 1: output.title = "{s}: 1 match".format(s=self.wild_spec) else: output.title = "{s}: {n} matches".format(s=self.wild_spec, n=match_count) # display order of the information output.header_names = [ spec, ptask_type, status, ] for ptask in sorted(matching_ptasks, key=lambda p: p.spec): # add all the information output.add_item( { spec: ptask.spec, ptask_type: ptask.ptask_type, status: ptask.status, }, colors={ spec: Style.bright, }) # dump the output as a list of key/value pairs output.dump(output_format='table')
def validate(self): # ---- make sure the supplied specs match actual ptasks, # set the properties try: self._source = PTask.get(self.source) except PTaskError: raise ActionError( "Unable to retrieve ptask from source argument: " + \ str(self.source) ) try: self._destination = PTask.get(self.destination) except PTaskError: raise ActinError( "Unable to retrieve ptask from destination argument: " + \ str(self.destination), self, ) self._source_latest_version = self.source.latest_version self._destination_latest_version = self.destination.latest_version # ---- make sure the ptasks are of the same type #if self.source.type != self.destination.type: # raise ActionError( # "Source and destination ptasks must be of the same type. " + \ # self.source.type + " != " + self.destination.type # ) # ---- if the target_type is not ptask, then the calling code has # overridden the target type. make sure the source and destination # types match the target type. target_type = self.__class__.target_type if target_type != "ptask": if self.source.type.lower() != target_type: raise ActionError("Source type must be a " + target_type) elif self.destination.type.lower() != target_type: raise ActionError("Destination type must a " + target_type) # ---- determine the source and destination versions and their locations if self.source_version: try: self._source_version = _get_ptask_version( self.source, self.source_version, ) except TypeError as e: raise ActionError(str(e)) source_location_code = self.source_version.location_code else: source_location_code = self.source_latest_version.location_code if self.destination_version: try: self._destination_version = _get_ptask_version( self.destination, self.destination_version, ) except TypeError as e: raise ActionError(str(e)) destination_location_code = self.destination_version.location_code else: destination_location_code = \ self.destination_latest_version.location_code # one of source or dest must be the current loation, unless the source # and destination are the same ptask. In that case, we'll assume the # goal is to sync the ptask to the current location or to source # directories/versions within the ptask cur_loc_code = current_location_code() if self.source == self.destination: location_override = Location.current() else: if (source_location_code != cur_loc_code and destination_location_code != cur_loc_code): raise ActionError( "One of source or destination must be this location.", ) location_override = None # ---- determine the source and desination paths self._source_path = self._get_path( ptask=self.source, version=self.source_version, latest_version=self.source_latest_version, directory=self.source_directory, ) self._destination_path = self._get_path( ptask=self.destination, version=self.destination_version, latest_version=self.destination_latest_version, directory=self.destination_directory, location_override=location_override, ) # ---- get the includes/excludes based on filter rules (includes, excludes) = self._get_filter_rules(self.destination) # exclude child ptask directories from the source for child in self.source.children: child_dir = os.path.sep + child.name excludes.append(child_dir) self._includes = includes self._excludes = excludes
def prompt(self): parent_spec = PTaskSpec.parent(self.spec) template_options = [] if parent_spec: par_ptask = PTask.get(parent_spec) par_ptask_type = par_ptask.ptask_type else: par_ptask_type = 'none' ptask_area = PTaskArea(parent_spec, validate=False) master_config = ptask_area.config( PROJECT_MASTER_CONFIG_PATH, composite_ancestors=True, ) if not master_config or not hasattr(master_config, 'hierarchy'): raise ActionError("Unable to find project master config.") if not self.ptask_type in master_config.hierarchy[par_ptask_type]: raise ActionError( "Cannot create '{t}' ptask inside '{p}' ptask".format( t=self.ptask_type, p=par_ptask_type, )) # ---- prompt for missing fields if not self.source and self.ptask_type in master_config.templates: for template_spec in master_config.templates[self.ptask_type]: trimmed_spec = re.sub("^templates?=", "", template_spec, flags=re.IGNORECASE) template_options.append( (re.sub("[=_-]+", " ", trimmed_spec).title(), template_spec)) self._source = Output.prompt_menu( "Select a template to source", prompt_str="Selection", options=template_options, help_str="Please choose from the templates listed.", none_option=True, custom_prompt="Custom Source", custom_blank=False) # see if the ptask already exists if not self.ptask: try: self._ptask = PTask.get(self.spec) except PTaskError: pass else: if not self.force: raise ActionAborted("PTask already exists.") else: if not self._description: self._description = self.ptask.description if not self._start_date: self._start_date = self.ptask.start_date if not self._due_date: self._due_date = self.ptask.due_date if (not self.description or not self.start_date or not self.due_date): if self.force: raise ActionError( "Cannot force creation without required fields.") else: print "\nPlease enter information about this new {b}{t}{r}:".\ format( b=Style.bright, t=self.ptask_type, r=Style.reset, ) ptask_display = " [{pt}] {b}{s}{r}".format( pt=self.ptask_type, b=Style.bright, s=self.spec, r=Style.reset, ) if not self.description: self._description = Output.prompt( '{pd} description'.format(pd=ptask_display), blank=False, ) if not self.start_date: self._start_date = Output.prompt_date( '{pd} start date'.format(pd=ptask_display), blank=False, ) if not self.due_date: self._due_date = Output.prompt_date( '{pd} due date'.format(pd=ptask_display), blank=False, )
def _print_ptask_env(self): # remove any whitespace on the head/tail of the spec spec = self.spec.strip() ptask_area = None if self.version: spec = PTaskSpec.VERSION.join([spec, str(self.version)]) replace_match = re.match("\.?/([=\w]+)/([=\w]+)/", spec) # handle 'none' as a valid spec - unset current ptask (set it to root) if spec.lower() == 'none': spec = "" full_spec = PTaskSpec.get(spec) try: ptask_area = PTaskArea(full_spec) except: pass # special character '-' indicates use the last set ptask spec elif spec == "-": ptask_area = PTaskArea.previous() # set to a similar ptask with text replacement elif replace_match: cur_area_spec = PTaskArea.current().spec repl_spec = cur_area_spec.replace( replace_match.group(1), replace_match.group(2)) try: ptask_area = PTaskArea(repl_spec) except: pass # use the supplied spec relative to the current ptask else: relative_to = PTaskArea.current().spec while ptask_area is None: try: full_spec = PTaskSpec.get(spec, relative_to=relative_to) except PTaskSpecError as e: raise ActionError(str(e)) try: # if this is successful, we'll break out of the while ptask_area = PTaskArea(full_spec) except PTaskAreaError as e: # no match, check the parent relative spec relative_to = PTaskSpec.parent(relative_to) # there is no parent, break out of the while if relative_to is None: break # dump out commands used for setting the environment for the supplied # spec. if not ptask_area: raise ActionError( "Could not determine ptask area from: " + str(spec), ) ptask = None # delay the db query to this point to prevent multiple, unnecessary db # queries. if we're at this point, we know there's at least a # corresponding directory on disk. if ptask_area.base_spec: try: ptask = PTask.get(ptask_area.base_spec) except PTaskError as e: pass if not ptask and ptask_area.spec != "": raise ActionError("Could not determine ptask from: " + str(spec)) ptask_area.set(shell=self.shell, ptask=ptask)
def ptask(self): return PTask.get(self.ptask_spec)
def create_product_before_render(node=None): if not node: node = nuke.thisNode() if not node.knob('product_name') or not node.knob('product_desc'): raise Exception("The supplied node is not a WriteProduct node.") print "Creating product for write node... " + str(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 = node['file_type'].value() if not file_type: file_type = 'exr' product_name = node['product_name'].value() product_desc = node['product_desc'].value() product_ver_note = node['product_ver_note'].value() if not product_desc: raise Exception("Please enter a product description.") width = nuke.value(node.name() + '.width') height = nuke.value(node.name() + '.height') resolution = width + 'x' + 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)) out_path = os.path.join(create_action.product_repr.area.path, product_name + '.####.' + file_type) node['file'].setValue(out_path) return create_action.product_repr
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()
def _print_ptask_env(self): # remove any whitespace on the head/tail of the spec spec = self.spec.strip() ptask_area = None if self.version: spec = PTaskSpec.VERSION.join([spec, str(self.version)]) replace_match = re.match("\.?/([=\w]+)/([=\w]+)/", spec) # handle 'none' as a valid spec - unset current ptask (set it to root) if spec.lower() == 'none': spec = "" full_spec = PTaskSpec.get(spec) try: ptask_area = PTaskArea(full_spec) except: pass # special character '-' indicates use the last set ptask spec elif spec == "-": ptask_area = PTaskArea.previous() # set to a similar ptask with text replacement elif replace_match: cur_area_spec = PTaskArea.current().spec repl_spec = cur_area_spec.replace(replace_match.group(1), replace_match.group(2)) try: ptask_area = PTaskArea(repl_spec) except: pass # use the supplied spec relative to the current ptask else: relative_to = PTaskArea.current().spec while ptask_area is None: try: full_spec = PTaskSpec.get(spec, relative_to=relative_to) except PTaskSpecError as e: raise ActionError(str(e)) try: # if this is successful, we'll break out of the while ptask_area = PTaskArea(full_spec) except PTaskAreaError as e: # no match, check the parent relative spec relative_to = PTaskSpec.parent(relative_to) # there is no parent, break out of the while if relative_to is None: break # dump out commands used for setting the environment for the supplied # spec. if not ptask_area: raise ActionError( "Could not determine ptask area from: " + str(spec), ) ptask = None # delay the db query to this point to prevent multiple, unnecessary db # queries. if we're at this point, we know there's at least a # corresponding directory on disk. if ptask_area.base_spec: try: ptask = PTask.get(ptask_area.base_spec) except PTaskError as e: pass if not ptask and ptask_area.spec != "": raise ActionError("Could not determine ptask from: " + str(spec)) ptask_area.set(shell=self.shell, ptask=ptask)
def validate(self): # validate the ptask if not isinstance(self._ptask, PTask): try: cur_spec = PTaskArea.current().spec full_spec = PTaskSpec.get(self._ptask, relative_to=cur_spec) self._ptask = PTask.get(full_spec) except PTaskError: raise ActionError("Could not determine ptask from: {p}".format( p=self._ptask)) # find the version of the ptask to update if isinstance(self._ptask_version, PTaskVersion): if not self._ptask_version.ptask_spec == self._ptask_spec: raise ActionError( "Supplied ptask version doesn't match supplied ptask.") elif self._ptask_version: matches = PTaskVersion.list( ptask=self._ptask.spec, number=self._ptask_version ) if len(matches) != 1: raise ActionError( "Unable to find ptask '{p}' at version '{v}'".format( p=self._ptask.spec, v=self._ptask_version ) ) else: self._ptask_version = matches[0] else: self._ptask_version = self._ptask.latest_version # XXX rule # don't allow if ptask_version already has published products if self._ptask_version.published: raise ActionError( "Subscriptions can not be modified." + \ "Version {v} of {p} has published products.".format( v=self._ptask_version.number_padded, p=self._ptask.spec )) # XXX subs = [] # valdiate the subscriptions to update if self._subs: # get explicit subs for sub in self._subs: if isinstance(sub, ProductSubscription): subs_to_udpate.append(sub) continue try: sub = int(sub) except: raise ActionError("Could not determine sub from: {s}".\ format(s=sub)) else: matches = ProductSubscription.list(id=sub) if len(matches) != 1: raise ActionError("Unable to identify sub for id: " + \ str(sub)) else: subs.append(matches[0]) else: # all subs for ptask version subs.extend(self._ptask_version.subscriptions) self._subs = subs update_map = defaultdict(dict) for sub in subs: sub_product_ver = sub.product_version update_map[sub.id]['old'] = sub_product_ver if sub.locked: update_map[sub.id]['new'] = None update_map[sub.id]['note'] = 'Subscription locked' continue if sub_product_ver.is_official: update_map[sub.id]['new'] = None update_map[sub.id]['note'] = 'Already subscribed to official' continue sub_product = sub_product_ver.product official_ver = sub_product.official_version if official_ver and official_ver.number > sub_product_ver.number: update_map[sub.id]['new'] = official_ver update_map[sub.id]['note'] = 'Official version' continue if sub.product_version.product.ptask.spec == self.ptask.spec: all_vers = [v for v in sub_product.versions] else: all_vers = [v for v in sub_product.versions if v.published] all_vers.sort(key=lambda v: v.number_padded) if all_vers: latest = all_vers[-1] if latest.number > sub_product_ver.number: update_map[sub.id]['new'] = latest if latest.published: update_map[sub.id]['note'] = 'Latest published version' else: update_map[sub.id]['note'] = 'Latest version' continue else: update_map[sub.id]['new'] = None if sub_product_ver.published: update_map[sub.id]['note'] = \ 'Already using latest published' else: update_map[sub.id]['note'] = 'Already using latest' continue else: update_map[sub.id]['new'] = None update_map[sub.id]['note'] = 'No new versions' continue self._update_map = update_map
def validate(self): # need to identify the product version being subscribed to and the # ptask version subscribing to it. # get the product if not isinstance(self._product, Product): try: self._product = Product.get(self._product) except ProductError: raise ActionError("Unable to find product: " + str(self._product)) # get ptask if not isinstance(self._ptask, PTask): try: cur_spec = PTaskArea.current().spec full_spec = PTaskSpec.get(self._ptask, relative_to=cur_spec) self._ptask = PTask.get(full_spec) except PTaskError: raise ActionError("Unable to find ptask: " + str(self._ptask)) # find the version to subscribe to if isinstance(self._product_version, ProductVersion): pass elif self._product_version: matches = ProductVersion.list(product=self.product.spec, number=int(self._product_version)) if len(matches) != 1: raise ActionError( "Unable to find product '{p}' at version '{v}'".format( p=self.product.spec, v=self._product_version)) else: self._product_version = matches[0] else: # get the official version official_version = self._product.official_version if official_version: self._product_version = official_version else: # get the latest, non-deprecated version latest_published = self._product.latest_published() if latest_published: self._product_version = latest_published else: raise ActionError( "No available versions of product '{p}'".format( p=self._product.spec)) # find the version of the ptask doing the subscribing if isinstance(self._ptask_version, PTaskVersion): pass elif self._ptask_version: matches = PTaskVersion.list(ptask=self._ptask.spec, number=self._ptask_version) if len(matches) != 1: raise ActionError( "Unable to find ptask '{p}' at version '{v}'".format( p=self._ptask.spec, v=self._ptask_version)) else: self._ptask_version = matches[0] else: self._ptask_version = self._ptask.latest_version # XXX the rules below need to be exposed outside of just the create # code. UIs, for example, should be able to check these cases before # allowing the user to perform actions... # if this ptask has any existing published versions, error out published = ProductVersion.list(ptask_version=self._ptask_version, published=True) if len(published) > 0: raise ActionError( "Unable to create new subscription. This ptask version " + \ "already has published product versions.\n" + \ "You need to version up to modify subscriptions." ) # see if there is an existing subscription: self._existing_sub = self._ptask_version.is_subscribed(self._product) if self._existing_sub: if (self._existing_sub.product_version_spec == \ self._product_version.spec): raise ActionError("Subscription already exists!") if self._product_version.deprecated: raise ActionError( "Product version is deprecated. Specify an alternate version.") # make sure product is published or from same ptask if (not self._product_version.published and not self._product.ptask_spec == self._ptask.spec): raise ActionError( "Product version is not published. Specify a published version." )
def validate(self): # need to identify the product version being subscribed to and the # ptask version subscribing to it. # get the product if not isinstance(self._product, Product): try: self._product = Product.get(self._product) except ProductError: raise ActionError("Unable to find product: " + str(self._product)) # get ptask if not isinstance(self._ptask, PTask): try: cur_spec = PTaskArea.current().spec full_spec = PTaskSpec.get(self._ptask, relative_to=cur_spec) self._ptask = PTask.get(full_spec) except PTaskError: raise ActionError("Unable to find ptask: " + str(self._ptask)) # find the version to subscribe to if isinstance(self._product_version, ProductVersion): pass elif self._product_version: matches = ProductVersion.list(product=self.product.spec, number=int(self._product_version)) if len(matches) != 1: raise ActionError( "Unable to find product '{p}' at version '{v}'".format(p=self.product.spec, v=self._product_version) ) else: self._product_version = matches[0] else: # get the official version official_version = self._product.official_version if official_version: self._product_version = official_version else: # get the latest, non-deprecated version latest_published = self._product.latest_published() if latest_published: self._product_version = latest_published else: raise ActionError("No available versions of product '{p}'".format(p=self._product.spec)) # find the version of the ptask doing the subscribing if isinstance(self._ptask_version, PTaskVersion): pass elif self._ptask_version: matches = PTaskVersion.list(ptask=self._ptask.spec, number=self._ptask_version) if len(matches) != 1: raise ActionError( "Unable to find ptask '{p}' at version '{v}'".format(p=self._ptask.spec, v=self._ptask_version) ) else: self._ptask_version = matches[0] else: self._ptask_version = self._ptask.latest_version # XXX the rules below need to be exposed outside of just the create # code. UIs, for example, should be able to check these cases before # allowing the user to perform actions... # if this ptask has any existing published versions, error out published = ProductVersion.list(ptask_version=self._ptask_version, published=True) if len(published) > 0: raise ActionError( "Unable to create new subscription. This ptask version " + "already has published product versions.\n" + "You need to version up to modify subscriptions." ) # see if there is an existing subscription: self._existing_sub = self._ptask_version.is_subscribed(self._product) if self._existing_sub: if self._existing_sub.product_version_spec == self._product_version.spec: raise ActionError("Subscription already exists!") if self._product_version.deprecated: raise ActionError("Product version is deprecated. Specify an alternate version.") # make sure product is published or from same ptask if not self._product_version.published and not self._product.ptask_spec == self._ptask.spec: raise ActionError("Product version is not published. Specify a published version.")
def execute(self): ptasks = [] # search the ptasks for specs matching the supplied string if PTaskSpec.WILDCARD in self.wild_spec: search_str = ",".join( filter(None, self.wild_spec.strip().split(PTaskSpec.WILDCARD) ) ) if not search_str: raise ActionError( "Search is too broad. " + \ "Please supply a string to search against." ) try: # XXX this is inefficient. need better filtering on the backend ptasks = PTask.list(search=search_str) except PTaskError: pass else: try: ptasks.append(PTask.get(self.wild_spec)) except PTaskError: pass matching_ptasks = [] # the rest api's search filter isn't that great. it doesn't maintain any # knowledge of order for the supplied filters. So, it will return ptasks # that match all of the search terms, but not necessarily in the order # supplied. Do one more match against the returned ptasks specs keeping # the order of the supplied wildcard spec. regex_spec = "^" + \ self.wild_spec.replace(PTaskSpec.WILDCARD, "([\w=]+)?") + "$" regex_spec = re.compile(regex_spec) for ptask in ptasks: if regex_spec.match(ptask.spec): matching_ptasks.append(ptask) match_count = len(matching_ptasks) if match_count == 0: print '\nFound 0 ptasks matching: "{s}"\n'.format(s=self.wild_spec) return # define the fields names spec = "Spec" ptask_type = "Type" status = "Status" # define the look of the output output = Output() output.vertical_separator = None output.table_cell_separator = ' ' output.table_header_separator = '-' if match_count == 1: output.title = "{s}: 1 match".format(s=self.wild_spec) else: output.title = "{s}: {n} matches".format( s=self.wild_spec, n=match_count) # display order of the information output.header_names = [ spec, ptask_type, status, ] for ptask in sorted(matching_ptasks, key=lambda p: p.spec): # add all the information output.add_item( { spec: ptask.spec, ptask_type: ptask.ptask_type, status: ptask.status, }, colors={ spec: Style.bright, } ) # dump the output as a list of key/value pairs output.dump(output_format='table')