From 4db9dc48784a8dbf90aa70fe30b195937ad9e314 Mon Sep 17 00:00:00 2001 From: Martin Cech Date: Wed, 22 May 2024 15:03:49 -0700 Subject: [PATCH 01/46] tighten the axt format sniffer e.g. it would incorrectly sniff chain as axt since the comment line starts with "##matrix=axtChain" --- lib/galaxy/datatypes/sequence.py | 35 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/lib/galaxy/datatypes/sequence.py b/lib/galaxy/datatypes/sequence.py index c88abfcde5cd..4e4982bb07a4 100644 --- a/lib/galaxy/datatypes/sequence.py +++ b/lib/galaxy/datatypes/sequence.py @@ -1206,25 +1206,24 @@ def sniff_prefix(self, file_prefix: FilePrefix) -> bool: >>> Axt().sniff( fname ) False """ - headers = get_headers(file_prefix, None) - if len(headers) < 4: + headers = get_headers(file_prefix, None, count=4, comment_designator="#") + if not ( + len(headers) == 4 + and len(headers[0]) == 9 + and headers[0][0].isdigit() + and headers[0][2].isdigit() + and headers[0][3].isdigit() + and headers[0][5].isdigit() + and headers[0][6].isdigit() + and headers[0][7] in data.valid_strand + and headers[0][8].isdigit() + and len(headers[1]) == 1 + and len(headers[2]) == 1 + and headers[3] == [] + ): return False - for hdr in headers: - if len(hdr) > 0 and hdr[0].startswith("##matrix=axt"): - return True - if len(hdr) > 0 and not hdr[0].startswith("#"): - if len(hdr) != 9: - return False - try: - for _ in (hdr[0], hdr[2], hdr[3], hdr[5], hdr[6], hdr[8]): - int(_) - except ValueError: - return False - if hdr[7] not in data.valid_strand: - return False - else: - return True - return False + else: + return True @build_sniff_from_prefix From 9ed4eda4b09a8f9fc626bb1ddcaa41ebf3885beb Mon Sep 17 00:00:00 2001 From: Martin Cech Date: Thu, 23 May 2024 11:45:11 -0700 Subject: [PATCH 02/46] add unit test to sniffer, minor improvements --- lib/galaxy/config/sample/datatypes_conf.xml.sample | 2 +- lib/galaxy/datatypes/sequence.py | 9 +++++++-- lib/galaxy/datatypes/test/2.chain | 10 ++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 lib/galaxy/datatypes/test/2.chain diff --git a/lib/galaxy/config/sample/datatypes_conf.xml.sample b/lib/galaxy/config/sample/datatypes_conf.xml.sample index 6ab652435950..f0f96237e80b 100644 --- a/lib/galaxy/config/sample/datatypes_conf.xml.sample +++ b/lib/galaxy/config/sample/datatypes_conf.xml.sample @@ -32,7 +32,7 @@ - + diff --git a/lib/galaxy/datatypes/sequence.py b/lib/galaxy/datatypes/sequence.py index 4e4982bb07a4..3aae50344d3e 100644 --- a/lib/galaxy/datatypes/sequence.py +++ b/lib/galaxy/datatypes/sequence.py @@ -1205,10 +1205,13 @@ def sniff_prefix(self, file_prefix: FilePrefix) -> bool: >>> fname = get_test_fname( 'alignment.lav' ) >>> Axt().sniff( fname ) False + >>> fname = get_test_fname( '2.chain' ) + >>> Axt().sniff( fname ) + False """ headers = get_headers(file_prefix, None, count=4, comment_designator="#") if not ( - len(headers) == 4 + len(headers) >= 3 and len(headers[0]) == 9 and headers[0][0].isdigit() and headers[0][2].isdigit() @@ -1219,9 +1222,11 @@ def sniff_prefix(self, file_prefix: FilePrefix) -> bool: and headers[0][8].isdigit() and len(headers[1]) == 1 and len(headers[2]) == 1 - and headers[3] == [] ): return False + # the optional fourth non-comment line has to be empty + if len(headers) == 4 and not headers[3] == []: + return False else: return True diff --git a/lib/galaxy/datatypes/test/2.chain b/lib/galaxy/datatypes/test/2.chain new file mode 100644 index 000000000000..822a4ff7b052 --- /dev/null +++ b/lib/galaxy/datatypes/test/2.chain @@ -0,0 +1,10 @@ +##matrix=axtChain 16 91,-114,-31,-123,-114,100,-125,-31,-31,-125,100,-114,-123,-31,-114,91 +##gapPenalties=axtChain O=400 E=30 +chain 67224 chr22 50818468 + 26560645 26561468 chr19 61431566 - 54838449 54839272 1 +823 + +chain 48985 chr22 50818468 + 26560497 26561116 chr19 61431566 + 29160089 29160708 2 +619 + +chain 46902 chr22 50818468 + 19792341 19793000 chr19 61431566 + 59180700 59181359 3 +659 From ce801e7d8ecdc707d574231772998b74b57b5082 Mon Sep 17 00:00:00 2001 From: Martin Cech Date: Thu, 23 May 2024 20:51:26 +0200 Subject: [PATCH 03/46] tighten more! Co-authored-by: Nicola Soranzo --- lib/galaxy/datatypes/sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/galaxy/datatypes/sequence.py b/lib/galaxy/datatypes/sequence.py index 3aae50344d3e..6bf58bacf4b0 100644 --- a/lib/galaxy/datatypes/sequence.py +++ b/lib/galaxy/datatypes/sequence.py @@ -1213,7 +1213,7 @@ def sniff_prefix(self, file_prefix: FilePrefix) -> bool: if not ( len(headers) >= 3 and len(headers[0]) == 9 - and headers[0][0].isdigit() + and headers[0][0] == "0" and headers[0][2].isdigit() and headers[0][3].isdigit() and headers[0][5].isdigit() From 6ac51e5a9b5d8fb8265770349b181149f267bdc2 Mon Sep 17 00:00:00 2001 From: Martin Cech Date: Thu, 23 May 2024 11:53:55 -0700 Subject: [PATCH 04/46] switch from isdigit to isdecimal --- lib/galaxy/datatypes/sequence.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/galaxy/datatypes/sequence.py b/lib/galaxy/datatypes/sequence.py index 6bf58bacf4b0..911a1a2db31a 100644 --- a/lib/galaxy/datatypes/sequence.py +++ b/lib/galaxy/datatypes/sequence.py @@ -1214,12 +1214,12 @@ def sniff_prefix(self, file_prefix: FilePrefix) -> bool: len(headers) >= 3 and len(headers[0]) == 9 and headers[0][0] == "0" - and headers[0][2].isdigit() - and headers[0][3].isdigit() - and headers[0][5].isdigit() - and headers[0][6].isdigit() + and headers[0][2].isdecimal() + and headers[0][3].isdecimal() + and headers[0][5].isdecimal() + and headers[0][6].isdecimal() and headers[0][7] in data.valid_strand - and headers[0][8].isdigit() + and headers[0][8].isdecimal() and len(headers[1]) == 1 and len(headers[2]) == 1 ): From 7819c9a3e9d8964d5e33a21b95e52de339cbae69 Mon Sep 17 00:00:00 2001 From: Martin Cech Date: Fri, 24 May 2024 10:14:49 -0700 Subject: [PATCH 05/46] switch to isdecimal --- lib/galaxy/datatypes/chain.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/galaxy/datatypes/chain.py b/lib/galaxy/datatypes/chain.py index acf451ce85c8..56e3b14e0e6f 100644 --- a/lib/galaxy/datatypes/chain.py +++ b/lib/galaxy/datatypes/chain.py @@ -103,9 +103,9 @@ def sniff_prefix(self, file_prefix: FilePrefix) -> bool: len(tokens) in [12, 13] and tokens[4] in self.strands and tokens[9] in self.strands - and tokens[3].isdigit() - and tokens[5].isdigit() - and tokens[6].isdigit() + and tokens[3].isdecimal() + and tokens[5].isdecimal() + and tokens[6].isdecimal() ): return False prior_token_len = 0 @@ -118,7 +118,7 @@ def sniff_prefix(self, file_prefix: FilePrefix) -> bool: return False if len(tokens) not in [1, 3]: return False - if not all(token.isdigit() for token in tokens): + if not all(token.isdecimal() for token in tokens): return False prior_token_len = len(tokens) if prior_token_len == 1: @@ -167,7 +167,7 @@ def sniff_prefix(self, file_prefix: FilePrefix) -> bool: if line: # first non-empty line if line.startswith("net"): tokens = line.split() - if not (len(tokens) == 3 and tokens[2].isdigit()): + if not (len(tokens) == 3 and tokens[2].isdecimal()): return False for line in fh: if line[0] != " ": # children are indented one space @@ -180,11 +180,11 @@ def sniff_prefix(self, file_prefix: FilePrefix) -> bool: len(tokens) >= 7 # seven fixed fields and len(tokens) <= 41 # plus seventeen optional name/value pairs and tokens[0] in allowed_classes - and tokens[1].isdigit() - and tokens[2].isdigit() + and tokens[1].isdecimal() + and tokens[2].isdecimal() and tokens[4] in strands - and tokens[5].isdigit() - and tokens[6].isdigit() + and tokens[5].isdecimal() + and tokens[6].isdecimal() ): return False else: From cae34c843a983219e45c10ec98aef293a8176ef3 Mon Sep 17 00:00:00 2001 From: Martin Cech Date: Fri, 24 May 2024 10:50:47 -0700 Subject: [PATCH 06/46] refactor chain and ucsc.net sniffing properly ignore comments do not read the dataset unbounded, I think we have enough info from the headers and the first data line --- lib/galaxy/datatypes/chain.py | 110 +++++++++++++--------------------- 1 file changed, 42 insertions(+), 68 deletions(-) diff --git a/lib/galaxy/datatypes/chain.py b/lib/galaxy/datatypes/chain.py index 56e3b14e0e6f..40fd847d4454 100644 --- a/lib/galaxy/datatypes/chain.py +++ b/lib/galaxy/datatypes/chain.py @@ -6,10 +6,7 @@ from galaxy.datatypes.metadata import MetadataElement from galaxy.datatypes.protocols import DatasetProtocol -from galaxy.datatypes.sniff import ( - build_sniff_from_prefix, - FilePrefix, -) +from galaxy.datatypes.sniff import build_sniff_from_prefix, FilePrefix, get_headers from galaxy.util import ( commaify, compression_utils, @@ -91,41 +88,31 @@ def sniff_prefix(self, file_prefix: FilePrefix) -> bool: >>> fname = get_test_fname( '1.chain' ) >>> Chain().sniff( fname ) True + >>> fname = get_test_fname( '2.chain' ) + >>> Chain().sniff( fname ) + True >>> """ - fh = file_prefix.string_io() - for line in fh: - line = line.strip() - if line: # first non-empty line - if line.startswith("chain"): - tokens = line.split() - if not ( - len(tokens) in [12, 13] - and tokens[4] in self.strands - and tokens[9] in self.strands - and tokens[3].isdecimal() - and tokens[5].isdecimal() - and tokens[6].isdecimal() - ): - return False - prior_token_len = 0 - for line in fh: - line = line.strip() - if line == "": - break - tokens = line.split() - if prior_token_len == 1: - return False - if len(tokens) not in [1, 3]: - return False - if not all(token.isdecimal() for token in tokens): - return False - prior_token_len = len(tokens) - if prior_token_len == 1: - return True - else: - return False - return False + headers = get_headers(file_prefix, None, count=2, comment_designator="#") + if not ( + len(headers) == 2 + and len(headers[0]) in [12, 13] + and headers[0][0] == "chain" + and headers[0][1].isdecimal() + and headers[0][3].isdecimal() + and headers[0][4] in self.strands + and headers[0][5].isdecimal() + and headers[0][6].isdecimal() + and headers[0][8].isdecimal() + and headers[0][9] in self.strands + and headers[0][10].isdecimal() + and headers[0][11].isdecimal() + and headers[1][0].isdecimal() + and len(headers[1]) in [1, 3] + ): + return False + else: + return True @build_sniff_from_prefix @@ -161,34 +148,21 @@ def sniff_prefix(self, file_prefix: FilePrefix) -> bool: allowed_classes = ["fill", "gap"] strands = ["+", "-"] - fh = file_prefix.string_io() - for line in fh: - line = line.strip() - if line: # first non-empty line - if line.startswith("net"): - tokens = line.split() - if not (len(tokens) == 3 and tokens[2].isdecimal()): - return False - for line in fh: - if line[0] != " ": # children are indented one space - return False - line = line.strip() - if line == "": - break - tokens = line.split() - if not ( - len(tokens) >= 7 # seven fixed fields - and len(tokens) <= 41 # plus seventeen optional name/value pairs - and tokens[0] in allowed_classes - and tokens[1].isdecimal() - and tokens[2].isdecimal() - and tokens[4] in strands - and tokens[5].isdecimal() - and tokens[6].isdecimal() - ): - return False - else: - return True - else: - return False - return False + headers = get_headers(file_prefix, None, count=2, comment_designator="#") + if not ( + len(headers) == 2 + and len(headers[0]) == 3 + and headers[0][0] == "net" + and headers[0][2].isdecimal() + and len(headers[1]) >= 7 # seven fixed fields + and len(headers[1]) <= 41 # plus seventeen optional name/value pairs + and headers[1][0] in allowed_classes + and headers[1][1].isdecimal() + and headers[1][2].isdecimal() + and headers[1][4] in strands + and headers[1][5].isdecimal() + and headers[1][6].isdecimal() + ): + return False + else: + return True From 706ae5fab1b839b549d64c12259a6863c3f8cde8 Mon Sep 17 00:00:00 2001 From: Martin Cech Date: Fri, 24 May 2024 14:17:57 -0700 Subject: [PATCH 07/46] sorty sort --- lib/galaxy/datatypes/chain.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/datatypes/chain.py b/lib/galaxy/datatypes/chain.py index 40fd847d4454..4e853865d042 100644 --- a/lib/galaxy/datatypes/chain.py +++ b/lib/galaxy/datatypes/chain.py @@ -6,7 +6,11 @@ from galaxy.datatypes.metadata import MetadataElement from galaxy.datatypes.protocols import DatasetProtocol -from galaxy.datatypes.sniff import build_sniff_from_prefix, FilePrefix, get_headers +from galaxy.datatypes.sniff import ( + build_sniff_from_prefix, + FilePrefix, + get_headers, +) from galaxy.util import ( commaify, compression_utils, From cdbd5e32ce83021e0f7812362cbc21416bc1cf41 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Mon, 27 May 2024 10:05:16 +0200 Subject: [PATCH 08/46] Drop backend restrictions to switch to immutable histories --- lib/galaxy/webapps/base/webapp.py | 4 ++-- lib/galaxy/webapps/galaxy/controllers/history.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/galaxy/webapps/base/webapp.py b/lib/galaxy/webapps/base/webapp.py index 0cd502606613..7874de2ef77b 100644 --- a/lib/galaxy/webapps/base/webapp.py +++ b/lib/galaxy/webapps/base/webapp.py @@ -914,7 +914,7 @@ def get_history(self, create=False, most_recent=False): return history def set_history(self, history): - if history and not history.deleted: + if history: self.galaxy_session.current_history = history self.sa_session.add(self.galaxy_session) with transaction(self.sa_session): @@ -932,7 +932,7 @@ def get_or_create_default_history(self): # Just return the current history if one exists and is not deleted. history = self.galaxy_session.current_history - if history and not history.deleted: + if history: return history # Look for an existing history that has the default name, is not diff --git a/lib/galaxy/webapps/galaxy/controllers/history.py b/lib/galaxy/webapps/galaxy/controllers/history.py index 3db60f29ad2a..a50a7b35c52f 100644 --- a/lib/galaxy/webapps/galaxy/controllers/history.py +++ b/lib/galaxy/webapps/galaxy/controllers/history.py @@ -325,7 +325,7 @@ def history_data(self, trans, history): def set_as_current(self, trans, id, **kwargs): """Change the current user's current history to one with `id`.""" try: - history = self.history_manager.get_mutable(self.decode_id(id), trans.user, current_history=trans.history) + history = self.history_manager.get_owned(self.decode_id(id), trans.user, current_history=trans.history) trans.set_history(history) return self.history_data(trans, history) except exceptions.MessageException as msg_exc: From ead71fd8b4be7e1001f83d069221f781edbd6d24 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Mon, 27 May 2024 10:15:42 +0200 Subject: [PATCH 09/46] Move immutability restriction check from HistoryView to HistoryPanel --- .../History/CurrentHistory/HistoryPanel.vue | 14 +++++++++++--- client/src/components/History/HistoryView.vue | 18 ++---------------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/client/src/components/History/CurrentHistory/HistoryPanel.vue b/client/src/components/History/CurrentHistory/HistoryPanel.vue index 759efea9a90b..8268153099f3 100644 --- a/client/src/components/History/CurrentHistory/HistoryPanel.vue +++ b/client/src/components/History/CurrentHistory/HistoryPanel.vue @@ -3,7 +3,7 @@ import { BAlert } from "bootstrap-vue"; import { storeToRefs } from "pinia"; import { computed, onMounted, type Ref, ref, set as VueSet, unref, watch } from "vue"; -import type { HistoryItemSummary, HistorySummaryExtended } from "@/api"; +import { type HistoryItemSummary, type HistorySummaryExtended, userOwnsHistory } from "@/api"; import { copyDataset } from "@/api/datasets"; import ExpandedItems from "@/components/History/Content/ExpandedItems"; import SelectedItems from "@/components/History/Content/SelectedItems"; @@ -15,6 +15,7 @@ import { startWatchingHistory } from "@/store/historyStore/model/watchHistory"; import { useEventStore } from "@/stores/eventStore"; import { useHistoryItemsStore } from "@/stores/historyItemsStore"; import { useHistoryStore } from "@/stores/historyStore"; +import { useUserStore } from "@/stores/userStore"; import { type Alias, getOperatorForAlias } from "@/utils/filtering"; import { setDrag } from "@/utils/setDrag"; @@ -49,7 +50,6 @@ interface Props { listOffset?: number; history: HistorySummaryExtended; filter?: string; - canEditHistory?: boolean; filterable?: boolean; isMultiViewItem?: boolean; } @@ -59,7 +59,6 @@ type ContentItemRef = Record | null const props = withDefaults(defineProps(), { listOffset: 0, filter: "", - canEditHistory: true, filterable: false, isMultiViewItem: false, }); @@ -90,6 +89,14 @@ const { lastCheckedTime, totalMatchesCount, isWatching } = storeToRefs(useHistor const historyStore = useHistoryStore(); const historyItemsStore = useHistoryItemsStore(); +const { currentUser } = storeToRefs(useUserStore()); + +const currentUserOwnsHistory = computed(() => { + return userOwnsHistory(currentUser.value, props.history); +}); +const canEditHistory = computed(() => { + return currentUserOwnsHistory.value && !props.history.deleted && !props.history.archived; +}); const historyUpdateTime = computed(() => { return props.history.update_time; @@ -542,6 +549,7 @@ function setItemDragstart( :is-watching="isWatching" :last-checked="lastCheckedTime" :show-controls="canEditHistory" + :owned-by-current-user="userOwnsHistory(currentUser, history)" :filter-text.sync="filterText" :hide-reload="isMultiViewItem" @reloadContents="reloadContents" /> diff --git a/client/src/components/History/HistoryView.vue b/client/src/components/History/HistoryView.vue index e185e7517147..23ccb4f62202 100644 --- a/client/src/components/History/HistoryView.vue +++ b/client/src/components/History/HistoryView.vue @@ -35,12 +35,7 @@ :selected-collections.sync="selectedCollections" :show-controls="false" @view-collection="onViewCollection" /> - + @@ -87,23 +82,14 @@ export default { return this.currentHistory?.id == this.history?.id; }, isSetAsCurrentDisabled() { - return this.isCurrentHistory || this.history.archived || this.history.purged; + return this.isCurrentHistory; }, setAsCurrentTitle() { if (this.isCurrentHistory) { return "This history is already your current history."; } - if (this.history.archived) { - return "This history has been archived and cannot be set as your current history. Unarchive it first."; - } - if (this.history.purged) { - return "This history has been purged and cannot be set as your current history."; - } return "Switch to this history"; }, - canEditHistory() { - return this.userOwnsHistory && !this.history.archived && !this.history.purged; - }, showHistoryArchived() { return this.history.archived && this.userOwnsHistory; }, From ade613ab5ded00c7651f19c42c346803b4693a52 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Mon, 27 May 2024 10:18:11 +0200 Subject: [PATCH 10/46] Display history status in Panel --- .../CurrentHistory/HistoryMessages.vue | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/client/src/components/History/CurrentHistory/HistoryMessages.vue b/client/src/components/History/CurrentHistory/HistoryMessages.vue index 91e06beb75fc..3d97f80dd24a 100644 --- a/client/src/components/History/CurrentHistory/HistoryMessages.vue +++ b/client/src/components/History/CurrentHistory/HistoryMessages.vue @@ -1,10 +1,15 @@