11"""构建时预生成 demo.db 和 querygpt.db,打包进桌面版。
22
3+ 完全独立脚本,不导入任何 app 模块,避免触发 security/config 初始化。
34运行时只需复制到用户目录即可,零初始化。
45"""
56
67import json
78import os
8- import sys
9- from datetime import datetime , timezone
9+ import random
10+ import sqlite3
11+ from datetime import datetime , timedelta , timezone
1012from 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" )
1715os .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
2063def 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
35137def 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 connection — database_name 用占位符,运行时替换
209+ # 插入 demo connection( database_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
205234if __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