PageRenderTime 44ms CodeModel.GetById 18ms app.highlight 21ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/galaxy/jobs/actions/post.py

https://bitbucket.org/cistrome/cistrome-harvard/
Python | 422 lines | 414 code | 5 blank | 3 comment | 11 complexity | 1dc61a4aaf4c894bea57c4bf275dfc37 MD5 | raw file
  1import datetime
  2import logging
  3from galaxy.util import send_mail
  4from galaxy.util.json import to_json_string
  5
  6log = logging.getLogger( __name__ )
  7
  8# DBTODO This still needs refactoring and general cleanup.
  9
 10def get_form_template(action_type, title, content, help, on_output = True ):
 11    if on_output:
 12        form = """
 13            if (pja.action_type == "%s"){
 14                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 + "\
 15                <div style='float: right;' class='buttons'><img src='/static/images/history-buttons/delete_icon.png'></div></div><div class='toolFormBody'>";
 16                %s
 17                p_str += "</div><div class='toolParamHelp'>%s</div></div>";
 18            }""" % (action_type, title, content, help)
 19    else:
 20        form =  """
 21            if (pja.action_type == "%s"){
 22                p_str = "<div class='pjaForm toolForm'><span class='action_tag' style='display:none'>"+ pja.action_type + "</span><div class='toolFormTitle'> %s \
 23                <div style='float: right;' class='buttons'><img src='/static/images/history-buttons/delete_icon.png'></div></div><div class='toolFormBody'>";
 24                %s
 25                p_str += "</div><div class='toolParamHelp'>%s</div></div>";
 26            }""" % (action_type, title, content, help)
 27    return form
 28
 29# def get_field(action, argument, i_type, label = None):
 30#     fstr = ''
 31#     fname = """pja__"+pja.output_name+"__%s__%s""" % (action, argument)
 32#     if label:
 33#         fstr += """<label for='pja__"+pja.output_name+"__ColumnSetAction__chromCol'>Chrom Column</label>"""
 34#     fstr += """<input type='text' value=" + chromCol + " name='pja__"+pja.output_name+"__ColumnSetAction__chromCol'/>"""
 35
 36class DefaultJobAction(object):
 37    name = "DefaultJobAction"
 38    verbose_name = "Default Job"
 39
 40    @classmethod
 41    def execute(cls, app, sa_session, action, job, replacement_dict = None):
 42        pass
 43
 44    @classmethod
 45    def get_config_form(cls, trans):
 46        return "<p>Default Job Action Config Form</p>"
 47
 48    @classmethod
 49    def get_short_str(cls, pja):
 50        if pja.action_arguments:
 51            return "%s -> %s" % (pja.action_type, pja.action_arguments)
 52        else:
 53            return "%s" % pja.action_type
 54
 55
 56class EmailAction(DefaultJobAction):
 57    name = "EmailAction"
 58    verbose_name = "Email Notification"
 59
 60    @classmethod
 61    def execute(cls, app, sa_session, action, job, replacement_dict):
 62        if action.action_arguments and action.action_arguments.has_key('host'):
 63            host = action.action_arguments['host']
 64        else:
 65            host = 'usegalaxy.org'
 66        frm = 'galaxy-noreply@%s' % host
 67        to  = job.user.email
 68        subject = "Galaxy workflow step notification '%s'" % (job.history.name)
 69        outdata = ', '.join(ds.dataset.display_name() for ds in job.output_datasets)
 70        body = "Your Galaxy job generating dataset '%s' is complete as of %s." % (outdata, datetime.datetime.now().strftime( "%I:%M" ))
 71        try:
 72            send_mail( frm, to, subject, body, app.config )
 73        except Exception, e:
 74            log.error("EmailAction PJA Failed, exception: %s" % e)
 75
 76    @classmethod
 77    def get_config_form(cls, trans):
 78        form = """
 79            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>\
 80                        <input type='hidden' value='%s' name='pja__"+pja.output_name+"__EmailAction__host'/><input type='hidden' name='pja__"+pja.output_name+"__EmailAction'/>";
 81            """ % trans.request.host
 82        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)
 83
 84    @classmethod
 85    def get_short_str(cls, pja):
 86        if pja.action_arguments and pja.action_arguments.has_key('host'):
 87            return "Email the current user from server %s when this job is complete." % pja.action_arguments['host']
 88        else:
 89            return "Email the current user when this job is complete."
 90
 91
 92class ChangeDatatypeAction(DefaultJobAction):
 93    name = "ChangeDatatypeAction"
 94    verbose_name = "Change Datatype"
 95    @classmethod
 96    def execute(cls, app, sa_session, action, job, replacement_dict):
 97        for dataset_assoc in job.output_datasets:
 98            if action.output_name == '' or dataset_assoc.name == action.output_name:
 99                app.datatypes_registry.change_datatype( dataset_assoc.dataset, action.action_arguments['newtype'])
100
101    @classmethod
102    def get_config_form(cls, trans):
103        dt_list = ""
104        dtnames = [ dtype_name for dtype_name, dtype_value in trans.app.datatypes_registry.datatypes_by_extension.iteritems()]
105        dtnames.sort()
106        for dt_name in dtnames:
107            dt_list += """<option id='pja__"+pja.output_name+"__ChangeDatatypeAction__newtype__%s' value='%s'>%s</option>""" % (dt_name, dt_name, dt_name)
108        ps = """
109            p_str += "<label for='pja__"+pja.output_name+"__ChangeDatatypeAction__newtype'>New Datatype:</label>\
110                <select id='pja__"+pja.output_name+"__ChangeDatatypeAction__newtype' name='pja__"+pja.output_name+"__ChangeDatatypeAction__newtype'>\
111                %s\
112                </select>";
113            if (pja.action_arguments !== undefined && pja.action_arguments.newtype !== undefined){
114                 p_str += "<scrip" + "t type='text/javascript'>$('#pja__" + pja.output_name + "__ChangeDatatypeAction__newtype').val('" + pja.action_arguments.newtype + "');</scrip" + "t>";
115            }
116            """ % dt_list
117            # Note the scrip + t hack above.  Is there a better way?
118        return get_form_template(cls.name, cls.verbose_name, ps, 'This action will change the datatype of the output to the indicated value.')
119
120    @classmethod
121    def get_short_str(cls, pja):
122        return "Set the datatype of output '%s' to '%s'" % (pja.output_name, pja.action_arguments['newtype'])
123
124
125class RenameDatasetAction(DefaultJobAction):
126    name = "RenameDatasetAction"
127    verbose_name = "Rename Dataset"
128
129    @classmethod
130    def execute(cls, app, sa_session, action, job, replacement_dict):
131        # Prevent renaming a dataset to the empty string.
132        if action.action_arguments and action.action_arguments.has_key('newname') and action.action_arguments['newname'] != '':
133            new_name = action.action_arguments['newname']
134
135            #  TODO: Unify and simplify replacement options.
136            #      Add interface through workflow editor UI
137
138            #  The following if statement will process a request to rename
139            #  using an input file name.
140            #  TODO: Replace all matching code with regex
141            #  Proper syntax is #{input_file_variable | option 1 | option n}
142            #    where
143            #      input_file_variable = is the name of an module input variable
144            #      |  = the delimiter for added options. Optional if no options.
145            #      options = basename, upper, lower
146            #      basename = keep all of the file name except the extension
147            #                 (everything before the final ".")
148            #      upper = force the file name to upper case
149            #      lower = force the file name to lower case
150            #  suggested additions:
151            #      "replace" option so you can replace a portion of the name,
152            #      support multiple #{name} in one rename action...
153
154            if new_name.find("#{") > -1:
155                to_be_replaced = ""
156                #  This assumes a single instance of #{variable} will exist
157                start_pos = new_name.find("#{") + 2
158                end_pos = new_name.find("}")
159                to_be_replaced = new_name[start_pos:end_pos]
160                input_file_var = to_be_replaced
161                #  Pull out the piped controls and store them for later
162                #  parsing.
163                tokens = to_be_replaced.split("|")
164                operations = []
165                if len(tokens) > 1:
166                   input_file_var = tokens[0].strip()
167                   for i in range(1, len(tokens)):
168                       operations.append(tokens[i].strip())
169
170                replacement = ""
171                #  Lookp through inputs find one with "to_be_replaced" input
172                #  variable name, and get the replacement name
173                for input_assoc in job.input_datasets:
174                    if input_assoc.name == input_file_var:
175                        replacement = input_assoc.dataset.name
176
177                #  Do operations on replacement
178                #  Any control that is not defined will be ignored.
179                #  This should be moved out to a class or module function
180                for operation in operations:
181                    # Basename returns everything prior to the final '.'
182                    if operation == "basename":
183                        fields = replacement.split(".")
184                        replacement = fields[0]
185                        if len(fields) > 1:
186                            temp = ""
187                            for i in range(1, len(fields) - 1):
188                                temp += "." + fields[i]
189                            replacement += temp
190                    elif operation == "upper":
191                        replacement = replacement.upper()
192                    elif operation == "lower":
193                        replacement = replacement.lower()
194
195                new_name = new_name.replace("#{%s}" % to_be_replaced, replacement)
196
197            if replacement_dict:
198                for k, v in replacement_dict.iteritems():
199                    new_name = new_name.replace("${%s}" % k, v)
200            for dataset_assoc in job.output_datasets:
201                if action.output_name == '' or dataset_assoc.name == action.output_name:
202                    dataset_assoc.dataset.name = new_name
203
204    @classmethod
205    def get_config_form(cls, trans):
206        form = """
207            if (pja.action_arguments && pja.action_arguments.newname){
208                p_str += "<label for='pja__"+pja.output_name+"__RenameDatasetAction__newname'>New output name:</label>\
209                          <input type='text' name='pja__"+pja.output_name+"__RenameDatasetAction__newname' value=\\"" + pja.action_arguments.newname.replace(/"/g, "&quot;") + "\\"/>";
210            }
211            else{
212                p_str += "<label for='pja__"+pja.output_name+"__RenameDatasetAction__newname'>New output name:</label>\
213                          <input type='text' name='pja__"+pja.output_name+"__RenameDatasetAction__newname' value=''/>";
214            }
215            """
216        return get_form_template(cls.name, cls.verbose_name, form, "This action will rename the result dataset.")
217
218    @classmethod
219    def get_short_str(cls, pja):
220        # Prevent renaming a dataset to the empty string.
221        if pja.action_arguments and pja.action_arguments.has_key('newname') and pja.action_arguments['newname'] != '':
222            return "Rename output '%s' to '%s'." % (pja.output_name, pja.action_arguments['newname'])
223        else:
224            return "Rename action used without a new name specified.  Output name will be unchanged."
225
226
227class HideDatasetAction(DefaultJobAction):
228    name = "HideDatasetAction"
229    verbose_name = "Hide Dataset"
230
231    @classmethod
232    def execute(cls, app, sa_session, action, job, replacement_dict):
233        for dataset_assoc in job.output_datasets:
234            if dataset_assoc.dataset.state != dataset_assoc.dataset.states.ERROR and ( action.output_name == '' or dataset_assoc.name == action.output_name ):
235                dataset_assoc.dataset.visible=False
236
237    @classmethod
238    def get_config_form(cls, trans):
239        return  """
240                if (pja.action_type == "HideDatasetAction"){
241                    p_str += "<input type='hidden' name='pja__"+pja.output_name+"__HideDatasetAction'/>";
242                }
243                """
244    @classmethod
245    def get_short_str(cls, pja):
246        return "Hide output '%s'." % pja.output_name
247
248class DeleteDatasetAction(DefaultJobAction):
249    # 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.
250    name = "DeleteDatasetAction"
251    verbose_name = "Delete Dataset"
252
253    @classmethod
254    def execute(cls, app, sa_session, action, job, replacement_dict):
255        for dataset_assoc in job.output_datasets:
256            if action.output_name == '' or dataset_assoc.name == action.output_name:
257                dataset_assoc.dataset.deleted=True
258
259    @classmethod
260    def get_config_form(cls, trans):
261        form = """
262            p_str += "<label for='pja__"+pja.output_name+"__DeleteDatasetAction'>There are no additional options for this action.  This dataset will be marked deleted.</label>\
263                        <input type='hidden' name='pja__"+pja.output_name+"__DeleteDatasetAction'/>";
264            """
265        return get_form_template(cls.name, cls.verbose_name, form, "This action will rename the result dataset.")
266
267    @classmethod
268    def get_short_str(cls, pja):
269        return "Delete this dataset after creation."
270
271
272
273class ColumnSetAction(DefaultJobAction):
274    name = "ColumnSetAction"
275    verbose_name = "Assign Columns"
276    @classmethod
277    def execute(cls, app, sa_session, action, job, replacement_dict):
278        for dataset_assoc in job.output_datasets:
279            if action.output_name == '' or dataset_assoc.name == action.output_name:
280                for k, v in action.action_arguments.items():
281                    if v != '':
282                        # Try to use both pure integer and 'cX' format.
283                        if v[0] == 'c':
284                            v = v[1:]
285                        v = int(v)
286                        if v != 0:
287                            setattr(dataset_assoc.dataset.metadata, k, v)
288
289    @classmethod
290    def get_config_form(cls, trans):
291        form = """
292            if (pja.action_arguments !== undefined){
293                (pja.action_arguments.chromCol === undefined) ? chromCol = "" : chromCol=pja.action_arguments.chromCol;
294                (pja.action_arguments.startCol === undefined) ? startCol = "" : startCol=pja.action_arguments.startCol;
295                (pja.action_arguments.endCol === undefined) ? endCol = "" : endCol=pja.action_arguments.endCol;
296                (pja.action_arguments.strandCol === undefined) ? strandCol = "" : strandCol=pja.action_arguments.strandCol;
297                (pja.action_arguments.nameCol === undefined) ? nameCol = "" : nameCol=pja.action_arguments.nameCol;
298            }else{
299                chromCol = '';
300                startCol = '';
301                endCol = '';
302                strandCol = '';
303                nameCol = '';
304            }
305            p_str += "<p>Leave any of these fields blank if they do not need to be set.</p>\
306                    <label for='pja__"+pja.output_name+"__ColumnSetAction__chromCol'>Chrom Column</label>\
307                        <input type='text' value='" + chromCol + "' name='pja__"+pja.output_name+"__ColumnSetAction__chromCol'/>\
308                    <label for='pja__"+pja.output_name+"__ColumnSetAction__startCol'>Start Column</label>\
309                        <input type='text' value='" + startCol + "' name='pja__"+pja.output_name+"__ColumnSetAction__startCol'/>\
310                    <label for='pja__"+pja.output_name+"__ColumnSetAction__endCol'>End Column</label>\
311                        <input type='text' value='" + endCol + "' name='pja__"+pja.output_name+"__ColumnSetAction__endCol'/>\
312                    <label for='pja__"+pja.output_name+"__ColumnSetAction__strandCol'>Strand Column</label>\
313                        <input type='text' value='" + strandCol + "' name='pja__"+pja.output_name+"__ColumnSetAction__strandCol'/>\
314                    <label for='pja__"+pja.output_name+"__ColumnSetAction__nameCol'>Name Column</label>\
315                        <input type='text' value='" + nameCol + "' name='pja__"+pja.output_name+"__ColumnSetAction__nameCol'/>\";
316            """
317        return get_form_template(cls.name, cls.verbose_name, form, "This action will set column assignments in the output dataset.  Blank fields are ignored.")
318
319    @classmethod
320    def get_short_str(cls, pja):
321        return "Set the following metadata values:<br/>" + "<br/>".join(['%s : %s' % (k, v) for k, v in pja.action_arguments.iteritems()])
322
323
324class SetMetadataAction(DefaultJobAction):
325    name = "SetMetadataAction"
326    # DBTODO Setting of Metadata is currently broken and disabled.  It should not be used (yet).
327
328    @classmethod
329    def execute(cls, app, sa_session, action, job, replacement_dict):
330        for data in job.output_datasets:
331            data.set_metadata( action.action_arguments['newtype'] )
332
333    @classmethod
334    def get_config_form(cls, trans):
335        #         dt_list = ""
336        #         mdict = {}
337        #         for dtype_name, dtype_value in trans.app.datatypes_registry.datatypes_by_extension.iteritems():
338        #             for mn, mt in dtype_value.metadata_spec.items():
339        #                 if mt.visible:
340        #                     mdict[mt.desc] = mt.param.get_html(value= mn).replace('"', "'").strip().replace('\n','')
341        #         for k, v in mdict.items():
342        #             dt_list += "<p><strong>" + k + ":</strong><br/>" + v + "</p>"
343        #         form = """
344        #           p_str += "%s";
345        #   """ % dt_list
346        # return get_form_template('SetMetadataAction', 'Set Metadata', form, "This action will change metadata for the dataset.")
347        form = """
348          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>\
349                        <input type='text' name='pja__"+pja.output_name+"__SetMetadataAction__chromCol'/>\
350                    <label for='pja__"+pja.output_name+"__SetMetadataAction__startCol'>Start Column</label>\
351                        <input type='text' name='pja__"+pja.output_name+"__SetMetadataAction__startCol'/>\
352                    <label for='pja__"+pja.output_name+"__SetMetadataAction__endCol'>End Column</label>\
353                        <input type='text' name='pja__"+pja.output_name+"__SetMetadataAction__endCol'/>\
354                    <label for='pja__"+pja.output_name+"__SetMetadataAction__comment_lines'>Comment Lines</label>\
355                        <input type='text' name='pja__"+pja.output_name+"__SetMetadataAction__comment_lines'/>\
356                      ";
357            """
358        return get_form_template(cls.name, cls.verbose_name, form, "This action will set metadata in the output dataset.")
359
360
361
362class ActionBox(object):
363
364    actions = { "RenameDatasetAction" : RenameDatasetAction,
365                "HideDatasetAction" : HideDatasetAction,
366                "ChangeDatatypeAction": ChangeDatatypeAction,
367                "ColumnSetAction" : ColumnSetAction,
368                "EmailAction" : EmailAction,
369                # "SetMetadataAction" : SetMetadataAction,
370                # "DeleteDatasetAction" : DeleteDatasetAction,
371                }
372    public_actions = ['RenameDatasetAction', 'ChangeDatatypeAction', 'ColumnSetAction', 'EmailAction']
373    immediate_actions = ['ChangeDatatypeAction', 'RenameDatasetAction']
374
375    @classmethod
376    def get_short_str(cls, action):
377        if action.action_type in ActionBox.actions:
378            return ActionBox.actions[action.action_type].get_short_str(action)
379        else:
380            return "Unknown Action"
381
382    @classmethod
383    def handle_incoming(cls, incoming):
384        npd = {}
385        for key, val in incoming.iteritems():
386            if key.startswith('pja'):
387                sp = key.split('__')
388                ao_key = sp[2] + sp[1]
389                # flag / output_name / pjatype / desc
390                if not ao_key in npd:
391                    npd[ao_key] = {'action_type' : sp[2],
392                                  'output_name' : sp[1],
393                                  'action_arguments' : {}}
394                if len(sp) > 3:
395                    if sp[3] == 'output_name':
396                        npd[ao_key]['output_name'] = val
397                    else:
398                        npd[ao_key]['action_arguments'][sp[3]] = val
399            else:
400                # Not pja stuff.
401                pass
402        return to_json_string(npd)
403
404    @classmethod
405    def get_add_list(cls):
406        addlist = "<select id='new_pja_list' name='new_pja_list'>"
407        for action in ActionBox.public_actions:
408            addlist += "<option value='%s'>%s</option>" % (ActionBox.actions[action].name, ActionBox.actions[action].verbose_name)
409        addlist += "</select>"
410        return addlist
411
412    @classmethod
413    def get_forms(cls, trans):
414        forms = ""
415        for action in ActionBox.actions:
416            forms += ActionBox.actions[action].get_config_form(trans)
417        return forms
418
419    @classmethod
420    def execute(cls, app, sa_session, pja, job, replacement_dict = None):
421        if ActionBox.actions.has_key(pja.action_type):
422            ActionBox.actions[pja.action_type].execute(app, sa_session, pja, job, replacement_dict)