From d98e374153805424fb57d300ce11f0e72b63616a Mon Sep 17 00:00:00 2001 From: JulianForeman <71847719+JulianForeman@users.noreply.github.com> Date: Wed, 23 Oct 2024 10:45:54 -0700 Subject: [PATCH] Added new critical error type (#401) --- django/api/services/spreadsheet_uploader.py | 42 ++++++++++----- frontend/src/uploads/UploadContainer.js | 45 +++++++++++++--- .../src/uploads/components/UploadIssues.js | 21 +++++++- .../uploads/components/UploadIssuesDetail.js | 51 ++++++++++++++++--- 4 files changed, 132 insertions(+), 27 deletions(-) diff --git a/django/api/services/spreadsheet_uploader.py b/django/api/services/spreadsheet_uploader.py index cbbd32ef..e6dd2285 100644 --- a/django/api/services/spreadsheet_uploader.py +++ b/django/api/services/spreadsheet_uploader.py @@ -33,8 +33,7 @@ def extract_data(excel_file, sheet_name, header_row): df = trim_all_columns(df) return df except Exception as e: - traceback.print_exc() - raise + return None def transform_data( @@ -50,9 +49,17 @@ def transform_data( df = df[[col for col in df.columns if col in required_columns]] + errors_and_warnings = {} + missing_columns = [col for col in required_columns if col not in df.columns] if (missing_columns): - raise ValueError(f"Missing columns: {', '.join(missing_columns)}") + errors_and_warnings['Headers'] = {} + errors_and_warnings['Headers']['Missing Headers'] = { + "Expected Type": "The spreadsheet provided is missing headers", + "Rows": missing_columns, + "Severity": "Critical" + } + return df, errors_and_warnings for prep_func in preparation_functions: df = prep_func(df) @@ -69,7 +76,6 @@ def transform_data( datetime: "Date (YYYY-MM-DD)" } - errors_and_warnings = {} df = df.replace({np.nan: None}) for index, row in df.iterrows(): @@ -211,17 +217,27 @@ def import_from_xls( validation_functions=[], check_for_warnings=True, ): + errors_and_warnings = {} try: df = extract_data(excel_file, sheet_name, header_row) - df, errors_and_warnings = transform_data( - df, - dataset_columns, - column_mapping_enum, - field_types, - model, - preparation_functions, - validation_functions, - ) + if df is not None: + df, errors_and_warnings = transform_data( + df, + dataset_columns, + column_mapping_enum, + field_types, + model, + preparation_functions, + validation_functions, + ) + + else: + errors_and_warnings['Spreadsheet'] = {} + errors_and_warnings['Spreadsheet']['Missing Worksheet'] = { + 'Expected Type': 'The worksheet is missing or incorrectly named', + 'Rows': [sheet_name], + 'Severity': 'Critical' + } if check_for_warnings: ## do the error checking diff --git a/frontend/src/uploads/UploadContainer.js b/frontend/src/uploads/UploadContainer.js index 53446c2e..5ed58c1c 100644 --- a/frontend/src/uploads/UploadContainer.js +++ b/frontend/src/uploads/UploadContainer.js @@ -25,6 +25,7 @@ const UploadContainer = () => { const [openDialog, setOpenDialog] = useState(false); const [adminUser, setAdminUser] = useState(false); const [totalIssueCount, setTotalIssueCount] = useState({}); + const [groupedCriticalErrors, setGroupedCriticalErrors] = useState({}); const [groupedErrors, setGroupedErrors] = useState({}); const [groupedWarnings, setGroupedWarnings] = useState({}); const [alertDialogText, setAlertDialogText] = useState({ @@ -58,25 +59,45 @@ const UploadContainer = () => { }; const groupAndCountRows = (issueArray) => { + const groupedCriticalErrors = {} const groupedErrors = {}; const groupedWarnings = {}; const totalIssueCount = { + criticalErrors: 0, errors: 0, warnings: 0, }; + issueArray.forEach((issue) => { Object.keys(issue).forEach((column) => { const errorDetails = issue[column]; + Object.keys(errorDetails).forEach((errorType) => { + const severity = errorDetails[errorType].Severity; const expectedType = errorDetails[errorType]["Expected Type"]; const groups = errorDetails[errorType].Groups || []; - - if (severity === "Error") { + + if (severity === "Critical") { const rows = errorDetails[errorType].Rows; const rowCount = rows.length; + totalIssueCount.criticalErrors += rowCount; + if (!groupedCriticalErrors[column]) { + groupedCriticalErrors[column] = {}; + } + if (!groupedCriticalErrors[column][errorType]) { + groupedCriticalErrors[column][errorType] = { + ExpectedType: expectedType, + Rows: rows, + }; + } else { + groupedCriticalErrors[column][errorType].Rows.push(...rows); + } + } else if (severity === "Error") { + const rows = errorDetails[errorType].Rows || null; + const rowCount = rows.length || groups.length; totalIssueCount.errors += rowCount; if (!groupedErrors[column]) { @@ -113,8 +134,9 @@ const UploadContainer = () => { }); }); }); + - return { groupedErrors, groupedWarnings, totalIssueCount }; + return { groupedCriticalErrors, groupedErrors, groupedWarnings, totalIssueCount }; }; const showError = (error) => { @@ -181,17 +203,27 @@ const UploadContainer = () => { }); if (Object.keys(warnings).length > 0 && checkForWarnings) { - const { groupedErrors, groupedWarnings, totalIssueCount } = + const { groupedCriticalErrors, groupedErrors, groupedWarnings, totalIssueCount } = groupAndCountRows(Object.values(warnings)); + + setGroupedCriticalErrors(groupedCriticalErrors) setGroupedErrors(groupedErrors); setGroupedWarnings(groupedWarnings); setTotalIssueCount(totalIssueCount); setAlertDialogText({ title: - "Your file has been processed and contains the following errors and warnings!", + totalIssueCount.criticalErrors > 0 ? "Your upload contained critical errors that must be fixed before it can be processed!" : "Your file has been processed and contains the following errors and warnings!", content: ( <> + {totalIssueCount.criticalErrors >= 1 && ( +