import logging, datetime from galaxy.util.json import to_json_string # For email notification PJA from email.MIMEText import MIMEText import smtplib log = logging.getLogger( __name__ ) # DBTODO This still needs refactoring and general cleanup. def get_form_template(action_type, title, content, help, on_output = True ): if on_output: form = """ if (pja.action_type == "%s"){ p_str = "
%s
on " + pja.output_name + "\
"; %s p_str += "
%s
"; }""" % (action_type, title, content, help) else: form = """ if (pja.action_type == "%s"){ p_str = "
%s \
"; %s p_str += "
%s
"; }""" % (action_type, title, content, help) return form # def get_field(action, argument, i_type, label = None): # fstr = '' # fname = """pja__"+pja.output_name+"__%s__%s""" % (action, argument) # if label: # fstr += """""" # fstr += """""" class DefaultJobAction(object): name = "DefaultJobAction" verbose_name = "Default Job" @classmethod def execute(cls, app, sa_session, action, job): pass @classmethod def get_config_form(cls, trans): return "

Default Job Action Config Form

" @classmethod def get_short_str(cls, pja): if pja.action_arguments: return "%s -> %s" % (pja.action_type, pja.action_arguments) else: return "%s" % pja.action_type class EmailAction(DefaultJobAction): name = "EmailAction" verbose_name = "Email Notification" @classmethod def execute(cls, app, sa_session, action, job): smtp_server = app.config.smtp_server if action.action_arguments: if action.action_arguments.has_key('host'): host = action.action_arguments['host'] else: host = 'usegalaxy.org' if smtp_server is None: log.error("Mail is not configured for this galaxy instance. Workflow action aborting after logging mail to info.") frm = 'galaxy-noreply@%s' % host to = job.user.email outdata = ', '.join(ds.dataset.display_name() for ds in job.output_datasets) msg = MIMEText( "Your Galaxy job generating dataset '%s' is complete as of %s." % (outdata, datetime.datetime.now().strftime( "%I:%M" ))) msg[ 'To' ] = to msg[ 'From' ] = frm msg[ 'Subject' ] = "Galaxy notification regarding history '%s'" % (job.history.name) log.info(msg) return # Build the email message frm = 'galaxy-noreply@%s' % host to = job.user.email outdata = ', '.join(ds.dataset.display_name() for ds in job.output_datasets) msg = MIMEText( "Your Galaxy job generating dataset '%s' is complete as of %s." % (outdata, datetime.datetime.now().strftime( "%I:%M" ))) msg[ 'To' ] = to msg[ 'From' ] = frm msg[ 'Subject' ] = "Galaxy workflow step notification '%s'" % (job.history.name) try: s = smtplib.SMTP() s.connect( smtp_server ) s.sendmail( frm, [ to ], msg.as_string() ) s.close() except Exception, e: log.error("EmailAction PJA Failed, exception: %s" % e) @classmethod def get_config_form(cls, trans): form = """ p_str += "\ "; """ % trans.request.host return get_form_template(cls.name, cls.verbose_name, form, "This action will send an email notifying you when the job is done.", on_output = False) @classmethod def get_short_str(cls, pja): if pja.action_arguments: if pja.action_arguments.has_key('host'): return "Email the current user from server %s when this job is complete." % pja.action_arguments['host'] else: return "Email the current user when this job is complete." class ChangeDatatypeAction(DefaultJobAction): name = "ChangeDatatypeAction" verbose_name = "Change Datatype" @classmethod def execute(cls, app, sa_session, action, job): for dataset_assoc in job.output_datasets: if action.output_name == '' or dataset_assoc.name == action.output_name: app.datatypes_registry.change_datatype( dataset_assoc.dataset, action.action_arguments['newtype']) @classmethod def get_config_form(cls, trans): dt_list = "" dtnames = [ dtype_name for dtype_name, dtype_value in trans.app.datatypes_registry.datatypes_by_extension.iteritems()] dtnames.sort() for dt_name in dtnames: dt_list += """""" % (dt_name, dt_name, dt_name) ps = """ p_str += "\ "; if (pja.action_arguments != undefined && pja.action_arguments.newtype != undefined){ p_str += "$('#pja__" + pja.output_name + "__ChangeDatatypeAction__newtype').val('" + pja.action_arguments.newtype + "');"; } """ % dt_list # Note the scrip + t hack above. Is there a better way? return get_form_template(cls.name, cls.verbose_name, ps, 'This action will change the datatype of the output to the indicated value.') @classmethod def get_short_str(cls, pja): return "Set the datatype of output '%s' to '%s'" % (pja.output_name, pja.action_arguments['newtype']) class RenameDatasetAction(DefaultJobAction): name = "RenameDatasetAction" verbose_name = "Rename Dataset" @classmethod def execute(cls, app, sa_session, action, job): for dataset_assoc in job.output_datasets: if action.output_name == '' or dataset_assoc.name == action.output_name: dataset_assoc.dataset.name = action.action_arguments['newname'] @classmethod def get_config_form(cls, trans): form = """ if ((pja.action_arguments != undefined) && (pja.action_arguments.newname != undefined)){ p_str += "\ "; } else{ p_str += "\ "; } """ return get_form_template(cls.name, cls.verbose_name, form, "This action will rename the result dataset.") @classmethod def get_short_str(cls, pja): return "Rename output '%s' to '%s'." % (pja.output_name, pja.action_arguments['newname']) class HideDatasetAction(DefaultJobAction): name = "HideDatasetAction" verbose_name = "Hide Dataset" @classmethod def execute(cls, app, sa_session, action, job): for dataset_assoc in job.output_datasets: if action.output_name == '' or dataset_assoc.name == action.output_name: dataset_assoc.dataset.visible=False @classmethod def get_config_form(cls, trans): form = """ p_str += "\ "; """ return get_form_template(cls.name, cls.verbose_name, form, "This action will hide the result dataset.") @classmethod def get_short_str(cls, trans): return "Hide this dataset." class DeleteDatasetAction(DefaultJobAction): # This is disabled for right now. Deleting a dataset in the middle of a workflow causes errors (obviously) for the subsequent steps using the data. name = "DeleteDatasetAction" verbose_name = "Delete Dataset" @classmethod def execute(cls, app, sa_session, action, job): for dataset_assoc in job.output_datasets: if action.output_name == '' or dataset_assoc.name == action.output_name: dataset_assoc.dataset.deleted=True @classmethod def get_config_form(cls, trans): form = """ p_str += "\ "; """ return get_form_template(cls.name, cls.verbose_name, form, "This action will rename the result dataset.") @classmethod def get_short_str(cls, pja): return "Delete this dataset after creation." class ColumnSetAction(DefaultJobAction): name = "ColumnSetAction" verbose_name = "Assign Columns" @classmethod def execute(cls, app, sa_session, action, job): for dataset_assoc in job.output_datasets: if action.output_name == '' or dataset_assoc.name == action.output_name: for k, v in action.action_arguments.items(): if v != '': # Try to use both pure integer and 'cX' format. if v[0] == 'c': v = v[1:] v = int(v) if v != 0: setattr(dataset_assoc.dataset.metadata, k, v) @classmethod def get_config_form(cls, trans): form = """ var chrom_col = '' if (pja.action_arguments != undefined){ (pja.action_arguments.chromCol == undefined) ? chromCol = "" : chromCol=pja.action_arguments.chromCol; (pja.action_arguments.startCol == undefined) ? startCol = "" : startCol=pja.action_arguments.startCol; (pja.action_arguments.endCol == undefined) ? endCol = "" : endCol=pja.action_arguments.endCol; (pja.action_arguments.strandCol == undefined) ? strandCol = "" : strandCol=pja.action_arguments.strandCol; (pja.action_arguments.nameCol == undefined) ? nameCol = "" : nameCol=pja.action_arguments.nameCol; }else{ chromCol = ''; startCol = ''; endCol = ''; strandCol = ''; nameCol = ''; } p_str += "

Leave any of these fields blank if they do not need to be set.

\ \ \ \ \ \ \ \ \ \ \"; """ return get_form_template(cls.name, cls.verbose_name, form, "This action will set column assignments in the output dataset. Blank fields are ignored.") @classmethod def get_short_str(cls, pja): return "Set the following metadata values:
" + "
".join(['%s : %s' % (k, v) for k, v in pja.action_arguments.iteritems()]) class SetMetadataAction(DefaultJobAction): name = "SetMetadataAction" # DBTODO Setting of Metadata is currently broken and disabled. It should not be used (yet). @classmethod def execute(cls, app, sa_session, action, job): for data in job.output_datasets: data.set_metadata( action.action_arguments['newtype'] ) @classmethod def get_config_form(cls, trans): # dt_list = "" # mdict = {} # for dtype_name, dtype_value in trans.app.datatypes_registry.datatypes_by_extension.iteritems(): # for mn, mt in dtype_value.metadata_spec.items(): # if mt.visible: # mdict[mt.desc] = mt.param.get_html(value= mn).replace('"', "'").strip().replace('\n','') # for k, v in mdict.items(): # dt_list += "

" + k + ":
" + v + "

" # form = """ # p_str += "%s"; # """ % dt_list # return get_form_template('SetMetadataAction', 'Set Metadata', form, "This action will change metadata for the dataset.") form = """ p_str += "

Leave any of these fields blank if they do not need to be set.

\ \ \ \ \ \ \ \ "; """ return get_form_template(cls.name, cls.verbose_name, form, "This action will set metadata in the output dataset.") class ActionBox(object): actions = { "RenameDatasetAction" : RenameDatasetAction, "HideDatasetAction" : HideDatasetAction, "ChangeDatatypeAction": ChangeDatatypeAction, "ColumnSetAction" : ColumnSetAction, "EmailAction" : EmailAction, # "SetMetadataAction" : SetMetadataAction, # "DeleteDatasetAction" : DeleteDatasetAction, } immediate_actions = ['ChangeDatatypeAction', 'RenameDatasetAction'] @classmethod def get_short_str(cls, action): if action.action_type in ActionBox.actions: return ActionBox.actions[action.action_type].get_short_str(action) else: return "Unknown Action" @classmethod def handle_incoming(cls, incoming): npd = {} for key, val in incoming.iteritems(): if key.startswith('pja'): sp = key.split('__') ao_key = sp[2] + sp[1] # flag / output_name / pjatype / desc if not ao_key in npd: npd[ao_key] = {'action_type' : sp[2], 'output_name' : sp[1], 'action_arguments' : {}} if len(sp) > 3: if sp[3] == 'output_name': npd[ao_key]['output_name'] = val else: npd[ao_key]['action_arguments'][sp[3]] = val else: # Not pja stuff. pass return to_json_string(npd) @classmethod def get_add_list(cls): addlist = "" return addlist @classmethod def get_forms(cls, trans): forms = "" for action in ActionBox.actions: forms += ActionBox.actions[action].get_config_form(trans) return forms @classmethod def execute(cls, app, sa_session, pja, job): if ActionBox.actions.has_key(pja.action_type): ActionBox.actions[pja.action_type].execute(app, sa_session, pja, job)