118 lines
5.1 KiB
Python
118 lines
5.1 KiB
Python
import os
|
||
from flask import Flask, session, request
|
||
from sqlalchemy import inspect
|
||
from dotenv import load_dotenv
|
||
|
||
# 1. Загрузка переменных окружения - в самом верху
|
||
load_dotenv()
|
||
|
||
# 2. Импорт расширений из центрального файла
|
||
from extensions import scheduler, db, migrate, login_manager, babel
|
||
from models import init_encryption
|
||
|
||
# 3. Фабрика приложений
|
||
def create_app():
|
||
"""
|
||
Создает и конфигурирует экземпляр Flask приложения.
|
||
"""
|
||
app = Flask(__name__)
|
||
|
||
# --- Конфигурация приложения ---
|
||
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'default-super-secret-key-for-dev')
|
||
|
||
# --- НАДЕЖНАЯ НАСТРОЙКА ПУТЕЙ ---
|
||
# Получаем абсолютный путь к директории, где находится app.py
|
||
basedir = os.path.abspath(os.path.dirname(__file__))
|
||
# Устанавливаем путь к папке data
|
||
data_dir = os.path.join(basedir, os.environ.get('DATA_DIR', 'data'))
|
||
# Создаем эту директорию, если ее не существует. Это ключевой момент.
|
||
os.makedirs(data_dir, exist_ok=True)
|
||
|
||
app.config['DATA_DIR'] = data_dir
|
||
|
||
# Устанавливаем путь к БД
|
||
db_path = os.path.join(data_dir, 'app.db')
|
||
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', f"sqlite:///{db_path}")
|
||
|
||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||
app.config['BABEL_DEFAULT_LOCALE'] = 'ru'
|
||
app.config['ENCRYPTION_KEY'] = os.environ.get('ENCRYPTION_KEY')
|
||
|
||
|
||
# --- Определяем селектор языка ---
|
||
def get_locale():
|
||
if 'language' in session:
|
||
return session['language']
|
||
return request.accept_languages.best_match(['ru', 'en'])
|
||
|
||
# --- Инициализация расширений с приложением ---
|
||
db.init_app(app)
|
||
migrate.init_app(app, db)
|
||
login_manager.init_app(app)
|
||
babel.init_app(app, locale_selector=get_locale)
|
||
scheduler.init_app(app)
|
||
|
||
init_encryption(app)
|
||
|
||
# --- Регистрация блюпринтов ---
|
||
from routes import main_bp, execute_olap_export
|
||
app.register_blueprint(main_bp)
|
||
|
||
login_manager.login_view = 'main.login'
|
||
login_manager.login_message = "Пожалуйста, войдите, чтобы получить доступ к этой странице."
|
||
login_manager.login_message_category = "info"
|
||
|
||
with app.app_context():
|
||
if inspect(db.engine).has_table('user_config'):
|
||
from models import User, UserConfig
|
||
all_configs = UserConfig.query.all()
|
||
for config in all_configs:
|
||
user_id = config.user_id
|
||
mappings = config.mappings
|
||
for sheer_title, params in mappings.items():
|
||
if isinstance(params, dict):
|
||
cron_schedule = params.get('schedule_cron')
|
||
if cron_schedule:
|
||
job_id = f"user_{user_id}_sheet_{sheer_title}"
|
||
try:
|
||
scheduler.add_job(
|
||
id=job_id,
|
||
func=execute_olap_export,
|
||
trigger='cron',
|
||
args=[user_id, sheer_title],
|
||
**_parse_cron_string(cron_schedule)
|
||
)
|
||
app.logger.info(f"Job {job_id} loaded on startup.")
|
||
except Exception as e:
|
||
app.logger.error(f"Failed to load job {job_id}: {e}")
|
||
else:
|
||
app.logger.warning("Database tables not found. Skipping job loading on startup. Run 'flask init-db' to create the tables.")
|
||
scheduler.start()
|
||
|
||
# --- Регистрация команд CLI ---
|
||
from models import User, UserConfig
|
||
@app.cli.command('init-db')
|
||
def init_db_command():
|
||
"""Создает или пересоздает таблицы в базе данных."""
|
||
print("Creating database tables...")
|
||
db.create_all()
|
||
print("Database tables created successfully.")
|
||
|
||
return app
|
||
|
||
# --- Точка входа для запуска ---
|
||
app = create_app()
|
||
|
||
# --- Вспомогательная функция для парсинга cron ---
|
||
def _parse_cron_string(cron_str):
|
||
"""Парсит строку cron в словарь для APScheduler."""
|
||
parts = cron_str.split()
|
||
if len(parts) != 5:
|
||
raise ValueError("Invalid cron string format. Expected 5 parts.")
|
||
|
||
keys = ['minute', 'hour', 'day', 'month', 'day_of_week']
|
||
return {keys[i]: part for i, part in enumerate(parts)}
|
||
|
||
if __name__ == '__main__':
|
||
# Для прямого запуска через `python app.py` (удобно для отладки)
|
||
app.run(host='0.0.0.0', port=int(os.environ.get("PORT", 5005))) |