From 8d1a8f57d6e788e6a32489c45d7e8eb44d7a58fc Mon Sep 17 00:00:00 2001 From: guerler Date: Fri, 9 Feb 2024 15:33:06 -0500 Subject: [PATCH 1/7] Switch custom published histories component to grid list --- .../Grid/configs/historiesPublished.ts | 112 ++++++++++++++++++ client/src/entry/analysis/router.js | 10 +- 2 files changed, 116 insertions(+), 6 deletions(-) create mode 100644 client/src/components/Grid/configs/historiesPublished.ts diff --git a/client/src/components/Grid/configs/historiesPublished.ts b/client/src/components/Grid/configs/historiesPublished.ts new file mode 100644 index 000000000000..6b9926179a04 --- /dev/null +++ b/client/src/components/Grid/configs/historiesPublished.ts @@ -0,0 +1,112 @@ +import { faEye } from "@fortawesome/free-solid-svg-icons"; +import { useEventBus } from "@vueuse/core"; + +import { historiesFetcher } from "@/api/histories"; +import Filtering, { contains, expandNameTag, type ValidFilter } from "@/utils/filtering"; +import _l from "@/utils/localization"; + +import type { FieldArray, GridConfig } from "./types"; + +const { emit } = useEventBus("grid-router-push"); + +/** + * Local types + */ +type HistoryEntry = Record; +type SortKeyLiteral = "name" | "update_time" | undefined; + +/** + * Request and return data from server + */ +async function getData(offset: number, limit: number, search: string, sort_by: string, sort_desc: boolean) { + const { data, headers } = await historiesFetcher({ + limit, + offset, + search, + sort_by: sort_by as SortKeyLiteral, + sort_desc, + show_own: false, + show_published: true, + show_shared: false, + }); + const totalMatches = parseInt(headers.get("total_matches") ?? "0"); + return [data, totalMatches]; +} + +/** + * Declare columns to be displayed + */ +const fields: FieldArray = [ + { + key: "name", + title: "Name", + type: "operations", + operations: [ + { + title: "View", + icon: faEye, + condition: (data: HistoryEntry) => !data.deleted, + handler: (data: HistoryEntry) => { + emit(`/histories/view?id=${data.id}`); + }, + }, + ], + }, + { + key: "annotation", + title: "Annotation", + type: "text", + }, + { + key: "id", + title: "Size", + type: "datasets", + }, + { + key: "tags", + title: "Tags", + type: "tags", + disabled: true, + }, + { + key: "update_time", + title: "Updated", + type: "date", + }, + { + key: "username", + title: "Username", + type: "text", + }, +]; + +/** + * Declare filter options + */ +const validFilters: Record> = { + name: { placeholder: "name", type: String, handler: contains("name"), menuItem: true }, + user: { placeholder: "user", type: String, handler: contains("username"), menuItem: true }, + tag: { + placeholder: "tag(s)", + type: "MultiTags", + handler: contains("tag", "tag", expandNameTag), + menuItem: true, + }, +}; + +/** + * Grid configuration + */ +const gridConfig: GridConfig = { + id: "histories-published-grid", + fields: fields, + filtering: new Filtering(validFilters, undefined, false, false), + getData: getData, + plural: "Histories", + sortBy: "name", + sortDesc: true, + sortKeys: ["name", "update_time", "username"], + title: "Published Histories", +}; + +export default gridConfig; diff --git a/client/src/entry/analysis/router.js b/client/src/entry/analysis/router.js index ce3a76b4386c..e8312f3814c0 100644 --- a/client/src/entry/analysis/router.js +++ b/client/src/entry/analysis/router.js @@ -8,13 +8,13 @@ import DatasetDetails from "components/DatasetInformation/DatasetDetails"; import DatasetError from "components/DatasetInformation/DatasetError"; import FormGeneric from "components/Form/FormGeneric"; import historiesGridConfig from "components/Grid/configs/histories"; +import historiesPublishedGridConfig from "components/Grid/configs/historiesPublished"; import historiesSharedGridConfig from "components/Grid/configs/historiesShared"; import visualizationsGridConfig from "components/Grid/configs/visualizations"; import visualizationsPublishedGridConfig from "components/Grid/configs/visualizationsPublished"; import GridList from "components/Grid/GridList"; import HistoryExportTasks from "components/History/Export/HistoryExport"; import HistoryPublished from "components/History/HistoryPublished"; -import HistoryPublishedList from "components/History/HistoryPublishedList"; import HistoryView from "components/History/HistoryView"; import HistoryMultipleView from "components/History/Multiple/MultipleView"; import { HistoryExport } from "components/HistoryExport/index"; @@ -282,11 +282,9 @@ export function getRouter(Galaxy) { }, { path: "histories/list_published", - component: HistoryPublishedList, - props: (route) => { - return { - ...route.query, - }; + component: GridList, + props: { + gridConfig: historiesPublishedGridConfig, }, }, { From 4f81d06449ad5f639c149f95186fbf5a07ec6a70 Mon Sep 17 00:00:00 2001 From: guerler Date: Sun, 11 Feb 2024 08:36:53 -0500 Subject: [PATCH 2/7] Sort published histories initially by update time --- client/src/components/Grid/configs/historiesPublished.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/Grid/configs/historiesPublished.ts b/client/src/components/Grid/configs/historiesPublished.ts index 6b9926179a04..1c5163c5f2a2 100644 --- a/client/src/components/Grid/configs/historiesPublished.ts +++ b/client/src/components/Grid/configs/historiesPublished.ts @@ -103,7 +103,7 @@ const gridConfig: GridConfig = { filtering: new Filtering(validFilters, undefined, false, false), getData: getData, plural: "Histories", - sortBy: "name", + sortBy: "update_time", sortDesc: true, sortKeys: ["name", "update_time", "username"], title: "Published Histories", From cb6b5b1251949c66246d6a2a28098dc3139e2beb Mon Sep 17 00:00:00 2001 From: guerler Date: Tue, 13 Feb 2024 18:01:43 -0500 Subject: [PATCH 3/7] Adjust published history selenium test cases --- client/src/utils/navigation/navigation.yml | 15 ++-- lib/galaxy/selenium/navigates_galaxy.py | 8 ++- .../selenium/test_histories_published.py | 69 ++++++++----------- 3 files changed, 39 insertions(+), 53 deletions(-) diff --git a/client/src/utils/navigation/navigation.yml b/client/src/utils/navigation/navigation.yml index 0f9c630d188f..70af6d07d63f 100644 --- a/client/src/utils/navigation/navigation.yml +++ b/client/src/utils/navigation/navigation.yml @@ -397,14 +397,13 @@ multi_history_panel: published_histories: selectors: - histories: '#published-histories-table tbody tr' - search_input: '#published-histories input.search-query[data-description="filter text input"]' - advanced_search_toggle: '#published-histories [data-description="toggle advanced search"]' - advanced_search_name_input: '#published-histories-advanced-filter-name' - advanced_search_tag_input: '#published-histories-advanced-filter-tag' - advanced_search_submit: '#published-histories-advanced-filter-submit' - tag_content: '#published-histories-table tbody tr .tag span' - column_header: '#published-histories-table thead tr th:nth-child(${column_number})' + histories: '#grid-histories-published tbody tr' + advanced_search_name_input: '#histories-advanced-filter-name' + advanced_search_tag_area: '#histories-advanced-filter-tag .stateless-tags button' + advanced_search_tag_input: '#histories-advanced-filter-tag .stateless-tags input' + advanced_search_toggle: '#histories-published-grid [data-description="toggle advanced search"]' + advanced_search_submit: '#histories-advanced-filter-submit' + search_input: '#histories-published-grid input.search-query[data-description="filter text input"]' shared_histories: selectors: diff --git a/lib/galaxy/selenium/navigates_galaxy.py b/lib/galaxy/selenium/navigates_galaxy.py index eb3d0a3e8731..b16bd8e9ba71 100644 --- a/lib/galaxy/selenium/navigates_galaxy.py +++ b/lib/galaxy/selenium/navigates_galaxy.py @@ -564,9 +564,11 @@ def select_grid_operation(self, item_name, option_label): def select_grid_cell(self, grid_name, item_name, column_index=3): cell = None - grid = self.wait_for_selector(grid_name) + grid = self.wait_for_selector(f"{grid_name} table") for row in grid.find_elements(By.TAG_NAME, "tr"): td = row.find_elements(By.TAG_NAME, "td") + print(td[0].text) + print(td[1].text) if item_name in [td[0].text, td[1].text]: cell = td[column_index] break @@ -1733,10 +1735,10 @@ def histories_click_advanced_search(self): self.wait_for_and_click_selector(search_selector) @retry_during_transitions - def histories_get_history_names(self): + def histories_get_history_names(self, selector="#histories-grid"): self.sleep_for(self.wait_types.UX_RENDER) names = [] - grid = self.wait_for_selector("#histories-grid") + grid = self.wait_for_selector(selector) for row in grid.find_elements(By.TAG_NAME, "tr"): td = row.find_elements(By.TAG_NAME, "td") name = td[1].text if td[0].text == "" else td[0].text diff --git a/lib/galaxy_test/selenium/test_histories_published.py b/lib/galaxy_test/selenium/test_histories_published.py index aa609a7880e8..0adb3e594400 100644 --- a/lib/galaxy_test/selenium/test_histories_published.py +++ b/lib/galaxy_test/selenium/test_histories_published.py @@ -19,8 +19,9 @@ def test_published_histories(self): def test_published_histories_sort_by_name(self): self._login() self.navigate_to_published_histories() - self.sleep_for(self.wait_types.UX_RENDER) - self.components.published_histories.column_header(column_number=1).wait_for_and_click() + + self.wait_for_and_click_selector('[data-description="grid sort key name"]') + sorted_histories = self.get_published_history_names_from_server(sort_by="name") self.assert_histories_present(sorted_histories, sort_by_matters=True) @@ -28,8 +29,9 @@ def test_published_histories_sort_by_name(self): def test_published_histories_sort_by_last_update(self): self._login() self.navigate_to_published_histories() - self.sleep_for(self.wait_types.UX_RENDER) - self.components.published_histories.column_header(column_number=5).wait_for_and_click() + + self.wait_for_and_click_selector('[data-description="grid sort key update_time"]') + expected_history_names = self.get_published_history_names_from_server(sort_by="update_time") self.assert_histories_present(expected_history_names, sort_by_matters=True) @@ -37,17 +39,12 @@ def test_published_histories_sort_by_last_update(self): def test_published_histories_tag_click(self): self._login() self.navigate_to_published_histories() - self.sleep_for(self.wait_types.UX_RENDER) - present_histories = self.get_present_histories() - clicked = False - for row in present_histories: - his = row.find_elements(By.TAG_NAME, "td")[0] - if self.history3_name in his.text: - row.find_elements(By.TAG_NAME, "td")[3].find_elements(By.CSS_SELECTOR, ".tag")[0].click() - clicked = True - break - - assert clicked + + # Search by tag + tags_cell = self.select_grid_cell("#histories-published-grid", self.history3_name) + tag = tags_cell.find_element(By.CSS_SELECTOR, ".tag") + tag.click() + text = self.components.published_histories.search_input.wait_for_value() if text == "": raise AssertionError("Failed to update search filter on tag click") @@ -55,33 +52,17 @@ def test_published_histories_tag_click(self): self.assert_histories_present([self.history3_name, self.history1_name]) @selenium_test - def test_published_histories_username_click(self): + def test_published_histories_username_filter(self): self._login() self.navigate_to_published_histories() - self.sleep_for(self.wait_types.UX_RENDER) - present_histories = self.get_present_histories() - clicked = False - for row in present_histories: - his = row.find_elements(By.TAG_NAME, "td")[0] - if self.history2_name in his.text: - row.find_elements(By.TAG_NAME, "td")[2].find_elements( - By.CSS_SELECTOR, ".published-histories-username-link" - )[0].click() - clicked = True - break - - assert clicked - text = self.components.published_histories.search_input.wait_for_value() - if "test2" not in text: - raise AssertionError("Failed to update search filter with username on username click") - + username = self.user2_email.split("@")[0] + self.components.published_histories.search_input.wait_for_and_send_keys(f"user:{username}") self.assert_histories_present([self.history2_name]) @selenium_test def test_published_histories_search_standard(self): self._login() self.navigate_to_published_histories() - self.sleep_for(self.wait_types.UX_RENDER) self.components.published_histories.search_input.wait_for_and_send_keys(self.history1_name) self.assert_histories_present([self.history1_name]) @@ -89,24 +70,28 @@ def test_published_histories_search_standard(self): def test_published_histories_search_advanced(self): self._login() self.navigate_to_published_histories() - self.sleep_for(self.wait_types.UX_RENDER) self.components.published_histories.advanced_search_toggle.wait_for_and_click() - # search by tag and name - self.components.published_histories.advanced_search_tag_input.wait_for_and_send_keys(self.history3_tags) self.components.published_histories.advanced_search_name_input.wait_for_and_send_keys(self.history3_name) + + self.components.published_histories.advanced_search_tag_area.wait_for_and_click() + input_element = self.components.published_histories.advanced_search_tag_input.wait_for_visible() + input_element.send_keys(self.history3_tags[0]) + self.send_enter(input_element) + self.send_escape(input_element) + self.sleep_for(self.wait_types.UX_RENDER) + self.components.published_histories.advanced_search_submit.wait_for_and_click() self.assert_histories_present([self.history3_name]) @retry_assertion_during_transitions def assert_histories_present(self, expected_histories, sort_by_matters=False): - present_histories = self.get_present_histories() + present_histories = self.histories_get_history_names(selector="#histories-published-grid") assert len(present_histories) == len(expected_histories) - for index, row in enumerate(present_histories): - cell = row.find_elements(By.TAG_NAME, "td")[0] + for index, history_name in enumerate(present_histories): if not sort_by_matters: - assert cell.text in expected_histories + assert history_name in expected_histories else: - assert cell.text == expected_histories[index] + assert history_name == expected_histories[index] def get_published_history_names_from_server(self, sort_by=None): published_histories = self.dataset_populator._get("histories/published").json() From 0a404983a7f462cf1c25dafbdff3b7d678ec85db Mon Sep 17 00:00:00 2001 From: guerler Date: Tue, 13 Feb 2024 18:02:09 -0500 Subject: [PATCH 4/7] Avoid duplicate entries in history index api response --- lib/galaxy/managers/histories.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/galaxy/managers/histories.py b/lib/galaxy/managers/histories.py index 6b1e3d256d9e..e704c3569815 100644 --- a/lib/galaxy/managers/histories.py +++ b/lib/galaxy/managers/histories.py @@ -207,6 +207,8 @@ def p_tag_filter(term_text: str, quoted: bool): self.model_class.deleted == (true() if show_deleted else false()) ) + stmt = stmt.distinct() + if include_total_count: total_matches = get_count(trans.sa_session, stmt) else: From cfdd3f3c156f4da16e95924a25650871dbc40e4d Mon Sep 17 00:00:00 2001 From: Ahmed Awan Date: Thu, 15 Feb 2024 12:24:14 -0600 Subject: [PATCH 5/7] change `GridList` header style to full width heading and filter --- client/src/components/Grid/GridList.vue | 57 +++++++++++++------------ 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/client/src/components/Grid/GridList.vue b/client/src/components/Grid/GridList.vue index 11ba2e2387be..a8a8bae298f3 100644 --- a/client/src/components/Grid/GridList.vue +++ b/client/src/components/Grid/GridList.vue @@ -15,6 +15,7 @@ import GridLink from "./GridElements/GridLink.vue"; import GridOperations from "./GridElements/GridOperations.vue"; import GridText from "./GridElements/GridText.vue"; import FilterMenu from "@/components/Common/FilterMenu.vue"; +import Heading from "@/components/Common/Heading.vue"; import SharingIndicators from "@/components/Indices/SharingIndicators.vue"; import LoadingSpan from "@/components/LoadingSpan.vue"; import StatelessTags from "@/components/TagsMultiselect/StatelessTags.vue"; @@ -235,35 +236,35 @@ watch(operationMessage, () => {
{{ errorMessage }} {{ operationMessage }} -
-
-

- {{ gridConfig.title }} -

- -
-
-
- - - {{ action.title }} - +
+
+ + {{ gridConfig.title }} + +
+ + + {{ action.title }} + +
+ +
From e35bf2b284bf2ae4ab1ca3dccbfdfb27e2993bb3 Mon Sep 17 00:00:00 2001 From: guerler Date: Thu, 15 Feb 2024 14:58:16 -0500 Subject: [PATCH 6/7] Remove unused custom history published list --- .../History/HistoryPublishedList.vue | 280 ------------------ 1 file changed, 280 deletions(-) delete mode 100644 client/src/components/History/HistoryPublishedList.vue diff --git a/client/src/components/History/HistoryPublishedList.vue b/client/src/components/History/HistoryPublishedList.vue deleted file mode 100644 index 3a98f71557a3..000000000000 --- a/client/src/components/History/HistoryPublishedList.vue +++ /dev/null @@ -1,280 +0,0 @@ - - - - - From 36b0f40fcc85b54882587cc288c6eaa97db3740f Mon Sep 17 00:00:00 2001 From: guerler Date: Thu, 15 Feb 2024 15:06:25 -0500 Subject: [PATCH 7/7] Add watcher to history dataset counter --- client/src/components/Grid/GridElements/GridDatasets.vue | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/src/components/Grid/GridElements/GridDatasets.vue b/client/src/components/Grid/GridElements/GridDatasets.vue index 005f5d96ce99..7a3092dcfca3 100644 --- a/client/src/components/Grid/GridElements/GridDatasets.vue +++ b/client/src/components/Grid/GridElements/GridDatasets.vue @@ -1,6 +1,6 @@