diff --git a/backend/audit/models/constants.py b/backend/audit/models/constants.py
index 3842d7993..844421b6c 100644
--- a/backend/audit/models/constants.py
+++ b/backend/audit/models/constants.py
@@ -51,6 +51,11 @@ class RESUBMISSION_STATUS:
UNKNOWN = "unknown_resubmission_status"
+class RESUBMISSION_TAGS:
+ MOST_RECENT = "MOST RECENT"
+ DEPRECATED = "RESUBMITTED"
+
+
RESUBMISSION_STATUS_CHOICES = (
(RESUBMISSION_STATUS.MOST_RECENT, "Most Recent"),
(RESUBMISSION_STATUS.DEPRECATED, "Deprecated via Resubmission"),
diff --git a/backend/dissemination/searchlib/search_resub_tags.py b/backend/dissemination/searchlib/search_resub_tags.py
index 525000350..f743e0557 100644
--- a/backend/dissemination/searchlib/search_resub_tags.py
+++ b/backend/dissemination/searchlib/search_resub_tags.py
@@ -1,7 +1,4 @@
-from typing import Iterable, Mapping, Optional, Tuple
-from collections.abc import MutableMapping
-
-from dissemination.models import General
+from audit.models.constants import RESUBMISSION_STATUS, RESUBMISSION_TAGS
def _safe_int(v) -> int:
@@ -11,64 +8,23 @@ def _safe_int(v) -> int:
return 0
-def build_resub_tag_map(rows: Iterable[General]) -> Mapping[str, Optional[str]]:
+def add_resub_tag_data(rows):
"""
- report_id -> tag
-
- Since everything is marked MOST_RECENT in the DB, we treat "is part of a resub chain"
- as: resubmission_version > 1.
-
- Per (auditee_uei, audit_year):
- - the row with the highest version => "Most Recent"
- - all other rows with version > 1 => "Resubmitted"
- - version <= 1 (or missing) => no tag
+ Adds resubmission data to given rows
+ Only tag if it's deprecated OR if it's the most recent amongst resubmissions
"""
-
- # (uei, year) -> best score (version, accepted_date, report_id) for stable tie-breaking
- best_score_by_group: dict[Tuple[str, str], tuple] = {}
- winner_report_id_by_group: dict[Tuple[str, str], str] = {}
-
- # 1) find the winner per group among rows where version > 1
- for row in rows:
- v = _safe_int(getattr(row, "resubmission_version", None))
- if v <= 1:
- continue
-
- key = (row.auditee_uei, row.audit_year)
-
- acc = getattr(row, "fac_accepted_date", None)
- # if date is None, keep it low so real dates win ties
- score = (v, acc or 0, row.report_id)
-
- if key not in best_score_by_group or score > best_score_by_group[key]:
- best_score_by_group[key] = score
- winner_report_id_by_group[key] = row.report_id
-
- # 2) assign tags
- tag_map: dict[str, Optional[str]] = {}
-
- for row in rows:
- v = _safe_int(getattr(row, "resubmission_version", None))
- if v < 1:
- tag_map[row.report_id] = None
- continue
-
- key = (row.auditee_uei, row.audit_year)
- if winner_report_id_by_group.get(key) == row.report_id:
- tag_map[row.report_id] = "Most Recent"
- else:
- tag_map[row.report_id] = "Resubmitted"
-
- return tag_map
-
-
-def attach_resubmission_tags(
- rows: Iterable[General], tag_map: Mapping[str, Optional[str]]
-) -> None:
-
for row in rows:
- tag = tag_map.get(row.report_id)
- if isinstance(row, MutableMapping):
- row["resubmission_tag"] = tag
- else:
- setattr(row, "resubmission_tag", tag)
+ v = _safe_int(getattr(row, "resubmission_version", 0))
+ resub_status = getattr(row, "resubmission_status", RESUBMISSION_STATUS.UNKNOWN)
+ tag = None
+ color = None
+
+ if resub_status == RESUBMISSION_STATUS.DEPRECATED:
+ tag = f"V{v} ({RESUBMISSION_TAGS.DEPRECATED})"
+ color = "bg-red"
+ elif v > 1 and resub_status == RESUBMISSION_STATUS.MOST_RECENT:
+ tag = f"V{v} ({RESUBMISSION_TAGS.MOST_RECENT})"
+ color = "bg-green"
+
+ setattr(row, "resubmission_tag", tag)
+ setattr(row, "tag_color", color)
diff --git a/backend/dissemination/templates/search.html b/backend/dissemination/templates/search.html
index 9da04745b..8a3c07084 100644
--- a/backend/dissemination/templates/search.html
+++ b/backend/dissemination/templates/search.html
@@ -161,10 +161,8 @@
Audit Submissions Basic Search
|
{{ result.auditee_name }}
- {% if result.resubmission_tag == "Most Recent" %}
- MOST RECENT
- {% elif result.resubmission_tag == "Resubmitted" %}
- RESUBMITTED
+ {% if result.resubmission_tag %}
+ {{ result.resubmission_tag }}
{% endif %}
|
{% comment %} Display UEI. If it's "GSA_MIGRATION", use the EIN instead. If no EIN, just show "GSA_MIGRATION". {% endcomment %}
diff --git a/backend/dissemination/test_search_resub_tags.py b/backend/dissemination/test_search_resub_tags.py
index 3cc781b09..d39bb1b3d 100644
--- a/backend/dissemination/test_search_resub_tags.py
+++ b/backend/dissemination/test_search_resub_tags.py
@@ -1,116 +1,120 @@
-from datetime import date
-
from django.test import TestCase
+from model_bakery import baker
+
from dissemination.models import General
from dissemination.searchlib.search_resub_tags import (
- build_resub_tag_map,
- attach_resubmission_tags,
+ add_resub_tag_data,
)
-from model_bakery import baker
+from audit.models.constants import RESUBMISSION_STATUS, RESUBMISSION_TAGS
class ResubmissionTagTests(TestCase):
- def test_tag_most_recent_single_row_version_gt_1(self):
- row = baker.make(
- General,
- report_id="1001",
- auditee_uei="UEI1",
- audit_year="2022",
- resubmission_version=2,
- )
- tag_map = build_resub_tag_map([row])
- self.assertEqual(tag_map["1001"], "Most Recent")
-
- def test_highest_version_wins_rest_resubmitted(self):
- row_v2 = baker.make(
- General,
- report_id="1002",
- auditee_uei="UEI2",
- audit_year="2022",
- resubmission_version=2,
- )
- row_v3 = baker.make(
- General,
- report_id="1003",
- auditee_uei="UEI2",
- audit_year="2022",
- resubmission_version=3,
- )
+ def test_v0_no_tag(self):
+ """v0 audits don't get a tag"""
+ rows = [
+ baker.make(
+ General,
+ report_id="1001",
+ auditee_uei="UEI1",
+ audit_year="2022",
+ resubmission_status=RESUBMISSION_STATUS.MOST_RECENT,
+ resubmission_version=0,
+ )
+ ]
+ add_resub_tag_data(rows)
- tag_map = build_resub_tag_map([row_v2, row_v3])
- self.assertEqual(tag_map["1003"], "Most Recent")
- self.assertEqual(tag_map["1002"], "Resubmitted")
-
- def test_version_lt_1_should_not_tag(self):
- row_v1 = baker.make(
- General,
- report_id="1004",
- auditee_uei="UEI3",
- audit_year="2022",
- resubmission_version=1,
- )
- row_v0 = baker.make(
- General,
- report_id="1005",
- auditee_uei="UEI3",
- audit_year="2023",
- resubmission_version=0, # use 0 instead of None (field is NOT NULL)
- )
+ self.assertEqual(rows[0].resubmission_tag, None)
- tag_map = build_resub_tag_map([row_v1, row_v0])
- self.assertIsNotNone(tag_map["1004"])
- self.assertIsNone(tag_map["1005"])
-
- def test_tie_breaker_fac_accepted_date_then_report_id(self):
- # same version -> later fac_accepted_date should win
- row_earlier = baker.make(
- General,
- report_id="2001",
- auditee_uei="UEI4",
- audit_year="2022",
- resubmission_version=2,
- fac_accepted_date=date(2025, 1, 1),
- )
- row_later = baker.make(
- General,
- report_id="2002",
- auditee_uei="UEI4",
- audit_year="2022",
- resubmission_version=2,
- fac_accepted_date=date(2025, 2, 1),
- )
+ def test_v1_no_tag(self):
+ """v1 most_recent audits don't get a tag"""
- tag_map = build_resub_tag_map([row_earlier, row_later])
- self.assertEqual(tag_map["2002"], "Most Recent")
- self.assertEqual(tag_map["2001"], "Resubmitted")
-
- def test_attach_resubmission_tags(self):
- row1 = baker.make(
- General,
- report_id="3001",
- auditee_uei="UEI5",
- audit_year="2022",
- resubmission_version=2,
- )
- row2 = baker.make(
- General,
- report_id="3002",
- auditee_uei="UEI5",
- audit_year="2022",
- resubmission_version=1,
+ rows = [
+ baker.make(
+ General,
+ report_id="1001",
+ auditee_uei="UEI1",
+ audit_year="2022",
+ resubmission_status=RESUBMISSION_STATUS.MOST_RECENT,
+ resubmission_version=1,
+ )
+ ]
+ add_resub_tag_data(rows)
+
+ self.assertEqual(rows[0].resubmission_tag, None)
+
+ def test_v2_most_recent(self):
+ """v2 most_recent audits do get a tag"""
+
+ rows = [
+ baker.make(
+ General,
+ report_id="1001",
+ auditee_uei="UEI1",
+ audit_year="2022",
+ resubmission_status=RESUBMISSION_STATUS.MOST_RECENT,
+ resubmission_version=2,
+ )
+ ]
+ add_resub_tag_data(rows)
+
+ self.assertEqual(
+ rows[0].resubmission_tag, f"V2 ({RESUBMISSION_TAGS.MOST_RECENT})"
)
- row3 = baker.make(
- General,
- report_id="3003",
- auditee_uei="UEI5",
- audit_year="2022",
- resubmission_version=0, # no tag (unknown version number)
+
+ def test_v1_resub(self):
+ """v1 deprecated audits do get a tag"""
+
+ rows = [
+ baker.make(
+ General,
+ report_id="1001",
+ auditee_uei="UEI1",
+ audit_year="2022",
+ resubmission_status=RESUBMISSION_STATUS.DEPRECATED,
+ resubmission_version=1,
+ )
+ ]
+ add_resub_tag_data(rows)
+
+ self.assertEqual(
+ rows[0].resubmission_tag, f"V1 ({RESUBMISSION_TAGS.DEPRECATED})"
)
- rows = [row1, row2, row3]
- tag_map = build_resub_tag_map(rows)
- attach_resubmission_tags(rows, tag_map)
+ def test_mixed(self):
+ """Simple case of audits that get different tags"""
+
+ rows = [
+ baker.make(
+ General,
+ report_id="1000",
+ auditee_uei="UEI1",
+ audit_year="2022",
+ resubmission_status=RESUBMISSION_STATUS.MOST_RECENT,
+ resubmission_version=1,
+ ),
+ baker.make(
+ General,
+ report_id="1001",
+ auditee_uei="UEI1",
+ audit_year="2022",
+ resubmission_status=RESUBMISSION_STATUS.DEPRECATED,
+ resubmission_version=1,
+ ),
+ baker.make(
+ General,
+ report_id="1002",
+ auditee_uei="UEI1",
+ audit_year="2022",
+ resubmission_status=RESUBMISSION_STATUS.MOST_RECENT,
+ resubmission_version=2,
+ ),
+ ]
+ add_resub_tag_data(rows)
- self.assertEqual(row1.resubmission_tag, "Most Recent")
- self.assertEqual(row2.resubmission_tag, "Resubmitted")
- self.assertIsNone(row3.resubmission_tag)
+ self.assertEqual(rows[0].resubmission_tag, None)
+ self.assertEqual(
+ rows[1].resubmission_tag, f"V1 ({RESUBMISSION_TAGS.DEPRECATED})"
+ )
+ self.assertEqual(
+ rows[2].resubmission_tag, f"V2 ({RESUBMISSION_TAGS.MOST_RECENT})"
+ )
diff --git a/backend/dissemination/views/search.py b/backend/dissemination/views/search.py
index 41f597dc1..93937f12b 100644
--- a/backend/dissemination/views/search.py
+++ b/backend/dissemination/views/search.py
@@ -20,8 +20,7 @@
run_search,
)
from dissemination.searchlib.search_resub_tags import (
- build_resub_tag_map,
- attach_resubmission_tags,
+ add_resub_tag_data,
)
from dissemination.views.utils import include_private_results
from support.decorators import newrelic_timing_metric
@@ -131,16 +130,12 @@ def post(self, request, *args, **kwargs):
if form_data.get("end_date"):
form_user_input["end_date"] = form_data["end_date"].strftime("%Y-%m-%d")
- # If there are results, populate the agency name in cog/over field
- if results_count > 0:
- paginator_results = populate_cog_over_name(paginator_results)
- resub_tag_map = build_resub_tag_map(paginator_results.object_list)
- else:
- resub_tag_map = {}
+ # Populate the agency name in cog/over field
+ paginator_results = populate_cog_over_name(paginator_results)
# Attach tag to each result so the template can use result.resubmission_tag
if include_private_results(request):
- attach_resubmission_tags(paginator_results.object_list, resub_tag_map)
+ add_resub_tag_data(paginator_results.object_list)
context = context | {
"form_user_input": form_user_input,
@@ -151,7 +146,6 @@ def post(self, request, *args, **kwargs):
"page": page,
"results_count": results_count,
"results": paginator_results,
- "resub_tag_map": resub_tag_map,
}
time_beginning_render = time.time()
total_time_ms = int(
@@ -256,15 +250,11 @@ def post(self, request, *args, **kwargs):
form_user_input["end_date"] = form_data["end_date"].strftime("%Y-%m-%d")
# If there are results, populate the agency name in cog/over field
- if results_count > 0:
- paginator_results = populate_cog_over_name(paginator_results)
- resub_tag_map = build_resub_tag_map(paginator_results.object_list)
- else:
- resub_tag_map = {}
+ paginator_results = populate_cog_over_name(paginator_results)
# Attach tag to each result so the template can use result.resubmission_tag
if include_private_results(request):
- attach_resubmission_tags(paginator_results.object_list, resub_tag_map)
+ add_resub_tag_data(paginator_results.object_list)
context = context | {
"form_user_input": form_user_input,
@@ -275,7 +265,6 @@ def post(self, request, *args, **kwargs):
"page": page,
"results_count": results_count,
"results": paginator_results,
- "resub_tag_map": resub_tag_map,
}
time_beginning_render = time.time()