From 1f51665e42d240a8608f2ec7ebd20b0ea686b246 Mon Sep 17 00:00:00 2001 From: MikiVL Date: Tue, 5 May 2026 05:52:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=EF=BC=8C=E8=87=AA=E5=8A=A8=E8=AF=86=E5=88=AB?= =?UTF-8?q?=20{{=E5=8D=A0=E4=BD=8D=E7=AC=A6}}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python/parser.py | 25 +++++++++++++++++++++++- tests/create_fixtures.py | 10 ++++++++++ tests/fixtures/sample_template.xlsx | Bin 0 -> 4866 bytes tests/python/test_parser.py | 29 +++++++++++++++++++++++++++- 4 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 tests/create_fixtures.py create mode 100644 tests/fixtures/sample_template.xlsx diff --git a/python/parser.py b/python/parser.py index fdffa2a..6737d44 100644 --- a/python/parser.py +++ b/python/parser.py @@ -1 +1,24 @@ -# placeholder +import re +from openpyxl import load_workbook + +PLACEHOLDER_RE = re.compile(r"\{\{(.+?)\}\}") + +def parse_template(file_path: str) -> dict: + wb = load_workbook(file_path, data_only=False) + sheets = wb.sheetnames + placeholders = [] + + for sheet_name in sheets: + ws = wb[sheet_name] + for row in ws.iter_rows(): + for cell in row: + if cell.value and isinstance(cell.value, str): + m = PLACEHOLDER_RE.search(cell.value) + if m: + placeholders.append({ + "name": m.group(1), + "sheet": sheet_name, + "cell": cell.coordinate, + }) + + return {"sheets": sheets, "placeholders": placeholders} diff --git a/tests/create_fixtures.py b/tests/create_fixtures.py new file mode 100644 index 0000000..671fdc4 --- /dev/null +++ b/tests/create_fixtures.py @@ -0,0 +1,10 @@ +from openpyxl import Workbook + +wb = Workbook() +ws = wb.active +ws.title = "Sheet1" +ws["B3"] = "{{编号}}" +ws["C5"] = "{{姓名}}" +ws["D7"] = "normal_value" # not a placeholder, should NOT be detected +wb.save("tests/fixtures/sample_template.xlsx") +print("fixture created") diff --git a/tests/fixtures/sample_template.xlsx b/tests/fixtures/sample_template.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..c6d220a9b73c1427c6f915ed73e4c76044575590 GIT binary patch literal 4866 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 literal 0 HcmV?d00001 diff --git a/tests/python/test_parser.py b/tests/python/test_parser.py index fdffa2a..537fcfb 100644 --- a/tests/python/test_parser.py +++ b/tests/python/test_parser.py @@ -1 +1,28 @@ -# placeholder +import sys, os +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../../python")) + +from parser import parse_template + +FIXTURE = os.path.join(os.path.dirname(__file__), "../fixtures/sample_template.xlsx") + +def test_returns_sheets(): + result = parse_template(FIXTURE) + assert "sheets" in result + assert "Sheet1" in result["sheets"] + +def test_detects_placeholders(): + result = parse_template(FIXTURE) + names = [p["name"] for p in result["placeholders"]] + assert "编号" in names + assert "姓名" in names + +def test_ignores_non_placeholders(): + result = parse_template(FIXTURE) + names = [p["name"] for p in result["placeholders"]] + assert "normal_value" not in names + +def test_placeholder_has_cell_info(): + result = parse_template(FIXTURE) + biaohao = next(p for p in result["placeholders"] if p["name"] == "编号") + assert biaohao["sheet"] == "Sheet1" + assert biaohao["cell"] == "B3"