mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-09-17 00:26:26 +00:00
fix(salary): print the privacy footnote only when a row rendered N/A* (#475)
format_entry appended "* N/A = Too few employees to publish (privacy)" under every category table, including one where every row has an index, so the output asserted a suppression that never happened - the residual noted on #470. Set a flag in the N/A* branch and print the footnote only when it fired; a table with a suppressed row renders exactly as before. Two FormatEntryTests cases pin both directions; the "omitted" case fails on master.
This commit is contained in:
@@ -62,6 +62,13 @@ per-file diff commands.
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- **`salary_lookup.py` prints the privacy footnote only when a row actually carries `N/A*`**
|
||||||
|
- the `* N/A = Too few employees to publish (privacy)` line was appended under every
|
||||||
|
category table, including one where every row has an index, so the output asserted a
|
||||||
|
suppression that never happened (the residual noted on #470). The footnote now follows a
|
||||||
|
flag set by the `N/A*` branch; a table with a suppressed row renders exactly as before.
|
||||||
|
Two `FormatEntryTests` cases pin both directions; the "omitted" one fails on master.
|
||||||
|
|
||||||
- **`convert_salary_excel.py` pairs a bare `Count`/`Index` column pair instead of
|
- **`convert_salary_excel.py` pairs a bare `Count`/`Index` column pair instead of
|
||||||
splitting it, so `salary_lookup.py` no longer labels a published headcount as
|
splitting it, so `salary_lookup.py` no longer labels a published headcount as
|
||||||
privacy-suppressed** - the pairing loop required a non-empty derived category name on
|
privacy-suppressed** - the pairing loop required a non-empty derived category name on
|
||||||
|
|||||||
+7
-1
@@ -319,6 +319,7 @@ def format_entry(entry, metadata):
|
|||||||
lines.append(f" {'Category':<22} {'Count':>6} {index_label:>8} {'vs Baseline':>10}")
|
lines.append(f" {'Category':<22} {'Count':>6} {index_label:>8} {'vs Baseline':>10}")
|
||||||
lines.append(f" {'-'*50}")
|
lines.append(f" {'-'*50}")
|
||||||
|
|
||||||
|
suppressed = False # did any row render its index as N/A*?
|
||||||
for label, data in categories.items():
|
for label, data in categories.items():
|
||||||
display_label = label.replace("_", " ").title()
|
display_label = label.replace("_", " ").title()
|
||||||
count = data.get("count")
|
count = data.get("count")
|
||||||
@@ -339,9 +340,14 @@ def format_entry(entry, metadata):
|
|||||||
else:
|
else:
|
||||||
index_str = "N/A*"
|
index_str = "N/A*"
|
||||||
diff_str = ""
|
diff_str = ""
|
||||||
|
suppressed = True
|
||||||
lines.append(f" {display_label:<22} {count_str:>6} {index_str:>8} {diff_str:>10}")
|
lines.append(f" {display_label:<22} {count_str:>6} {index_str:>8} {diff_str:>10}")
|
||||||
|
|
||||||
lines.append(f"\n * N/A = Too few employees to publish (privacy)")
|
# The footnote explains the N/A* marker; printing it under a table with
|
||||||
|
# no such row asserts a privacy suppression that did not happen.
|
||||||
|
lines.append("")
|
||||||
|
if suppressed:
|
||||||
|
lines.append(" * N/A = Too few employees to publish (privacy)")
|
||||||
if metadata.get("baseline_description"):
|
if metadata.get("baseline_description"):
|
||||||
lines.append(f" {metadata['baseline_description']}")
|
lines.append(f" {metadata['baseline_description']}")
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -25,6 +25,39 @@ from salary_lookup import (
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
class FormatEntryTests(unittest.TestCase):
|
class FormatEntryTests(unittest.TestCase):
|
||||||
|
PRIVACY_FOOTNOTE = "* N/A = Too few employees to publish (privacy)"
|
||||||
|
|
||||||
|
def test_privacy_footnote_is_omitted_when_no_row_is_suppressed(self):
|
||||||
|
# The footnote explains the N/A* marker. Printed under a table where
|
||||||
|
# every row has an index, it asserts a privacy suppression that never
|
||||||
|
# happened (residual noted on #470).
|
||||||
|
entry = {
|
||||||
|
"company": "Example Corp",
|
||||||
|
"city": "",
|
||||||
|
"categories": {"all_employees": {"count": 500, "index": 108.5}},
|
||||||
|
}
|
||||||
|
|
||||||
|
rendered = format_entry(entry, {"index_baseline": 100, "index_label": "Index"})
|
||||||
|
|
||||||
|
self.assertNotIn("N/A", rendered)
|
||||||
|
self.assertNotIn(self.PRIVACY_FOOTNOTE, rendered)
|
||||||
|
self.assertRegex(rendered, r"All Employees\s+500\s+108\.5\s+\+8\.5%")
|
||||||
|
|
||||||
|
def test_privacy_footnote_is_printed_when_a_row_is_suppressed(self):
|
||||||
|
entry = {
|
||||||
|
"company": "Example Corp",
|
||||||
|
"city": "",
|
||||||
|
"categories": {
|
||||||
|
"all_employees": {"count": 500, "index": 108.5},
|
||||||
|
"small_team": {"count": 3, "index": None},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rendered = format_entry(entry, {"index_baseline": 100, "index_label": "Index"})
|
||||||
|
|
||||||
|
self.assertRegex(rendered, r"Small Team\s+3\s+N/A\*")
|
||||||
|
self.assertIn(self.PRIVACY_FOOTNOTE, rendered)
|
||||||
|
|
||||||
def test_zero_count_is_displayed_as_zero(self):
|
def test_zero_count_is_displayed_as_zero(self):
|
||||||
entry = {
|
entry = {
|
||||||
"company": "Example Corp",
|
"company": "Example Corp",
|
||||||
|
|||||||
Reference in New Issue
Block a user