Source code for galaxy.views

# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
# general python
import json
from datetime import datetime
import operator
import re
import six
import sys

if sys.version_info > (3, 0):
    from functools import reduce

# standard django
from django.shortcuts import render
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView, View, ListView, DeleteView, UpdateView
from django.http import JsonResponse
from django.shortcuts import redirect
from django.db.models import Q
from django.urls import reverse_lazy

# django external apps
from django_tables2 import RequestConfig
import django_tables2 as tables
from django_tables2.views import SingleTableMixin, MultiTableMixin
from django_filters.views import FilterView
from django_tables2.export.views import ExportMixin

# django custom user external apps
from gfiles.models import GenericFile
from gfiles.views import GFileListView
from gfiles.filter import GFileFilter
from gfiles.tables import GFileTable

# django 'this' app
from galaxy.forms import (
    WorkflowForm,
    FilesToGalaxyDataLibraryParamForm,
    GenericFilesToGalaxyHistoryParamForm,
    GalaxyUserForm,
    WorkflowRunForm,
    GalaxyInstanceTrackingForm,
    DeleteGalaxyHistoryForm,
    HistoryDataForm
)
from galaxy.models import (
    GalaxyInstanceTracking,
    GalaxyUser,
    Workflow,
    History,
    HistoryData
)
from galaxy.tables import (
    WorkflowStatusTable,
    WorkflowTable,
    HistoryTable,
    HistoryDataTable,
    GalaxyInstanceTrackingTable,
    GalaxyUserTable
)
from galaxy.filter import (
    WorkflowFilter,
    HistoryFilter,
    GalaxyUserFilter
)

from galaxy.utils.upload_to_galaxy import f2dl_action, f2h_action
from galaxy.utils.sync_files import sync_galaxy_files
from galaxy.utils.workflow_actions import (
    run_galaxy_workflow,
    get_workflow_status,
    workflow_sync
)
from galaxy.utils.history_actions import (
    get_history_status,
    delete_galaxy_histories,
    get_history_data,
    init_history_data_save_form,
    history_data_save_form
)





[docs]class GalaxyInstanceCreateView(LoginRequiredMixin, CreateView): ''' Create a Galaxy instance to track in django. Note that the Galaxy needs to be accessible at the point of initialisation. If file transfer is required to Galaxy that is not located on the same server as the Django server then the associated FTP host & post details need to be added as well, see `galaxy docs`_ User login required .. _galaxy docs: https://galaxyproject.org/ftp-upload/ ''' model = GalaxyInstanceTracking success_url = reverse_lazy('galaxy_summary') form_class = GalaxyInstanceTrackingForm
[docs]class GalaxyInstanceTrackingUpdateView(LoginRequiredMixin, UpdateView): model = GalaxyInstanceTracking success_url = reverse_lazy('galaxy_summary') form_class = GalaxyInstanceTrackingForm
[docs]class GalaxyInstanceTrackingDeleteView(DeleteView): model = GalaxyInstanceTracking success_url = reverse_lazy('list_ontologyterm') template_name = 'galaxy/confirm_delete.html'
[docs]class GalaxySummaryView(LoginRequiredMixin, SingleTableMixin, ListView): # initial = {'key': 'value'} template_name = 'galaxy/galaxy_summary.html' table_class = GalaxyInstanceTrackingTable model = GalaxyInstanceTracking
[docs]class GalaxyUserCreateView(LoginRequiredMixin, CreateView): ''' Register a Galaxy user to a Galaxy instance that we have tracked. A django user can be linked to many Galaxy instances and each Galaxy User HAS to be linked to Galaxy instance django-user [1 --- *] galaxy-users galaxy-user [* --- 1] galaxy-instances However, a django user can't be linked to multiple of the same galaxy instances ''' model = GalaxyUser success_url = '/galaxy/success' form_class = GalaxyUserForm
[docs] def get_form_kwargs(self): # Get the user form as a kwarg argument kwargs = super(CreateView, self).get_form_kwargs() kwargs['user'] = self.request.user return kwargs
[docs] def form_valid(self, form): # The user is automatically added to the model based on whoever is logged in at the time gu = form.save(commit=False) gu.user = self.request.user gu.save() return super(GalaxyUserCreateView, self).form_valid(form)
[docs] def get_initial(self): # show the most recent galaxy instance as default value return {'galaxyinstancetracking':GalaxyInstanceTracking.objects.last()}
[docs]class GalaxyUserUpdateView(LoginRequiredMixin, UpdateView): model = GalaxyUser success_url = reverse_lazy('list_galaxy_user') form_class = GalaxyUserForm
[docs]class GalaxyUserDeleteView(DeleteView): model = GalaxyUser success_url = reverse_lazy('list_galaxy_user') template_name = 'galaxy/confirm_delete.html'
[docs]class GalaxyUserListView(LoginRequiredMixin, SingleTableMixin, ListView): # initial = {'key': 'value'} template_name = 'galaxy/galaxy_user_list.html' table_class = GalaxyUserTable model = GalaxyUser
[docs]class GalaxySync(LoginRequiredMixin, View): ''' Sync workflows from available galaxy instances for the current user. This will add any workflows to the django database that have not already been added. And will update any that have been updated on the galaxy instance. todo: It might be worth changing this view to a more general GalaxySync option. That syncs workflows & files ''' def get(self, request, *args, **kwargs): return render(request, 'galaxy/galaxy_sync.html') def post(self, request, *args, **kwargs): workflow_sync(request.user) sync_galaxy_files(request.user) get_history_status(request.user) # redirects to show the current available workflows return redirect('workflow_summary')
[docs]class WorkflowListView(LoginRequiredMixin, SingleTableMixin, FilterView): ''' View and initiate a run for all registered workflows. Workflows can also be synced here as well ''' table_class = WorkflowTable model = Workflow template_name = 'galaxy/workflow_summary.html' filterset_class = WorkflowFilter def post(self, request, *args, **kwargs): workflow_sync(request.user) # redirects to show the current available workflows return redirect('workflow_summary')
[docs]class TableFileSelectMixin: ''' General class for file selection with ajax multipage ''' success_msg = "" success_url = '/galaxy/success' table_pagination = {"per_page": 15} # form_class = GenericFilesToGalaxyDataLibraryParamForm # should add generic form for this, perhaps based on a related model # template_name = 'galaxy/files_to_galaxy.html' # should add generic template for this, perhaps based on a related model initial_context = {} # pass any default parameters required for any context that is used def get(self, request, *args, **kwargs): # we have to overide the standard get to add some extra information to the context form = self.form_class() context = self.form2context(form) return render(request, self.template_name, context=context) def form2context(self, form): filterset_class = self.get_filterset_class() self.filterset = self.get_filterset(filterset_class) self.object_list = self.filterset.qs context = self.get_context_data(filter=self.filterset, object_list=self.object_list, form=form) context.update(self.initial_context) return context def post(self, request, *args, **kwargs): if request.is_ajax(): form = self.form_class() context = self.form2context(form) # keep track of the checkboxes that have been added by the user request = ajax_post_selected(request) return render(request, self.template_name, context=context) else: form = self.form_class(request.POST, request.FILES) context = self.form2context(form) if form.is_valid(): return self.form_valid(request, form) else: return render(request, self.template_name, context=context) def form_valid(self, request, form): # first save the form and the user who posted return render(request, 'gfiles/submitted.html')
[docs]class FilesToGalaxyDataLib(LoginRequiredMixin, TableFileSelectMixin, GFileListView): ''' Select Files to be added to Galaxy data Library Inherit the GFileListView that view that shows current files and allows some basic filtering ''' template_name = 'galaxy/files_to_galaxy.html' form_class = FilesToGalaxyDataLibraryParamForm initial_context = {'library': True, 'django_url': '/galaxy/files_to_galaxy_datalib/'} def save_form_param(self, request, form): user = request.user f2dl = form.save(commit=False) f2dl.added_by = user if not f2dl.folder_name: now = datetime.now() f2dl.folder_name = '{}_{}'.format(user.username, now.strftime("%Y_%m_%d")) f2dl.save() return f2dl def form_valid(self, request, form): # first save the form and the user who posted f2dl = self.save_form_param(request, form) # then get the ids of the files that were used, and add the files to the galaxy library pks = request.POST.getlist("check") f2dl_action(pks, f2dl, galaxy_pass=form.cleaned_data['galaxy_password']) # reset the form checkboxes request.session['selected_items'] = '' return render(request, 'gfiles/submitted.html')
[docs]class GenericFilesToGalaxyHistory(LoginRequiredMixin, TableFileSelectMixin, GFileListView): template_name = 'galaxy/files_to_galaxy.html' form_class = GenericFilesToGalaxyHistoryParamForm initial_context = {'library': False, 'django_url': '/galaxy/files_to_galaxy_datalib/'} def form_valid(self, request, form): user = self.request.user f2h = form.save(commit=False) f2h.added_by = user f2h.save() pks = request.POST.getlist("check") f2h_action(pks, f2h, galaxy_pass=form.cleaned_data['galaxy_password']) return render(request, 'gfiles/submitted.html')
[docs]class WorkflowRunView(LoginRequiredMixin, View): ''' Run a registered workflow ''' success_msg = "Run started" success_url = '/galaxy/success' template_name = 'galaxy/workflow_run.html' table_class = GFileTable filter_class = GFileFilter form_class = WorkflowRunForm redirect_to = 'history_status' def update_workflow_inputs(self, l): # update the worklow inputs before submission (to be used with classes that inherit this class return l def get_queryset(self): return Workflow.objects.get(pk=self.kwargs['wid']) def get_datainputs(self): w = self.get_queryset() return w.workflowinput_set.all() def get(self, request, *args, **kwargs): form = self.form_class() l = self.page_setup(request) return render(request, self.template_name, {'form': form, 'list': l, 'wid':self.kwargs['wid']}) def page_setup(self, request): # Dynamic number of tables are added to the template based on what the workflow-inputs are (e.g. if # there are multiple data-inputs or data-input-collections). The choice of files to choose is based # on what the user has uploaded previously to either the gfiles or mfiles applications data_inputs = self.get_datainputs() tables_l = [] filters = [] data_input_names = [] data_input_steps = [] data_input_types = [] # Need to setup a config for the tables (only 1 required per template page) rc = RequestConfig(request, paginate={'per_page': 10}) # Get all the generic files (this will include all the mfiles as well, as the MFiles inherits the genericfile # class. # At the moment it is hard coded to include galaxy_library. But I wan't to put some javascript in so # the user can choose between files that have been registered with the galaxy library or with galaxy history # (needs to be distinct because we allow files to be added to galaxy multiple times) gfqs = GenericFile.objects.filter(galaxyfilelink__galaxy_library=True, galaxyfilelink__removed=False).distinct() # loop through all the data_inputs from the associated workflow for i in range(0, len(data_inputs)): # create a few lists of Input Datasetvarious useful information for the template di = data_inputs[i] data_input_names.append(di.name) data_input_steps.append(di.step) data_input_types.append(di.datatype) print('name', di.name) mtch = re.match('.*\((.*)\).*', di.name) if mtch: filetypes = mtch.group(1) filetype_l = filetypes.split(',') print('FILETYPE LIST', filetype_l) filetype_l = [f.strip() for f in filetype_l] # https://stackoverflow.com/questions/4824759/django-query-using-contains-each-value-in-a-list query = reduce(operator.or_, (Q(original_filename__icontains=item) for item in filetype_l)) # will output something like this # <Q: (OR: ('original_filename__icontains', '.txt'), ('original_filename__icontains', '.tsv'), # ('original_filename__icontains', '.tabular'))> print(query) gfqs_f = gfqs.filter(query) print(gfqs_f) else: gfqs_f = gfqs # Create an invidivual filter for each table f = self.filter_class(request.GET, queryset=gfqs_f, prefix=i) # Create a checkbox column for each table, so that the javascript can see which checkbox has been # selected check = tables.CheckBoxColumn(accessor="pk", attrs={ "th__input": {"onclick": "toggle(this)"}, "td__input": {"onclick": "addfile(this)"}}, orderable=False) # create a new table with the custom column table = self.table_class(f.qs, prefix=i, extra_columns=(('check{}'.format(i), check),), attrs={'name':i, 'id':i, 'class': 'paleblue'}) # load the table into the requestconfig rc.configure(table) # add the tables and filters to the list used in the template tables_l.append(table) filters.append(f) # create a list of all the information. Using a simple list format as it is just easy to use in the template l = zip(tables_l, filters, data_input_names, data_input_steps, data_input_types) return l def post(self, request, *args, **kwargs): form = self.form_class(request.POST) l = self.page_setup(request) if request.is_ajax(): # the ajax is used to get to record the chosen files request = ajax_post_selected(request) return render(request, self.template_name, {'form': form, 'list': l, 'wid':self.kwargs['wid']}) if form.is_valid(): # if valid then we can save the run form for tracking who has performed what wr = form.save(commit=False) wr.ran_by = request.user wr.workflow = self.get_queryset() wr.save() # Now get the files we want to use for the workflow # Table prefix is the the id for the PKD pkd = selected_items_2_pks(json.loads(request.session['selected_items'])) # reset so that the checkboxes are cleared for the next run request.session['selected_items'] = '' # Finally run the galaxy workflow!! run_galaxy_workflow(wr.workflow.id, request.user, wr.workflow.galaxyinstancetracking, pkd, l, wr.history_name, wr.library) # Show the progress of all the workflows running (including the one we have just started) return redirect(self.redirect_to) return render(request, self.template_name, {'form': form, 'list': l, 'wid':self.kwargs['wid']})
[docs]class WorkflowStatus(LoginRequiredMixin, View): ''' View available Galaxy. If any new workflows are added to a Galaxy instance the user should sync first before they can be seen in the table. ''' # template_name = 'galaxy/status.html' def get(self, request, *args, **kwargs): data = get_workflow_status(request.user) table = WorkflowStatusTable(data) return render(request, 'galaxy/workflow_status.html', {'table':table}) def post(self, request, *args, **kwargs): workflow_sync(request.user) # redirects to show the current available workflows return redirect('workflow_summary')
[docs]class HistoryDataBioBlendListView(LoginRequiredMixin, View): def get(self, request, *args, **kwargs): data = get_history_data(self.kwargs['pk'], request.user) table = HistoryDataTable(data) return render(request, 'galaxy/history_data_bioblend_list.html', {'table': table})
# return render(request, 'galaxy/history_status.html', {'table': table})
[docs]class HistoryDataCreateView(LoginRequiredMixin, CreateView): model = HistoryData success_url = '/galaxy/success' form_class = HistoryDataForm
[docs] def get_initial(self): user = self.request.user history_internal_id = self.kwargs.get('history_internal_id') history_data_galaxy_id = self.kwargs.get('galaxy_id') history_d = init_history_data_save_form(user, history_internal_id, history_data_galaxy_id) return {'history': history_internal_id, 'name': history_d['name'] }
def save_form(self, form): history_data_obj = form.save(commit=False) history_data_obj.user = self.request.user history_internal_id = self.kwargs.get('history_internal_id') history_data_galaxy_id = self.kwargs.get('galaxy_id') return history_data_save_form(self.request.user, history_internal_id, history_data_galaxy_id, history_data_obj)
[docs] def form_valid(self, form): self.save_form(form) return render(self.request, 'gfiles/submitted.html')
[docs]class HistoryListView(LoginRequiredMixin, TableFileSelectMixin,SingleTableMixin, ExportMixin, FilterView): ''' View and initiate a run for all registered workflows. Workflows can also be synced here as well ''' table_class = HistoryTable model = History filterset_class = HistoryFilter form_class = DeleteGalaxyHistoryForm template_name = 'galaxy/history_status.html' table_pagination = {"per_page": 50} initial_context = {'library': True, 'django_url': '/galaxy/history_status/', 'django_update_url': '/galaxy/history_status_update/'} def get(self, request, *args, **kwargs): # we have to overide the standard get to add some extra information to the context form = self.form_class() context = self.form2context(form) return render(request, self.template_name, context=context) def form_valid(self, request, form): pks = request.POST.getlist("check") delete_galaxy_histories(pks, purge=form.cleaned_data['purge'], user=request.user) return render(request, 'gfiles/submitted.html')
def history_status_update(request): data = get_history_status(request.user) return JsonResponse({'table_data':data}) def workflow_status_update(request): data = get_workflow_status(request.user) return JsonResponse({'table_data':data})
[docs]class WorkflowCreateView(LoginRequiredMixin, CreateView): model = Workflow form_class = WorkflowForm success_url = '/galaxy/success'
[docs] def form_valid(self, form): user = self.request.user w = form.save(commit=False) w.added_by = user w.save() w.save_related() return super(WorkflowCreateView, self).form_valid(form)
[docs] def get_initial(self): return {'galaxyinstancetracking':GalaxyInstanceTracking.objects.last()}
[docs] def get_form_kwargs(self, **kwargs): form_kwargs = super(WorkflowCreateView, self).get_form_kwargs(**kwargs) form_kwargs["user"] = self.request.user return form_kwargs
def checkbox_selected(request): selected = request.session['selected'] return JsonResponse({'selected': selected}) def selected_items_2_pks(selected_items): pkd = {} for k, v in six.iteritems(selected_items): if v: kl = k.split('_') table_id = kl[0] pk = kl[1] if not table_id in pkd: pkd[table_id] = [] pkd[table_id].append(pk) return pkd def ajax_post_selected(request): # request dictionary rd = json.loads(request.body) if 'selected_items' in request.session and request.session['selected_items']: # session dictionary # if isinstance(request.session['selected_items'], str) or isinstance(request.session['selected_items'], unicode): sd = json.loads(request.session['selected_items']) # else: # sd = request.session['selected_items'] for k, v in six.iteritems(rd['selected_items']): sd[k] = v request.session['selected_items'] = json.dumps(sd) else: request.session['selected_items'] = json.dumps(rd['selected_items']) return request def success(request): return render(request, 'galaxy/success.html')