From fa27e77fc44b021041c11d7a00ac72aaf60ce7b2 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Mon, 11 Jul 2022 18:04:20 +0200 Subject: [PATCH 1/6] Sort history switcher by most recently updated --- client/src/components/History/Modals/SelectorModal.vue | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/src/components/History/Modals/SelectorModal.vue b/client/src/components/History/Modals/SelectorModal.vue index 50a2b639995..0b5fac79c91 100644 --- a/client/src/components/History/Modals/SelectorModal.vue +++ b/client/src/components/History/Modals/SelectorModal.vue @@ -15,6 +15,8 @@ :per-page="perPage" :current-page="currentPage" :selectable="true" + :sort-by.sync="sortBy" + :sort-desc.sync="sortDesc" select-mode="single" selected-variant="success" @row-selected="switchToHistory" @@ -58,6 +60,8 @@ export default { filter: null, currentPage: 1, totalRows: 0, + sortBy: "update_time", + sortDesc: true, }; }, computed: { From c87a9a373c08c51ce319705edbb52750125db62a Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Mon, 11 Jul 2022 20:50:43 +0200 Subject: [PATCH 2/6] Make current history appear always first --- .../src/components/History/Modals/SelectorModal.vue | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/src/components/History/Modals/SelectorModal.vue b/client/src/components/History/Modals/SelectorModal.vue index 0b5fac79c91..5955f17667d 100644 --- a/client/src/components/History/Modals/SelectorModal.vue +++ b/client/src/components/History/Modals/SelectorModal.vue @@ -17,6 +17,7 @@ :selectable="true" :sort-by.sync="sortBy" :sort-desc.sync="sortDesc" + :sort-compare="currentFirstSortCompare" select-mode="single" selected-variant="success" @row-selected="switchToHistory" @@ -96,6 +97,17 @@ export default { this.totalRows = filteredItems.length; this.currentPage = 1; }, + /** Make the current history appear always first when sorting. */ + currentFirstSortCompare(a, b, key, sortDesc) { + if (a.id == this.currentHistoryId) { + return sortDesc ? 1 : -1; + } else if (b.id == this.currentHistoryId) { + return sortDesc ? -1 : 1; + } else { + // Fallback to default sorting + return false; + } + }, }, }; From 4a83e6f04df0e7f2c810dbd816d45dc09d4fbc16 Mon Sep 17 00:00:00 2001 From: Daniel Blankenberg Date: Mon, 11 Jul 2022 18:06:35 -0400 Subject: [PATCH 3/6] Fixes for JupyTool, and overlapping versions with jupyter. --- lib/galaxy/config/sample/tool_conf.xml.sample | 2 +- ...nteractivetool_jupyter_notebook_1.x.x.xml} | 24 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) rename tools/interactive/{interactivetool_jupyter_notebook_0.3.xml => interactivetool_jupyter_notebook_1.x.x.xml} (95%) diff --git a/lib/galaxy/config/sample/tool_conf.xml.sample b/lib/galaxy/config/sample/tool_conf.xml.sample index 1ec3b3140ef..790bc93a14f 100644 --- a/lib/galaxy/config/sample/tool_conf.xml.sample +++ b/lib/galaxy/config/sample/tool_conf.xml.sample @@ -136,7 +136,7 @@ - + diff --git a/tools/interactive/interactivetool_jupyter_notebook_0.3.xml b/tools/interactive/interactivetool_jupyter_notebook_1.x.x.xml similarity index 95% rename from tools/interactive/interactivetool_jupyter_notebook_0.3.xml rename to tools/interactive/interactivetool_jupyter_notebook_1.x.x.xml index 76a3bbfeda1..c7e4c9bb850 100644 --- a/tools/interactive/interactivetool_jupyter_notebook_0.3.xml +++ b/tools/interactive/interactivetool_jupyter_notebook_1.x.x.xml @@ -1,6 +1,6 @@ - + - quay.io/bgruening/docker-jupyter-notebook:ie2 + quay.io/bgruening/docker-jupyter-notebook:2021-03-05 @@ -75,7 +75,7 @@ except FileNotFoundError: ]]> - From 689a91d83b2f05a9296a73fb50a6bc0af2df06e8 Mon Sep 17 00:00:00 2001 From: Daniel Blankenberg Date: Tue, 12 Jul 2022 12:20:46 -0400 Subject: [PATCH 4/6] superfluous --- lib/galaxy/config/sample/tool_conf.xml.sample | 2 +- ...ook_1.x.x.xml => interactivetool_jupyter_notebook_1.0.0.xml} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename tools/interactive/{interactivetool_jupyter_notebook_1.x.x.xml => interactivetool_jupyter_notebook_1.0.0.xml} (100%) diff --git a/lib/galaxy/config/sample/tool_conf.xml.sample b/lib/galaxy/config/sample/tool_conf.xml.sample index 790bc93a14f..aeceadf2be5 100644 --- a/lib/galaxy/config/sample/tool_conf.xml.sample +++ b/lib/galaxy/config/sample/tool_conf.xml.sample @@ -136,7 +136,7 @@ - + diff --git a/tools/interactive/interactivetool_jupyter_notebook_1.x.x.xml b/tools/interactive/interactivetool_jupyter_notebook_1.0.0.xml similarity index 100% rename from tools/interactive/interactivetool_jupyter_notebook_1.x.x.xml rename to tools/interactive/interactivetool_jupyter_notebook_1.0.0.xml From 50d339e2d08e0b136f1a0ed36652e010b7d24cba Mon Sep 17 00:00:00 2001 From: Nicola Soranzo Date: Tue, 12 Jul 2022 13:58:23 +0100 Subject: [PATCH 5/6] Fix jobs API not allowing dates for ``date_range_min`` and ``date_range_max`` Dates (without time) are specifically mentioned in our parameters' documentation: `Limit listing of jobs to those that are updated after specified date (e.g. '2014-01-01')` Follow-up on https://github.com/galaxyproject/galaxy/pull/14281 , which broke backward compatibility as shown in this traceback: ``` _________________________ TestGalaxyJobs.test_get_jobs _________________________ has_gi = args = (), kwargs = {} tools = [{'config_file': '/home/runner/work/bioblend/bioblend/galaxy-release_22.05/tools/data_source/upload.xml', 'description...ata_source/ebi_sra.xml', 'description': 'ENA SRA', 'edam_operations': ['operation_0224'], 'edam_topics': [], ...}, ...] tool_ids = ['upload1', 'ucsc_table_direct1', 'ucsc_table_direct_archaea1', 'ncbi_datasets_source', 'sra_source', 'ebi_sra_main', ...] def wrapped_method(has_gi, *args, **kwargs): tools = has_gi.gi.tools.get_tools() # In panels by default, so flatten out sections... tool_ids = [_["id"] for _ in tools] if tool_id not in tool_ids: raise unittest.SkipTest(MISSING_TOOL_MESSAGE % tool_id) > return method(has_gi, *args, **kwargs) bioblend/_tests/test_util.py:113: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ bioblend/_tests/TestGalaxyJobs.py:46: in test_get_jobs jobs = self.gi.jobs.get_jobs(date_range_max=yesterday.strftime("%Y-%m-%d"), history_id=self.history_id) bioblend/galaxy/jobs/__init__.py:136: in get_jobs return self._get(params=params) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = , id = None deleted = False, contents = False, url = 'http://localhost:8080/api/jobs' params = {'date_range_max': '2022-07-11', 'history_id': '915ae9a80309f157', 'limit': 500, 'offset': 0} json = True def _get( self, id: Optional[str] = None, deleted: bool = False, contents: bool = False, url: Optional[str] = None, params=None, json: bool = True, ): """ Do a GET request, composing the URL from ``id``, ``deleted`` and ``contents``. Alternatively, an explicit ``url`` can be provided. If ``json`` is set to ``True``, return a decoded JSON object (and treat an empty or undecodable response as an error). The request will optionally be retried as configured by ``max_get_retries`` and ``get_retry_delay``: this offers some resilience in the presence of temporary failures. :return: The decoded response if ``json`` is set to ``True``, otherwise the response object """ if not url: url = self._make_url(module_id=id, deleted=deleted, contents=contents) attempts_left = self.max_get_retries() retry_delay = self.get_retry_delay() bioblend.log.debug("GET - attempts left: %s; retry delay: %s", attempts_left, retry_delay) msg = "" while attempts_left > 0: attempts_left -= 1 try: r = self.gi.make_get_request(url, params=params) except requests.exceptions.ConnectionError as e: msg = str(e) r = requests.Response() # empty Response object used when raising ConnectionError else: if r.status_code == 200: if not json: return r elif not r.content: msg = "GET: empty response" else: try: return r.json() except ValueError: msg = f"GET: invalid JSON : {r.content!r}" else: msg = f"GET: error {r.status_code}: {r.content!r}" msg = f"{msg}, {attempts_left} attempts left" if attempts_left <= 0: bioblend.log.error(msg) raise ConnectionError( msg, body=r.text, > status_code=r.status_code, ) E bioblend.ConnectionError: GET: error 400: b'{"err_msg":"1 validation error for Request\\nquery -> date_range_max\\n invalid datetime format (type=value_error.datetime)","err_code":400008,"validation_errors":[{"loc":["query","date_range_max"],"msg":"invalid datetime format","type":"value_error.datetime"}]}', 0 attempts left: {"err_msg":"1 validation error for Request\nquery -> date_range_max\n invalid datetime format (type=value_error.datetime)","err_code":400008,"validation_errors":[{"loc":["query","date_range_max"],"msg":"invalid datetime format","type":"value_error.datetime"}]} bioblend/galaxy/client.py:166: ConnectionError ``` from BioBlend CI tests: https://github.com/galaxyproject/bioblend/runs/7292858519?check_suite_focus=true --- lib/galaxy/managers/jobs.py | 7 +++++-- lib/galaxy/schema/schema.py | 9 ++++++--- lib/galaxy/webapps/galaxy/api/jobs.py | 14 +++++++++----- lib/galaxy_test/api/test_jobs.py | 18 +++++++++++------- 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/lib/galaxy/managers/jobs.py b/lib/galaxy/managers/jobs.py index 9a5ff96c317..a32a994b602 100644 --- a/lib/galaxy/managers/jobs.py +++ b/lib/galaxy/managers/jobs.py @@ -1,7 +1,10 @@ import json import logging import typing -from datetime import datetime +from datetime import ( + date, + datetime, +) from boltons.iterutils import remap from pydantic import ( @@ -108,7 +111,7 @@ class JobManager: def build_and_apply_filters(query, objects, filter_func): if objects is not None: - if isinstance(objects, str) or isinstance(objects, datetime): + if isinstance(objects, (str, date, datetime)): query = query.filter(filter_func(objects)) elif isinstance(objects, list): t = [] diff --git a/lib/galaxy/schema/schema.py b/lib/galaxy/schema/schema.py index 9fe7063aeb5..b4d5d46af2c 100644 --- a/lib/galaxy/schema/schema.py +++ b/lib/galaxy/schema/schema.py @@ -2,7 +2,10 @@ import json import re -from datetime import datetime +from datetime import ( + date, + datetime, +) from enum import Enum from typing import ( Any, @@ -1109,8 +1112,8 @@ class JobIndexQueryPayload(Model): user_id: Optional[DecodedDatabaseIdField] = None tool_ids: Optional[List[str]] = None tool_ids_like: Optional[List[str]] = None - date_range_min: Optional[datetime] = None - date_range_max: Optional[datetime] = None + date_range_min: Optional[Union[datetime, date]] = None + date_range_max: Optional[Union[datetime, date]] = None history_id: Optional[DecodedDatabaseIdField] = None workflow_id: Optional[DecodedDatabaseIdField] = None invocation_id: Optional[DecodedDatabaseIdField] = None diff --git a/lib/galaxy/webapps/galaxy/api/jobs.py b/lib/galaxy/webapps/galaxy/api/jobs.py index 9c33290f9f3..d88ba5c40f8 100644 --- a/lib/galaxy/webapps/galaxy/api/jobs.py +++ b/lib/galaxy/webapps/galaxy/api/jobs.py @@ -5,12 +5,16 @@ API operations on a jobs. """ import logging -from datetime import datetime +from datetime import ( + date, + datetime, +) from typing import ( Any, Dict, List, Optional, + Union, ) from fastapi import Query @@ -99,13 +103,13 @@ ToolIdLikeQueryParam: Optional[str] = Query( description="Limit listing of jobs to those that match one of the included tool ID sql-like patterns. If none, all are returned", ) -DateRangeMinQueryParam: Optional[datetime] = Query( +DateRangeMinQueryParam: Optional[Union[datetime, date]] = Query( default=None, title="Date Range Minimum", description="Limit listing of jobs to those that are updated after specified date (e.g. '2014-01-01')", ) -DateRangeMaxQueryParam: Optional[datetime] = Query( +DateRangeMaxQueryParam: Optional[Union[datetime, date]] = Query( default=None, title="Date Range Maximum", description="Limit listing of jobs to those that are updated before specified date (e.g. '2014-01-01')", @@ -187,8 +191,8 @@ class FastAPIJobs: view: JobIndexViewEnum = ViewQueryParam, tool_id: Optional[str] = ToolIdQueryParam, tool_id_like: Optional[str] = ToolIdLikeQueryParam, - date_range_min: Optional[datetime] = DateRangeMinQueryParam, - date_range_max: Optional[datetime] = DateRangeMaxQueryParam, + date_range_min: Optional[Union[datetime, date]] = DateRangeMinQueryParam, + date_range_max: Optional[Union[datetime, date]] = DateRangeMaxQueryParam, history_id: Optional[EncodedDatabaseIdField] = HistoryIdQueryParam, workflow_id: Optional[EncodedDatabaseIdField] = WorkflowIdQueryParam, invocation_id: Optional[EncodedDatabaseIdField] = InvocationIdQueryParam, diff --git a/lib/galaxy_test/api/test_jobs.py b/lib/galaxy_test/api/test_jobs.py index 6f2c0324ac8..92a8a30d960 100644 --- a/lib/galaxy_test/api/test_jobs.py +++ b/lib/galaxy_test/api/test_jobs.py @@ -84,21 +84,25 @@ class JobsApiTestCase(ApiTestCase, TestsTools): def test_index_date_filter(self, history_id): two_weeks_ago = (datetime.datetime.utcnow() - datetime.timedelta(14)).isoformat() last_week = (datetime.datetime.utcnow() - datetime.timedelta(7)).isoformat() - next_week = (datetime.datetime.utcnow() + datetime.timedelta(7)).isoformat() - today = datetime.datetime.utcnow().isoformat() - tomorrow = (datetime.datetime.utcnow() + datetime.timedelta(1)).isoformat() + before = datetime.datetime.utcnow().isoformat() + today = before[:10] + tomorrow = (datetime.datetime.utcnow() + datetime.timedelta(1)).isoformat()[:10] self.__history_with_new_dataset(history_id) + after = datetime.datetime.utcnow().isoformat() + # Test using dates jobs = self.__jobs_index(data={"date_range_min": today, "date_range_max": tomorrow}) assert len(jobs) > 0 - today_job_id = jobs[0]["id"] + today_job = jobs[0] + today_job_id = today_job["id"] + + # Test using datetimes + jobs = self.__jobs_index(data={"date_range_min": before, "date_range_max": after}) + assert today_job_id in map(itemgetter("id"), jobs), f"before: {before}, after: {after}, job: {today_job}" jobs = self.__jobs_index(data={"date_range_min": two_weeks_ago, "date_range_max": last_week}) assert today_job_id not in map(itemgetter("id"), jobs) - jobs = self.__jobs_index(data={"date_range_min": last_week, "date_range_max": next_week}) - assert today_job_id in map(itemgetter("id"), jobs) - @uses_test_history(require_new=True) def test_index_history(self, history_id): self.__history_with_new_dataset(history_id) From 3fd8541a1b4b04cb9b26807a664115b04f2123d7 Mon Sep 17 00:00:00 2001 From: guerler Date: Tue, 12 Jul 2022 14:04:37 -0400 Subject: [PATCH 6/6] Prevent iframe from snatching drag and drop events from uploader --- client/src/components/Upload/UploadModal.vue | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/client/src/components/Upload/UploadModal.vue b/client/src/components/Upload/UploadModal.vue index 106a16bbb5e..6bd89a2f129 100644 --- a/client/src/components/Upload/UploadModal.vue +++ b/client/src/components/Upload/UploadModal.vue @@ -52,6 +52,11 @@ export default { modalShow: false, }; }, + watch: { + modalShow() { + this.setIframeEvents(this.modalShow); + }, + }, mounted() { this.show(); // handles subsequent external requests to re-open a re-used modal @@ -70,6 +75,15 @@ export default { } this.hide(); }, + /** Disable mouse events in iframe to prevent interference with uploader drop box */ + setIframeEvents(disableEvents) { + const element = document.getElementById("galaxy_main"); + if (element) { + element.style["pointer-events"] = disableEvents ? "none" : "auto"; + } else { + console.warn("UploadModal::setIframeEvents - `galaxy_main` not found."); + } + }, }, };