所有需求实现完成

This commit is contained in:
Your Name
2026-02-01 15:57:42 +08:00
parent e651b92d7c
commit 6e1174a59a
16 changed files with 2354 additions and 173 deletions
+28
View File
@@ -47,6 +47,34 @@ cd backend && source .venv/bin/activate && PYTHONPATH=src python scripts/clean_d
---
## migrate_add_statistics_type.py:为 projects 表增加统计类型列
为已有 `projects` 表添加 `statistics_type` 列(若不存在)。
```bash
cd backend && PYTHONPATH=src python scripts/migrate_add_statistics_type.py
```
**migrate_statistics_type_repair.py**:将已有数据中的「检修、技改、抢修」统一改为「检修-技改-抢修」(一次性执行)。
```bash
cd backend && PYTHONPATH=src python scripts/migrate_statistics_type_repair.py
```
---
## fill_statistics_type_from_xls.py:按「统计」表填充统计类型
根据 `docs/example.xls` 的「统计」表,为已有项目填充 `statistics_type`(基建工程、业扩项目、户表、客户工程、营销项目、检修-技改-抢修、其他)。导入数据后建议执行一次。
```bash
cd backend && PYTHONPATH=src python scripts/fill_statistics_type_from_xls.py
# 或指定 xls 路径
PYTHONPATH=src python scripts/fill_statistics_type_from_xls.py /path/to/example.xls
```
---
## import_xls.py:导入 example.xls 到 projects 表
`docs/example.xls` 中的项目数据导入 ocean 数据库的 `projects` 表。
@@ -0,0 +1,134 @@
#!/usr/bin/env python3
"""
根据 docs/example.xls 的「统计」表,为 projects 表填充 statistics_type。
统计表结构:一、基建工程(3-29) 二、业扩项目(33-50) 三、户表(54-71) 四、客户工程 五、营销项目 六、检修、技改、抢修 -> 统计类型存为「检修-技改-抢修」
通过 项目名称 或 所属项目部 包含统计表中的名称/关键字进行匹配。
用法:cd backend && PYTHONPATH=src python3 scripts/fill_statistics_type_from_xls.py [path_to_example.xls]
"""
import sys
from pathlib import Path
_backend = Path(__file__).resolve().parent.parent
if str(_backend) not in sys.path:
sys.path.insert(0, str(_backend))
import xlrd
from src.database import get_connection
def _normalize(s):
if s is None:
return ""
return str(s).strip().replace("\n", "").replace("\r", "")
def collect_names_from_stat_sheet(wb: xlrd.Book) -> dict:
"""从「统计」表收集各类型的名称/关键字列表。返回 { 统计类型: [名称1, 名称2, ...] }"""
if "统计" not in wb.sheet_names():
return {}
sh = wb.sheet_by_name("统计")
out = {
"基建工程": [],
"业扩项目": [],
"户表": [],
"客户工程": [],
"营销项目": [],
"检修-技改-抢修": [],
}
# 一、基建工程: row 0 title, row 1 header, rows 3-29 data, col 1 = 项目名称
for r in range(3, 30):
v = _normalize(sh.cell_value(r, 1))
if v and v != "合计" and not v.replace(".", "").isdigit():
out["基建工程"].append(v)
# 二、业扩项目: row 30 title, row 31 header?, row 32 合计, rows 33-50 col 0
for r in range(33, 51):
v = _normalize(sh.cell_value(r, 0))
if v and v != "合计" and "业扩" in v:
out["业扩项目"].append(v)
# 三、户表: row 51 title, row 52 header, row 53 合计, rows 54-71 col 0
for r in range(54, 72):
v = _normalize(sh.cell_value(r, 0))
if v and v != "合计" and "户表" in v:
out["户表"].append(v)
# 四、客户工程: row 72 title, row 73 header(序号,所属项目部,项目个数,总投资), 74+ 所属项目部 col 1
for r in range(75, 96):
v = _normalize(sh.cell_value(r, 1))
if v and v != "合计" and not v.replace(".", "").isdigit():
out["客户工程"].append(v)
# 五、营销项目: row 96 title, rows 97-107
for r in range(97, 108):
v = _normalize(sh.cell_value(r, 0)) or _normalize(sh.cell_value(r, 1))
if v and v != "合计":
out["营销项目"].append(v)
# 六、检修、技改、抢修: row 108 title, rows 109+ col 1 主项目名称 -> 统计类型「检修-技改-抢修」
for r in range(109, min(134, sh.nrows)):
v = _normalize(sh.cell_value(r, 1))
if v and v not in ("合计", "小计", ""):
out["检修-技改-抢修"].append(v)
return out
def main():
if len(sys.argv) >= 2:
xls_path = Path(sys.argv[1]).resolve()
else:
xls_path = _backend.parent / "docs" / "example.xls"
if not xls_path.exists():
xls_path = _backend / "docs" / "example.xls"
if not xls_path.exists():
print(f"未找到文件:{xls_path}")
sys.exit(1)
wb = xlrd.open_workbook(str(xls_path))
by_type = collect_names_from_stat_sheet(wb)
# 基建工程:从「凯里XX供电局」「都匀XX供电局」提取 XX,匹配 所属项目部 含 XX
build_keywords = []
for n in by_type.get("基建工程", []):
n = _normalize(n)
if "供电局" in n:
# 凯里城区供电分局 -> 城区,凯里三穗供电局 -> 三穗,都匀瓮安供电局 -> 瓮安
for prefix in ("凯里", "都匀"):
if n.startswith(prefix):
mid = n[len(prefix) :].split("供电局")[0].strip()
if mid and len(mid) <= 6:
build_keywords.append((mid, "基建工程"))
break
# 匹配顺序:检修-技改-抢修、业扩、户表、营销、基建(先关键字再名称)、客户工程
order = ["检修-技改-抢修", "业扩项目", "户表", "营销项目", "基建工程", "客户工程"]
with get_connection() as conn:
cur = conn.cursor(dictionary=True)
cur.execute("SELECT id, project_name, project_department FROM projects")
rows = cur.fetchall()
cur.close()
updated = 0
with get_connection() as conn:
cur = conn.cursor()
for r in rows:
pid = r["id"]
pname = _normalize(r.get("project_name") or "")
pdept = _normalize(r.get("project_department") or "")
st = None
# 基建:项目部关键字匹配(如 剑河项目部 -> 基建工程)
for kw, typ in build_keywords:
if kw in pdept or kw in pname:
st = typ
break
if not st:
for t in order:
names = by_type.get(t, [])
for n in names:
if n and (n in pname or n in pdept or (len(n) >= 4 and pname.startswith(n[:4]))):
st = t
break
if st:
break
if not st:
st = "其他"
cur.execute("UPDATE projects SET statistics_type = %s WHERE id = %s", (st, pid))
updated += 1
conn.commit()
cur.close()
print(f"已根据统计表填充 statistics_type,共更新 {updated} 条项目。")
if __name__ == "__main__":
main()
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
"""
为 projects 表增加 statistics_type 列(若不存在)。
用法:cd backend && PYTHONPATH=src python3 scripts/migrate_add_statistics_type.py
"""
import sys
from pathlib import Path
_backend = Path(__file__).resolve().parent.parent
if str(_backend) not in sys.path:
sys.path.insert(0, str(_backend))
from src.database import get_connection
def main():
with get_connection() as conn:
cur = conn.cursor()
try:
cur.execute(
"ALTER TABLE projects "
"ADD COLUMN statistics_type varchar(32) DEFAULT NULL "
"COMMENT '统计类型:基建工程、业扩项目、户表、客户工程、营销项目、检修-技改-抢修、其他' "
"AFTER cost"
)
conn.commit()
print("已为 projects 表添加 statistics_type 列。")
except Exception as e:
if "Duplicate column name" in str(e):
print("列已存在,无需迁移。")
else:
raise
finally:
cur.close()
if __name__ == "__main__":
main()
@@ -0,0 +1,28 @@
#!/usr/bin/env python3
"""
将 projects 表中 statistics_type 为「检修、技改、抢修」的项统一改为「检修-技改-抢修」。
用法:cd backend && PYTHONPATH=src python3 scripts/migrate_statistics_type_repair.py
"""
import sys
from pathlib import Path
_backend = Path(__file__).resolve().parent.parent
if str(_backend) not in sys.path:
sys.path.insert(0, str(_backend))
from src.database import get_connection
def main():
with get_connection() as conn:
cur = conn.cursor()
cur.execute(
"UPDATE projects SET statistics_type = %s WHERE statistics_type = %s",
("检修-技改-抢修", "检修、技改、抢修"),
)
n = cur.rowcount
conn.commit()
cur.close()
print(f"已将 statistics_type「检修、技改、抢修」更新为「检修-技改-抢修」,共 {n} 条。")
if __name__ == "__main__":
main()