mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-11-02 13:12:50 +00:00
fix(salary): collapse line breaks in wrapped workbook headers (#643)
* fix(salary): collapse line breaks in wrapped workbook headers
A wrapped header cell ("Median\nsalary", "Løn-\nindeks") kept its line
break, so the converter derived a category name containing a newline:
the column no longer paired with its count column, and salary_lookup
printed the category label across two lines.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(changelog): record fix for #643
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* changelog: place the #643 entry under [Unreleased]
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Mads Lorentzen <madslorentzen_17@hotmail.com>
This commit is contained in:
3 files changed
+60
-2
No files matched your search
@@ -96,6 +96,12 @@ per-file diff commands.
|
|||||||
"newly expired" by the sweep, then re-ranked by `apply` from its fresh deadline, so the same job appeared
|
"newly expired" by the sweep, then re-ranked by `apply` from its fresh deadline, so the same job appeared
|
||||||
as both expired and shortlisted. Keys are now stripped before matching.
|
as both expired and shortlisted. Keys are now stripped before matching.
|
||||||
|
|
||||||
|
- **Salary workbook conversion handles wrapped header cells** (#643, `tools/convert_salary_excel.py`,
|
||||||
|
`tests/test_salary_wrapped_header_cli.py`) - a header cell with a line break inside it
|
||||||
|
("Median⏎salary", "Løn-⏎indeks") kept the break in the derived category name, so the column no
|
||||||
|
longer paired with its count column and `salary_lookup.py` printed the label across two lines.
|
||||||
|
Header whitespace runs now collapse to one space, so a wrapped header converts like its one-line spelling.
|
||||||
|
|
||||||
## [1.7.3] - 2026-10-19
|
## [1.7.3] - 2026-10-19
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
"""Wrapped workbook header cells must convert like their one-line spelling."""
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from tempfile import TemporaryDirectory
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
try:
|
||||||
|
import openpyxl
|
||||||
|
except ImportError:
|
||||||
|
openpyxl = None
|
||||||
|
|
||||||
|
|
||||||
|
TOOL = Path(__file__).resolve().parent.parent / "tools" / "convert_salary_excel.py"
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(openpyxl, "requires optional openpyxl (installed in CI)")
|
||||||
|
class WrappedHeaderCLI(unittest.TestCase):
|
||||||
|
def convert(self, header):
|
||||||
|
with TemporaryDirectory() as directory:
|
||||||
|
root = Path(directory)
|
||||||
|
workbook, output = root / "salary.xlsx", root / "salary_data.json"
|
||||||
|
book = openpyxl.Workbook()
|
||||||
|
try:
|
||||||
|
book.active.append(header)
|
||||||
|
book.active.append(["Acme", "Aarhus", 12, 104.5])
|
||||||
|
book.save(workbook)
|
||||||
|
finally:
|
||||||
|
book.close()
|
||||||
|
result = subprocess.run(
|
||||||
|
[sys.executable, str(TOOL), str(workbook), "--output", str(output)],
|
||||||
|
cwd=root, capture_output=True, text=True,
|
||||||
|
)
|
||||||
|
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
||||||
|
return json.loads(output.read_text(encoding="utf-8"))["companies"][0]["categories"]
|
||||||
|
|
||||||
|
def test_wrapped_headers_pair_like_their_single_line_spelling(self):
|
||||||
|
expected = {"all_employees": {"count": 12, "index": 104.5}}
|
||||||
|
self.assertEqual(self.convert(["Company", "City", "Employees", "Median salary"]), expected)
|
||||||
|
self.assertEqual(self.convert(["Company", "City", "Employees", "Median\nsalary"]), expected)
|
||||||
|
self.assertEqual(self.convert(["Firma", "By", "Antal\nmedarbejdere", "Løn-\nindeks"]), expected)
|
||||||
|
|
||||||
|
def test_wrapped_category_name_has_no_line_break(self):
|
||||||
|
categories = self.convert(["Company", "City", "Count\nengineers", "Index\nengineers"])
|
||||||
|
self.assertEqual(categories, {"engineers": {"count": 12, "index": 104.5}})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
@@ -186,10 +186,12 @@ def parse_sheet(ws, sheet_label=None):
|
|||||||
print(f"Warning: Could not find header row in sheet '{ws.title}'. Skipping.", file=sys.stderr)
|
print(f"Warning: Could not find header row in sheet '{ws.title}'. Skipping.", file=sys.stderr)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# Read headers
|
# Read headers. Wrapped header cells carry a line break ("Median\nsalary"),
|
||||||
|
# which would survive into the category name and keep the column from
|
||||||
|
# pairing with its count, so collapse every whitespace run to one space.
|
||||||
headers = []
|
headers = []
|
||||||
for cell in ws[header_row]:
|
for cell in ws[header_row]:
|
||||||
headers.append(str(cell.value).strip() if cell.value else "")
|
headers.append(" ".join(str(cell.value).split()) if cell.value else "")
|
||||||
|
|
||||||
# Find company and city columns
|
# Find company and city columns
|
||||||
company_col = None
|
company_col = None
|
||||||
|
|||||||
Reference in new issue
Block a user