diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 52f722a248c..64477073c0a 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -15,6 +15,7 @@ import galaxy.datatypes.registry from galaxy.datatypes.metadata import MetadataCollection from galaxy.security import RBACAgent, get_permitted_actions + import logging log = logging.getLogger( __name__ ) @@ -1095,6 +1096,58 @@ class MetadataFile( object ): raise # Return filename inside hashed directory return os.path.abspath( os.path.join( path, "metadata_%d.dat" % self.id ) ) + + +class FormDefinition( object ): + def __init__(self, name=None, desc=None, fields=[], current_form_id=None): + self.name = name + self.desc = desc + self.fields = fields + self.form_definition_current_id = current_form_id + +class FormDefinitionCurrent( object ): + def __init__(self, form_definition_id=None): + self.latest_form_id = form_definition_id + +class FormValues( object ): + def __init__(self, form_def_id=None, content=None): + self.form_definition_id = form_def_id + self.content = content + +class Request( object ): + def __init__(self, name=None, desc=None, request_type_id=None, user_id=None, form_values_id=None, library_id=None): + self.name = name + self.desc = desc + self.request_type_id = request_type_id + self.form_values_id = form_values_id + self.user_id = user_id + self.library_id = library_id + +class RequestType( object ): + def __init__(self, request_form_id=None, sample_form_id=None): + self.request_form_id = request_form_id + self.sample_form_id = sample_form_id + +class Sample( object ): + def __init__(self, name=None, desc=None, request_id=None, form_values_id=None): + self.name = name + self.desc = desc + self.request_id = request_id + self.form_values_id = form_values_id + +class SampleState( object ): + def __init__(self, name=None, desc=None, request_type_id=None): + self.name = name + self.desc = desc + self.request_type_id = request_type_id + +class SampleEvent( object ): + def __init__(self, sample_id=None, sample_state_id=None, comment=''): + self.sample_id = sample_id + self.sample_state_id = sample_state_id + self.comment = comment + + ## ---- Utility methods ------------------------------------------------------- diff --git a/lib/galaxy/model/mapping.py b/lib/galaxy/model/mapping.py index 5ccf0233a7c..6d4ed4144eb 100644 --- a/lib/galaxy/model/mapping.py +++ b/lib/galaxy/model/mapping.py @@ -520,9 +520,115 @@ MetadataFile.table = Table( "metadata_file", metadata, Column( "deleted", Boolean, index=True, default=False ), Column( "purged", Boolean, index=True, default=False ) ) + +FormDefinitionCurrent.table = Table('form_definition_current', metadata, + Column( "id", Integer, primary_key=True), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "latest_form_id", Integer, ForeignKey( "form_definition.id" ), index=True ), + Column( "deleted", Boolean, index=True, default=False )) + +# new table to store all the forms which is created by the admin +FormDefinition.table = Table('form_definition', metadata, + Column( "id", Integer, primary_key=True), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "name", TrimmedString( 255 ), nullable=False ), + Column( "desc", TEXT ), + Column( "form_definition_current_id", Integer, ForeignKey( "form_definition_current.id" ), index=True ), + Column( "fields", JSONType())) + +RequestType.table = Table('request_type', metadata, + Column( "id", Integer, primary_key=True), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "name", TrimmedString( 255 ), nullable=False ), + Column( "desc", TEXT ), + Column( "request_form_id", Integer, ForeignKey( "form_definition.id" ), index=True ), + Column( "sample_form_id", Integer, ForeignKey( "form_definition.id" ), index=True ) ) + +FormValues.table = Table('form_values', metadata, + Column( "id", Integer, primary_key=True), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "form_definition_id", Integer, ForeignKey( "form_definition.id" ), index=True ), + Column( "content", JSONType()) ) + +Request.table = Table('request', metadata, + Column( "id", Integer, primary_key=True), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "name", TrimmedString( 255 ), nullable=False ), + Column( "desc", TEXT ), + Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), + Column( "request_type_id", Integer, ForeignKey( "request_type.id" ), index=True ), + Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), + Column( "library_id", Integer, ForeignKey( "library.id" ), index=True ), + Column( "deleted", Boolean, index=True, default=False ) ) + +Sample.table = Table('sample', metadata, + Column( "id", Integer, primary_key=True), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "name", TrimmedString( 255 ), nullable=False ), + Column( "desc", TEXT ), + Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), + Column( "request_id", Integer, ForeignKey( "request.id" ), index=True ), + Column( "deleted", Boolean, index=True, default=False ) ) + +# new table to store all the possible sample states and the sample type it +# belongs to +SampleState.table = Table('sample_state', metadata, + Column( "id", Integer, primary_key=True), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "name", TrimmedString( 255 ), nullable=False ), + Column( "desc", TEXT ), + Column( "request_type_id", Integer, ForeignKey( "request_type.id" ), index=True ) ) + +SampleEvent.table = Table('sample_event', metadata, + Column( "id", Integer, primary_key=True), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "sample_id", Integer, ForeignKey( "sample.id" ), index=True ), + Column( "sample_state_id", Integer, ForeignKey( "sample_state.id" ), index=True ), + Column( "comment", TEXT ) ) + + + # With the tables defined we can define the mappers and setup the # relationships between the model objects. +assign_mapper( context, Sample, Sample.table, + properties=dict( events=relation( SampleEvent, backref="sample", + order_by=desc(SampleEvent.table.c.update_time) ), + ) ) + +assign_mapper( context, FormValues, FormValues.table, properties=None) + +assign_mapper( context, Request, Request.table, properties=None) + +assign_mapper( context, RequestType, RequestType.table, + properties=dict( states=relation( SampleState, backref="request_type", + order_by=desc(SampleState.table.c.update_time) ), + ) ) + +assign_mapper( context, FormDefinition, FormDefinition.table, properties=None) + +assign_mapper( context, FormDefinitionCurrent, FormDefinitionCurrent.table, + properties=dict( forms=relation( FormDefinition, backref='form_definition_current', + cascade="all, delete-orphan", + primaryjoin=( FormDefinitionCurrent.table.c.id == FormDefinition.table.c.form_definition_current_id ) ), + latest_form=relation( FormDefinition, post_update=True, + primaryjoin=( FormDefinitionCurrent.table.c.latest_form_id == FormDefinition.table.c.id ) ) + ) ) + +assign_mapper( context, SampleEvent, SampleEvent.table, properties=None) + +assign_mapper( context, SampleState, SampleState.table, + properties=None #dict( sample=relation( Sample, backref="sample" ), + )# ) + assign_mapper( context, ValidationError, ValidationError.table ) assign_mapper( context, HistoryDatasetAssociation, HistoryDatasetAssociation.table, @@ -590,6 +696,8 @@ assign_mapper( context, HistoryUserShareAssociation, HistoryUserShareAssociation assign_mapper( context, User, User.table, properties=dict( histories=relation( History, backref="user", order_by=desc(History.table.c.update_time) ), + requests=relation( Request, backref="user", + order_by=desc(Request.table.c.update_time) ), active_histories=relation( History, primaryjoin=( ( History.table.c.user_id == User.table.c.id ) & ( not_( History.table.c.deleted ) ) ), order_by=desc( History.table.c.update_time ) ), galaxy_sessions=relation( GalaxySession, order_by=desc( GalaxySession.table.c.update_time ) ), stored_workflow_menu_entries=relation( StoredWorkflowMenuEntry, backref="user", diff --git a/lib/galaxy/model/migrate/versions/0008_galaxy_forms.py b/lib/galaxy/model/migrate/versions/0008_galaxy_forms.py new file mode 100644 index 00000000000..81b1558b432 --- /dev/null +++ b/lib/galaxy/model/migrate/versions/0008_galaxy_forms.py @@ -0,0 +1,189 @@ +from sqlalchemy import * +from sqlalchemy.orm import * +from sqlalchemy.exceptions import * +from migrate import * +from migrate.changeset import * + +import datetime +now = datetime.datetime.utcnow + +import sys, logging +log = logging.getLogger( __name__ ) +log.setLevel(logging.DEBUG) +handler = logging.StreamHandler( sys.stdout ) +format = "%(name)s %(levelname)s %(asctime)s %(message)s" +formatter = logging.Formatter( format ) +handler.setFormatter( formatter ) +log.addHandler( handler ) + +# Need our custom types, but don't import anything else from model +from galaxy.model.custom_types import * + +metadata = MetaData( migrate_engine ) +db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, transactional=False ) ) + + +FormDefinitionCurrent_table = Table('form_definition_current', metadata, + Column( "id", Integer, primary_key=True), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "latest_form_id", Integer, + #ForeignKey( "form_definition.id", use_alter=True, name='form_definition_current_latest_form_id_fk'), + index=True ), + Column( "deleted", Boolean, index=True, default=False )) +FormDefinition_table = Table('form_definition', metadata, + Column( "id", Integer, primary_key=True), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "name", TrimmedString( 255 ), nullable=False ), + Column( "desc", TEXT ), + Column( "form_definition_current_id", Integer, ForeignKey( "form_definition_current.id" ), index=True, nullable=False ), + Column( "fields", JSONType()) ) + +FormValues_table = Table('form_values', metadata, + Column( "id", Integer, primary_key=True), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "form_definition_id", Integer, ForeignKey( "form_definition.id" ), index=True ), + Column( "content", JSONType()) ) + +RequestType_table = Table('request_type', metadata, + Column( "id", Integer, primary_key=True), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "name", TrimmedString( 255 ), nullable=False ), + Column( "desc", TEXT ), + Column( "request_form_id", Integer, ForeignKey( "form_definition.id" ), index=True ), + Column( "sample_form_id", Integer, ForeignKey( "form_definition.id" ), index=True ) ) +# request table +Request_table = Table('request', metadata, + Column( "id", Integer, primary_key=True), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "name", TrimmedString( 255 ), nullable=False ), + Column( "desc", TEXT ), + Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), + Column( "request_type_id", Integer, ForeignKey( "request_type.id" ), index=True ), + Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), + Column( "library_id", Integer, ForeignKey( "library.id" ), index=True ), + Column( "deleted", Boolean, index=True, default=False ) ) + +Sample_table = Table('sample', metadata, + Column( "id", Integer, primary_key=True), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "name", TrimmedString( 255 ), nullable=False ), + Column( "desc", TEXT ), + Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), + Column( "request_id", Integer, ForeignKey( "request.id" ), index=True ), + Column( "deleted", Boolean, index=True, default=False ) ) + +SampleState_table = Table('sample_state', metadata, + Column( "id", Integer, primary_key=True), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "name", TrimmedString( 255 ), nullable=False ), + Column( "desc", TEXT ), + Column( "request_type_id", Integer, ForeignKey( "request_type.id" ), index=True ) ) + +SampleEvent_table = Table('sample_event', metadata, + Column( "id", Integer, primary_key=True), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "sample_id", Integer, ForeignKey( "sample.id" ), index=True ), + Column( "sample_state_id", Integer, ForeignKey( "sample_state.id" ), index=True ), + Column( "comment", TEXT ) ) + + + + +def upgrade(): + # Load existing tables + metadata.reflect() + + # Add all of the new tables above +# metadata.create_all() + try: + FormDefinitionCurrent_table.create() + except Exception, e: + log.debug( "Creating form_definition_current table failed: %s" % str( e ) ) + try: + FormDefinition_table.create() + except Exception, e: + log.debug( "Creating form_definition table failed: %s" % str( e ) ) + # Add 1 foreign key constraint to the form_definition_current table + if FormDefinitionCurrent_table and FormDefinition_table: + try: + cons = ForeignKeyConstraint( [FormDefinitionCurrent_table.c.latest_form_id], + [FormDefinition_table.c.id], + name='form_definition_current_latest_form_id_fk' ) + # Create the constraint + cons.create() + except Exception, e: + log.debug( "Adding foreign key constraint 'form_definition_current_latest_form_id_fk' to table 'form_definition_current' failed: %s" % ( str( e ) ) ) + try: + FormValues_table.create() + except Exception, e: + log.debug( "Creating form_values table failed: %s" % str( e ) ) + try: + RequestType_table.create() + except Exception, e: + log.debug( "Creating request_type table failed: %s" % str( e ) ) + try: + Request_table.create() + except Exception, e: + log.debug( "Creating request table failed: %s" % str( e ) ) + try: + Sample_table.create() + except Exception, e: + log.debug( "Creating sample table failed: %s" % str( e ) ) + try: + SampleState_table.create() + except Exception, e: + log.debug( "Creating sample_state table failed: %s" % str( e ) ) + try: + SampleEvent_table.create() + except Exception, e: + log.debug( "Creating sample_event table failed: %s" % str( e ) ) + + + +def downgrade(): + # Load existing tables + metadata.reflect() + try: + FormDefinition_table.drop() + except Exception, e: + log.debug( "Dropping form_definition table failed: %s" % str( e ) ) + try: + FormDefinitionCurrent_table.drop() + except Exception, e: + log.debug( "Dropping form_definition_current table failed: %s" % str( e ) ) + try: + FormValues_table.drop() + except Exception, e: + log.debug( "Dropping form_values table failed: %s" % str( e ) ) + try: + Request_table.drop() + except Exception, e: + log.debug( "Dropping request table failed: %s" % str( e ) ) + try: + RequestType_table.drop() + except Exception, e: + log.debug( "Dropping request_type table failed: %s" % str( e ) ) + try: + Sample_table.drop() + except Exception, e: + log.debug( "Dropping sample table failed: %s" % str( e ) ) + try: + SampleState_table.drop() + except Exception, e: + log.debug( "Dropping sample_state table failed: %s" % str( e ) ) + try: + SampleEvent_table.drop() + except Exception, e: + log.debug( "Dropping sample_event table failed: %s" % str( e ) ) + + + + diff --git a/lib/galaxy/web/controllers/admin.py b/lib/galaxy/web/controllers/admin.py index 75b60fe3ea5..b2dde3b99e3 100644 --- a/lib/galaxy/web/controllers/admin.py +++ b/lib/galaxy/web/controllers/admin.py @@ -3,6 +3,7 @@ from datetime import datetime, timedelta from galaxy import util, datatypes from galaxy.web.base.controller import * from galaxy.model.orm import * +import sys import logging log = logging.getLogger( __name__ ) @@ -2097,4 +2098,123 @@ class Admin( BaseController ): else: last_updated[job.id] = '%s minutes' % int( delta.seconds / 60 ) return trans.fill_template( '/admin/jobs.mako', jobs = jobs, last_updated = last_updated, cutoff = cutoff, msg = msg, messagetype = messagetype ) - + + def _get_all_forms(self, trans, all_versions=False): + ''' + This method returns all the latest forms from the + form_definition_current table if all_versions is set to True. Otherwise + this method return all the versions of all the forms from form_definition + table + ''' + if all_versions: + return trans.app.model.FormDefinition.query().all() + else: + fdc_list = trans.app.model.FormDefinitionCurrent.query().all() + return [fdc.latest_form for fdc in fdc_list] + @web.expose + @web.require_admin + def manage_request_types( self, trans, **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + forms = self._get_all_forms(trans, all_versions=True) + return trans.fill_template( '/admin/requests/manage_request_types.mako', + request_types=trans.app.model.RequestType.query().all(), + forms=forms, + deleted=False, + show_deleted=False, + msg=msg, + messagetype=messagetype ) + @web.expose + @web.require_admin + def request_type( self, trans, **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + if params.get('create', False) == 'True': + return trans.fill_template( '/admin/requests/create_request_type.mako', + forms=self._get_all_forms(trans, all_versions=False), + msg=msg, + messagetype=messagetype) + elif params.get('add_states', False) == 'True': + return trans.fill_template( '/admin/requests/add_states.mako', + sample_type_name=util.restore_text( params.name ), + desc=util.restore_text( params.description ), + num_states=int(util.restore_text( params.num_states )), + request_form_id=int(util.restore_text( params.request_form_id )), + sample_form_id=int(util.restore_text( params.sample_form_id )), + msg=msg, + messagetype=messagetype) + elif params.get('save_new', False) == 'True': + st, msg = self._save_request_type(trans, params, None) + if not st: + return trans.fill_template( '/admin/requests/create_request_type.mako', + forms=self._get_all_forms(trans, all_versions=False), + msg=msg, + messagetype='error') + return trans.fill_template( '/admin/requests/manage_request_types.mako', + request_types=trans.app.model.RequestType.query().all(), + forms=self._get_all_forms(trans, all_versions=True), + deleted=False, + show_deleted=False, + msg=msg, + messagetype=messagetype ) + elif params.get('edit', False) == 'True': + rt = trans.app.model.RequestType.get(int(util.restore_text( params.id ))) + ss_list = trans.app.model.SampleState.filter(trans.app.model.SampleState.table.c.request_type_id == rt.id).all() + return trans.fill_template( '/admin/requests/edit_request_type.mako', + request_type=rt, + forms=self._get_all_forms(trans, all_versions=False), + states_list=ss_list, + deleted=False, + show_deleted=False, + msg=msg, + messagetype=messagetype ) + elif params.get('save_changes', False) == 'True': + st = trans.app.model.SampleType.get(int(util.restore_text( params.id ))) + st, msg = self._save_sample_type(trans, params, st.id) + if st: + msg = "The sample type '%s' has been updated with the changes." % st.name + messagetype = 'done' + else: + messagetype = 'error' + ss_list = trans.app.model.SampleState.filter(trans.app.model.SampleState.table.c.sample_type_id == st.id).all() + return trans.fill_template( '/admin/samples/edit_sample_type.mako', + sample_type=st, + forms=self._get_all_forms(trans, all_versions=False), + states_list=ss_list, + deleted=False, + show_deleted=False, + msg=msg, + messagetype=messagetype ) + def _save_request_type(self, trans, params, request_type_id): + num_states = int( params.get( 'num_states', 0 ) ) + proceed = True + for i in range( num_states ): + if not params.get( 'new_element_name_%i' % i, None ): + proceed = False + break + if not proceed: + msg = "All the state name(s) must be completed." + return None, msg + if not request_type_id: # create a new sample type to save + rt = trans.app.model.RequestType() + else: # use the existing sample type to save changes + rt = trans.app.model.RequestType.get(request_type_id) + rt.name = util.restore_text( params.name ) + rt.desc = util.restore_text( params.description ) or "" + rt.request_form_id = int(util.restore_text( params.request_form_id )) + rt.sample_form_id = int(util.restore_text( params.sample_form_id )) + rt.flush() + # set sample states + ss_list = trans.app.model.SampleState.filter(trans.app.model.SampleState.table.c.request_type_id == rt.id).all() + for ss in ss_list: + ss.delete() + ss.flush() + for i in range( num_states ): + name = params.get( 'new_element_name_%i' % i, None ) + desc = params.get( 'new_element_description_%i' % i, None ) + ss = trans.app.model.SampleState(name, desc, rt.id) + ss.flush() + msg = "The new sample type named '%s' with %s state(s) has been created" % (rt.name, num_states) + return rt, msg diff --git a/lib/galaxy/web/controllers/forms.py b/lib/galaxy/web/controllers/forms.py new file mode 100644 index 00000000000..bde94a2a4cf --- /dev/null +++ b/lib/galaxy/web/controllers/forms.py @@ -0,0 +1,323 @@ +from galaxy.web.base.controller import * +from galaxy.model.orm import * +from galaxy.datatypes import sniff +from galaxy import util +import logging, os, sys +from galaxy.web.form_builder import * +from galaxy.tools.parameters.basic import parameter_types +from elementtree.ElementTree import XML, Element +from galaxy.util.odict import odict +import copy + +log = logging.getLogger( __name__ ) + + +class Forms( BaseController ): + @web.expose + @web.require_admin + def index( self, trans, **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + return trans.fill_template( "/sample/index.mako", + default_action=params.get( 'default_action', None ), + msg=msg, + messagetype=messagetype ) + @web.expose + @web.require_admin + def manage( self, trans, **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + return self._show_forms_list(trans, msg, messagetype) + def _show_forms_list(self, trans, msg, messagetype): + fdc_list = trans.app.model.FormDefinitionCurrent.query().all() + return trans.fill_template( '/admin/forms/manage_forms.mako', + fdc_list=fdc_list, + deleted=False, + show_deleted=False, + msg=msg, + messagetype=messagetype ) + def _get_all_forms(self, trans, all_versions=False): + ''' + This method returns all the latest forms from the + form_definition_current table if all_versions is set to True. Otherwise + this method return all the versions of all the forms from form_definition + table + ''' + if all_versions: + return trans.app.model.FormDefinition.query().all() + else: + fdc_list = trans.app.model.FormDefinitionCurrent.query().all() + return [fdc.latest_form for fdc in fdc_list] + @web.expose + @web.require_admin + def new( self, trans, **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + if params.get('new', False) and not params.get('create_form', False): + self.current_form = {} + self.current_form['name'] = 'New Form' + self.current_form['desc'] = '' + self.current_form['fields'] = [] + inputs = [ ( 'Name', TextField('name', 40,self.current_form['name'] ) ), + ( 'Description', TextField('description', 40, self.current_form['desc']) ) ] + return trans.fill_template( '/admin/forms/create_form.mako', + inputs=inputs, + msg=msg, + messagetype=messagetype ) + elif params.get('create_form', False) == 'True': + if 'submitted' in params.new: + self.num_add_fields = 0 + fd, msg = self.__save_form(trans, params) + self.__get_saved_form(fd) + return self._show_forms_list(trans, msg, messagetype) + @web.expose + @web.require_admin + def edit( self, trans, **kwd ): + ''' + This callback method is for handling all the editing functions like: + remaning fields, adding/deleting fields, changing fields attributes + ''' + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + fd = trans.app.model.FormDefinition.get(int(util.restore_text( params.form_id ))) + # SHOW THE FORM FOR EDITING. + if params.get('show_form', False) == 'True': + self.__get_saved_form(fd) + # the following two dicts store the unsaved select box options + self.del_options = {} + self.add_options = {} + return self.__show(trans, params, fd) + # DELETE FIELD + elif params.get('remove_button', False): + self.__update_current_form(params) + index = int(params.get('remove_button', None).split(' ')[2])-1 + self.__remove_field(index) + return self.__show(trans, params, fd) + # SAVE CHANGES + elif params.get('save_changes_button', False) == 'Save': + self.__update_current_form(params) + fd_new, msg = self.__save_form(trans, params, fd.form_definition_current.id) + if not fd_new: + return self.__show(trans, params, fd, msg, 'error') + else: + fd = fd_new + msg = "The form '%s' has been updated with the changes." % fd.name + return self.__show(trans, params, fd, msg) + #ADD A FIELD + elif params.get('add_field_button', False) == 'Add field': + self.__update_current_form(params) + self.__add_field() + # show the form again with one empty field + return self.__show(trans, params, fd) + # SHOW FORM READ ONLY + elif params.get('read_only', False): + return trans.fill_template( '/admin/forms/show_form_read_only.mako', + form=fd, + msg=msg, + messagetype=messagetype ) + # REFRESH PAGE, SelectField is selected/deselected as the type of a field + elif params.get('refresh', False) == 'true': + self.__update_current_form(params) + return self.__show(trans, params, fd) + # REMOVE SelectField OPTION + elif params.get('select_box_options', False) == 'remove': + #self.__update_current_form(params) + index = int(params.get( 'field_index', None )) + option = int(params.get( 'option_index', None )) + del self.current_form['fields'][index]['selectlist'][option] + return self.__show(trans, params, fd) + # ADD SelectField OPTION + elif params.get('select_box_options', False) == 'add': + #self.__update_current_form(params) + index = int(params.get( 'field_index', None )) + self.current_form['fields'][index]['selectlist'].append('') + return self.__show(trans, params, fd) + def __remove_field(self, index): + del self.current_form['fields'][index] + def __add_field(self): + ''' + add an empty field to the fields list + ''' + empty_field = { 'label': '', + 'helptext': '', + 'visible': True, + 'required': False, + 'type': BaseField.form_field_types()[0], + 'selectlist': '' } + self.current_form['fields'].append(empty_field) + def __get_field(self, params, index): + name = params.get( 'field_name_%i' % index, None ) + helptext = params.get( 'field_helptext_%i' % index, None ) + required = params.get( 'field_required_%i' % index, False ) + field_type = params.get( 'field_type_%i' % index, None ) + if field_type == 'SelectField': + selectlist = self.__get_selectbox_options(params, index) + else: + selectlist = None + return {'label': name, + 'helptext': helptext, + 'visible': True, + 'required': required, + 'type': field_type, + 'selectlist': selectlist } + def __get_selectbox_options(self, params, index): + ''' + This method gets all the options entered by the user for field when + the fieldtype is SelectField + ''' + ctr=0 + sb_options = [] + while True: + option = params.get( 'field_'+str(index)+'_option_'+str(ctr), None ) + ctr = ctr+1 + if option: + sb_options.append(option) + else: + return sb_options + def __get_saved_form(self, fd): + self.current_form = {} + self.current_form['name'] = fd.name + self.current_form['desc'] = fd.desc + self.current_form['fields'] = list(copy.deepcopy(fd.fields)) + def __validate_form(self, params): + ''' + This method checks the following text inputs are filled out by the user + - the name of form + - name of all the fields + ''' + # form name + if not util.restore_text( params.name ): + return None, 'Form name must be filled.' + # fields + for i in range( len(self.current_form['fields']) ): + if not params.get( 'field_name_%i' % i, None ): + return None, "All the field label(s) must be completed." + return True, '' + def __get_form(self, params): + name = util.restore_text( params.name ) + desc = util.restore_text( params.description ) or "" + # set form fields + fields = [] + for i in range( len(self.current_form['fields']) ): + fields.append(self.__get_field(params, i)) + fields = fields + return name, desc, fields + def __update_current_form(self, params): + name, desc, fields = self.__get_form(params) + self.current_form = {} + self.current_form['name'] = name + self.current_form['desc'] = desc + self.current_form['fields'] = fields + + def __save_form(self, trans, params, fdc_id=None): + ''' + This method saves the current form + ''' + # check the form for invalid inputs + flag, msg = self.__validate_form(params) + if not flag: + return None, msg + fd = trans.app.model.FormDefinition() + fd.name, fd.desc, fd.fields = self.__get_form(params) + if fdc_id: # save changes to the existing form + # change the pointer in the form_definition_current table to point + # to this new record + fdc = trans.app.model.FormDefinitionCurrent.get(fdc_id) + else: # create a new form + fdc = trans.app.model.FormDefinitionCurrent() + # create corresponding row in the form_definition_current table + fd.form_definition_current = fdc + fdc.latest_form = fd + trans.sa_session.save_or_update( fdc ) + trans.sa_session.flush() + msg = "The new form named '%s' has been created. " % (fd.name) + request_types = trans.app.model.RequestType.query().all() + if not request_types: + msg = msg + "Now you can create requests to associate with this form." + return fd, msg + + class FieldUI(object): + def __init__(self, index, field=None, field_type=None): + ''' + This method returns a list of widgets which describes a field. This + includes label, helptext, type, & required/optional + ''' + self.index = index + self.label = TextField('field_name_'+str(index), 40, '') + self.helptext = TextField('field_helptext_'+str(index), 40, '') + self.fieldtype = SelectField('field_type_'+str(index), + refresh_on_change=True, + refresh_on_change_values=['SelectField']) + self.selectbox_options = [] + for ft in BaseField.form_field_types(): + self.fieldtype.add_option(ft, ft) + self.required = SelectField('field_required_'+str(index), display='radio') + self.required.add_option('Required', 'true') + self.required.add_option('Optional', 'true', selected=True) + if field: + self.fill(field, field_type) + def fill(self, field, field_type=None): + # label + self.label.value = field['label'] + # helptext + self.helptext.value = field['helptext'] + # type + self.fieldtype = SelectField('field_type_'+str(self.index), + refresh_on_change=True, + refresh_on_change_values=['SelectField']) + if field_type: + field['type'] = unicode(field_type) + if field_type == 'SelectField' and not field['selectlist']: + field['selectlist'] = ['', ''] + for ft in BaseField.form_field_types(): + if ft == field['type']: + self.fieldtype.add_option(ft, ft, selected=True) + if ft == 'SelectField': + self.selectbox_ui(field) + else: + self.fieldtype.add_option(ft, ft) + # required/optional + if field['required']: + self.required = SelectField('required_'+str(self.index), display='radio') + self.required.add_option('Required', 'true', selected=True) + self.required.add_option('Optional', 'true') + def selectbox_ui(self, field): + self.selectbox_options = [] + if field['selectlist']: + for ctr, option in enumerate(field['selectlist']): + self.selectbox_options.append(('Option '+str(ctr+1), + TextField('field_'+str(self.index)+'_option_'+str(ctr), + 40, option))) + def get(self): + return [( 'Label', self.label ), + ( 'Help text', self.helptext ), + ( 'Type', self.fieldtype, self.selectbox_options), + ( '', self.required)] + def __repr__(self): + return str(self.index)+'.'+self.label + def label(self): + return str(self.index)+'.'+self.label + + def __show(self, trans, params, form, msg=None, messagetype='done'): + ''' + This method displays the form and any of the changes made to it + ''' + # name & description + form_details = [ ( 'Name', TextField('name', 40, self.current_form['name']) ), + ( 'Description', TextField('description', 40, self.current_form['desc']) ) ] + # fields + field_details = [] + for index, field in enumerate(self.current_form['fields']): + field_ui = self.FieldUI(index, field) + field_details.append( field_ui.get() ) + return trans.fill_template( '/admin/forms/edit_form.mako', + form_details=form_details, + field_details=field_details, + form=form, + field_types=BaseField.form_field_types(), + msg=msg, + messagetype=messagetype ) diff --git a/lib/galaxy/web/controllers/requests.py b/lib/galaxy/web/controllers/requests.py new file mode 100644 index 00000000000..73f39573703 --- /dev/null +++ b/lib/galaxy/web/controllers/requests.py @@ -0,0 +1,429 @@ +from galaxy.web.base.controller import * +from galaxy.web.framework.helpers import time_ago, iff, grids +from galaxy.model.orm import * +from galaxy.datatypes import sniff +from galaxy import util +from galaxy.util.streamball import StreamBall +import logging, tempfile, zipfile, tarfile, os, sys +from galaxy.web.form_builder import * + +log = logging.getLogger( __name__ ) + +# States for passing messages +SUCCESS, INFO, WARNING, ERROR = "done", "info", "warning", "error" + +class RequestsListGrid( grids.Grid ): + title = "Requests" + model_class = model.Request + default_sort_key = "-create_time" + columns = [ + grids.GridColumn( "Name", key="name", + link=( lambda item: iff( item.deleted, None, dict( operation="show_request", id=item.id ) ) ), + attach_popup=True ), + grids.GridColumn( "Description", key='desc'), + grids.GridColumn( "Sample(s)", method='number_of_samples', + link=( lambda item: iff( item.deleted, None, dict( operation="samples", id=item.id ) ) ), ), + grids.GridColumn( "Type", key="request_type_id", method='get_request_type'), + grids.GridColumn( "Last update", key="update_time", format=time_ago ) + ] + operations = [ +# grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted ) ), + grids.GridOperation( "Samples", allow_multiple=False, condition=( lambda item: not item.deleted ) ), +# grids.GridOperation( "Delete", condition=( lambda item: not item.deleted ) ), +# grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ), + ] + standard_filters = [ + grids.GridColumnFilter( "Active", args=dict( deleted=False ) ), + grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ), + grids.GridColumnFilter( "All", args=dict( deleted='All' ) ) + ] + #default_filter = dict( deleted=False ) + def get_current_item( self, trans ): + return None + def get_request_type(self, trans, request): + request_type = trans.app.model.RequestType.get(request.request_type_id) + return request_type.name + def apply_default_filter( self, trans, query ): + return query.filter_by( user=trans.user ) + def number_of_samples(self, trans, request): + return str(len(trans.app.model.Sample.filter(trans.app.model.Sample.table.c.request_id==request.id).all())) + +class SamplesListGrid( grids.Grid ): + model_class = model.Sample + default_sort_key = "-create_time" + columns = [ + grids.GridColumn( "Name", key="name", + link=( lambda item: iff( item.deleted, None, dict( operation="show_sample", id=item.id ) ) ), + attach_popup=True ), + grids.GridColumn( "Description", key='desc' ), + grids.GridColumn( "Status", method="get_status", + link=( lambda item: iff( item.deleted, None, dict( operation="events", id=item.id ) ) )), + + grids.GridColumn( "Last update", key="update_time", format=time_ago ) + + # Valid for filtering but invisible + #grids.GridColumn( "Deleted", key="deleted", visible=False ) + ] + operations = [ +# grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted ) ), +# grids.GridOperation( "Delete", condition=( lambda item: not item.deleted ) ), +# grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ), + + ] + standard_filters = [ + grids.GridColumnFilter( "Active", args=dict( deleted=False ) ), + grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ), + grids.GridColumnFilter( "All", args=dict( deleted='All' ) ) + ] + def __init__(self, request): + self.request = request + def get_current_item( self, trans ): + return None + def apply_default_filter( self, trans, query ): + return query.filter_by( request_id=self.request.id ) + def get_status(self, trans, sample): + all_states = trans.app.model.SampleEvent.filter(trans.app.model.SampleEvent.table.c.sample_id == sample.id).all() + curr_state = trans.app.model.SampleState.get(all_states[len(all_states)-1].sample_state_id) + return curr_state.name + +class Requests( BaseController ): + request_grid = RequestsListGrid() + + @web.expose + def index( self, trans ): + return trans.fill_template( "requests/index.mako" ) + def get_authorized_libs(self, trans): + all_libraries = trans.app.model.Library.filter(trans.app.model.Library.table.c.deleted == False).order_by(trans.app.model.Library.name).all() + authorized_libraries = [] + for library in all_libraries: + if trans.app.security_agent.allow_action(trans.user, trans.app.security_agent.permitted_actions.LIBRARY_ADD, library_item=library) or trans.app.security_agent.allow_action(trans.user, trans.app.security_agent.permitted_actions.LIBRARY_MODIFY, library_item=library) or trans.app.security_agent.allow_action(trans.user, trans.app.security_agent.permitted_actions.LIBRARY_MANAGE, library_item=library) or trans.app.security_agent.check_folder_contents(trans.user, library) or trans.app.security_agent.show_library_item(trans.user, library): + authorized_libraries.append(library) + return authorized_libraries + @web.expose + def list( self, trans, **kwargs ): + ''' + List all request made by the current user + ''' + status = message = None + if 'operation' in kwargs: + operation = kwargs['operation'].lower() + if operation == "show_request": + id = trans.security.decode_id(kwargs['id']) + return self.show_read_only(trans, id) + elif operation == "samples": + id = trans.security.decode_id(kwargs['id']) + return self.show_samples(trans, id, kwargs) + elif operation == "show_sample": + id = trans.security.decode_id(kwargs['id']) + return self.show_sample_read_only(trans, id) + elif operation == "events": + id = trans.security.decode_id(kwargs['id']) + return self.show_events(trans, id) + # Render the list view + return self.request_grid( trans, status=status, message=message, template='/requests/grid.mako', **kwargs ) + def show_samples(self, trans, id, kwargs): + ''' + Shows all the samples associated with this request + ''' + status = message = None + request = trans.app.model.Request.get(id) + self.samples_grid = SamplesListGrid(request) + return self.samples_grid( trans, status=status, message=message, template='/sample/grid.mako', **kwargs ) + def show_read_only(self, trans, id): + ''' + Shows the request details + ''' + request = trans.app.model.Request.get(id) + request_type = trans.app.model.RequestType.get(request.request_type_id) + request_form = trans.app.model.FormDefinition.get(request_type.request_form_id) + request_values = trans.app.model.FormValues.get(request.form_values_id) + libraries = self.get_authorized_libs(trans) + # list of widgets to be rendered on the request form + request_details = [] + # main details + request_details.append(dict(label='Name', + value=request.name, + helptext='')) + request_details.append(dict(label='Description', + value=request.desc, + helptext='')) + request_details.append(dict(label='Type', + value=request_type.name, + helptext='')) + request_details.append(dict(label='Date created', + value=request.create_time, + helptext='')) + request_details.append(dict(label='Date updated', + value=request.create_time, + helptext='')) + request_details.append(dict(label='User', + value=str(trans.user.email), + helptext='')) + # library associated + request_details.append(dict(label='Library', + value=trans.app.model.Library.get(request.library_id).name, + helptext='Associated library where the resultant \ + dataset will be stored')) + # form fields + for field in request_form.fields: + if field['required']: + req = 'Required' + else: + req = 'Optional' + request_details.append(dict(label=field['label'], + value=request_values.content[field['label']], + helptext=field['helptext']+' ('+req+')')) + return trans.fill_template( '/requests/view_request.mako', + request_form_id=request_form.id, + request_details=request_details, + request_type=request_type) + @web.expose + def new(self, trans, **kwd): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + if params.get('select_request_type', False) == 'True': + return trans.fill_template( '/requests/select_request_type.mako', + request_types=trans.app.model.RequestType.query().all(), + libraries=self.get_authorized_libs(trans), + msg=msg, + messagetype=messagetype ) + elif params.get('create', False) == 'True': + request_type_id = int(util.restore_text( params.request_type_id )) + return self.__show_request_form(trans, params, request_type_id) + elif params.get('save', False) == 'True': + request = self.__save(trans, params) + msg = 'The new request named %s has been created' % request.name + request_type_id = int(util.restore_text( params.request_type_id )) + return trans.response.send_redirect( web.url_for( controller='requests', + action='list', + msg=msg , + messagetype='done') ) + return self.__show_request_form(trans, params, request_type_id, request=request) + def __save(self, trans, params, request_id=None): + ''' + This method save a new request if request_id is None. + ''' + if not request_id: + request_type_id = int(util.restore_text( params.request_type_id )) + request_form_id = trans.app.model.RequestType.get(request_type_id).request_form_id + request_form = trans.app.model.FormDefinition.get(request_form_id) + else: + request = trans.app.model.Request.get(request_id) + form_values = trans.app.model.FormValues.get(request.form_values_id) + request_form = trans.app.model.FormDefinition.get(form_values.request_form_id) + name = params.get('name', '') + desc = params.get('desc', '') + library_id = params.get('library', '') + values = {} + for field in request_form.fields: + values[field['label']] = params.get(field['label'], '') + if not request_id: + form_values = trans.app.model.FormValues(request_form_id, values) + form_values.flush() + request = trans.app.model.Request(name, desc, request_type_id, + trans.user.id, form_values.id, + library_id) + request.flush() + else: + form_values.content = values + form_values.flush() + return request + def __show_request_form(self, trans, params, request_type_id, request=None): + request_type = trans.app.model.RequestType.get(request_type_id) + request_form_id = request_type.request_form_id + if request: + form_values = trans.app.model.FormValues.get(request.form_values_id) + else: + form_values = None + # list of widgets to be rendered on the request form + widgets = [] + widgets.append(dict(label='Name', + widget=TextField('name'), + helptext='(Required)')) + widgets.append(dict(label='Description', + widget=TextField('desc'), + helptext='(Optional)')) + widgets[0]['widget'].set_size(40) + widgets[1]['widget'].set_size(40) + # libraries selectbox + libraries = self.get_authorized_libs(trans) + lib_list = SelectField('library') + for lib in libraries: + lib_list.add_option(lib.name, lib.id) + widgets.append(dict(label='Library', + widget=lib_list, + helptext='Associated library where the resultant \ + dataset will be stored')) + widgets = self.__create_form(trans, params, request_form_id, widgets, form_values) + title = 'Add a new request of type: %s' % request_type.name + return trans.fill_template( '/requests/new_request.mako', + request_form_id=request_form_id, + request_type=request_type, + widgets=widgets, + title=title) + + def __create_form(self, trans, params, form_id, widgets=[], form_values=None): + form = trans.app.model.FormDefinition.get(form_id) + if not form_values: + values = {} + for field in form.fields: + if field['type'] in ['SelectField' or 'CheckBoxField']: + values[field['label']] = False + else: + values[field['label']] = '' + else: + values = form_values.content + # form fields + for field in form.fields: + fw = eval(field['type'])(field['label']) + if field['type'] == 'TextField': + fw.set_size(40) + fw.value = values[field['label']] + elif field['type'] == 'TextArea': + fw.set_size(3, 40) + fw.value = values[field['label']] + elif field['type'] == 'SelectField': + for option in field['selectlist']: + fw.add_option(option, option, values[field['label']]) + elif field['type'] == 'CheckBoxField': + fw.checked = values[field['label']] + + if field['required']: + req = 'Required' + else: + req = 'Optional' + widgets.append(dict(label=field['label'], + widget=fw, + helptext=field['helptext']+' ('+req+')')) + return widgets + @web.expose + def add_sample(self, trans, **kwd): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + request_id = int(util.restore_text( params.get( 'id', '' ) )) + return self.__show_sample_form(trans, params, request_id) + + def __show_sample_form(self, trans, params, request_id, sample=None): + request = trans.app.model.Request.get(request_id) + request_type = trans.app.model.RequestType.get(request.request_type_id) + sample_form_id = request_type.sample_form_id + if sample: + form_values = trans.app.model.FormValues.get(sample.form_values_id) + else: + form_values = None + # list of widgets to be rendered on the request form + widgets = [] + widgets.append(dict(label='Name', + widget=TextField('name'), + helptext='(Required)')) + widgets.append(dict(label='Description', + widget=TextField('desc'), + helptext='(Optional)')) + widgets[0]['widget'].set_size(40) + widgets[1]['widget'].set_size(40) + widgets = self.__create_form(trans, params, sample_form_id, widgets, form_values) + title = 'Add a new sample to request: %s of type: %s' % (request.name, request_type.name) + return trans.fill_template( '/sample/new_sample.mako', + sample_form_id=sample_form_id, + request_id=request.id, + widgets=widgets, + title=title) + @web.expose + def samples(self, trans, **kwd): + params = util.Params( kwd ) + if params.get('save', False) == 'True': + sample = self.__save_sample(trans, params) + return trans.response.send_redirect( web.url_for( controller='requests', + action='list', + operation='samples', + id=trans.security.encode_id(sample.request_id)) ) + def __save_sample(self, trans, params, sample_id=None): + if not sample_id: + request = trans.app.model.Request.get(int(util.restore_text( params.request_id ))) + request_type = trans.app.model.RequestType.get(request.request_type_id) + sample_form = trans.app.model.FormDefinition.get(request_type.sample_form_id) + else: + sample = trans.app.model.Sample.get(sample_id) + form_data = trans.app.model.FormData.get(sample.form_data_id) + form = trans.app.model.FormDefinition.get(form_data.form_definition_id) + name = params.get('name', '') + desc = params.get('desc', '') + values = {} + for field in sample_form.fields: + values[field['label']] = params.get(field['label'], '') + if not sample_id: + form_values = trans.app.model.FormValues(sample_form.id, values) + form_values.flush() + sample = trans.app.model.Sample(name, desc, request.id, form_values.id) + sample.flush() + # set the initial state + state = trans.app.model.SampleState.filter(trans.app.model.SampleState.table.c.request_type_id == request_type.id).first() + event = trans.app.model.SampleEvent(sample.id, state.id) + event.flush() + else: + form_data.content = values + form_data.flush() + return sample + def show_sample_read_only(self, trans, sample_id): + ''' + Shows the sample details + ''' + sample = trans.app.model.Sample.get(sample_id) + request = trans.app.model.Request.get(sample.request_id) + request_type = trans.app.model.RequestType.get(request.request_type_id) + sample_form = trans.app.model.FormDefinition.get(request_type.sample_form_id) + sample_values = trans.app.model.FormValues.get(sample.form_values_id) + # list of widgets to be rendered on the request form + sample_details = [] + # main details + sample_details.append(dict(label='Name', + value=sample.name, + helptext='')) + sample_details.append(dict(label='Description', + value=sample.desc, + helptext='')) + sample_details.append(dict(label='Date created', + value=sample.create_time, + helptext='')) + sample_details.append(dict(label='Date updated', + value=sample.create_time, + helptext='')) + sample_details.append(dict(label='User', + value=str(trans.user.email), + helptext='')) + sample_details.append(dict(label='Request', + value=request.name, + helptext='Name/ID of the request this sample belongs to.')) + # get the current state of the sample + all_states = trans.app.model.SampleEvent.filter(trans.app.model.SampleEvent.table.c.sample_id == sample_id).all() + curr_state = trans.app.model.SampleState.get(all_states[len(all_states)-1].sample_state_id) + sample_details.append(dict(label='State', + value=curr_state.name, + helptext=curr_state.desc)) + # form fields + for field in sample_form.fields: + if field['required']: + req = 'Required' + else: + req = 'Optional' + sample_details.append(dict(label=field['label'], + value=sample_values.content[field['label']], + helptext=field['helptext']+' ('+req+')')) + return trans.fill_template( '/sample/view_sample.mako', + sample_details=sample_details) + def show_events(self, trans, sample_id): + sample = trans.app.model.Sample.get(sample_id) + events_list = [] + for event in trans.app.model.SampleEvent.filter(trans.app.model.SampleEvent.table.c.sample_id == sample_id).all(): + state = trans.app.model.SampleState.get(event.sample_state_id) + events_list.append((state.name, event.update_time, state.desc, event.comment)) + return trans.fill_template( '/sample/sample_events.mako', + events_list=events_list, + sample_name=sample.name, + request=trans.app.model.Request.get(sample.request_id).name) + + + + diff --git a/lib/galaxy/web/controllers/requests_admin.py b/lib/galaxy/web/controllers/requests_admin.py new file mode 100644 index 00000000000..e5af7438d4f --- /dev/null +++ b/lib/galaxy/web/controllers/requests_admin.py @@ -0,0 +1,315 @@ +from galaxy.web.base.controller import * +from galaxy.web.framework.helpers import time_ago, iff, grids +from galaxy.model.orm import * +from galaxy.datatypes import sniff +from galaxy import util +from galaxy.util.streamball import StreamBall +import logging, tempfile, zipfile, tarfile, os, sys +from galaxy.web.form_builder import * +from datetime import datetime, timedelta + +log = logging.getLogger( __name__ ) + + +# States for passing messages +SUCCESS, INFO, WARNING, ERROR = "done", "info", "warning", "error" + +class RequestsListGrid( grids.Grid ): + title = "Requests" + model_class = model.Request + default_sort_key = "-create_time" + columns = [ + grids.GridColumn( "Name", key="name", + link=( lambda item: iff( item.deleted, None, dict( operation="show_request", id=item.id ) ) ), + attach_popup=True ), + grids.GridColumn( "Description", key="desc"), + grids.GridColumn( "Sample(s)", method='number_of_samples', + link=( lambda item: iff( item.deleted, None, dict( operation="samples", id=item.id ) ) ), ), + grids.GridColumn( "Type", key="request_type_id", method='get_request_type'), + grids.GridColumn( "Last update", key="update_time", format=time_ago ), + grids.GridColumn( "User", key="user_id", method='get_user') + + ] + operations = [ +# grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted ) ), + grids.GridOperation( "Samples", allow_multiple=False, condition=( lambda item: not item.deleted ) ), +# grids.GridOperation( "Delete", condition=( lambda item: not item.deleted ) ), +# grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ), + ] + standard_filters = [ + grids.GridColumnFilter( "Active", args=dict( deleted=False ) ), + grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ), + grids.GridColumnFilter( "All", args=dict( deleted='All' ) ) + ] + def get_user(self, trans, request): + return trans.app.model.User.get(request.user_id).email + def get_current_item( self, trans ): + return None + def get_request_type(self, trans, request): + request_type = trans.app.model.RequestType.get(request.request_type_id) + return request_type.name + def apply_default_filter( self, trans, query ): + return query + def number_of_samples(self, trans, request): + return str(len(trans.app.model.Sample.filter(trans.app.model.Sample.table.c.request_id==request.id).all())) + +class SamplesListGrid( grids.Grid ): + model_class = model.Sample + default_sort_key = "-create_time" + columns = [ + grids.GridColumn( "Name", key="name", + link=( lambda item: iff( item.deleted, None, dict( operation="show_sample", id=item.id ) ) ), + attach_popup=True ), + grids.GridColumn( "Description", key='desc' ), + grids.GridColumn( "Status", method="get_status", + link=( lambda item: iff( item.deleted, None, dict( operation="events", id=item.id ) ) )), + + grids.GridColumn( "Last update", key="update_time", format=time_ago ) + + # Valid for filtering but invisible + #grids.GridColumn( "Deleted", key="deleted", visible=False ) + ] + operations = [ +# grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted ) ), + grids.GridOperation( "Change state", condition=( lambda item: not item.deleted ) ), +# grids.GridOperation( "Delete", condition=( lambda item: not item.deleted ) ), +# grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ), + + ] + standard_filters = [ + grids.GridColumnFilter( "Active", args=dict( deleted=False ) ), + grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ), + grids.GridColumnFilter( "All", args=dict( deleted='All' ) ) + ] + def __init__(self, request, user): + self.request = request + self.user = user + def get_current_item( self, trans ): + return None + def apply_default_filter( self, trans, query ): + return query.filter_by( request_id=self.request.id ) + def get_status(self, trans, sample): + all_states = trans.app.model.SampleEvent.filter(trans.app.model.SampleEvent.table.c.sample_id == sample.id).all() + curr_state = trans.app.model.SampleState.get(all_states[len(all_states)-1].sample_state_id) + return curr_state.name + +class Requests( BaseController ): + request_grid = RequestsListGrid() + + @web.expose + @web.require_admin + def index( self, trans ): + return trans.fill_template( "/admin/requests/index.mako" ) + def get_authorized_libs(self, trans): + all_libraries = trans.app.model.Library.filter(trans.app.model.Library.table.c.deleted == False).order_by(trans.app.model.Library.name).all() + authorized_libraries = [] + for library in all_libraries: + if trans.app.security_agent.allow_action(trans.user, trans.app.security_agent.permitted_actions.LIBRARY_ADD, library_item=library) or trans.app.security_agent.allow_action(trans.user, trans.app.security_agent.permitted_actions.LIBRARY_MODIFY, library_item=library) or trans.app.security_agent.allow_action(trans.user, trans.app.security_agent.permitted_actions.LIBRARY_MANAGE, library_item=library) or trans.app.security_agent.check_folder_contents(trans.user, library) or trans.app.security_agent.show_library_item(trans.user, library): + authorized_libraries.append(library) + return authorized_libraries + @web.expose + @web.require_admin + def list( self, trans, **kwargs ): + ''' + List all request made by the current user + ''' + status = message = None + if 'operation' in kwargs: + operation = kwargs['operation'].lower() + if operation == "show_request": + id = trans.security.decode_id(kwargs['id']) + return self.show_read_only(trans, id) + elif operation == "samples": + id = trans.security.decode_id(kwargs['id']) + return self.show_samples(trans, id, kwargs) + elif operation == "show_sample": + id = trans.security.decode_id(kwargs['id']) + return self.show_sample_read_only(trans, id) + elif operation == "change state": + id_list = [trans.security.decode_id(id) for id in util.listify(kwargs['id'])] + return self.change_state(trans, id_list) + elif operation == "events": + id = trans.security.decode_id(kwargs['id']) + return self.show_events(trans, id) + # Render the list view + return self.request_grid( trans, status=status, message=message, template='/admin/requests/grid.mako', **kwargs ) + def show_samples(self, trans, id, kwargs): + ''' + Shows all the samples associated with this request + ''' + status = message = None + request = trans.app.model.Request.get(id) + self.samples_grid = SamplesListGrid(request, trans.app.model.User.get(request.user_id)) + return self.samples_grid( trans, status=status, message=message, template='/admin/samples/grid.mako', **kwargs ) + def show_read_only(self, trans, id): + ''' + Shows the request details + ''' + request = trans.app.model.Request.get(id) + request_type = trans.app.model.RequestType.get(request.request_type_id) + request_form = trans.app.model.FormDefinition.get(request_type.request_form_id) + request_values = trans.app.model.FormValues.get(request.form_values_id) + libraries = self.get_authorized_libs(trans) + # list of widgets to be rendered on the request form + request_details = [] + # main details + request_details.append(dict(label='Name', + value=request.name, + helptext='')) + request_details.append(dict(label='Description', + value=request.desc, + helptext='')) + request_details.append(dict(label='Type', + value=request_type.name, + helptext='')) + request_details.append(dict(label='Date created', + value=request.create_time, + helptext='')) + request_details.append(dict(label='Date updated', + value=request.create_time, + helptext='')) + request_details.append(dict(label='User', + value=str(trans.user.email), + helptext='')) + # library associated + request_details.append(dict(label='Library', + value=trans.app.model.Library.get(request.library_id).name, + helptext='Associated library where the resultant \ + dataset will be stored')) + # form fields + for field in request_form.fields: + if field['required']: + req = 'Required' + else: + req = 'Optional' + request_details.append(dict(label=field['label'], + value=request_values.content[field['label']], + helptext=field['helptext']+' ('+req+')')) + return trans.fill_template( '/admin/requests/view_request.mako', + request_form_id=request_form.id, + request_details=request_details, + request_type=request_type) + + @web.expose + @web.require_admin + def samples(self, trans, **kwd): + params = util.Params( kwd ) + if params.get('save', False) == 'True': + sample = self.__save_sample(trans, params) + return self.show_samples(trans, sample.request_id, {}) + def show_sample_read_only(self, trans, sample_id): + ''' + Shows the sample details + ''' + sample = trans.app.model.Sample.get(sample_id) + request = trans.app.model.Request.get(sample.request_id) + request_type = trans.app.model.RequestType.get(request.request_type_id) + sample_form = trans.app.model.FormDefinition.get(request_type.sample_form_id) + sample_values = trans.app.model.FormValues.get(sample.form_values_id) + # list of widgets to be rendered on the request form + sample_details = [] + # main details + sample_details.append(dict(label='Name', + value=request.name, + helptext='')) + sample_details.append(dict(label='Description', + value=request.desc, + helptext='')) + sample_details.append(dict(label='Date created', + value=sample.create_time, + helptext='')) + sample_details.append(dict(label='Date updated', + value=sample.create_time, + helptext='')) + sample_details.append(dict(label='User', + value=str(trans.user.email), + helptext='')) + sample_details.append(dict(label='Request', + value=request.name, + helptext='Name/ID of the request this sample belongs to.')) + # get the current state of the sample + all_states = trans.app.model.SampleEvent.filter(trans.app.model.SampleEvent.table.c.sample_id == sample_id).all() + curr_state = trans.app.model.SampleState.get(all_states[len(all_states)-1].sample_state_id) + sample_details.append(dict(label='State', + value=curr_state.name, + helptext=curr_state.desc)) + # form fields + for field in sample_form.fields: + if field['required']: + req = 'Required' + else: + req = 'Optional' + sample_details.append(dict(label=field['label'], + value=sample_values.content[field['label']], + helptext=field['helptext']+' ('+req+')')) + return trans.fill_template( '/admin/samples/view_sample.mako', + sample_details=sample_details) + def __get_all_states(self, trans, sample): + request = trans.app.model.Request.get(sample.request_id) + request_type = trans.app.model.RequestType.get(request.request_type_id) + all_states = trans.app.model.SampleEvent.filter(trans.app.model.SampleEvent.table.c.sample_id == sample.id).all() + curr_state = trans.app.model.SampleState.get(all_states[len(all_states)-1].sample_state_id) + states_list = trans.app.model.SampleState.filter(trans.app.model.SampleState.table.c.request_type_id == request_type.id) + return states_list + def __get_curr_state(self, trans, sample): + all_states = trans.app.model.SampleEvent.filter(trans.app.model.SampleEvent.table.c.sample_id == sample.id).all() + curr_state = trans.app.model.SampleState.get(all_states[len(all_states)-1].sample_state_id) + return curr_state + def change_state(self, trans, sample_id_list): + sample = trans.app.model.Sample.get(sample_id_list[0]) + states_list = self.__get_all_states(trans, sample) + curr_state = self.__get_curr_state(trans, sample) + states_input = SelectField('select_state') + for state in states_list: + if len(sample_id_list) == 1: + if curr_state.name == state.name: + states_input.add_option(state.name+' (Current)', state.name, selected=True) + else: + states_input.add_option(state.name, state.name) + else: + states_input.add_option(state.name, state.name) + widgets = [] + widgets.append(('Select the new state of the sample(s) from the list of possible state(s)', + states_input)) + widgets.append(('Comments', TextArea('comment'))) + title = 'Change current state of sample: ' + sample.name + return trans.fill_template( '/admin/samples/change_state.mako', + widgets=widgets, title=title, + sample_id_list=util.object_to_string(sample_id_list)) + @web.expose + @web.require_admin + def save_state(self, trans, **kwd): + params = util.Params( kwd ) + sample_id_list = util.string_to_object(util.restore_text( params.sample_id_list )) + comments = util.restore_text( params.comment ) + sample = trans.app.model.Sample.get(sample_id_list[0]) + request = trans.app.model.Request.get(sample.request_id) + selected_state = util.restore_text( params.select_state ) + new_state = trans.app.model.SampleState.filter(trans.app.model.SampleState.table.c.request_type_id == request.request_type_id + and trans.app.model.SampleState.table.c.name == selected_state)[0] + for sample_id in sample_id_list: + event = trans.app.model.SampleEvent(sample_id, new_state.id, comments) + event.flush() + return trans.response.send_redirect( web.url_for( controller='requests_admin', + action='list', + operation='samples', + id=trans.security.encode_id(request.id)) ) + @web.expose + @web.require_admin + def show_events(self, trans, sample_id): + sample = trans.app.model.Sample.get(sample_id) + request = trans.app.model.Request.get(sample.request_id) + events_list = [] + for event in trans.app.model.SampleEvent.filter(trans.app.model.SampleEvent.table.c.sample_id == sample_id).order_by(trans.app.model.SampleEvent.c.update_time.desc()).all(): + state = trans.app.model.SampleState.get(event.sample_state_id) + delta = datetime.utcnow() - event.update_time + if delta > timedelta( minutes=60 ): + last_update = '%s hours' % int( delta.seconds / 60 / 60 ) + else: + last_update = '%s minutes' % int( delta.seconds / 60 ) + events_list.append((state.name, state.desc, last_update, event.comment)) + return trans.fill_template( '/admin/samples/events.mako', + events_list=events_list, + sample_name=sample.name, user=trans.app.model.User.get(request.user_id), + request=request.name) + \ No newline at end of file diff --git a/lib/galaxy/web/form_builder.py b/lib/galaxy/web/form_builder.py index f250af01e34..c550f88905b 100644 --- a/lib/galaxy/web/form_builder.py +++ b/lib/galaxy/web/form_builder.py @@ -9,6 +9,11 @@ class BaseField(object): def get_html( self, prefix="" ): """Returns the html widget corresponding to the parameter""" raise TypeError( "Abstract Method" ) + @staticmethod + def form_field_types(): + return ['TextField', 'TextArea', 'SelectField', 'CheckboxField'] + + class TextField(BaseField): """ @@ -26,6 +31,8 @@ class TextField(BaseField): def get_html( self, prefix="" ): return '' \ % ( prefix, self.name, self.size, self.value ) + def set_size(self, size): + self.size = int( size ) class TextArea(BaseField): """ @@ -45,6 +52,9 @@ class TextArea(BaseField): def get_html( self, prefix="" ): return '' \ % ( prefix, self.name, self.rows, self.cols, self.value ) + def set_size(self, rows, cols): + self.rows = rows + self.cols = cols class CheckboxField(BaseField): """ @@ -215,6 +225,17 @@ class SelectField(BaseField): rval.insert( 0, '' ) return "\n".join( rval ) + def get_selected(self): + ''' + This method returns the currently selected option's text and value + ''' + for text, value, selected in self.options: + if selected: + return text, value + if self.options: + return self.options[0] + return None + class DrillDownField( BaseField ): diff --git a/lib/galaxy/web/framework/__init__.py b/lib/galaxy/web/framework/__init__.py index b4c2b5c82c2..f51a11bf5d9 100644 --- a/lib/galaxy/web/framework/__init__.py +++ b/lib/galaxy/web/framework/__init__.py @@ -607,6 +607,11 @@ class UniverseWebTransaction( base.DefaultWebTransaction ): if dbkey == ds.dbkey: return ds return None + + def request_types(self): + if self.app.model.RequestType.query().all(): + return True + return False class FormBuilder( object ): """ diff --git a/run_functional_tests.sh b/run_functional_tests.sh old mode 100644 new mode 100755 diff --git a/templates/admin/forms/create_form.mako b/templates/admin/forms/create_form.mako new file mode 100644 index 00000000000..1a647668750 --- /dev/null +++ b/templates/admin/forms/create_form.mako @@ -0,0 +1,31 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +
+
Create a new form definition
+
+
+ %for label, input in inputs: +
+ +
+ ${input.get_html()} +
+
+
+ %endfor +
+
+ +
+
+
+ +
+
+
diff --git a/templates/admin/forms/edit_form.mako b/templates/admin/forms/edit_form.mako new file mode 100644 index 00000000000..ee9a3e64f03 --- /dev/null +++ b/templates/admin/forms/edit_form.mako @@ -0,0 +1,105 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + + + +<%def name="render_selectbox_options( index, field_attr )"> + %if field_attr[0] == 'Type': + %if field_attr[1].get_selected()[0] == 'SelectField': + <% options = field_attr[2] %> +
+ + %for i, option in enumerate(options): + ${i+1} + ${option[1].get_html()} + ##
+ Remove
+ %endfor +
+
+ Add +
+ ##
+ ## + ##
+ %endif + %endif + + + +<%def name="render_field( index, field )"> +
+
+ +
+ %for field_attr in field: +
+ + ${field_attr[1].get_html()} + ${render_selectbox_options( index, field_attr )} +
+ %endfor +
+ +
+
+ + +
+
Edit form definition '${form.name}'
+
+ %for label, input in form_details: +
+ +
+ ${input.get_html()} +
+
+
+ %endfor +
Sample fields (${len(form.fields)})
+ %for ctr, field in enumerate(field_details): + ${render_field( ctr, field )} + %endfor +
+ +
+
+
+ +
+
+
+ +
+
+ \ No newline at end of file diff --git a/templates/admin/forms/manage_forms.mako b/templates/admin/forms/manage_forms.mako new file mode 100644 index 00000000000..588258672a6 --- /dev/null +++ b/templates/admin/forms/manage_forms.mako @@ -0,0 +1,73 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +<%def name="title()">Manage Form Definitions + +## Render a row +<%def name="render_row( form, ctr )"> + %if ctr % 2 == 1: + + %else: + + %endif + + ${form.name} + +
+ Edit form definition +
+ + ${form.desc} + + + + +

+ %if deleted: + Deleted + %endif + Forms +

+ + + + + + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +%if not fdc_list: + %if deleted: + There are no deleted forms + %else: + There are no forms. + %endif +%else: + + + + + + + + + %for ctr, fdc in enumerate( fdc_list ): + + ${render_row( fdc.latest_form, ctr )} + + %endfor + +
NameDescription
+%endif diff --git a/templates/admin/forms/show_form_read_only.mako b/templates/admin/forms/show_form_read_only.mako new file mode 100644 index 00000000000..e295b67ad4c --- /dev/null +++ b/templates/admin/forms/show_form_read_only.mako @@ -0,0 +1,67 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +
+
${form.name} - ${form.desc}
+
+ + + %for index, field in enumerate(form.fields): + + + + + + + %endfor + +
+
+ + ${field['label']} + %if field['type'] == 'SelectField': + + %for option in field['selectlist']: +
+ ${option} +
+ %endfor + %endif +
+
+
+ + %if not field['helptext']: + No helptext + %else: + ${field['helptext']} + %endif +
+
+
+ + ${field['type']} + %if field['type'] == 'SelectField': + + %for option in field['selectlist']: +
+ ${option} +
+ %endfor + %endif +
+
+
+ + ${field['required']} +
+
+ ## +
+
+ \ No newline at end of file diff --git a/templates/admin/index.mako b/templates/admin/index.mako index 158489ac515..e78c0355a4c 100644 --- a/templates/admin/index.mako +++ b/templates/admin/index.mako @@ -103,13 +103,35 @@
Manage jobs
+
+
+ Forms +
+
+
+ +
+
+
+
+ Requests +
+
+
+ + +
+
+ ## <%def name="center_panel()"> - - + <% + center_url = h.url_for( action='center' ) + %> + diff --git a/templates/admin/requests/add_states.mako b/templates/admin/requests/add_states.mako new file mode 100644 index 00000000000..0c58bfdee00 --- /dev/null +++ b/templates/admin/requests/add_states.mako @@ -0,0 +1,26 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +
+
Create ${num_states} states for the '${sample_type_name}' sample type
+
+
+ %for element_count in range( num_states ): +
+ + + + +
+
+ %endfor +
+
+ +
+
+
\ No newline at end of file diff --git a/templates/admin/requests/create_request_type.mako b/templates/admin/requests/create_request_type.mako new file mode 100644 index 00000000000..6a336e0eadd --- /dev/null +++ b/templates/admin/requests/create_request_type.mako @@ -0,0 +1,66 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +
+
Create a new request type
+ %if not forms: + Create a form definition first to create a new request type. + %else: +
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+ + +
+
+ + +
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+
+ %endif +
\ No newline at end of file diff --git a/templates/admin/requests/edit_request_type.mako b/templates/admin/requests/edit_request_type.mako new file mode 100644 index 00000000000..b67a0c8e96d --- /dev/null +++ b/templates/admin/requests/edit_request_type.mako @@ -0,0 +1,78 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +<% num_states=len(states_list) %> + +
+
Edit the request type
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+ + +
+
+ + +
+
+ %for element_count, state in enumerate(states_list): +
+ + + + +
+
+ %endfor +
+
+
+ +
+
+
+ + +
+
+
\ No newline at end of file diff --git a/templates/admin/requests/grid.mako b/templates/admin/requests/grid.mako new file mode 100644 index 00000000000..bb54a758589 --- /dev/null +++ b/templates/admin/requests/grid.mako @@ -0,0 +1,205 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + +<%def name="title()">Browse Requests + +%if message: +

+

${message}
+
+

+%endif + +<%def name="javascripts()"> + ${parent.javascripts()} + + + +<%def name="stylesheets()"> + + + + +
+

${grid.title}

+ %if len(query.all()): +

All Users

+ %endif +## %if len(query.all()): +## Filter: +## %for i, filter in enumerate( grid.standard_filters ): +## %if i > 0: +## | +## %endif +## ${filter.label} +## %endfor +## %endif +
+ + +%if not len(query.all()): + There are no request(s). +%else: +
+ + + + + %for column in grid.columns: + %if column.visible: + <% + href = "" + extra = "" + if column.sortable: + if sort_key == column.key: + if sort_order == "asc": + href = url( sort=( "-" + column.key ) ) + extra = "↓" + else: + href = url( sort=( column.key ) ) + extra = "↑" + else: + href = url( sort=column.key ) + %> + 1: + colspan="${column.ncells}" + %endif + > + %if href: + ${column.label} + %else: + ${column.label} + %endif + ${extra} + + %endif + %endfor + + + + + %for i, item in enumerate( query ): + + ## Item selection column + + ## Data columns + %for column in grid.columns: + %if column.visible: + <% + # Link + if column.link and column.link( item ): + href = url( **column.link( item ) ) + else: + href = None + # Value (coerced to list so we can loop) + value = column.get_value( trans, grid, item ) + if column.ncells == 1: + value = [ value ] + %> + %for cellnum, v in enumerate( value ): + <% + # Attach popup menu? + if column.attach_popup and cellnum == 0: + extra = '' % i + else: + extra = "" + %> + %if href: + + %else: + + %endif + + %endfor + %endif + %endfor + ## Actions column + + + %endfor + +## +## +## +## +## +## +
+ + ${v} ${extra}${v}${extra} +
+ %for operation in grid.operations: + %if operation.allowed( item ): + ${operation.label} + %endif + %endfor +
+
+## For selected requests: +## %for operation in grid.operations: +## %if operation.allow_multiple: +## +## %endif +## %endfor +##
+
+%endif \ No newline at end of file diff --git a/templates/admin/requests/manage_request_types.mako b/templates/admin/requests/manage_request_types.mako new file mode 100644 index 00000000000..4841211935e --- /dev/null +++ b/templates/admin/requests/manage_request_types.mako @@ -0,0 +1,60 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + +<%def name="title()">request Types + +

+ %if deleted: + Deleted + %endif + Request Types +

+ + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +%if not request_types: + %if deleted: + There are no deleted request types + %else: + There are no request types. + %endif +%else: + + + + + + + + + + + %for request_type in request_types: + + + + <% for form in forms: + if form.id == request_type.request_form_id: + form_name = form.name + break + %> + + <% for form in forms: + if form.id == request_type.sample_form_id: + form_name = form.name + break + %> + + + %endfor + +
NameDescriptionRequest FormSample Form
${request_type.name}${request_type.desc}${form_name}${form_name}
+%endif \ No newline at end of file diff --git a/templates/admin/requests/view_request.mako b/templates/admin/requests/view_request.mako new file mode 100644 index 00000000000..3671ca81a5f --- /dev/null +++ b/templates/admin/requests/view_request.mako @@ -0,0 +1,20 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +
+
Request Details: '${request_details[0]['value']}'
+ %for index, rd in enumerate(request_details): +
+ + ##${rd['helptext']} + ${rd['value']} +
+
+ %endfor +
+ \ No newline at end of file diff --git a/templates/admin/samples/change_state.mako b/templates/admin/samples/change_state.mako new file mode 100644 index 00000000000..48e110a1e61 --- /dev/null +++ b/templates/admin/samples/change_state.mako @@ -0,0 +1,30 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif +
+
${title}
+
+
+ %for w in widgets: +
+ + ${w[1].get_html()} + %if w[0] == 'Comments': +
+ Optional +
+ %endif +
+ %endfor +
+ +
+
+
+
\ No newline at end of file diff --git a/templates/admin/samples/edit_sample.mako b/templates/admin/samples/edit_sample.mako new file mode 100644 index 00000000000..d007cd7339f --- /dev/null +++ b/templates/admin/samples/edit_sample.mako @@ -0,0 +1,83 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +
+
Edit sample named: ${values.content['Name']}
+
+
+
+ + +
+ %for i, field in enumerate(form.fields): +
+ + %if field['type'] == 'TextField': + + %elif field['type'] == 'TextArea': + + %elif field['type'] == 'CheckBox': + %if values.content[field['label']] == "true": + + %else: + + %endif + %elif field['type'] == 'SelectBox': + + %endif +
+ ${field['helptext']} +
+
+
+ %endfor +
+
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+ + +
+
+ +
+
+
+
\ No newline at end of file diff --git a/templates/admin/samples/events.mako b/templates/admin/samples/events.mako new file mode 100644 index 00000000000..fbe75f89144 --- /dev/null +++ b/templates/admin/samples/events.mako @@ -0,0 +1,33 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + +<%def name="title()">Events for Sample ${sample_name} + +

Events for Sample "${sample_name}" of Request: ${request}

+

User: ${user.email}

+ +%if msg: + ${render_msg( msg, messagetype )} +%endif + + + + + + + + + + + + + %for state, desc, updated, comments in events_list: + + + + + + + %endfor + +
StateDescriptionLast UpdateComments
${state}${desc}${updated}${comments}
\ No newline at end of file diff --git a/templates/admin/samples/grid.mako b/templates/admin/samples/grid.mako new file mode 100644 index 00000000000..116e05ad64f --- /dev/null +++ b/templates/admin/samples/grid.mako @@ -0,0 +1,203 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + +<%def name="title()">Browse Samples + +%if message: +

+

${message}
+
+

+%endif + +<%def name="javascripts()"> + ${parent.javascripts()} + + + +<%def name="stylesheets()"> + + + + +
+

Samples associated with request: ${grid.request.name}

+

User: ${grid.user.email}

+## %if len(query.all()): +## Filter: +## %for i, filter in enumerate( grid.standard_filters ): +## %if i > 0: +## | +## %endif +## ${filter.label} +## %endfor +## %endif +
+ +%if not len(query.all()): + There are no sample(s). +%else: +
+ + + + + %for column in grid.columns: + %if column.visible: + <% + href = "" + extra = "" + if column.sortable: + if sort_key == column.key: + if sort_order == "asc": + href = url( sort=( "-" + column.key ) ) + extra = "↓" + else: + href = url( sort=( column.key ) ) + extra = "↑" + else: + href = url( sort=column.key ) + %> + 1: + colspan="${column.ncells}" + %endif + > + %if href: + ${column.label} + %else: + ${column.label} + %endif + ${extra} + + %endif + %endfor + + + + + %for i, item in enumerate( query ): + + ## Item selection column + + ## Data columns + %for column in grid.columns: + %if column.visible: + <% + # Link + if column.link and column.link( item ): + href = url( **column.link( item ) ) + else: + href = None + # Value (coerced to list so we can loop) + value = column.get_value( trans, grid, item ) + if column.ncells == 1: + value = [ value ] + %> + %for cellnum, v in enumerate( value ): + <% + # Attach popup menu? + if column.attach_popup and cellnum == 0: + extra = '' % i + else: + extra = "" + %> + %if href: + + %else: + + %endif + + %endfor + %endif + %endfor + ## Actions column + + + %endfor + + + + + + + +
+ + ${v} ${extra}${v}${extra} +
+ %for operation in grid.operations: + %if operation.allowed( item ): + ${operation.label} + %endif + %endfor +
+
+ For selected requests: + %for operation in grid.operations: + %if operation.allow_multiple: + + %endif + %endfor +
+
+ +%endif diff --git a/templates/admin/samples/view_sample.mako b/templates/admin/samples/view_sample.mako new file mode 100644 index 00000000000..9df6128f09c --- /dev/null +++ b/templates/admin/samples/view_sample.mako @@ -0,0 +1,19 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +
+
Sample Details: '${sample_details[0]['value']}'
+ %for index, rd in enumerate(sample_details): +
+ + ${rd['value']} +
+
+ %endfor +
+ diff --git a/templates/base.mako b/templates/base.mako index f0098fad9d2..e48bd2896dd 100644 --- a/templates/base.mako +++ b/templates/base.mako @@ -29,4 +29,33 @@ ${self.javascripts()} ## + + + + + + +<%def name="stylesheets()"> + + + + +
+

${grid.title}

+## %if len(query.all()): +## Filter: +## %for i, filter in enumerate( grid.standard_filters ): +## %if i > 0: +## | +## %endif +## ${filter.label} +## %endfor +## %endif +
+ + + + +%if not len(query.all()): + There are no request(s). +%else: +
+ + + + + %for column in grid.columns: + %if column.visible: + <% + href = "" + extra = "" + if column.sortable: + if sort_key == column.key: + if sort_order == "asc": + href = url( sort=( "-" + column.key ) ) + extra = "↓" + else: + href = url( sort=( column.key ) ) + extra = "↑" + else: + href = url( sort=column.key ) + %> + 1: + colspan="${column.ncells}" + %endif + > + %if href: + ${column.label} + %else: + ${column.label} + %endif + ${extra} + + %endif + %endfor + + + + + %for i, item in enumerate( query ): + + ## Item selection column + + ## Data columns + %for column in grid.columns: + %if column.visible: + <% + # Link + if column.link and column.link( item ): + href = url( **column.link( item ) ) + else: + href = None + # Value (coerced to list so we can loop) + value = column.get_value( trans, grid, item ) + if column.ncells == 1: + value = [ value ] + %> + %for cellnum, v in enumerate( value ): + <% + # Attach popup menu? + if column.attach_popup and cellnum == 0: + extra = '' % i + else: + extra = "" + %> + %if href: + + %else: + + %endif + + %endfor + %endif + %endfor + ## Actions column + + + %endfor + +## +## +## +## +## +## +
+ + ${v} ${extra}${v}${extra} +
+ %for operation in grid.operations: + %if operation.allowed( item ): + ${operation.label} + %endif + %endfor +
+
+## For selected requests: +## %for operation in grid.operations: +## %if operation.allow_multiple: +## +## %endif +## %endfor +##
+
+%endif \ No newline at end of file diff --git a/templates/requests/index.mako b/templates/requests/index.mako new file mode 100644 index 00000000000..5ce6aa9ca31 --- /dev/null +++ b/templates/requests/index.mako @@ -0,0 +1,16 @@ +<%inherit file="/base_panels.mako"/> + +<%def name="init()"> +<% + self.has_left_panel=False + self.has_right_panel=False + self.active_view="requests" + self.message_box_visible=False +%> + + +<%def name="center_panel()"> + + + + diff --git a/templates/requests/new_request.mako b/templates/requests/new_request.mako new file mode 100644 index 00000000000..c97974b19ae --- /dev/null +++ b/templates/requests/new_request.mako @@ -0,0 +1,32 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +
+
${title}
+
+
+ %for i, field in enumerate(widgets): +
+ + ${field['widget'].get_html()} +
+ ${field['helptext']} +
+
+
+ %endfor +
+
+ +
+
+
+ +
+
+
\ No newline at end of file diff --git a/templates/requests/select_request_type.mako b/templates/requests/select_request_type.mako new file mode 100644 index 00000000000..9d99d61458b --- /dev/null +++ b/templates/requests/select_request_type.mako @@ -0,0 +1,31 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + + +
+
Add a new request
+ %if not request_types: + There are no request types created for a new request. + %else: +
+
+
+ + + +
+
+
+ %endif +
diff --git a/templates/requests/view_request.mako b/templates/requests/view_request.mako new file mode 100644 index 00000000000..3671ca81a5f --- /dev/null +++ b/templates/requests/view_request.mako @@ -0,0 +1,20 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +
+
Request Details: '${request_details[0]['value']}'
+ %for index, rd in enumerate(request_details): +
+ + ##${rd['helptext']} + ${rd['value']} +
+
+ %endfor +
+ \ No newline at end of file diff --git a/templates/sample/browse_samples.mako b/templates/sample/browse_samples.mako new file mode 100644 index 00000000000..5224fa929d3 --- /dev/null +++ b/templates/sample/browse_samples.mako @@ -0,0 +1,43 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + +<%def name="title()">Browse Samples + +

Samples

+ + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +%if not samples_list: + You have no samples. +%else: + + + + + + + + + + + %for id, name, desc, st, state in samples_list: + + + + + + + %endfor + +
NameDescriptionTypeState
${name}${desc}${st}${state}
+%endif \ No newline at end of file diff --git a/templates/sample/edit_sample.mako b/templates/sample/edit_sample.mako new file mode 100644 index 00000000000..402970fe248 --- /dev/null +++ b/templates/sample/edit_sample.mako @@ -0,0 +1,64 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +
+
Edit sample named: ${values.content['Name']}
+
+
+
+ + +
+ %for i, field in enumerate(form.fields): +
+ + %if field['type'] == 'TextField': + + %elif field['type'] == 'TextArea': + + %elif field['type'] == 'CheckBox': + %if values.content[field['label']] == "true": + + %else: + + %endif + %elif field['type'] == 'SelectBox': + + %endif +
+ ${field['helptext']} +
+
+
+ %endfor +
+
+ +
+
+
+
+ +
+
+
+
diff --git a/templates/sample/grid.mako b/templates/sample/grid.mako new file mode 100644 index 00000000000..bbeeb080a50 --- /dev/null +++ b/templates/sample/grid.mako @@ -0,0 +1,210 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + +<%def name="title()">Browse Samples + +%if message: +

+

${message}
+
+

+%endif + +<%def name="javascripts()"> + ${parent.javascripts()} + + + +<%def name="stylesheets()"> + + + + +
+

Samples associated with request: ${grid.request.name}

+## %if len(query.all()): +## Filter: +## %for i, filter in enumerate( grid.standard_filters ): +## %if i > 0: +## | +## %endif +## ${filter.label} +## %endfor +## %endif +
+ + + +%if not len(query.all()): + There are no sample(s). +%else: +
+ + + + + %for column in grid.columns: + %if column.visible: + <% + href = "" + extra = "" + if column.sortable: + if sort_key == column.key: + if sort_order == "asc": + href = url( sort=( "-" + column.key ) ) + extra = "↓" + else: + href = url( sort=( column.key ) ) + extra = "↑" + else: + href = url( sort=column.key ) + %> + 1: + colspan="${column.ncells}" + %endif + > + %if href: + ${column.label} + %else: + ${column.label} + %endif + ${extra} + + %endif + %endfor + + + + + %for i, item in enumerate( query ): + + ## Item selection column + + ## Data columns + %for column in grid.columns: + %if column.visible: + <% + # Link + if column.link and column.link( item ): + href = url( **column.link( item ) ) + else: + href = None + # Value (coerced to list so we can loop) + value = column.get_value( trans, grid, item ) + if column.ncells == 1: + value = [ value ] + %> + %for cellnum, v in enumerate( value ): + <% + # Attach popup menu? + if column.attach_popup and cellnum == 0: + extra = '' % i + else: + extra = "" + %> + %if href: + + %else: + + %endif + + %endfor + %endif + %endfor + ## Actions column +## + + %endfor + +## +## +## +## +## +## +
+ + ${v} ${extra}${v}${extra} +##
+## %for operation in grid.operations: +## %if operation.allowed( item ): +## ${operation.label} +## %endif +## %endfor +##
+##
+## For selected requests: +## %for operation in grid.operations: +## %if operation.allow_multiple: +## +## %endif +## %endfor +##
+
+ +%endif diff --git a/templates/sample/index.mako b/templates/sample/index.mako new file mode 100644 index 00000000000..9d15439a35c --- /dev/null +++ b/templates/sample/index.mako @@ -0,0 +1,16 @@ +<%inherit file="/base_panels.mako"/> + +<%def name="init()"> +<% + self.has_left_panel=False + self.has_right_panel=False + self.active_view="requests" + +%> + + +<%def name="center_panel()"> + + + + \ No newline at end of file diff --git a/templates/sample/new_sample.mako b/templates/sample/new_sample.mako new file mode 100644 index 00000000000..6cac41769d6 --- /dev/null +++ b/templates/sample/new_sample.mako @@ -0,0 +1,32 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +
+
${title}
+
+
+ %for i, field in enumerate(widgets): +
+ + ${field['widget'].get_html()} +
+ ${field['helptext']} +
+
+
+ %endfor +
+
+ +
+
+
+ +
+
+
\ No newline at end of file diff --git a/templates/sample/sample_events.mako b/templates/sample/sample_events.mako new file mode 100644 index 00000000000..7ee2bd1ef1a --- /dev/null +++ b/templates/sample/sample_events.mako @@ -0,0 +1,32 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + +<%def name="title()">Events for Sample ${sample_name} + +

Events for Sample "${sample_name}" of Request: ${request}

+ +%if msg: + ${render_msg( msg, messagetype )} +%endif + + + + + + + + + + + + + %for state, updated, desc, comments in events_list: + + + + + + + %endfor + +
StateUpdatedDescriptionComments
${state}${updated}${desc}${comments}
\ No newline at end of file diff --git a/templates/sample/view_sample.mako b/templates/sample/view_sample.mako new file mode 100644 index 00000000000..b1a954f1c3f --- /dev/null +++ b/templates/sample/view_sample.mako @@ -0,0 +1,19 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +
+
Sample Details: '${sample_details[0]['value']}'
+ %for index, rd in enumerate(sample_details): +
+ + ${rd['value']} +
+
+ %endfor +
+