1 | """ |
---|
2 | Contains the main interface in the Universe class |
---|
3 | """ |
---|
4 | import logging, os, string, shutil, urllib, re, socket |
---|
5 | from cgi import escape, FieldStorage |
---|
6 | from galaxy import util, datatypes, jobs, web, util |
---|
7 | from galaxy.web.base.controller import * |
---|
8 | from galaxy.util.sanitize_html import sanitize_html |
---|
9 | from galaxy.model.orm import * |
---|
10 | from galaxy.model.item_attrs import UsesAnnotations |
---|
11 | |
---|
12 | log = logging.getLogger( __name__ ) |
---|
13 | |
---|
14 | class RootController( BaseController, UsesHistory, UsesAnnotations ): |
---|
15 | |
---|
16 | @web.expose |
---|
17 | def default(self, trans, target1=None, target2=None, **kwd): |
---|
18 | return 'This link may not be followed from within Galaxy.' |
---|
19 | |
---|
20 | @web.expose |
---|
21 | def index(self, trans, id=None, tool_id=None, mode=None, workflow_id=None, m_c=None, m_a=None, **kwd): |
---|
22 | return trans.fill_template( "root/index.mako", |
---|
23 | tool_id=tool_id, |
---|
24 | workflow_id=workflow_id, |
---|
25 | m_c=m_c, m_a=m_a ) |
---|
26 | |
---|
27 | ## ---- Tool related ----------------------------------------------------- |
---|
28 | |
---|
29 | @web.expose |
---|
30 | def tool_menu( self, trans ): |
---|
31 | if trans.app.config.require_login and not trans.user: |
---|
32 | return trans.fill_template( '/no_access.mako', message = 'Please log in to access Galaxy tools.' ) |
---|
33 | else: |
---|
34 | ## Get most recently used tools. |
---|
35 | toolbox = self.get_toolbox() |
---|
36 | recent_tools = [] |
---|
37 | if trans.user: |
---|
38 | for row in trans.sa_session.query( self.app.model.Job.tool_id ). \ |
---|
39 | filter( self.app.model.Job.user==trans.user ). \ |
---|
40 | order_by( self.app.model.Job.create_time.desc() ): |
---|
41 | tool_id = row[0] |
---|
42 | a_tool = toolbox.tools_by_id.get( tool_id, None ) |
---|
43 | if a_tool and not a_tool.hidden and a_tool not in recent_tools: |
---|
44 | recent_tools.append( a_tool ) |
---|
45 | ## TODO: make number of recently used tools a user preference. |
---|
46 | if len ( recent_tools ) == 5: |
---|
47 | break |
---|
48 | |
---|
49 | return trans.fill_template('/root/tool_menu.mako', toolbox=toolbox, recent_tools=recent_tools ) |
---|
50 | |
---|
51 | @web.json |
---|
52 | def tool_search( self, trans, query ): |
---|
53 | trans.log_action( trans.get_user(), "tool_search.search", "", { "query" : query } ) |
---|
54 | return trans.app.toolbox_search.search( query ) |
---|
55 | |
---|
56 | @web.expose |
---|
57 | def tool_help( self, trans, id ): |
---|
58 | """Return help page for tool identified by 'id' if available""" |
---|
59 | toolbox = self.get_toolbox() |
---|
60 | tool = toolbox.tools_by_id.get(id, '') |
---|
61 | yield "<html><body>" |
---|
62 | if not tool: |
---|
63 | yield "Unknown tool id '%d'" % id |
---|
64 | elif tool.help: |
---|
65 | yield tool.help |
---|
66 | else: |
---|
67 | yield "No additional help available for tool '%s'" % tool.name |
---|
68 | yield "</body></html>" |
---|
69 | |
---|
70 | ## ---- Root history display --------------------------------------------- |
---|
71 | |
---|
72 | @web.expose |
---|
73 | def my_data( self, trans, **kwd ): |
---|
74 | """ |
---|
75 | Display user's data. |
---|
76 | """ |
---|
77 | return trans.fill_template_mako( "/my_data.mako" ) |
---|
78 | |
---|
79 | @web.expose |
---|
80 | def history( self, trans, as_xml=False, show_deleted=False, show_hidden=False, hda_id=None ): |
---|
81 | """ |
---|
82 | Display the current history, creating a new history if necessary. |
---|
83 | NOTE: No longer accepts "id" or "template" options for security reasons. |
---|
84 | """ |
---|
85 | if trans.app.config.require_login and not trans.user: |
---|
86 | return trans.fill_template( '/no_access.mako', message = 'Please log in to access Galaxy histories.' ) |
---|
87 | history = trans.get_history( create=True ) |
---|
88 | if as_xml: |
---|
89 | trans.response.set_content_type('text/xml') |
---|
90 | return trans.fill_template_mako( "root/history_as_xml.mako", |
---|
91 | history=history, |
---|
92 | show_deleted=util.string_as_bool( show_deleted ), |
---|
93 | show_hidden=util.string_as_bool( show_hidden ) ) |
---|
94 | else: |
---|
95 | show_deleted = util.string_as_bool( show_deleted ) |
---|
96 | show_hidden = util.string_as_bool( show_hidden ) |
---|
97 | datasets = self.get_history_datasets( trans, history, show_deleted, show_hidden ) |
---|
98 | return trans.stream_template_mako( "root/history.mako", |
---|
99 | history = history, |
---|
100 | annotation = self.get_item_annotation_str( trans.sa_session, trans.user, history ), |
---|
101 | datasets = datasets, |
---|
102 | hda_id = hda_id, |
---|
103 | show_deleted = show_deleted, |
---|
104 | show_hidden=show_hidden ) |
---|
105 | |
---|
106 | @web.expose |
---|
107 | def dataset_state ( self, trans, id=None, stamp=None ): |
---|
108 | if id is not None: |
---|
109 | try: |
---|
110 | data = trans.sa_session.query( self.app.model.HistoryDatasetAssociation ).get( id ) |
---|
111 | except: |
---|
112 | return trans.show_error_message( "Unable to check dataset %s." %str( id ) ) |
---|
113 | trans.response.headers['X-Dataset-State'] = data.state |
---|
114 | trans.response.headers['Pragma'] = 'no-cache' |
---|
115 | trans.response.headers['Expires'] = '0' |
---|
116 | return data.state |
---|
117 | else: |
---|
118 | return trans.show_error_message( "Must specify a dataset id.") |
---|
119 | |
---|
120 | @web.expose |
---|
121 | def dataset_code( self, trans, id=None, hid=None, stamp=None ): |
---|
122 | if id is not None: |
---|
123 | try: |
---|
124 | data = trans.sa_session.query( self.app.model.HistoryDatasetAssociation ).get( id ) |
---|
125 | except: |
---|
126 | return trans.show_error_message( "Unable to check dataset %s." %str( id ) ) |
---|
127 | trans.response.headers['Pragma'] = 'no-cache' |
---|
128 | trans.response.headers['Expires'] = '0' |
---|
129 | return trans.fill_template("root/history_item.mako", data=data, hid=hid) |
---|
130 | else: |
---|
131 | return trans.show_error_message( "Must specify a dataset id.") |
---|
132 | |
---|
133 | @web.json |
---|
134 | def history_item_updates( self, trans, ids=None, states=None ): |
---|
135 | # Avoid caching |
---|
136 | trans.response.headers['Pragma'] = 'no-cache' |
---|
137 | trans.response.headers['Expires'] = '0' |
---|
138 | # Create new HTML for any that have changed |
---|
139 | rval = {} |
---|
140 | if ids is not None and states is not None: |
---|
141 | ids = map( int, ids.split( "," ) ) |
---|
142 | states = states.split( "," ) |
---|
143 | for id, state in zip( ids, states ): |
---|
144 | data = trans.sa_session.query( self.app.model.HistoryDatasetAssociation ).get( id ) |
---|
145 | if data.state != state: |
---|
146 | job_hda = data |
---|
147 | while job_hda.copied_from_history_dataset_association: |
---|
148 | job_hda = job_hda.copied_from_history_dataset_association |
---|
149 | force_history_refresh = False |
---|
150 | if job_hda.creating_job_associations: |
---|
151 | tool = trans.app.toolbox.tools_by_id.get( job_hda.creating_job_associations[ 0 ].job.tool_id, None ) |
---|
152 | if tool: |
---|
153 | force_history_refresh = tool.force_history_refresh |
---|
154 | if not job_hda.visible: |
---|
155 | force_history_refresh = True |
---|
156 | rval[id] = { |
---|
157 | "state": data.state, |
---|
158 | "html": unicode( trans.fill_template( "root/history_item.mako", data=data, hid=data.hid ), 'utf-8' ), |
---|
159 | "force_history_refresh": force_history_refresh |
---|
160 | } |
---|
161 | return rval |
---|
162 | |
---|
163 | ## ---- Dataset display / editing ---------------------------------------- |
---|
164 | |
---|
165 | @web.expose |
---|
166 | def display( self, trans, id=None, hid=None, tofile=None, toext=".txt", **kwd ): |
---|
167 | """ |
---|
168 | Returns data directly into the browser. |
---|
169 | Sets the mime-type according to the extension |
---|
170 | """ |
---|
171 | if hid is not None: |
---|
172 | try: |
---|
173 | hid = int( hid ) |
---|
174 | except: |
---|
175 | return "hid '%s' is invalid" %str( hid ) |
---|
176 | history = trans.get_history() |
---|
177 | for dataset in history.datasets: |
---|
178 | if dataset.hid == hid: |
---|
179 | data = dataset |
---|
180 | break |
---|
181 | else: |
---|
182 | raise Exception( "No dataset with hid '%d'" % hid ) |
---|
183 | else: |
---|
184 | try: |
---|
185 | data = self.app.model.HistoryDatasetAssociation.get( id ) |
---|
186 | except: |
---|
187 | return "Dataset id '%s' is invalid" %str( id ) |
---|
188 | if data: |
---|
189 | current_user_roles = trans.get_current_user_roles() |
---|
190 | if trans.app.security_agent.can_access_dataset( current_user_roles, data.dataset ): |
---|
191 | mime = trans.app.datatypes_registry.get_mimetype_by_extension( data.extension.lower() ) |
---|
192 | trans.response.set_content_type(mime) |
---|
193 | if tofile: |
---|
194 | fStat = os.stat(data.file_name) |
---|
195 | trans.response.headers['Content-Length'] = int(fStat.st_size) |
---|
196 | if toext[0:1] != ".": |
---|
197 | toext = "." + toext |
---|
198 | valid_chars = '.,^_-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' |
---|
199 | fname = data.name |
---|
200 | fname = ''.join(c in valid_chars and c or '_' for c in fname)[0:150] |
---|
201 | trans.response.headers["Content-Disposition"] = "attachment; filename=GalaxyHistoryItem-%s-[%s]%s" % (data.hid, fname, toext) |
---|
202 | trans.log_event( "Display dataset id: %s" % str(id) ) |
---|
203 | try: |
---|
204 | return open( data.file_name ) |
---|
205 | except: |
---|
206 | return "This dataset contains no content" |
---|
207 | else: |
---|
208 | return "You are not allowed to access this dataset" |
---|
209 | else: |
---|
210 | return "No dataset with id '%s'" % str( id ) |
---|
211 | |
---|
212 | @web.expose |
---|
213 | def display_child(self, trans, parent_id=None, designation=None, tofile=None, toext=".txt"): |
---|
214 | """ |
---|
215 | Returns child data directly into the browser, based upon parent_id and designation. |
---|
216 | """ |
---|
217 | try: |
---|
218 | data = trans.sa_session.query( self.app.model.HistoryDatasetAssociation ).get( parent_id ) |
---|
219 | if data: |
---|
220 | child = data.get_child_by_designation( designation ) |
---|
221 | if child: |
---|
222 | current_user_roles = trans.get_current_user_roles() |
---|
223 | if trans.app.security_agent.can_access_dataset( current_user_roles, child ): |
---|
224 | return self.display( trans, id=child.id, tofile=tofile, toext=toext ) |
---|
225 | else: |
---|
226 | return "You are not privileged to access this dataset." |
---|
227 | except Exception: |
---|
228 | pass |
---|
229 | return "A child named %s could not be found for data %s" % ( designation, parent_id ) |
---|
230 | |
---|
231 | @web.expose |
---|
232 | def display_as( self, trans, id=None, display_app=None, **kwd ): |
---|
233 | """Returns a file in a format that can successfully be displayed in display_app""" |
---|
234 | data = trans.sa_session.query( self.app.model.HistoryDatasetAssociation ).get( id ) |
---|
235 | authz_method = 'rbac' |
---|
236 | if 'authz_method' in kwd: |
---|
237 | authz_method = kwd['authz_method'] |
---|
238 | if data: |
---|
239 | current_user_roles = trans.get_current_user_roles() |
---|
240 | if authz_method == 'rbac' and trans.app.security_agent.can_access_dataset( current_user_roles, data ): |
---|
241 | trans.response.set_content_type( data.get_mime() ) |
---|
242 | trans.log_event( "Formatted dataset id %s for display at %s" % ( str( id ), display_app ) ) |
---|
243 | return data.as_display_type( display_app, **kwd ) |
---|
244 | elif authz_method == 'display_at' and trans.app.host_security_agent.allow_action( trans.request.remote_addr, |
---|
245 | data.permitted_actions.DATASET_ACCESS, |
---|
246 | dataset = data ): |
---|
247 | trans.response.set_content_type( data.get_mime() ) |
---|
248 | return data.as_display_type( display_app, **kwd ) |
---|
249 | else: |
---|
250 | return "You are not allowed to access this dataset." |
---|
251 | else: |
---|
252 | return "No data with id=%d" % id |
---|
253 | |
---|
254 | @web.expose |
---|
255 | def peek(self, trans, id=None): |
---|
256 | """Returns a 'peek' at the data""" |
---|
257 | data = trans.sa_session.query( self.app.model.HistoryDatasetAssociation ).get( id ) |
---|
258 | if data: |
---|
259 | yield "<html><body><pre>" |
---|
260 | yield data.peek |
---|
261 | yield "</pre></body></html>" |
---|
262 | else: |
---|
263 | yield "No data with id=%d" % id |
---|
264 | |
---|
265 | @web.expose |
---|
266 | def edit(self, trans, id=None, hid=None, **kwd): |
---|
267 | """Allows user to modify parameters of an HDA.""" |
---|
268 | message = '' |
---|
269 | error = False |
---|
270 | def __ok_to_edit_metadata( dataset_id ): |
---|
271 | #prevent modifying metadata when dataset is queued or running as input/output |
---|
272 | #This code could be more efficient, i.e. by using mappers, but to prevent slowing down loading a History panel, we'll leave the code here for now |
---|
273 | for job_to_dataset_association in trans.sa_session.query( self.app.model.JobToInputDatasetAssociation ) \ |
---|
274 | .filter_by( dataset_id=dataset_id ) \ |
---|
275 | .all() \ |
---|
276 | + trans.sa_session.query( self.app.model.JobToOutputDatasetAssociation ) \ |
---|
277 | .filter_by( dataset_id=dataset_id ) \ |
---|
278 | .all(): |
---|
279 | if job_to_dataset_association.job.state not in [ job_to_dataset_association.job.states.OK, job_to_dataset_association.job.states.ERROR, job_to_dataset_association.job.states.DELETED ]: |
---|
280 | return False |
---|
281 | return True |
---|
282 | if hid is not None: |
---|
283 | history = trans.get_history() |
---|
284 | # TODO: hid handling |
---|
285 | data = history.datasets[ int( hid ) - 1 ] |
---|
286 | elif id is not None: |
---|
287 | data = trans.sa_session.query( self.app.model.HistoryDatasetAssociation ).get( id ) |
---|
288 | else: |
---|
289 | trans.log_event( "Problem loading dataset id %s with history id %s." % ( str( id ), str( hid ) ) ) |
---|
290 | return trans.show_error_message( "Problem loading dataset." ) |
---|
291 | if data is None: |
---|
292 | trans.log_event( "Problem retrieving dataset id %s with history id." % ( str( id ), str( hid ) ) ) |
---|
293 | return trans.show_error_message( "Problem retrieving dataset." ) |
---|
294 | if id is not None and data.history.user is not None and data.history.user != trans.user: |
---|
295 | return trans.show_error_message( "This instance of a dataset (%s) in a history does not belong to you." % ( data.id ) ) |
---|
296 | current_user_roles = trans.get_current_user_roles() |
---|
297 | if trans.app.security_agent.can_access_dataset( current_user_roles, data.dataset ): |
---|
298 | if data.state == trans.model.Dataset.states.UPLOAD: |
---|
299 | return trans.show_error_message( "Please wait until this dataset finishes uploading before attempting to edit its metadata." ) |
---|
300 | params = util.Params( kwd, sanitize=False ) |
---|
301 | if params.change: |
---|
302 | # The user clicked the Save button on the 'Change data type' form |
---|
303 | if data.datatype.allow_datatype_change and trans.app.datatypes_registry.get_datatype_by_extension( params.datatype ).allow_datatype_change: |
---|
304 | #prevent modifying datatype when dataset is queued or running as input/output |
---|
305 | if not __ok_to_edit_metadata( data.id ): |
---|
306 | return trans.show_error_message( "This dataset is currently being used as input or output. You cannot change datatype until the jobs have completed or you have canceled them." ) |
---|
307 | trans.app.datatypes_registry.change_datatype( data, params.datatype, set_meta = not trans.app.config.set_metadata_externally ) |
---|
308 | trans.sa_session.flush() |
---|
309 | if trans.app.config.set_metadata_externally: |
---|
310 | trans.app.datatypes_registry.set_external_metadata_tool.tool_action.execute( trans.app.datatypes_registry.set_external_metadata_tool, trans, incoming = { 'input1':data }, overwrite = False ) #overwrite is False as per existing behavior |
---|
311 | return trans.show_ok_message( "Changed the type of dataset '%s' to %s" % ( data.name, params.datatype ), refresh_frames=['history'] ) |
---|
312 | else: |
---|
313 | return trans.show_error_message( "You are unable to change datatypes in this manner. Changing %s to %s is not allowed." % ( data.extension, params.datatype ) ) |
---|
314 | elif params.save: |
---|
315 | # The user clicked the Save button on the 'Edit Attributes' form |
---|
316 | data.name = params.name |
---|
317 | data.info = params.info |
---|
318 | message = '' |
---|
319 | if __ok_to_edit_metadata( data.id ): |
---|
320 | # The following for loop will save all metadata_spec items |
---|
321 | for name, spec in data.datatype.metadata_spec.items(): |
---|
322 | if spec.get("readonly"): |
---|
323 | continue |
---|
324 | optional = params.get("is_"+name, None) |
---|
325 | other = params.get("or_"+name, None) |
---|
326 | if optional and optional == 'true': |
---|
327 | # optional element... == 'true' actually means it is NOT checked (and therefore omitted) |
---|
328 | setattr(data.metadata, name, None) |
---|
329 | else: |
---|
330 | if other: |
---|
331 | setattr( data.metadata, name, other ) |
---|
332 | else: |
---|
333 | setattr( data.metadata, name, spec.unwrap( params.get (name, None) ) ) |
---|
334 | data.datatype.after_setting_metadata( data ) |
---|
335 | # Sanitize annotation before adding it. |
---|
336 | if params.annotation: |
---|
337 | annotation = sanitize_html( params.annotation, 'utf-8', 'text/html' ) |
---|
338 | self.add_item_annotation( trans.sa_session, trans.get_user(), data, annotation ) |
---|
339 | # If setting metadata previously failed and all required elements have now been set, clear the failed state. |
---|
340 | if data._state == trans.model.Dataset.states.FAILED_METADATA and not data.missing_meta(): |
---|
341 | data._state = None |
---|
342 | trans.sa_session.flush() |
---|
343 | return trans.show_ok_message( "Attributes updated%s" % message, refresh_frames=['history'] ) |
---|
344 | else: |
---|
345 | trans.sa_session.flush() |
---|
346 | return trans.show_warn_message( "Attributes updated, but metadata could not be changed because this dataset is currently being used as input or output. You must cancel or wait for these jobs to complete before changing metadata.", refresh_frames=['history'] ) |
---|
347 | elif params.detect: |
---|
348 | # The user clicked the Auto-detect button on the 'Edit Attributes' form |
---|
349 | #prevent modifying metadata when dataset is queued or running as input/output |
---|
350 | if not __ok_to_edit_metadata( data.id ): |
---|
351 | return trans.show_error_message( "This dataset is currently being used as input or output. You cannot change metadata until the jobs have completed or you have canceled them." ) |
---|
352 | for name, spec in data.metadata.spec.items(): |
---|
353 | # We need to be careful about the attributes we are resetting |
---|
354 | if name not in [ 'name', 'info', 'dbkey', 'base_name' ]: |
---|
355 | if spec.get( 'default' ): |
---|
356 | setattr( data.metadata, name, spec.unwrap( spec.get( 'default' ) ) ) |
---|
357 | if trans.app.config.set_metadata_externally: |
---|
358 | message = 'Attributes have been queued to be updated' |
---|
359 | trans.app.datatypes_registry.set_external_metadata_tool.tool_action.execute( trans.app.datatypes_registry.set_external_metadata_tool, trans, incoming = { 'input1':data } ) |
---|
360 | else: |
---|
361 | message = 'Attributes updated' |
---|
362 | data.set_meta() |
---|
363 | data.datatype.after_setting_metadata( data ) |
---|
364 | trans.sa_session.flush() |
---|
365 | return trans.show_ok_message( message, refresh_frames=['history'] ) |
---|
366 | elif params.convert_data: |
---|
367 | target_type = kwd.get("target_type", None) |
---|
368 | if target_type: |
---|
369 | message = data.datatype.convert_dataset(trans, data, target_type) |
---|
370 | return trans.show_ok_message( message, refresh_frames=['history'] ) |
---|
371 | elif params.update_roles_button: |
---|
372 | if not trans.user: |
---|
373 | return trans.show_error_message( "You must be logged in if you want to change permissions." ) |
---|
374 | if trans.app.security_agent.can_manage_dataset( current_user_roles, data.dataset ): |
---|
375 | # The user associated the DATASET_ACCESS permission on the dataset with 1 or more roles. We |
---|
376 | # need to ensure that they did not associate roles that would cause accessibility problems. |
---|
377 | permissions, in_roles, error, message = \ |
---|
378 | trans.app.security_agent.derive_roles_from_access( trans, data.dataset.id, 'root', **kwd ) |
---|
379 | a = trans.app.security_agent.get_action( trans.app.security_agent.permitted_actions.DATASET_ACCESS.action ) |
---|
380 | if error: |
---|
381 | # Keep the original role associations for the DATASET_ACCESS permission on the dataset. |
---|
382 | permissions[ a ] = data.dataset.get_access_roles( trans ) |
---|
383 | trans.app.security_agent.set_all_dataset_permissions( data.dataset, permissions ) |
---|
384 | trans.sa_session.refresh( data.dataset ) |
---|
385 | if not message: |
---|
386 | message = 'Your changes completed successfully.' |
---|
387 | else: |
---|
388 | return trans.show_error_message( "You are not authorized to change this dataset's permissions" ) |
---|
389 | if "dbkey" in data.datatype.metadata_spec and not data.metadata.dbkey: |
---|
390 | # Copy dbkey into metadata, for backwards compatability |
---|
391 | # This looks like it does nothing, but getting the dbkey |
---|
392 | # returns the metadata dbkey unless it is None, in which |
---|
393 | # case it resorts to the old dbkey. Setting the dbkey |
---|
394 | # sets it properly in the metadata |
---|
395 | #### This is likely no longer required, since the dbkey exists entirely within metadata (the old_dbkey field is gone): REMOVE ME? |
---|
396 | data.metadata.dbkey = data.dbkey |
---|
397 | # let's not overwrite the imported datatypes module with the variable datatypes? |
---|
398 | # the built-in 'id' is overwritten in lots of places as well |
---|
399 | ldatatypes = [ dtype_name for dtype_name, dtype_value in trans.app.datatypes_registry.datatypes_by_extension.iteritems() if dtype_value.allow_datatype_change ] |
---|
400 | ldatatypes.sort() |
---|
401 | all_roles = trans.app.security_agent.get_legitimate_roles( trans, data.dataset, 'root' ) |
---|
402 | if error: |
---|
403 | status = 'error' |
---|
404 | else: |
---|
405 | status = 'done' |
---|
406 | return trans.fill_template( "/dataset/edit_attributes.mako", |
---|
407 | data=data, |
---|
408 | data_annotation=self.get_item_annotation_str( trans.sa_session, trans.user, data ), |
---|
409 | datatypes=ldatatypes, |
---|
410 | current_user_roles=current_user_roles, |
---|
411 | all_roles=all_roles, |
---|
412 | message=message, |
---|
413 | status=status ) |
---|
414 | else: |
---|
415 | return trans.show_error_message( "You do not have permission to edit this dataset's ( id: %s ) information." % str( id ) ) |
---|
416 | |
---|
417 | def __delete_dataset( self, trans, id ): |
---|
418 | data = trans.sa_session.query( self.app.model.HistoryDatasetAssociation ).get( id ) |
---|
419 | if data: |
---|
420 | # Walk up parent datasets to find the containing history |
---|
421 | topmost_parent = data |
---|
422 | while topmost_parent.parent: |
---|
423 | topmost_parent = topmost_parent.parent |
---|
424 | assert topmost_parent in trans.history.datasets, "Data does not belong to current history" |
---|
425 | # Mark deleted and cleanup |
---|
426 | data.mark_deleted() |
---|
427 | data.clear_associated_files() |
---|
428 | trans.log_event( "Dataset id %s marked as deleted" % str(id) ) |
---|
429 | if data.parent_id is None and len( data.creating_job_associations ) > 0: |
---|
430 | # Mark associated job for deletion |
---|
431 | job = data.creating_job_associations[0].job |
---|
432 | if job.state in [ self.app.model.Job.states.QUEUED, self.app.model.Job.states.RUNNING, self.app.model.Job.states.NEW ]: |
---|
433 | # Are *all* of the job's other output datasets deleted? |
---|
434 | if job.check_if_output_datasets_deleted(): |
---|
435 | job.mark_deleted() |
---|
436 | self.app.job_manager.job_stop_queue.put( job.id ) |
---|
437 | trans.sa_session.flush() |
---|
438 | |
---|
439 | @web.expose |
---|
440 | def delete( self, trans, id = None, show_deleted_on_refresh = False, **kwd): |
---|
441 | if id: |
---|
442 | if isinstance( id, list ): |
---|
443 | dataset_ids = id |
---|
444 | else: |
---|
445 | dataset_ids = [ id ] |
---|
446 | history = trans.get_history() |
---|
447 | for id in dataset_ids: |
---|
448 | try: |
---|
449 | id = int( id ) |
---|
450 | except: |
---|
451 | continue |
---|
452 | self.__delete_dataset( trans, id ) |
---|
453 | return self.history( trans, show_deleted = show_deleted_on_refresh ) |
---|
454 | |
---|
455 | @web.expose |
---|
456 | def delete_async( self, trans, id = None, **kwd): |
---|
457 | if id: |
---|
458 | try: |
---|
459 | id = int( id ) |
---|
460 | except: |
---|
461 | return "Dataset id '%s' is invalid" %str( id ) |
---|
462 | self.__delete_dataset( trans, id ) |
---|
463 | return "OK" |
---|
464 | |
---|
465 | ## ---- History management ----------------------------------------------- |
---|
466 | |
---|
467 | @web.expose |
---|
468 | def history_options( self, trans ): |
---|
469 | """Displays a list of history related actions""" |
---|
470 | return trans.fill_template( "/history/options.mako", |
---|
471 | user=trans.get_user(), |
---|
472 | history=trans.get_history( create=True ) ) |
---|
473 | @web.expose |
---|
474 | def history_delete( self, trans, id ): |
---|
475 | """ |
---|
476 | Backward compatibility with check_galaxy script. |
---|
477 | """ |
---|
478 | return trans.webapp.controllers['history'].list( trans, id, operation='delete' ) |
---|
479 | @web.expose |
---|
480 | def clear_history( self, trans ): |
---|
481 | """Clears the history for a user""" |
---|
482 | history = trans.get_history() |
---|
483 | for dataset in history.datasets: |
---|
484 | dataset.deleted = True |
---|
485 | dataset.clear_associated_files() |
---|
486 | trans.sa_session.flush() |
---|
487 | trans.log_event( "History id %s cleared" % (str(history.id)) ) |
---|
488 | trans.response.send_redirect( url_for("/index" ) ) |
---|
489 | @web.expose |
---|
490 | def history_import( self, trans, id=None, confirm=False, **kwd ): |
---|
491 | msg = "" |
---|
492 | user = trans.get_user() |
---|
493 | user_history = trans.get_history() |
---|
494 | if not id: |
---|
495 | return trans.show_error_message( "You must specify a history you want to import.") |
---|
496 | import_history = trans.sa_session.query( trans.app.model.History ).get( id ) |
---|
497 | if not import_history: |
---|
498 | return trans.show_error_message( "The specified history does not exist.") |
---|
499 | if user: |
---|
500 | if import_history.user_id == user.id: |
---|
501 | return trans.show_error_message( "You cannot import your own history.") |
---|
502 | new_history = import_history.copy( target_user=trans.user ) |
---|
503 | new_history.name = "imported: "+new_history.name |
---|
504 | new_history.user_id = user.id |
---|
505 | galaxy_session = trans.get_galaxy_session() |
---|
506 | try: |
---|
507 | association = trans.sa_session.query( trans.app.model.GalaxySessionToHistoryAssociation ) \ |
---|
508 | .filter_by( session_id=galaxy_session.id, history_id=new_history.id ) \ |
---|
509 | .first() |
---|
510 | except: |
---|
511 | association = None |
---|
512 | new_history.add_galaxy_session( galaxy_session, association=association ) |
---|
513 | trans.sa_session.add( new_history ) |
---|
514 | trans.sa_session.flush() |
---|
515 | if not user_history.datasets: |
---|
516 | trans.set_history( new_history ) |
---|
517 | trans.log_event( "History imported, id: %s, name: '%s': " % (str(new_history.id) , new_history.name ) ) |
---|
518 | return trans.show_ok_message( """ |
---|
519 | History "%s" has been imported. Click <a href="%s">here</a> |
---|
520 | to begin.""" % ( new_history.name, web.url_for( '/' ) ) ) |
---|
521 | elif not user_history.datasets or confirm: |
---|
522 | new_history = import_history.copy() |
---|
523 | new_history.name = "imported: "+new_history.name |
---|
524 | new_history.user_id = None |
---|
525 | galaxy_session = trans.get_galaxy_session() |
---|
526 | try: |
---|
527 | association = trans.sa_session.query( trans.app.model.GalaxySessionToHistoryAssociation ) \ |
---|
528 | .filter_by( session_id=galaxy_session.id, history_id=new_history.id ) \ |
---|
529 | .first() |
---|
530 | except: |
---|
531 | association = None |
---|
532 | new_history.add_galaxy_session( galaxy_session, association=association ) |
---|
533 | trans.sa_session.add( new_history ) |
---|
534 | trans.sa_session.flush() |
---|
535 | trans.set_history( new_history ) |
---|
536 | trans.log_event( "History imported, id: %s, name: '%s': " % (str(new_history.id) , new_history.name ) ) |
---|
537 | return trans.show_ok_message( """ |
---|
538 | History "%s" has been imported. Click <a href="%s">here</a> |
---|
539 | to begin.""" % ( new_history.name, web.url_for( '/' ) ) ) |
---|
540 | return trans.show_warn_message( """ |
---|
541 | Warning! If you import this history, you will lose your current |
---|
542 | history. Click <a href="%s">here</a> to confirm. |
---|
543 | """ % web.url_for( id=id, confirm=True ) ) |
---|
544 | @web.expose |
---|
545 | def history_new( self, trans, name=None ): |
---|
546 | trans.new_history( name=name ) |
---|
547 | trans.log_event( "Created new History, id: %s." % str(trans.history.id) ) |
---|
548 | return trans.show_message( "New history created", refresh_frames = ['history'] ) |
---|
549 | @web.expose |
---|
550 | def history_add_to( self, trans, history_id=None, file_data=None, name="Data Added to History",info=None,ext="txt",dbkey="?",copy_access_from=None,**kwd ): |
---|
551 | """Adds a POSTed file to a History""" |
---|
552 | try: |
---|
553 | history = trans.sa_session.query( trans.app.model.History ).get( history_id ) |
---|
554 | data = trans.app.model.HistoryDatasetAssociation( name = name, |
---|
555 | info = info, |
---|
556 | extension = ext, |
---|
557 | dbkey = dbkey, |
---|
558 | create_dataset = True, |
---|
559 | sa_session = trans.sa_session ) |
---|
560 | if copy_access_from: |
---|
561 | copy_access_from = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( copy_access_from ) |
---|
562 | trans.app.security_agent.copy_dataset_permissions( copy_access_from.dataset, data.dataset ) |
---|
563 | else: |
---|
564 | permissions = trans.app.security_agent.history_get_default_permissions( history ) |
---|
565 | trans.app.security_agent.set_all_dataset_permissions( data.dataset, permissions ) |
---|
566 | trans.sa_session.add( data ) |
---|
567 | trans.sa_session.flush() |
---|
568 | data_file = open( data.file_name, "wb" ) |
---|
569 | file_data.file.seek( 0 ) |
---|
570 | data_file.write( file_data.file.read() ) |
---|
571 | data_file.close() |
---|
572 | data.state = data.states.OK |
---|
573 | data.set_size() |
---|
574 | data.init_meta() |
---|
575 | data.set_meta() |
---|
576 | trans.sa_session.flush() |
---|
577 | history.add_dataset( data ) |
---|
578 | trans.sa_session.flush() |
---|
579 | data.set_peek() |
---|
580 | trans.sa_session.flush() |
---|
581 | trans.log_event("Added dataset %d to history %d" %(data.id, trans.history.id)) |
---|
582 | return trans.show_ok_message("Dataset "+str(data.hid)+" added to history "+str(history_id)+".") |
---|
583 | except Exception, e: |
---|
584 | trans.log_event( "Failed to add dataset to history: %s" % ( e ) ) |
---|
585 | return trans.show_error_message("Adding File to History has Failed") |
---|
586 | @web.expose |
---|
587 | def history_set_default_permissions( self, trans, id=None, **kwd ): |
---|
588 | """Sets the permissions on a history""" |
---|
589 | if trans.user: |
---|
590 | if 'update_roles_button' in kwd: |
---|
591 | history = None |
---|
592 | if id: |
---|
593 | try: |
---|
594 | id = int( id ) |
---|
595 | except: |
---|
596 | id = None |
---|
597 | if id: |
---|
598 | history = trans.sa_session.query( trans.app.model.History ).get( id ) |
---|
599 | if not history: |
---|
600 | # If we haven't retrieved a history, use the current one |
---|
601 | history = trans.get_history() |
---|
602 | p = util.Params( kwd ) |
---|
603 | permissions = {} |
---|
604 | for k, v in trans.app.model.Dataset.permitted_actions.items(): |
---|
605 | in_roles = p.get( k + '_in', [] ) |
---|
606 | if not isinstance( in_roles, list ): |
---|
607 | in_roles = [ in_roles ] |
---|
608 | in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in in_roles ] |
---|
609 | permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles |
---|
610 | dataset = 'dataset' in kwd |
---|
611 | bypass_manage_permission = 'bypass_manage_permission' in kwd |
---|
612 | trans.app.security_agent.history_set_default_permissions( history, permissions, dataset=dataset, bypass_manage_permission=bypass_manage_permission ) |
---|
613 | return trans.show_ok_message( 'Default history permissions have been changed.' ) |
---|
614 | return trans.fill_template( 'history/permissions.mako' ) |
---|
615 | else: |
---|
616 | #user not logged in, history group must be only public |
---|
617 | return trans.show_error_message( "You must be logged in to change a history's default permissions." ) |
---|
618 | @web.expose |
---|
619 | def dataset_make_primary( self, trans, id=None): |
---|
620 | """Copies a dataset and makes primary""" |
---|
621 | try: |
---|
622 | old_data = trans.sa_session.query( self.app.model.HistoryDatasetAssociation ).get( id ) |
---|
623 | new_data = old_data.copy() |
---|
624 | ## new_data.parent = None |
---|
625 | ## history = trans.app.model.History.get( old_data.history_id ) |
---|
626 | history = trans.get_history() |
---|
627 | history.add_dataset(new_data) |
---|
628 | trans.sa_session.add( new_data ) |
---|
629 | trans.sa_session.flush() |
---|
630 | return trans.show_message( "<p>Secondary dataset has been made primary.</p>", refresh_frames=['history'] ) |
---|
631 | except: |
---|
632 | return trans.show_error_message( "<p>Failed to make secondary dataset primary.</p>" ) |
---|
633 | |
---|
634 | # @web.expose |
---|
635 | # def masthead( self, trans, active_view=None ): |
---|
636 | # brand = trans.app.config.get( "brand", "" ) |
---|
637 | # if brand: |
---|
638 | # brand ="<span class='brand'>/%s</span>" % brand |
---|
639 | # wiki_url = trans.app.config.get( "wiki_url", "http://g2.trac.bx.psu.edu/" ) |
---|
640 | # bugs_email = trans.app.config.get( "bugs_email", "mailto:galaxy-bugs@bx.psu.edu" ) |
---|
641 | # blog_url = trans.app.config.get( "blog_url", "http://g2.trac.bx.psu.edu/blog" ) |
---|
642 | # screencasts_url = trans.app.config.get( "screencasts_url", "http://g2.trac.bx.psu.edu/wiki/ScreenCasts" ) |
---|
643 | # admin_user = "false" |
---|
644 | # admin_users = trans.app.config.get( "admin_users", "" ).split( "," ) |
---|
645 | # user = trans.get_user() |
---|
646 | # if user: |
---|
647 | # user_email = trans.get_user().email |
---|
648 | # if user_email in admin_users: |
---|
649 | # admin_user = "true" |
---|
650 | # return trans.fill_template( "/root/masthead.mako", brand=brand, wiki_url=wiki_url, |
---|
651 | # blog_url=blog_url,bugs_email=bugs_email, screencasts_url=screencasts_url, admin_user=admin_user, active_view=active_view ) |
---|
652 | |
---|
653 | # @web.expose |
---|
654 | # def dataset_errors( self, trans, id=None, **kwd ): |
---|
655 | # """View/fix errors associated with dataset""" |
---|
656 | # data = trans.app.model.HistoryDatasetAssociation.get( id ) |
---|
657 | # p = kwd |
---|
658 | # if p.get("fix_errors", None): |
---|
659 | # # launch tool to create new, (hopefully) error free dataset |
---|
660 | # tool_params = {} |
---|
661 | # tool_params["tool_id"] = 'fix_errors' |
---|
662 | # tool_params["runtool_btn"] = 'T' |
---|
663 | # tool_params["input"] = id |
---|
664 | # tool_params["ext"] = data.ext |
---|
665 | # # send methods selected |
---|
666 | # repair_methods = data.datatype.repair_methods( data ) |
---|
667 | # methods = [] |
---|
668 | # for method, description in repair_methods: |
---|
669 | # if method in p: methods.append(method) |
---|
670 | # tool_params["methods"] = ",".join(methods) |
---|
671 | # url = "/tool_runner/index?" + urllib.urlencode(tool_params) |
---|
672 | # trans.response.send_redirect(url) |
---|
673 | # else: |
---|
674 | # history = trans.app.model.History.get( data.history_id ) |
---|
675 | # return trans.fill_template('dataset/validation.tmpl', data=data, history=history) |
---|
676 | |
---|
677 | # ---- Debug methods ---------------------------------------------------- |
---|
678 | |
---|
679 | @web.expose |
---|
680 | def echo(self, trans, **kwd): |
---|
681 | """Echos parameters (debugging)""" |
---|
682 | rval = "" |
---|
683 | for k in trans.request.headers: |
---|
684 | rval += "%s: %s <br/>" % ( k, trans.request.headers[k] ) |
---|
685 | for k in kwd: |
---|
686 | rval += "%s: %s <br/>" % ( k, kwd[k] ) |
---|
687 | if isinstance( kwd[k], FieldStorage ): |
---|
688 | rval += "-> %s" % kwd[k].file.read() |
---|
689 | return rval |
---|
690 | |
---|
691 | @web.expose |
---|
692 | def generate_error( self, trans ): |
---|
693 | raise Exception( "Fake error!" ) |
---|