Pack archive xlsx plus attachment files under 附件/, with HYPERLINK columns on each plan's first row. Co-authored-by: Cursor <cursoragent@cursor.com>
113 lines
3.5 KiB
Python
113 lines
3.5 KiB
Python
import io
|
|
import zipfile
|
|
|
|
from openpyxl import load_workbook
|
|
|
|
from app.services.pl_plan_archive_zip_export import (
|
|
sanitize_zip_segment,
|
|
build_attachment_filename,
|
|
first_row_no_by_plan_id,
|
|
build_hyperlink_formula,
|
|
parse_url_list,
|
|
collect_hazard_cert_items,
|
|
merge_plan_attachment_items,
|
|
build_archive_zip_bytes,
|
|
)
|
|
from app.utils.excel_export import ExportColumn
|
|
|
|
|
|
def test_sanitize_zip_segment():
|
|
assert sanitize_zip_segment("装货·司机/A") == "装货·司机_A"
|
|
assert sanitize_zip_segment(" a ") == "a"
|
|
|
|
|
|
def test_build_attachment_filename():
|
|
name = build_attachment_filename(1, "装货·司机", "https://cdn.example/path/a.jpg")
|
|
assert name.startswith("01_装货·司机_")
|
|
assert name.endswith(".jpg")
|
|
|
|
|
|
def test_first_row_no_by_plan_id():
|
|
rows = [
|
|
{"planId": "p1", "rowNo": 1},
|
|
{"planId": "p1", "rowNo": 2},
|
|
{"planId": "p2", "rowNo": 3},
|
|
]
|
|
assert first_row_no_by_plan_id(rows) == {"p1": 1, "p2": 3}
|
|
|
|
|
|
def test_build_hyperlink_formula():
|
|
f = build_hyperlink_formula("附件/01_司机_ab12.jpg", "司机")
|
|
assert f.startswith('=HYPERLINK(')
|
|
assert "附件/01_司机_ab12.jpg" in f
|
|
|
|
|
|
def test_parse_url_list_csv_and_json():
|
|
assert parse_url_list("a.jpg,b.jpg") == ["a.jpg", "b.jpg"]
|
|
assert parse_url_list('["x.png","y.png"]') == ["x.png", "y.png"]
|
|
|
|
|
|
def test_collect_hazard_cert_items_labels():
|
|
class P:
|
|
product_name = "甲醇"
|
|
purchase_cert_url = "https://x/p.pdf"
|
|
transport_cert_url = None
|
|
legal_use_cert_url = "https://x/l1.pdf,https://x/l2.pdf"
|
|
|
|
items = collect_hazard_cert_items([P()])
|
|
labels = [i["label"] for i in items]
|
|
assert "购买证" in labels[0]
|
|
assert any("合法使用证明" in x for x in labels)
|
|
assert len(items) == 3
|
|
|
|
|
|
def test_merge_dedupes_url():
|
|
doc = [{"url": "https://a/1.jpg", "label": "司机"}]
|
|
haz = [{"url": "https://a/1.jpg", "label": "购买证"}]
|
|
merged = merge_plan_attachment_items(doc, haz)
|
|
assert len(merged) == 1
|
|
assert merged[0]["label"] == "司机"
|
|
|
|
|
|
def test_build_archive_zip_bytes_structure_and_hyperlink():
|
|
columns = [
|
|
ExportColumn(title="序号", field="rowNo"),
|
|
ExportColumn(title="计划编号", field="planNo"),
|
|
]
|
|
rows = [
|
|
{"planId": "p1", "rowNo": 1, "planNo": "PL001"},
|
|
{"planId": "p1", "rowNo": 2, "planNo": "PL001"},
|
|
]
|
|
attachments_by_plan = {
|
|
"p1": [{"url": "https://cdn.example/a.jpg", "label": "司机"}],
|
|
}
|
|
|
|
def fake_download(url: str) -> bytes:
|
|
return b"fake-image-bytes"
|
|
|
|
stamp = "202609231230"
|
|
data, failures = build_archive_zip_bytes(
|
|
db=None,
|
|
tenant_no="t1",
|
|
columns=columns,
|
|
rows=rows,
|
|
attachments_by_plan=attachments_by_plan,
|
|
stamp=stamp,
|
|
download_fn=fake_download,
|
|
)
|
|
assert failures == []
|
|
with zipfile.ZipFile(io.BytesIO(data)) as zf:
|
|
names = zf.namelist()
|
|
assert f"计划归档({stamp})/(总表)计划归档.xlsx" in names
|
|
att = [n for n in names if n.startswith(f"计划归档({stamp})/附件/")]
|
|
assert len(att) == 1
|
|
assert "01_司机_" in att[0]
|
|
xlsx_bytes = zf.read(f"计划归档({stamp})/(总表)计划归档.xlsx")
|
|
wb = load_workbook(io.BytesIO(xlsx_bytes))
|
|
ws = wb.active
|
|
headers = [c.value for c in ws[1]]
|
|
assert "附件_1" in headers
|
|
attach_col = headers.index("附件_1") + 1
|
|
assert str(ws.cell(2, attach_col).value).startswith("=HYPERLINK(")
|
|
assert ws.cell(3, attach_col).value in (None, "")
|