Files
piano-plan/app/__init__.py
T
hmo 18351212e8 feat: 问题数据迁移到数据库;学员详情页URL导航改造;侧边栏统一
- 问题从文件系统迁移到数据库 problems 表
- 移除 PROBLEMS_DIR 配置和文件读取逻辑
- student.html 完整重写:编辑/添加/删除问题,生成方案进度显示
- 学员详情页支持独立URL访问 (/student/<id>)
- 统一侧边栏到 base.html
- 更新文档:DEPLOYMENT_SOP, MODELS, STRUCTURE, FRONTEND_ARCH
- 部署到生产环境 v1.2.0
2026-04-23 06:35:32 +08:00

127 lines
4.8 KiB
Python

# Flask 应用工厂
from flask import Flask
from app.models import db
import os
import pathlib
def create_app():
app = Flask(__name__)
# 使用 run.py 的目录作为项目根目录(绝对路径,不依赖工作目录)
import sys
if hasattr(sys, '_MEIPASS'):
BASE_DIR = pathlib.Path(sys._MEIPASS)
else:
BASE_DIR = pathlib.Path(__file__).resolve().parent.parent
# 存储到 app.config,各处引用,不再各自计算
app.config["BASE_DIR"] = BASE_DIR
app.config["SECRET_KEY"] = "piano-practice-plan-secret-key-2026"
app.config["DEBUG"] = True
app.config["SQLALCHEMY_DATABASE_URI"] = (
f"sqlite:///{BASE_DIR / 'data' / 'piano_plans.db'}"
)
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config["PDF_OUTPUT_DIR"] = BASE_DIR / "output"
app.config["API_CONFIG_FILE"] = BASE_DIR / "config" / "api_config.json"
# 初始化数据库
db.init_app(app)
# 注册蓝图
from app.routes import main_bp
from app.routes.templates import templates_bp
app.register_blueprint(main_bp)
app.register_blueprint(templates_bp)
# 创建数据库和目录
with app.app_context():
os.makedirs(os.path.join(BASE_DIR, "data"), exist_ok=True)
os.makedirs(app.config["PDF_OUTPUT_DIR"], exist_ok=True)
db.create_all()
# 简单迁移:为已存在的数据库添加新字段(必须在init_default_templates之前)
try:
from sqlalchemy import text
# 检查practice_time字段是否存在
result = db.session.execute(text("PRAGMA table_info(students)"))
columns = [row[1] for row in result]
if "practice_time" not in columns:
db.session.execute(
text(
"ALTER TABLE students ADD COLUMN practice_time VARCHAR(20) DEFAULT '30-60分钟'"
)
)
db.session.commit()
if "wechat_nickname" not in columns:
db.session.execute(
text("ALTER TABLE students ADD COLUMN wechat_nickname VARCHAR(100)")
)
db.session.commit()
# 检查student_problems表的level字段
result2 = db.session.execute(text("PRAGMA table_info(student_problems)"))
columns2 = [row[1] for row in result2]
if "level" not in columns2:
db.session.execute(
text("ALTER TABLE student_problems ADD COLUMN level VARCHAR(20)")
)
db.session.commit()
# 检查users表是否存在
result3 = db.session.execute(
text(
"SELECT name FROM sqlite_master WHERE type='table' AND name='users'"
)
)
if not result3.fetchone():
db.session.execute(
text(
"""
CREATE TABLE users (
id INTEGER PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
password_hash VARCHAR(200) NOT NULL,
role VARCHAR(20) DEFAULT 'user',
created_at TIMESTAMP
)
"""
)
)
db.session.commit()
# 检查templates表是否有sort_order字段
result4 = db.session.execute(text("PRAGMA table_info(templates)"))
template_columns = [row[1] for row in result4]
if "sort_order" not in template_columns:
db.session.execute(text("ALTER TABLE templates ADD COLUMN sort_order INTEGER DEFAULT 0"))
db.session.commit()
# 检查practice_plans表是否有template_id字段
result5 = db.session.execute(text("PRAGMA table_info(practice_plans)"))
plan_columns = [row[1] for row in result5]
if "template_id" not in plan_columns:
db.session.execute(text("ALTER TABLE practice_plans ADD COLUMN template_id INTEGER REFERENCES templates(id)"))
db.session.commit()
# 检查practice_plans表是否有is_typical字段
result6 = db.session.execute(text("PRAGMA table_info(practice_plans)"))
plan_columns2 = [row[1] for row in result6]
if "is_typical" not in plan_columns2:
db.session.execute(text("ALTER TABLE practice_plans ADD COLUMN is_typical INTEGER DEFAULT 0"))
db.session.commit()
except Exception as e:
print(f"数据库迁移: {e}")
# 初始化默认模板(必须在迁移之后)
# 已禁用:如果需要默认模板,请手动创建
# from app.routes.templates import init_default_templates
# init_default_templates()
return app