Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace History Dataset Picker in Library Folder #18518

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
ceb40a2
🛠️: refactor `SelectionDialog/selectionTypes.ts` to use TypeScript li…
itisAliRH Jul 9, 2024
5101d0f
✨: add `FilterMenu`, folderIcon and `okButtonText` and `searchTitle` …
itisAliRH Jul 9, 2024
504ccbb
✨: create `api/folders` with `postFolderContent` endpoint
itisAliRH Jul 9, 2024
ef6c289
🎉: create `HistoryDatasetPicker` in Library
itisAliRH Jul 9, 2024
57f43c7
🎨: use `HistoryDatasetPicker` in `FolderTopBar`
itisAliRH Jul 9, 2024
d6eba5a
✨: add and expose `resetPagination` method to `SelectDialog`
itisAliRH Jul 19, 2024
a950dd7
🐛: fix reactivity issue when selecting an item in `HistoryDatasetPicker`
itisAliRH Jul 19, 2024
2684bf7
🐛: fix filter and pagination issues in `HistoryDatasetPicker`
itisAliRH Jul 19, 2024
e951cdd
✨: add and expose `resetFilter` method to `SelectDialog`
itisAliRH Jul 24, 2024
669a695
🐛: fix filter reset issue in `HistoryDatasetPicker`
itisAliRH Jul 24, 2024
ae1169c
🛠️: update `navigation.yml` selectors for `HistoryDatasetPicker`
itisAliRH Jul 24, 2024
03f50f5
🛠️: add `libraries_dataset_import_from_history_search_for` in `naviga…
itisAliRH Jul 24, 2024
9fd716f
🛠️: update `test_library_contents` to work with `HistoryDatasetPicker`
itisAliRH Jul 24, 2024
be37798
🎨: add submitting state and success toast to `HistoryDatasetPicker`
itisAliRH Jul 24, 2024
45205f5
🛠️: update `FolderTopBar` to use `useConfig` composable
itisAliRH Jul 25, 2024
5aac94b
🐛: reset filter in HistoryDatasetPicker on onUndo
itisAliRH Jul 25, 2024
617c3b6
✨: add `total_matches` to GET "/api/datasets"
itisAliRH Jul 25, 2024
6859769
🛠️: update `getDatasets` to return `total_matches` and update the usa…
itisAliRH Jul 25, 2024
489af10
🛠️: import missing types in `HistoryDatasetPicker`
itisAliRH Jul 26, 2024
73c1f49
🔥: remove `addFilesFromHistoryModal` and related methods from `add-da…
itisAliRH Jul 29, 2024
a394944
🛠️: refactor `HistoryDatasetPicker` to use `GalaxyApi` for fetching h…
itisAliRH Sep 11, 2024
cd702b8
🔥: drop `api/folders` file
itisAliRH Sep 11, 2024
4b9c768
🛠️: refactor use const assertion for `SELECTION_STATES`
itisAliRH Sep 13, 2024
e5738fa
🛠️: use `IconDefinition` for `folderIcon` in SelectionDialog`
itisAliRH Sep 13, 2024
86c2be2
🎨: fix no items condition in `SelectionDialog`
itisAliRH Sep 13, 2024
561a635
✨: add `HistorySortByLiteral` type definition to `api/index`
itisAliRH Sep 13, 2024
afa9cf9
🛠️: refactor sorting in history grids and `HistoryDatasetPicker` to u…
itisAliRH Sep 13, 2024
bce8051
🐛: fix `datasetsProvider` sorting and filtering in `HistoryDatasetPic…
itisAliRH Sep 13, 2024
224bf2e
🛠️: refactor FontAwesome icon import in `HistoryDatasetPicker`
itisAliRH Sep 13, 2024
22fb14d
✨: adding progress bar on import datasets from history and make `His…
itisAliRH Sep 13, 2024
3125bba
✨: add customizable title and action button text to `HistoryDatasetP…
itisAliRH Sep 13, 2024
b728a49
🚚: moved `HistoryDatasetPicker` from the `libraries` to the `Selectio…
itisAliRH Sep 13, 2024
4b36af5
🎨: changing progress bar in `FolderTopBar` to be dismissible only wh…
itisAliRH Sep 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions client/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ export { type components, GalaxyApi, type GalaxyApiPaths };
*/
export type HistorySummary = components["schemas"]["HistorySummary"];

/**
* Represents the possible values for the `sort_by` parameter when querying histories.
* We can not extract this from the schema for an unknown reason.
* The desired solution would be: `GalaxyApiPaths["/api/histories"]["get"]["parameters"]["query"]["sort_by"]`.
*/
export type HistorySortByLiteral = "create_time" | "name" | "update_time" | "username" | undefined;

/**
* Contains minimal information about a History with additional content stats.
* This is a subset of information that can be relatively frequently updated after
Expand Down
6 changes: 3 additions & 3 deletions client/src/components/FilesDialog/FilesDialog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import flushPromises from "flush-promises";
import { getLocalVue } from "tests/jest/helpers";

import { useServerMock } from "@/api/client/__mocks__";
import { SELECTION_STATES, type SelectionItem } from "@/components/SelectionDialog/selectionTypes";
import { SELECTION_STATES, type SelectionItem, type SelectionState } from "@/components/SelectionDialog/selectionTypes";

/**
* The following imports mock a remote file resource directory structure,
Expand Down Expand Up @@ -57,7 +57,7 @@ jest.mock("@/composables/config", () => ({
const { server, http } = useServerMock();

interface RowElement extends SelectionItem, Element {
_rowVariant: string;
_rowVariant: SelectionState;
}

function paramsToKey(query: { target?: string | null; recursive?: string | null; writeable?: string | null }): string {
Expand Down Expand Up @@ -392,7 +392,7 @@ class Utils {
}

expectSelectAllIconStatusToBe(status: string) {
expect(this.getSelectionDialog().props("selectAllIcon")).toBe(status);
expect(this.getSelectionDialog().props("selectAllVariant")).toBe(status);
}

expectNoErrorMessage() {
Expand Down
5 changes: 3 additions & 2 deletions client/src/components/FilesDialog/FilesDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
type ItemsProviderContext,
SELECTION_STATES,
type SelectionItem,
type SelectionState,
} from "@/components/SelectionDialog/selectionTypes";
import { useConfig } from "@/composables/config";
import { useFileSources } from "@/composables/fileSources";
Expand Down Expand Up @@ -73,7 +74,7 @@ const showDetails = ref(true);
const isBusy = ref(false);
const currentDirectory = ref<SelectionItem>();
const showFTPHelper = ref(false);
const selectAllIcon = ref(SELECTION_STATES.UNSELECTED);
const selectAllIcon = ref<SelectionState>(SELECTION_STATES.UNSELECTED);
const urlTracker = ref(new UrlTracker(""));
const totalItems = ref(0);

Expand Down Expand Up @@ -415,7 +416,7 @@ onMounted(() => {
:modal-static="modalStatic"
:multiple="multiple"
:options-show="optionsShow"
:select-all-icon="selectAllIcon"
:select-all-variant="selectAllIcon"
:show-select-icon="undoShow && multiple"
:undo-show="undoShow"
@onCancel="() => (modalShow = false)"
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/Grid/configs/histories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { useEventBus } from "@vueuse/core";

import { GalaxyApi } from "@/api";
import { type HistorySortByLiteral } from "@/api";
import { updateTags } from "@/api/tags";
import { useHistoryStore } from "@/stores/historyStore";
import Filtering, { contains, equals, expandNameTag, toBool, type ValidFilter } from "@/utils/filtering";
Expand All @@ -26,7 +27,6 @@ const { emit } = useEventBus<string>("grid-router-push");
* Local types
*/
type HistoryEntry = Record<string, unknown>;
type SortKeyLiteral = "create_time" | "name" | "update_time" | undefined;

/**
* Request and return data from server
Expand All @@ -40,7 +40,7 @@ async function getData(offset: number, limit: number, search: string, sort_by: s
limit,
offset,
search,
sort_by: sort_by as SortKeyLiteral,
sort_by: sort_by as HistorySortByLiteral,
sort_desc,
show_own: true,
show_published: false,
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/Grid/configs/historiesShared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { faEye } from "@fortawesome/free-solid-svg-icons";
import { useEventBus } from "@vueuse/core";

import { GalaxyApi } from "@/api";
import { type HistorySortByLiteral } from "@/api";
import { updateTags } from "@/api/tags";
import Filtering, { contains, expandNameTag, type ValidFilter } from "@/utils/filtering";
import _l from "@/utils/localization";
Expand All @@ -15,7 +16,6 @@ const { emit } = useEventBus<string>("grid-router-push");
* Local types
*/
type HistoryEntry = Record<string, unknown>;
type SortKeyLiteral = "create_time" | "name" | "update_time" | undefined;

/**
* Request and return data from server
Expand All @@ -29,7 +29,7 @@ async function getData(offset: number, limit: number, search: string, sort_by: s
limit,
offset,
search,
sort_by: sort_by as SortKeyLiteral,
sort_by: sort_by as HistorySortByLiteral,
sort_desc,
show_own: false,
show_published: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,27 @@
import { library } from "@fortawesome/fontawesome-svg-core";
import { faBook, faCaretDown, faDownload, faHome, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BButton, BDropdown, BDropdownDivider, BDropdownGroup, BDropdownItem, BFormCheckbox } from "bootstrap-vue";
import {
BAlert,
BButton,
BDropdown,
BDropdownDivider,
BDropdownGroup,
BDropdownItem,
BFormCheckbox,
} from "bootstrap-vue";
import { storeToRefs } from "pinia";
import { computed, onMounted, ref } from "vue";
import { computed, reactive, ref } from "vue";

import { getGalaxyInstance } from "@/app";
import { GalaxyApi } from "@/api";
import { Services } from "@/components/Libraries/LibraryFolder/services";
import mod_add_datasets from "@/components/Libraries/LibraryFolder/TopToolbar/add-datasets";
import { deleteSelectedItems } from "@/components/Libraries/LibraryFolder/TopToolbar/delete-selected";
import download from "@/components/Libraries/LibraryFolder/TopToolbar/download";
import mod_import_collection from "@/components/Libraries/LibraryFolder/TopToolbar/import-to-history/import-collection";
import mod_import_dataset from "@/components/Libraries/LibraryFolder/TopToolbar/import-to-history/import-dataset";
import { type SelectionItem } from "@/components/SelectionDialog/selectionTypes";
import { useConfig } from "@/composables/config";
import { type DetailedDatatypes, useDetailedDatatypes } from "@/composables/datatypes";
import { Toast } from "@/composables/toast";
import { useDbKeyStore } from "@/stores/dbKeyStore";
Expand All @@ -21,6 +31,8 @@ import { useUserStore } from "@/stores/userStore";
import FolderDetails from "@/components/Libraries/LibraryFolder/FolderDetails/FolderDetails.vue";
import LibraryBreadcrumb from "@/components/Libraries/LibraryFolder/LibraryBreadcrumb.vue";
import SearchField from "@/components/Libraries/LibraryFolder/SearchField.vue";
import ProgressBar from "@/components/ProgressBar.vue";
import HistoryDatasetPicker from "@/components/SelectionDialog/HistoryDatasetPicker.vue";

library.add(faBook, faCaretDown, faDownload, faHome, faPlus, faTrash);

Expand Down Expand Up @@ -51,18 +63,26 @@ const emit = defineEmits<{
(e: "update:includeDeleted", value: boolean): void;
}>();

const { config, isConfigLoaded } = useConfig();

const userStore = useUserStore();
const { isAdmin } = storeToRefs(userStore);

const { datatypes } = useDetailedDatatypes();

const dbKeyStore = useDbKeyStore();

const libraryImportDir = ref(false);
const allowLibraryPathPaste = ref(false);
const modalShow = ref("");
const genomesList = ref<GenomesList>([]);
const extensionsList = ref<DetailedDatatypes[]>([]);
const userLibraryImportDirAvailable = ref(false);
const progress = ref(false);
const progressNote = ref("");
const progressStatus = reactive({
total: 0,
okCount: 0,
errorCount: 0,
runningCount: 0,
});
const auto = ref({
id: "auto",
extension: "auto",
Expand All @@ -76,18 +96,19 @@ const auto = ref({
description_url: "",
});

const Galaxy = getGalaxyInstance();

const services = new Services();

const libraryImportDir = computed(() => isConfigLoaded && config.value?.library_import_dir);
const allowLibraryPathPaste = computed(() => isConfigLoaded && config.value?.allow_library_path_paste);
const userLibraryImportDirAvailable = computed(() => isConfigLoaded && config.value?.user_library_import_dir_available);
const containsFileOrFolder = computed(() => {
return props.folderContents.find((el) => el.type === "folder" || el.type === "file");
});
const canDelete = computed(() => {
return !!(containsFileOrFolder.value && isAdmin.value);
});
const datasetManipulation = computed(() => {
return !!(containsFileOrFolder.value && Galaxy.user);
return !!(containsFileOrFolder.value && userStore.currentUser);
});
const totalRows = computed(() => {
return props.metadata?.total_rows ?? 0;
Expand Down Expand Up @@ -201,6 +222,10 @@ async function importToHistoryModal(isCollection: boolean) {
}
}

function onAddDatasets(source: string = "") {
modalShow.value = source;
}

// TODO: after replacing the selection dialog with the new component that is not using jquery
async function addDatasets(source: string) {
await fetchExtAndGenomes();
Expand Down Expand Up @@ -241,11 +266,63 @@ async function fetchExtAndGenomes() {
}
}

onMounted(async () => {
libraryImportDir.value = Galaxy.config.library_import_dir;
allowLibraryPathPaste.value = Galaxy.config.allow_library_path_paste;
userLibraryImportDirAvailable.value = Galaxy.config.user_library_import_dir_available;
});
function resetProgress() {
progressStatus.total = 0;
progressStatus.okCount = 0;
progressStatus.errorCount = 0;
progressStatus.runningCount = 0;
}

async function onAddDatasetsFromHistory(selectedDatasets: SelectionItem[]) {
resetProgress();

progress.value = true;
progressStatus.total = selectedDatasets.length;
progressNote.value = "Adding datasets to the folder";

emit("setBusy", true);

for (const dataset of selectedDatasets) {
try {
progressStatus.runningCount++;

const { error } = await GalaxyApi().POST("/api/folders/{folder_id}/contents", {
params: {
path: { folder_id: props.folderId },
},
body: {
ldda_message: null,
from_hda_id: dataset.id,
},
});

if (error) {
throw new Error(error.err_msg);
}

progressStatus.okCount++;
} catch (err) {
progressStatus.errorCount++;
} finally {
progressStatus.runningCount--;
}
}

if (progressStatus.errorCount > 0) {
progressNote.value = `Added ${progressStatus.okCount} dataset${
progressStatus.okCount > 1 ? "s" : ""
}, but failed to add ${progressStatus.errorCount} dataset${
progressStatus.errorCount > 1 ? "s" : ""
} to the folder`;
} else {
progressNote.value = `Added ${progressStatus.okCount} dataset${
progressStatus.okCount > 1 ? "s" : ""
} to the folder`;
}

emit("setBusy", false);
emit("fetchFolderContents");
}
</script>

<template>
Expand Down Expand Up @@ -285,7 +362,7 @@ onMounted(async () => {
<FontAwesomeIcon :icon="faCaretDown" />
</template>

<BDropdownItem @click="addDatasets('history')"> from History </BDropdownItem>
<BDropdownItem @click="onAddDatasets('history')"> from History </BDropdownItem>

<BDropdownItem v-if="userLibraryImportDirAvailable" @click="addDatasets('userdir')">
from User Directory
Expand Down Expand Up @@ -352,9 +429,25 @@ onMounted(async () => {
</div>
</div>

<BAlert v-model="progress" :dismissible="progressStatus.runningCount === 0" variant="info" class="mb-1">
<ProgressBar
:loading="progressStatus.runningCount > 0"
:note="progressNote"
:total="progressStatus.total"
:ok-count="progressStatus.okCount"
:error-count="progressStatus.errorCount"
:running-count="progressStatus.runningCount" />
</BAlert>

<LibraryBreadcrumb
v-if="props.metadata && props.metadata.full_path"
:full_path="props.metadata.full_path"
:current-id="props.folderId" />

<HistoryDatasetPicker
v-if="modalShow === 'history'"
:folder-id="props.folderId"
@onSelect="onAddDatasetsFromHistory"
@onClose="onAddDatasets" />
</div>
</template>
Loading
Loading