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

CRDCDH-1995 Validation Results Aggregated/Expanded Table Views #557

Merged
merged 28 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
300d5c6
Created a component to handle double label switches
Alejandro-Vega Dec 6, 2024
945f2e0
Update pagination to support additional actions before/after and top/…
Alejandro-Vega Dec 6, 2024
9238917
Update additional actions prop usage
Alejandro-Vega Dec 6, 2024
3bb12c4
Added submissionAggQCResults query fragments and types
Alejandro-Vega Dec 9, 2024
707352a
Fix null check
Alejandro-Vega Dec 9, 2024
b93c41d
Separated QC Filters to separate component, and added support for agg…
Alejandro-Vega Dec 9, 2024
2013054
Update submissionQCResults query to include issueCode param
Alejandro-Vega Dec 9, 2024
f0391ef
Implemented navigating to expanded view when expand button is clicked…
Alejandro-Vega Dec 9, 2024
f0cea1c
Create tests for QC filters
Alejandro-Vega Dec 10, 2024
eb131d1
Remove dummy data and fix tests
Alejandro-Vega Dec 10, 2024
a37a142
Merge branch '3.2.0' of https://github.com/CBIIT/crdc-datahub-ui into…
Alejandro-Vega Dec 31, 2024
5c6df35
Rename aggregated submission QC results query
Alejandro-Vega Dec 31, 2024
db45468
Update imports and fix types
Alejandro-Vega Dec 31, 2024
783e24a
Fix table pagination positioning
Alejandro-Vega Dec 31, 2024
3607600
Add tests and aria label to double label switch
Alejandro-Vega Dec 31, 2024
5dd0da0
Remove redundant variables
Alejandro-Vega Jan 2, 2025
d7b574b
Updated type from Issue to AggregatedQCResult
Alejandro-Vega Jan 3, 2025
5311349
Show correct filters based on table view
Alejandro-Vega Jan 6, 2025
78403db
Update tests based on if table is aggregated or expanded view
Alejandro-Vega Jan 6, 2025
f9ab5c1
Allow export CSV of aggregated view table
Alejandro-Vega Jan 7, 2025
247179e
Fixed test by awaiting for action
Alejandro-Vega Jan 7, 2025
4b741d6
Merge branch '3.2.0' of https://github.com/CBIIT/crdc-datahub-ui into…
Alejandro-Vega Jan 7, 2025
7bda97e
Reset filters when table view is changed, also fixed zindex issue
Alejandro-Vega Jan 15, 2025
663cb38
Merge branch '3.2.0' into CRDCDH-1995
Alejandro-Vega Jan 15, 2025
136264d
Fix tests
Alejandro-Vega Jan 16, 2025
8efdb23
Merge branch 'CRDCDH-1995' of https://github.com/CBIIT/crdc-datahub-u…
Alejandro-Vega Jan 16, 2025
8581bdb
Updated query skips and updated mock order on tests
Alejandro-Vega Jan 16, 2025
2a439bd
Merge branch '3.2.0' into CRDCDH-1995
Alejandro-Vega Jan 16, 2025
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
373 changes: 314 additions & 59 deletions src/components/DataSubmissions/ExportValidationButton.test.tsx

Large diffs are not rendered by default.

200 changes: 158 additions & 42 deletions src/components/DataSubmissions/ExportValidationButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ import { useSnackbar } from "notistack";
import dayjs from "dayjs";
import { unparse } from "papaparse";
import StyledFormTooltip from "../StyledFormComponents/StyledTooltip";
import { SUBMISSION_QC_RESULTS, SubmissionQCResultsResp } from "../../graphql";
import { downloadBlob, filterAlphaNumeric, unpackValidationSeverities } from "../../utils";
import {
AGGREGATED_SUBMISSION_QC_RESULTS,
AggregatedSubmissionQCResultsInput,
AggregatedSubmissionQCResultsResp,
SUBMISSION_QC_RESULTS,
SubmissionQCResultsResp,
} from "../../graphql";
import { downloadBlob, filterAlphaNumeric, Logger, unpackValidationSeverities } from "../../utils";

export type Props = {
/**
Expand All @@ -21,7 +27,12 @@ export type Props = {
*
* @example { "Batch ID": (d) => d.displayID }
*/
fields: Record<string, (row: QCResult) => string | number>;
fields: Record<string, (row: QCResult | AggregatedQCResult) => string | number>;
/**
* Tells the component whether to export the "aggregated" or the "expanded" data.
* @default false
*/
isAggregated?: boolean;
} & IconButtonProps;

const StyledIconButton = styled(IconButton)({
Expand All @@ -42,73 +53,178 @@ const StyledTooltip = styled(StyledFormTooltip)({
export const ExportValidationButton: React.FC<Props> = ({
submission,
fields,
isAggregated = false,
disabled,
...buttonProps
}: Props) => {
const { enqueueSnackbar } = useSnackbar();
const [loading, setLoading] = useState<boolean>(false);

const [submissionQCResults] = useLazyQuery<SubmissionQCResultsResp>(SUBMISSION_QC_RESULTS, {
const [getSubmissionQCResults] = useLazyQuery<SubmissionQCResultsResp>(SUBMISSION_QC_RESULTS, {
context: { clientName: "backend" },
fetchPolicy: "cache-and-network",
fetchPolicy: "no-cache",
});

const handleClick = async () => {
setLoading(true);
const [getAggregatedSubmissionQCResults] = useLazyQuery<
AggregatedSubmissionQCResultsResp,
AggregatedSubmissionQCResultsInput
>(AGGREGATED_SUBMISSION_QC_RESULTS, {
context: { clientName: "backend" },
fetchPolicy: "no-cache",
});

const { data: d, error } = await submissionQCResults({
variables: {
id: submission?._id,
sortDirection: "asc",
orderBy: "displayID",
first: -1,
offset: 0,
},
context: { clientName: "backend" },
fetchPolicy: "no-cache",
});

if (error || !d?.submissionQCResults?.results) {
enqueueSnackbar("Unable to retrieve submission quality control results.", {
variant: "error",
/**
* Helper to generate CSV and trigger download.
* This function:
* 1) Optionally unpacks severities if not aggregated
* 2) Uses the given `fields` to generate CSV rows
* 3) Calls `downloadBlob` to save the CSV file
*
* @returns {void}
*/
const createCSVAndDownload = (
rows: (QCResult | AggregatedQCResult)[],
filename: string,
isAggregated: boolean
): void => {
try {
let finalRows = rows;

if (!isAggregated) {
finalRows = unpackValidationSeverities<QCResult>(rows as QCResult[]);
}

const fieldEntries = Object.entries(fields);
const csvArray = finalRows.map((row) => {
const csvRow: Record<string, string | number> = {};
fieldEntries.forEach(([header, fn]) => {
csvRow[header] = fn(row) ?? "";
});
return csvRow;
});
setLoading(false);
return;

downloadBlob(unparse(csvArray), filename, "text/csv");
} catch (err) {
enqueueSnackbar(`Unable to export validation results. Error: ${err}`, { variant: "error" });
}
};

/**
* Creates a file name by using the submission name, filtering by alpha-numeric characters,
* then adding the date and time
*
* @returns {string} A formatted file name for the exported file
*/
const createFileName = (): string => {
const filteredName = filterAlphaNumeric(submission.name?.trim()?.replaceAll(" ", "-"), "-");
return `${filteredName}-${dayjs().format("YYYY-MM-DDTHHmmss")}.csv`;
};

/**
* Will retrieve all of the aggregated submission QC results to
* construct and download a CSV file
*
*
* @returns {Promise<void>}
*/
const handleAggregatedExportSetup = async (): Promise<void> => {
setLoading(true);

if (!d?.submissionQCResults?.results.length) {
enqueueSnackbar("There are no validation results to export.", {
try {
const { data, error } = await getAggregatedSubmissionQCResults({
variables: {
submissionID: submission?._id,
partial: false,
first: -1,
orderBy: "title",
sortDirection: "asc",
},
});

if (error || !data?.aggregatedSubmissionQCResults?.results) {
enqueueSnackbar("Unable to retrieve submission aggregated quality control results.", {
variant: "error",
});
return;
}

if (!data.aggregatedSubmissionQCResults.results.length) {
enqueueSnackbar("There are no aggregated validation results to export.", {
variant: "error",
});
return;
}

createCSVAndDownload(data.aggregatedSubmissionQCResults.results, createFileName(), true);
} catch (err) {
enqueueSnackbar(`Unable to export aggregated validation results. Error: ${err}`, {
variant: "error",
});
Logger.error(
`ExportValidationButton: Unable to export aggregated validation results. Error: ${err}`
);
} finally {
setLoading(false);
return;
}
};

try {
const filteredName = filterAlphaNumeric(submission.name?.trim()?.replaceAll(" ", "-"), "-");
const filename = `${filteredName}-${dayjs().format("YYYY-MM-DDTHHmmss")}.csv`;
const unpacked = unpackValidationSeverities<QCResult>(d.submissionQCResults.results);
const fieldset = Object.entries(fields);
const csvArray = [];
/**
* Will retrieve all of the expanded submission QC results to
* construct and download a CSV file
*
*
* @returns {Promise<void>}
*/
const handleExpandedExportSetup = async () => {
setLoading(true);

unpacked.forEach((row) => {
const csvRow = {};
try {
const { data, error } = await getSubmissionQCResults({
variables: {
id: submission?._id,
sortDirection: "asc",
orderBy: "displayID",
first: -1,
offset: 0,
},
});

fieldset.forEach(([field, value]) => {
csvRow[field] = value(row) || "";
if (error || !data?.submissionQCResults?.results) {
enqueueSnackbar("Unable to retrieve submission quality control results.", {
variant: "error",
});
return;
}

csvArray.push(csvRow);
});
if (!data.submissionQCResults.results.length) {
enqueueSnackbar("There are no validation results to export.", { variant: "error" });
return;
}

downloadBlob(unparse(csvArray), filename, "text/csv");
createCSVAndDownload(data.submissionQCResults.results, createFileName(), false);
} catch (err) {
enqueueSnackbar(`Unable to export validation results. Error: ${err}`, {
enqueueSnackbar(`Unable to export expanded validation results. Error: ${err}`, {
variant: "error",
});
Logger.error(
`ExportValidationButton: Unable to export expanded validation results. Error: ${err}`
);
} finally {
setLoading(false);
}
};

/**
* Click handler that triggers the setup
* for aggregated or expanded CSV file exporting
*/
const handleClick = async () => {
if (isAggregated) {
handleAggregatedExportSetup();
return;
}

setLoading(false);
handleExpandedExportSetup();
};

return (
Expand Down
Loading
Loading