From da0b09039cdb013e0ad15c325bc500abf003f7e3 Mon Sep 17 00:00:00 2001 From: Daniel Blankenberg Date: Tue, 12 Aug 2008 16:56:26 -0400 Subject: [PATCH] Initial check in of library functionality. The ui is Very rough, at best. Libraries can be accessed by users through a new tool under datasources. Libraries can be created and accessed for modification via a link on the admin page. Currently, all library datasets are currently associated with the public group. Tags have been defined (tables and mappings) and can be associated with a dataset or a folder, but there is currenty no interface for displaying or creating these. Creation and editing of a library should be an admin only function, however this check is not currently present. The creation/editing page for a dataset is currently hard coded, it should be dynamic (i.e. datatypes and dbkeys) Brief description of a library: A library is composed of a name, description and a root folder. A folder is composed of a name, description, and a parent folder (none when is the root), where a folder can have many or none child folders and datasets. A library dataset is composed of most of the same attributes as a history dataset, but it is linked via a folder_id to a folder (instead of a history_id to a history). Ordering of the library is maintained by the 'order_id' attribute of each folder and dataset object (which is populated by the item_count attribute of a folder) --- lib/galaxy/model/__init__.py | 123 ++- lib/galaxy/model/mapping.py | 118 ++- lib/galaxy/security/__init__.py | 2 +- lib/galaxy/web/controllers/library.py | 218 +++++ lib/galaxy/web/framework/__init__.py | 7 +- templates/admin_main.mako | 6 + templates/form.mako | 2 + templates/library/admin_list_libraries.mako | 16 + templates/library/manage_dataset.mako | 100 ++ templates/library/manage_folder.mako | 191 ++++ templates/library/manage_library.mako | 46 + templates/library/new_dataset.mako | 997 ++++++++++++++++++++ templates/library/user_list_libraries.mako | 14 + templates/library/user_view_library.mako | 58 ++ tool_conf.xml.sample | 1 + tools/data_source/access_libraries.xml | 11 + 16 files changed, 1883 insertions(+), 27 deletions(-) create mode 100644 lib/galaxy/web/controllers/library.py create mode 100644 templates/library/admin_list_libraries.mako create mode 100644 templates/library/manage_dataset.mako create mode 100644 templates/library/manage_folder.mako create mode 100644 templates/library/manage_library.mako create mode 100644 templates/library/new_dataset.mako create mode 100644 templates/library/user_list_libraries.mako create mode 100644 templates/library/user_view_library.mako create mode 100644 tools/data_source/access_libraries.xml diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 7dda26375bb..e6637b2ad7f 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -262,16 +262,16 @@ class Dataset( object ): except OSError, e: log.critical('%s delete error %s' % (self.__class__.__name__, e)) -class HistoryDatasetAssociation( object ): + +class DatasetInstance( object ): + """A base class for all 'dataset instances', HDAs, LDAs, etc""" states = Dataset.states permitted_actions = Dataset.permitted_actions def __init__( self, id=None, hid=None, name=None, info=None, blurb=None, peek=None, extension=None, dbkey=None, metadata=None, history=None, dataset=None, deleted=False, designation=None, - parent_id=None, copied_from_history_dataset_association = None, validation_errors=None, - visible=True, create_dataset = False ): + parent_id=None, validation_errors=None, visible=True, create_dataset = False ): self.name = name or "Unnamed dataset" self.id = id - self.hid = hid self.info = info self.blurb = blurb self.peek = peek @@ -282,14 +282,12 @@ class HistoryDatasetAssociation( object ): self.deleted = deleted self.visible = visible # Relationships - self.history = history if not dataset and create_dataset: dataset = Dataset() dataset.flush() self.dataset = dataset self.parent_id = parent_id self.validation_errors = validation_errors - self.copied_from_history_dataset_association = copied_from_history_dataset_association @property def ext( self ): @@ -399,10 +397,7 @@ class HistoryDatasetAssociation( object ): valid.append( assoc.dataset ) return valid def clear_associated_files( self, metadata_safe = False, purge = False ): - #metadata_safe = True means to only clear when assoc.metadata_safe == False - for assoc in self.implicitly_converted_datasets: - if not metadata_safe or not assoc.metadata_safe: - assoc.clear( purge = purge ) + raise 'Unimplemented' def get_child_by_designation(self, designation): for child in self.children: if child.designation == designation: @@ -411,17 +406,6 @@ class HistoryDatasetAssociation( object ): def get_converter_types(self): return self.datatype.get_converter_types( self, datatypes_registry) - - def copy( self, copy_children = False, parent_id = None, target_user = None ): - if target_user is None: target_user = self.user - des = HistoryDatasetAssociation( hid=self.hid, name=self.name, info=self.info, blurb=self.blurb, peek=self.peek, extension=self.extension, dbkey=self.dbkey, metadata=self._metadata, dataset = self.dataset, visible=self.visible, deleted=self.deleted, parent_id=parent_id, copied_from_history_dataset_association = self ) - des.flush() - if copy_children: - for child in self.children: - child_copy = child.copy( copy_children = copy_children, parent_id = des.id ) - des.set_peek() #in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs - des.flush() - return des def add_validation_error( self, validation_error ): self.validation_errors.append( validation_error ) @@ -436,6 +420,35 @@ class HistoryDatasetAssociation( object ): child.mark_deleted() + +class HistoryDatasetAssociation( DatasetInstance ): + def __init__( self, hid = None, history = None, copied_from_history_dataset_association = None, copied_from_library_folder_dataset_association = None, **kwd ): + DatasetInstance.__init__( self, **kwd ) + self.hid = hid + # Relationships + self.history = history + self.copied_from_history_dataset_association = copied_from_history_dataset_association + self.copied_from_library_folder_dataset_association = copied_from_library_folder_dataset_association + + def copy( self, copy_children = False, parent_id = None ): + print "self.dataset", self.dataset + + des = HistoryDatasetAssociation( hid=self.hid, name=self.name, info=self.info, blurb=self.blurb, peek=self.peek, extension=self.extension, dbkey=self.dbkey, metadata=self._metadata, dataset = self.dataset, visible=self.visible, deleted=self.deleted, parent_id=parent_id, copied_from_history_dataset_association = self ) + print "des data", des.dataset + des.flush() + if copy_children: + for child in self.children: + child_copy = child.copy( copy_children = copy_children, parent_id = des.id ) + des.set_peek() #in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs + des.flush() + return des + + def clear_associated_files( self, metadata_safe = False, purge = False ): + #metadata_safe = True means to only clear when assoc.metadata_safe == False + for assoc in self.implicitly_converted_datasets: + if not metadata_safe or not assoc.metadata_safe: + assoc.clear( purge = purge ) + class History( object ): def __init__( self, id=None, name=None, user=None ): self.id = id @@ -499,9 +512,75 @@ class History( object ): des.hid_counter = self.hid_counter des.flush() return des - +class Library( object ): + def __init__( self, name = None, description = None, root_folder = None ): + self.name = name or "Unnamed library" + self.description = description + self.root_folder = root_folder + +class LibraryFolder( object ): + def __init__( self, name = None, description = None, order_id = None ): + self.name = name or "Unnamed folder" + self.description = description + self.item_count = item_count + self.order_id = order_id + def add_dataset( self, dataset ): + dataset.folder_id = self.id + dataset.order_id = self.item_count + self.item_count += 1 + def add_folder( self, folder ): + folder.parent_id = self.id + folder.order_id = self.item_count + self.item_count += 1 + +class LibraryFolderDatasetAssociation( DatasetInstance ): + def __init__( self, folder = None, order_id = None, copied_from_history_dataset_association = None, copied_from_library_folder_dataset_association = None, **kwd ): + DatasetInstance.__init__( self, **kwd ) + self.folder = folder + self.order_id = order_id + self.copied_from_history_dataset_association = copied_from_history_dataset_association + self.copied_from_library_folder_dataset_association = copied_from_library_folder_dataset_association + + def to_history_dataset_association( self, parent_id = None ): + des = HistoryDatasetAssociation( name=self.name, info=self.info, blurb=self.blurb, peek=self.peek, extension=self.extension, dbkey=self.dbkey, metadata=self._metadata, dataset = self.dataset, visible=self.visible, deleted=self.deleted, parent_id=parent_id, copied_from_library_folder_dataset_association = self ) + des.flush() + for child in self.children: + child_copy = child.to_history_dataset_association( parent_id = des.id ) + des.set_peek() #in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs + des.flush() + return des + + + def copy( self, copy_children = False, parent_id = None ): + des = LibraryFolderDatasetAssociation( name=self.name, info=self.info, blurb=self.blurb, peek=self.peek, extension=self.extension, dbkey=self.dbkey, metadata=self._metadata, dataset = self.dataset, visible=self.visible, deleted=self.deleted, parent_id=parent_id, copied_from_library_folder_dataset_association = self ) + des.flush() + if copy_children: + for child in self.children: + child_copy = child.copy( copy_children = copy_children, parent_id = des.id ) + des.set_peek() #in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs + des.flush() + return des + + def clear_associated_files( self, metadata_safe = False, purge = False ): + return + + +class LibraryTag( object ): + def __init__( self, tag ): + self.tag = tag + +class LibraryTagFolderAssociation( object ): + def __init__( self, tag, folder ): + self.tag = tag + self.folder = folder + +class LibraryTagDatasetAssociation( object ): + def __init__( self, tag, dataset ): + self.tag = tag + self.dataset = dataset + # class Query( object ): # def __init__( self, name=None, state=None, tool_parameters=None, history=None ): # self.name = name or "Unnamed query" diff --git a/lib/galaxy/model/mapping.py b/lib/galaxy/model/mapping.py index 8c11ec41a2c..6a8f982fc81 100644 --- a/lib/galaxy/model/mapping.py +++ b/lib/galaxy/model/mapping.py @@ -74,6 +74,7 @@ HistoryDatasetAssociation.table = Table( "history_dataset_association", metadata Column( "create_time", DateTime, default=now ), Column( "update_time", DateTime, default=now, onupdate=now ), Column( "copied_from_history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id" ), nullable=True ), + Column( "copied_from_library_folder_dataset_association_id", Integer, ForeignKey( "library_folder_dataset_association.id" ), nullable=True ), Column( "hid", Integer ), Column( "name", TrimmedString( 255 ) ), Column( "info", TrimmedString( 255 ) ), @@ -157,6 +158,66 @@ DefaultHistoryGroupAssociation.table = Table( "default_history_group_association Column( "update_time", DateTime, default=now, onupdate=now ), Column( "permitted_actions", JSONType(), default=[] ) ) +LibraryFolderDatasetAssociation.table = Table( "library_folder_dataset_association", metadata, + Column( "id", Integer, primary_key=True ), + Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ), + Column( "folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ), + Column( "order_id", Integer ), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "copied_from_history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id", use_alter=True, name='history_dataset_association_dataset_id_fkey' ), nullable=True ), + Column( "copied_from_library_folder_dataset_association_id", Integer, ForeignKey( "library_folder_dataset_association.id", use_alter=True, name='library_folder_dataset_association_id_fkey' ), nullable=True ), + Column( "name", TrimmedString( 255 ) ), + Column( "info", TrimmedString( 255 ) ), + Column( "blurb", TrimmedString( 255 ) ), + Column( "peek" , TEXT ), + Column( "extension", TrimmedString( 64 ) ), + Column( "metadata", MetadataType(), key="_metadata" ), + Column( "parent_id", Integer, ForeignKey( "library_folder_dataset_association.id" ), nullable=True ), + Column( "designation", TrimmedString( 255 ) ), + Column( "deleted", Boolean, index=True, default=False ), + Column( "visible", Boolean ) ) + +Library.table = Table( "library", metadata, + Column( "id", Integer, primary_key=True ), + Column( "root_folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "name", TEXT ), + Column( "description", TEXT ) ) + + +LibraryFolder.table = Table( "library_folder", metadata, + Column( "id", Integer, primary_key=True ), + Column( "parent_id", Integer, ForeignKey( "library_folder.id" ), nullable = True, index=True ), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "name", TEXT ), + Column( "description", TEXT ), + Column( "order_id", Integer ), + Column( "item_count", Integer ) ) + +LibraryTag.table = Table( "library_tag", metadata, + Column( "id", Integer, primary_key=True ), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ), + Column( "text", TEXT ) ) + +LibraryTagFolderAssociation.table = Table( "library_tag_folder_association", metadata, + Column( "id", Integer, primary_key=True ), + Column( "folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ), + Column( "tag_id", Integer, ForeignKey( "library_tag.id" ), index=True ), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ) ) + +LibraryTagDatasetAssociation.table = Table( "library_tag_dataset_association", metadata, + Column( "id", Integer, primary_key=True ), + Column( "dataset_id", Integer, ForeignKey( "library_folder_dataset_association.id" ), index=True ), + Column( "tag_id", Integer, ForeignKey( "library_tag.id" ), index=True ), + Column( "create_time", DateTime, default=now ), + Column( "update_time", DateTime, default=now, onupdate=now ) ) + + Job.table = Table( "job", metadata, Column( "id", Integer, primary_key=True ), Column( "create_time", DateTime, default=now ), @@ -298,6 +359,10 @@ assign_mapper( context, HistoryDatasetAssociation, HistoryDatasetAssociation.tab HistoryDatasetAssociation, primaryjoin=( HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == HistoryDatasetAssociation.table.c.id ), backref=backref( "copied_from_history_dataset_association", primaryjoin=( HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == HistoryDatasetAssociation.table.c.id ), remote_side=[HistoryDatasetAssociation.table.c.id] ) ), + copied_to_library_folder_dataset_associations=relation( + LibraryFolderDatasetAssociation, + primaryjoin=( HistoryDatasetAssociation.table.c.copied_from_library_folder_dataset_association_id == LibraryFolderDatasetAssociation.table.c.id ), + backref=backref( "copied_from_history_dataset_association", primaryjoin=( HistoryDatasetAssociation.table.c.copied_from_library_folder_dataset_association_id == LibraryFolderDatasetAssociation.table.c.id ), remote_side=[LibraryFolderDatasetAssociation.table.c.id] ) ), implicitly_converted_datasets=relation( ImplicitlyConvertedDatasetAssociation, primaryjoin=( ImplicitlyConvertedDatasetAssociation.table.c.hda_parent_id == HistoryDatasetAssociation.table.c.id ) ), @@ -311,7 +376,10 @@ assign_mapper( context, Dataset, Dataset.table, properties=dict( history_associations=relation( HistoryDatasetAssociation, - primaryjoin=( Dataset.table.c.id == HistoryDatasetAssociation.table.c.dataset_id ) ) + primaryjoin=( Dataset.table.c.id == HistoryDatasetAssociation.table.c.dataset_id ) ), + library_associations=relation( + LibraryFolderDatasetAssociation, + primaryjoin=( Dataset.table.c.id == LibraryFolderDatasetAssociation.table.c.dataset_id ) ) ) ) @@ -365,6 +433,54 @@ assign_mapper( context, DefaultHistoryGroupAssociation, DefaultHistoryGroupAssoc properties=dict( history=relation( History, backref = "default_groups" ), group=relation( Group ) ) ) +assign_mapper( context, Library, Library.table, + properties=dict( + root_folder=relation( LibraryFolder, + backref = backref( "library_root" ) ) + ) ) + +assign_mapper( context, LibraryFolder, LibraryFolder.table, + properties=dict( + folders=relation( + LibraryFolder, + primaryjoin=( LibraryFolder.table.c.parent_id == LibraryFolder.table.c.id ), + backref=backref( "parent", primaryjoin=( LibraryFolder.table.c.parent_id == LibraryFolder.table.c.id ), remote_side=[LibraryFolder.table.c.id] ) ), + tags=relation( + LibraryTagFolderAssociation, + primaryjoin=( LibraryFolder.table.c.id == LibraryTagFolderAssociation.table.c.folder_id ), + backref=backref( "folders" ) ) + ) ) + +assign_mapper( context, LibraryFolderDatasetAssociation, LibraryFolderDatasetAssociation.table, + properties=dict( + dataset=relation( Dataset ), + folder=relation( + LibraryFolder, + backref=backref( "datasets" ) ), + copied_to_library_folder_dataset_associations=relation( + LibraryFolderDatasetAssociation, + primaryjoin=( LibraryFolderDatasetAssociation.table.c.copied_from_library_folder_dataset_association_id == LibraryFolderDatasetAssociation.table.c.id ), + backref=backref( "copied_from_library_folder_dataset_association", primaryjoin=( LibraryFolderDatasetAssociation.table.c.copied_from_library_folder_dataset_association_id == LibraryFolderDatasetAssociation.table.c.id ), remote_side=[LibraryFolderDatasetAssociation.table.c.id] ) ), + children=relation( + LibraryFolderDatasetAssociation, + primaryjoin=( LibraryFolderDatasetAssociation.table.c.parent_id == LibraryFolderDatasetAssociation.table.c.id ), + backref=backref( "parent", primaryjoin=( LibraryFolderDatasetAssociation.table.c.parent_id == LibraryFolderDatasetAssociation.table.c.id ), remote_side=[LibraryFolderDatasetAssociation.table.c.id] ) ), + tags=relation( + LibraryTagDatasetAssociation, + primaryjoin=( LibraryFolderDatasetAssociation.table.c.id == LibraryTagDatasetAssociation.table.c.dataset_id ), + backref=backref( "datasets" ) ) + ) ) + +assign_mapper( context, LibraryTag, LibraryTag.table ) + +assign_mapper( context, LibraryTagFolderAssociation, LibraryTagFolderAssociation.table, + properties=dict( tag=relation( LibraryTag ), + folder=relation( LibraryFolder ) ) ) + +assign_mapper( context, LibraryTagDatasetAssociation, LibraryTagDatasetAssociation.table, + properties=dict( tag=relation( LibraryTag ), + dataset=relation( LibraryFolderDatasetAssociation ) ) ) + assign_mapper( context, JobToInputDatasetAssociation, JobToInputDatasetAssociation.table, properties=dict( job=relation( Job ), dataset=relation( HistoryDatasetAssociation ) ) ) diff --git a/lib/galaxy/security/__init__.py b/lib/galaxy/security/__init__.py index 94315714c4f..0ba0bf6b621 100644 --- a/lib/galaxy/security/__init__.py +++ b/lib/galaxy/security/__init__.py @@ -1,5 +1,5 @@ """ -Utility functions used systemwide. +Galaxy Security """ import logging diff --git a/lib/galaxy/web/controllers/library.py b/lib/galaxy/web/controllers/library.py new file mode 100644 index 00000000000..c8b1f6f0573 --- /dev/null +++ b/lib/galaxy/web/controllers/library.py @@ -0,0 +1,218 @@ + +from galaxy.web.base.controller import * +from galaxy.datatypes import sniff +import logging, shutil, StringIO + +log = logging.getLogger( __name__ ) + +class Library( BaseController ): + + @web.expose + def index( self, trans, library_id = None, import_ids = [], **kwd ): + #use for importing an entry into your history + if import_ids: + if not isinstance( import_ids, list ): + import_ids = [import_ids] + history = trans.get_history() + for id in import_ids: + dataset = trans.app.model.LibraryFolderDatasetAssociation.get( id ).to_history_dataset_association() + history.add_dataset( dataset ) + dataset.flush() + history.flush() + return trans.show_ok_message( "%i datasets have been imported into your history" % len( import_ids ), refresh_frames=['history'] ) + elif library_id: + return trans.fill_template( 'library/user_view_library.mako', library = trans.app.model.Library.get( library_id ) ) + return trans.fill_template( 'library/user_list_libraries.mako', libraries = trans.app.model.Library.select() ) + + #make admin only + @web.expose + def manage_libraries( self, trans, **kwd ): + return trans.fill_template( 'library/admin_list_libraries.mako', libraries = trans.app.model.Library.select() ) + + #make admin only + @web.expose + def manage_library( self, trans, id=None, name="Unnamed", description=None, **kwd ): + if 'create_library' in kwd: + library = trans.app.model.Library( name = name, description = description ) + root_folder = trans.app.model.LibraryFolder( name = name, description = description ) + root_folder.flush() + library.root_folder = root_folder + library.flush() + trans.response.send_redirect( web.url_for( action='manage_folder', id = root_folder.id ) ) + elif id is None: + return trans.show_form( + web.FormBuilder( action = web.url_for(), title = "Create a new Library", name = "create_library", submit_text = "Submit" ) + .add_text( name = "name", label = "Name", value = "Unnamed", error = None, help = None ) + .add_text( name = "description", label = "Description", value = None, error = None, help = None ) + .add_input( 'hidden', "Create Library", 'create_library', use_label = False ) ) + library = trans.app.model.Library.get( id ) + if library: + return trans.fill_template( 'library/manage_library.mako', library = library ) + else: + return trans.show_error_message( "Invalid library specified" ) + + #make admin only + @web.expose + def manage_folder( self, trans, id=None, name="Unnamed", description=None, parent_id = None, **kwd ): + if 'create_folder' in kwd: + folder = trans.app.model.LibraryFolder( name = name, description = description ) + if parent_id: + parent_folder = trans.app.model.LibraryFolder.get( parent_id ) + parent_folder.add_folder( folder ) + folder.flush() + trans.response.send_redirect( web.url_for( action='manage_folder', id = folder.id ) ) + elif id is None: + return trans.show_form( + web.FormBuilder( action = web.url_for(), title = "Create a new Folder", name = "create_folder", submit_text = "Submit" ) + .add_text( name = "name", label = "Name", value = "Unnamed", error = None, help = None ) + .add_text( name = "description", label = "Description", value = None, error = None, help = None ) + .add_input( 'hidden', None, 'parent_id', value = parent_id, use_label = False ) + .add_input( 'hidden', "Create Folder", 'create_folder', use_label = False ) ) + folder = trans.app.model.LibraryFolder.get( id ) + if folder: + msg = '' + if 'rename_folder' in kwd: + folder.name = name + folder.description = description + folder.flush() + msg = 'Folder has been renamed.' + return trans.fill_template( 'library/manage_folder.mako', folder = folder, msg = msg ) + else: + return trans.show_error_message( "Invalid folder specified" ) + + + #make admin only + @web.expose + def manage_dataset( self, trans, id=None, name="Unnamed", info = 'no info', extension = None, folder_id = None, dbkey = None, **kwd ): + data_files = [] + def add_file( file_obj, name, extension, dbkey, info = 'no info', space_to_tab = False ): + data_type = None + temp_name = sniff.stream_to_file( file_obj ) + if space_to_tab: + line_count = sniff.convert_newlines_sep2tabs( temp_name ) + else: + line_count = sniff.convert_newlines( temp_name ) + if extension == 'auto': + data_type = sniff.guess_ext( temp_name, sniff_order=trans.app.datatypes_registry.sniff_order ) + else: + data_type = extension + dataset = trans.app.model.LibraryFolderDatasetAssociation( name = name, info = info, extension = data_type, dbkey = dbkey, create_dataset = True ) + folder = trans.app.model.LibraryFolder.get( folder_id ) + folder.add_dataset( dataset ) + dataset.flush() + # TODO, SET SECURTY INTERACTIVELY ON DATASET, right now everything is public + trans.app.security_agent.set_dataset_groups( dataset.dataset, [trans.app.security_agent.get_public_group()] ) + shutil.move( temp_name, dataset.dataset.file_name ) + dataset.dataset.state = dataset.dataset.states.OK + dataset.init_meta() + if line_count is not None: + try: + dataset.set_peek( line_count=line_count ) + except: + dataset.set_peek() + else: + dataset.set_peek() + dataset.set_size() + + if dataset.missing_meta(): + dataset.datatype.set_meta( dataset ) + trans.app.model.flush() + + return dataset + if 'create_dataset' in kwd: + #copied from upload tool action + last_dataset_created = None + data_file = kwd['file_data'] + url_paste = kwd['url_paste'] + space_to_tab = False + if 'space_to_tab' in kwd: + if kwd['space_to_tab'] not in ["None", None]: + space_to_tab = True + temp_name = "" + data_list = [] + + if 'filename' in dir( data_file ): + file_name = data_file.filename + file_name = file_name.split( '\\' )[-1] + file_name = file_name.split( '/' )[-1] + last_dataset_created = add_file( data_file.file, file_name, extension, dbkey, info="uploaded file", space_to_tab = space_to_tab ) + elif url_paste not in [ None, "" ]: + if url_paste.lower().find( 'http://' ) >= 0 or url_paste.lower().find( 'ftp://' ) >= 0: + url_paste = url_paste.replace( '\r', '' ).split( '\n' ) + for line in url_paste: + line = line.rstrip( '\r\n' ) + if line: + last_dataset_created = add_file( urllib.urlopen( line ), line, extension, dbkey, info="uploaded url", space_to_tab=space_to_tab ) + else: + is_valid = False + for line in url_paste: + line = line.rstrip( '\r\n' ) + if line: + is_valid = True + break + if is_valid: + last_dataset_created = add_file( StringIO.StringIO( url_paste ), 'Pasted Entry', extension, dbkey, info="pasted entry", space_to_tab=space_to_tab ) + trans.response.send_redirect( web.url_for( action='manage_dataset', id = last_dataset_created.id ) ) + #return self.manage_dataset( trans, id = last_dataset_created.id ) + elif id is None: + return trans.fill_template( 'library/new_dataset.mako', folder_id = folder_id ) + dataset = trans.app.model.LibraryFolderDatasetAssociation.get( id ) + if dataset: + #copied from edit attributes for 'regular' datasets + p = util.Params(kwd, safe=False) + if p.change: + # The user clicked the Save button on the 'Change data type' form + trans.app.datatypes_registry.change_datatype( dataset, p.datatype ) + trans.app.model.flush() + elif p.save: + # The user clicked the Save button on the 'Edit Attributes' form + dataset.name = name + dataset.info = info + + # The following for loop will save all metadata_spec items + for name, spec in dataset.datatype.metadata_spec.items(): + if spec.get("readonly"): + continue + optional = p.get("is_"+name, None) + if optional and optional == 'true': + # optional element... == 'true' actually means it is NOT checked (and therefore ommitted) + setattr(dataset.metadata,name,None) + else: + setattr(dataset.metadata,name,spec.unwrap(p.get(name, None), p)) + + dataset.datatype.after_edit( dataset ) + trans.app.model.flush() + return trans.show_ok_message( "Attributes updated" ) + elif p.detect: + # The user clicked the Auto-detect button on the 'Edit Attributes' form + for name, spec in dataset.datatype.metadata_spec.items(): + # We need to be careful about the attributes we are resetting + if name != 'name' and name != 'info' and name != 'dbkey': + if spec.get( 'default' ): + setattr( dataset.metadata,name,spec.unwrap( spec.get( 'default' ), spec )) + dataset.datatype.set_meta( dataset ) + dataset.datatype.after_edit( dataset ) + trans.app.model.flush() + return trans.show_ok_message( "Attributes updated" ) + + dataset.datatype.before_edit( dataset ) + + if "dbkey" in dataset.datatype.metadata_spec and not dataset.metadata.dbkey: + # Copy dbkey into metadata, for backwards compatability + # This looks like it does nothing, but getting the dbkey + # returns the metadata dbkey unless it is None, in which + # case it resorts to the old dbkey. Setting the dbkey + # sets it properly in the metadata + dataset.metadata.dbkey = dataset.dbkey + metadata = list() + # a list of MetadataParemeters + for name, spec in dataset.datatype.metadata_spec.items(): + if spec.visible: + metadata.append( spec.wrap( dataset.metadata.get(name), dataset ) ) + # let's not overwrite the imported datatypes module with the variable datatypes? + ldatatypes = [x for x in trans.app.datatypes_registry.datatypes_by_extension.iterkeys()] + ldatatypes.sort() + return trans.fill_template( "/library/manage_dataset.mako", dataset=dataset, metadata=metadata, + datatypes=ldatatypes, err=None ) + else: + return trans.show_error_message( "Invalid dataset specified" ) diff --git a/lib/galaxy/web/framework/__init__.py b/lib/galaxy/web/framework/__init__.py index 529cb6156b0..124d1549453 100644 --- a/lib/galaxy/web/framework/__init__.py +++ b/lib/galaxy/web/framework/__init__.py @@ -527,8 +527,8 @@ class FormBuilder( object ): self.action = action self.submit_text = submit_text self.inputs = [] - def add_input( self, type, name, label, value=None, error=None, help=None ): - self.inputs.append( FormInput( type, label, name, value, error, help ) ) + def add_input( self, type, name, label, value=None, error=None, help=None, use_label=True ): + self.inputs.append( FormInput( type, label, name, value, error, help, use_label ) ) return self def add_text( self, name, label, value=None, error=None, help=None ): return self.add_input( 'text', label, name, value, error, help ) @@ -539,13 +539,14 @@ class FormInput( object ): """ Simple class describing a form input element """ - def __init__( self, type, name, label, value=None, error=None, help=None ): + def __init__( self, type, name, label, value=None, error=None, help=None, use_label=True ): self.type = type self.name = name self.label = label self.value = value self.error = error self.help = help + self.use_label = use_label class FormData( object ): """ diff --git a/templates/admin_main.mako b/templates/admin_main.mako index f9c26121124..02e39576685 100644 --- a/templates/admin_main.mako +++ b/templates/admin_main.mako @@ -29,5 +29,11 @@ + + + Manage Libraries + + + diff --git a/templates/form.mako b/templates/form.mako index c003de02585..42fdb745ba3 100644 --- a/templates/form.mako +++ b/templates/form.mako @@ -21,9 +21,11 @@ $(function(){ cls += " form-row-error" %>
+ %if input.use_label: + %endif
diff --git a/templates/library/admin_list_libraries.mako b/templates/library/admin_list_libraries.mako new file mode 100644 index 00000000000..ffe82787da2 --- /dev/null +++ b/templates/library/admin_list_libraries.mako @@ -0,0 +1,16 @@ +<%inherit file="/base.mako"/> +<%def name="title()">View Libraries + +
+
Manage Libraries
+
+ %for library in libraries: + + %endfor + +
+
diff --git a/templates/library/manage_dataset.mako b/templates/library/manage_dataset.mako new file mode 100644 index 00000000000..f04615def51 --- /dev/null +++ b/templates/library/manage_dataset.mako @@ -0,0 +1,100 @@ +<%inherit file="/base.mako"/> +<%def name="title()">Edit Dataset Attributes + + +<%def name="datatype( dataset, datatypes )"> + + + +
+
Edit Attributes
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+
+
+ %for element in metadata: +
+ +
+ ${element.get_html()} +
+
+
+ %endfor +
+ +
+
+
+ +
+ +
+
+ This will inspect the dataset and attempt to correct the above column values + if they are not accurate. +
+
+
+
+ +

+ + +

+
Change data type
+
+
+ +
+ +
+ ${datatype( dataset, datatypes )} +
+ +
+ This will change the datatype of the existing dataset + but not modify its contents. Use this if Galaxy + has incorrectly guessed the type of your dataset. +
+
+
+
+ +
+
+
+
+ +manage containing folder +

diff --git a/templates/library/manage_folder.mako b/templates/library/manage_folder.mako new file mode 100644 index 00000000000..19e12555625 --- /dev/null +++ b/templates/library/manage_folder.mako @@ -0,0 +1,191 @@ +<%inherit file="/base.mako"/> + +<%def name="render_component( component )"> + <% + if isinstance( component, trans.app.model.LibraryFolder ): + return render_folder( component ) + elif isinstance( component, trans.app.model.LibraryFolderDatasetAssociation ): + return render_dataset( component ) + %> + + + +## Render the dataset `data` as history item, using `hid` as the displayed id +<%def name="render_dataset( data )"> + <% + if data.state in ['no state','',None]: + data_state = "queued" + else: + data_state = data.state + %> + ##

+
+
${data.display_name()}
+
+
+ + ## Header row for history items (name, state, action buttons) + +
+ %if data_state != 'ok': +
+ %endif +
+
+ ##display data + edit attributes + ##delete +
+ ##${data.display_name()} +
+ + ## Body for history items, extra info and actions, data "peek" + +
+ %if data_state == "queued": +
Job is waiting to run
+ %elif data_state == "running": +
Job is currently running
+ %elif data_state == "error": +
+ An error occurred running this job: ${data.display_info().strip()}, + report this error +
+ %elif data_state == "empty": +
No data: ${data.display_info()}
+ %elif data_state == "ok": +
+ ${data.blurb}, + format: ${data.ext}, + database: + %if data.dbkey == '?': + ${data.dbkey} + %else: + ${data.dbkey} + %endif +
+
Info: ${data.display_info()}
+ %if data.peek != "no peek": +
${data.display_peek()}
+ %endif + %else: +
Error: unknown dataset state "${data_state}".
+ %endif + + ## Recurse for child datasets + + +
+
+
+ + +## Render a folder +<%def name="render_folder( this_folder )"> + +
+
Contents of Folder: ${this_folder.name}
+
+
+
+ <% + components = list( this_folder.folders ) + list( this_folder.datasets ) + components = [ ( getattr( components[i], "order_id" ), i, components [i] ) for i in xrange( len( components ) ) ] + components.sort() + components = [ tup[-1] for tup in components ] + %> + %for component in components: + ${render_component( component )} + %endfor + +
+
+
+ +
+ +
+
+ + + + + + +<%def name="title()">Manage Folder: ${folder.name} + +
+
Change Folder Attributes
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+
+ + + +
+ +
+
Manage Folder Contents: ${folder.name}
+
+
+ %if folder.parent: + up a level + %elif folder.library_root: + manage library + %endif +
+
+
+ ${render_folder( folder )} +
+ +
+ +
+ +
+
+ + diff --git a/templates/library/manage_library.mako b/templates/library/manage_library.mako new file mode 100644 index 00000000000..5d94dfb6b78 --- /dev/null +++ b/templates/library/manage_library.mako @@ -0,0 +1,46 @@ +<%inherit file="/base.mako"/> +<%def name="title()">Manage Library + + +
+
Edit a Library: ${library.name}
+
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+ manage root folder +
+
+ diff --git a/templates/library/new_dataset.mako b/templates/library/new_dataset.mako new file mode 100644 index 00000000000..1c517cdfede --- /dev/null +++ b/templates/library/new_dataset.mako @@ -0,0 +1,997 @@ +<%inherit file="/base.mako"/> +<%def name="title()">Create New Library Dataset + +
+
Create a new Library Dataset
+
+
+ + + +
+ + +
+ + +
+ + +
+ +
+ + +
+ +
+ Here you may specify a list of URLs (one per line) or paste the contents of a file. +
+ + + +
+ +
+ +
+ + +
Yes
+ +
+ + Use this option if you are entering intervals by hand. +
+ + +
+ +
+ +
+ + +
+ +
+ Which format? See help below +
+ + +
+ +
+ +
+ + +##this should be generated dynamically +
+ + + +
+ +
+ + +
+ +
+ + +
+
+
+ diff --git a/templates/library/user_list_libraries.mako b/templates/library/user_list_libraries.mako new file mode 100644 index 00000000000..23a2b3b9ad1 --- /dev/null +++ b/templates/library/user_list_libraries.mako @@ -0,0 +1,14 @@ +<%inherit file="/base.mako"/> +<%def name="title()">View Libraries + +
+
View Library
+
+ %for library in libraries: + + %endfor + +
+
diff --git a/templates/library/user_view_library.mako b/templates/library/user_view_library.mako new file mode 100644 index 00000000000..3b948558f28 --- /dev/null +++ b/templates/library/user_view_library.mako @@ -0,0 +1,58 @@ +<%inherit file="/base.mako"/> + +<%def name="render_component( component )"> + <% + if isinstance( component, trans.app.model.LibraryFolder ): + return render_folder( component ) + elif isinstance( component, trans.app.model.LibraryFolderDatasetAssociation ): + return render_dataset( component ) + %> + + + +## Render the dataset `data` as history item, using `hid` as the displayed id +<%def name="render_dataset( data )"> +
+ ${data.name} +
+ + +## Render a folder +<%def name="render_folder( this_folder )"> + +
+ Folder: ${this_folder.name} + <% + components = list( this_folder.folders ) + list( this_folder.datasets ) + components = [ ( getattr( components[i], "order_id" ), i, components [i] ) for i in xrange( len( components ) ) ] + components.sort() + components = [ tup[-1] for tup in components ] + %> +
+ %for component in components: + ${render_component( component )} + %endfor +
+
+ + + + + + + +<%def name="title()">View Library: ${library.name} + +
+
Import from Library: ${library.name}
+
+
+ ${render_folder( library.root_folder )} + +
+ +
+ +
+
+ diff --git a/tool_conf.xml.sample b/tool_conf.xml.sample index e46f0c54ad1..69ccfe583ee 100644 --- a/tool_conf.xml.sample +++ b/tool_conf.xml.sample @@ -12,6 +12,7 @@ +
diff --git a/tools/data_source/access_libraries.xml b/tools/data_source/access_libraries.xml new file mode 100644 index 00000000000..ffd383c481a --- /dev/null +++ b/tools/data_source/access_libraries.xml @@ -0,0 +1,11 @@ + + + + stored locally + + + + + + +