92 lines
4.3 KiB
Python
92 lines
4.3 KiB
Python
import csv
|
|
import importlib.util
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
from xml.sax.saxutils import escape
|
|
from zipfile import ZIP_DEFLATED, ZipFile
|
|
|
|
|
|
MODULE_PATH = Path(__file__).with_name("export_thirdparty_catalog_xlsx.py")
|
|
SPEC = importlib.util.spec_from_file_location("thirdparty_export", MODULE_PATH)
|
|
assert SPEC and SPEC.loader
|
|
catalog = importlib.util.module_from_spec(SPEC)
|
|
sys.modules[SPEC.name] = catalog
|
|
SPEC.loader.exec_module(catalog)
|
|
|
|
|
|
HEADERS = [
|
|
"Parent SKU", "产品标题", "sku", "变种属性名称一", "变种属性名称二",
|
|
"变种属性值一", "变种属性值二", "主图(URL)地址", "来源URL", "店铺名",
|
|
"产品id", "更新时间",
|
|
]
|
|
|
|
|
|
def inline_cell(column, row, value):
|
|
return f'<c r="{column}{row}" t="inlineStr"><is><t>{escape(value)}</t></is></c>'
|
|
|
|
|
|
def make_xlsx(path, data_rows):
|
|
rows = []
|
|
for row_number, values in enumerate([HEADERS, *data_rows], start=1):
|
|
cells = "".join(inline_cell(chr(65 + index), row_number, value) for index, value in enumerate(values))
|
|
rows.append(f'<row r="{row_number}">{cells}</row>')
|
|
worksheet = (
|
|
'<?xml version="1.0" encoding="UTF-8"?>'
|
|
'<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">'
|
|
'<dimension ref="A1"/><sheetData>' + "".join(rows) + '</sheetData></worksheet>'
|
|
)
|
|
with ZipFile(path, "w", ZIP_DEFLATED) as archive:
|
|
archive.writestr("xl/worksheets/sheet1.xml", worksheet)
|
|
|
|
|
|
class ThirdPartyCatalogExportTest(unittest.TestCase):
|
|
def make_source(self, rows):
|
|
temp = tempfile.TemporaryDirectory()
|
|
root = Path(temp.name) / "source"
|
|
source = root / "pinduoduo 域名" / "sample.xlsx"
|
|
source.parent.mkdir(parents=True)
|
|
make_xlsx(source, rows)
|
|
self.addCleanup(temp.cleanup)
|
|
return root, source, Path(temp.name) / "output"
|
|
|
|
def test_错误_dimension_仍导出完整数据和规范_pdd_url(self):
|
|
root, source, output = self.make_source([
|
|
["MAIN", "标题", "SKU-A", "颜色分类", "尺码", "黑色", "M", "https://img.example/a.jpg", "https://mobile.pinduoduo.com/goods.html?goods_id=123456789&track=1", "店铺 A", "24948397488", "2026-08-19"],
|
|
["MAIN", "标题", "SKU-B", "尺碼", "顏色", "L", "白色", "https://img.example/a.jpg", "https://mobile.yangkeduo.com/goods.html?goods_id=987654321", "店铺 A", "24948397488", "2026-08-19"],
|
|
])
|
|
result = catalog.export_file(source, root, output)
|
|
self.assertEqual((result.rows, result.valid_pdd_rows), (2, 2))
|
|
with result.output.open(encoding="utf-8-sig", newline="") as handle:
|
|
exported = list(csv.DictReader(handle))
|
|
self.assertEqual(len(exported), 2)
|
|
self.assertEqual(exported[0]["pdd_goods_url"], "https://mobile.yangkeduo.com/goods.html?goods_id=123456789")
|
|
self.assertEqual((exported[0]["color"], exported[0]["size"], exported[0]["parse_ok"]), ("黑色", "M", "true"))
|
|
self.assertEqual((exported[1]["color"], exported[1]["size"]), ("白色", "L"))
|
|
self.assertEqual(exported[0]["source_file"], "pinduoduo 域名/sample.xlsx")
|
|
|
|
def test_无效_pdd_url_保留蝦皮字段但不创建关联(self):
|
|
root, source, output = self.make_source([
|
|
["MAIN", "标题", "SKU-A", "款式", "套餐", "甲", "乙", "https://img.example/a.jpg", "https://example.com/item", "店铺 A", "24948397488", ""],
|
|
])
|
|
result = catalog.export_file(source, root, output)
|
|
self.assertEqual((result.rows, result.valid_pdd_rows), (1, 0))
|
|
with result.output.open(encoding="utf-8-sig", newline="") as handle:
|
|
exported = next(csv.DictReader(handle))
|
|
self.assertEqual(exported["shopee_goods_id"], "24948397488")
|
|
self.assertEqual(exported["spec_raw"], "甲,乙")
|
|
self.assertEqual(exported["parse_ok"], "false")
|
|
self.assertEqual(exported["association_pdd_goods_id"], "")
|
|
self.assertEqual(exported["validation_code"], "PDD_URL_HOST_INVALID")
|
|
|
|
def test_输出目录不能位于输入目录内(self):
|
|
root, _, _ = self.make_source([])
|
|
args = catalog.build_parser().parse_args([str(root), "--output-dir", str(root / "csv")])
|
|
with self.assertRaisesRegex(catalog.CatalogExportError, "输出目录"):
|
|
catalog.run(args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|