class MyNode(Node): p1 = Param() p2 = Param() def param_touched(self, param_name): if param_name == 'p1': self.p2.touch()
class ComplexNode(Node): p1 = Param() p2 = Param() def param_touched(self, param_name): if param_name == 'p1': self.p2.touch() child1 = Child(MyNode) child2 = Child(MyNode)
class NamingNode(Node): in_naming = Param() config = Param({}) out_naming = ComputedParam() def _configure(self): super(NamingNode, self)._configure() parent = self.parent() if not parent: raise Exception('NamingNode cannot be a root') # default config is class_name:node_id self.config.set({parent.__class__.__name__:parent.node_id}) # output is for parent _naming: parent._naming.add_source(self.out_naming) # input is from grand parent naming, or empty gparent = parent.parent() if not gparent: self.in_naming.set({}) elif not gparent.has_param('_naming'): self.in_naming.set({'Project':gparent.node_id}) else: self.in_naming.add_source(gparent._naming) def param_touched(self, param_name): if param_name in ('in_naming', 'config'): self.out_naming.touch() def compute(self, param_name): if param_name == 'out_naming': cfg = dict(self.in_naming.get() or {}) config = self.config.get() if config: # Update cfg and remove the key with None value: for k, v in config.items(): if v is None: try: del cfg[k] except KeyError: pass else: cfg[k] = v self.out_naming.set(cfg)
class MyNode(Node): p1 = Param() p2 = Param() mult = ComputedParam() def param_touched(self, param_name): if param_name in ('p1', 'p2'): self.mult.touch() def compute(self, param_name): if param_name == 'mult': p1 = self.p1.get() if not isinstance(p1, int): raise Exception('Invalid value for param p1: must be int') p2 = self.p2.get() if not isinstance(p2, int): raise Exception('Invalid value for param p2: must be int') self.mult.set(p1 * p2)
class TaskGroup(NamedNode): ICON_NAME = 'action' with param_group('Tasks'): tasks_statuses = Param({}, sources_as_dict=True).ui(editor='status_sumary') tasks_progresses = Param({}, sources_as_dict=True) status = ComputedParam() progress = ComputedParam().ui(editor='percent') _status_manager = Child(TaskGroupStatus) # NB: this is a status summary, it does not affect a param notes = Child(NoteThread) def _configure(self): super(TaskGroup, self)._configure() for _, child in self.iterchildren(): if child.has_param('status'): self.tasks_statuses.add_source(child.status) if child.has_param('progress'): self.tasks_progresses.add_source(child.progress) def param_touched(self, param_name): if param_name == 'tasks_progresses': self.progress.touch() elif param_name == 'progress': self.status.touch() def compute(self, param_name): if param_name == 'progress': self.progress.set( self._status_manager.get_average_progress( (self.tasks_progresses.get() or {}).values() ) ) if param_name == 'status': self.status.set( self._status_manager.get_status( self.progress.get() ) )
class NamedNode(Node): with param_group('_Naming', group_index=1000): _naming = Param() _namer = Child(NamingNode) def set_namer_config(self, **config): self._namer.config.set(config) def add_namer_config(self, **config): cfg = self._namer.config.get() or {} cfg.update(config) self._namer.config.set(cfg) def set_namer_from_id(self, key=None): key = key or self.__class__.__name__ self._namer.config.set({key:self.node_id})
class NodeWithRelations(Node): nwr_param = Param() contained = Many(CasedNode)
class CasedNode(Node): node_param = Param() node_case_param = CaseParam(default='Case Param Default') c1 = Child(ChildNode).configure('Param in c1') c2 = Child(ChildNode).configure('Param in c2')
class ChildNode(Node): child_param = Param() def configure(self, value): self.child_param.set(value)
class Scheduled(TaskGroup): ICON_NAME = 'scheduled' AFTER_PREV_END = 'After Previous End' AFTER_PREV_START = 'After Previous Start' FIXED_START = 'Fixed Start Date' SCHEDULE_MODES = ( AFTER_PREV_END, AFTER_PREV_START, FIXED_START, ) with param_group('Planning'): schedule_mode = CaseParam().ui( editor='choice', editor_options={'choices': SCHEDULE_MODES}) fixed_start_date = CaseParam().ui(editor='date') start_offset = CaseParam().ui(editor='int') start_date = ComputedParam().ui(editor='date') work_days = CaseParam().ui(editor='int') end_date = ComputedParam().ui(editor='date') with param_group('Planning Dependencies'): dep_start_dates = Param().ui(editor='date') dep_end_dates = Param().ui(editor='date') with param_group('Assigned'): manager = CaseParam().ui(editor='node_ref', editor_options={'root_name': 'resources'}) lead = CaseParam().ui(editor='node_ref', editor_options={'root_name': 'resources'}) user_groups = CaseParam().ui(editor='node_refs', editor_options={'root_name': 'resources'}) def add_dependency(self, scheduled): self.dep_start_dates.add_source(scheduled.start_date) self.dep_end_dates.add_source(scheduled.end_date) def param_touched(self, param_name): if param_name in ('schedule_mode', 'fixed_start_date', 'start_offset', 'dep_start_dates', 'dep_end_dates'): self.start_date.touch() elif param_name in ('start_date', 'work_days'): self.end_date.touch() else: return super(Scheduled, self).param_touched(param_name) def compute(self, param_name): if param_name == 'start_date': mode = self.schedule_mode.get() if mode == self.FIXED_START: self.start_date.set( self.fixed_start_date.get() + datetime.timedelta(self.start_offset.get() or 0)) elif not mode or mode == self.AFTER_PREV_END: prev_end = self.dep_end_dates.get() if prev_end is None: raise Exception( 'Cannot use schedule mode %r without a dependency.' % (mode, )) self.start_date.set( prev_end + datetime.timedelta(self.start_offset.get() or 0)) elif mode == self.AFTER_PREV_START: prev_start = self.dep_start_dates.get() if prev_start is None: raise Exception( 'Cannot use schedule mode %r without a dependency.' % (mode, )) self.start_date.set( prev_start + datetime.timedelta(self.start_offset.get() or 0)) else: raise Exception('Unknown Schedule Mode %r' % (mode, )) elif param_name == 'end_date': start = self.start_date.get() if start is None: raise Exception('Cannot find end date without start date') self.end_date.set(start + datetime.timedelta(self.work_days.get() or 0)) else: return super(Scheduled, self).compute(param_name)
class BrowseCmdNode(ProcNode): NEEDED_FEATURES = ['kabaret'] ICON_NAME = 'editor_proc' filename = Param().tag('filename') folder = ComputedParam() existing_folder = ComputedParam() def _needs_to_run(self, ups_needs_to_run): # This proc is not intended to be dependent on # upstream procs. It should run whenever requested: return True, 'By User Request' def param_touched(self, param_name): if param_name == 'filename': self.folder.touch() return if param_name == 'folder': self.existing_folder.touch() return super(BrowseCmdNode, self).param_touched(param_name) def compute(self, param_name): if param_name == 'folder': self.folder.set( os.path.dirname(self.filename.get() or '') ) return if param_name == 'existing_folder': p = self.folder.get() deepest_folder = '' while p: if os.path.exists(p): deepest_folder = p break p, _ = os.path.split(p) self.existing_folder.set(p) return super(BrowseCmdNode, self).compute(param_name) def validate_execution_context(self, context): folder = self.folder.get() existing_folder = self.existing_folder.get() if folder == existing_folder: context.allow_exec = True context.why = None return if not context.get_value('Open this instead', ''): context.set_param( 'Open this instead', existing_folder, doc=''' The path %r does not exists, the deepest existing folder is %r. (Clear the value to update the deepest existing folder) '''%(folder, existing_folder)) if not context.has_param('Confirm'): context.set_param( 'Confirm', False, editor='bool', doc='Check this to allow execution' ) context.allow_exec = False context.why = 'Please confirm' else: if context.get_value('Confirm'): context.allow_exec = True context.why = 'Confirmed' else: context.allow_exec = False context.why = 'Please confirm' def process(self, context, worker): if context.has_param('Open this instead'): path = context.get_value('Open this instead') else: path = self.existing_folder.get() # Get the system command and its env: station_class = worker.get_station_class() cmd = self.flow().get_client_system_cmd(station_class, 'BROWSE') if 'window' in station_class.lower(): path = path.replace('/', '\\') additional_env = cmd['additional_env'] additional_env['KABARET_FOCUS_ID'] = repr(self.uid()) # define a launcher function: def run_sys_cmd(executable, args, env, additional_env): import kabaret.core.syscmd cmd = kabaret.core.syscmd.SysCmd( executable, args, env, additional_env ) cmd.execute() # trigger the launcher function on the worker with worker.busy(): worker.send_func(run_sys_cmd) worker.call( 'run_sys_cmd', cmd['executable'], [path], cmd['env'], additional_env )
class FileNode(NamedNode): thumbnail = ComputedParam().tag('preview').ui(editor='thumbnail', group_index=-1, index=-1) with param_group('Previous File'): prev_filename = Param().tag('filename') prev_mtime = Param().ui(editor='timestamp') with param_group('File'): filename = ComputedParam().tag('filename') exists = ComputedParam() mtime = ComputedParam().ui(editor='timestamp') def set_prev_file(self, file_node): self.prev_filename.disconnect() self.prev_filename.add_source(file_node.filename) self.prev_mtime.disconnect() self.prev_mtime.add_source(file_node.mtime) browse = Proc(BrowseCmdNode) def _configure(self): super(FileNode, self)._configure() self.browse.filename.add_source(self.filename) self.set_ticked() def tick(self): ''' Checks for mtime modifications and update it if needed. ''' # Skip tick when it might come from a touch of the params used here: # (it could lead to a loop) if self.exists.is_dirty() or self.mtime.is_dirty() or self.filename.is_dirty(): return # Check file existence first filename = self.filename.get() if not os.path.exists(filename): if self.exists.get(): self.exists.touch() return elif not self.exists.get(): self.exists.touch() return curr_mtime = self.mtime.get() real_mtime = os.stat(filename)[stat.ST_MTIME] if real_mtime != curr_mtime: self.mtime.touch() def param_touched(self, param_name): if param_name == '_naming': self.filename.touch() elif param_name == 'filename': self.exists.touch() self.thumbnail.touch() elif param_name == 'exists': self.mtime.touch() else: super(FileNode, self).param_touched(param_name) def compute(self, param_name): if param_name == 'filename': config = self._naming.get() named = self.flow().get_named(config) self.filename.set(named.path()) elif param_name == 'exists': filename = self.filename.get() if not filename: self.exists.set(False) else: self.exists.set(os.path.exists(filename)) elif param_name == 'mtime': if not self.exists.get(): self.mtime.set(0) else: filename = self.filename.get() self.mtime.set( os.stat(filename)[stat.ST_MTIME] ) elif param_name == 'thumbnail': dir, name = os.path.split(self.filename.get()) thumbnail = os.path.join(dir, '.thumbnails', name+'.png') self.thumbnail.set(thumbnail)