Skip to content

Commit

Permalink
Merge pull request #18518 from itisAliRH/libraries-modernization-1-hi…
Browse files Browse the repository at this point in the history
…story-dataset-picker

Replace History Dataset Picker in Library Folder
  • Loading branch information
davelopez committed Sep 16, 2024
2 parents 73c617f + 4b36af5 commit fc8dc87
Show file tree
Hide file tree
Showing 15 changed files with 572 additions and 328 deletions.
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

0 comments on commit fc8dc87

Please sign in to comment.