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

Add section completion info to submission progress page #2910

Merged
merged 3 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 15 additions & 0 deletions backend/audit/cross_validation/naming.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from types import new_class
from typing import NamedTuple

from audit.models.submission_event import SubmissionEvent

# We need a canonical source of the different versions of each name.


Expand All @@ -17,6 +19,7 @@ class SectionBabelFish(NamedTuple):
snake_case: str # Mostly used for the field names in SingleAuditChecklist.
url_tail: str | None # Hyphenated version of snake_case, mostly.
workbook_number: int | None # Our upload ordering of workbooks.
submission_event: str # The event type we log to the SubmissionEvents table when this section is updated


SECTION_NAMES = {
Expand All @@ -29,6 +32,7 @@ class SectionBabelFish(NamedTuple):
snake_case="additional_eins",
url_tail="additional-eins",
workbook_number=8,
submission_event=SubmissionEvent.EventType.ADDITIONAL_EINS_UPDATED,
),
"additional_ueis": SectionBabelFish(
all_caps="ADDITIONAL_UEIS",
Expand All @@ -39,6 +43,7 @@ class SectionBabelFish(NamedTuple):
snake_case="additional_ueis",
url_tail="additional-ueis",
workbook_number=6,
submission_event=SubmissionEvent.EventType.ADDITIONAL_UEIS_UPDATED,
),
"audit_information": SectionBabelFish(
all_caps="AUDIT_INFORMATION",
Expand All @@ -49,6 +54,7 @@ class SectionBabelFish(NamedTuple):
snake_case="audit_information",
url_tail="audit-information",
workbook_number=None,
submission_event=SubmissionEvent.EventType.AUDIT_INFORMATION_UPDATED,
),
"corrective_action_plan": SectionBabelFish(
all_caps="CORRECTIVE_ACTION_PLAN",
Expand All @@ -59,6 +65,7 @@ class SectionBabelFish(NamedTuple):
reverse_url="report_submission:CAP",
url_tail="cap",
workbook_number=5,
submission_event=SubmissionEvent.EventType.CORRECTIVE_ACTION_PLAN_UPDATED,
),
"federal_awards": SectionBabelFish(
all_caps="FEDERAL_AWARDS",
Expand All @@ -69,6 +76,7 @@ class SectionBabelFish(NamedTuple):
snake_case="federal_awards",
url_tail="federal-awards",
workbook_number=1,
submission_event=SubmissionEvent.EventType.FEDERAL_AWARDS_UPDATED,
),
"findings_text": SectionBabelFish(
all_caps="FINDINGS_TEXT",
Expand All @@ -79,6 +87,7 @@ class SectionBabelFish(NamedTuple):
snake_case="findings_text",
url_tail="audit-findings-text",
workbook_number=4,
submission_event=SubmissionEvent.EventType.FEDERAL_AWARDS_AUDIT_FINDINGS_TEXT_UPDATED,
),
"findings_uniform_guidance": SectionBabelFish(
all_caps="FINDINGS_UNIFORM_GUIDANCE",
Expand All @@ -89,6 +98,7 @@ class SectionBabelFish(NamedTuple):
snake_case="findings_uniform_guidance",
url_tail="audit-findings",
workbook_number=3,
submission_event=SubmissionEvent.EventType.FINDINGS_UNIFORM_GUIDANCE_UPDATED,
),
"general_information": SectionBabelFish(
all_caps="GENERAL_INFORMATION",
Expand All @@ -99,6 +109,7 @@ class SectionBabelFish(NamedTuple):
snake_case="general_information",
url_tail="general-information",
workbook_number=None,
submission_event=SubmissionEvent.EventType.GENERAL_INFORMATION_UPDATED,
),
"notes_to_sefa": SectionBabelFish(
all_caps="NOTES_TO_SEFA",
Expand All @@ -109,6 +120,7 @@ class SectionBabelFish(NamedTuple):
snake_case="notes_to_sefa",
url_tail="notes-to-sefa",
workbook_number=2,
submission_event=SubmissionEvent.EventType.NOTES_TO_SEFA_UPDATED,
),
"single_audit_report": SectionBabelFish(
all_caps="SINGLE_AUDIT_REPORT",
Expand All @@ -119,6 +131,7 @@ class SectionBabelFish(NamedTuple):
snake_case="single_audit_report",
url_tail="upload-report",
workbook_number=None,
submission_event=SubmissionEvent.EventType.AUDIT_REPORT_PDF_UPDATED,
),
"secondary_auditors": SectionBabelFish(
all_caps="SECONDARY_AUDITORS",
Expand All @@ -129,6 +142,7 @@ class SectionBabelFish(NamedTuple):
snake_case="secondary_auditors",
url_tail="secondary-auditors",
workbook_number=7,
submission_event=SubmissionEvent.EventType.SECONDARY_AUDITORS_UPDATED,
),
"tribal_data_consent": SectionBabelFish(
all_caps="TRIBAL_DATA_CONSENT",
Expand All @@ -139,6 +153,7 @@ class SectionBabelFish(NamedTuple):
snake_case="tribal_data_consent",
url_tail=None,
workbook_number=None,
submission_event=SubmissionEvent.EventType.TRIBAL_CONSENT_UPDATED,
),
}

Expand Down
48 changes: 42 additions & 6 deletions backend/audit/cross_validation/submission_progress_check.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from audit.cross_validation.naming import NC, find_section_by_name
from audit.models.submission_event import SubmissionEvent
from audit.validators import validate_general_information_complete_json
from django.core.exceptions import ValidationError

Expand Down Expand Up @@ -44,7 +45,7 @@ def submission_progress_check(sac, sar=None, crossval=True):
result = {k: None for k in sac["sf_sac_sections"]}

for key in sac["sf_sac_sections"]:
result = result | progress_check(sac["sf_sac_sections"], key)
result = result | progress_check(sac, sac["sf_sac_sections"], key)

incomplete_sections = []
for k in result:
Expand All @@ -66,7 +67,7 @@ def submission_progress_check(sac, sar=None, crossval=True):
]


def progress_check(sections, key):
def progress_check(sac, sections, key):
"""
Given the content of sf_sac_sections from sac_validation_shape (plus a
single_audit_report key) and a key, determine whether that key is required, and
Expand Down Expand Up @@ -111,20 +112,45 @@ def get_num_findings(award):

# The General Information has its own condition, as it can be partially completed.
if key == "general_information":
return general_information_progress_check(progress, general_info)
return general_information_progress_check(progress, general_info, sac)

# If it's not required, it's inactive:
if not conditions[key]:
return {key: progress | {"display": "inactive"}}

# If it is required, it should be present
if sections.get(key):
return {key: progress | {"display": "complete", "completed": True}}
completed_by, completed_date = section_completed_metadata(sac, key)

return {
key: progress
| {
"display": "complete",
"completed": True,
"completed_by": completed_by,
"completed_date": completed_date,
}
}

return {key: progress | {"display": "incomplete", "completed": False}}


def general_information_progress_check(progress, general_info):
def section_completed_metadata(sac, section_key):
try:
section = find_section_by_name(section_key)
event_type = section.submission_event

report_id = sac["sf_sac_meta"]["report_id"]
event = SubmissionEvent.objects.filter(
sac__report_id=report_id, event=event_type
).latest("timestamp")

return event.user.email, event.timestamp
except SubmissionEvent.DoesNotExist:
return None, None


def general_information_progress_check(progress, general_info, sac):
"""
Given a base "progress" dictionary and the general_info object from a submission,
run validations to determine its completeness. Then, return a dictionary with
Expand All @@ -138,8 +164,18 @@ def general_information_progress_check(progress, general_info):
is_general_info_complete = False

if is_general_info_complete:
completed_by, completed_date = section_completed_metadata(
sac, "general_information"
)

return {
"general_information": progress | {"display": "complete", "completed": True}
"general_information": progress
| {
"display": "complete",
"completed": True,
"completed_by": completed_by,
"completed_date": completed_date,
}
}
return {
"general_information": progress | {"display": "incomplete", "completed": False}
Expand Down
64 changes: 63 additions & 1 deletion backend/audit/test_submission_progress_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
submission_progress_check,
)
from audit.cross_validation.naming import SECTION_NAMES, find_section_by_name
from .models import Access, SingleAuditChecklist
from .models import Access, SingleAuditChecklist, SubmissionEvent
from .test_views import _load_json

import datetime


AUDIT_JSON_FIXTURES = Path(__file__).parent / "fixtures" / "json"
User = get_user_model()
Expand Down Expand Up @@ -105,11 +107,71 @@ def test_submission_progress_check_simple_pass(self):
addl_sections["federal_awards"] = {"FederalAwards": {"federal_awards": []}}
addl_sections["general_information"] = info
del addl_sections["single_audit_report"]
user = baker.make(User, email="[email protected]")
sac = baker.make(SingleAuditChecklist, **addl_sections)

baker.make(
SubmissionEvent,
sac=sac,
user=user,
event=SubmissionEvent.EventType.GENERAL_INFORMATION_UPDATED,
)
baker.make(
SubmissionEvent,
sac=sac,
user=user,
event=SubmissionEvent.EventType.AUDIT_INFORMATION_UPDATED,
)
baker.make(
SubmissionEvent,
sac=sac,
user=user,
event=SubmissionEvent.EventType.AUDIT_REPORT_PDF_UPDATED,
)
baker.make(
SubmissionEvent,
sac=sac,
user=user,
event=SubmissionEvent.EventType.FEDERAL_AWARDS_UPDATED,
)
baker.make(
SubmissionEvent,
sac=sac,
user=user,
event=SubmissionEvent.EventType.NOTES_TO_SEFA_UPDATED,
)

shaped_sac = sac_validation_shape(sac)
result = submission_progress_check(shaped_sac, sar=True, crossval=False)

self.assertEqual(result["general_information"]["display"], "complete")
self.assertTrue(result["general_information"]["completed"])

self.assertEqual(result["audit_information"]["completed_by"], "[email protected]")
self.assertIsInstance(
result["audit_information"]["completed_date"], datetime.datetime
)

self.assertEqual(result["federal_awards"]["completed_by"], "[email protected]")
self.assertIsInstance(
result["federal_awards"]["completed_date"], datetime.datetime
)

self.assertEqual(result["general_information"]["completed_by"], "[email protected]")
self.assertIsInstance(
result["general_information"]["completed_date"], datetime.datetime
)

self.assertEqual(result["notes_to_sefa"]["completed_by"], "[email protected]")
self.assertIsInstance(
result["notes_to_sefa"]["completed_date"], datetime.datetime
)

self.assertEqual(result["single_audit_report"]["completed_by"], "[email protected]")
self.assertIsInstance(
result["single_audit_report"]["completed_date"], datetime.datetime
)

conditional_keys = (
"additional_ueis",
"additional_eins",
Expand Down