Skip to content

Commit

Permalink
Merge branch 'main' into prod
Browse files Browse the repository at this point in the history
  • Loading branch information
mogul committed Jun 21, 2023
2 parents bcc9487 + f7079db commit e07ae03
Show file tree
Hide file tree
Showing 31 changed files with 829 additions and 316 deletions.
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,6 @@ staticfiles/

# pa11y_tests/screenshots/*.png

excel/

# Things that folks might want to have in their local directories
# that don't need to be checked in
.local/
Expand Down
22 changes: 18 additions & 4 deletions backend/audit/fixtures/excel.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from collections import namedtuple
from django.conf import settings

SHEETS_DIR = settings.XLSX_TEMPLATE_SHEET_DIR
Expand Down Expand Up @@ -37,7 +38,20 @@

SIMPLE_CASES_TEST_FILE = settings.AUDIT_TEST_DATA_ENTRY_DIR / "simple-cases.json"

FEDERAL_AWARDS_EXPENDED = "FederalAwardsExpended"
CORRECTIVE_ACTION_PLAN = "CorrectiveActionPlan"
FINDINGS_TEXT = "FindingsText"
FINDINGS_UNIFORM_GUIDANCE = "FindingsUniformGuidance"
# Make FORM_SECTIONS convenient to both iterate over and access by field name:
FormSections = namedtuple(
"FormSections",
(
"CORRECTIVE_ACTION_PLAN",
"FEDERAL_AWARDS_EXPENDED",
"FINDINGS_TEXT",
"FINDINGS_UNIFORM_GUIDANCE",
),
)
# Note: we turn these into hyphenated lowercase for URLs, e.g. federal-awards-expended
FORM_SECTIONS = FormSections(
CORRECTIVE_ACTION_PLAN="CorrectiveActionPlan",
FEDERAL_AWARDS_EXPENDED="FederalAwardsExpended",
FINDINGS_TEXT="FindingsText",
FINDINGS_UNIFORM_GUIDANCE="FindingsUniformGuidance",
)
17 changes: 17 additions & 0 deletions backend/audit/migrations/0024_excelfile_form_section.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.1 on 2023-06-17 00:25

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("audit", "0023_alter_access_role"),
]

operations = [
migrations.AddField(
model_name="excelfile",
name="form_section",
field=models.CharField(default="not-actually-default", max_length=255),
),
]
17 changes: 17 additions & 0 deletions backend/audit/migrations/0025_alter_excelfile_form_section.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.1 on 2023-06-17 06:27

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("audit", "0024_excelfile_form_section"),
]

operations = [
migrations.AlterField(
model_name="excelfile",
name="form_section",
field=models.CharField(max_length=255),
),
]
5 changes: 3 additions & 2 deletions backend/audit/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

from .validators import (
validate_excel_file,
validate_excel_filename,
validate_corrective_action_plan_json,
validate_federal_award_json,
validate_findings_text_json,
Expand Down Expand Up @@ -448,10 +447,12 @@ class ExcelFile(models.Model):

file = models.FileField(upload_to="excel", validators=[validate_excel_file])
filename = models.CharField(max_length=255)
form_section = models.CharField(max_length=255)
sac = models.ForeignKey(SingleAuditChecklist, on_delete=models.CASCADE)
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
date_created = models.DateTimeField(auto_now_add=True)

def save(self, *args, **kwargs):
self.filename = validate_excel_filename(self.file)
report_id = SingleAuditChecklist.objects.get(id=self.sac.id).report_id
self.filename = f"{report_id}--{self.form_section}.xlsx"
super(ExcelFile, self).save(*args, **kwargs)
5 changes: 3 additions & 2 deletions backend/audit/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ def test_filename_generated(self):
"""
file = SimpleUploadedFile("this is a file.xlsx", b"this is a file")

excel_file = baker.make(ExcelFile, file=file)
excel_file = baker.make(ExcelFile, file=file, form_section="sectionname")
report_id = SingleAuditChecklist.objects.get(id=excel_file.sac.id).report_id

self.assertEqual("this-is-a-file.xlsx", excel_file.filename)
self.assertEqual(f"{report_id}--sectionname.xlsx", excel_file.filename)
69 changes: 12 additions & 57 deletions backend/audit/test_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import string
from unittest import TestCase
from unittest.mock import patch
from django.conf import settings
from django.test import SimpleTestCase
from django.core.exceptions import ValidationError
from django.core.files.uploadedfile import TemporaryUploadedFile
Expand All @@ -11,18 +12,19 @@

import requests

from audit.fixtures.excel import SIMPLE_CASES_TEST_FILE
from audit.fixtures.excel import (
SIMPLE_CASES_TEST_FILE,
CORRECTIVE_ACTION_TEMPLATE_DEFINITION,
)

from .validators import (
ALLOWED_EXCEL_CONTENT_TYPES,
ALLOWED_EXCEL_FILE_EXTENSIONS,
ERROR_MESSAGE,
MAX_EXCEL_FILE_SIZE_MB,
validate_corrective_action_plan_json,
validate_excel_file_content_type,
validate_excel_file_extension,
validate_excel_file_integrity,
validate_excel_filename,
validate_excel_file_size,
validate_federal_award_json,
validate_file_infection,
Expand Down Expand Up @@ -429,59 +431,6 @@ def test_uei_does_not_contain_9_digit_sequence(self):
validate_uei_nine_digit_sequences(self.valid)


class ExcelFileFilenameValidatorTests(SimpleTestCase):
def test_valid_filename_slug(self):
"""
Filenames that can be slugified are valid
"""
test_cases = [
("this one just has spaces.xlsx", "this-one-just-has-spaces.xlsx"),
(
"this_one\\ has some? other things!.xlsx",
"this-one-has-some-other-things.xlsx",
),
("this/one/has/forward/slashes.xlsx", "slashes.xlsx"),
(
"this.one.has.multiple.extensions.xlsx",
"this-one-has-multiple-extensions.xlsx",
),
]

for test_case in test_cases:
with self.subTest():
before, after = test_case
valid_file = TemporaryUploadedFile(
before, ALLOWED_EXCEL_CONTENT_TYPES[0], 10000, "utf-8"
)

validated_filename = validate_excel_filename(valid_file)

self.assertEqual(validated_filename, after)

def test_invalid_filename_slug(self):
"""
Filenames that cannot be slugified are not valid
"""
test_cases = [
"no-extension",
".xlsx",
"".join(choice("!?#$%^&*") for _ in range(9)),
"".join(choice("!?#$%^&*") for _ in range(9)) + ".xlsx",
]

for test_case in test_cases:
with self.subTest():
file = TemporaryUploadedFile(
test_case, ALLOWED_EXCEL_CONTENT_TYPES[0], 10000, "utf-8"
)

with self.assertRaises(
ValidationError,
msg=f"ValidationError not raised with filename = {test_case}",
):
validate_excel_filename(file)


class ExcelFileExtensionValidatorTests(SimpleTestCase):
def test_invalid_file_extensions(self):
"""
Expand Down Expand Up @@ -692,8 +641,14 @@ def test_validation_is_applied(self):
"""
Empty Corrective Action Plan should fail, simple case should pass.
"""
template_definition_path = (
settings.XLSX_TEMPLATE_JSON_DIR / CORRECTIVE_ACTION_TEMPLATE_DEFINITION
)
template = json.loads(template_definition_path.read_text(encoding="utf-8"))
invalid = json.loads('{"CorrectiveActionPlan":{}}')
expected_msg = str(("B", "2", "Auditee UEI", ERROR_MESSAGE))
expected_msg = str(
("B", "2", "Auditee UEI", template["sheets"][0]["single_cells"][0]["help"])
)
self.assertRaisesRegex(
ValidationError, expected_msg, validate_corrective_action_plan_json, invalid
)
Expand Down
38 changes: 14 additions & 24 deletions backend/audit/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@
FINDINGS_TEXT_ENTRY_FIXTURES,
FINDINGS_UNIFORM_GUIDANCE_ENTRY_FIXTURES,
FEDERAL_AWARDS_ENTRY_FIXTURES,
FEDERAL_AWARDS_EXPENDED,
CORRECTIVE_ACTION_PLAN,
FINDINGS_TEXT,
FINDINGS_UNIFORM_GUIDANCE,
FORM_SECTIONS,
)
from .models import Access, SingleAuditChecklist
from .views import MySubmissions
Expand Down Expand Up @@ -56,13 +53,6 @@
"auditor_contacts": ["[email protected]"],
}

EXCEL_FILES = [
CORRECTIVE_ACTION_PLAN,
FEDERAL_AWARDS_EXPENDED,
FINDINGS_UNIFORM_GUIDANCE,
FINDINGS_TEXT,
]


# Mocking the user login and file scan functions
def _mock_login_and_scan(client, mock_scan_file):
Expand Down Expand Up @@ -262,7 +252,7 @@ class ExcelFileHandlerViewTests(TestCase):
def test_login_required(self):
"""When an unauthenticated request is made"""

for form_section in EXCEL_FILES:
for form_section in FORM_SECTIONS:
response = self.client.post(
reverse(
f"audit:{form_section}",
Expand All @@ -278,7 +268,7 @@ def test_bad_report_id_returns_403(self):

self.client.force_login(user)

for form_section in EXCEL_FILES:
for form_section in FORM_SECTIONS:
response = self.client.post(
reverse(
f"audit:{form_section}",
Expand All @@ -296,7 +286,7 @@ def test_inaccessible_audit_returns_403(self):
user, sac = _make_user_and_sac()

self.client.force_login(user)
for form_section in EXCEL_FILES:
for form_section in FORM_SECTIONS:
response = self.client.post(
reverse(
f"audit:{form_section}",
Expand All @@ -313,7 +303,7 @@ def test_no_file_attached_returns_400(self):

self.client.force_login(user)

for form_section in EXCEL_FILES:
for form_section in FORM_SECTIONS:
response = self.client.post(
reverse(
f"audit:{form_section}",
Expand All @@ -332,7 +322,7 @@ def test_invalid_file_upload_returns_400(self):

file = SimpleUploadedFile("not-excel.txt", b"asdf", "text/plain")

for form_section in EXCEL_FILES:
for form_section in FORM_SECTIONS:
response = self.client.post(
reverse(
f"audit:{form_section}",
Expand Down Expand Up @@ -365,10 +355,10 @@ def test_valid_file_upload_for_federal_awards(self, mock_scan_file):
with open(tmp.name, "rb") as excel_file:
response = self.client.post(
reverse(
f"audit:{EXCEL_FILES[1]}",
f"audit:{FORM_SECTIONS.FEDERAL_AWARDS_EXPENDED}",
kwargs={
"report_id": sac.report_id,
"form_section": EXCEL_FILES[1],
"form_section": FORM_SECTIONS.FEDERAL_AWARDS_EXPENDED,
},
),
data={"FILES": excel_file},
Expand Down Expand Up @@ -488,10 +478,10 @@ def test_valid_file_upload_for_corrective_action_plan(self, mock_scan_file):
with open(tmp.name, "rb") as excel_file:
response = self.client.post(
reverse(
f"audit:{EXCEL_FILES[0]}",
f"audit:{FORM_SECTIONS.CORRECTIVE_ACTION_PLAN}",
kwargs={
"report_id": sac.report_id,
"form_section": EXCEL_FILES[0],
"form_section": FORM_SECTIONS.CORRECTIVE_ACTION_PLAN,
},
),
data={"FILES": excel_file},
Expand Down Expand Up @@ -554,10 +544,10 @@ def test_valid_file_upload_for_findings_uniform_guidance(self, mock_scan_file):
with open(tmp.name, "rb") as excel_file:
response = self.client.post(
reverse(
f"audit:{EXCEL_FILES[2]}",
f"audit:{FORM_SECTIONS.FINDINGS_UNIFORM_GUIDANCE}",
kwargs={
"report_id": sac.report_id,
"form_section": EXCEL_FILES[2],
"form_section": FORM_SECTIONS.FINDINGS_UNIFORM_GUIDANCE,
},
),
data={"FILES": excel_file},
Expand Down Expand Up @@ -632,10 +622,10 @@ def test_valid_file_upload_for_findings_text(self, mock_scan_file):
with open(tmp.name, "rb") as excel_file:
response = self.client.post(
reverse(
f"audit:{EXCEL_FILES[3]}",
f"audit:{FORM_SECTIONS.FINDINGS_TEXT}",
kwargs={
"report_id": sac.report_id,
"form_section": EXCEL_FILES[3],
"form_section": FORM_SECTIONS.FINDINGS_TEXT,
},
),
data={"FILES": excel_file},
Expand Down
25 changes: 11 additions & 14 deletions backend/audit/urls.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
from django.urls import path

from .fixtures.excel import (
CORRECTIVE_ACTION_PLAN,
FEDERAL_AWARDS_EXPENDED,
FINDINGS_TEXT,
FINDINGS_UNIFORM_GUIDANCE,
)
from .fixtures.excel import FORM_SECTIONS
from . import views

app_name = "audit"
form_sections = [
CORRECTIVE_ACTION_PLAN,
FEDERAL_AWARDS_EXPENDED,
FINDINGS_TEXT,
FINDINGS_UNIFORM_GUIDANCE,
]


def camel_to_hyphen(raw: str) -> str:
"""Convert camel case to hyphen-delimited."""
text = f"{raw[0].lower()}{raw[1:]}"
return "".join(c if c.islower() else f"-{c.lower()}" for c in text)


urlpatterns = [
path("", views.MySubmissions.as_view(), name="MySubmissions"),
path("<str:report_id>", views.EditSubmission.as_view(), name="EditSubmission"),
Expand Down Expand Up @@ -50,10 +47,10 @@
),
]

for form_section in form_sections:
for form_section in FORM_SECTIONS:
urlpatterns.append(
path(
f"excel/{form_section}/<str:report_id>",
f"excel/{camel_to_hyphen(form_section)}/<str:report_id>",
views.ExcelFileHandlerView.as_view(),
name=form_section,
kwargs={"form_section": form_section},
Expand Down
Loading

0 comments on commit e07ae03

Please sign in to comment.