87 lines
2.4 KiB
Python
87 lines
2.4 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
# -*- coding: utf-8 -*-
|
|||
|
|
|
|||
|
|
import psycopg2
|
|||
|
|
import os
|
|||
|
|
import sys
|
|||
|
|
from dotenv import load_dotenv
|
|||
|
|
|
|||
|
|
|
|||
|
|
load_dotenv("../../.env", override=True)
|
|||
|
|
DB_CONFIG = {
|
|||
|
|
"host": os.getenv("ROOT_DB_HOST", "localhost"),
|
|||
|
|
"port": os.getenv("ROOT_DB_PORT", "5432"),
|
|||
|
|
"dbname": os.getenv("ROOT_DB_NAME", "mydb"),
|
|||
|
|
"user": os.getenv("ROOT_DB_USER", "postgres"),
|
|||
|
|
"password": os.getenv("ROOT_DB_PASSWD", "postgres"),
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
def fetch_nodes():
|
|||
|
|
conn = psycopg2.connect(**DB_CONFIG)
|
|||
|
|
cur = conn.cursor()
|
|||
|
|
cur.execute("SELECT id, name, parent_id FROM fst")
|
|||
|
|
rows = cur.fetchall()
|
|||
|
|
cur.close()
|
|||
|
|
conn.close()
|
|||
|
|
return rows
|
|||
|
|
|
|||
|
|
|
|||
|
|
def build_tree(rows):
|
|||
|
|
nodes = {}
|
|||
|
|
for id_, name, parent_id in rows:
|
|||
|
|
nodes[id_] = {"id": id_, "name": name, "parent_id": parent_id, "children": []}
|
|||
|
|
roots = []
|
|||
|
|
for n in nodes.values():
|
|||
|
|
if n["parent_id"] is None or n["parent_id"] not in nodes:
|
|||
|
|
roots.append(n)
|
|||
|
|
else:
|
|||
|
|
nodes[n["parent_id"]]["children"].append(n)
|
|||
|
|
|
|||
|
|
def sort_rec(n):
|
|||
|
|
n["children"].sort(key=lambda x: x["name"])
|
|||
|
|
for c in n["children"]:
|
|||
|
|
sort_rec(c)
|
|||
|
|
|
|||
|
|
for r in roots:
|
|||
|
|
sort_rec(r)
|
|||
|
|
return roots
|
|||
|
|
|
|||
|
|
|
|||
|
|
def print_subtree(node, prefix="", is_last=True, depth=0):
|
|||
|
|
# depth==0: 根节点;depth>=1: 各层子节点
|
|||
|
|
if depth == 0:
|
|||
|
|
# 根直接打印名字
|
|||
|
|
print(node["name"])
|
|||
|
|
else:
|
|||
|
|
# 非根:打印前置 prefix + 连接符 + 名字
|
|||
|
|
connector = "└── " if is_last else "├── "
|
|||
|
|
print(prefix + connector + node["name"])
|
|||
|
|
# 计算传给下一层孩子的前缀
|
|||
|
|
if depth == 0:
|
|||
|
|
# 第一层孩子不缩进,prefix 仍然空
|
|||
|
|
child_prefix = ""
|
|||
|
|
else:
|
|||
|
|
# depth>=1:如果我是本层最后一个,用空格填充,否则保留竖线
|
|||
|
|
child_prefix = prefix + (" " if is_last else "│ ")
|
|||
|
|
# 递归打印子节点
|
|||
|
|
cnt = len(node["children"])
|
|||
|
|
for idx, child in enumerate(node["children"]):
|
|||
|
|
last = idx == cnt - 1
|
|||
|
|
print_subtree(child, child_prefix, last, depth + 1)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
rows = fetch_nodes()
|
|||
|
|
if not rows:
|
|||
|
|
print("fst 表中没有数据。", file=sys.stderr)
|
|||
|
|
sys.exit(1)
|
|||
|
|
roots = build_tree(rows)
|
|||
|
|
for idx, r in enumerate(roots):
|
|||
|
|
last_root = idx == len(roots) - 1
|
|||
|
|
print_subtree(r, prefix="", is_last=last_root, depth=0)
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|