Skip to content

Commit

Permalink
Merge pull request #429 from AllenInstitute/feature/update-dataset-ta…
Browse files Browse the repository at this point in the history
…ble-metadata

Feature/update dataset table metadata
  • Loading branch information
aswallace authored Feb 18, 2025
2 parents d22f026 + a9141bd commit dac0cb6
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 101 deletions.
2 changes: 1 addition & 1 deletion packages/core/components/DataSourcePrompt/FilePrompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default function FilePrompt(props: Props) {
const { name, extensionGuess } = getNameAndTypeFromSourceUrl(dataSourceURL);
props.onSelectFile({
name,
type: extensionGuess as "csv" | "json" | "parquet",
type: extensionGuess,
uri: dataSourceURL,
});
}
Expand Down
11 changes: 8 additions & 3 deletions packages/core/entity/FileExplorerURL/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ export enum FileView {
LARGE_THUMBNAIL = "3",
}

export const ACCEPTED_SOURCE_TYPES = ["csv", "json", "parquet"] as const;

export interface Source {
name: string;
type?: "csv" | "json" | "parquet";
type?: typeof ACCEPTED_SOURCE_TYPES[number];
uri?: string | File;
}

Expand Down Expand Up @@ -72,8 +74,11 @@ export const DEFAULT_AICS_FMS_QUERY: FileExplorerURLComponents = {
export const getNameAndTypeFromSourceUrl = (dataSourceURL: string) => {
const uriResource = dataSourceURL.substring(dataSourceURL.lastIndexOf("/") + 1).split("?")[0];
const name = `${uriResource} (${new Date().toLocaleDateString()} ${new Date().toLocaleTimeString()})`;
let extensionGuess = uriResource.split(".").pop();
if (!(extensionGuess === "csv" || extensionGuess === "json" || extensionGuess === "parquet")) {
// Returns undefined if can't find a match
let extensionGuess = ACCEPTED_SOURCE_TYPES.find(
(validSourcetype) => validSourcetype === uriResource.split(".").pop()
);
if (!extensionGuess) {
console.warn("Assuming the source is csv since no extension was recognized");
extensionGuess = "csv";
}
Expand Down
57 changes: 24 additions & 33 deletions packages/web/src/components/DatasetDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,27 @@ import classNames from "classnames";
import { get as _get } from "lodash";
import * as React from "react";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";

import DatasetDetailsRow from "./DatasetDetailsRow";
import PublicDataset, {
DATASET_DISPLAY_FIELDS,
DatasetAnnotations,
} from "../../entity/PublicDataset";
import { interaction, selection } from "../../../../core/state";
import { interaction } from "../../../../core/state";
import {
PrimaryButton,
SecondaryButton,
TertiaryButton,
} from "../../../../core/components/Buttons";
import { getNameAndTypeFromSourceUrl, Source } from "../../../../core/entity/FileExplorerURL";

import styles from "./DatasetDetails.module.css";

export default function DatasetDetails() {
interface DatasetDetailsProps {
onLoadDataset: (datasetDetails: PublicDataset | undefined) => void;
}

export default function DatasetDetails(props: DatasetDetailsProps) {
const dispatch = useDispatch();
const navigate = useNavigate();
const isDetailsPanelVisible = useSelector(interaction.selectors.getDatasetDetailsVisibility);
const datasetDetails: PublicDataset | undefined = useSelector(
interaction.selectors.getSelectedPublicDataset
Expand All @@ -47,11 +48,24 @@ export default function DatasetDetails() {
if (datasetDetails.details.hasOwnProperty(fieldName)) {
datasetFieldValue = _get(datasetDetails.details, fieldName);
if (
(fieldName === DatasetAnnotations.RELATED_PUBLICATON.name ||
fieldName === DatasetAnnotations.DOI.name) &&
datasetDetails.details.hasOwnProperty(DatasetAnnotations.DOI.name)
)
fieldName === DatasetAnnotations.DOI.name ||
fieldName === DatasetAnnotations.RELATED_PUBLICATON.name
) {
// Start by using the DOI for both links
link = _get(datasetDetails.details, DatasetAnnotations.DOI.name);
}
if (
fieldName === DatasetAnnotations.RELATED_PUBLICATON.name &&
datasetDetails.details.hasOwnProperty(
DatasetAnnotations.RELATED_PUBLICATION_LINK.name
)
) {
// If RELATED_PUBLICATON has its own link other than the DOI, prioritize that
link = _get(
datasetDetails.details,
DatasetAnnotations.RELATED_PUBLICATION_LINK.name
);
}
} else datasetFieldValue = "--"; // Still display field, just indicate no value provided
const ret = [
...accum,
Expand All @@ -67,29 +81,6 @@ export default function DatasetDetails() {
}, [] as JSX.Element[]);
}, [datasetDetails]);

const openDatasetInApp = (source: Source) => {
navigate("/app");
dispatch(
selection.actions.addQuery({
name: `New ${source.name} Query on ${
datasetDetails?.name || "open-source dataset"
}`,
parts: { sources: [source] },
})
);
};

const loadDataset = () => {
const dataSourceURL = datasetDetails?.path;
if (!dataSourceURL) throw new Error("No path provided to dataset");
const { name, extensionGuess } = getNameAndTypeFromSourceUrl(dataSourceURL);
openDatasetInApp({
name,
type: extensionGuess as "csv" | "json" | "parquet",
uri: dataSourceURL,
});
};

const toggleDescriptionButton = (
<a className={styles.link} onClick={() => setShowLongDescription(!showLongDescription)}>
Read {showLongDescription ? "less" : "more"}
Expand Down Expand Up @@ -117,7 +108,7 @@ export default function DatasetDetails() {
iconName="Upload"
title="Load dataset"
text="LOAD DATASET"
onClick={loadDataset}
onClick={() => props.onLoadDataset(datasetDetails)}
/>
</div>
<hr></hr>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import { configureMockStore, mergeState } from "@aics/redux-utils";
import { fireEvent, render } from "@testing-library/react";
import { expect } from "chai";
import { get as _get } from "lodash";
import { get as _get, noop } from "lodash";
import * as React from "react";
import { Provider } from "react-redux";
import { spy } from "sinon";
import { createBrowserRouter, RouterProvider } from "react-router-dom";

import DatasetDetails from "../";
import PublicDataset, { DATASET_DISPLAY_FIELDS } from "../../../entity/PublicDataset";
import { makePublicDatasetMock } from "../../../entity/PublicDataset/mocks";
import { initialState, selection } from "../../../../../core/state";
import { initialState } from "../../../../../core/state";
import DatabaseServiceNoop from "../../../../../core/services/DatabaseService/DatabaseServiceNoop";

describe("<DatasetDetails />", () => {
describe("render", () => {
const mockRouter = createBrowserRouter([
{
path: "/",
element: <DatasetDetails />,
element: <DatasetDetails onLoadDataset={noop} />,
},
]);
it("renders correct dataset field names and values for a fully defined dataset", () => {
Expand Down Expand Up @@ -139,7 +140,7 @@ describe("<DatasetDetails />", () => {
const mockRouter = createBrowserRouter([
{
path: "/",
element: <DatasetDetails />,
element: <DatasetDetails onLoadDataset={noop} />,
},
]);
const mockDescriptionShort = "This is a string that has 40 characters.";
Expand Down Expand Up @@ -227,27 +228,20 @@ describe("<DatasetDetails />", () => {
});
});
describe("loadDataset", () => {
const onLoadDataset = spy();
const mockRouter = createBrowserRouter([
{
path: "/",
element: <DatasetDetails />,
},
{
path: "/app",
element: <></>,
element: <DatasetDetails onLoadDataset={onLoadDataset} />,
},
]);
it("calls dispatch", () => {
it("calls loadDataset with data", () => {
const mockDataset = makePublicDatasetMock("test-id");

// Arrange
const { store, actions } = configureMockStore({
const { store } = configureMockStore({
state: mergeState(initialState, {
interaction: {
isOnWeb: true,
platformDependentServices: {
databaseService: new DatabaseServiceNoop(),
},
selectedPublicDataset: mockDataset,
},
}),
Expand All @@ -260,18 +254,14 @@ describe("<DatasetDetails />", () => {

// consistency checks, button exists & no actions fired
expect(getByLabelText("Load dataset")).to.exist;
expect(actions.list.length).to.equal(0);
expect(onLoadDataset.called).to.equal(false);

// Act
fireEvent.click(getByLabelText("Load dataset"));

// Assert
expect(actions.list.length).to.equal(1);
expect(
actions.includesMatch({
type: selection.actions.ADD_QUERY,
})
).to.equal(true);
expect(onLoadDataset.called).to.equal(true);
expect(onLoadDataset.getCalls()[0].args).to.contain(mockDataset);
});
});
});
35 changes: 4 additions & 31 deletions packages/web/src/components/OpenSourceDatasets/DatasetRow.tsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,30 @@
import { IDetailsRowProps, IRenderFunction } from "@fluentui/react";
import classNames from "classnames";
import * as React from "react";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import { useDispatch } from "react-redux";

import PublicDataset from "../../entity/PublicDataset";
import { interaction, selection } from "../../../../core/state";
import { getNameAndTypeFromSourceUrl, Source } from "../../../../core/entity/FileExplorerURL";
import { interaction } from "../../../../core/state";
import { PrimaryButton } from "../../../../core/components/Buttons";

import styles from "./DatasetRow.module.css";

interface DatasetRowProps {
rowProps: IDetailsRowProps;
defaultRender: IRenderFunction<IDetailsRowProps>;
onLoadDataset: (dataset: PublicDataset) => void;
}

export default function DatasetRow(props: DatasetRowProps) {
const dispatch = useDispatch();
const navigate = useNavigate();
const [showActions, setShowActions] = React.useState(true);
const dataset = new PublicDataset(props.rowProps.item);
const currentGlobalURL = useSelector(selection.selectors.getEncodedFileExplorerUrl);

const selectDataset = () => {
dispatch(interaction.actions.setSelectedPublicDataset(dataset));
dispatch(interaction.actions.showDatasetDetailsPanel());
};

const openDatasetInApp = (source: Source) => {
dispatch(
selection.actions.addQuery({
name: `New ${source.name} Query on ${dataset?.name || "open-source dataset"}`,
parts: { sources: [source] },
})
);
navigate({
pathname: "/app",
search: `?${currentGlobalURL}`,
});
};

const loadDataset = () => {
const dataSourceURL = dataset?.path;
if (!dataSourceURL) throw new Error("No path provided to dataset");
const { name, extensionGuess } = getNameAndTypeFromSourceUrl(dataSourceURL);
openDatasetInApp({
name,
type: extensionGuess as "csv" | "json" | "parquet",
uri: dataSourceURL,
});
};

return (
<div
className={styles.tableRow}
Expand All @@ -75,7 +48,7 @@ export default function DatasetRow(props: DatasetRowProps) {
iconName="Upload"
title="Load dataset"
text="LOAD"
onClick={loadDataset}
onClick={() => props.onLoadDataset(dataset)}
/>
</div>
</div>
Expand Down
18 changes: 14 additions & 4 deletions packages/web/src/components/OpenSourceDatasets/DatasetTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import * as React from "react";

import DatasetRow from "./DatasetRow";
import useDatasetDetails from "./useDatasetDetails";
import {
import PublicDataset, {
PublicDatasetProps,
DATASET_TABLE_FIELDS,
DatasetAnnotations,
Expand All @@ -25,6 +25,7 @@ import styles from "./DatasetTable.module.css";

interface DatasetTableProps {
filters?: FileFilter[];
onLoadDataset: (dataset: PublicDataset) => void;
}

export default function DatasetTable(props: DatasetTableProps) {
Expand Down Expand Up @@ -53,7 +54,13 @@ export default function DatasetTable(props: DatasetTableProps) {
defaultRender: IRenderFunction<IDetailsRowProps> | undefined
): JSX.Element => {
if (rowProps && defaultRender) {
return <DatasetRow rowProps={rowProps} defaultRender={defaultRender} />;
return (
<DatasetRow
rowProps={rowProps}
defaultRender={defaultRender}
onLoadDataset={props.onLoadDataset}
/>
);
}
return <></>;
};
Expand All @@ -75,11 +82,14 @@ export default function DatasetTable(props: DatasetTableProps) {
) {
const fieldContent = item[column?.fieldName as keyof PublicDatasetProps] as string;
if (!fieldContent) return <>--</>;
if (column?.fieldName === DatasetAnnotations.RELATED_PUBLICATON.name && item?.doi) {
if (
column?.fieldName === DatasetAnnotations.RELATED_PUBLICATON.name &&
(item?.related_publication_link || item?.doi)
) {
return (
<a
className={classNames(styles.link, styles.doubleLine)}
href={item.doi}
href={item.related_publication_link || item.doi}
target="_blank"
rel="noopener noreferrer"
>
Expand Down
Loading

0 comments on commit dac0cb6

Please sign in to comment.