From 9e26de2c67644b697df898cf7645e672c728d573 Mon Sep 17 00:00:00 2001 From: Kushida Date: Wed, 8 Jul 2026 22:12:36 +0300 Subject: [PATCH] Fix salary tool edge cases (#75) * fix: handle salary tool edge cases * fix: preserve Danish salary compounds --- salary_lookup.py | 7 +++-- tests/test_convert_salary_excel.py | 17 +++++++++++++ tests/test_salary_lookup.py | 41 ++++++++++++++++++++++++++++++ tools/convert_salary_excel.py | 13 ++++------ 4 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 tests/test_salary_lookup.py diff --git a/salary_lookup.py b/salary_lookup.py index f01ecd3..d0cb379 100644 --- a/salary_lookup.py +++ b/salary_lookup.py @@ -204,12 +204,15 @@ def format_entry(entry, metadata): count = data.get("count") index = data.get("index") if count is not None or index is not None: - count_str = str(count) if count else "-" - if index is not None: + count_str = str(count) if count is not None else "-" + if isinstance(index, (int, float)): diff = index - baseline sign = "+" if diff >= 0 else "" index_str = f"{index:.1f}" diff_str = f"{sign}{diff:.1f}%" + elif index is not None: + index_str = str(index) + diff_str = "" else: index_str = "N/A*" diff_str = "" diff --git a/tests/test_convert_salary_excel.py b/tests/test_convert_salary_excel.py index 7abb1ca..7726f22 100644 --- a/tests/test_convert_salary_excel.py +++ b/tests/test_convert_salary_excel.py @@ -37,6 +37,13 @@ class DetectColumnTypeTests(unittest.TestCase): with self.subTest(header=header): self.assertEqual(detect_column_type(header), "count") + def test_count_inside_word_does_not_make_count_header(self): + self.assertIsNone(detect_column_type("Accounting Total")) + self.assertEqual(detect_column_type("Accounting Index"), "index") + + def test_danish_compound_headers_still_match(self): + self.assertEqual(detect_column_type("Lønindeks"), "index") + def test_parse_sheet_preserves_category_name_with_letter_n(self): ws = FakeWorksheet([ ("Company", "Engineering Count", "Engineering Index"), @@ -47,6 +54,16 @@ class DetectColumnTypeTests(unittest.TestCase): self.assertEqual(companies[0]["categories"]["engineering"], {"count": 12, "index": 105.5}) + def test_parse_sheet_groups_accounting_count_index_pair(self): + ws = FakeWorksheet([ + ("Company", "Accounting Count", "Accounting Index"), + ("Example Corp", 12, 105.5), + ]) + + companies = parse_sheet(ws) + + self.assertEqual(companies[0]["categories"]["accounting"], {"count": 12, "index": 105.5}) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_salary_lookup.py b/tests/test_salary_lookup.py new file mode 100644 index 0000000..361771c --- /dev/null +++ b/tests/test_salary_lookup.py @@ -0,0 +1,41 @@ +import unittest + +from salary_lookup import format_entry + + +class FormatEntryTests(unittest.TestCase): + def test_zero_count_is_displayed_as_zero(self): + entry = { + "company": "Example Corp", + "city": "", + "categories": { + "public_data": { + "count": 0, + "index": 100.0, + }, + }, + } + + rendered = format_entry(entry, {"index_baseline": 100, "index_label": "Index"}) + + self.assertRegex(rendered, r"Public Data\s+0\s+100\.0") + + def test_text_index_does_not_crash(self): + entry = { + "company": "Example Corp", + "city": "", + "categories": { + "sample": { + "count": 3, + "index": "private", + }, + }, + } + + rendered = format_entry(entry, {"index_baseline": 100, "index_label": "Index"}) + + self.assertIn("private", rendered) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/convert_salary_excel.py b/tools/convert_salary_excel.py index 9ef46d3..8b26ff5 100644 --- a/tools/convert_salary_excel.py +++ b/tools/convert_salary_excel.py @@ -42,6 +42,7 @@ COMPANY_PATTERNS = {"firma", "company", "virksomhed", "employer", "arbejdsgiver" CITY_PATTERNS = {"by", "city", "kommune", "location", "lokation", "sted"} COUNT_PATTERNS = {"antal", "count", "number", "n", "employees", "medarbejdere"} INDEX_PATTERNS = {"indeks", "index", "idx", "salary", "løn", "median", "average", "gennemsnit"} +DANISH_COMPOUND_PATTERNS = {"antal", "indeks", "løn", "gennemsnit", "medarbejdere"} def header_matches(header, patterns): @@ -50,10 +51,9 @@ def header_matches(header, patterns): tokens = set(re.findall(r"[a-zæøåöäü0-9]+", h)) for p in patterns: - if len(p) == 1: - if p in tokens: - return True - elif p in h: + if p in tokens: + return True + if p in DANISH_COMPOUND_PATTERNS and p in h: return True return False @@ -62,10 +62,7 @@ def strip_type_patterns(header, patterns): """Remove count/index words from a header to derive a category name.""" name = header.lower() for p in patterns: - if len(p) == 1: - name = re.sub(rf"\b{re.escape(p)}\b", "", name) - else: - name = name.replace(p, "") + name = re.sub(rf"(?