import gtk import sys import os import inspect from system import logger def _workflow_classes(modname): """Returns a list of all subclasses of Workflow in a given module""" workflow_classes = [] __import__('workflows.%s' % modname) module = sys.modules['workflows.%s' % modname] d = module.__dict__ for wf in d.values(): try: if issubclass(wf, Workflow): workflow_classes.append(wf) except TypeError, e: pass return workflow_classes def workflow_list(): """Returns a ListStore containing all new workflows""" retval = [] # List all .py files that can contain workflow classes wf_path = sys.modules['workflows'].__path__ wf_files = [] for dir in wf_path: for fn in os.listdir(dir): if fn.endswith('.py') and ('#' not in fn): wf_files.append(fn[:-3]) # Try to load each file and look for Workflow derived classes for fn in wf_files: try: for wf in _workflow_classes(fn): retval.append(wf) except Exception, e: logger.log('warning', 'Cannot load workflow: %s' % fn) logger.log('warning', e) return retval class Workflow: """Defines a workflow that contains a set of analysis stages. A Workflow is a set of analysis stages for a certain type of analysis. Each stage contains some possible operations to do accomplish that task. """ name = "Workflow" ident = None description = "Workflow Description" def __init__(self, app): self.stages = [] self.stages_by_id = {} self.app = app def add_stage(self, stage): self.stages.append(stage) self.stages_by_id[stage.id] = stage def print_tree(self): print self.name for stage in self.stages: print ' %s' % stage.name for fun in stage.functions: print ' %s' % fun.name def add_project(self,project): if project == None: logger.log('notice','Proejct is empty') logger.log('notice','Project added in : %s' %self.name) self.project = project class EmptyWorkflow(Workflow): name = 'Empty Workflow' def __init__(self, app): Workflow.__init__(self, None) class Stage: """A stage is a part of the data analysis process. Each stage contains a set of functions that can be used to accomplish the task. A typical early stage is 'preprocessing', which can be done in several ways, each represented by a function. """ def __init__(self, id, name): self.id = id self.name = name self.functions = [] self.functions_by_id = {} def add_function(self, fun): self.functions.append(fun) self.functions_by_id[fun.id] = fun class Function: """A Function object encapsulates a function on a data set. Each Function instance encapsulates some function that can be applied to one or more types of data. """ def __init__(self, id, name): self.id = id self.name = name # ,ust return a Validation object def validate_input(input): return Validation(True,"Validation Not Implemented") def run(self): pass class Validation: def __init__(self,result, reason): self.succeeded = result self.reason = reason class WorkflowView (gtk.VBox): def __init__(self, wf): gtk.VBox.__init__(self) self.workflow = wf self.setup_workflow(wf) def setup_workflow(self, wf): # Add stage in the process for stage in wf.stages: exp = gtk.Expander(stage.name) btn_box = gtk.VBox() btn_box.show() exp.add(btn_box) # Add functions in each stage for fun in stage.functions: btn = gtk.Button(fun.name) btn.connect('clicked', lambda button, f=fun : self.run_function(f)) btn_box.add(btn) btn.show() exp.show() self.pack_start(exp, expand=False, fill=False) def remove_workflow(self): for c in self.get_children(): c.hide() self.remove(c) def set_workflow(self, workflow): self.workflow = workflow self.remove_workflow() self.setup_workflow(workflow) def run_function(self, function): logger.log('debug', 'Starting function: %s' % function.name) project = self.workflow.app.project parent_data = project.current_data validation = function.validate_input() if not validation.succeeded: logger.log('warning','Invalid Inputdata: ' + str(reason)) return args, varargs, varkw, defaults = inspect.getargspec(function.run) # first argument is 'self' and second should be the selection # and we don't care about those... args.remove('self') if "selection" in args: pass_selection = True args.remove('selection') else: pass_selection = False if varargs and len(parent_data) < len(args): logger.log('warning', "Function requires minimum %d datasets selected." % len(args)) return elif not varargs and args and len(args) != len(parent_data): # functions requiring datasets have to have the right number logger.log('warning', "Function requires %d datasets, but only %d selected." % (len(args), len(parent_data))) return if not args: # we allow functions requiring no data to be run even if a # dataset is is selected data = [] else: data = parent_data if pass_selection: # if the function has a 'selection' argument, we pass in # the selection new_data = function.run(selection=project.get_selection(), *data) else: new_data = function.run(*data) if new_data != None: project.add_data(parent_data, new_data, function.name) logger.log('debug', 'Function ended: %s' % function.name)