diff --git a/client/galaxy/scripts/mvc/library/library-dataset-view.js b/client/galaxy/scripts/mvc/library/library-dataset-view.js
index 53c6d30eb6f..8137376b77f 100644
--- a/client/galaxy/scripts/mvc/library/library-dataset-view.js
+++ b/client/galaxy/scripts/mvc/library/library-dataset-view.js
@@ -703,6 +703,12 @@ var LibraryDatasetView = Backbone.View.extend({
<%= _.escape(item.get("file_size")) %> |
<% } %>
+ <% if (item.get("update_time")) { %>
+
+ | Date last updated (UTC) |
+ <%= _.escape(item.get("update_time")) %> |
+
+ <% } %>
<% if (item.get("date_uploaded")) { %>
| Date uploaded (UTC) |
@@ -880,6 +886,12 @@ var LibraryDatasetView = Backbone.View.extend({
<%= _.escape(ldda.get("file_size")) %> |
<% } %>
+ <% if (ldda.get("update_time")) { %>
+
+ | Date last updated (UTC) |
+ <%= _.escape(ldda.get("update_time")) %> |
+
+ <% } %>
<% if (ldda.get("date_uploaded")) { %>
| Date uploaded (UTC) |
@@ -1006,6 +1018,10 @@ var LibraryDatasetView = Backbone.View.extend({
Size |
<%= _.escape(item.get("file_size")) %> |
+
+ | Date last updated (UTC) |
+ <%= _.escape(item.get("update_time")) %> |
+
| Date uploaded (UTC) |
<%= _.escape(item.get("date_uploaded")) %> |
diff --git a/client/galaxy/scripts/mvc/library/library-folderlist-view.js b/client/galaxy/scripts/mvc/library/library-folderlist-view.js
index 1b6c271f99c..cedc161f341 100644
--- a/client/galaxy/scripts/mvc/library/library-folderlist-view.js
+++ b/client/galaxy/scripts/mvc/library/library-folderlist-view.js
@@ -517,7 +517,7 @@ var FolderListView = Backbone.View.extend({
- Time Updated (UTC)
+ Date Updated (UTC)
|
diff --git a/client/galaxy/scripts/mvc/library/library-util.js b/client/galaxy/scripts/mvc/library/library-util.js
index c4d546c4b25..bb174a7cb56 100644
--- a/client/galaxy/scripts/mvc/library/library-util.js
+++ b/client/galaxy/scripts/mvc/library/library-util.js
@@ -1,45 +1,38 @@
/**
- * Create alphabetical based two-argument comparator
- * that takes into account that Folder comes before Dataset.
+ * Create alphabetical based two-argument comparator to handle library items (including folders)
* If sort_key is not present it is set to ''.
* @param {str} sort_key key to sort by
* @param {str} sort_order order to sort by (asc, desc)
* @return {function} two-argument comparator function
*/
var generateComparator = (sort_key, sort_order) => (itemA, itemB) => {
- if (itemA.get("type") === itemB.get("type")) {
- if (!itemA.has(sort_key) && !itemB.has(sort_key)) {
- return 0;
- } else if (!itemA.has(sort_key)) {
- return 1;
- } else if (!itemB.has(sort_key)) {
- return -1;
- }
- var comparable_itemA_key;
- var comparable_itemB_key;
- if (typeof itemA.get(sort_key) === "number") {
- comparable_itemA_key = itemA.get(sort_key);
- comparable_itemB_key = itemB.get(sort_key);
- }
- else
- {
- comparable_itemA_key = itemA.get(sort_key).toLowerCase();
- comparable_itemB_key = itemB.get(sort_key).toLowerCase();
- }
- if ( comparable_itemA_key > comparable_itemB_key) {
- return sort_order === "asc" ? 1 : -1;
- }
- if (comparable_itemB_key > comparable_itemA_key) {
- return sort_order === "asc" ? -1 : 1;
- }
-
- return 0; // equal
- } else {
- if (itemA.get("type") === "folder") {
- return -1; // folder is always before dataset
- }
+ if (!itemA.has(sort_key) && !itemB.has(sort_key)) {
+ return 0;
+ } else if (!itemA.has(sort_key)) {
return 1;
+ } else if (!itemB.has(sort_key)) {
+ return -1;
}
+ var comparable_itemA_key;
+ var comparable_itemB_key;
+ if (typeof itemA.get(sort_key) === "number") {
+ comparable_itemA_key = itemA.get(sort_key);
+ comparable_itemB_key = itemB.get(sort_key);
+ }
+ else
+ {
+ comparable_itemA_key = itemA.get(sort_key).toLowerCase();
+ comparable_itemB_key = itemB.get(sort_key).toLowerCase();
+ }
+
+ if ( comparable_itemA_key > comparable_itemB_key) {
+ return sort_order === "asc" ? 1 : -1;
+ }
+ if (comparable_itemB_key > comparable_itemA_key) {
+ return sort_order === "asc" ? -1 : 1;
+ }
+
+ return 0; // equal
};
export default {
generateComparator: generateComparator
diff --git a/lib/galaxy/managers/library_datasets.py b/lib/galaxy/managers/library_datasets.py
index 6cdf791fd3a..20b76a1bf50 100644
--- a/lib/galaxy/managers/library_datasets.py
+++ b/lib/galaxy/managers/library_datasets.py
@@ -101,6 +101,7 @@ class LibraryDatasetsManager(datasets.DatasetAssociationManager):
ldda.dbkey = new_genome_build
changed = True
if changed:
+ ldda.update_parent_folder_update_times()
trans.sa_session.add(ldda)
trans.sa_session.flush()
return changed
@@ -223,6 +224,7 @@ class LibraryDatasetsManager(datasets.DatasetAssociationManager):
rval['full_path'] = full_path
rval['file_size'] = util.nice_size(int(ldda.get_size()))
rval['date_uploaded'] = ldda.create_time.strftime("%Y-%m-%d %I:%M %p")
+ rval['update_time'] = ldda.update_time.strftime("%Y-%m-%d %I:%M %p")
rval['can_user_modify'] = trans.user_is_admin or trans.app.security_agent.can_modify_library_item(current_user_roles, ld)
rval['is_unrestricted'] = trans.app.security_agent.dataset_is_public(ldda.dataset)
rval['tags'] = self.tag_handler.get_tags_str(ldda.tags)
diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py
index 448f42f442b..1e075149559 100644
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -31,6 +31,7 @@ from sqlalchemy import (
not_,
or_,
select,
+ text,
true,
type_coerce,
types)
@@ -3425,6 +3426,7 @@ class LibraryDataset(RepresentById):
uploaded_by=ldda.user.email,
message=ldda.message,
date_uploaded=ldda.create_time.isoformat(),
+ update_time=ldda.update_time.isoformat(),
file_size=int(ldda.get_size()),
file_ext=ldda.ext,
data_type=ldda.datatype.__class__.__module__ + '.' + ldda.datatype.__class__.__name__,
@@ -3593,6 +3595,32 @@ class LibraryDatasetDatasetAssociation(DatasetInstance, HasName, RepresentById):
rval['metadata_' + name] = val
return rval
+ def update_parent_folder_update_times(self):
+ # sets the update_time for all continaing folders up the tree
+ ldda = self
+
+ sql = text(
+ '''
+ WITH RECURSIVE parent_folders_of(folder_id) AS
+ (SELECT folder_id
+ FROM library_dataset
+ WHERE id = :library_dataset_id
+ UNION ALL
+ SELECT library_folder.parent_id
+ FROM library_folder, parent_folders_of
+ WHERE library_folder.id = parent_folders_of.folder_id )
+ UPDATE library_folder
+ SET update_time =
+ (SELECT update_time
+ FROM library_dataset_dataset_association
+ WHERE id = :ldda_id)
+ WHERE exists (SELECT 1 FROM parent_folders_of
+ WHERE library_folder.id = parent_folders_of.folder_id)
+ ''').execution_options(autocommit=True)
+ ret = object_session(self).execute(sql, {'library_dataset_id': ldda.library_dataset_id, 'ldda_id': ldda.id})
+ if ret.rowcount < 1:
+ log.warn('Attempt to updated parent folder times failed: {0} records updated.'.format(ret.rowcount))
+
class ExtendedMetadata(RepresentById):
def __init__(self, data):
diff --git a/lib/galaxy/webapps/base/controller.py b/lib/galaxy/webapps/base/controller.py
index 41c1d66722e..f08c3b81d0b 100644
--- a/lib/galaxy/webapps/base/controller.py
+++ b/lib/galaxy/webapps/base/controller.py
@@ -546,6 +546,7 @@ class UsesLibraryMixinItems(SharableItemSecurityMixin):
# I don't see a reason why hdas copied into libraries should not be visible.
# If there is, refactor `ldda.visible = True` to do this only when adding HDCAs.
ldda.visible = True
+ ldda.update_parent_folder_update_times()
trans.sa_session.flush()
ldda_dict = ldda.to_dict()
rval = trans.security.encode_dict_ids(ldda_dict)
diff --git a/lib/galaxy/webapps/galaxy/api/folder_contents.py b/lib/galaxy/webapps/galaxy/api/folder_contents.py
index a7ecc98481d..da81e0229d8 100644
--- a/lib/galaxy/webapps/galaxy/api/folder_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/folder_contents.py
@@ -81,13 +81,13 @@ class FolderContentsController(BaseAPIController, UsesLibraryMixin, UsesLibraryM
for content_item in self._load_folder_contents(trans, folder, deleted):
return_item = {}
encoded_id = trans.security.encode_id(content_item.id)
- update_time = content_item.update_time.strftime("%Y-%m-%d %I:%M %p")
create_time = content_item.create_time.strftime("%Y-%m-%d %I:%M %p")
if content_item.api_type == 'folder':
encoded_id = 'F' + encoded_id
can_modify = is_admin or (trans.user and trans.app.security_agent.can_modify_library_item(current_user_roles, folder))
can_manage = is_admin or (trans.user and trans.app.security_agent.can_manage_library_item(current_user_roles, folder))
+ update_time = content_item.update_time.strftime("%Y-%m-%d %I:%M %p")
return_item.update(dict(can_modify=can_modify, can_manage=can_manage))
if content_item.description:
return_item.update(dict(description=content_item.description))
@@ -107,11 +107,13 @@ class FolderContentsController(BaseAPIController, UsesLibraryMixin, UsesLibraryM
can_manage = is_admin or (trans.user and trans.app.security_agent.can_manage_dataset(current_user_roles, content_item.library_dataset_dataset_association.dataset))
raw_size = int(content_item.library_dataset_dataset_association.get_size())
nice_size = util.nice_size(raw_size)
+ update_time = content_item.library_dataset_dataset_association.update_time.strftime("%Y-%m-%d %I:%M %p")
library_dataset_dict = content_item.to_dict()
encoded_ldda_id = trans.security.encode_id(content_item.library_dataset_dataset_association.id)
return_item.update(dict(file_ext=library_dataset_dict['file_ext'],
date_uploaded=library_dataset_dict['date_uploaded'],
+ update_time=update_time,
is_unrestricted=is_unrestricted,
is_private=is_private,
can_manage=can_manage,
diff --git a/test/api/test_libraries.py b/test/api/test_libraries.py
index a9f8d5f4f05..11454c23695 100644
--- a/test/api/test_libraries.py
+++ b/test/api/test_libraries.py
@@ -232,6 +232,26 @@ class LibrariesApiTestCase(api.ApiTestCase, TestsDatasets):
self._assert_status_code_is(create_response, 200)
self._assert_has_keys(create_response.json(), "name", "id")
+ def test_create_dataset_in_subfolder(self):
+ library = self.library_populator.new_private_library("ForCreateDatasets")
+ folder_response = self._create_folder(library)
+ self._assert_status_code_is(folder_response, 200)
+ folder_id = folder_response.json()[0]['id']
+ subfolder_response = self._create_subfolder(folder_id)
+ self._assert_status_code_is(folder_response, 200)
+ print(subfolder_response.json())
+ subfolder_id = subfolder_response.json()['id']
+ history_id = self.dataset_populator.new_history()
+ hda_id = self.dataset_populator.new_dataset(history_id, content="1 2 3 sub")['id']
+ payload = {'from_hda_id': hda_id}
+ create_response = self._post("folders/%s/contents" % subfolder_id, payload)
+ self._assert_status_code_is(create_response, 200)
+ self._assert_has_keys(create_response.json(), "name", "id")
+ dataset_update_time = create_response.json()['update_time']
+ container_fetch_response = self.galaxy_interactor.get("folders/%s/contents" % folder_id)
+ container_update_time = container_fetch_response.json()['folder_contents'][0]['update_time']
+ assert dataset_update_time == container_update_time, container_fetch_response
+
def test_update_dataset_in_folder(self):
ld = self._create_dataset_in_folder_in_library("ForUpdateDataset")
data = {'name': 'updated_name', 'file_ext': 'fastq', 'misc_info': 'updated_info', 'genome_build': 'updated_genome_build'}
@@ -301,6 +321,13 @@ class LibrariesApiTestCase(api.ApiTestCase, TestsDatasets):
)
return self._post("libraries/%s/contents" % library["id"], data=create_data)
+ def _create_subfolder(self, containing_folder_id):
+ create_data = dict(
+ description="new subfolder desc",
+ name="New Subfolder",
+ )
+ return self._post("folders/%s" % containing_folder_id, data=create_data)
+
def _create_dataset_in_folder_in_library(self, library_name):
library = self.library_populator.new_private_library(library_name)
folder_response = self._create_folder(library)
|