Skip to content

Commit c1ea226

Browse files
committed
fix: make seed script standalone, no app module imports
Avoids triggering security/config init chain on Windows CI where cp1252 encoding can't handle Chinese log messages.
1 parent 204577d commit c1ea226

File tree

2 files changed

+145
-115
lines changed

2 files changed

+145
-115
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,4 @@ mock-llm.log
4949
*.egg
5050
*.whl
5151
CLAUDE.md
52+
QueryGPT-*.dmg
Lines changed: 144 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,190 +1,220 @@
11
"""构建时预生成 demo.db 和 querygpt.db,打包进桌面版。
22
3+
完全独立脚本,不导入任何 app 模块,避免触发 security/config 初始化。
34
运行时只需复制到用户目录即可,零初始化。
45
"""
56

67
import json
78
import os
8-
import sys
9-
from datetime import datetime, timezone
9+
import random
10+
import sqlite3
11+
from datetime import datetime, timedelta, timezone
1012
from uuid import uuid4
1113

12-
# 让 import app.* 能找到
13-
API_DIR = os.path.join(os.path.dirname(__file__), "..", "..", "api")
14-
sys.path.insert(0, API_DIR)
15-
16-
DATA_DIR = os.path.join(API_DIR, "data")
14+
DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "..", "api", "data")
1715
os.makedirs(DATA_DIR, exist_ok=True)
1816

17+
# ── Demo data ──
18+
19+
PRODUCTS = [
20+
("iPhone 15 Pro", "手机", 7999, 5500),
21+
("iPhone 15", "手机", 5999, 4200),
22+
("MacBook Pro 14", "电脑", 14999, 10500),
23+
("MacBook Air", "电脑", 8999, 6300),
24+
("iPad Pro", "平板", 6499, 4500),
25+
("iPad Air", "平板", 4399, 3100),
26+
("AirPods Pro", "配件", 1899, 1200),
27+
("Apple Watch", "穿戴", 2999, 2100),
28+
("Magic Keyboard", "配件", 999, 650),
29+
("Studio Display", "显示器", 11499, 8000),
30+
("Mac Mini", "电脑", 4499, 3100),
31+
("HomePod", "智能家居", 2299, 1600),
32+
]
33+
REGIONS = ["华东", "华南", "华北", "西南", "华中"]
34+
FIRST_NAMES = ["张", "李", "王", "刘", "陈", "杨", "黄", "赵", "周", "吴"]
35+
LAST_NAMES = ["伟", "芳", "娜", "敏", "静", "丽", "强", "磊", "军", "洋", "勇", "艳", "杰", "涛", "明"]
36+
ORDER_STATUSES = ["已完成", "已发货", "处理中", "已取消"]
37+
38+
DEMO_SEMANTIC_TERMS = [
39+
{
40+
"term": "GMV",
41+
"expression": "SUM(sales.quantity * sales.unit_price)",
42+
"term_type": "metric",
43+
"description": "Gross Merchandise Value — total sales revenue (quantity × unit price)",
44+
"examples": ["What is this month's GMV?", "GMV by region"],
45+
},
46+
{
47+
"term": "Top Customers",
48+
"expression": "customers.id IN (SELECT customer_id FROM sales GROUP BY customer_id HAVING SUM(amount) > 100000)",
49+
"term_type": "filter",
50+
"description": "Customers with cumulative spending over 100,000",
51+
"examples": ["List all top customers", "Top customers by region"],
52+
},
53+
{
54+
"term": "Average Order Value",
55+
"expression": "AVG(sales.amount)",
56+
"term_type": "metric",
57+
"description": "Average transaction amount per sale",
58+
"examples": ["What is the average order value?", "AOV trend over the past 6 months"],
59+
},
60+
]
61+
1962

2063
def build_demo_db():
2164
"""生成 demo.db(示例销售数据库)"""
2265
demo_path = os.path.join(DATA_DIR, "demo.db")
2366
if os.path.exists(demo_path):
2467
os.remove(demo_path)
2568

26-
# DATA_DIR env 让 demo_db 模块把文件写到正确位置
27-
os.environ["DATA_DIR"] = DATA_DIR
28-
from app.core.demo_db import init_demo_database
69+
conn = sqlite3.connect(demo_path)
70+
c = conn.cursor()
71+
72+
# 建表
73+
c.execute("""CREATE TABLE products (
74+
id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, category TEXT NOT NULL,
75+
price REAL NOT NULL, cost REAL NOT NULL, created_at DATE DEFAULT CURRENT_DATE)""")
76+
c.execute("""CREATE TABLE customers (
77+
id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, region TEXT NOT NULL,
78+
email TEXT, registered_at DATE NOT NULL)""")
79+
c.execute("""CREATE TABLE sales (
80+
id INTEGER PRIMARY KEY AUTOINCREMENT, date DATE NOT NULL, product_id INTEGER NOT NULL,
81+
customer_id INTEGER NOT NULL, quantity INTEGER NOT NULL, unit_price REAL NOT NULL,
82+
amount REAL NOT NULL, region TEXT NOT NULL,
83+
FOREIGN KEY (product_id) REFERENCES products(id),
84+
FOREIGN KEY (customer_id) REFERENCES customers(id))""")
85+
c.execute("""CREATE TABLE orders (
86+
id INTEGER PRIMARY KEY AUTOINCREMENT, order_no TEXT NOT NULL UNIQUE,
87+
order_date DATE NOT NULL, customer_id INTEGER NOT NULL, total_amount REAL NOT NULL,
88+
status TEXT NOT NULL, FOREIGN KEY (customer_id) REFERENCES customers(id))""")
89+
c.execute("CREATE INDEX idx_sales_date ON sales(date)")
90+
c.execute("CREATE INDEX idx_sales_region ON sales(region)")
91+
c.execute("CREATE INDEX idx_orders_date ON orders(order_date)")
92+
93+
# 产品
94+
c.executemany("INSERT INTO products (name, category, price, cost) VALUES (?, ?, ?, ?)", PRODUCTS)
95+
96+
# 客户
97+
base_date = datetime.now() - timedelta(days=365)
98+
customers = []
99+
for i in range(100):
100+
name = random.choice(FIRST_NAMES) + random.choice(LAST_NAMES)
101+
registered_at = base_date + timedelta(days=random.randint(0, 365))
102+
customers.append((name, random.choice(REGIONS), f"user{i+1}@example.com", registered_at.strftime("%Y-%m-%d")))
103+
c.executemany("INSERT INTO customers (name, region, email, registered_at) VALUES (?, ?, ?, ?)", customers)
104+
105+
# 销售
106+
today = datetime.now()
107+
sales = []
108+
for month_offset in range(12, 0, -1):
109+
month_date = today - timedelta(days=month_offset * 30)
110+
month = month_date.month
111+
seasonal = 1.5 if month in [11, 12, 1] else (0.8 if month in [6, 7, 8] else 1.0)
112+
growth = 1 + (12 - month_offset) * 0.03
113+
for _ in range(int(random.randint(40, 80) * seasonal * growth)):
114+
day = random.randint(1, 28)
115+
sale_date = month_date.replace(day=min(day, 28))
116+
pid = random.randint(1, len(PRODUCTS))
117+
qty = random.randint(1, 5)
118+
price = PRODUCTS[pid - 1][2]
119+
sales.append((sale_date.strftime("%Y-%m-%d"), pid, random.randint(1, 100), qty, price, price * qty, random.choice(REGIONS)))
120+
c.executemany("INSERT INTO sales (date, product_id, customer_id, quantity, unit_price, amount, region) VALUES (?, ?, ?, ?, ?, ?, ?)", sales)
121+
122+
# 订单
123+
orders = []
124+
for i in range(200):
125+
order_date = today - timedelta(days=random.randint(0, 180))
126+
r = random.random()
127+
status = "已完成" if r < 0.70 else ("已发货" if r < 0.85 else ("处理中" if r < 0.95 else "已取消"))
128+
orders.append((f"ORD{order_date.strftime('%Y%m%d')}{i+1:04d}", order_date.strftime("%Y-%m-%d"), random.randint(1, 100), round(random.uniform(1000, 50000), 2), status))
129+
c.executemany("INSERT INTO orders (order_no, order_date, customer_id, total_amount, status) VALUES (?, ?, ?, ?, ?)", orders)
29130

30-
path = init_demo_database()
31-
print(f" demo.db created: {path}")
131+
conn.commit()
132+
conn.close()
133+
print(f" demo.db created: {demo_path}")
32134
return demo_path
33135

34136

35137
def build_querygpt_db(demo_db_path: str):
36138
"""生成预填充的 querygpt.db(连接 + 术语 + AppSettings)"""
37-
import sqlite3
38-
39139
db_path = os.path.join(DATA_DIR, "querygpt.db")
40140
if os.path.exists(db_path):
41141
os.remove(db_path)
42142

43143
conn = sqlite3.connect(db_path)
44144
c = conn.cursor()
45-
46145
now = datetime.now(timezone.utc).isoformat()
47146

48147
# 建表(与 SQLAlchemy 模型对应)
49148
c.execute("""CREATE TABLE connections (
50-
id CHAR(32) PRIMARY KEY,
51-
name VARCHAR(100) NOT NULL,
52-
driver VARCHAR(20) NOT NULL,
53-
host VARCHAR(255),
54-
port INTEGER,
55-
username VARCHAR(100),
56-
password_encrypted TEXT,
57-
database_name VARCHAR(100),
58-
extra_options JSON DEFAULT '{}',
59-
is_default BOOLEAN DEFAULT 0,
60-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
61-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
62-
)""")
149+
id CHAR(32) PRIMARY KEY, name VARCHAR(100) NOT NULL, driver VARCHAR(20) NOT NULL,
150+
host VARCHAR(255), port INTEGER, username VARCHAR(100), password_encrypted TEXT,
151+
database_name VARCHAR(100), extra_options JSON DEFAULT '{}', is_default BOOLEAN DEFAULT 0,
152+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP)""")
63153

64154
c.execute("""CREATE TABLE models (
65-
id CHAR(32) PRIMARY KEY,
66-
name VARCHAR(100) NOT NULL,
67-
provider VARCHAR(50) NOT NULL,
68-
model_id VARCHAR(100) NOT NULL,
69-
base_url VARCHAR(500),
70-
api_key_encrypted TEXT,
71-
extra_options JSON DEFAULT '{}',
72-
is_default BOOLEAN DEFAULT 0,
73-
is_active BOOLEAN DEFAULT 1,
74-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
75-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
76-
)""")
155+
id CHAR(32) PRIMARY KEY, name VARCHAR(100) NOT NULL, provider VARCHAR(50) NOT NULL,
156+
model_id VARCHAR(100) NOT NULL, base_url VARCHAR(500), api_key_encrypted TEXT,
157+
extra_options JSON DEFAULT '{}', is_default BOOLEAN DEFAULT 0, is_active BOOLEAN DEFAULT 1,
158+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP)""")
77159

78160
c.execute("""CREATE TABLE conversations (
79161
id CHAR(32) PRIMARY KEY,
80162
connection_id CHAR(32) REFERENCES connections(id) ON DELETE SET NULL,
81163
model_id CHAR(32) REFERENCES models(id) ON DELETE SET NULL,
82-
title VARCHAR(200),
83-
status VARCHAR(20) DEFAULT 'active',
84-
is_favorite BOOLEAN DEFAULT 0,
164+
title VARCHAR(200), status VARCHAR(20) DEFAULT 'active', is_favorite BOOLEAN DEFAULT 0,
85165
extra_data JSON DEFAULT '{}',
86-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
87-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
88-
)""")
166+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP)""")
89167
c.execute("CREATE INDEX idx_conversations_status ON conversations(status)")
90168

91169
c.execute("""CREATE TABLE messages (
92170
id CHAR(32) PRIMARY KEY,
93171
conversation_id CHAR(32) NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
94-
role VARCHAR(20) NOT NULL,
95-
content TEXT NOT NULL,
96-
extra_data JSON DEFAULT '{}',
97-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
98-
)""")
172+
role VARCHAR(20) NOT NULL, content TEXT NOT NULL, extra_data JSON DEFAULT '{}',
173+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP)""")
99174
c.execute("CREATE INDEX idx_messages_conversation ON messages(conversation_id)")
100175

101176
c.execute("""CREATE TABLE app_settings (
102177
id INTEGER PRIMARY KEY,
103178
default_model_id CHAR(32) REFERENCES models(id) ON DELETE SET NULL,
104179
default_connection_id CHAR(32) REFERENCES connections(id) ON DELETE SET NULL,
105-
context_rounds INTEGER DEFAULT 5,
106-
python_enabled BOOLEAN DEFAULT 1,
107-
diagnostics_enabled BOOLEAN DEFAULT 1,
108-
auto_repair_enabled BOOLEAN DEFAULT 1,
180+
context_rounds INTEGER DEFAULT 5, python_enabled BOOLEAN DEFAULT 1,
181+
diagnostics_enabled BOOLEAN DEFAULT 1, auto_repair_enabled BOOLEAN DEFAULT 1,
109182
demo_initialized BOOLEAN DEFAULT 0,
110-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
111-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
112-
)""")
183+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP)""")
113184

114185
c.execute("""CREATE TABLE semantic_terms (
115186
id CHAR(32) PRIMARY KEY,
116187
connection_id CHAR(32) REFERENCES connections(id) ON DELETE CASCADE,
117-
term VARCHAR(100) NOT NULL,
118-
expression TEXT NOT NULL,
119-
term_type VARCHAR(20) DEFAULT 'metric',
120-
description TEXT,
121-
examples JSON DEFAULT '[]',
122-
is_active BOOLEAN DEFAULT 1,
123-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
124-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
125-
)""")
188+
term VARCHAR(100) NOT NULL, expression TEXT NOT NULL, term_type VARCHAR(20) DEFAULT 'metric',
189+
description TEXT, examples JSON DEFAULT '[]', is_active BOOLEAN DEFAULT 1,
190+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP)""")
126191
c.execute("CREATE INDEX idx_semantic_terms_term ON semantic_terms(term)")
127192

128193
c.execute("""CREATE TABLE table_relationships (
129194
id CHAR(32) PRIMARY KEY,
130195
connection_id CHAR(32) NOT NULL REFERENCES connections(id) ON DELETE CASCADE,
131-
source_table VARCHAR(100) NOT NULL,
132-
source_column VARCHAR(100) NOT NULL,
133-
target_table VARCHAR(100) NOT NULL,
134-
target_column VARCHAR(100) NOT NULL,
135-
relationship_type VARCHAR(10) DEFAULT '1:N',
136-
join_type VARCHAR(20) DEFAULT 'LEFT',
137-
description TEXT,
138-
is_active BOOLEAN DEFAULT 1,
139-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
140-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
141-
)""")
196+
source_table VARCHAR(100) NOT NULL, source_column VARCHAR(100) NOT NULL,
197+
target_table VARCHAR(100) NOT NULL, target_column VARCHAR(100) NOT NULL,
198+
relationship_type VARCHAR(10) DEFAULT '1:N', join_type VARCHAR(20) DEFAULT 'LEFT',
199+
description TEXT, is_active BOOLEAN DEFAULT 1,
200+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP)""")
142201

143202
c.execute("""CREATE TABLE prompts (
144-
id CHAR(32) PRIMARY KEY,
145-
name VARCHAR(100) NOT NULL,
146-
content TEXT NOT NULL,
147-
description TEXT,
148-
version INTEGER DEFAULT 1,
149-
is_active BOOLEAN DEFAULT 1,
203+
id CHAR(32) PRIMARY KEY, name VARCHAR(100) NOT NULL, content TEXT NOT NULL,
204+
description TEXT, version INTEGER DEFAULT 1, is_active BOOLEAN DEFAULT 1,
150205
is_default BOOLEAN DEFAULT 0,
151206
parent_id CHAR(32) REFERENCES prompts(id) ON DELETE SET NULL,
152-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
153-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
154-
)""")
207+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP)""")
155208

156-
# 插入 demo connectiondatabase_name 用占位符,运行时替换
209+
# 插入 demo connectiondatabase_name 用占位符,运行时替换
157210
conn_id = uuid4().hex
158211
c.execute(
159212
"INSERT INTO connections (id, name, driver, database_name, extra_options, is_default, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
160213
(conn_id, "Sample Database", "sqlite", "__DEMO_DB_PATH__", "{}", 1, now, now),
161214
)
162215

163216
# 插入 demo semantic terms
164-
terms = [
165-
{
166-
"term": "GMV",
167-
"expression": "SUM(sales.quantity * sales.unit_price)",
168-
"term_type": "metric",
169-
"description": "Gross Merchandise Value — total sales revenue (quantity × unit price)",
170-
"examples": ["What is this month's GMV?", "GMV by region"],
171-
},
172-
{
173-
"term": "Top Customers",
174-
"expression": "customers.id IN (SELECT customer_id FROM sales GROUP BY customer_id HAVING SUM(amount) > 100000)",
175-
"term_type": "filter",
176-
"description": "Customers with cumulative spending over 100,000",
177-
"examples": ["List all top customers", "Top customers by region"],
178-
},
179-
{
180-
"term": "Average Order Value",
181-
"expression": "AVG(sales.amount)",
182-
"term_type": "metric",
183-
"description": "Average transaction amount per sale",
184-
"examples": ["What is the average order value?", "AOV trend over the past 6 months"],
185-
},
186-
]
187-
for t in terms:
217+
for t in DEMO_SEMANTIC_TERMS:
188218
c.execute(
189219
"INSERT INTO semantic_terms (id, connection_id, term, expression, term_type, description, examples, is_active, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
190220
(uuid4().hex, conn_id, t["term"], t["expression"], t["term_type"], t["description"], json.dumps(t["examples"]), 1, now, now),
@@ -199,11 +229,10 @@ def build_querygpt_db(demo_db_path: str):
199229
conn.commit()
200230
conn.close()
201231
print(f" querygpt.db created: {db_path}")
202-
return db_path
203232

204233

205234
if __name__ == "__main__":
206235
print("=== Seeding databases for desktop build ===")
207-
demo_path = build_demo_db()
208-
build_querygpt_db(demo_path)
236+
build_demo_db()
237+
build_querygpt_db(os.path.join(DATA_DIR, "demo.db"))
209238
print("Done.")

0 commit comments

Comments
 (0)