init: AI培训与智能巡检系统

This commit is contained in:
selfrelease
2026-06-16 00:55:20 +08:00
commit c55598494b
201 changed files with 53131 additions and 0 deletions
View File
Binary file not shown.
Binary file not shown.
+109
View File
@@ -0,0 +1,109 @@
"""清洗规则单元测试 — 对应 T-1.2 UT。"""
import os
import sys
import unittest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from app.etl import clean as cl
class TestCleanCell(unittest.TestCase):
def test_strip_newlines_and_spaces(self):
self.assertEqual(cl.clean_cell("\n\n\n"), "东风")
self.assertEqual(cl.clean_cell(" HXD1 型 "), "HXD1 型")
def test_none(self):
self.assertEqual(cl.clean_cell(None), "")
def test_is_empty(self):
for v in ["", "-", "——", "", "/", None, ""]:
self.assertTrue(cl.is_empty(v), v)
self.assertFalse(cl.is_empty("现役"))
class TestParseValueUnit(unittest.TestCase):
def test_plain_number(self):
v, u, _ = cl.parse_value_unit("126.0", "t")
self.assertEqual(v, 126.0)
self.assertEqual(u, "t")
def test_with_unit_in_text(self):
v, u, _ = cl.parse_value_unit("400km/h(试验)")
self.assertEqual(v, 400.0)
self.assertEqual(u, "km/h")
def test_speed_with_default_unit(self):
v, u, _ = cl.parse_value_unit("160km/h", "km/h")
self.assertEqual(v, 160.0)
self.assertEqual(u, "km/h")
def test_axle_load_with_paren(self):
v, u, _ = cl.parse_value_unit("23(25)", "t")
self.assertEqual(v, 23.0)
self.assertEqual(u, "t")
def test_composite_takes_first_number(self):
v, u, _ = cl.parse_value_unit("2×92(100)", "t")
self.assertEqual(v, 2.0) # 取第一个数值,原文保真在 raw_json
def test_empty_markers(self):
for raw in ["——", "-", "", "/"]:
v, u, txt = cl.parse_value_unit(raw, "t")
self.assertIsNone(v)
self.assertEqual(u, "")
def test_no_number(self):
v, u, txt = cl.parse_value_unit("交-直-交传动", "")
self.assertIsNone(v)
self.assertEqual(txt, "交-直-交传动")
class TestParseYear(unittest.TestCase):
def test_year_with_char(self):
self.assertEqual(cl.parse_year("1971 年"), 1971)
def test_year_datetime(self):
self.assertEqual(cl.parse_year("2007-12-22 00:00:00"), 2007)
def test_year_plain(self):
self.assertEqual(cl.parse_year("2006"), 2006)
def test_year_dashes(self):
self.assertIsNone(cl.parse_year("——"))
self.assertIsNone(cl.parse_year(""))
class TestNormalizeStatus(unittest.TestCase):
def test_mapping(self):
self.assertEqual(cl.normalize_status("半封存"), "半封存")
self.assertEqual(cl.normalize_status("封存"), "封存")
self.assertEqual(cl.normalize_status("已淘汰"), "退役")
self.assertEqual(cl.normalize_status("样车"), "试验")
self.assertEqual(cl.normalize_status(""), "未知")
class TestInferCountryType(unittest.TestCase):
def test_default_domestic(self):
self.assertEqual(cl.infer_country_type("大连机车车辆厂"), "国产")
def test_import(self):
self.assertEqual(cl.infer_country_type("苏联引进"), "引进仿制")
self.assertEqual(cl.infer_country_type("日本制造"), "进口")
def test_joint(self):
self.assertEqual(cl.infer_country_type("中外合资生产"), "中外合资")
class TestForwardFill(unittest.TestCase):
def test_fill(self):
self.assertEqual(
cl.forward_fill(["东风", "", "", "韶山", ""]),
["东风", "东风", "东风", "韶山", "韶山"])
def test_leading_empty(self):
self.assertEqual(cl.forward_fill(["", "A"]), ["", "A"])
if __name__ == "__main__":
unittest.main()
+44
View File
@@ -0,0 +1,44 @@
"""字段字典/映射单元测试 — 对应 T-1.1 UT。"""
import os
import sys
import unittest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from app.etl import field_dict as fd
class TestHeaderMapping(unittest.TestCase):
def test_basic(self):
self.assertEqual(fd.map_header("型号"), "model_code")
self.assertEqual(fd.map_header("车号"), "car_number")
self.assertEqual(fd.map_header("系列"), "series")
def test_with_spaces_and_newlines(self):
self.assertEqual(fd.map_header("车体长度 /mm"), "length")
self.assertEqual(fd.map_header("牵引力 (起动)/kN"), "tractive_start")
self.assertEqual(fd.map_header("最高运营时速"), "max_speed")
def test_unknown(self):
self.assertIsNone(fd.map_header("不存在的列"))
def test_normalize(self):
self.assertEqual(fd.normalize_header("车体长度 /mm"), "车体长度/mm")
self.assertEqual(fd.normalize_header("\n"), "东风")
class TestConfig(unittest.TestCase):
def test_all_12_sheets_configured(self):
self.assertEqual(len(fd.CATEGORY_CONFIG), 12)
def test_grain_values(self):
for cfg in fd.CATEGORY_CONFIG.values():
self.assertIn(cfg["grain"], ("model", "unit"))
def test_numeric_fields_have_synonyms(self):
for field in fd.NUMERIC_UNIT_FIELDS:
self.assertIn(field, fd.FIELD_SYNONYMS, field)
if __name__ == "__main__":
unittest.main()
+64
View File
@@ -0,0 +1,64 @@
"""导入管线集成/幂等性测试 — 对应 T-1.2 UT/E2E。"""
import os
import sqlite3
import sys
import tempfile
import unittest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from app.etl import importer
class TestHeaderDetection(unittest.TestCase):
def test_find_header_row_skips_title(self):
rows = [["全国电力机车型号表"], [], ["型号", "首产时间", "停产时间", "生产商"]]
self.assertEqual(importer.find_header_row(rows), 2)
def test_build_column_map(self):
header = ["系列", "型号", "首产时间", "未知列X"]
cmap = importer.build_column_map(header)
self.assertEqual(cmap[0], "series")
self.assertEqual(cmap[1], "model_code")
self.assertEqual(cmap[2], "first_year")
self.assertTrue(cmap[3].startswith("raw::"))
class TestFullImport(unittest.TestCase):
"""跑真实 12 表导入,断言结果与幂等性。"""
def setUp(self):
self.tmp = tempfile.mkdtemp()
self.db = os.path.join(self.tmp, "t.db")
def test_import_and_idempotent(self):
if not os.path.isdir(importer.CSV_DIR):
self.skipTest("CSV 目录不存在")
r1 = importer.import_all(db_path=self.db)
self.assertGreater(r1["models"], 0)
self.assertGreater(r1["units"], 0)
conn = sqlite3.connect(self.db)
n1 = conn.execute("SELECT COUNT(*) FROM model").fetchone()[0]
conn.close()
# 再次导入:应重建而非翻倍(幂等)
importer.import_all(db_path=self.db)
conn = sqlite3.connect(self.db)
n2 = conn.execute("SELECT COUNT(*) FROM model").fetchone()[0]
conn.close()
self.assertEqual(n1, n2)
def test_numeric_split_persisted(self):
if not os.path.isdir(importer.CSV_DIR):
self.skipTest("CSV 目录不存在")
importer.import_all(db_path=self.db)
conn = sqlite3.connect(self.db)
row = conn.execute(
"SELECT max_speed_value, max_speed_unit FROM model "
"WHERE max_speed_value IS NOT NULL LIMIT 1").fetchone()
conn.close()
self.assertIsNotNone(row)
self.assertIsInstance(row[0], float)
if __name__ == "__main__":
unittest.main()