[2] | 1 | import logging, datetime |
---|
| 2 | |
---|
| 3 | from galaxy.util.json import to_json_string |
---|
| 4 | |
---|
| 5 | # For email notification PJA |
---|
| 6 | from email.MIMEText import MIMEText |
---|
| 7 | import smtplib |
---|
| 8 | |
---|
| 9 | log = logging.getLogger( __name__ ) |
---|
| 10 | |
---|
| 11 | # DBTODO This still needs refactoring and general cleanup. |
---|
| 12 | |
---|
| 13 | def get_form_template(action_type, title, content, help, on_output = True ): |
---|
| 14 | if on_output: |
---|
| 15 | form = """ |
---|
| 16 | if (pja.action_type == "%s"){ |
---|
| 17 | p_str = "<div class='pjaForm toolForm'><span class='action_tag' style='display:none'>"+ pja.action_type + pja.output_name + "</span><div class='toolFormTitle'> %s <br/> on " + pja.output_name + "\ |
---|
| 18 | <div style='float: right;' class='buttons'><img src='/static/images/delete_icon.png'></div></div><div class='toolFormBody'>"; |
---|
| 19 | %s |
---|
| 20 | p_str += "</div><div class='toolParamHelp'>%s</div></div>"; |
---|
| 21 | }""" % (action_type, title, content, help) |
---|
| 22 | else: |
---|
| 23 | form = """ |
---|
| 24 | if (pja.action_type == "%s"){ |
---|
| 25 | p_str = "<div class='pjaForm toolForm'><span class='action_tag' style='display:none'>"+ pja.action_type + "</span><div class='toolFormTitle'> %s \ |
---|
| 26 | <div style='float: right;' class='buttons'><img src='/static/images/delete_icon.png'></div></div><div class='toolFormBody'>"; |
---|
| 27 | %s |
---|
| 28 | p_str += "</div><div class='toolParamHelp'>%s</div></div>"; |
---|
| 29 | }""" % (action_type, title, content, help) |
---|
| 30 | return form |
---|
| 31 | |
---|
| 32 | # def get_field(action, argument, i_type, label = None): |
---|
| 33 | # fstr = '' |
---|
| 34 | # fname = """pja__"+pja.output_name+"__%s__%s""" % (action, argument) |
---|
| 35 | # if label: |
---|
| 36 | # fstr += """<label for='pja__"+pja.output_name+"__ColumnSetAction__chromCol'>Chrom Column</label>""" |
---|
| 37 | # fstr += """<input type='text' value=" + chromCol + " name='pja__"+pja.output_name+"__ColumnSetAction__chromCol'/>""" |
---|
| 38 | |
---|
| 39 | class DefaultJobAction(object): |
---|
| 40 | name = "DefaultJobAction" |
---|
| 41 | verbose_name = "Default Job" |
---|
| 42 | |
---|
| 43 | |
---|
| 44 | @classmethod |
---|
| 45 | def execute(cls, app, sa_session, action, job): |
---|
| 46 | pass |
---|
| 47 | |
---|
| 48 | @classmethod |
---|
| 49 | def get_config_form(cls, trans): |
---|
| 50 | return "<p>Default Job Action Config Form</p>" |
---|
| 51 | |
---|
| 52 | @classmethod |
---|
| 53 | def get_short_str(cls, pja): |
---|
| 54 | if pja.action_arguments: |
---|
| 55 | return "%s -> %s" % (pja.action_type, pja.action_arguments) |
---|
| 56 | else: |
---|
| 57 | return "%s" % pja.action_type |
---|
| 58 | |
---|
| 59 | |
---|
| 60 | class EmailAction(DefaultJobAction): |
---|
| 61 | name = "EmailAction" |
---|
| 62 | verbose_name = "Email Notification" |
---|
| 63 | |
---|
| 64 | |
---|
| 65 | @classmethod |
---|
| 66 | def execute(cls, app, sa_session, action, job): |
---|
| 67 | smtp_server = app.config.smtp_server |
---|
| 68 | if action.action_arguments: |
---|
| 69 | if action.action_arguments.has_key('host'): |
---|
| 70 | host = action.action_arguments['host'] |
---|
| 71 | else: |
---|
| 72 | host = 'usegalaxy.org' |
---|
| 73 | if smtp_server is None: |
---|
| 74 | log.error("Mail is not configured for this galaxy instance. Workflow action aborting after logging mail to info.") |
---|
| 75 | frm = 'galaxy-noreply@%s' % host |
---|
| 76 | to = job.user.email |
---|
| 77 | outdata = ', '.join(ds.dataset.display_name() for ds in job.output_datasets) |
---|
| 78 | msg = MIMEText( "Your Galaxy job generating dataset '%s' is complete as of %s." % (outdata, datetime.datetime.now().strftime( "%I:%M" ))) |
---|
| 79 | msg[ 'To' ] = to |
---|
| 80 | msg[ 'From' ] = frm |
---|
| 81 | msg[ 'Subject' ] = "Galaxy notification regarding history '%s'" % (job.history.name) |
---|
| 82 | log.info(msg) |
---|
| 83 | return |
---|
| 84 | # Build the email message |
---|
| 85 | frm = 'galaxy-noreply@%s' % host |
---|
| 86 | to = job.user.email |
---|
| 87 | outdata = ', '.join(ds.dataset.display_name() for ds in job.output_datasets) |
---|
| 88 | msg = MIMEText( "Your Galaxy job generating dataset '%s' is complete as of %s." % (outdata, datetime.datetime.now().strftime( "%I:%M" ))) |
---|
| 89 | msg[ 'To' ] = to |
---|
| 90 | msg[ 'From' ] = frm |
---|
| 91 | msg[ 'Subject' ] = "Galaxy workflow step notification '%s'" % (job.history.name) |
---|
| 92 | try: |
---|
| 93 | s = smtplib.SMTP() |
---|
| 94 | s.connect( smtp_server ) |
---|
| 95 | s.sendmail( frm, [ to ], msg.as_string() ) |
---|
| 96 | s.close() |
---|
| 97 | except Exception, e: |
---|
| 98 | log.error("EmailAction PJA Failed, exception: %s" % e) |
---|
| 99 | |
---|
| 100 | @classmethod |
---|
| 101 | def get_config_form(cls, trans): |
---|
| 102 | form = """ |
---|
| 103 | p_str += "<label for='pja__"+pja.output_name+"__EmailAction'>There are no additional options for this action. You will be emailed upon job completion.</label>\ |
---|
| 104 | <input type='hidden' value='%s' name='pja__"+pja.output_name+"__EmailAction__host'/><input type='hidden' name='pja__"+pja.output_name+"__EmailAction'/>"; |
---|
| 105 | """ % trans.request.host |
---|
| 106 | 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) |
---|
| 107 | |
---|
| 108 | @classmethod |
---|
| 109 | def get_short_str(cls, pja): |
---|
| 110 | if pja.action_arguments: |
---|
| 111 | if pja.action_arguments.has_key('host'): |
---|
| 112 | return "Email the current user from server %s when this job is complete." % pja.action_arguments['host'] |
---|
| 113 | else: |
---|
| 114 | return "Email the current user when this job is complete." |
---|
| 115 | |
---|
| 116 | |
---|
| 117 | class ChangeDatatypeAction(DefaultJobAction): |
---|
| 118 | name = "ChangeDatatypeAction" |
---|
| 119 | verbose_name = "Change Datatype" |
---|
| 120 | @classmethod |
---|
| 121 | def execute(cls, app, sa_session, action, job): |
---|
| 122 | for dataset_assoc in job.output_datasets: |
---|
| 123 | if action.output_name == '' or dataset_assoc.name == action.output_name: |
---|
| 124 | app.datatypes_registry.change_datatype( dataset_assoc.dataset, action.action_arguments['newtype']) |
---|
| 125 | |
---|
| 126 | @classmethod |
---|
| 127 | def get_config_form(cls, trans): |
---|
| 128 | dt_list = "" |
---|
| 129 | dtnames = [ dtype_name for dtype_name, dtype_value in trans.app.datatypes_registry.datatypes_by_extension.iteritems()] |
---|
| 130 | dtnames.sort() |
---|
| 131 | for dt_name in dtnames: |
---|
| 132 | dt_list += """<option id='pja__"+pja.output_name+"__ChangeDatatypeAction__newtype__%s' value='%s'>%s</option>""" % (dt_name, dt_name, dt_name) |
---|
| 133 | ps = """ |
---|
| 134 | p_str += "<label for='pja__"+pja.output_name+"__ChangeDatatypeAction__newtype'>New Datatype:</label>\ |
---|
| 135 | <select id='pja__"+pja.output_name+"__ChangeDatatypeAction__newtype' name='pja__"+pja.output_name+"__ChangeDatatypeAction__newtype'>\ |
---|
| 136 | %s\ |
---|
| 137 | </select>"; |
---|
| 138 | if (pja.action_arguments != undefined && pja.action_arguments.newtype != undefined){ |
---|
| 139 | p_str += "<scrip" + "t type='text/javascript'>$('#pja__" + pja.output_name + "__ChangeDatatypeAction__newtype').val('" + pja.action_arguments.newtype + "');</scrip" + "t>"; |
---|
| 140 | } |
---|
| 141 | """ % dt_list |
---|
| 142 | # Note the scrip + t hack above. Is there a better way? |
---|
| 143 | return get_form_template(cls.name, cls.verbose_name, ps, 'This action will change the datatype of the output to the indicated value.') |
---|
| 144 | |
---|
| 145 | @classmethod |
---|
| 146 | def get_short_str(cls, pja): |
---|
| 147 | return "Set the datatype of output '%s' to '%s'" % (pja.output_name, pja.action_arguments['newtype']) |
---|
| 148 | |
---|
| 149 | |
---|
| 150 | class RenameDatasetAction(DefaultJobAction): |
---|
| 151 | name = "RenameDatasetAction" |
---|
| 152 | verbose_name = "Rename Dataset" |
---|
| 153 | |
---|
| 154 | @classmethod |
---|
| 155 | def execute(cls, app, sa_session, action, job): |
---|
| 156 | for dataset_assoc in job.output_datasets: |
---|
| 157 | if action.output_name == '' or dataset_assoc.name == action.output_name: |
---|
| 158 | dataset_assoc.dataset.name = action.action_arguments['newname'] |
---|
| 159 | |
---|
| 160 | @classmethod |
---|
| 161 | def get_config_form(cls, trans): |
---|
| 162 | form = """ |
---|
| 163 | if ((pja.action_arguments != undefined) && (pja.action_arguments.newname != undefined)){ |
---|
| 164 | p_str += "<label for='pja__"+pja.output_name+"__RenameDatasetAction__newname'>New output name:</label>\ |
---|
| 165 | <input type='text' name='pja__"+pja.output_name+"__RenameDatasetAction__newname' value='"+pja.action_arguments.newname + "'/>"; |
---|
| 166 | } |
---|
| 167 | else{ |
---|
| 168 | p_str += "<label for='pja__"+pja.output_name+"__RenameDatasetAction__newname'>New output name:</label>\ |
---|
| 169 | <input type='text' name='pja__"+pja.output_name+"__RenameDatasetAction__newname' value=''/>"; |
---|
| 170 | } |
---|
| 171 | """ |
---|
| 172 | return get_form_template(cls.name, cls.verbose_name, form, "This action will rename the result dataset.") |
---|
| 173 | |
---|
| 174 | @classmethod |
---|
| 175 | def get_short_str(cls, pja): |
---|
| 176 | return "Rename output '%s' to '%s'." % (pja.output_name, pja.action_arguments['newname']) |
---|
| 177 | |
---|
| 178 | |
---|
| 179 | class HideDatasetAction(DefaultJobAction): |
---|
| 180 | name = "HideDatasetAction" |
---|
| 181 | verbose_name = "Hide Dataset" |
---|
| 182 | |
---|
| 183 | @classmethod |
---|
| 184 | def execute(cls, app, sa_session, action, job): |
---|
| 185 | for dataset_assoc in job.output_datasets: |
---|
| 186 | if action.output_name == '' or dataset_assoc.name == action.output_name: |
---|
| 187 | dataset_assoc.dataset.visible=False |
---|
| 188 | |
---|
| 189 | @classmethod |
---|
| 190 | def get_config_form(cls, trans): |
---|
| 191 | form = """ |
---|
| 192 | p_str += "<label for='pja__"+pja.output_name+"__HideDatasetAction'>There are no additional options for this action.</label>\ |
---|
| 193 | <input type='hidden' name='pja__"+pja.output_name+"__HideDatasetAction'/>"; |
---|
| 194 | """ |
---|
| 195 | return get_form_template(cls.name, cls.verbose_name, form, "This action will hide the result dataset.") |
---|
| 196 | |
---|
| 197 | @classmethod |
---|
| 198 | def get_short_str(cls, trans): |
---|
| 199 | return "Hide this dataset." |
---|
| 200 | |
---|
| 201 | class DeleteDatasetAction(DefaultJobAction): |
---|
| 202 | # 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. |
---|
| 203 | name = "DeleteDatasetAction" |
---|
| 204 | verbose_name = "Delete Dataset" |
---|
| 205 | |
---|
| 206 | @classmethod |
---|
| 207 | def execute(cls, app, sa_session, action, job): |
---|
| 208 | for dataset_assoc in job.output_datasets: |
---|
| 209 | if action.output_name == '' or dataset_assoc.name == action.output_name: |
---|
| 210 | dataset_assoc.dataset.deleted=True |
---|
| 211 | |
---|
| 212 | @classmethod |
---|
| 213 | def get_config_form(cls, trans): |
---|
| 214 | form = """ |
---|
| 215 | p_str += "<label for='pja__"+pja.output_name+"__DeleteDatasetAction'>There are no additional options for this action. This dataset will be marked deleted.</label>\ |
---|
| 216 | <input type='hidden' name='pja__"+pja.output_name+"__DeleteDatasetAction'/>"; |
---|
| 217 | """ |
---|
| 218 | return get_form_template(cls.name, cls.verbose_name, form, "This action will rename the result dataset.") |
---|
| 219 | |
---|
| 220 | @classmethod |
---|
| 221 | def get_short_str(cls, pja): |
---|
| 222 | return "Delete this dataset after creation." |
---|
| 223 | |
---|
| 224 | |
---|
| 225 | |
---|
| 226 | class ColumnSetAction(DefaultJobAction): |
---|
| 227 | name = "ColumnSetAction" |
---|
| 228 | verbose_name = "Assign Columns" |
---|
| 229 | @classmethod |
---|
| 230 | def execute(cls, app, sa_session, action, job): |
---|
| 231 | for dataset_assoc in job.output_datasets: |
---|
| 232 | if action.output_name == '' or dataset_assoc.name == action.output_name: |
---|
| 233 | for k, v in action.action_arguments.items(): |
---|
| 234 | if v != '': |
---|
| 235 | # Try to use both pure integer and 'cX' format. |
---|
| 236 | if v[0] == 'c': |
---|
| 237 | v = v[1:] |
---|
| 238 | v = int(v) |
---|
| 239 | if v != 0: |
---|
| 240 | setattr(dataset_assoc.dataset.metadata, k, v) |
---|
| 241 | |
---|
| 242 | @classmethod |
---|
| 243 | def get_config_form(cls, trans): |
---|
| 244 | form = """ |
---|
| 245 | var chrom_col = '' |
---|
| 246 | if (pja.action_arguments != undefined){ |
---|
| 247 | (pja.action_arguments.chromCol == undefined) ? chromCol = "" : chromCol=pja.action_arguments.chromCol; |
---|
| 248 | (pja.action_arguments.startCol == undefined) ? startCol = "" : startCol=pja.action_arguments.startCol; |
---|
| 249 | (pja.action_arguments.endCol == undefined) ? endCol = "" : endCol=pja.action_arguments.endCol; |
---|
| 250 | (pja.action_arguments.strandCol == undefined) ? strandCol = "" : strandCol=pja.action_arguments.strandCol; |
---|
| 251 | (pja.action_arguments.nameCol == undefined) ? nameCol = "" : nameCol=pja.action_arguments.nameCol; |
---|
| 252 | }else{ |
---|
| 253 | chromCol = ''; |
---|
| 254 | startCol = ''; |
---|
| 255 | endCol = ''; |
---|
| 256 | strandCol = ''; |
---|
| 257 | nameCol = ''; |
---|
| 258 | } |
---|
| 259 | p_str += "<p>Leave any of these fields blank if they do not need to be set.</p>\ |
---|
| 260 | <label for='pja__"+pja.output_name+"__ColumnSetAction__chromCol'>Chrom Column</label>\ |
---|
| 261 | <input type='text' value='" + chromCol + "' name='pja__"+pja.output_name+"__ColumnSetAction__chromCol'/>\ |
---|
| 262 | <label for='pja__"+pja.output_name+"__ColumnSetAction__startCol'>Start Column</label>\ |
---|
| 263 | <input type='text' value='" + startCol + "' name='pja__"+pja.output_name+"__ColumnSetAction__startCol'/>\ |
---|
| 264 | <label for='pja__"+pja.output_name+"__ColumnSetAction__endCol'>End Column</label>\ |
---|
| 265 | <input type='text' value='" + endCol + "' name='pja__"+pja.output_name+"__ColumnSetAction__endCol'/>\ |
---|
| 266 | <label for='pja__"+pja.output_name+"__ColumnSetAction__strandCol'>Strand Column</label>\ |
---|
| 267 | <input type='text' value='" + strandCol + "' name='pja__"+pja.output_name+"__ColumnSetAction__strandCol'/>\ |
---|
| 268 | <label for='pja__"+pja.output_name+"__ColumnSetAction__nameCol'>Name Column</label>\ |
---|
| 269 | <input type='text' value='" + nameCol + "' name='pja__"+pja.output_name+"__ColumnSetAction__nameCol'/>\"; |
---|
| 270 | """ |
---|
| 271 | return get_form_template(cls.name, cls.verbose_name, form, "This action will set column assignments in the output dataset. Blank fields are ignored.") |
---|
| 272 | |
---|
| 273 | @classmethod |
---|
| 274 | def get_short_str(cls, pja): |
---|
| 275 | return "Set the following metadata values:<br/>" + "<br/>".join(['%s : %s' % (k, v) for k, v in pja.action_arguments.iteritems()]) |
---|
| 276 | |
---|
| 277 | |
---|
| 278 | class SetMetadataAction(DefaultJobAction): |
---|
| 279 | name = "SetMetadataAction" |
---|
| 280 | # DBTODO Setting of Metadata is currently broken and disabled. It should not be used (yet). |
---|
| 281 | |
---|
| 282 | @classmethod |
---|
| 283 | def execute(cls, app, sa_session, action, job): |
---|
| 284 | for data in job.output_datasets: |
---|
| 285 | data.set_metadata( action.action_arguments['newtype'] ) |
---|
| 286 | |
---|
| 287 | @classmethod |
---|
| 288 | def get_config_form(cls, trans): |
---|
| 289 | # dt_list = "" |
---|
| 290 | # mdict = {} |
---|
| 291 | # for dtype_name, dtype_value in trans.app.datatypes_registry.datatypes_by_extension.iteritems(): |
---|
| 292 | # for mn, mt in dtype_value.metadata_spec.items(): |
---|
| 293 | # if mt.visible: |
---|
| 294 | # mdict[mt.desc] = mt.param.get_html(value= mn).replace('"', "'").strip().replace('\n','') |
---|
| 295 | # for k, v in mdict.items(): |
---|
| 296 | # dt_list += "<p><strong>" + k + ":</strong><br/>" + v + "</p>" |
---|
| 297 | # form = """ |
---|
| 298 | # p_str += "%s"; |
---|
| 299 | # """ % dt_list |
---|
| 300 | # return get_form_template('SetMetadataAction', 'Set Metadata', form, "This action will change metadata for the dataset.") |
---|
| 301 | form = """ |
---|
| 302 | p_str += "<p>Leave any of these fields blank if they do not need to be set.</p><label for='pja__"+pja.output_name+"__SetMetadataAction__chromCol'>Chrom Column</label>\ |
---|
| 303 | <input type='text' name='pja__"+pja.output_name+"__SetMetadataAction__chromCol'/>\ |
---|
| 304 | <label for='pja__"+pja.output_name+"__SetMetadataAction__startCol'>Start Column</label>\ |
---|
| 305 | <input type='text' name='pja__"+pja.output_name+"__SetMetadataAction__startCol'/>\ |
---|
| 306 | <label for='pja__"+pja.output_name+"__SetMetadataAction__endCol'>End Column</label>\ |
---|
| 307 | <input type='text' name='pja__"+pja.output_name+"__SetMetadataAction__endCol'/>\ |
---|
| 308 | <label for='pja__"+pja.output_name+"__SetMetadataAction__comment_lines'>Comment Lines</label>\ |
---|
| 309 | <input type='text' name='pja__"+pja.output_name+"__SetMetadataAction__comment_lines'/>\ |
---|
| 310 | "; |
---|
| 311 | """ |
---|
| 312 | return get_form_template(cls.name, cls.verbose_name, form, "This action will set metadata in the output dataset.") |
---|
| 313 | |
---|
| 314 | |
---|
| 315 | |
---|
| 316 | class ActionBox(object): |
---|
| 317 | |
---|
| 318 | actions = { "RenameDatasetAction" : RenameDatasetAction, |
---|
| 319 | "HideDatasetAction" : HideDatasetAction, |
---|
| 320 | "ChangeDatatypeAction": ChangeDatatypeAction, |
---|
| 321 | "ColumnSetAction" : ColumnSetAction, |
---|
| 322 | "EmailAction" : EmailAction, |
---|
| 323 | # "SetMetadataAction" : SetMetadataAction, |
---|
| 324 | # "DeleteDatasetAction" : DeleteDatasetAction, |
---|
| 325 | } |
---|
| 326 | immediate_actions = ['ChangeDatatypeAction', 'RenameDatasetAction'] |
---|
| 327 | |
---|
| 328 | @classmethod |
---|
| 329 | def get_short_str(cls, action): |
---|
| 330 | if action.action_type in ActionBox.actions: |
---|
| 331 | return ActionBox.actions[action.action_type].get_short_str(action) |
---|
| 332 | else: |
---|
| 333 | return "Unknown Action" |
---|
| 334 | |
---|
| 335 | @classmethod |
---|
| 336 | def handle_incoming(cls, incoming): |
---|
| 337 | npd = {} |
---|
| 338 | for key, val in incoming.iteritems(): |
---|
| 339 | if key.startswith('pja'): |
---|
| 340 | sp = key.split('__') |
---|
| 341 | ao_key = sp[2] + sp[1] |
---|
| 342 | # flag / output_name / pjatype / desc |
---|
| 343 | if not ao_key in npd: |
---|
| 344 | npd[ao_key] = {'action_type' : sp[2], |
---|
| 345 | 'output_name' : sp[1], |
---|
| 346 | 'action_arguments' : {}} |
---|
| 347 | if len(sp) > 3: |
---|
| 348 | if sp[3] == 'output_name': |
---|
| 349 | npd[ao_key]['output_name'] = val |
---|
| 350 | else: |
---|
| 351 | npd[ao_key]['action_arguments'][sp[3]] = val |
---|
| 352 | else: |
---|
| 353 | # Not pja stuff. |
---|
| 354 | pass |
---|
| 355 | return to_json_string(npd) |
---|
| 356 | |
---|
| 357 | @classmethod |
---|
| 358 | def get_add_list(cls): |
---|
| 359 | addlist = "<select id='new_pja_list' name='new_pja_list'>" |
---|
| 360 | for action in ActionBox.actions: |
---|
| 361 | addlist += "<option value='%s'>%s</option>" % (ActionBox.actions[action].name, ActionBox.actions[action].verbose_name) |
---|
| 362 | addlist += "</select>" |
---|
| 363 | return addlist |
---|
| 364 | |
---|
| 365 | @classmethod |
---|
| 366 | def get_forms(cls, trans): |
---|
| 367 | forms = "" |
---|
| 368 | for action in ActionBox.actions: |
---|
| 369 | forms += ActionBox.actions[action].get_config_form(trans) |
---|
| 370 | return forms |
---|
| 371 | |
---|
| 372 | @classmethod |
---|
| 373 | def execute(cls, app, sa_session, pja, job): |
---|
| 374 | if ActionBox.actions.has_key(pja.action_type): |
---|
| 375 | ActionBox.actions[pja.action_type].execute(app, sa_session, pja, job) |
---|