@hupan56/wlkj 2.7.12 → 3.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +1435 -1107
- package/package.json +29 -29
- package/templates/qoder/agents/insight-planning.md +1 -1
- package/templates/qoder/agents/insight-research.md +28 -15
- package/templates/qoder/commands/optional/wl-insight.md +161 -161
- package/templates/qoder/commands/optional/wl-report.md +20 -39
- package/templates/qoder/commands/optional/wl-status.md +4 -4
- package/templates/qoder/commands/wl-code.md +2 -2
- package/templates/qoder/commands/wl-design.md +6 -6
- package/templates/qoder/commands/wl-init.md +3 -3
- package/templates/qoder/commands/wl-prd.md +26 -13
- package/templates/qoder/commands/wl-req.md +43 -0
- package/templates/qoder/commands/wl-search.md +78 -47
- package/templates/qoder/commands/wl-task.md +343 -40
- package/templates/qoder/commands/wl-test.md +43 -17
- package/templates/qoder/config.yaml +52 -14
- package/templates/qoder/hooks/post-tool-use.py +181 -0
- package/templates/qoder/hooks/session-start.py +94 -23
- package/templates/qoder/hooks/stop-eval.py +207 -0
- package/templates/qoder/hooks/user-prompt-submit.py +139 -0
- package/templates/qoder/rules/wl-pipeline.md +85 -49
- package/templates/qoder/scripts/README.md +102 -0
- package/templates/qoder/scripts/capability/__init__.py +26 -0
- package/templates/qoder/scripts/capability/__main__.py +72 -0
- package/templates/qoder/scripts/capability/adapters/__init__.py +29 -0
- package/templates/qoder/scripts/capability/adapters/cli.py +316 -0
- package/templates/qoder/scripts/capability/adapters/mcp.py +334 -0
- package/templates/qoder/scripts/capability/adapters/qw.py +271 -0
- package/templates/qoder/scripts/capability/caps/__init__.py +27 -0
- package/templates/qoder/scripts/capability/caps/context.py +36 -0
- package/templates/qoder/scripts/capability/caps/cron.py +50 -0
- package/templates/qoder/scripts/capability/caps/identity.py +43 -0
- package/templates/qoder/scripts/capability/caps/memory.py +71 -0
- package/templates/qoder/scripts/capability/caps/notify.py +41 -0
- package/templates/qoder/scripts/capability/caps/present.py +48 -0
- package/templates/qoder/scripts/capability/caps/repo.py +31 -0
- package/templates/qoder/scripts/capability/caps/sandbox.py +43 -0
- package/templates/qoder/scripts/capability/chain.py +92 -0
- package/templates/qoder/scripts/capability/memory_chain.py +41 -0
- package/templates/qoder/scripts/capability/registry.py +246 -0
- package/templates/qoder/scripts/capability/registry_mcp.py +250 -0
- package/templates/qoder/scripts/capability/smoke_test.py +211 -0
- package/templates/qoder/scripts/capability/smoke_test_report.json +94 -0
- package/templates/qoder/scripts/deployment/setup/__init__.py +11 -0
- package/templates/qoder/scripts/deployment/setup/carriers.py +669 -0
- package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
- package/templates/qoder/scripts/{init_doctor.py → deployment/setup/init_doctor.py} +100 -39
- package/templates/qoder/scripts/{install_qoderwork.py → deployment/setup/install_qoderwork.py} +48 -30
- package/templates/qoder/scripts/{platform_doctor.py → deployment/setup/platform_doctor.py} +271 -259
- package/templates/qoder/scripts/{repo_root.py → deployment/setup/repo_root.py} +9 -2
- package/templates/qoder/scripts/{setup.py → deployment/setup/setup.py} +143 -14
- package/templates/qoder/scripts/{setup_lanhu.py → deployment/setup/setup_lanhu.py} +979 -963
- package/templates/qoder/scripts/domain/design/__init__.py +0 -0
- package/templates/qoder/scripts/{fill_prototype.py → domain/design/fill_prototype.py} +17 -5
- package/templates/qoder/scripts/{gen_design_doc.py → domain/design/gen_design_doc.py} +406 -394
- package/templates/qoder/scripts/domain/kg/__init__.py +11 -0
- package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/build/build_entity_registry.py +201 -0
- package/templates/qoder/scripts/domain/kg/build/build_relations.py +132 -0
- package/templates/qoder/scripts/{build_style_index.py → domain/kg/build/build_style_index.py} +50 -15
- package/templates/qoder/scripts/domain/kg/build/build_workflows.py +149 -0
- package/templates/qoder/scripts/{kg_build.py → domain/kg/build/kg_build.py} +685 -612
- package/templates/qoder/scripts/{kg_build_db.py → domain/kg/build/kg_build_db.py} +338 -327
- package/templates/qoder/scripts/{kg_incremental.py → domain/kg/build/kg_incremental.py} +425 -393
- package/templates/qoder/scripts/{learn_aggregate.py → domain/kg/build/learn_aggregate.py} +212 -201
- package/templates/qoder/scripts/domain/kg/extract/__init__.py +0 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/extract.py +430 -419
- package/templates/qoder/scripts/domain/kg/extract/test_extract.py +115 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/ts_extract.py +614 -536
- package/templates/qoder/scripts/domain/kg/graph/__init__.py +0 -0
- package/templates/qoder/scripts/{kg_link_db.py → domain/kg/graph/kg_link_db.py} +235 -224
- package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
- package/templates/qoder/scripts/{kg.py → domain/kg/kg.py} +871 -708
- package/templates/qoder/scripts/domain/kg/kg_capabilities.py +182 -0
- package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
- package/templates/qoder/scripts/{context_pack.py → domain/kg/search/context_pack.py} +45 -17
- package/templates/qoder/scripts/{enrich_prompt.py → domain/kg/search/enrich_prompt.py} +238 -226
- package/templates/qoder/scripts/domain/kg/search/prefetch.py +384 -0
- package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
- package/templates/qoder/scripts/{search_index.py → domain/kg/search/search_index.py} +210 -88
- package/templates/qoder/scripts/domain/kg/server/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/server/kgd.py +549 -0
- package/templates/qoder/scripts/domain/kg/server/perf_bench.py +197 -0
- package/templates/qoder/scripts/domain/kg/storage/__init__.py +0 -0
- package/templates/qoder/scripts/{kg_duckdb.py → domain/kg/storage/kg_duckdb.py} +70 -45
- package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
- package/templates/qoder/scripts/{learn.py → domain/learning/learn.py} +157 -146
- package/templates/qoder/scripts/domain/report/__init__.py +11 -0
- package/templates/qoder/scripts/{add_session.py → domain/report/add_session.py} +256 -244
- package/templates/qoder/scripts/{export.py → domain/report/export.py} +72 -4
- package/templates/qoder/scripts/{report.py → domain/report/report.py} +292 -281
- package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
- package/templates/qoder/scripts/domain/report/role.py +39 -0
- package/templates/qoder/scripts/{status.py → domain/report/status.py} +737 -628
- package/templates/qoder/scripts/domain/report/status_snapshot.py +191 -0
- package/templates/qoder/scripts/domain/requirement/__init__.py +0 -0
- package/templates/qoder/scripts/{archive_prd.py → domain/requirement/archive_prd.py} +389 -377
- package/templates/qoder/scripts/domain/requirement/req.py +228 -0
- package/templates/qoder/scripts/domain/task/__init__.py +11 -0
- package/templates/qoder/scripts/{git_sync.py → domain/task/git_sync.py} +90 -50
- package/templates/qoder/scripts/{syncgate.py → domain/task/syncgate.py} +18 -7
- package/templates/qoder/scripts/domain/task/task.py +229 -0
- package/templates/qoder/scripts/domain/task/task_lifecycle.py +606 -0
- package/templates/qoder/scripts/domain/task/task_query.py +162 -0
- package/templates/qoder/scripts/domain/task/task_relations.py +425 -0
- package/templates/qoder/scripts/{team_sync.py → domain/task/team_sync.py} +101 -23
- package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
- package/templates/qoder/scripts/foundation/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/bootstrap.py +145 -0
- package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/core/cmd_registry.py +112 -0
- package/templates/qoder/scripts/foundation/core/config.py +187 -0
- package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -400
- package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/data/contract.py +317 -0
- package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
- package/templates/qoder/scripts/foundation/data/result.py +223 -0
- package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -392
- package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/identity/check_publish.py +103 -0
- package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -238
- package/templates/qoder/scripts/foundation/identity/guard.py +159 -0
- package/templates/qoder/scripts/{common → foundation/identity}/identity.py +132 -9
- package/templates/qoder/scripts/foundation/identity/roles.py +60 -0
- package/templates/qoder/scripts/foundation/integrations/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/integrations}/active_task.py +30 -15
- package/templates/qoder/scripts/{common → foundation/integrations}/feishu.py +11 -10
- package/templates/qoder/scripts/{common → foundation/integrations}/terms.py +3 -3
- package/templates/qoder/scripts/foundation/integrations/zentao_client.py +220 -0
- package/templates/qoder/scripts/foundation/io/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/io}/filelock.py +1 -1
- package/templates/qoder/scripts/{common → foundation/io}/reqid.py +1 -1
- package/templates/qoder/scripts/foundation/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/protocol/duckdb_conn.py +39 -0
- package/templates/qoder/scripts/foundation/protocol/mcp_base.py +268 -0
- package/templates/qoder/scripts/foundation/utils/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/utils}/eval_api.py +12 -1
- package/templates/qoder/scripts/{common → foundation/utils}/pip_install.py +23 -6
- package/templates/qoder/scripts/{common → foundation/utils}/platform_guard.py +1 -1
- package/templates/qoder/scripts/orchestration/__init__.py +0 -0
- package/templates/qoder/scripts/orchestration/wlkj.py +185 -0
- package/templates/qoder/scripts/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/protocol/browser/README.md +207 -0
- package/templates/qoder/scripts/protocol/browser/SKILL.md +265 -0
- package/templates/qoder/scripts/protocol/browser/config.env +9 -0
- package/templates/qoder/scripts/protocol/browser/references/cdp-api.md +110 -0
- package/templates/qoder/scripts/protocol/browser/references/migration-2.5.3.md +72 -0
- package/templates/qoder/scripts/protocol/browser/references/site-patterns/.gitkeep +0 -0
- package/templates/qoder/scripts/protocol/browser/scripts/browser-discovery.mjs +138 -0
- package/templates/qoder/scripts/protocol/browser/scripts/cdp-proxy.mjs +672 -0
- package/templates/qoder/scripts/protocol/browser/scripts/check-deps.mjs +206 -0
- package/templates/qoder/scripts/protocol/browser/scripts/find-url.mjs +253 -0
- package/templates/qoder/scripts/protocol/browser/scripts/match-site.mjs +46 -0
- package/templates/qoder/scripts/protocol/browser/templates/config.env.template +9 -0
- package/templates/qoder/scripts/protocol/mcp/__init__.py +11 -0
- package/templates/qoder/scripts/protocol/mcp/kg_mcp_server.py +478 -0
- package/templates/qoder/scripts/{lanhu_stdio_wrapper.py → protocol/mcp/lanhu_stdio_wrapper.py} +131 -119
- package/templates/qoder/scripts/protocol/mcp/mcp_doctor.py +548 -0
- package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
- package/templates/qoder/scripts/protocol/mcp/mysql_mcp_server.py +461 -0
- package/templates/qoder/scripts/protocol/mcp/zentao_mcp_server.py +1920 -0
- package/templates/qoder/scripts/protocol/transports/__init__.py +56 -0
- package/templates/qoder/scripts/protocol/transports/base.py +87 -0
- package/templates/qoder/scripts/protocol/transports/cli.py +132 -0
- package/templates/qoder/scripts/protocol/transports/http.py +141 -0
- package/templates/qoder/scripts/protocol/transports/stdio.py +293 -0
- package/templates/qoder/scripts/run_weekly_update.bat +27 -18
- package/templates/qoder/scripts/run_weekly_update.sh +22 -13
- package/templates/qoder/scripts/validation/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/perf-report-2026-06-24.md +139 -0
- package/templates/qoder/scripts/validation/eval/qwork_harness.py +617 -0
- package/templates/qoder/scripts/validation/eval/report-commands.md +220 -0
- package/templates/qoder/scripts/validation/eval/transcript_timing.py +386 -0
- package/templates/qoder/scripts/validation/metrics/__init__.py +0 -0
- package/templates/qoder/scripts/{eval_prd.py → validation/metrics/eval_prd.py} +84 -15
- package/templates/qoder/scripts/validation/metrics/usability_score.py +750 -0
- package/templates/qoder/scripts/validation/test/__init__.py +11 -0
- package/templates/qoder/scripts/validation/test/assertion_gen.py +551 -0
- package/templates/qoder/scripts/{autotest.py → validation/test/autotest.py} +1234 -1751
- package/templates/qoder/scripts/validation/test/autotest_auth.py +109 -0
- package/templates/qoder/scripts/{autotest_batch.py → validation/test/autotest_batch.py} +255 -224
- package/templates/qoder/scripts/validation/test/autotest_data.py +680 -0
- package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
- package/templates/qoder/scripts/{autotest_run.py → validation/test/autotest_run.py} +323 -297
- package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
- package/templates/qoder/scripts/{benchmark.py → validation/test/benchmark.py} +17 -5
- package/templates/qoder/scripts/{kg_auto_login.py → validation/test/kg_auto_login.py} +208 -196
- package/templates/qoder/scripts/{kg_test_runner.py → validation/test/kg_test_runner.py} +13 -2
- package/templates/qoder/scripts/{page_probe.py → validation/test/page_probe.py} +470 -459
- package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
- package/templates/qoder/settings.json +23 -2
- package/templates/qoder/skills/design-import/SKILL.md +235 -226
- package/templates/qoder/skills/design-review/SKILL.md +91 -82
- package/templates/qoder/skills/prd-generator/SKILL.md +38 -24
- package/templates/qoder/skills/prd-review/SKILL.md +8 -6
- package/templates/qoder/skills/prototype-generator/SKILL.md +296 -247
- package/templates/qoder/skills/spec-coder/SKILL.md +24 -15
- package/templates/qoder/skills/spec-generator/SKILL.md +24 -12
- package/templates/qoder/skills/test-generator/SKILL.md +9 -7
- package/templates/qoder/skills/wl-code/SKILL.md +25 -13
- package/templates/qoder/skills/wl-commit/SKILL.md +10 -9
- package/templates/qoder/skills/wl-design/SKILL.md +7 -5
- package/templates/qoder/skills/wl-init/SKILL.md +8 -8
- package/templates/qoder/skills/wl-insight/SKILL.md +25 -12
- package/templates/qoder/skills/wl-prd-full/SKILL.md +59 -8
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +7 -7
- package/templates/qoder/skills/wl-prd-review/SKILL.md +18 -6
- package/templates/qoder/skills/wl-report/SKILL.md +27 -25
- package/templates/qoder/skills/wl-search/SKILL.md +151 -80
- package/templates/qoder/skills/wl-spec/SKILL.md +9 -7
- package/templates/qoder/skills/wl-status/SKILL.md +50 -18
- package/templates/qoder/skills/wl-task/SKILL.md +94 -12
- package/templates/qoder/skills/wl-test/SKILL.md +154 -45
- package/templates/qoder/templates/prd-full-template.md +7 -0
- package/templates/root/AGENTS.md +275 -237
- package/templates/root/requirements.txt +3 -0
- package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +3 -3
- package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +2 -2
- package/templates/qoder/hooks/inject-workflow-state.py +0 -169
- package/templates/qoder/scripts/__pycache__/check_mcp_launch.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/install_qoderwork.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/mcp_launcher.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/platform_doctor.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/check_carriers.py +0 -238
- package/templates/qoder/scripts/check_mcp.py +0 -298
- package/templates/qoder/scripts/check_mcp_launch.py +0 -183
- package/templates/qoder/scripts/check_qoderwork_consistency.py +0 -166
- package/templates/qoder/scripts/collect_prds.py +0 -31
- package/templates/qoder/scripts/common/mentions.py +0 -134
- package/templates/qoder/scripts/common/utf8.py +0 -38
- package/templates/qoder/scripts/extract_api_params.py +0 -246
- package/templates/qoder/scripts/extract_routes.py +0 -54
- package/templates/qoder/scripts/extract_routes_tree.py +0 -78
- package/templates/qoder/scripts/handoff.py +0 -22
- package/templates/qoder/scripts/init_developer.py +0 -76
- package/templates/qoder/scripts/kg_mcp_server.py +0 -801
- package/templates/qoder/scripts/kg_semantic.py +0 -150
- package/templates/qoder/scripts/mcp_launcher.py +0 -414
- package/templates/qoder/scripts/mysql_mcp_server.py +0 -396
- package/templates/qoder/scripts/parse_prds.py +0 -33
- package/templates/qoder/scripts/role.py +0 -51
- package/templates/qoder/scripts/sync_carriers.py +0 -259
- package/templates/qoder/scripts/task.py +0 -1261
- package/templates/qoder/scripts/workspace_init.py +0 -102
- package/templates/qoder/scripts/zentao_mcp_server.py +0 -424
- package/templates/qoder/skills/prompt-enrich/SKILL.md +0 -90
- /package/templates/qoder/scripts/{common → deployment}/__init__.py +0 -0
- /package/templates/qoder/{skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp → scripts/domain/__init__.py} +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/graph}/graph_traverse.py +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/server}/repowiki.py +0 -0
- /package/templates/qoder/scripts/{common → foundation/io}/atomicio.py +0 -0
- /package/templates/qoder/scripts/{secure-ls.js → validation/test/secure-ls.js} +0 -0
|
@@ -1,327 +1,338 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
"FROM information_schema.
|
|
93
|
-
"WHERE
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
)"""
|
|
125
|
-
con.execute("CREATE TABLE IF NOT EXISTS
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
"""
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
con.close()
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# v3.0 路径自举: 引导到 common/bootstrap, 统一 sys.path 逻辑
|
|
4
|
+
import os as _o, sys as _s
|
|
5
|
+
_f = _o.path.abspath(__file__)
|
|
6
|
+
for _ in range(10):
|
|
7
|
+
_f = _o.path.dirname(_f)
|
|
8
|
+
_cp = _o.path.join(_f, 'foundation')
|
|
9
|
+
if _o.path.isfile(_o.path.join(_cp, 'bootstrap.py')):
|
|
10
|
+
break
|
|
11
|
+
if _cp not in _s.path: _s.path.insert(0, _cp)
|
|
12
|
+
from bootstrap import setup; setup()
|
|
13
|
+
|
|
14
|
+
"""kg_build_db.py - 把 MySQL 数据库 schema 导入 kg.duckdb (数据层图谱)。
|
|
15
|
+
|
|
16
|
+
和 kg_build.py 对称:
|
|
17
|
+
kg_build.py: 扫 data/code/* → kg.duckdb (代码图谱: entities/fields/...)
|
|
18
|
+
kg_build_db.py: 扫 MySQL schema → kg.duckdb (数据图谱: DB_TABLE/db_columns/db_table_fields)
|
|
19
|
+
|
|
20
|
+
产出 3 张新表 (复用现有 kg.duckdb, 不另建文件):
|
|
21
|
+
- db_tables: 表级实体 (db, table, table_comment, entity_count)
|
|
22
|
+
- db_columns: 列级真相 (db, table, column_name, data_type, is_nullable, column_comment)
|
|
23
|
+
← eval_prd.py 的 A1 第二层锚定读这张表
|
|
24
|
+
- db_table_fields: 表↔字段边 (table_id, column_name) 给图谱遍历用
|
|
25
|
+
|
|
26
|
+
同时在 entities 表插入 DB_TABLE 类型实体 (让图谱认识"数据库表"这个概念),
|
|
27
|
+
并在 edges 表连 DB_TABLE → 同名 ENDPOINT (若有), 做"改表影响哪些接口"的影响分析。
|
|
28
|
+
|
|
29
|
+
安全: 只读 MySQL (INFORMATION_SCHEMA), 不碰业务数据行。
|
|
30
|
+
|
|
31
|
+
增量策略 (库级签名对比, 不再全量重扫):
|
|
32
|
+
- 每个库算签名 (表名列表 + 列总数), 存 build_meta。
|
|
33
|
+
- 下次跑: 签名没变 = 该库 schema 没变 = 跳过。变了才重建该库。
|
|
34
|
+
- 比 MySQL update_time 准 (后者数据变更也会刷新, 不代表 schema 变)。
|
|
35
|
+
|
|
36
|
+
用法:
|
|
37
|
+
python kg_build_db.py # 增量: 只重建有变化的库
|
|
38
|
+
python kg_build_db.py --db fywl_ics_quality # 只扫指定库 (强制重建它)
|
|
39
|
+
python kg_build_db.py --rebuild # 全量重建 (清所有签名 + 数据)
|
|
40
|
+
python kg_build_db.py --stats # 看统计
|
|
41
|
+
"""
|
|
42
|
+
import os
|
|
43
|
+
import sys
|
|
44
|
+
import csv
|
|
45
|
+
import socket
|
|
46
|
+
import tempfile
|
|
47
|
+
import time
|
|
48
|
+
|
|
49
|
+
# 顶部 import (避免函数内延迟 import 触发 GIL 冲突: pymysql 的 C 扩展
|
|
50
|
+
# 和 duckdb 在同进程内延迟加载时线程状态会打架)
|
|
51
|
+
import duckdb
|
|
52
|
+
import pymysql
|
|
53
|
+
|
|
54
|
+
SCRIPTS = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 子包→scripts/根
|
|
55
|
+
sys.path.insert(0, SCRIPTS)
|
|
56
|
+
|
|
57
|
+
from foundation.core.paths import DATA_INDEX_DIR
|
|
58
|
+
|
|
59
|
+
# MySQL 配置 (复用 mysql_mcp_server 的环境变量)
|
|
60
|
+
MYSQL_HOST = os.environ.get('MYSQL_HOST', '10.54.6.9')
|
|
61
|
+
MYSQL_PORT = int(os.environ.get('MYSQL_PORT', '3306'))
|
|
62
|
+
MYSQL_USER = os.environ.get('MYSQL_USER', 'fywl_ics_test')
|
|
63
|
+
MYSQL_PASSWORD = os.environ.get('MYSQL_PASSWORD', 'Fywl_ics_test56476')
|
|
64
|
+
DB_PATH = str(DATA_INDEX_DIR / 'kg_db.duckdb') # 数据层图谱独立文件 (不和 kg.duckdb 抢写锁)
|
|
65
|
+
|
|
66
|
+
# 只扫业务库 (跳过 information_schema/mysql/sys/performance_schema)
|
|
67
|
+
DB_NAME_PREFIX = 'fywl_ics_'
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def check_intranet():
|
|
71
|
+
"""TCP 连通性校验 (同 mysql_mcp_server)。"""
|
|
72
|
+
try:
|
|
73
|
+
with socket.create_connection((MYSQL_HOST, MYSQL_PORT), timeout=3):
|
|
74
|
+
return True
|
|
75
|
+
except (socket.timeout, OSError):
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _connect_mysql():
|
|
80
|
+
return pymysql.connect(
|
|
81
|
+
host=MYSQL_HOST, port=MYSQL_PORT,
|
|
82
|
+
user=MYSQL_USER, password=MYSQL_PASSWORD,
|
|
83
|
+
charset='utf8mb4',
|
|
84
|
+
cursorclass=pymysql.cursors.DictCursor,
|
|
85
|
+
connect_timeout=5, read_timeout=30,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _discover_databases(cur):
|
|
90
|
+
"""列出所有 fywl_ics_* 业务库。"""
|
|
91
|
+
cur.execute(
|
|
92
|
+
"SELECT schema_name FROM information_schema.schemata "
|
|
93
|
+
"WHERE schema_name LIKE %s ORDER BY schema_name",
|
|
94
|
+
(DB_NAME_PREFIX + '%',)
|
|
95
|
+
)
|
|
96
|
+
return [r['schema_name'] for r in cur.fetchall()]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _fetch_tables(cur, db):
|
|
100
|
+
"""某库的所有表 + 注释。"""
|
|
101
|
+
cur.execute(
|
|
102
|
+
"SELECT table_name, table_comment "
|
|
103
|
+
"FROM information_schema.tables "
|
|
104
|
+
"WHERE table_schema = %s AND table_type = 'BASE TABLE' "
|
|
105
|
+
"ORDER BY table_name",
|
|
106
|
+
(db,)
|
|
107
|
+
)
|
|
108
|
+
return cur.fetchall()
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _fetch_columns(cur, db):
|
|
112
|
+
"""某库所有表的所有列。"""
|
|
113
|
+
cur.execute(
|
|
114
|
+
"SELECT table_name, column_name, data_type, column_type, "
|
|
115
|
+
"is_nullable, column_key, column_comment "
|
|
116
|
+
"FROM information_schema.columns "
|
|
117
|
+
"WHERE table_schema = %s ORDER BY table_name, ordinal_position",
|
|
118
|
+
(db,)
|
|
119
|
+
)
|
|
120
|
+
return cur.fetchall()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _init_db_tables(con):
|
|
124
|
+
"""建数据层表 (IF NOT EXISTS)。独立文件, 自带 build_meta。"""
|
|
125
|
+
con.execute("""CREATE TABLE IF NOT EXISTS db_tables (
|
|
126
|
+
db TEXT, table_name TEXT, table_comment TEXT, column_count INTEGER,
|
|
127
|
+
PRIMARY KEY (db, table_name)
|
|
128
|
+
)""")
|
|
129
|
+
con.execute("""CREATE TABLE IF NOT EXISTS db_columns (
|
|
130
|
+
db TEXT, table_name TEXT, column_name TEXT, data_type TEXT,
|
|
131
|
+
column_type TEXT, is_nullable TEXT, column_key TEXT, column_comment TEXT
|
|
132
|
+
)""")
|
|
133
|
+
con.execute("""CREATE TABLE IF NOT EXISTS db_table_fields (
|
|
134
|
+
table_id TEXT, column_name TEXT
|
|
135
|
+
)""")
|
|
136
|
+
con.execute("CREATE TABLE IF NOT EXISTS build_meta(key TEXT PRIMARY KEY, value TEXT)")
|
|
137
|
+
con.execute("CREATE INDEX IF NOT EXISTS idx_dbcol_name ON db_columns(column_name)")
|
|
138
|
+
con.execute("CREATE INDEX IF NOT EXISTS idx_dbcol_table ON db_columns(table_name)")
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _bulk_insert(con, table, columns, rows):
|
|
142
|
+
"""批量插入: JSONL → duckdb COPY (万行级 0.01s)。
|
|
143
|
+
executemany 在 DuckDB 1.4 有性能 bug (每行 ~22ms), JSONL COPY 快 5000 倍。
|
|
144
|
+
JSON 天然处理逗号/引号/换行符, 无 CSV 转义问题。
|
|
145
|
+
rows: list of tuples, 顺序与 columns 对应。"""
|
|
146
|
+
if not rows:
|
|
147
|
+
return
|
|
148
|
+
import json as _json
|
|
149
|
+
tmp = tempfile.NamedTemporaryFile(suffix='.jsonl', delete=False,
|
|
150
|
+
mode='w', encoding='utf-8')
|
|
151
|
+
for r in rows:
|
|
152
|
+
obj = {columns[i]: ('' if r[i] is None else str(r[i]))
|
|
153
|
+
for i in range(len(columns))}
|
|
154
|
+
tmp.write(_json.dumps(obj, ensure_ascii=False) + '\n')
|
|
155
|
+
tmp.close()
|
|
156
|
+
try:
|
|
157
|
+
sql = "COPY %s FROM '%s' (FORMAT JSON)" % (
|
|
158
|
+
table, tmp.name.replace('\\', '/'))
|
|
159
|
+
con.execute(sql)
|
|
160
|
+
finally:
|
|
161
|
+
os.unlink(tmp.name)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _db_signature(mcur, db):
|
|
165
|
+
"""算某库的 schema 签名 (表名列表 + 列总数)。
|
|
166
|
+
用于增量: 签名没变 = 该库 schema 没变, 跳过。变了才重建该库。
|
|
167
|
+
比 update_time 准 (后者数据变更也会刷新)。"""
|
|
168
|
+
mcur.execute(
|
|
169
|
+
"SELECT table_name FROM information_schema.tables "
|
|
170
|
+
"WHERE table_schema=%s AND table_type='BASE TABLE' ORDER BY table_name",
|
|
171
|
+
(db,)
|
|
172
|
+
)
|
|
173
|
+
tables = sorted(r['table_name'] for r in mcur.fetchall())
|
|
174
|
+
mcur.execute(
|
|
175
|
+
"SELECT COUNT(*) c FROM information_schema.columns WHERE table_schema=%s",
|
|
176
|
+
(db,)
|
|
177
|
+
)
|
|
178
|
+
col_cnt = mcur.fetchone()['c']
|
|
179
|
+
return '%d:%s' % (col_cnt, ','.join(tables))
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _load_sig(con, db):
|
|
183
|
+
"""读上次该库的签名。无记录返回 None。"""
|
|
184
|
+
row = con.execute(
|
|
185
|
+
"SELECT value FROM build_meta WHERE key=?", ['db_sig:' + db]
|
|
186
|
+
).fetchone()
|
|
187
|
+
return row[0] if row else None
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def build(dbs=None, rebuild=False):
|
|
191
|
+
"""扫 MySQL schema → 写 kg_db.duckdb。
|
|
192
|
+
增量策略: 库级签名对比, 只重建 schema 有变化的库。
|
|
193
|
+
rebuild=True: 忽略签名, 全量重建。
|
|
194
|
+
"""
|
|
195
|
+
if not check_intranet():
|
|
196
|
+
print('⚠️ 无法连接内网 MySQL (%s:%s)。请连接公司内网后重试。' % (MYSQL_HOST, MYSQL_PORT))
|
|
197
|
+
return 0, 0
|
|
198
|
+
|
|
199
|
+
mcon = _connect_mysql()
|
|
200
|
+
try:
|
|
201
|
+
mcur = mcon.cursor()
|
|
202
|
+
all_dbs = dbs or _discover_databases(mcur)
|
|
203
|
+
|
|
204
|
+
# 预读 duckdb 现有签名 (增量对比用)
|
|
205
|
+
con = duckdb.connect(DB_PATH)
|
|
206
|
+
_init_db_tables(con)
|
|
207
|
+
if rebuild:
|
|
208
|
+
con.execute("DELETE FROM db_tables")
|
|
209
|
+
con.execute("DELETE FROM db_columns")
|
|
210
|
+
con.execute("DELETE FROM db_table_fields")
|
|
211
|
+
con.execute("DELETE FROM build_meta WHERE key LIKE 'db_sig:%'")
|
|
212
|
+
|
|
213
|
+
changed_dbs = []
|
|
214
|
+
skipped_dbs = []
|
|
215
|
+
for db in all_dbs:
|
|
216
|
+
sig = _db_signature(mcur, db)
|
|
217
|
+
old_sig = _load_sig(con, db)
|
|
218
|
+
if not rebuild and old_sig == sig:
|
|
219
|
+
skipped_dbs.append(db)
|
|
220
|
+
continue
|
|
221
|
+
changed_dbs.append((db, sig))
|
|
222
|
+
|
|
223
|
+
con.close() # 释放, 后面重新开写
|
|
224
|
+
|
|
225
|
+
if skipped_dbs:
|
|
226
|
+
print('跳过 %d 个未变库: %s' % (len(skipped_dbs), ', '.join(skipped_dbs)))
|
|
227
|
+
if not changed_dbs:
|
|
228
|
+
print('✓ 全部库 schema 未变化, 无需重建。')
|
|
229
|
+
return 0, 0
|
|
230
|
+
print('重建 %d 个有变化的库: %s'
|
|
231
|
+
% (len(changed_dbs), ', '.join(d for d, _ in changed_dbs)))
|
|
232
|
+
|
|
233
|
+
all_tables = []
|
|
234
|
+
all_columns = []
|
|
235
|
+
for db, sig in changed_dbs:
|
|
236
|
+
tables = _fetch_tables(mcur, db)
|
|
237
|
+
cols = _fetch_columns(mcur, db)
|
|
238
|
+
col_count = {}
|
|
239
|
+
for c in cols:
|
|
240
|
+
t = c['table_name']
|
|
241
|
+
col_count[t] = col_count.get(t, 0) + 1
|
|
242
|
+
for t in tables:
|
|
243
|
+
tn = t['table_name']
|
|
244
|
+
all_tables.append((db, tn, t.get('table_comment', '') or '',
|
|
245
|
+
col_count.get(tn, 0)))
|
|
246
|
+
for c in cols:
|
|
247
|
+
all_columns.append((
|
|
248
|
+
db, c['table_name'], c['column_name'], c['data_type'],
|
|
249
|
+
c['column_type'], c['is_nullable'], c['column_key'],
|
|
250
|
+
c.get('column_comment', '') or '',
|
|
251
|
+
))
|
|
252
|
+
print(' %s: %d 表, %d 列' % (db, len(tables), len(cols)))
|
|
253
|
+
finally:
|
|
254
|
+
mcon.close()
|
|
255
|
+
|
|
256
|
+
# 写 kg_db.duckdb (只写变化的库的数据)
|
|
257
|
+
con = duckdb.connect(DB_PATH)
|
|
258
|
+
try:
|
|
259
|
+
_init_db_tables(con)
|
|
260
|
+
# 删掉变化库的旧数据 (库级增量: 只删该库的, 不动其它库)
|
|
261
|
+
for db, _ in changed_dbs:
|
|
262
|
+
con.execute("DELETE FROM db_tables WHERE db=?", [db])
|
|
263
|
+
con.execute("DELETE FROM db_columns WHERE db=?", [db])
|
|
264
|
+
con.execute("DELETE FROM db_table_fields WHERE table_id LIKE ?", ['dbt:%s:%%' % db])
|
|
265
|
+
|
|
266
|
+
if all_tables:
|
|
267
|
+
_bulk_insert(con, 'db_tables',
|
|
268
|
+
['db', 'table_name', 'table_comment', 'column_count'],
|
|
269
|
+
all_tables)
|
|
270
|
+
if all_columns:
|
|
271
|
+
_bulk_insert(con, 'db_columns',
|
|
272
|
+
['db', 'table_name', 'column_name', 'data_type',
|
|
273
|
+
'column_type', 'is_nullable', 'column_key', 'column_comment'],
|
|
274
|
+
all_columns)
|
|
275
|
+
tf_rows = [('dbt:%s:%s' % (row[0], row[1]), row[2])
|
|
276
|
+
for row in all_columns]
|
|
277
|
+
_bulk_insert(con, 'db_table_fields', ['table_id', 'column_name'], tf_rows)
|
|
278
|
+
|
|
279
|
+
# 记录每个变化库的新签名 + 总构建时间
|
|
280
|
+
for db, sig in changed_dbs:
|
|
281
|
+
con.execute(
|
|
282
|
+
"INSERT OR REPLACE INTO build_meta(key, value) VALUES (?,?)",
|
|
283
|
+
['db_sig:' + db, sig]
|
|
284
|
+
)
|
|
285
|
+
con.execute(
|
|
286
|
+
"INSERT OR REPLACE INTO build_meta(key, value) VALUES (?,?)",
|
|
287
|
+
['db_schema_last_build', str(time.time())]
|
|
288
|
+
)
|
|
289
|
+
print('\n增量导入完成: 重建 %d 库, %d 表, %d 列 → %s'
|
|
290
|
+
% (len(changed_dbs), len(all_tables), len(all_columns), os.path.basename(DB_PATH)))
|
|
291
|
+
return len(all_tables), len(all_columns)
|
|
292
|
+
finally:
|
|
293
|
+
con.close()
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def query_table_columns(table):
|
|
297
|
+
"""查询某表的所有列 (给 eval_prd 等用)。返回 {column_name_lower}。"""
|
|
298
|
+
try:
|
|
299
|
+
con = duckdb.connect(DB_PATH, read_only=True)
|
|
300
|
+
rows = con.execute(
|
|
301
|
+
"SELECT DISTINCT column_name FROM db_columns WHERE table_name = ?",
|
|
302
|
+
[table]
|
|
303
|
+
).fetchall()
|
|
304
|
+
con.close()
|
|
305
|
+
return {r[0].lower() for r in rows if r[0]}
|
|
306
|
+
except Exception:
|
|
307
|
+
return set()
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def stats():
|
|
311
|
+
"""数据层图谱统计。"""
|
|
312
|
+
try:
|
|
313
|
+
con = duckdb.connect(DB_PATH, read_only=True)
|
|
314
|
+
t = con.execute("SELECT COUNT(*) FROM db_tables").fetchone()[0]
|
|
315
|
+
c = con.execute("SELECT COUNT(*) FROM db_columns").fetchone()[0]
|
|
316
|
+
by_db = con.execute(
|
|
317
|
+
"SELECT db, COUNT(*) FROM db_tables GROUP BY db ORDER BY COUNT(*) DESC"
|
|
318
|
+
).fetchall()
|
|
319
|
+
con.close()
|
|
320
|
+
print('数据层图谱 (%s): %d 表 / %d 列' % (os.path.basename(DB_PATH), t, c))
|
|
321
|
+
for db, cnt in by_db:
|
|
322
|
+
print(' %s: %d 表' % (db, cnt))
|
|
323
|
+
except Exception as e:
|
|
324
|
+
print('统计失败: %s' % e)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
if __name__ == '__main__':
|
|
328
|
+
args = sys.argv[1:]
|
|
329
|
+
rebuild = '--rebuild' in args
|
|
330
|
+
db_filter = None
|
|
331
|
+
for i, a in enumerate(args):
|
|
332
|
+
if a == '--db' and i + 1 < len(args):
|
|
333
|
+
db_filter = [args[i + 1]]
|
|
334
|
+
if '--stats' in args:
|
|
335
|
+
stats()
|
|
336
|
+
else:
|
|
337
|
+
build(dbs=db_filter, rebuild=rebuild)
|
|
338
|
+
stats()
|