+
-
- {% if federal_awards_workbook.completed == True %}
-
Upload the Audit Findings workbook
- {% else %}
-
-
Upload the Audit Findings workbook
- {% endif %}
+
+
Upload the Findings Text workbook
Sample description text, lorem ipsum, various questionably accurate latin phrases.
@@ -189,15 +175,8 @@
{{ auditee_name | default_if_none:''
- {% if federal_awards_workbook.completed == True %}
-
Upload the Corrective Action Plan (CAP) workbook
- {% else %}
-
-
Upload the Corrective Action Plan (CAP) workbook
- {% endif %}
+
Upload the Corrective Action Plan (CAP) workbook
Sample description text, lorem ipsum, various questionably accurate latin phrases.
diff --git a/backend/audit/views.py b/backend/audit/views.py
index 505b10e54e..6a017b280c 100644
--- a/backend/audit/views.py
+++ b/backend/audit/views.py
@@ -9,6 +9,7 @@
from django.urls import reverse
from django.utils.datastructures import MultiValueDictKeyError
from django.utils.decorators import method_decorator
+from django.http import JsonResponse
from .fixtures.excel import FORM_SECTIONS
@@ -137,11 +138,17 @@ def post(self, request, *args, **kwargs):
logger.warn(f"no SingleAuditChecklist found with report ID {report_id}")
raise PermissionDenied()
except ValidationError as e:
+ # The good error, where bad rows/columns are sent back in the request.
+ # These come back as tuples:
+ # [(col1, row1, field1, link1, help-text1), (col2, row2, ...), ...]
logger.warn(f"{form_section} Excel upload failed validation: {e}")
- raise BadRequest(e)
+ return JsonResponse({"errors": list(e), "type": "error_row"}, status=400)
except MultiValueDictKeyError:
- logger.warn("no file found in request")
+ logger.warn("No file found in request")
raise BadRequest()
+ except KeyError as e:
+ logger.warn(f"Field error. Field: {e}")
+ return JsonResponse({"errors": str(e), "type": "error_field"}, status=400)
class ReadyForCertificationView(SingleAuditChecklistAccessRequiredMixin, generic.View):
diff --git a/backend/cypress/e2e/check-ueid.cy.js b/backend/cypress/e2e/check-ueid.cy.js
index e5fe535b35..795e8f1b9a 100644
--- a/backend/cypress/e2e/check-ueid.cy.js
+++ b/backend/cypress/e2e/check-ueid.cy.js
@@ -358,7 +358,7 @@ describe('Create New Audit', () => {
interception.response.body.validueid,
'Failure API Response'
);
- console.log('Response:' + interception.response.body.validueid);
+ // console.log('Response:' + interception.response.body.validueid);
});
});
@@ -377,7 +377,7 @@ describe('Create New Audit', () => {
interception.response.body.validueid,
'Succcessful API Response'
);
- console.log('Response:' + interception.response.body.validueid);
+ // console.log('Response:' + interception.response.body.validueid);
});
cy.url().should('include', '/report_submission/accessandsubmission');
});
diff --git a/backend/cypress/e2e/display-submissions.cy.js b/backend/cypress/e2e/display-submissions.cy.js
index 1f6b614279..e7cafc156c 100644
--- a/backend/cypress/e2e/display-submissions.cy.js
+++ b/backend/cypress/e2e/display-submissions.cy.js
@@ -19,7 +19,7 @@ describe('Display my audit submissions', () => {
).as('hasNoData');
cy.visit('/submissions/');
cy.wait('@hasNoData').then((interception) => {
- console.log(interception);
+ // console.log(interception);
cy.get('.usa-table-container')
.should('have.attr', 'class')
.and('contain', 'display-none');
diff --git a/backend/report_submission/templates/report_submission/upload-page.html b/backend/report_submission/templates/report_submission/upload-page.html
index bfb5ef7935..deee9bebe5 100644
--- a/backend/report_submission/templates/report_submission/upload-page.html
+++ b/backend/report_submission/templates/report_submission/upload-page.html
@@ -1,19 +1,20 @@
{% extends "base.html" %}
{% load static %}
{% block content %}
-
+
-
+
{% comment %} {% include "../sidenav.html" %} {% endcomment %}
{% if already_submitted %}
-
A file has already been uploaded for this section. A successful reupload will overwrite your previous submission.
+
+ A file has already been uploaded for this section. A successful reupload will overwrite your previous submission.
+
{% endif %}
-
-
+
+
{% include "audit-metadata.html" %}
diff --git a/backend/report_submission/views.py b/backend/report_submission/views.py
index 2aa30978c9..31f27dba4d 100644
--- a/backend/report_submission/views.py
+++ b/backend/report_submission/views.py
@@ -182,7 +182,7 @@ def post(self, request, *args, **kwargs):
SingleAuditChecklist.objects.filter(pk=sac.id).update(**update)
return redirect(
- "/report_submission/federal-awards/{}".format(report_id)
+ "/audit/submission-progress/{report_id}".format(report_id=report_id)
)
except SingleAuditChecklist.DoesNotExist:
raise PermissionDenied("You do not have access to this audit.")
diff --git a/backend/static/js/upload-page.js b/backend/static/js/upload-page.js
index 9e1f65c303..ed6621f89b 100644
--- a/backend/static/js/upload-page.js
+++ b/backend/static/js/upload-page.js
@@ -22,13 +22,13 @@ const already_submitted = document.getElementById(`already-submitted`);
Function definitions
*/
// Disable/enable "Continue" button
-function setContinueButtonDisabled(disabled) {
+function setFormDisabled(shouldDisable) {
const submitButton = document.getElementById('continue');
- submitButton.disabled = disabled;
+ submitButton.disabled = shouldDisable;
}
// Print helpful error info to page & console on unsuccessful upload
-function handleUploadErrors(error) {
+function handleErrorOnUpload(error) {
if (typeof error.text === 'function') {
error.text().then((errorMessage) => {
console.error(
@@ -36,12 +36,52 @@ function handleUploadErrors(error) {
JSON.parse(errorMessage)
);
info_box.innerHTML =
- 'Error when uploading file. See the console for more information.';
+ 'Error when uploading file. See the console for more information, or contact an administrator.';
});
} else if (error.name === 'AbortError') {
console.error(`Timeout - Response took longer than expected.\n`, error);
- info_box.innerHTML = `Timeout - Response took longer than expected.`;
- } else console.error(`Unexpected error.\n`, error);
+ info_box.innerHTML = `Timeout - Response took longer than expected. Try again later.`;
+ } else {
+ info_box.innerHTML =
+ 'Error when uploading file. Ensure you have uploaded the correct template, or contact an administrator.';
+ console.error(`Unexpected error.\n`, error);
+ }
+}
+
+function get_error_table(data) {
+ var rows_html = '';
+ var row_array = [];
+ for (let i = 0; i < data.errors.length; i++) {
+ // Convert given string-tuples into arrays:
+ // "(col, row...)" -> [col, row, ...]
+ row_array = data.errors[i];
+ row_array = JSON.parse(
+ row_array.replaceAll('(', '[').replaceAll(')', ']').replaceAll(`'`, `"`)
+ );
+
+ rows_html += `
+
+ ${row_array[0]}${row_array[1]} |
+ ${row_array[2]} |
+ ${row_array[3]['text']}. |
+
`;
+ //
Link
+ }
+ const validationTable = `
Error on validation. Check the following cells for errors, and re-upload.
+ Common errors include incorrect data types or missing information.
+
+
+
+ Cell |
+ Field |
+ Help Text |
+
+
+
+ ${rows_html}
+
+
`;
+ return validationTable;
}
// On file upload, send it off for verification.
@@ -61,37 +101,42 @@ function attachFileUploadHandler() {
info_box.hidden = false;
info_box.innerHTML = 'Validating your file...';
- const currentURL = new URL(window.location.href);
+ const current_url = new URL(window.location.href);
const report_submission_url =
- UPLOAD_URLS[currentURL.pathname.split('/')[2]];
+ UPLOAD_URLS[current_url.pathname.split('/')[2]];
if (!report_submission_url) throw 'No upload URL available.';
if (!e.target.files[0]) throw 'No file selected.';
if (e.target.files[0].name.split('.').pop() !== 'xlsx')
throw 'File type not accepted.';
- var data = new FormData();
- data.append('FILES', e.target.files[0]);
- data.append('filename', e.target.files[0].name);
- data.append('sac_id', sac_id);
+ var body = new FormData();
+ body.append('FILES', e.target.files[0]);
+ body.append('filename', e.target.files[0].name);
+ body.append('sac_id', sac_id);
fetch(`/audit/excel/${report_submission_url}/${sac_id}`, {
method: 'POST',
- body: data,
+ body: body,
})
- .then((response) => {
- if (response.status == 200) {
+ .then((res) => {
+ if (res.status == 200) {
info_box.innerHTML =
'File successfully validated! Your work has been saved.';
- setContinueButtonDisabled(false);
+ setFormDisabled(false);
} else {
- // TODO: Handle helpful validation errors here
- console.error('Error when validating excel file.\n', response);
- info_box.innerHTML =
- 'Error on validation. See the console for more information.';
+ res.json().then((data) => {
+ if (data.type === 'error_row') {
+ info_box.innerHTML = get_error_table(data);
+ } else if (data.type === 'error_field') {
+ info_box.innerHTML = `Field Error: ${res.errors}`;
+ } else {
+ throw new Error('Returned error type is missing!');
+ }
+ });
}
})
.catch((error) => {
- handleUploadErrors(error);
+ handleErrorOnUpload(error);
});
} catch (error) {
info_box.innerHTML = `Error when sending excel file.\n ${error}`;
@@ -108,9 +153,7 @@ function attachEventHandlers() {
function init() {
attachEventHandlers();
- already_submitted
- ? setContinueButtonDisabled(false)
- : setContinueButtonDisabled(true);
+ already_submitted ? setFormDisabled(false) : setFormDisabled(true);
}
init();